1 /** @file
2 
3   EFI_REGULAR_EXPRESSION_PROTOCOL Implementation
4 
5   (C) Copyright 2015-2016 Hewlett Packard Enterprise Development LP<BR>
6 
7   SPDX-License-Identifier: BSD-2-Clause-Patent
8 
9 **/
10 
11 #include "RegularExpressionDxe.h"
12 
13 STATIC
14 EFI_REGEX_SYNTAX_TYPE * CONST mSupportedSyntaxes[] = {
15   &gEfiRegexSyntaxTypePosixExtendedGuid,
16   &gEfiRegexSyntaxTypePerlGuid
17 };
18 
19 STATIC
20 EFI_REGULAR_EXPRESSION_PROTOCOL mProtocolInstance = {
21   RegularExpressionMatch,
22   RegularExpressionGetInfo
23 };
24 
25 
26 
27 #define CHAR16_ENCODING ONIG_ENCODING_UTF16_LE
28 
29 /**
30   Call the Oniguruma regex match API.
31 
32   Same parameters as RegularExpressionMatch, except SyntaxType is required.
33 
34   @param String         A pointer to a NULL terminated string to match against the
35                         regular expression string specified by Pattern.
36 
37   @param Pattern        A pointer to a NULL terminated string that represents the
38                         regular expression.
39   @param SyntaxType     A pointer to the EFI_REGEX_SYNTAX_TYPE that identifies the
40                         regular expression syntax type to use. May be NULL in which
41                         case the function will use its default regular expression
42                         syntax type.
43 
44   @param Result         On return, points to TRUE if String fully matches against
45                         the regular expression Pattern using the regular expression
46                         SyntaxType. Otherwise, points to FALSE.
47 
48   @param Captures       A Pointer to an array of EFI_REGEX_CAPTURE objects to receive
49                         the captured groups in the event of a match. The full
50                         sub-string match is put in Captures[0], and the results of N
51                         capturing groups are put in Captures[1:N]. If Captures is
52                         NULL, then this function doesn't allocate the memory for the
53                         array and does not build up the elements. It only returns the
54                         number of matching patterns in CapturesCount. If Captures is
55                         not NULL, this function returns a pointer to an array and
56                         builds up the elements in the array. CapturesCount is also
57                         updated to the number of matching patterns found. It is the
58                         caller's responsibility to free the memory pool in Captures
59                         and in each CapturePtr in the array elements.
60 
61   @param CapturesCount  On output, CapturesCount is the number of matching patterns
62                         found in String. Zero means no matching patterns were found
63                         in the string.
64 
65   @retval  EFI_SUCCESS       Regex compilation and match completed successfully.
66   @retval  EFI_DEVICE_ERROR  Regex compilation failed.
67 
68 **/
69 STATIC
70 EFI_STATUS
OnigurumaMatch(IN CHAR16 * String,IN CHAR16 * Pattern,IN EFI_REGEX_SYNTAX_TYPE * SyntaxType,OUT BOOLEAN * Result,OUT EFI_REGEX_CAPTURE ** Captures,OPTIONAL OUT UINTN * CapturesCount)71 OnigurumaMatch (
72   IN  CHAR16                *String,
73   IN  CHAR16                *Pattern,
74   IN  EFI_REGEX_SYNTAX_TYPE *SyntaxType,
75   OUT BOOLEAN               *Result,
76   OUT EFI_REGEX_CAPTURE     **Captures,     OPTIONAL
77   OUT UINTN                 *CapturesCount
78   )
79 {
80   regex_t         *OnigRegex;
81   OnigSyntaxType  *OnigSyntax;
82   OnigRegion      *Region;
83   INT32           OnigResult;
84   OnigErrorInfo   ErrorInfo;
85   OnigUChar       ErrorMessage[ONIG_MAX_ERROR_MESSAGE_LEN];
86   UINT32          Index;
87   OnigUChar       *Start;
88   EFI_STATUS      Status;
89 
90 
91   Status = EFI_SUCCESS;
92 
93   //
94   // Detemine the internal syntax type
95   //
96   OnigSyntax = ONIG_SYNTAX_DEFAULT;
97   if (CompareGuid (SyntaxType, &gEfiRegexSyntaxTypePosixExtendedGuid)) {
98     OnigSyntax = ONIG_SYNTAX_POSIX_EXTENDED;
99   } else if (CompareGuid (SyntaxType, &gEfiRegexSyntaxTypePerlGuid)) {
100     OnigSyntax = ONIG_SYNTAX_PERL;
101   } else {
102     DEBUG ((DEBUG_ERROR, "Unsupported regex syntax - using default\n"));
103     return EFI_UNSUPPORTED;
104   }
105 
106   //
107   // Compile pattern
108   //
109   Start = (OnigUChar*)Pattern;
110   OnigResult = onig_new (
111                  &OnigRegex,
112                  Start,
113                  Start + onigenc_str_bytelen_null (CHAR16_ENCODING, Start),
114                  ONIG_OPTION_DEFAULT,
115                  CHAR16_ENCODING,
116                  OnigSyntax,
117                  &ErrorInfo
118                  );
119 
120   if (OnigResult != ONIG_NORMAL) {
121     onig_error_code_to_str (ErrorMessage, OnigResult, &ErrorInfo);
122     DEBUG ((DEBUG_ERROR, "Regex compilation failed: %a\n", ErrorMessage));
123     return EFI_DEVICE_ERROR;
124   }
125 
126   //
127   // Try to match
128   //
129   Start = (OnigUChar*)String;
130   Region = onig_region_new ();
131   if (Region == NULL) {
132     onig_free (OnigRegex);
133     return EFI_OUT_OF_RESOURCES;
134   }
135   OnigResult = onig_search (
136                  OnigRegex,
137                  Start,
138                  Start + onigenc_str_bytelen_null (CHAR16_ENCODING, Start),
139                  Start,
140                  Start + onigenc_str_bytelen_null (CHAR16_ENCODING, Start),
141                  Region,
142                  ONIG_OPTION_NONE
143                  );
144 
145   if (OnigResult >= 0) {
146     *Result = TRUE;
147   } else {
148     *Result = FALSE;
149     if (OnigResult != ONIG_MISMATCH) {
150       onig_error_code_to_str (ErrorMessage, OnigResult);
151       DEBUG ((DEBUG_ERROR, "Regex match failed: %a\n", ErrorMessage));
152       onig_region_free (Region, 1);
153       onig_free (OnigRegex);
154       return EFI_DEVICE_ERROR;
155     }
156   }
157 
158   //
159   // If successful, copy out the region (capture) information
160   //
161   if (*Result && Captures != NULL) {
162     *CapturesCount = Region->num_regs;
163     *Captures = AllocateZeroPool (*CapturesCount * sizeof(**Captures));
164     if (*Captures != NULL) {
165       for (Index = 0; Index < *CapturesCount; ++Index) {
166         //
167         // Region beg/end values represent bytes, not characters
168         //
169         (*Captures)[Index].Length = (Region->end[Index] - Region->beg[Index]) / sizeof(CHAR16);
170         (*Captures)[Index].CapturePtr = AllocateCopyPool (
171                                           ((*Captures)[Index].Length) * sizeof (CHAR16),
172                                           (CHAR16*)((UINTN)String + Region->beg[Index])
173                                           );
174         if ((*Captures)[Index].CapturePtr == NULL) {
175           Status = EFI_OUT_OF_RESOURCES;
176           break;
177         }
178       }
179 
180       if (EFI_ERROR (Status)) {
181         for (Index = 0; Index < *CapturesCount; ++Index) {
182           if ((*Captures)[Index].CapturePtr != NULL) {
183             FreePool ((CHAR16*)(*Captures)[Index].CapturePtr);
184           }
185         }
186         FreePool (*Captures);
187       }
188     }
189   }
190 
191   onig_region_free (Region, 1);
192   onig_free (OnigRegex);
193 
194   return Status;
195 }
196 
197 /**
198   Returns information about the regular expression syntax types supported
199   by the implementation.
200 
201   @param This                      A pointer to the EFI_REGULAR_EXPRESSION_PROTOCOL
202                                    instance.
203 
204   @param  RegExSyntaxTypeListSize  On input, the size in bytes of RegExSyntaxTypeList.
205                                    On output with a return code of EFI_SUCCESS, the
206                                    size in bytes of the data returned in
207                                    RegExSyntaxTypeList. On output with a return code
208                                    of EFI_BUFFER_TOO_SMALL, the size of
209                                    RegExSyntaxTypeList required to obtain the list.
210 
211   @param   RegExSyntaxTypeList     A caller-allocated memory buffer filled by the
212                                    driver with one EFI_REGEX_SYNTAX_TYPE element
213                                    for each supported Regular expression syntax
214                                    type. The list must not change across multiple
215                                    calls to the same driver. The first syntax
216                                    type in the list is the default type for the
217                                    driver.
218 
219   @retval EFI_SUCCESS            The regular expression syntax types list
220                                  was returned successfully.
221   @retval EFI_UNSUPPORTED        The service is not supported by this driver.
222   @retval EFI_DEVICE_ERROR       The list of syntax types could not be
223                                  retrieved due to a hardware or firmware error.
224   @retval EFI_BUFFER_TOO_SMALL   The buffer RegExSyntaxTypeList is too small
225                                  to hold the result.
226   @retval EFI_INVALID_PARAMETER  RegExSyntaxTypeListSize is NULL
227 
228 **/
229 EFI_STATUS
230 EFIAPI
RegularExpressionGetInfo(IN EFI_REGULAR_EXPRESSION_PROTOCOL * This,IN OUT UINTN * RegExSyntaxTypeListSize,OUT EFI_REGEX_SYNTAX_TYPE * RegExSyntaxTypeList)231 RegularExpressionGetInfo (
232   IN     EFI_REGULAR_EXPRESSION_PROTOCOL *This,
233   IN OUT UINTN                           *RegExSyntaxTypeListSize,
234   OUT    EFI_REGEX_SYNTAX_TYPE           *RegExSyntaxTypeList
235   )
236 {
237   UINTN SyntaxSize;
238   UINTN Index;
239 
240   if (This == NULL || RegExSyntaxTypeListSize == NULL) {
241     return EFI_INVALID_PARAMETER;
242   }
243 
244   if (*RegExSyntaxTypeListSize != 0 && RegExSyntaxTypeList == NULL) {
245     return EFI_INVALID_PARAMETER;
246   }
247 
248   SyntaxSize = ARRAY_SIZE (mSupportedSyntaxes) * sizeof(**mSupportedSyntaxes);
249 
250   if (*RegExSyntaxTypeListSize < SyntaxSize) {
251     *RegExSyntaxTypeListSize = SyntaxSize;
252     return EFI_BUFFER_TOO_SMALL;
253   }
254 
255   for (Index = 0; Index < ARRAY_SIZE (mSupportedSyntaxes); ++Index) {
256     CopyMem (&RegExSyntaxTypeList[Index], mSupportedSyntaxes[Index], sizeof(**mSupportedSyntaxes));
257   }
258   *RegExSyntaxTypeListSize = SyntaxSize;
259 
260   return EFI_SUCCESS;
261 }
262 
263 /**
264   Checks if the input string matches to the regular expression pattern.
265 
266   @param This          A pointer to the EFI_REGULAR_EXPRESSION_PROTOCOL instance.
267                        Type EFI_REGULAR_EXPRESSION_PROTOCOL is defined in Section
268                        XYZ.
269 
270   @param String        A pointer to a NULL terminated string to match against the
271                        regular expression string specified by Pattern.
272 
273   @param Pattern       A pointer to a NULL terminated string that represents the
274                        regular expression.
275 
276   @param SyntaxType    A pointer to the EFI_REGEX_SYNTAX_TYPE that identifies the
277                        regular expression syntax type to use. May be NULL in which
278                        case the function will use its default regular expression
279                        syntax type.
280 
281   @param Result        On return, points to TRUE if String fully matches against
282                        the regular expression Pattern using the regular expression
283                        SyntaxType. Otherwise, points to FALSE.
284 
285   @param Captures      A Pointer to an array of EFI_REGEX_CAPTURE objects to receive
286                        the captured groups in the event of a match. The full
287                        sub-string match is put in Captures[0], and the results of N
288                        capturing groups are put in Captures[1:N]. If Captures is
289                        NULL, then this function doesn't allocate the memory for the
290                        array and does not build up the elements. It only returns the
291                        number of matching patterns in CapturesCount. If Captures is
292                        not NULL, this function returns a pointer to an array and
293                        builds up the elements in the array. CapturesCount is also
294                        updated to the number of matching patterns found. It is the
295                        caller's responsibility to free the memory pool in Captures
296                        and in each CapturePtr in the array elements.
297 
298   @param CapturesCount On output, CapturesCount is the number of matching patterns
299                        found in String. Zero means no matching patterns were found
300                        in the string.
301 
302   @retval EFI_SUCCESS            The regular expression string matching
303                                  completed successfully.
304   @retval EFI_UNSUPPORTED        The regular expression syntax specified by
305                                  SyntaxType is not supported by this driver.
306   @retval EFI_DEVICE_ERROR       The regular expression string matching
307                                  failed due to a hardware or firmware error.
308   @retval EFI_INVALID_PARAMETER  String, Pattern, Result, or CapturesCountis
309                                  NULL.
310 
311 **/
312 EFI_STATUS
313 EFIAPI
RegularExpressionMatch(IN EFI_REGULAR_EXPRESSION_PROTOCOL * This,IN CHAR16 * String,IN CHAR16 * Pattern,IN EFI_REGEX_SYNTAX_TYPE * SyntaxType,OPTIONAL OUT BOOLEAN * Result,OUT EFI_REGEX_CAPTURE ** Captures,OPTIONAL OUT UINTN * CapturesCount)314 RegularExpressionMatch (
315   IN  EFI_REGULAR_EXPRESSION_PROTOCOL *This,
316   IN  CHAR16                          *String,
317   IN  CHAR16                          *Pattern,
318   IN  EFI_REGEX_SYNTAX_TYPE           *SyntaxType, OPTIONAL
319   OUT BOOLEAN                         *Result,
320   OUT EFI_REGEX_CAPTURE               **Captures, OPTIONAL
321   OUT UINTN                           *CapturesCount
322   )
323 {
324   EFI_STATUS  Status;
325   UINT32      Index;
326   BOOLEAN     Supported;
327 
328   if (This == NULL || String == NULL || Pattern == NULL || Result == NULL || CapturesCount == NULL) {
329     return EFI_INVALID_PARAMETER;
330   }
331 
332   //
333   // Figure out which syntax to use
334   //
335   if (SyntaxType == NULL) {
336     SyntaxType = mSupportedSyntaxes[0];
337   } else {
338     Supported = FALSE;
339     for (Index = 0; Index < ARRAY_SIZE (mSupportedSyntaxes); ++Index) {
340       if (CompareGuid (SyntaxType, mSupportedSyntaxes[Index])) {
341         Supported = TRUE;
342         break;
343       }
344     }
345     if (!Supported) {
346       return EFI_UNSUPPORTED;
347     }
348   }
349 
350   Status = OnigurumaMatch (String, Pattern, SyntaxType, Result, Captures, CapturesCount);
351 
352   return Status;
353 }
354 
355 /**
356   Entry point for RegularExpressionDxe.
357 
358   @param ImageHandle     Image handle this driver.
359   @param SystemTable     Pointer to SystemTable.
360 
361   @retval Status         Whether this function complete successfully.
362 
363 **/
364 EFI_STATUS
365 EFIAPI
RegularExpressionDxeEntry(IN EFI_HANDLE ImageHandle,IN EFI_SYSTEM_TABLE * SystemTable)366 RegularExpressionDxeEntry (
367   IN  EFI_HANDLE        ImageHandle,
368   IN  EFI_SYSTEM_TABLE  *SystemTable
369   )
370 {
371   EFI_STATUS  Status;
372 
373   Status = gBS->InstallMultipleProtocolInterfaces (
374                   &ImageHandle,
375                   &gEfiRegularExpressionProtocolGuid,
376                   &mProtocolInstance,
377                   NULL
378                   );
379 
380   return Status;
381 }
382