/src/freeradius-server/src/lib/util/regex.c
Line | Count | Source |
1 | | /* |
2 | | * This program is free software; you can redistribute it and/or modify |
3 | | * it under the terms of the GNU General Public License as published by |
4 | | * the Free Software Foundation; either version 2 of the License, or |
5 | | * (at your option) any later version. |
6 | | * |
7 | | * This program is distributed in the hope that it will be useful, |
8 | | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
9 | | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
10 | | * GNU General Public License for more details. |
11 | | * |
12 | | * You should have received a copy of the GNU General Public License |
13 | | * along with this program; if not, write to the Free Software |
14 | | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA |
15 | | */ |
16 | | |
17 | | /** Wrappers around various regular expression libraries |
18 | | * |
19 | | * @file src/lib/util/regex.c |
20 | | * |
21 | | * @copyright 2014 The FreeRADIUS server project |
22 | | * @copyright 2014 Arran Cudbard-Bell (a.cudbardb@freeradius.org) |
23 | | */ |
24 | | RCSID("$Id: e6fcff50b267f384e2eab2b47ae39db47f3f7367 $") |
25 | | |
26 | | #ifdef HAVE_REGEX |
27 | | |
28 | | #include <freeradius-devel/util/regex.h> |
29 | | |
30 | | #if defined(HAVE_REGEX_PCRE2) && defined(PCRE2_CONFIG_JIT) |
31 | | #ifndef FR_PCRE_JIT_STACK_MIN |
32 | | # define FR_PCRE_JIT_STACK_MIN (128 * 1024) |
33 | | #endif |
34 | | #ifndef FR_PCRE_JIT_STACK_MAX |
35 | | # define FR_PCRE_JIT_STACK_MAX (512 * 1024) |
36 | | #endif |
37 | | #endif |
38 | | |
39 | | /* |
40 | | * pcre2_set_max_pattern_compiled_length() arrived in PCRE2 10.41. |
41 | | */ |
42 | | #if defined(HAVE_REGEX_PCRE2) && defined(PCRE2_MAJOR) && \ |
43 | | ((PCRE2_MAJOR > 10) || ((PCRE2_MAJOR == 10) && (PCRE2_MINOR >= 41))) |
44 | | # define FR_PCRE2_HAVE_MAX_PATTERN_LENGTH 1 |
45 | | |
46 | | /* |
47 | | * The largest a single pattern may compile to. |
48 | | * |
49 | | * A bounded repeat is expanded by the engine, and nesting them |
50 | | * multiplies: "((((a){40}){40}){40}){40}" is 25 characters, and expands |
51 | | * to around 40^4 nodes. With no limit, a pattern of a few dozen bytes |
52 | | * compiles to gigabytes. That matters because regex_compile() runs at |
53 | | * runtime as well as at config time, see xlat_func_regex_search(). |
54 | | * |
55 | | * Hand-written patterns compile to a few KB at most, so this bound is |
56 | | * orders of magnitude above anything legitimate, and only fires on |
57 | | * patterns built to exhaust memory. |
58 | | */ |
59 | | # ifndef FR_PCRE2_MAX_PATTERN_LENGTH |
60 | | # define FR_PCRE2_MAX_PATTERN_LENGTH (1024 * 1024) |
61 | | # endif |
62 | | #endif |
63 | | |
64 | | const fr_sbuff_escape_rules_t regex_escape_rules = { |
65 | | .name = "regex", |
66 | | .chr = '\\', |
67 | | .subs = { |
68 | | ['$'] = '$', |
69 | | ['*'] = '*', |
70 | | ['+'] = '+', |
71 | | ['.'] = '.', |
72 | | ['/'] = '/', |
73 | | ['?'] = '?', |
74 | | ['('] = '(', |
75 | | [')'] = ')', |
76 | | ['['] = '[', |
77 | | [']'] = ']', |
78 | | ['{'] = '{', |
79 | | ['}'] = '}', |
80 | | ['\\'] = '\\', |
81 | | ['^'] = '^', |
82 | | ['`'] = '`', |
83 | | ['|'] = '|', |
84 | | ['\a'] = 'a', |
85 | | ['\b'] = 'b', |
86 | | ['\n'] = 'n', |
87 | | ['\r'] = 'r', |
88 | | ['\t'] = 't', |
89 | | ['\v'] = 'v' |
90 | | }, |
91 | | .esc = { |
92 | | SBUFF_CHAR_UNPRINTABLES_LOW, |
93 | | SBUFF_CHAR_UNPRINTABLES_EXTENDED |
94 | | }, |
95 | | .do_utf8 = true, |
96 | | .do_oct = true |
97 | | }; |
98 | | |
99 | | |
100 | | /* |
101 | | *###################################### |
102 | | *# FUNCTIONS FOR LIBPCRE2 # |
103 | | *###################################### |
104 | | */ |
105 | | #ifdef HAVE_REGEX_PCRE2 |
106 | | /* |
107 | | * Wrapper functions for libpcre2. Much more powerful, and guaranteed |
108 | | * to be binary safe for both patterns and subjects but require |
109 | | * libpcre2. |
110 | | */ |
111 | | |
112 | | /** Thread local storage for PCRE2 |
113 | | * |
114 | | * Not all this storage is thread local, but it simplifies cleanup if |
115 | | * we bind its lifetime to the thread, and lets us get away with not |
116 | | * having specific init/free functions. |
117 | | */ |
118 | | typedef struct { |
119 | | TALLOC_CTX *alloc_ctx; //!< Context used for any allocations. |
120 | | pcre2_general_context *gcontext; //!< General context. |
121 | | pcre2_compile_context *ccontext; //!< Compile context. |
122 | | pcre2_match_context *mcontext; //!< Match context. |
123 | | #ifdef PCRE2_CONFIG_JIT |
124 | | pcre2_jit_stack *jit_stack; //!< Jit stack for executing jit'd patterns. |
125 | | bool do_jit; //!< Whether we have runtime JIT support. |
126 | | #endif |
127 | | } fr_pcre2_tls_t; |
128 | | |
129 | | /** Thread local storage for pcre2 |
130 | | * |
131 | | */ |
132 | | static _Thread_local fr_pcre2_tls_t *fr_pcre2_tls; |
133 | | |
134 | | /** Talloc wrapper for pcre2 memory allocation |
135 | | * |
136 | | * @param[in] to_alloc How many bytes to alloc. |
137 | | * @param[in] uctx UNUSED. |
138 | | */ |
139 | | static void *_pcre2_talloc(PCRE2_SIZE to_alloc, UNUSED void *uctx) |
140 | | { |
141 | | return talloc_array(fr_pcre2_tls->alloc_ctx, uint8_t, to_alloc); |
142 | | } |
143 | | |
144 | | /** Talloc wrapper for pcre2 memory freeing |
145 | | * |
146 | | * @param[in] to_free Memory to free. |
147 | | * @param[in] uctx UNUSED. |
148 | | */ |
149 | | static void _pcre2_talloc_free(void *to_free, UNUSED void *uctx) |
150 | | { |
151 | | talloc_free(to_free); |
152 | | } |
153 | | |
154 | | /** Free thread local data |
155 | | * |
156 | | * @param[in] tls Thread local data to free. |
157 | | */ |
158 | | static int _pcre2_tls_free(fr_pcre2_tls_t *tls) |
159 | | { |
160 | | if (tls->gcontext) pcre2_general_context_free(tls->gcontext); |
161 | | if (tls->ccontext) pcre2_compile_context_free(tls->ccontext); |
162 | | if (tls->mcontext) pcre2_match_context_free(tls->mcontext); |
163 | | #ifdef PCRE2_CONFIG_JIT |
164 | | if (tls->jit_stack) pcre2_jit_stack_free(tls->jit_stack); |
165 | | #endif |
166 | | |
167 | | return 0; |
168 | | } |
169 | | |
170 | | static int _pcre2_tls_free_on_exit(void *arg) |
171 | | { |
172 | | return talloc_free(arg); |
173 | | } |
174 | | |
175 | | /** Thread local init for pcre2 |
176 | | * |
177 | | */ |
178 | | static int fr_pcre2_tls_init(void) |
179 | | { |
180 | | fr_pcre2_tls_t *tls; |
181 | | |
182 | | if (unlikely(fr_pcre2_tls != NULL)) return 0; |
183 | | |
184 | | fr_pcre2_tls = tls = talloc_zero(NULL, fr_pcre2_tls_t); |
185 | | if (!tls) return -1; |
186 | | talloc_set_destructor(tls, _pcre2_tls_free); |
187 | | |
188 | | tls->gcontext = pcre2_general_context_create(_pcre2_talloc, _pcre2_talloc_free, NULL); |
189 | | if (!tls->gcontext) { |
190 | | fr_strerror_const("Failed allocating general context"); |
191 | | return -1; |
192 | | } |
193 | | |
194 | | tls->ccontext = pcre2_compile_context_create(tls->gcontext); |
195 | | if (!tls->ccontext) { |
196 | | fr_strerror_const("Failed allocating compile context"); |
197 | | error: |
198 | | fr_pcre2_tls = NULL; |
199 | | _pcre2_tls_free(tls); |
200 | | return -1; |
201 | | } |
202 | | |
203 | | #ifdef FR_PCRE2_HAVE_MAX_PATTERN_LENGTH |
204 | | /* |
205 | | * Cap memory usage. Patterns over this limit will fail |
206 | | * with an error in pcre2_compile() or regex_compile(). |
207 | | */ |
208 | | pcre2_set_max_pattern_length(tls->ccontext, FR_PCRE2_MAX_PATTERN_LENGTH); |
209 | | #endif |
210 | | |
211 | | tls->mcontext = pcre2_match_context_create(tls->gcontext); |
212 | | if (!tls->mcontext) { |
213 | | fr_strerror_const("Failed allocating match context"); |
214 | | goto error; |
215 | | } |
216 | | |
217 | | #ifdef PCRE2_CONFIG_JIT |
218 | | pcre2_config(PCRE2_CONFIG_JIT, &tls->do_jit); |
219 | | if (tls->do_jit) { |
220 | | tls->jit_stack = pcre2_jit_stack_create(FR_PCRE_JIT_STACK_MIN, FR_PCRE_JIT_STACK_MAX, tls->gcontext); |
221 | | if (!tls->jit_stack) { |
222 | | fr_strerror_const("Failed allocating JIT stack"); |
223 | | goto error; |
224 | | } |
225 | | pcre2_jit_stack_assign(tls->mcontext, NULL, tls->jit_stack); |
226 | | } |
227 | | #endif |
228 | | |
229 | | /* |
230 | | * Free on thread exit |
231 | | */ |
232 | | fr_atexit_thread_local(fr_pcre2_tls, _pcre2_tls_free_on_exit, tls); |
233 | | fr_pcre2_tls = tls; /* Assign to thread local storage */ |
234 | | |
235 | | return 0; |
236 | | } |
237 | | |
238 | | /** Free regex_t structure |
239 | | * |
240 | | * Calls libpcre specific free functions for the expression and study. |
241 | | * |
242 | | * @param preg to free. |
243 | | */ |
244 | | static int _regex_free(regex_t *preg) |
245 | | { |
246 | | if (preg->compiled) pcre2_code_free(preg->compiled); |
247 | | |
248 | | return 0; |
249 | | } |
250 | | |
251 | | /** Wrapper around pcre2_compile |
252 | | * |
253 | | * Allows the rest of the code to do compilations using one function signature. |
254 | | * |
255 | | * @note Compiled expression must be freed with talloc_free. |
256 | | * |
257 | | * @param[out] out Where to write out a pointer to the structure containing |
258 | | * the compiled expression. |
259 | | * @param[in] pattern to compile. |
260 | | * @param[in] len of pattern. |
261 | | * @param[in] flags controlling matching. May be NULL. |
262 | | * @param[in] subcaptures Whether to compile the regular expression to store subcapture |
263 | | * data. |
264 | | * @param[in] runtime If false run the pattern through the PCRE JIT (if available) |
265 | | * to convert it to machine code. This trades startup time (longer) |
266 | | * for runtime performance (better). |
267 | | * @return |
268 | | * - >= 1 on success. |
269 | | * - <= 0 on error. Negative value is offset of parse error. |
270 | | */ |
271 | | ssize_t regex_compile(TALLOC_CTX *ctx, regex_t **out, char const *pattern, size_t len, |
272 | | fr_regex_flags_t const *flags, bool subcaptures, bool runtime) |
273 | | { |
274 | | int ret; |
275 | | PCRE2_SIZE offset; |
276 | | uint32_t cflags = 0; |
277 | | regex_t *preg; |
278 | | |
279 | | /* |
280 | | * Check inputs |
281 | | */ |
282 | | *out = NULL; |
283 | | |
284 | | /* |
285 | | * Thread local initialisation |
286 | | */ |
287 | | if (unlikely(!fr_pcre2_tls) && (fr_pcre2_tls_init() < 0)) return -1; |
288 | | |
289 | | if (len == 0) { |
290 | | fr_strerror_const("Empty expression"); |
291 | | return 0; |
292 | | } |
293 | | |
294 | | /* |
295 | | * Options |
296 | | */ |
297 | | if (flags) { |
298 | | /* flags->global implemented by substitution function */ |
299 | | if (flags->ignore_case) cflags |= PCRE2_CASELESS; |
300 | | if (flags->multiline) cflags |= PCRE2_MULTILINE; |
301 | | if (flags->dot_all) cflags |= PCRE2_DOTALL; |
302 | | if (flags->unicode) cflags |= PCRE2_UTF; |
303 | | if (flags->extended) cflags |= PCRE2_EXTENDED; |
304 | | } |
305 | | |
306 | | if (!subcaptures) cflags |= PCRE2_NO_AUTO_CAPTURE; |
307 | | |
308 | | preg = talloc_zero(ctx, regex_t); |
309 | | talloc_set_destructor(preg, _regex_free); |
310 | | |
311 | | preg->compiled = pcre2_compile((PCRE2_SPTR8)pattern, len, |
312 | | cflags, &ret, &offset, fr_pcre2_tls->ccontext); |
313 | | if (!preg->compiled) { |
314 | | PCRE2_UCHAR errbuff[128]; |
315 | | |
316 | | pcre2_get_error_message(ret, errbuff, sizeof(errbuff)); |
317 | | fr_strerror_printf("%s", (char *)errbuff); |
318 | | talloc_free(preg); |
319 | | |
320 | | return -(ssize_t)offset; |
321 | | } |
322 | | |
323 | | if (!runtime) { |
324 | | preg->precompiled = true; |
325 | | |
326 | | #ifdef PCRE2_CONFIG_JIT |
327 | | /* |
328 | | * This is expensive, so only do it for |
329 | | * expressions that are going to be |
330 | | * evaluated repeatedly. |
331 | | */ |
332 | | if (fr_pcre2_tls->do_jit) { |
333 | | ret = pcre2_jit_compile(preg->compiled, PCRE2_JIT_COMPLETE); |
334 | | if (ret < 0) { |
335 | | PCRE2_UCHAR errbuff[128]; |
336 | | |
337 | | pcre2_get_error_message(ret, errbuff, sizeof(errbuff)); |
338 | | fr_strerror_printf("Pattern JIT failed: %s", (char *)errbuff); |
339 | | talloc_free(preg); |
340 | | |
341 | | return 0; |
342 | | } |
343 | | preg->jitd = true; |
344 | | } |
345 | | #endif |
346 | | } |
347 | | |
348 | | *out = preg; |
349 | | |
350 | | return len; |
351 | | } |
352 | | |
353 | | /** Wrapper around pcre2_exec |
354 | | * |
355 | | * @param[in] preg The compiled expression. |
356 | | * @param[in] subject to match. |
357 | | * @param[in] len Length of subject. |
358 | | * @param[in] regmatch Array of match pointers. |
359 | | * @return |
360 | | * - -1 on failure. |
361 | | * - 0 on no match. |
362 | | * - 1 on match. |
363 | | */ |
364 | | int regex_exec(regex_t *preg, char const *subject, size_t len, fr_regmatch_t *regmatch) |
365 | | { |
366 | | int ret; |
367 | | uint32_t options = 0; |
368 | | |
369 | | char *our_subject = NULL; |
370 | | bool dup_subject = true; |
371 | | pcre2_match_data *match_data; |
372 | | |
373 | | /* |
374 | | * Thread local initialisation |
375 | | */ |
376 | | if (unlikely(!fr_pcre2_tls) && (fr_pcre2_tls_init() < 0)) return -1; |
377 | | |
378 | | if (regmatch) { |
379 | | #ifdef PCRE2_COPY_MATCHED_SUBJECT |
380 | | /* |
381 | | * This is apparently only supported for pcre2_match |
382 | | * NOT pcre2_jit_match. |
383 | | */ |
384 | | # ifdef PCRE2_CONFIG_JIT |
385 | | if (!preg->jitd) { |
386 | | # endif |
387 | | dup_subject = false; |
388 | | |
389 | | /* |
390 | | * If PCRE2_COPY_MATCHED_SUBJECT is available |
391 | | * and set as an options flag, pcre2_match will |
392 | | * strdup the subject string if pcre2_match is |
393 | | * successful and store a pointer to it in the |
394 | | * regmatch struct. |
395 | | * |
396 | | * The lifetime of the string memory will be |
397 | | * bound to the regmatch struct. This is more |
398 | | * efficient that doing it ourselves, as the |
399 | | * strdup only occurs if the subject matches. |
400 | | */ |
401 | | options |= PCRE2_COPY_MATCHED_SUBJECT; |
402 | | # ifdef PCRE2_CONFIG_JIT |
403 | | } |
404 | | # endif |
405 | | #endif |
406 | | if (dup_subject) { |
407 | | /* |
408 | | * We have to dup and operate on the duplicate |
409 | | * of the subject, because pcre2_jit_match and |
410 | | * pcre2_match store a pointer to the subject |
411 | | * in the regmatch structure. |
412 | | */ |
413 | | subject = our_subject = talloc_bstrndup(regmatch, subject, len); |
414 | | if (!subject) { |
415 | | fr_strerror_const("Out of memory"); |
416 | | return -1; |
417 | | } |
418 | | #ifndef NDEBUG |
419 | | regmatch->subject = subject; /* Stored only for tracking memory issues */ |
420 | | #endif |
421 | | } |
422 | | } |
423 | | |
424 | | /* |
425 | | * If we weren't given match data we |
426 | | * need to alloc it else pcre2_match |
427 | | * fails when passed NULL match data. |
428 | | */ |
429 | | if (!regmatch) { |
430 | | match_data = pcre2_match_data_create_from_pattern(preg->compiled, fr_pcre2_tls->gcontext); |
431 | | if (!match_data) { |
432 | | fr_strerror_const("Failed allocating temporary match data"); |
433 | | return -1; |
434 | | } |
435 | | } else { |
436 | | match_data = regmatch->match_data; |
437 | | } |
438 | | |
439 | | #ifdef PCRE2_CONFIG_JIT |
440 | | if (preg->jitd) { |
441 | | ret = pcre2_jit_match(preg->compiled, (PCRE2_SPTR8)subject, len, 0, options, |
442 | | match_data, fr_pcre2_tls->mcontext); |
443 | | } else |
444 | | #endif |
445 | | { |
446 | | ret = pcre2_match(preg->compiled, (PCRE2_SPTR8)subject, len, 0, options, |
447 | | match_data, fr_pcre2_tls->mcontext); |
448 | | } |
449 | | if (!regmatch) pcre2_match_data_free(match_data); |
450 | | if (ret < 0) { |
451 | | PCRE2_UCHAR errbuff[128]; |
452 | | |
453 | | if (dup_subject) talloc_free(our_subject); |
454 | | |
455 | | if (ret == PCRE2_ERROR_NOMATCH) { |
456 | | if (regmatch) regmatch->used = 0; |
457 | | return 0; |
458 | | } |
459 | | |
460 | | pcre2_get_error_message(ret, errbuff, sizeof(errbuff)); |
461 | | fr_strerror_printf("regex evaluation failed with code (%i): %s", ret, errbuff); |
462 | | |
463 | | return -1; |
464 | | } |
465 | | |
466 | | if (regmatch) regmatch->used = ret; |
467 | | |
468 | | return 1; |
469 | | } |
470 | | |
471 | | /** Wrapper around pcre2_substitute |
472 | | * |
473 | | * @param[in] ctx to allocate output string in. |
474 | | * @param[out] out Output string with replacements performed. |
475 | | * @param[in] max_out Maximum length of output buffer. If this is 0 then |
476 | | * the output length is unlimited. |
477 | | * @param[in] preg The compiled expression. |
478 | | * @param[in] flags that affect matching. |
479 | | * @param[in] subject to perform replacements on. |
480 | | * @param[in] subject_len the length of the subject. |
481 | | * @param[in] replacement replacement string containing substitution |
482 | | * markers. |
483 | | * @param[in] replacement_len Length of the replacement string. |
484 | | * @param[in] regmatch Array of match pointers. |
485 | | * @return |
486 | | * - >= 0 the length of the output string. |
487 | | * - < 0 on error. |
488 | | */ |
489 | | int regex_substitute(TALLOC_CTX *ctx, char **out, size_t max_out, regex_t *preg, fr_regex_flags_t const *flags, |
490 | | char const *subject, size_t subject_len, |
491 | | char const *replacement, size_t replacement_len, |
492 | | fr_regmatch_t *regmatch) |
493 | | { |
494 | | int ret; |
495 | | uint32_t options = 0; |
496 | | size_t buff_len, actual_len; |
497 | | char *buff; |
498 | | |
499 | | #ifndef PCRE2_COPY_MATCHED_SUBJECT |
500 | | char *our_subject = NULL; |
501 | | #endif |
502 | | |
503 | | /* |
504 | | * Thread local initialisation |
505 | | */ |
506 | | if (unlikely(!fr_pcre2_tls) && (fr_pcre2_tls_init() < 0)) return -1; |
507 | | |
508 | | /* |
509 | | * Internally pcre2_substitute just calls pcre2_match to |
510 | | * generate the match data, so the same hack as the |
511 | | * regex_exec function above is required. |
512 | | */ |
513 | | if (regmatch) { |
514 | | #ifndef PCRE2_COPY_MATCHED_SUBJECT |
515 | | /* |
516 | | * We have to dup and operate on the duplicate |
517 | | * of the subject, because pcre2_jit_match and |
518 | | * pcre2_match store a pointer to the subject |
519 | | * in the regmatch structure. |
520 | | */ |
521 | | subject = our_subject = talloc_bstrndup(regmatch, subject, subject_len); |
522 | | if (!subject) { |
523 | | fr_strerror_const("Out of memory"); |
524 | | return -1; |
525 | | } |
526 | | #else |
527 | | /* |
528 | | * If PCRE2_COPY_MATCHED_SUBJECT is available |
529 | | * and set as an options flag, pcre2_match will |
530 | | * strdup the subject string if pcre2_match is |
531 | | * successful and store a pointer to it in the |
532 | | * regmatch struct. |
533 | | * |
534 | | * The lifetime of the string memory will be |
535 | | * bound to the regmatch struct. This is more |
536 | | * efficient that doing it ourselves, as the |
537 | | * strdup only occurs if the subject matches. |
538 | | */ |
539 | | options |= PCRE2_COPY_MATCHED_SUBJECT; |
540 | | #endif |
541 | | } |
542 | | |
543 | | /* |
544 | | * Guess (badly) what the length of the output buffer should be |
545 | | */ |
546 | | actual_len = buff_len = subject_len + 1; /* +1 for the \0 */ |
547 | | buff = talloc_array(ctx, char, buff_len); |
548 | | if (!buff) { |
549 | | #ifndef PCRE2_COPY_MATCHED_SUBJECT |
550 | | talloc_free(our_subject); |
551 | | #endif |
552 | | fr_strerror_const("Out of memory"); |
553 | | return -1; |
554 | | } |
555 | | |
556 | | options |= PCRE2_SUBSTITUTE_OVERFLOW_LENGTH; |
557 | | if (flags->global) options |= PCRE2_SUBSTITUTE_GLOBAL; |
558 | | |
559 | | again: |
560 | | /* |
561 | | * actual_len input value should be the size of the |
562 | | * buffer including space for '\0'. |
563 | | * If input buffer is too small, then actual_len will be set |
564 | | * to the buffer space needed including space for '\0'. |
565 | | * If input buffer is the correct size, then actual_len |
566 | | * will be set to the size of the string written to buff |
567 | | * without the terminating '\0'. |
568 | | */ |
569 | | ret = pcre2_substitute(preg->compiled, |
570 | | (PCRE2_SPTR8)subject, (PCRE2_SIZE)subject_len, 0, |
571 | | options, NULL, fr_pcre2_tls->mcontext, |
572 | | (PCRE2_UCHAR const *)replacement, replacement_len, (PCRE2_UCHAR *)buff, &actual_len); |
573 | | |
574 | | if (ret < 0) { |
575 | | PCRE2_UCHAR errbuff[128]; |
576 | | |
577 | | #ifndef PCRE2_COPY_MATCHED_SUBJECT |
578 | | talloc_free(our_subject); |
579 | | #endif |
580 | | talloc_free(buff); |
581 | | |
582 | | if (ret == PCRE2_ERROR_NOMEMORY) { |
583 | | if ((max_out > 0) && (actual_len > max_out)) { |
584 | | fr_strerror_printf("String length with substitutions (%zu) " |
585 | | "exceeds max string length (%zu)", actual_len - 1, max_out - 1); |
586 | | return -1; |
587 | | } |
588 | | |
589 | | /* |
590 | | * Check that actual_len != buff_len as that'd be |
591 | | * an actual error. |
592 | | */ |
593 | | if (actual_len == buff_len) { |
594 | | fr_strerror_const("libpcre2 out of memory"); |
595 | | return -1; |
596 | | } |
597 | | buff_len = actual_len; /* The length we get passed back includes the \0 */ |
598 | | buff = talloc_array(ctx, char, buff_len); |
599 | | if (!buff) { |
600 | | fr_strerror_const("Out of memory"); |
601 | | return -1; |
602 | | } |
603 | | goto again; |
604 | | } |
605 | | |
606 | | if (ret == PCRE2_ERROR_NOMATCH) { |
607 | | if (regmatch) regmatch->used = 0; |
608 | | return 0; |
609 | | } |
610 | | |
611 | | pcre2_get_error_message(ret, errbuff, sizeof(errbuff)); |
612 | | fr_strerror_printf("regex evaluation failed with code (%i): %s", ret, errbuff); |
613 | | return -1; |
614 | | } |
615 | | |
616 | | /* |
617 | | * Trim the replacement buffer to the correct length |
618 | | * |
619 | | * buff_len includes \0. |
620 | | * ...and as pcre2_substitute just succeeded actual_len does not include \0. |
621 | | */ |
622 | | if (actual_len < (buff_len - 1)) { |
623 | | buff = talloc_bstr_realloc(ctx, buff, actual_len); |
624 | | if (!buff) { |
625 | | fr_strerror_const("reallocing pcre2_substitute result buffer failed"); |
626 | | return -1; |
627 | | } |
628 | | } |
629 | | |
630 | | if (regmatch) regmatch->used = ret; |
631 | | *out = buff; |
632 | | |
633 | | return 1; |
634 | | } |
635 | | |
636 | | |
637 | | /** Returns the number of subcapture groups |
638 | | * |
639 | | * @return |
640 | | * - >0 The number of subcaptures contained within the pattern |
641 | | * - 0 if the number of subcaptures can't be determined. |
642 | | */ |
643 | | uint32_t regex_subcapture_count(regex_t const *preg) |
644 | | { |
645 | | uint32_t count; |
646 | | |
647 | | if (pcre2_pattern_info(preg->compiled, PCRE2_INFO_CAPTURECOUNT, &count) != 0) { |
648 | | fr_strerror_const("Error determining subcapture group count"); |
649 | | return 0; |
650 | | } |
651 | | |
652 | | return count + 1; |
653 | | } |
654 | | |
655 | | /** Free libpcre2's matchdata |
656 | | * |
657 | | * @note Don't call directly, will be called if talloc_free is called on a #regmatch_t. |
658 | | */ |
659 | | static int _pcre2_match_data_free(fr_regmatch_t *regmatch) |
660 | | { |
661 | | pcre2_match_data_free(regmatch->match_data); |
662 | | return 0; |
663 | | } |
664 | | |
665 | | /** Allocate vectors to fill with match data |
666 | | * |
667 | | * @param[in] ctx to allocate match vectors in. |
668 | | * @param[in] count The number of vectors to allocate. |
669 | | * @return |
670 | | * - NULL on error. |
671 | | * - Array of match vectors. |
672 | | */ |
673 | | fr_regmatch_t *regex_match_data_alloc(TALLOC_CTX *ctx, uint32_t count) |
674 | | { |
675 | | fr_regmatch_t *regmatch; |
676 | | |
677 | | /* |
678 | | * Thread local initialisation |
679 | | */ |
680 | | if (unlikely(!fr_pcre2_tls) && (fr_pcre2_tls_init() < 0)) return NULL; |
681 | | |
682 | | regmatch = talloc(ctx, fr_regmatch_t); |
683 | | if (!regmatch) { |
684 | | oom: |
685 | | fr_strerror_const("Out of memory"); |
686 | | return NULL; |
687 | | } |
688 | | |
689 | | regmatch->match_data = pcre2_match_data_create(count, fr_pcre2_tls->gcontext); |
690 | | if (!regmatch->match_data) { |
691 | | talloc_free(regmatch); |
692 | | goto oom; |
693 | | } |
694 | | talloc_set_type(regmatch->match_data, pcre2_match_data); |
695 | | |
696 | | talloc_set_destructor(regmatch, _pcre2_match_data_free); |
697 | | |
698 | | return regmatch; |
699 | | } |
700 | | |
701 | | /* |
702 | | *###################################### |
703 | | *# FUNCTIONS FOR POSIX-REGEX # |
704 | | *###################################### |
705 | | */ |
706 | | # else |
707 | | /* |
708 | | * Wrapper functions for POSIX like, and extended regular |
709 | | * expressions. These use the system regex library. |
710 | | */ |
711 | | |
712 | | /** Free heap allocated regex_t structure |
713 | | * |
714 | | * Heap allocation of regex_t is needed so regex_compile has the same signature with |
715 | | * POSIX or libpcre. |
716 | | * |
717 | | * @param preg to free. |
718 | | */ |
719 | | static int _regex_free(regex_t *preg) |
720 | 3.89k | { |
721 | 3.89k | regfree(preg); |
722 | | |
723 | 3.89k | return 0; |
724 | 3.89k | } |
725 | | |
726 | | /** Binary safe wrapper around regcomp |
727 | | * |
728 | | * If we have the BSD extensions we don't need to do any special work |
729 | | * if we don't have the BSD extensions we need to check to see if the |
730 | | * regular expression contains any \0 bytes. |
731 | | * |
732 | | * If it does we fail and print the appropriate error message. |
733 | | * |
734 | | * @note Compiled expression must be freed with talloc_free. |
735 | | * |
736 | | * @param[in] ctx To allocate memory in. |
737 | | * @param[out] out Where to write out a pointer |
738 | | * to the structure containing the compiled expression. |
739 | | * @param[in] pattern to compile. |
740 | | * @param[in] len of pattern. |
741 | | * @param[in] flags controlling matching. May be NULL. |
742 | | * @param[in] subcaptures Whether to compile the regular expression |
743 | | * to store subcapture data. |
744 | | * @param[in] runtime Whether the compilation is being done at runtime. |
745 | | * @return |
746 | | * - >= 1 on success. |
747 | | * - <= 0 on error. Negative value is offset of parse error. |
748 | | * With POSIX regex we only give the correct offset for embedded \0 errors. |
749 | | */ |
750 | | ssize_t regex_compile(TALLOC_CTX *ctx, regex_t **out, char const *pattern, size_t len, |
751 | | fr_regex_flags_t const *flags, bool subcaptures, UNUSED bool runtime) |
752 | 3.99k | { |
753 | 3.99k | int ret; |
754 | 3.99k | int cflags = REG_EXTENDED; |
755 | 3.99k | regex_t *preg; |
756 | | |
757 | 3.99k | if (len == 0) { |
758 | 0 | fr_strerror_const("Empty expression"); |
759 | 0 | return 0; |
760 | 0 | } |
761 | | |
762 | | /* |
763 | | * Options |
764 | | */ |
765 | 3.99k | if (flags) { |
766 | 3.99k | if (flags->global) { |
767 | 12 | fr_strerror_const("g - Global matching/substitution not supported with posix-regex"); |
768 | 12 | return 0; |
769 | 12 | } |
770 | 3.98k | if (flags->dot_all) { |
771 | 9 | fr_strerror_const("s - Single line matching is not supported with posix-regex"); |
772 | 9 | return 0; |
773 | 9 | } |
774 | 3.97k | if (flags->unicode) { |
775 | 7 | fr_strerror_const("u - Unicode matching not supported with posix-regex"); |
776 | 7 | return 0; |
777 | 7 | } |
778 | 3.96k | if (flags->extended) { |
779 | 3 | fr_strerror_const("x - Whitespace and comments not supported with posix-regex"); |
780 | 3 | return 0; |
781 | 3 | } |
782 | | |
783 | 3.96k | if (flags->ignore_case) cflags |= REG_ICASE; |
784 | 3.96k | if (flags->multiline) cflags |= REG_NEWLINE; |
785 | 3.96k | } |
786 | | |
787 | | |
788 | 3.96k | if (!subcaptures) cflags |= REG_NOSUB; |
789 | | |
790 | 3.96k | #ifndef HAVE_REGNCOMP |
791 | 3.96k | { |
792 | 3.96k | char const *p; |
793 | | |
794 | 3.96k | p = pattern; |
795 | 3.96k | p += strlen(pattern); |
796 | | |
797 | 3.96k | if ((size_t)(p - pattern) != len) { |
798 | 59 | fr_strerror_printf("Found null in pattern at offset %zu. Pattern unsafe for compilation", |
799 | 59 | (size_t)(p - pattern)); |
800 | 59 | return -(p - pattern); |
801 | 59 | } |
802 | | |
803 | 3.90k | preg = talloc_zero(ctx, regex_t); |
804 | 3.90k | if (!preg) return 0; |
805 | | |
806 | 3.90k | ret = regcomp(preg, pattern, cflags); |
807 | 3.90k | } |
808 | | #else |
809 | | preg = talloc_zero(ctx, regex_t); |
810 | | if (!preg) return 0; |
811 | | ret = regncomp(preg, pattern, len, cflags); |
812 | | #endif |
813 | 3.90k | if (ret != 0) { |
814 | 8 | char errbuf[128]; |
815 | | |
816 | 8 | regerror(ret, preg, errbuf, sizeof(errbuf)); |
817 | 8 | fr_strerror_printf("%s", errbuf); |
818 | | |
819 | 8 | talloc_free(preg); |
820 | | |
821 | 8 | return 0; /* POSIX expressions don't give us the failure offset */ |
822 | 8 | } |
823 | | |
824 | 3.90k | talloc_set_destructor(preg, _regex_free); |
825 | 3.89k | *out = preg; |
826 | | |
827 | 3.89k | return len; |
828 | 3.90k | } |
829 | | |
830 | | /** Binary safe wrapper around regexec |
831 | | * |
832 | | * If we have the BSD extensions we don't need to do any special work |
833 | | * If we don't have the BSD extensions we need to check to see if the |
834 | | * value to be compared contains any \0 bytes. |
835 | | * |
836 | | * If it does, we fail and print the appropriate error message. |
837 | | * |
838 | | * @param[in] preg The compiled expression. |
839 | | * @param[in] subject to match. |
840 | | * @param[in] regmatch Match result structure. |
841 | | * @return |
842 | | * - -1 on failure. |
843 | | * - 0 on no match. |
844 | | * - 1 on match. |
845 | | */ |
846 | | int regex_exec(regex_t *preg, char const *subject, size_t len, fr_regmatch_t *regmatch) |
847 | 0 | { |
848 | 0 | int ret; |
849 | 0 | size_t matches; |
850 | | |
851 | | /* |
852 | | * Disable capturing |
853 | | */ |
854 | 0 | if (!regmatch) { |
855 | 0 | matches = 0; |
856 | 0 | } else { |
857 | 0 | matches = regmatch->allocd; |
858 | | |
859 | | /* |
860 | | * Reset the match result structure |
861 | | */ |
862 | 0 | memset(regmatch->match_data, 0, sizeof(regmatch->match_data[0]) * matches); |
863 | 0 | regmatch->used = 0; |
864 | 0 | } |
865 | |
|
866 | 0 | #ifndef HAVE_REGNEXEC |
867 | 0 | { |
868 | 0 | char const *p; |
869 | |
|
870 | 0 | p = subject; |
871 | 0 | p += strlen(subject); |
872 | |
|
873 | 0 | if ((size_t)(p - subject) != len) { |
874 | 0 | fr_strerror_printf("Found null in subject at offset %zu. String unsafe for evaluation", |
875 | 0 | (size_t)(p - subject)); |
876 | 0 | if (regmatch) regmatch->used = 0; |
877 | 0 | return -1; |
878 | 0 | } |
879 | 0 | ret = regexec(preg, subject, matches, regmatch ? regmatch->match_data : NULL, 0); |
880 | 0 | } |
881 | | #else |
882 | | ret = regnexec(preg, subject, len, matches, regmatch ? regmatch->match_data : NULL, 0); |
883 | | #endif |
884 | 0 | if (ret != 0) { |
885 | 0 | if (ret != REG_NOMATCH) { |
886 | 0 | char errbuf[128]; |
887 | |
|
888 | 0 | regerror(ret, preg, errbuf, sizeof(errbuf)); |
889 | |
|
890 | 0 | fr_strerror_printf("regex evaluation failed: %s", errbuf); |
891 | 0 | return -1; |
892 | 0 | } |
893 | 0 | return 0; |
894 | 0 | } |
895 | | |
896 | | /* |
897 | | * Update regmatch->count to be the maximum number of |
898 | | * groups that *could* have been populated as we don't |
899 | | * have the number of matches. |
900 | | */ |
901 | 0 | if (regmatch) { |
902 | 0 | regmatch->used = preg->re_nsub + 1; |
903 | |
|
904 | 0 | if (regmatch->subject) talloc_const_free(regmatch->subject); |
905 | 0 | regmatch->subject = talloc_bstrndup(regmatch, subject, len); |
906 | 0 | if (!regmatch->subject) { |
907 | 0 | fr_strerror_const("Out of memory"); |
908 | 0 | return -1; |
909 | 0 | } |
910 | 0 | } |
911 | 0 | return 1; |
912 | 0 | } |
913 | | |
914 | | /** Returns the number of subcapture groups |
915 | | * |
916 | | * @return |
917 | | * - 0 we can't determine this for POSIX regular expressions. |
918 | | */ |
919 | | uint32_t regex_subcapture_count(UNUSED regex_t const *preg) |
920 | 0 | { |
921 | 0 | return 0; |
922 | 0 | } |
923 | | # endif |
924 | | |
925 | | # if defined(HAVE_REGEX_POSIX) |
926 | | /** Allocate vectors to fill with match data |
927 | | * |
928 | | * @param[in] ctx to allocate match vectors in. |
929 | | * @param[in] count The number of vectors to allocate. |
930 | | * @return |
931 | | * - NULL on error. |
932 | | * - Array of match vectors. |
933 | | */ |
934 | | fr_regmatch_t *regex_match_data_alloc(TALLOC_CTX *ctx, uint32_t count) |
935 | 0 | { |
936 | 0 | fr_regmatch_t *regmatch; |
937 | | |
938 | | /* |
939 | | * Pre-allocate space for the match structure |
940 | | * and for a 128b subject string. |
941 | | */ |
942 | 0 | regmatch = talloc_zero_pooled_object(ctx, fr_regmatch_t, 2, (sizeof(regmatch_t) * count) + 128); |
943 | 0 | if (unlikely(!regmatch)) { |
944 | 0 | error: |
945 | 0 | fr_strerror_const("Out of memory"); |
946 | 0 | talloc_free(regmatch); |
947 | 0 | return NULL; |
948 | 0 | } |
949 | 0 | regmatch->match_data = talloc_array(regmatch, regmatch_t, count); |
950 | 0 | if (unlikely(!regmatch->match_data)) goto error; |
951 | | |
952 | 0 | regmatch->allocd = count; |
953 | 0 | regmatch->used = 0; |
954 | 0 | regmatch->subject = NULL; |
955 | |
|
956 | 0 | return regmatch; |
957 | 0 | } |
958 | | # endif |
959 | | |
960 | | /* |
961 | | *######################################## |
962 | | *# UNIVERSAL FUNCTIONS # |
963 | | *######################################## |
964 | | */ |
965 | | |
966 | | /** Parse a string containing one or more regex flags |
967 | | * |
968 | | * @param[out] err May be NULL. If not NULL will be set to: |
969 | | * - 0 on success. |
970 | | * - -1 on unknown flag. |
971 | | * - -2 on duplicate. |
972 | | * @param[out] out Flag structure to populate. Must be initialised to zero |
973 | | * if this is the first call to regex_flags_parse. |
974 | | * @param[in] in Flag string to parse. |
975 | | * @param[in] terminals Terminal characters. If parsing ends before the buffer |
976 | | * is exhausted, and is pointing to one of these chars |
977 | | * it's not considered an error. |
978 | | * @param[in] err_on_dup Error if the flag is already set. |
979 | | * @return |
980 | | * - > 0 on success. The number of flag bytes parsed. |
981 | | * - <= 0 on failure. Negative offset of first unrecognised flag. |
982 | | */ |
983 | | fr_slen_t regex_flags_parse(int *err, fr_regex_flags_t *out, fr_sbuff_t *in, |
984 | | fr_sbuff_term_t const *terminals, bool err_on_dup) |
985 | 6.72k | { |
986 | 6.72k | fr_sbuff_t our_in = FR_SBUFF(in); |
987 | | |
988 | 6.72k | if (err) *err = 0; |
989 | | |
990 | 10.5k | while (fr_sbuff_extend(&our_in)) { |
991 | 10.5k | switch (*our_in.p) { |
992 | 0 | #define DO_REGEX_FLAG(_f, _c) \ |
993 | 4.10k | case _c: \ |
994 | 4.10k | if (err_on_dup && out->_f) { \ |
995 | 13 | fr_strerror_printf("Duplicate regex flag '%c'", *our_in.p); \ |
996 | 13 | if (err) *err = -2; \ |
997 | 13 | FR_SBUFF_ERROR_RETURN(&our_in); \ |
998 | 13 | } \ |
999 | 4.10k | out->_f = 1; \ |
1000 | 4.09k | break |
1001 | | |
1002 | 628 | DO_REGEX_FLAG(global, 'g'); |
1003 | 810 | DO_REGEX_FLAG(ignore_case, 'i'); |
1004 | 689 | DO_REGEX_FLAG(multiline, 'm'); |
1005 | 630 | DO_REGEX_FLAG(dot_all, 's'); |
1006 | 797 | DO_REGEX_FLAG(unicode, 'u'); |
1007 | 551 | DO_REGEX_FLAG(extended, 'x'); |
1008 | 0 | #undef DO_REGEX_FLAG |
1009 | | |
1010 | 6.48k | default: |
1011 | 6.48k | if (fr_sbuff_is_terminal(&our_in, terminals)) FR_SBUFF_SET_RETURN(in, &our_in); |
1012 | | |
1013 | 4 | fr_strerror_printf("Unsupported regex flag '%c'", *our_in.p); |
1014 | 4 | if (err) *err = -1; |
1015 | 4 | FR_SBUFF_ERROR_RETURN(&our_in); |
1016 | 10.5k | } |
1017 | 4.09k | fr_sbuff_advance(&our_in, 1); |
1018 | 4.09k | } |
1019 | 6.72k | FR_SBUFF_SET_RETURN(in, &our_in); |
1020 | 6.72k | } |
1021 | | |
1022 | | /** Print the flags |
1023 | | * |
1024 | | * @param[out] sbuff where to write flags. |
1025 | | * @param[in] flags to print. |
1026 | | * @return |
1027 | | * - The number of bytes written to the out buffer. |
1028 | | * - A number >= outlen if truncation has occurred. |
1029 | | */ |
1030 | | ssize_t regex_flags_print(fr_sbuff_t *sbuff, fr_regex_flags_t const *flags) |
1031 | 2.26k | { |
1032 | 2.26k | fr_sbuff_t our_sbuff = FR_SBUFF(sbuff); |
1033 | | |
1034 | 2.26k | #define DO_REGEX_FLAG(_f, _c) \ |
1035 | 13.5k | if (flags->_f) FR_SBUFF_IN_CHAR_RETURN(&our_sbuff, _c) |
1036 | | |
1037 | 2.26k | DO_REGEX_FLAG(global, 'g'); |
1038 | 2.25k | DO_REGEX_FLAG(ignore_case, 'i'); |
1039 | 2.25k | DO_REGEX_FLAG(multiline, 'm'); |
1040 | 2.25k | DO_REGEX_FLAG(dot_all, 's'); |
1041 | 2.25k | DO_REGEX_FLAG(unicode, 'u'); |
1042 | 2.24k | DO_REGEX_FLAG(extended, 'x'); |
1043 | 2.24k | #undef DO_REGEX_FLAG |
1044 | | |
1045 | 2.24k | FR_SBUFF_SET_RETURN(sbuff, &our_sbuff); |
1046 | 2.24k | } |
1047 | | #endif |
1048 | | |
1049 | | /** Compare two boxes using an operator |
1050 | | * |
1051 | | * @todo - allow /foo/i on the RHS |
1052 | | * |
1053 | | * However, this involves allocating intermediate sbuffs for the |
1054 | | * unescaped RHS, and all kinds of extra work. It's not overly hard, |
1055 | | * but it's something we wish to avoid for now. |
1056 | | * |
1057 | | * @param[in] op to use in comparison. MUST be T_OP_REG_EQ or T_OP_REG_NE |
1058 | | * @param[in] a Value to compare, MUST be FR_TYPE_STRING |
1059 | | * @param[in] b uncompiled regex as FR_TYPE_STRING |
1060 | | * @return |
1061 | | * - 1 if true |
1062 | | * - 0 if false |
1063 | | * - -1 on failure. |
1064 | | */ |
1065 | | int fr_regex_cmp_op(fr_token_t op, fr_value_box_t const *a, fr_value_box_t const *b) |
1066 | 0 | { |
1067 | 0 | int rcode; |
1068 | 0 | TALLOC_CTX *ctx = NULL; |
1069 | 0 | size_t lhs_len; |
1070 | 0 | char const *lhs; |
1071 | 0 | regex_t *regex = NULL; |
1072 | |
|
1073 | 0 | if (!((op == T_OP_REG_EQ) || (op == T_OP_REG_NE))) { |
1074 | 0 | fr_strerror_const("Invalid operator for regex comparison"); |
1075 | 0 | return -1; |
1076 | 0 | } |
1077 | | |
1078 | 0 | if (b->type != FR_TYPE_STRING) { |
1079 | 0 | fr_strerror_const("RHS must be regular expression"); |
1080 | 0 | return -1; |
1081 | 0 | } |
1082 | | |
1083 | 0 | ctx = talloc_init_const("regex_cmp_op"); |
1084 | 0 | if (!ctx) return -1; |
1085 | | |
1086 | 0 | if ((a->type != FR_TYPE_STRING) && (a->type != FR_TYPE_OCTETS)) { |
1087 | 0 | fr_slen_t slen; |
1088 | 0 | char *p; |
1089 | |
|
1090 | 0 | slen = fr_value_box_aprint(ctx, &p, a, NULL); /* no escaping */ |
1091 | 0 | if (slen < 0) { |
1092 | 0 | talloc_free(ctx); |
1093 | 0 | return -1; |
1094 | 0 | } |
1095 | | |
1096 | 0 | lhs = p; |
1097 | 0 | lhs_len = slen; |
1098 | |
|
1099 | 0 | } else { |
1100 | 0 | lhs = a->vb_strvalue; |
1101 | 0 | lhs_len = a->vb_length; |
1102 | 0 | } |
1103 | | |
1104 | 0 | if (regex_compile(ctx, ®ex, b->vb_strvalue, b->vb_length, NULL, false, true) < 0) { |
1105 | 0 | talloc_free(ctx); |
1106 | 0 | return -1; |
1107 | 0 | } |
1108 | | |
1109 | | #ifdef STATIC_ANALYZER |
1110 | | if (!regex) { |
1111 | | talloc_free(ctx); |
1112 | | return -1; |
1113 | | } |
1114 | | #endif |
1115 | | |
1116 | 0 | rcode = regex_exec(regex, lhs, lhs_len, NULL); |
1117 | 0 | talloc_free(ctx); |
1118 | 0 | if (rcode < 0) return rcode; |
1119 | | |
1120 | | /* |
1121 | | * Invert the sense of the rcode for !~ |
1122 | | */ |
1123 | 0 | if (op == T_OP_REG_NE) rcode = (rcode == 0); |
1124 | |
|
1125 | 0 | return rcode; |
1126 | 0 | } |