Coverage Report

Created: 2026-11-12 06:42

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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, &regex, 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
}