The FreeRADIUS server $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
Loading...
Searching...
No Matches
xlat_builtin.c
Go to the documentation of this file.
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/**
18 * $Id: 9755e088c02f2b93e2725ba01911d796054ee161 $
19 *
20 * @file xlat_builtin.c
21 * @brief String expansion ("translation"). Baked in expansions.
22 *
23 * @copyright 2000,2006 The FreeRADIUS server project
24 * @copyright 2000 Alan DeKok (aland@freeradius.org)
25 */
26
27
28RCSID("$Id: 9755e088c02f2b93e2725ba01911d796054ee161 $")
29
30/**
31 * @defgroup xlat_functions xlat expansion functions
32 */
33
34#include <freeradius-devel/server/base.h>
35#include <freeradius-devel/server/tmpl_dcursor.h>
36#include <freeradius-devel/unlang/interpret.h>
37#include <freeradius-devel/unlang/xlat_priv.h>
38#include <freeradius-devel/unlang/xlat_func.h>
39#include <freeradius-devel/unlang/xlat.h>
40#include <freeradius-devel/unlang/xlat_ctx.h>
41
42#include <freeradius-devel/io/test_point.h>
43
44#include <freeradius-devel/util/base64.h>
45#include <freeradius-devel/util/base16.h>
46#include <freeradius-devel/util/dlist.h>
47#include <freeradius-devel/util/md5.h>
48#include <freeradius-devel/util/misc.h>
49#include <freeradius-devel/util/print.h>
50#include <freeradius-devel/util/rand.h>
51#include <freeradius-devel/util/regex.h>
52#include <freeradius-devel/util/sbuff.h>
53#include <freeradius-devel/util/sha1.h>
54#include <freeradius-devel/util/value.h>
55
56#ifdef HAVE_OPENSSL_EVP_H
57# include <freeradius-devel/tls/openssl_user_macros.h>
58# include <openssl/evp.h>
59#endif
60
61#include <sys/stat.h>
62#include <fcntl.h>
63
64static char const hextab[] = "0123456789abcdef";
65static TALLOC_CTX *xlat_ctx;
66
67/*
68 * Regular xlat functions
69 */
71 { .single = true, .type = FR_TYPE_INT8 },
73};
74
75/** Dynamically change the debugging level for the current request
76 *
77 * Example:
78@verbatim
79%debug(3)
80@endverbatim
81 *
82 * @ingroup xlat_functions
83 */
85 UNUSED xlat_ctx_t const *xctx,
86 request_t *request, fr_value_box_list_t *args)
87{
88 int level = 0;
89 fr_value_box_t *vb, *lvl_vb;
90
91 XLAT_ARGS(args, &lvl_vb);
92
93 /*
94 * Expand to previous (or current) level
95 */
96 MEM(vb = fr_value_box_alloc(ctx, FR_TYPE_INT8, NULL));
97 vb->vb_int8 = request->log.lvl;
99
100 /*
101 * Assume we just want to get the current value and NOT set it to 0
102 */
103 if (!lvl_vb) goto done;
104
105 level = lvl_vb->vb_int8;
106 if (level == 0) {
107 request->log.lvl = RAD_REQUEST_LVL_NONE;
108 } else {
109 if (level > L_DBG_LVL_MAX) level = L_DBG_LVL_MAX;
110 request->log.lvl = level;
111 }
112
113done:
114 return XLAT_ACTION_DONE;
115}
116
117
119 { .required = true, .single = true, .type = FR_TYPE_STRING },
121};
122
124{
125 fr_dict_vendor_t const *vendor;
127 size_t i;
128
129 switch (vp->vp_type) {
131 if (vpt) {
132 RIDEBUG2("&%s.%s = {",
133 tmpl_list_name(tmpl_list(vpt), "<INVALID>"),
134 vp->da->name);
135 } else {
136 RIDEBUG2("%s = {", vp->da->name);
137 }
138 RINDENT();
139 xlat_debug_attr_list(request, &vp->vp_group);
140 REXDENT();
141 RIDEBUG2("}");
142 break;
143
144 default:
145 if (vpt) {
146 RIDEBUG2("&%s.%s = %pV",
147 tmpl_list_name(tmpl_list(vpt), "<INVALID>"),
148 vp->da->name,
149 &vp->data);
150 } else {
151 RIDEBUG2("%s = %pV", vp->da->name, &vp->data);
152 }
153 }
154
155 if (!RDEBUG_ENABLED3) return;
156
157 RINDENT();
158 RIDEBUG3("da : %p", vp->da);
159 RIDEBUG3("is_raw : %pV", fr_box_bool(vp->vp_raw));
160 RIDEBUG3("is_unknown : %pV", fr_box_bool(vp->da->flags.is_unknown));
161
162 if (RDEBUG_ENABLED3) {
163 RIDEBUG3("parent : %s (%p)", vp->da->parent->name, vp->da->parent);
164 } else {
165 RIDEBUG2("parent : %s", vp->da->parent->name);
166 }
167 RIDEBUG3("attr : %u", vp->da->attr);
168 vendor = fr_dict_vendor_by_da(vp->da);
169 if (vendor) RIDEBUG2("vendor : %u (%s)", vendor->pen, vendor->name);
170 RIDEBUG3("type : %s", fr_type_to_str(vp->vp_type));
171
172 switch (vp->vp_type) {
173 case FR_TYPE_LEAF:
174 if (fr_box_is_variable_size(&vp->data)) {
175 RIDEBUG3("length : %zu", vp->vp_length);
176 }
177 RIDEBUG3("tainted : %pV", fr_box_bool(vp->data.tainted));
178 break;
179 default:
180 break;
181 }
182
183 if (!RDEBUG_ENABLED4) {
184 REXDENT();
185 return;
186 }
187
188 for (i = 0; i < fr_type_table_len; i++) {
189 int pad;
190
191 fr_value_box_t *dst = NULL;
192
193 type = &fr_type_table[i];
194
195 if ((fr_type_t) type->value == vp->vp_type) goto next_type;
196
197 /*
198 * Don't cast TO structural, or FROM structural types.
199 */
200 if (!fr_type_is_leaf(type->value) || !fr_type_is_leaf(vp->vp_type)) goto next_type;
201
203 /* We expect some to fail */
204 if (fr_value_box_cast(dst, dst, type->value, NULL, &vp->data) < 0) {
205 goto next_type;
206 }
207
208 if ((pad = (11 - type->name.len)) < 0) pad = 0;
209
210 RINDENT();
211 RDEBUG4("as %s%*s: %pV", type->name.str, pad, " ", dst);
212 REXDENT();
213
214 next_type:
215 talloc_free(dst);
216 }
217
218 REXDENT();
219}
220
222{
223 fr_pair_t *vp;
224
225 for (vp = fr_pair_list_next(list, NULL);
226 vp != NULL;
227 vp = fr_pair_list_next(list, vp)) {
228 xlat_debug_attr_vp(request, vp, NULL);
229 }
230}
231
232/** Common function to move boxes form input list to output list
233 *
234 * This can be used to implement safe_for functions, as the xlat framework
235 * can be used for concatenation, casting, and marking up output boxes as
236 * safe_for.
237 */
239 UNUSED xlat_ctx_t const *xctx,
240 UNUSED request_t *request, fr_value_box_list_t *args)
241{
243 fr_value_box_list_remove(args, vb);
245 }}
246
248}
249
250/** Print out attribute info
251 *
252 * Prints out all instances of a current attribute, or all attributes in a list.
253 *
254 * At higher debugging levels, also prints out alternative decodings of the same
255 * value. This is helpful to determine types for unknown attributes of long
256 * passed vendors, or just crazy/broken NAS.
257 *
258 * This expands to a zero length string.
259 *
260 * Example:
261@verbatim
262%debug_attr(&request)
263@endverbatim
264 *
265 * @ingroup xlat_functions
266 */
268 UNUSED xlat_ctx_t const *xctx,
269 request_t *request, fr_value_box_list_t *args)
270{
271 fr_pair_t *vp;
272 fr_dcursor_t cursor;
274 tmpl_t *vpt;
275 fr_value_box_t *attr;
276 char const *fmt;
277
278 XLAT_ARGS(args, &attr);
279
280 if (!RDEBUG_ENABLED2) return XLAT_ACTION_DONE; /* NOOP if debugging isn't enabled */
281
282 fmt = attr->vb_strvalue;
283
284 if (tmpl_afrom_attr_str(request, NULL, &vpt, fmt,
285 &(tmpl_rules_t){
286 .attr = {
287 .dict_def = request->dict,
288 .list_def = request_attr_request,
289 .allow_wildcard = true,
291 }
292 }) <= 0) {
293 RPEDEBUG("Invalid input");
294 return XLAT_ACTION_FAIL;
295 }
296
297 RIDEBUG("Attributes matching \"%s\"", fmt);
298
299 RINDENT();
300 for (vp = tmpl_dcursor_init(NULL, NULL, &cc, &cursor, request, vpt);
301 vp;
302 vp = fr_dcursor_next(&cursor)) {
303 xlat_debug_attr_vp(request, vp, vpt);
304 }
306 REXDENT();
307
309
310 return XLAT_ACTION_DONE;
311}
312
313#ifdef __clang__
314#pragma clang diagnostic ignored "-Wgnu-designator"
315#endif
316
318 .name = "filename",
319 .chr = '_',
320 .do_utf8 = true,
321 .do_hex = true,
322
323 .esc = {
324 [ 0x00 ... 0x2d ] = true, // special characters, but not '.'
325 [ 0x2f ] = true, // /
326 [ 0x3A ... 0x3f ] = true, // :;<=>?, but not "@"
327 [ 0x5b ... 0x5e ] = true, // [\]^
328 [ 0x60 ] = true, // back-tick
329 [ 0x7b ... 0xff ] = true, // {|}, and all chars which have high bit set, but aren't UTF-8
330 },
331};
332
334 .name = "filename",
335 .chr = '_',
336 .do_utf8 = true,
337 .do_hex = true,
338
339 .esc = {
340 [ 0x00 ... 0x2f ] = true, // special characters, '.', '/', etc.
341 [ 0x3A ... 0x3f ] = true, // :;<=>?, but not "@"
342 [ 0x5b ... 0x5e ] = true, // [\]^
343 [ 0x60 ] = true, // back-tick
344 [ 0x7b ... 0xff ] = true, // {|}, and all chars which have high bit set, but aren't UTF-8
345 },
346};
347
348/** Escape the paths as necessary
349 *
350 */
352{
353 fr_sbuff_t our_in = FR_SBUFF(in);
355 char buffer[256];
356
357 if (vb->type == FR_TYPE_GROUP) {
358 fr_value_box_list_foreach(&vb->vb_group, box) {
359 if (xlat_file_escape_path(&our_in, box) < 0) return -1;
360 }
361
362 goto done;
363 }
364
365 fr_assert(fr_type_is_leaf(vb->type));
366
367 /*
368 * Untainted values get passed through, as do base integer types.
369 */
370 if (!vb->tainted || (vb->type == FR_TYPE_OCTETS) || fr_type_is_integer(vb->type)) {
371 fr_value_box_print(&our_in, vb, NULL);
372 goto done;
373 }
374
375 /*
376 * If the tainted string has a leading '.', then escape _all_ periods in it. This is so that we
377 * don't accidentally allow a "safe" value to end with '/', and then an "unsafe" value contains
378 * "..", and we now have a directory traversal attack.
379 *
380 * The escape rules will escape '/' in unsafe strings, so there's no possibility for an unsafe
381 * string to either end with a '/', or to contain "/.." itself.
382 *
383 * Allowing '.' in the middle of the string means we can have filenames based on realms, such as
384 * "log/aland@freeradius.org".
385 */
386 if (vb->type == FR_TYPE_STRING) {
387 if (vb->vb_length == 0) goto done;
388
389 if (vb->vb_strvalue[0] == '.') {
391 } else {
393 }
394 goto done;
395 }
396
397 /*
398 * Ethernet addresses have ':'. IP prefixes have '/'. Floats have '+' and '-' in them.
399 * Dates have pretty much all of that, plus spaces.
400 *
401 * Lesson: print dates as %Y() or %l().
402 *
403 * We use an intermediate buffer to print the type, and then copy it to the output
404 * buffer, escaping it along the way.
405 */
406 out = FR_SBUFF_OUT(buffer, sizeof(buffer));
407 fr_value_box_print(&out, vb, NULL);
409
410done:
411 FR_SBUFF_SET_RETURN(in, &our_in);
412}
413
414static const char *xlat_file_name(fr_value_box_t *vb)
415{
416 fr_sbuff_t *path;
417
418 FR_SBUFF_TALLOC_THREAD_LOCAL(&path, 256, PATH_MAX + 1);
419
420 if (xlat_file_escape_path(path, vb) < 0) return NULL;
421
422 if (fr_sbuff_in_char(path, '\0') < 0) return NULL; /* file functions take NUL delimited strings */
423
424 return fr_sbuff_start(path);
425}
426
431
433 { .required = true, .type = FR_TYPE_STRING },
434 { .required = false, .type = FR_TYPE_UINT32 },
436};
437
438
440 UNUSED xlat_ctx_t const *xctx,
441 UNUSED request_t *request, fr_value_box_list_t *args)
442{
443 fr_value_box_t *dst, *vb;
444 char const *filename;
445
446 XLAT_ARGS(args, &vb);
447 filename = xlat_file_name(vb);
448 if (!filename) return XLAT_ACTION_FAIL;
449
450 MEM(dst = fr_value_box_alloc(ctx, FR_TYPE_STRING, NULL));
451 if (fr_value_box_bstrndup(dst, dst, NULL, filename, strlen(filename), false) < 0) {
452 talloc_free(dst);
453 return XLAT_ACTION_FAIL;
454 }
455
457
458 return XLAT_ACTION_DONE;
459}
460
461
463 UNUSED xlat_ctx_t const *xctx,
464 UNUSED request_t *request, fr_value_box_list_t *args)
465{
466 fr_value_box_t *dst, *vb;
467 char const *filename;
468 struct stat buf;
469
470 XLAT_ARGS(args, &vb);
471 filename = xlat_file_name(vb);
472 if (!filename) return XLAT_ACTION_FAIL;
473
474 MEM(dst = fr_value_box_alloc(ctx, FR_TYPE_BOOL, NULL));
476
477 dst->vb_bool = (stat(filename, &buf) == 0);
478
479 return XLAT_ACTION_DONE;
480}
481
482
484 UNUSED xlat_ctx_t const *xctx,
485 request_t *request, fr_value_box_list_t *in)
486{
487 fr_value_box_t *dst, *vb;
488 char const *filename;
489 ssize_t len;
490 int fd;
491 char *p, buffer[256];
492
493 XLAT_ARGS(in, &vb);
494 filename = xlat_file_name(vb);
495 if (!filename) return XLAT_ACTION_FAIL;
496
497 fd = open(filename, O_RDONLY);
498 if (fd < 0) {
499 REDEBUG3("Failed opening file %s - %s", filename, fr_syserror(errno));
500 return XLAT_ACTION_FAIL;
501 }
502
503 len = read(fd, buffer, sizeof(buffer));
504 if (len < 0) {
505 REDEBUG3("Failed reading file %s - %s", filename, fr_syserror(errno));
506 close(fd);
507 return XLAT_ACTION_FAIL;
508 }
509
510 /*
511 * Find the first CR/LF, but bail if we get any weird characters.
512 */
513 for (p = buffer; p < (buffer + len); p++) {
514 if ((*p == '\r') || (*p == '\n')) {
515 break;
516 }
517
518 if ((*p < ' ') && (*p != '\t')) {
519 invalid:
520 REDEBUG("Invalid text in file %s", filename);
521 close(fd);
522 return XLAT_ACTION_FAIL;
523 }
524 }
525
526 if ((p - buffer) > len) goto invalid;
527 close(fd);
528
529 MEM(dst = fr_value_box_alloc(ctx, FR_TYPE_STRING, NULL));
530 if (fr_value_box_bstrndup(dst, dst, NULL, buffer, p - buffer, false) < 0) {
531 talloc_free(dst);
532 return XLAT_ACTION_FAIL;
533 }
534
536
537 return XLAT_ACTION_DONE;
538}
539
540
542 UNUSED xlat_ctx_t const *xctx,
543 request_t *request, fr_value_box_list_t *in)
544{
545 fr_value_box_t *dst, *vb;
546 char const *filename;
547 struct stat buf;
548
549 XLAT_ARGS(in, &vb);
550 filename = xlat_file_name(vb);
551 if (!filename) return XLAT_ACTION_FAIL;
552
553 if (stat(filename, &buf) < 0) {
554 REDEBUG3("Failed checking file %s - %s", filename, fr_syserror(errno));
555 return XLAT_ACTION_FAIL;
556 }
557
558 MEM(dst = fr_value_box_alloc(ctx, FR_TYPE_UINT64, NULL)); /* off_t is signed, but file sizes shouldn't be negative */
560
561 dst->vb_uint64 = buf.st_size;
562
563 return XLAT_ACTION_DONE;
564}
565
566
568 UNUSED xlat_ctx_t const *xctx,
569 request_t *request, fr_value_box_list_t *in)
570{
571 fr_value_box_t *dst, *vb, *num = NULL;
572 char const *filename;
573 ssize_t len;
574 size_t count = 0;
575 off_t offset;
576 int fd;
577 int n, r, stop = 2;
578 char *p, *end, *found, buffer[256];
579
580 XLAT_ARGS(in, &vb, &num);
581 filename = xlat_file_name(vb);
582 if (!filename) return XLAT_ACTION_FAIL;
583
584 fd = open(filename, O_RDONLY);
585 if (fd < 0) {
586 REDEBUG3("Failed opening file %s - %s", filename, fr_syserror(errno));
587 return XLAT_ACTION_FAIL;
588 }
589
590 offset = lseek(fd, 0, SEEK_END);
591 if (offset < 0) {
592 REDEBUG3("Failed seeking to end of file %s - %s", filename, fr_syserror(errno));
593 goto fail;
594 }
595
596 if (offset > (off_t) sizeof(buffer)) {
597 offset -= sizeof(buffer);
598 } else {
599 offset = 0;
600 }
601
602 if (lseek(fd, offset, SEEK_SET) < 0) {
603 REDEBUG3("Failed seeking backwards from end of file %s - %s", filename, fr_syserror(errno));
604 goto fail;
605 }
606
607 len = read(fd, buffer, sizeof(buffer));
608 if (len < 0) {
609 fail:
610 REDEBUG3("Failed reading file %s - %s", filename, fr_syserror(errno));
611 close(fd);
612 return XLAT_ACTION_FAIL;
613 }
614
615 if (len == 0) {
616 found = buffer; /* count is zero, so who cares */
617 goto done;
618 }
619
620 n = r = 0; /* be agnostic over CR / LF */
621
622 /*
623 * Clamp number of lines to a reasonable value. They
624 * still all have to fit into 256 characters, though.
625 *
626 * @todo - have a large thread-local temporary buffer for this stuff.
627 */
628 if (num) {
629 fr_assert(num->type == FR_TYPE_GROUP);
630 fr_assert(fr_value_box_list_num_elements(&num->vb_group) == 1);
631
632 num = fr_value_box_list_head(&num->vb_group);
633 fr_assert(num->type == FR_TYPE_UINT32);
634
635 if (!num->vb_uint32) {
636 stop = 2;
637 } else if (num->vb_uint32 < 15) {
638 stop = num->vb_uint64 + 1;
639 } else {
640 stop = 16;
641 }
642 } else {
643 stop = 2;
644 }
645
646 end = NULL;
647 found = NULL;
648
649 /*
650 * Nuke any trailing CR/LF
651 */
652 p = buffer + len - 1;
653 while (p >= buffer) {
654 if (*p == '\r') {
655 r++;
656
657 if (r == stop) break;
658
659 if (!end) end = p;
660
661 } else if (*p == '\n') {
662 n++;
663
664 if (n == stop) break;
665
666 if (!end) end = p;
667
668 } else {
669 if (!r) r++; /* if we didn't get a CR/LF at EOF, pretend we did */
670 if (!n) n++;
671
672 found = p;
673 }
674
675 p--;
676 }
677
678 if (!end) end = buffer + len;
679
680 /*
681 * The buffer was only one line of CR/LF.
682 */
683 if (!found) {
684 found = buffer;
685 goto done;
686 }
687
688 count = (end - found);
689
690done:
691 close(fd);
692
693 MEM(dst = fr_value_box_alloc(ctx, FR_TYPE_STRING, NULL));
694 if (fr_value_box_bstrndup(dst, dst, NULL, found, count, false) < 0) {
695 talloc_free(dst);
696 return XLAT_ACTION_FAIL;
697 }
698
700
701 return XLAT_ACTION_DONE;
702}
703
704
706 UNUSED xlat_ctx_t const *xctx,
707 request_t *request, fr_value_box_list_t *in)
708{
709 fr_value_box_t *dst, *vb;
710 char const *filename;
711
712 XLAT_ARGS(in, &vb);
713 filename = xlat_file_name(vb);
714 if (!filename) return XLAT_ACTION_FAIL;
715
716 MEM(dst = fr_value_box_alloc(ctx, FR_TYPE_BOOL, NULL));
718
719 dst->vb_bool = (unlink(filename) == 0);
720 if (!dst->vb_bool) {
721 REDEBUG3("Failed unlinking file %s - %s", filename, fr_syserror(errno));
722 }
723
724 return XLAT_ACTION_DONE;
725}
726
727
729 UNUSED xlat_ctx_t const *xctx,
730 UNUSED request_t *request, fr_value_box_list_t *in)
731{
732 fr_value_box_t *vb;
733
735 while ((vb = fr_value_box_list_pop_head(in)) != NULL) {
737 }
738
739 return XLAT_ACTION_DONE;
740}
741
743 UNUSED xlat_ctx_t const *xctx,
744 UNUSED request_t *request, fr_value_box_list_t *in)
745{
746 fr_value_box_t *vb;
747
748 while ((vb = fr_value_box_list_pop_head(in)) != NULL) {
749 fr_value_box_t *child;
750
751 fr_assert(vb->type == FR_TYPE_GROUP);
752
753 while ((child = fr_value_box_list_pop_head(&vb->vb_group)) != NULL) {
754 child->tainted = true;
755
756 fr_dcursor_append(out, child);
757 }
758 }
759
760 return XLAT_ACTION_DONE;
761}
762
764 { .required = true, .type = FR_TYPE_STRING },
765 { .required = true, .concat = true, .type = FR_TYPE_STRING },
767};
768
769/** Split a string into multiple new strings based on a delimiter
770 *
771@verbatim
772%explode(<string>, <delim>)
773@endverbatim
774 *
775 * Example:
776@verbatim
777update request {
778 &Tmp-String-1 := "a,b,c"
779}
780"%concat(%explode(%{Tmp-String-1}, ','), '|')" == "a|b|c"g
781@endverbatim
782 *
783 * @ingroup xlat_functions
784 */
786 UNUSED xlat_ctx_t const *xctx,
787 request_t *request, fr_value_box_list_t *args)
788{
790 fr_value_box_list_t *list;
791 fr_value_box_t *delim_vb;
792 ssize_t delim_len;
793 char const *delim;
794 fr_value_box_t *string, *vb;
795
796 XLAT_ARGS(args, &strings, &delim_vb);
797
798 list = &strings->vb_group;
799
800 /* coverity[dereference] */
801 if (delim_vb->vb_length == 0) {
802 REDEBUG("Delimiter must be greater than zero characters");
803 return XLAT_ACTION_FAIL;
804 }
805
806 delim = delim_vb->vb_strvalue;
807 delim_len = delim_vb->vb_length;
808
809 while((string = fr_value_box_list_pop_head(list))) {
810 fr_sbuff_t sbuff = FR_SBUFF_IN(string->vb_strvalue, string->vb_length);
811 fr_sbuff_marker_t m_start;
812
813 /*
814 * If the delimiter is not in the string, just move to the output
815 */
816 if (!fr_sbuff_adv_to_str(&sbuff, SIZE_MAX, delim, delim_len)) {
817 fr_dcursor_append(out, string);
818 continue;
819 }
820
821 fr_sbuff_set_to_start(&sbuff);
822 fr_sbuff_marker(&m_start, &sbuff);
823
824 while (fr_sbuff_remaining(&sbuff)) {
825 if (fr_sbuff_adv_to_str(&sbuff, SIZE_MAX, delim, delim_len)) {
826 /*
827 * If there's nothing before the delimiter skip
828 */
829 if (fr_sbuff_behind(&m_start) == 0) goto advance;
830
831 MEM(vb = fr_value_box_alloc_null(ctx));
832 fr_value_box_bstrndup(vb, vb, NULL, fr_sbuff_current(&m_start),
833 fr_sbuff_behind(&m_start), string->tainted);
835
836 advance:
837 fr_sbuff_advance(&sbuff, delim_len);
838 fr_sbuff_set(&m_start, &sbuff);
839 continue;
840 }
841 fr_sbuff_set_to_end(&sbuff);
842 MEM(vb = fr_value_box_alloc_null(ctx));
843 fr_value_box_bstrndup(vb, vb, NULL, fr_sbuff_current(&m_start),
844 fr_sbuff_behind(&m_start), string->tainted);
846 break;
847 }
848 talloc_free(string);
849 }
850
851 return XLAT_ACTION_DONE;
852}
853
855 { .required = true, .single = true, .type = FR_TYPE_STRING },
857};
858
859/** Mark one or more attributes as immutable
860 *
861 * Example:
862@verbatim
863%immutable(&request.State[*])
864@endverbatim
865 *
866 * @ingroup xlat_functions
867 */
869 UNUSED xlat_ctx_t const *xctx,
870 request_t *request, fr_value_box_list_t *args)
871{
872 fr_pair_t *vp;
873 fr_dcursor_t cursor;
875 tmpl_t *vpt;
876 fr_value_box_t *attr;
877 char const *fmt;
878
879 XLAT_ARGS(args, &attr);
880
881 fmt = attr->vb_strvalue;
882
883 if (tmpl_afrom_attr_str(request, NULL, &vpt, fmt,
884 &(tmpl_rules_t){
885 .attr = {
886 .dict_def = request->dict,
887 .list_def = request_attr_request,
888 .allow_wildcard = true,
890 }
891 }) <= 0) {
892 RPEDEBUG("Invalid input");
893 return XLAT_ACTION_FAIL;
894 }
895
896 RIDEBUG("Attributes matching \"%s\"", fmt);
897
898 RINDENT();
899 for (vp = tmpl_dcursor_init(NULL, NULL, &cc, &cursor, request, vpt);
900 vp;
901 vp = fr_dcursor_next(&cursor)) {
903 }
905 REXDENT();
906
908
909 return XLAT_ACTION_DONE;
910}
911
913 { .required = true, .single = true, .type = FR_TYPE_VOID },
915};
916
917/** Print data as integer, not as VALUE.
918 *
919 * Example:
920@verbatim
921update request {
922 &Tmp-IP-Address-0 := "127.0.0.5"
923}
924%integer(%{Tmp-IP-Address-0}) == 2130706437
925@endverbatim
926 * @ingroup xlat_functions
927 */
929 UNUSED xlat_ctx_t const *xctx,
930 request_t *request, fr_value_box_list_t *args)
931{
932 fr_value_box_t *in_vb;
933 char const *p;
934
935 XLAT_ARGS(args, &in_vb);
936
937 fr_strerror_clear(); /* Make sure we don't print old errors */
938
939 fr_value_box_list_remove(args, in_vb);
940
941 switch (in_vb->type) {
942 default:
943 error:
944 RPEDEBUG("Failed converting %pV (%s) to an integer", in_vb,
945 fr_type_to_str(in_vb->type));
946 talloc_free(in_vb);
947 return XLAT_ACTION_FAIL;
948
949 case FR_TYPE_NUMERIC:
950 /*
951 * Ensure enumeration is NULL so that the integer
952 * version of a box is returned
953 */
954 in_vb->enumv = NULL;
955
956 /*
957 * FR_TYPE_DATE and FR_TYPE_TIME_DELTA need to be cast
958 * to int64_t so that they're printed in a
959 * numeric format.
960 */
961 if ((in_vb->type == FR_TYPE_DATE) || (in_vb->type == FR_TYPE_TIME_DELTA)) {
962 if (fr_value_box_cast_in_place(ctx, in_vb, FR_TYPE_INT64, NULL) < 0) goto error;
963 }
964 break;
965
966 case FR_TYPE_STRING:
967 /*
968 * Strings are always zero terminated. They may
969 * also have zeros in the middle, but if that
970 * happens, the caller will only get the part up
971 * to the first zero.
972 *
973 * We check for negative numbers, just to be
974 * nice.
975 */
976 for (p = in_vb->vb_strvalue; *p != '\0'; p++) {
977 if (*p == '-') break;
978 }
979
980 if (*p == '-') {
981 if (fr_value_box_cast_in_place(ctx, in_vb, FR_TYPE_INT64, NULL) < 0) goto error;
982 } else {
983 if (fr_value_box_cast_in_place(ctx, in_vb, FR_TYPE_UINT64, NULL) < 0) goto error;
984 }
985 break;
986
987 case FR_TYPE_OCTETS:
988 if (in_vb->vb_length > sizeof(uint64_t)) {
989 fr_strerror_printf("Expected octets length <= %zu, got %zu", sizeof(uint64_t), in_vb->vb_length);
990 goto error;
991 }
992
993 if (in_vb->vb_length > sizeof(uint32_t)) {
994 if (unlikely(fr_value_box_cast_in_place(ctx, in_vb, FR_TYPE_UINT64, NULL) < 0)) goto error;
995 } else if (in_vb->vb_length > sizeof(uint16_t)) {
996 if (unlikely(fr_value_box_cast_in_place(ctx, in_vb, FR_TYPE_UINT32, NULL) < 0)) goto error;
997 } else if (in_vb->vb_length > sizeof(uint8_t)) {
998 if (unlikely(fr_value_box_cast_in_place(ctx, in_vb, FR_TYPE_UINT16, NULL) < 0)) goto error;
999 } else {
1000 if (unlikely(fr_value_box_cast_in_place(ctx, in_vb, FR_TYPE_UINT8, NULL) < 0)) goto error;
1001 }
1002
1003 break;
1004
1005 case FR_TYPE_IPV4_ADDR:
1007 if (fr_value_box_cast_in_place(ctx, in_vb, FR_TYPE_UINT32, NULL) < 0) goto error;
1008 break;
1009
1010 case FR_TYPE_ETHERNET:
1011 if (fr_value_box_cast_in_place(ctx, in_vb, FR_TYPE_UINT64, NULL) < 0) goto error;
1012 break;
1013
1014 case FR_TYPE_IPV6_ADDR:
1016 {
1017 uint128_t ipv6int;
1018 char buff[40];
1019 fr_value_box_t *vb;
1020
1021 /*
1022 * Needed for correct alignment (as flagged by ubsan)
1023 */
1024 memcpy(&ipv6int, &in_vb->vb_ip.addr.v6.s6_addr, sizeof(ipv6int));
1025
1026 fr_snprint_uint128(buff, sizeof(buff), ntohlll(ipv6int));
1027
1028 MEM(vb = fr_value_box_alloc_null(ctx));
1029 fr_value_box_bstrndup(vb, vb, NULL, buff, strlen(buff), false);
1031 talloc_free(in_vb);
1032 return XLAT_ACTION_DONE;
1033 }
1034 }
1035
1036 fr_dcursor_append(out, in_vb);
1037
1038 return XLAT_ACTION_DONE;
1039}
1040
1042 { .concat = true, .type = FR_TYPE_STRING },
1044};
1045
1046/** Log something at INFO level.
1047 *
1048 * Example:
1049@verbatim
1050%log("This is an informational message")
1051@endverbatim
1052 *
1053 * @ingroup xlat_functions
1054 */
1056 UNUSED xlat_ctx_t const *xctx,
1057 request_t *request, fr_value_box_list_t *args)
1058{
1059 fr_value_box_t *vb;
1060
1061 XLAT_ARGS(args, &vb);
1062
1063 if (!vb) return XLAT_ACTION_DONE;
1064
1065 RINFO("%s", vb->vb_strvalue);
1066
1067 return XLAT_ACTION_DONE;
1068}
1069
1070
1071/** Log something at DEBUG level.
1072 *
1073 * Example:
1074@verbatim
1075%log.debug("This is a message")
1076@endverbatim
1077 *
1078 * @ingroup xlat_functions
1079 */
1081 UNUSED xlat_ctx_t const *xctx,
1082 request_t *request, fr_value_box_list_t *args)
1083{
1084 fr_value_box_t *vb;
1085
1086 XLAT_ARGS(args, &vb);
1087
1088 if (!vb) return XLAT_ACTION_DONE;
1089
1090 RDEBUG("%s", vb->vb_strvalue);
1091
1092 return XLAT_ACTION_DONE;
1093}
1094
1095
1096/** Log something at DEBUG level.
1097 *
1098 * Example:
1099@verbatim
1100%log.err("Big error here")
1101@endverbatim
1102 *
1103 * @ingroup xlat_functions
1104 */
1106 UNUSED xlat_ctx_t const *xctx,
1107 request_t *request, fr_value_box_list_t *args)
1108{
1109 fr_value_box_t *vb;
1110
1111 XLAT_ARGS(args, &vb);
1112
1113 if (!vb) return XLAT_ACTION_DONE;
1114
1115 REDEBUG("%s", vb->vb_strvalue);
1116
1117 return XLAT_ACTION_DONE;
1118}
1119
1120
1121/** Log something at WARN level.
1122 *
1123 * Example:
1124@verbatim
1125%log.warn("Maybe something bad happened")
1126@endverbatim
1127 *
1128 * @ingroup xlat_functions
1129 */
1131 UNUSED xlat_ctx_t const *xctx,
1132 request_t *request, fr_value_box_list_t *args)
1133{
1134 fr_value_box_t *vb;
1135
1136 XLAT_ARGS(args, &vb);
1137
1138 if (!vb) return XLAT_ACTION_DONE;
1139
1140 RWDEBUG("%s", vb->vb_strvalue);
1141
1142 return XLAT_ACTION_DONE;
1143}
1144
1145static int _log_dst_free(fr_log_t *log)
1146{
1147 close(log->fd);
1148 return 0;
1149}
1150
1152 { .required = false, .type = FR_TYPE_STRING, .concat = true },
1153 { .required = false, .type = FR_TYPE_UINT32, .single = true },
1154 { .required = false, .type = FR_TYPE_STRING, .concat = true },
1156};
1157
1158/** Change the log destination to the named one
1159 *
1160 * Example:
1161@verbatim
1162%log.destination('foo')
1163@endverbatim
1164 *
1165 * @ingroup xlat_functions
1166 */
1168 UNUSED xlat_ctx_t const *xctx,
1169 request_t *request, fr_value_box_list_t *args)
1170{
1171 fr_value_box_t *dst, *lvl, *file;
1172 fr_log_t *log, *dbg;
1173 uint32_t level = 2;
1174
1175 XLAT_ARGS(args, &dst, &lvl, &file);
1176
1177 if (!dst || !*dst->vb_strvalue) {
1178 request_log_prepend(request, NULL, L_DBG_LVL_DISABLE);
1179 return XLAT_ACTION_DONE;
1180 }
1181
1182 log = log_dst_by_name(dst->vb_strvalue);
1183 if (!log) return XLAT_ACTION_FAIL;
1184
1185 if (lvl) level = lvl->vb_uint32;
1186
1187 if (!file || ((log->dst != L_DST_NULL) && (log->dst != L_DST_FILES))) {
1188 request_log_prepend(request, log, level);
1189 return XLAT_ACTION_DONE;
1190 }
1191
1192 /*
1193 * Clone it.
1194 */
1195 MEM(dbg = talloc_memdup(request, log, sizeof(*log)));
1196 dbg->parent = log;
1197
1198 /*
1199 * Open the new filename.
1200 */
1201 dbg->dst = L_DST_FILES;
1202 dbg->file = talloc_strdup(dbg, file->vb_strvalue);
1203 dbg->fd = open(dbg->file, O_WRONLY | O_CREAT | O_CLOEXEC, 0600);
1204 if (dbg->fd < 0) {
1205 REDEBUG("Failed opening %s - %s", dbg->file, fr_syserror(errno));
1206 talloc_free(dbg);
1207 return XLAT_ACTION_DONE;
1208 }
1209
1210 /*
1211 * Ensure that we close the file handle when done.
1212 */
1213 talloc_set_destructor(dbg, _log_dst_free);
1214
1215 request_log_prepend(request, dbg, level);
1216 return XLAT_ACTION_DONE;
1217}
1218
1219
1221 { .required = true, .concat = true, .type = FR_TYPE_STRING },
1223};
1224
1225/** Processes fmt as a map string and applies it to the current request
1226 *
1227 * e.g.
1228@verbatim
1229%map("&User-Name := 'foo'")
1230@endverbatim
1231 *
1232 * Allows sets of modifications to be cached and then applied.
1233 * Useful for processing generic attributes from LDAP.
1234 *
1235 * @ingroup xlat_functions
1236 */
1238 UNUSED xlat_ctx_t const *xctx,
1239 request_t *request, fr_value_box_list_t *args)
1240{
1241 map_t *map = NULL;
1242 int ret;
1243 fr_value_box_t *fmt_vb;
1244 fr_value_box_t *vb;
1245
1246 tmpl_rules_t attr_rules = {
1247 .attr = {
1248 .dict_def = request->dict,
1249 .list_def = request_attr_request,
1251 },
1252 .xlat = {
1253 .runtime_el = unlang_interpret_event_list(request)
1254 }
1255 };
1256
1257 XLAT_ARGS(args, &fmt_vb);
1258
1259 if (map_afrom_attr_str(request, &map, fmt_vb->vb_strvalue, &attr_rules, &attr_rules) < 0) {
1260 RPEDEBUG("Failed parsing \"%s\" as map", fmt_vb->vb_strvalue);
1261 return XLAT_ACTION_FAIL;
1262 }
1263
1264 MEM(vb = fr_value_box_alloc(ctx, FR_TYPE_INT8, NULL));
1265 vb->vb_int8 = 0; /* Default fail value - changed to 1 on success */
1267
1268 switch (map->lhs->type) {
1269 case TMPL_TYPE_ATTR:
1270 case TMPL_TYPE_XLAT:
1271 break;
1272
1273 default:
1274 REDEBUG("Unexpected type %s in left hand side of expression",
1275 tmpl_type_to_str(map->lhs->type));
1276 return XLAT_ACTION_FAIL;
1277 }
1278
1279 switch (map->rhs->type) {
1280 case TMPL_TYPE_ATTR:
1281 case TMPL_TYPE_EXEC:
1282 case TMPL_TYPE_DATA:
1285 case TMPL_TYPE_XLAT:
1286 break;
1287
1288 default:
1289 REDEBUG("Unexpected type %s in right hand side of expression",
1290 tmpl_type_to_str(map->rhs->type));
1291 return XLAT_ACTION_FAIL;
1292 }
1293
1294 RINDENT();
1295 ret = map_to_request(request, map, map_to_vp, NULL);
1296 REXDENT();
1297 talloc_free(map);
1298 if (ret < 0) return XLAT_ACTION_FAIL;
1299
1300 vb->vb_int8 = 1;
1301 return XLAT_ACTION_DONE;
1302}
1303
1304
1306 { .required = true, .concat = true, .type = FR_TYPE_STRING },
1308};
1309
1310/** Calculate number of seconds until the next n hour(s), day(s), week(s), year(s).
1311 *
1312 * For example, if it were 16:18 %nexttime(1h) would expand to 2520.
1313 *
1314 * The envisaged usage for this function is to limit sessions so that they don't
1315 * cross billing periods. The output of the xlat should be combined with %rand() to create
1316 * some jitter, unless the desired effect is every subscriber on the network
1317 * re-authenticating at the same time.
1318 *
1319 * @ingroup xlat_functions
1320 */
1322 UNUSED xlat_ctx_t const *xctx,
1323 request_t *request, fr_value_box_list_t *args)
1324{
1325 long num;
1326
1327 char const *p;
1328 char *q;
1329 time_t now;
1330 struct tm *local, local_buff;
1331 fr_value_box_t *in_head;
1332 fr_value_box_t *vb;
1333
1334 XLAT_ARGS(args, &in_head);
1335
1336 /*
1337 * We want to limit based on _now_, not on when they logged in.
1338 */
1339 now = time(NULL);
1340 local = localtime_r(&now, &local_buff);
1341
1342 p = in_head->vb_strvalue;
1343
1344 num = strtoul(p, &q, 10);
1345 if (!q || *q == '\0') {
1346 REDEBUG("nexttime: <int> must be followed by period specifier (h|d|w|m|y)");
1347 return XLAT_ACTION_FAIL;
1348 }
1349
1350 if (p == q) {
1351 num = 1;
1352 } else {
1353 p += q - p;
1354 }
1355
1356 local->tm_sec = 0;
1357 local->tm_min = 0;
1358
1359 switch (*p) {
1360 case 'h':
1361 local->tm_hour += num;
1362 break;
1363
1364 case 'd':
1365 local->tm_hour = 0;
1366 local->tm_mday += num;
1367 break;
1368
1369 case 'w':
1370 local->tm_hour = 0;
1371 local->tm_mday += (7 - local->tm_wday) + (7 * (num-1));
1372 break;
1373
1374 case 'm':
1375 local->tm_hour = 0;
1376 local->tm_mday = 1;
1377 local->tm_mon += num;
1378 break;
1379
1380 case 'y':
1381 local->tm_hour = 0;
1382 local->tm_mday = 1;
1383 local->tm_mon = 0;
1384 local->tm_year += num;
1385 break;
1386
1387 default:
1388 REDEBUG("nexttime: Invalid period specifier '%c', must be h|d|w|m|y", *p);
1389 return XLAT_ACTION_FAIL;
1390 }
1391
1392 MEM(vb = fr_value_box_alloc_null(ctx));
1393 fr_value_box_uint64(vb, NULL, (uint64_t)(mktime(local) - now), false);
1395 return XLAT_ACTION_DONE;
1396}
1397
1402
1403/** Just serves to push the result up the stack
1404 *
1405 */
1407 xlat_ctx_t const *xctx,
1408 UNUSED request_t *request, UNUSED fr_value_box_list_t *in)
1409{
1410 xlat_eval_rctx_t *rctx = talloc_get_type_abort(xctx->rctx, xlat_eval_rctx_t);
1412
1413 talloc_free(rctx);
1414
1415 return xa;
1416}
1417
1418typedef struct {
1419 fr_dict_t const *namespace; //!< Namespace we use for evaluating runtime expansions
1421
1423{
1424 xlat_eval_inst_t *inst = talloc_get_type_abort(xctx->inst, xlat_eval_inst_t);
1425
1426 inst->namespace = xctx->ex->call.dict;
1427
1428 return 0;
1429}
1430
1432 { .required = true, .concat = true, .type = FR_TYPE_STRING },
1434};
1435
1436/** Dynamically evaluate an expansion string
1437 *
1438 * @ingroup xlat_functions
1439 */
1441 xlat_ctx_t const *xctx,
1442 request_t *request, fr_value_box_list_t *args)
1443{
1445
1446 /*
1447 * These are escaping rules applied to the
1448 * input string. They're mostly here to
1449 * allow \% and \\ to work.
1450 *
1451 * Everything else should be passed in as
1452 * unescaped data.
1453 */
1454 static fr_sbuff_unescape_rules_t const escape_rules = {
1455 .name = "xlat",
1456 .chr = '\\',
1457 .subs = {
1458 ['%'] = '%',
1459 ['\\'] = '\\',
1460 },
1461 .do_hex = false,
1462 .do_oct = false
1463 };
1464
1465 xlat_eval_rctx_t *rctx;
1466 fr_value_box_t *arg = fr_value_box_list_head(args);
1467
1468 XLAT_ARGS(args, &arg);
1469
1470 MEM(rctx = talloc_zero(unlang_interpret_frame_talloc_ctx(request), xlat_eval_rctx_t));
1471
1472 /*
1473 * Parse the input as a literal expansion
1474 */
1475 if (xlat_tokenize_expression(rctx,
1476 &rctx->ex,
1477 &FR_SBUFF_IN(arg->vb_strvalue, arg->vb_length),
1478 &(fr_sbuff_parse_rules_t){
1479 .escapes = &escape_rules
1480 },
1481 &(tmpl_rules_t){
1482 .attr = {
1483 .dict_def = inst->namespace,
1484 .list_def = request_attr_request,
1485 .allow_unknown = false,
1486 .allow_unresolved = false,
1487 .allow_foreign = false,
1488 },
1489 .xlat = {
1490 .runtime_el = unlang_interpret_event_list(request),
1491 },
1492 .at_runtime = true
1493 }) < 0) {
1494 RPEDEBUG("Failed parsing expansion");
1495 error:
1496 talloc_free(rctx);
1497 return XLAT_ACTION_FAIL;
1498 }
1499
1500 /*
1501 * Call the resolution function so we produce
1502 * good errors about what function was
1503 * unresolved.
1504 */
1505 if (rctx->ex->flags.needs_resolving &&
1506 (xlat_resolve(rctx->ex, &(xlat_res_rules_t){ .allow_unresolved = false }) < 0)) {
1507 RPEDEBUG("Unresolved expansion functions in expansion");
1508 goto error;
1509
1510 }
1511
1512 if (unlang_xlat_yield(request, xlat_eval_resume, NULL, 0, rctx) != XLAT_ACTION_YIELD) goto error;
1513
1514 if (unlang_xlat_push(ctx, &rctx->last_success, (fr_value_box_list_t *)out->dlist,
1515 request, rctx->ex, UNLANG_SUB_FRAME) < 0) goto error;
1516
1518}
1519
1521 { .required = true, .type = FR_TYPE_STRING },
1522 { .required = true, .single = true, .type = FR_TYPE_UINT64 },
1523 { .concat = true, .type = FR_TYPE_STRING },
1525};
1526
1527/** lpad a string
1528 *
1529@verbatim
1530%lpad(%{Attribute-Name}, <length> [, <fill>])
1531@endverbatim
1532 *
1533 * Example: (User-Name = "foo")
1534@verbatim
1535%lpad(%{User-Name}, 5 'x') == "xxfoo"
1536@endverbatim
1537 *
1538 * @ingroup xlat_functions
1539 */
1541 UNUSED xlat_ctx_t const *xctx,
1542 request_t *request, fr_value_box_list_t *args)
1543{
1544 fr_value_box_t *values;
1545 fr_value_box_t *pad;
1547
1548 fr_value_box_list_t *list;
1549
1550 size_t pad_len;
1551
1552 char const *fill_str = NULL;
1553 size_t fill_len = 0;
1554
1555 fr_value_box_t *in = NULL;
1556
1557 XLAT_ARGS(args, &values, &pad, &fill);
1558
1559 /* coverity[dereference] */
1560 list = &values->vb_group;
1561 /* coverity[dereference] */
1562 pad_len = (size_t)pad->vb_uint64;
1563
1564 /*
1565 * Fill is optional
1566 */
1567 if (fill) {
1568 fill_str = fill->vb_strvalue;
1569 fill_len = talloc_array_length(fill_str) - 1;
1570 }
1571
1572 if (fill_len == 0) {
1573 fill_str = " ";
1574 fill_len = 1;
1575 }
1576
1577 while ((in = fr_value_box_list_pop_head(list))) {
1578 size_t len = talloc_array_length(in->vb_strvalue) - 1;
1579 size_t remaining;
1580 char *buff;
1581 fr_sbuff_t sbuff;
1582 fr_sbuff_marker_t m_data;
1583
1585
1586 if (len >= pad_len) continue;
1587
1588 if (fr_value_box_bstr_realloc(in, &buff, in, pad_len) < 0) {
1589 RPEDEBUG("Failed reallocing input data");
1590 return XLAT_ACTION_FAIL;
1591 }
1592
1593 fr_sbuff_init_in(&sbuff, buff, pad_len);
1594 fr_sbuff_marker(&m_data, &sbuff);
1595
1596 /*
1597 * ...nothing to move if the input
1598 * string is empty.
1599 */
1600 if (len > 0) {
1601 fr_sbuff_advance(&m_data, pad_len - len); /* Mark where we want the data to go */
1602 fr_sbuff_move(&FR_SBUFF(&m_data), &FR_SBUFF(&sbuff), len); /* Shift the data */
1603 }
1604
1605 if (fill_len == 1) {
1606 memset(fr_sbuff_current(&sbuff), *fill_str, fr_sbuff_ahead(&m_data));
1607 continue;
1608 }
1609
1610 /*
1611 * Copy fill as a repeating pattern
1612 */
1613 while ((remaining = fr_sbuff_ahead(&m_data))) {
1614 size_t to_copy = remaining >= fill_len ? fill_len : remaining;
1615 memcpy(fr_sbuff_current(&sbuff), fill_str, to_copy); /* avoid \0 termination */
1616 fr_sbuff_advance(&sbuff, to_copy);
1617 }
1618 fr_sbuff_set_to_end(&sbuff);
1619 fr_sbuff_terminate(&sbuff); /* Move doesn't re-terminate */
1620 }
1621
1622 return XLAT_ACTION_DONE;
1623}
1624
1625/** Right pad a string
1626 *
1627@verbatim
1628%rpad(%{Attribute-Name}, <length> [, <fill>])
1629@endverbatim
1630 *
1631 * Example: (User-Name = "foo")
1632@verbatim
1633%rpad(%{User-Name}, 5 'x') == "fooxx"
1634@endverbatim
1635 *
1636 * @ingroup xlat_functions
1637 */
1639 UNUSED xlat_ctx_t const *xctx,
1640 request_t *request, fr_value_box_list_t *args)
1641{
1642 fr_value_box_t *values;
1643 fr_value_box_list_t *list;
1644 fr_value_box_t *pad;
1645 /* coverity[dereference] */
1646 size_t pad_len;
1648 char const *fill_str = NULL;
1649 size_t fill_len = 0;
1650
1651 fr_value_box_t *in = NULL;
1652
1653 XLAT_ARGS(args, &values, &pad, &fill);
1654
1655 list = &values->vb_group;
1656 pad_len = (size_t)pad->vb_uint64;
1657
1658 /*
1659 * Fill is optional
1660 */
1661 if (fill) {
1662 fill_str = fill->vb_strvalue;
1663 fill_len = talloc_array_length(fill_str) - 1;
1664 }
1665
1666 if (fill_len == 0) {
1667 fill_str = " ";
1668 fill_len = 1;
1669 }
1670
1671 while ((in = fr_value_box_list_pop_head(list))) {
1672 size_t len = talloc_array_length(in->vb_strvalue) - 1;
1673 size_t remaining;
1674 char *buff;
1675 fr_sbuff_t sbuff;
1676
1678
1679 if (len >= pad_len) continue;
1680
1681 if (fr_value_box_bstr_realloc(in, &buff, in, pad_len) < 0) {
1682 fail:
1683 RPEDEBUG("Failed reallocing input data");
1684 return XLAT_ACTION_FAIL;
1685 }
1686
1687 fr_sbuff_init_in(&sbuff, buff, pad_len);
1688 fr_sbuff_advance(&sbuff, len);
1689
1690 if (fill_len == 1) {
1691 memset(fr_sbuff_current(&sbuff), *fill_str, fr_sbuff_remaining(&sbuff));
1692 continue;
1693 }
1694
1695 /*
1696 * Copy fill as a repeating pattern
1697 */
1698 while ((remaining = fr_sbuff_remaining(&sbuff))) {
1699 if (fr_sbuff_in_bstrncpy(&sbuff, fill_str, remaining >= fill_len ? fill_len : remaining) < 0) {
1700 goto fail;
1701 }
1702 }
1703 }
1704
1705 return XLAT_ACTION_DONE;
1706}
1707
1709 { .required = true, .concat = true, .type = FR_TYPE_OCTETS },
1711};
1712
1713/** Encode string or attribute as base64
1714 *
1715 * Example:
1716@verbatim
1717%base64.encode("foo") == "Zm9v"
1718@endverbatim
1719 *
1720 * @ingroup xlat_functions
1721 */
1723 UNUSED xlat_ctx_t const *xctx,
1724 request_t *request, fr_value_box_list_t *args)
1725{
1726 size_t alen;
1727 ssize_t elen;
1728 char *buff;
1729 fr_value_box_t *vb;
1731
1732 XLAT_ARGS(args, &in);
1733
1734 alen = FR_BASE64_ENC_LENGTH(in->vb_length);
1735
1736 MEM(vb = fr_value_box_alloc_null(ctx));
1737 if (fr_value_box_bstr_alloc(vb, &buff, vb, NULL, alen, false) < 0) {
1738 talloc_free(vb);
1739 return XLAT_ACTION_FAIL;
1740 }
1741
1742 elen = fr_base64_encode(&FR_SBUFF_OUT(buff, talloc_array_length(buff)),
1743 &FR_DBUFF_TMP(in->vb_octets, in->vb_length), true);
1744 if (elen < 0) {
1745 RPEDEBUG("Base64 encoding failed");
1746 talloc_free(vb);
1747 return XLAT_ACTION_FAIL;
1748 }
1749 fr_assert((size_t)elen <= alen);
1750 vb->tainted = in->tainted;
1753
1754 return XLAT_ACTION_DONE;
1755}
1756
1758 { .required = true, .concat = true, .type = FR_TYPE_OCTETS },
1760};
1761
1762/** Decode base64 string
1763 *
1764 * Example:
1765@verbatim
1766%base64.decode("Zm9v") == "foo"
1767@endverbatim
1768 *
1769 * @ingroup xlat_functions
1770 */
1772 UNUSED xlat_ctx_t const *xctx,
1773 request_t *request, fr_value_box_list_t *args)
1774{
1775 size_t alen;
1776 ssize_t declen = 0;
1777 uint8_t *decbuf;
1778 fr_value_box_t *vb;
1780
1781 XLAT_ARGS(args, &in);
1782
1783 /*
1784 * Pass empty arguments through
1785 *
1786 * FR_BASE64_DEC_LENGTH produces 2 for empty strings...
1787 */
1788 if (in->vb_length == 0) {
1789 fr_value_box_list_remove(args, in);
1791 return XLAT_ACTION_DONE;
1792 }
1793
1794 alen = FR_BASE64_DEC_LENGTH(in->vb_length);
1795 MEM(vb = fr_value_box_alloc_null(ctx));
1796 if (alen > 0) {
1797 MEM(fr_value_box_mem_alloc(vb, &decbuf, vb, NULL, alen, in->tainted) == 0);
1798 declen = fr_base64_decode(&FR_DBUFF_TMP(decbuf, alen),
1799 &FR_SBUFF_IN(in->vb_strvalue, in->vb_length), true, true);
1800 if (declen < 0) {
1801 RPEDEBUG("Base64 string invalid");
1802 talloc_free(vb);
1803 return XLAT_ACTION_FAIL;
1804 }
1805
1806 MEM(fr_value_box_mem_realloc(vb, NULL, vb, declen) == 0);
1807 }
1808
1809 vb->tainted = in->tainted;
1812
1813 return XLAT_ACTION_DONE;
1814}
1815
1817 { .required = true, .type = FR_TYPE_STRING },
1819};
1820
1821/** Convert hex string to binary
1822 *
1823 * Example:
1824@verbatim
1825%bin("666f6f626172") == "foobar"
1826@endverbatim
1827 *
1828 * @see #xlat_func_hex
1829 *
1830 * @ingroup xlat_functions
1831 */
1833 UNUSED xlat_ctx_t const *xctx,
1834 request_t *request, fr_value_box_list_t *args)
1835{
1836 fr_value_box_t *result;
1837 char const *p, *end;
1838 uint8_t *bin;
1839 size_t len, outlen;
1841 fr_value_box_t *list, *hex;
1842
1843 XLAT_ARGS(args, &list);
1844
1845 while ((hex = fr_value_box_list_pop_head(&list->vb_group))) {
1846 len = hex->vb_length;
1847 if ((len > 1) && (len & 0x01)) {
1848 REDEBUG("Input data length must be >1 and even, got %zu", len);
1849 return XLAT_ACTION_FAIL;
1850 }
1851
1852 p = hex->vb_strvalue;
1853 end = p + len;
1854
1855 /*
1856 * Look for 0x at the start of the string
1857 */
1858 if ((p[0] == '0') && (p[1] == 'x')) {
1859 p += 2;
1860 len -=2;
1861 }
1862
1863 /*
1864 * Zero length octets string
1865 */
1866 if (p == end) continue;
1867
1868 outlen = len / 2;
1869
1870 MEM(result = fr_value_box_alloc_null(ctx));
1871 MEM(fr_value_box_mem_alloc(result, &bin, result, NULL, outlen, fr_value_box_list_tainted(args)) == 0);
1872 fr_base16_decode(&err, &FR_DBUFF_TMP(bin, outlen), &FR_SBUFF_IN(p, end - p), true);
1873 if (err) {
1874 REDEBUG2("Invalid hex string");
1875 talloc_free(result);
1876 return XLAT_ACTION_FAIL;
1877 }
1878
1879 fr_dcursor_append(out, result);
1880 }
1881
1882 return XLAT_ACTION_DONE;
1883}
1884
1886 { .required = true, .single = true, .type = FR_TYPE_VOID },
1887 { .type = FR_TYPE_VOID },
1888 { .variadic = XLAT_ARG_VARIADIC_EMPTY_KEEP, .type = FR_TYPE_VOID },
1890};
1891
1892/** Cast one or more output value-boxes to the given type
1893 *
1894 * First argument of is type to cast to.
1895 *
1896 * Example:
1897@verbatim
1898%cast('string', %{request[*]}) results in all of the input boxes being cast to string/
1899@endverbatim
1900 *
1901 * @ingroup xlat_functions
1902 */
1904 UNUSED xlat_ctx_t const *xctx,
1905 request_t *request, fr_value_box_list_t *args)
1906{
1908 fr_value_box_t *arg;
1910 fr_dict_attr_t const *time_res = NULL;
1911
1912 XLAT_ARGS(args, &name);
1913
1914 /*
1915 * Get the type, which can be in one of a few formats.
1916 */
1917 if (fr_type_is_numeric(name->type)) {
1919 RPEDEBUG("Failed parsing '%pV' as a numerical data type", name);
1920 return XLAT_ACTION_FAIL;
1921 }
1922 type = name->vb_uint8;
1923
1924 } else {
1925 if (name->type != FR_TYPE_STRING) {
1927 RPEDEBUG("Failed parsing '%pV' as a string data type", name);
1928 return XLAT_ACTION_FAIL;
1929 }
1930 }
1931
1933 if (type == FR_TYPE_NULL) {
1934 if ((time_res = xlat_time_res_attr(name->vb_strvalue)) == NULL) {
1935 RDEBUG("Unknown data type '%s'", name->vb_strvalue);
1936 return XLAT_ACTION_FAIL;
1937 }
1938
1940 }
1941 }
1942
1943 (void) fr_value_box_list_pop_head(args);
1944
1945 /*
1946 * When we cast nothing to a string / octets, the result is an empty string/octets.
1947 */
1948 if (unlikely(!fr_value_box_list_head(args))) {
1949 if ((type == FR_TYPE_STRING) || (type == FR_TYPE_OCTETS)) {
1950 fr_value_box_t *dst;
1951
1952 MEM(dst = fr_value_box_alloc(ctx, type, NULL));
1953 fr_dcursor_append(out, dst);
1954 VALUE_BOX_LIST_VERIFY((fr_value_box_list_t *)out->dlist);
1955
1956 return XLAT_ACTION_DONE;
1957 }
1958
1959 RDEBUG("No data for cast to '%s'", fr_type_to_str(type));
1960 return XLAT_ACTION_FAIL;
1961 }
1962
1963 /*
1964 * Cast to string means *print* to string.
1965 */
1966 if (type == FR_TYPE_STRING) {
1967 fr_sbuff_t *agg;
1968 fr_value_box_t *dst;
1969
1971
1972 FR_SBUFF_TALLOC_THREAD_LOCAL(&agg, 256, SIZE_MAX);
1973
1974 MEM(dst = fr_value_box_alloc_null(ctx));
1975 if (fr_value_box_list_concat_as_string(NULL, NULL, agg, args, NULL, 0, NULL,
1976 FR_VALUE_BOX_LIST_FREE_BOX, 0, true) < 0) {
1977 RPEDEBUG("Failed concatenating string");
1978 return XLAT_ACTION_FAIL;
1979 }
1980
1981 fr_value_box_bstrndup(dst, dst, NULL, fr_sbuff_start(agg), fr_sbuff_used(agg), false);
1982 fr_dcursor_append(out, dst);
1983 VALUE_BOX_LIST_VERIFY((fr_value_box_list_t *)out->dlist);
1984
1985 return XLAT_ACTION_DONE;
1986 }
1987
1988 /*
1989 * Copy inputs to outputs, casting them along the way.
1990 */
1991 arg = NULL;
1992 while ((arg = fr_value_box_list_next(args, arg)) != NULL) {
1993 fr_value_box_t *vb, *p;
1994
1995 fr_assert(arg->type == FR_TYPE_GROUP);
1996
1997 vb = fr_value_box_list_head(&arg->vb_group);
1998 while (vb) {
1999 p = fr_value_box_list_remove(&arg->vb_group, vb);
2000
2001 if (fr_value_box_cast_in_place(vb, vb, type, time_res) < 0) {
2002 RPEDEBUG("Failed casting %pV to data type '%s'", vb, fr_type_to_str(type));
2003 return XLAT_ACTION_FAIL;
2004 }
2006 vb = fr_value_box_list_next(&arg->vb_group, p);
2007 }
2008 }
2009 VALUE_BOX_LIST_VERIFY((fr_value_box_list_t *)out->dlist);
2010
2011 return XLAT_ACTION_DONE;
2012}
2013
2015 { .required = true, .type = FR_TYPE_VOID },
2016 { .concat = true, .type = FR_TYPE_STRING },
2018};
2019
2020/** Concatenate string representation of values of given attributes using separator
2021 *
2022 * First argument of is the list of attributes to concatenate, followed
2023 * by an optional separator
2024 *
2025 * Example:
2026@verbatim
2027%concat(%{request.[*]}, ',') == "<attr1value>,<attr2value>,<attr3value>,..."
2028%concat(%{Tmp-String-0[*]}, '. ') == "<str1value>. <str2value>. <str3value>. ..."
2029%concat(%join(%{User-Name}, %{Calling-Station-Id}), ', ') == "bob, aa:bb:cc:dd:ee:ff"
2030@endverbatim
2031 *
2032 * @ingroup xlat_functions
2033 */
2035 UNUSED xlat_ctx_t const *xctx,
2036 request_t *request, fr_value_box_list_t *args)
2037{
2038 fr_value_box_t *result;
2039 fr_value_box_t *list;
2040 fr_value_box_t *separator;
2041 fr_value_box_list_t *to_concat;
2042 char *buff;
2043 char const *sep;
2044
2045 XLAT_ARGS(args, &list, &separator);
2046
2047 sep = (separator) ? separator->vb_strvalue : "";
2048 to_concat = &list->vb_group;
2049
2050 result = fr_value_box_alloc(ctx, FR_TYPE_STRING, NULL);
2051 if (!result) {
2052 error:
2053 RPEDEBUG("Failed concatenating input");
2054 return XLAT_ACTION_FAIL;
2055 }
2056
2057 buff = fr_value_box_list_aprint(result, to_concat, sep, NULL);
2058 if (!buff) goto error;
2059
2061
2062 fr_dcursor_append(out, result);
2063
2064 return XLAT_ACTION_DONE;
2065}
2066
2068 { .required = true, .type = FR_TYPE_OCTETS },
2070};
2071
2072/** Print data as hex, not as VALUE.
2073 *
2074 * Example:
2075@verbatim
2076%hex("foobar") == "666f6f626172"
2077@endverbatim
2078 *
2079 * @see #xlat_func_bin
2080 *
2081 * @ingroup xlat_functions
2082 */
2084 UNUSED xlat_ctx_t const *xctx,
2085 UNUSED request_t *request, fr_value_box_list_t *args)
2086{
2087 char *new_buff;
2088 fr_value_box_t *list, *bin;
2089
2090 XLAT_ARGS(args, &list);
2091
2092 while ((bin = fr_value_box_list_pop_head(&list->vb_group))) {
2093 /*
2094 * Use existing box, but with new buffer
2095 */
2096 MEM(new_buff = talloc_zero_array(bin, char, (bin->vb_length * 2) + 1));
2097 if (bin->vb_length) {
2098 fr_base16_encode(&FR_SBUFF_OUT(new_buff, (bin->vb_length * 2) + 1),
2099 &FR_DBUFF_TMP(bin->vb_octets, bin->vb_length));
2101 fr_value_box_strdup_shallow(bin, NULL, new_buff, bin->tainted);
2102 /*
2103 * Zero length binary > zero length hex string
2104 */
2105 } else {
2107 fr_value_box_strdup(bin, bin, NULL, "", bin->tainted);
2108 }
2109 fr_dcursor_append(out, bin);
2110 }
2111
2112 return XLAT_ACTION_DONE;
2113}
2114
2119
2120static xlat_action_t xlat_hmac(TALLOC_CTX *ctx, fr_dcursor_t *out,
2121 fr_value_box_list_t *args, uint8_t *digest, int digest_len, hmac_type type)
2122{
2123 fr_value_box_t *vb, *data, *key;
2124
2125 XLAT_ARGS(args, &data, &key);
2126
2127 if (type == HMAC_MD5) {
2128 /* coverity[dereference] */
2129 fr_hmac_md5(digest, data->vb_octets, data->vb_length, key->vb_octets, key->vb_length);
2130 } else if (type == HMAC_SHA1) {
2131 /* coverity[dereference] */
2132 fr_hmac_sha1(digest, data->vb_octets, data->vb_length, key->vb_octets, key->vb_length);
2133 }
2134
2135 MEM(vb = fr_value_box_alloc_null(ctx));
2136 fr_value_box_memdup(vb, vb, NULL, digest, digest_len, false);
2137
2139
2140 return XLAT_ACTION_DONE;
2141}
2142
2144 { .required = true, .concat = true, .type = FR_TYPE_STRING },
2145 { .required = true, .concat = true, .type = FR_TYPE_STRING },
2147};
2148
2149/** Generate the HMAC-MD5 of a string or attribute
2150 *
2151 * Example:
2152@verbatim
2153%hmacmd5('foo', 'bar') == "0x31b6db9e5eb4addb42f1a6ca07367adc"
2154@endverbatim
2155 *
2156 * @ingroup xlat_functions
2157 */
2159 UNUSED xlat_ctx_t const *xctx,
2160 UNUSED request_t *request, fr_value_box_list_t *in)
2161{
2162 uint8_t digest[MD5_DIGEST_LENGTH];
2163 return xlat_hmac(ctx, out, in, digest, MD5_DIGEST_LENGTH, HMAC_MD5);
2164}
2165
2166
2167/** Generate the HMAC-SHA1 of a string or attribute
2168 *
2169 * Example:
2170@verbatim
2171%hmacsha1('foo', 'bar') == "0x85d155c55ed286a300bd1cf124de08d87e914f3a"
2172@endverbatim
2173 *
2174 * @ingroup xlat_functions
2175 */
2177 UNUSED xlat_ctx_t const *xctx,
2178 UNUSED request_t *request, fr_value_box_list_t *in)
2179{
2181 return xlat_hmac(ctx, out, in, digest, SHA1_DIGEST_LENGTH, HMAC_SHA1);
2182}
2183
2185 { .required = true, .type = FR_TYPE_VOID },
2186 { .variadic = XLAT_ARG_VARIADIC_EMPTY_SQUASH, .type = FR_TYPE_VOID },
2188};
2189
2190/** Join a series of arguments to form a single list
2191 *
2192 * null boxes are not preserved.
2193 */
2195 UNUSED xlat_ctx_t const *xctx,
2196 UNUSED request_t *request, fr_value_box_list_t *in)
2197{
2199 fr_assert(arg->type == FR_TYPE_GROUP);
2200
2201 fr_value_box_list_foreach_safe(&arg->vb_group, vb) {
2202 fr_value_box_list_remove(&arg->vb_group, vb);
2204 }}
2205 }
2206 return XLAT_ACTION_DONE;
2207}
2208
2209static void ungroup(fr_dcursor_t *out, fr_value_box_list_t *in)
2210{
2211 fr_value_box_t *vb;
2212
2213 while ((vb = fr_value_box_list_pop_head(in)) != NULL) {
2214 if (vb->type != FR_TYPE_GROUP) {
2216 continue;
2217 }
2218 talloc_free(vb);
2219 }
2220}
2221
2222/** Ungroups all of its arguments into one flat list.
2223 *
2224 */
2226 UNUSED xlat_ctx_t const *xctx,
2227 UNUSED request_t *request, fr_value_box_list_t *in)
2228{
2229 fr_value_box_t *arg = NULL;
2230
2231 while ((arg = fr_value_box_list_next(in, arg)) != NULL) {
2232 fr_assert(arg->type == FR_TYPE_GROUP);
2233
2234 ungroup(out, &arg->vb_group);
2235 }
2236 return XLAT_ACTION_DONE;
2237}
2238
2240 { .single = true, .variadic = XLAT_ARG_VARIADIC_EMPTY_KEEP, .type = FR_TYPE_VOID },
2242};
2243
2244/** Return the on-the-wire size of the boxes in bytes
2245 *
2246 * skips null values
2247 *
2248 * Example:
2249@verbatim
2250%length(foobar) == 6
2251%length(%bin("0102030005060708")) == 8
2252@endverbatim
2253 *
2254 * @see #xlat_func_strlen
2255 *
2256 * @ingroup xlat_functions
2257 */
2259 UNUSED xlat_ctx_t const *xctx,
2260 UNUSED request_t *request, fr_value_box_list_t *in)
2261
2262{
2265
2266 MEM(my = fr_value_box_alloc(ctx, FR_TYPE_SIZE, NULL));
2267 if (!fr_type_is_null(vb->type)) my->vb_size = fr_value_box_network_length(vb);
2269 }
2270
2271 return XLAT_ACTION_DONE;
2272}
2273
2274
2276 { .concat = true, .type = FR_TYPE_OCTETS },
2278};
2279
2280/** Calculate the MD4 hash of a string or attribute.
2281 *
2282 * Example:
2283@verbatim
2284%md4("foo") == "0ac6700c491d70fb8650940b1ca1e4b2"
2285@endverbatim
2286 *
2287 * @ingroup xlat_functions
2288 */
2290 UNUSED xlat_ctx_t const *xctx,
2291 UNUSED request_t *request, fr_value_box_list_t *args)
2292{
2293 uint8_t digest[MD4_DIGEST_LENGTH];
2294 fr_value_box_t *vb;
2295 fr_value_box_t *in_head;
2296
2297 XLAT_ARGS(args, &in_head);
2298
2299 if (in_head) {
2300 fr_md4_calc(digest, in_head->vb_octets, in_head->vb_length);
2301 } else {
2302 /* Digest of empty string */
2303 fr_md4_calc(digest, NULL, 0);
2304 }
2305
2306 MEM(vb = fr_value_box_alloc_null(ctx));
2307 fr_value_box_memdup(vb, vb, NULL, digest, sizeof(digest), false);
2308
2310 VALUE_BOX_LIST_VERIFY((fr_value_box_list_t *)out->dlist);
2311
2312 return XLAT_ACTION_DONE;
2313}
2314
2316 { .concat = true, .type = FR_TYPE_OCTETS },
2318};
2319
2320/** Calculate the MD5 hash of a string or attribute.
2321 *
2322 * Example:
2323@verbatim
2324%md5("foo") == "acbd18db4cc2f85cedef654fccc4a4d8"
2325@endverbatim
2326 *
2327 * @ingroup xlat_functions
2328 */
2330 UNUSED xlat_ctx_t const *xctx,
2331 UNUSED request_t *request, fr_value_box_list_t *args)
2332{
2333 uint8_t digest[MD5_DIGEST_LENGTH];
2334 fr_value_box_t *vb;
2335 fr_value_box_t *in_head;
2336
2337 XLAT_ARGS(args, &in_head);
2338
2339 if (in_head) {
2340 fr_md5_calc(digest, in_head->vb_octets, in_head->vb_length);
2341 } else {
2342 /* Digest of empty string */
2343 fr_md5_calc(digest, NULL, 0);
2344 }
2345
2346 MEM(vb = fr_value_box_alloc_null(ctx));
2347 fr_value_box_memdup(vb, vb, NULL, digest, sizeof(digest), false);
2348
2350
2351 return XLAT_ACTION_DONE;
2352}
2353
2354
2356 { .required = true, .single = true, .type = FR_TYPE_STRING },
2358};
2359
2360/** Encode attributes as a series of string attribute/value pairs
2361 *
2362 * This is intended to serialize one or more attributes as a comma
2363 * delimited string.
2364 *
2365 * Example:
2366@verbatim
2367%pairs(request.[*]) == 'User-Name = "foo"User-Password = "bar"'
2368%concat(%pairs(request.[*]), ', ') == 'User-Name = "foo", User-Password = "bar"'
2369@endverbatim
2370 *
2371 * @see #xlat_func_concat
2372 *
2373 * @ingroup xlat_functions
2374 */
2376 UNUSED xlat_ctx_t const *xctx,
2377 request_t *request, fr_value_box_list_t *args)
2378{
2379 tmpl_t *vpt = NULL;
2380 fr_dcursor_t cursor;
2382 fr_value_box_t *vb;
2383 fr_value_box_t *in_head;
2384
2385 fr_pair_t *vp;
2386
2387 XLAT_ARGS(args, &in_head);
2388
2389 if (tmpl_afrom_attr_str(ctx, NULL, &vpt, in_head->vb_strvalue,
2390 &(tmpl_rules_t){
2391 .attr = {
2392 .dict_def = request->dict,
2393 .list_def = request_attr_request,
2394 .allow_wildcard = true,
2395 .prefix = TMPL_ATTR_REF_PREFIX_AUTO
2396 }
2397 }) <= 0) {
2398 RPEDEBUG("Invalid input");
2399 return XLAT_ACTION_FAIL;
2400 }
2401
2402 for (vp = tmpl_dcursor_init(NULL, NULL, &cc, &cursor, request, vpt);
2403 vp;
2404 vp = fr_dcursor_next(&cursor)) {
2405 char *buff;
2406
2407 MEM(vb = fr_value_box_alloc_null(ctx));
2408 if (unlikely(fr_pair_aprint(vb, &buff, NULL, vp) < 0)) {
2409 RPEDEBUG("Failed printing pair");
2410 talloc_free(vb);
2411 tmpl_dcursor_clear(&cc);
2412 return XLAT_ACTION_FAIL;
2413 }
2414
2415 fr_value_box_bstrdup_buffer_shallow(NULL, vb, NULL, buff, false);
2417 }
2418 tmpl_dcursor_clear(&cc);
2420
2421 return XLAT_ACTION_DONE;
2422}
2423
2425 { .required = true, .single = true, .type = FR_TYPE_UINT32 },
2427};
2428
2429/** Generate a random integer value
2430 *
2431 * For "N = %rand(MAX)", 0 <= N < MAX
2432 *
2433 * Example:
2434@verbatim
2435%rand(100) == 42
2436@endverbatim
2437 *
2438 * @ingroup xlat_functions
2439 */
2441 UNUSED xlat_ctx_t const *xctx,
2442 UNUSED request_t *request, fr_value_box_list_t *in)
2443{
2444 int64_t result;
2445 fr_value_box_t *vb;
2446 fr_value_box_t *in_head = fr_value_box_list_head(in);
2447
2448 result = in_head->vb_uint32;
2449
2450 /* Make sure it isn't too big */
2451 if (result > (1 << 30)) result = (1 << 30);
2452
2453 result *= fr_rand(); /* 0..2^32-1 */
2454 result >>= 32;
2455
2456 MEM(vb = fr_value_box_alloc(ctx, FR_TYPE_UINT64, NULL));
2457 vb->vb_uint64 = result;
2458
2460
2461 return XLAT_ACTION_DONE;
2462}
2463
2465 { .required = true, .concat = true, .type = FR_TYPE_STRING },
2467};
2468
2469/** Generate a string of random chars
2470 *
2471 * Build strings of random chars, useful for generating tokens and passcodes
2472 * Format similar to String::Random.
2473 *
2474 * Format characters may include the following, and may be
2475 * preceded by a repetition count:
2476 * - "c" lowercase letters
2477 * - "C" uppercase letters
2478 * - "n" numbers
2479 * - "a" alphanumeric
2480 * - "!" punctuation
2481 * - "." alphanumeric + punctuation
2482 * - "s" alphanumeric + "./"
2483 * - "o" characters suitable for OTP (easily confused removed)
2484 * - "b" binary data
2485 *
2486 * Example:
2487@verbatim
2488%randstr("CCCC!!cccnnn") == "IPFL>{saf874"
2489%randstr("42o") == "yHdupUwVbdHprKCJRYfGbaWzVwJwUXG9zPabdGAhM9"
2490%hex(%randstr("bbbb")) == "a9ce04f3"
2491%hex(%randstr("8b")) == "fe165529f9f66839"
2492@endverbatim
2493 * @ingroup xlat_functions
2494 */
2496 UNUSED xlat_ctx_t const *xctx,
2497 request_t *request, fr_value_box_list_t *args)
2498{
2499 /*
2500 * Lookup tables for randstr char classes
2501 */
2502 static char randstr_punc[] = "!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~";
2503 static char randstr_salt[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmopqrstuvwxyz/.";
2504
2505 /*
2506 * Characters humans rarely confuse. Reduces char set considerably
2507 * should only be used for things such as one time passwords.
2508 */
2509 static char randstr_otp[] = "469ACGHJKLMNPQRUVWXYabdfhijkprstuvwxyz";
2510
2511 char const *p, *start, *end;
2512 char *endptr;
2513 char *buff_p;
2514 unsigned int result;
2515 unsigned int reps;
2516 size_t outlen = 0;
2517 fr_value_box_t* vb;
2518 fr_value_box_t *in_head;
2519
2520 XLAT_ARGS(args, &in_head);
2521
2522 /** Max repetitions of a single character class
2523 *
2524 */
2525#define REPETITION_MAX 1024
2526
2527 start = p = in_head->vb_strvalue;
2528 end = p + in_head->vb_length;
2529
2530 /*
2531 * Calculate size of output
2532 */
2533 while (p < end) {
2534 /*
2535 * Repetition modifiers.
2536 *
2537 * We limit it to REPETITION_MAX, because we don't want
2538 * utter stupidity.
2539 */
2540 if (isdigit((uint8_t) *p)) {
2541 reps = strtol(p, &endptr, 10);
2542 if (reps > REPETITION_MAX) reps = REPETITION_MAX;
2543 outlen += reps;
2544 p = endptr;
2545 } else {
2546 outlen++;
2547 }
2548 p++;
2549 }
2550
2551 MEM(vb = fr_value_box_alloc_null(ctx));
2552 MEM(fr_value_box_bstr_alloc(vb, &buff_p, vb, NULL, outlen, false) == 0);
2553
2554 /* Reset p to start position */
2555 p = start;
2556
2557 while (p < end) {
2558 size_t i;
2559
2560 if (isdigit((uint8_t) *p)) {
2561 reps = strtol(p, &endptr, 10);
2562 if (reps > REPETITION_MAX) {
2563 reps = REPETITION_MAX;
2564 RMARKER(L_WARN, L_DBG_LVL_2, start, start - p,
2565 "Forcing repetition to %u", (unsigned int)REPETITION_MAX);
2566 }
2567 p = endptr;
2568 } else {
2569 reps = 1;
2570 }
2571
2572 for (i = 0; i < reps; i++) {
2573 result = fr_rand();
2574 switch (*p) {
2575 /*
2576 * Lowercase letters
2577 */
2578 case 'c':
2579 *buff_p++ = 'a' + (result % 26);
2580 break;
2581
2582 /*
2583 * Uppercase letters
2584 */
2585 case 'C':
2586 *buff_p++ = 'A' + (result % 26);
2587 break;
2588
2589 /*
2590 * Numbers
2591 */
2592 case 'n':
2593 *buff_p++ = '0' + (result % 10);
2594 break;
2595
2596 /*
2597 * Alpha numeric
2598 */
2599 case 'a':
2600 *buff_p++ = randstr_salt[result % (sizeof(randstr_salt) - 3)];
2601 break;
2602
2603 /*
2604 * Punctuation
2605 */
2606 case '!':
2607 *buff_p++ = randstr_punc[result % (sizeof(randstr_punc) - 1)];
2608 break;
2609
2610 /*
2611 * Alpha numeric + punctuation
2612 */
2613 case '.':
2614 *buff_p++ = '!' + (result % 95);
2615 break;
2616
2617 /*
2618 * Alpha numeric + salt chars './'
2619 */
2620 case 's':
2621 *buff_p++ = randstr_salt[result % (sizeof(randstr_salt) - 1)];
2622 break;
2623
2624 /*
2625 * Chars suitable for One Time Password tokens.
2626 * Alpha numeric with easily confused char pairs removed.
2627 */
2628 case 'o':
2629 *buff_p++ = randstr_otp[result % (sizeof(randstr_otp) - 1)];
2630 break;
2631
2632 /*
2633 * Binary data - Copy between 1-4 bytes at a time
2634 */
2635 case 'b':
2636 {
2637 size_t copy = (reps - i) > sizeof(result) ? sizeof(result) : reps - i;
2638
2639 memcpy(buff_p, (uint8_t *)&result, copy);
2640 buff_p += copy;
2641 i += (copy - 1); /* Loop +1 */
2642 }
2643 break;
2644
2645 default:
2646 REDEBUG("Invalid character class '%c'", *p);
2647 talloc_free(vb);
2648
2649 return XLAT_ACTION_FAIL;
2650 }
2651 }
2652
2653 p++;
2654 }
2655
2656 *buff_p++ = '\0';
2657
2659
2660 return XLAT_ACTION_DONE;
2661}
2662
2663
2664#if defined(HAVE_REGEX_PCRE) || defined(HAVE_REGEX_PCRE2)
2665/** Get named subcapture value from previous regex
2666 *
2667 * Example:
2668@verbatim
2669if ("foo" =~ /^(?<name>.*)/) {
2670 noop
2671}
2672%regex(name) == "foo"
2673@endverbatim
2674 *
2675 * @ingroup xlat_functions
2676 */
2677static xlat_action_t xlat_func_regex(TALLOC_CTX *ctx, fr_dcursor_t *out,
2678 UNUSED xlat_ctx_t const *xctx,
2679 request_t *request, fr_value_box_list_t *in)
2680{
2681 fr_value_box_t *in_head = fr_value_box_list_head(in);
2682
2683 /*
2684 * Find the first child of the first argument group
2685 */
2686 fr_value_box_t *arg = fr_value_box_list_head(&in_head->vb_group);
2687
2688 /*
2689 * Return the complete capture if no other capture is specified
2690 */
2691 if (!arg) {
2692 fr_value_box_t *vb;
2693 char *p;
2694
2695 MEM(vb = fr_value_box_alloc_null(ctx));
2696 if (regex_request_to_sub(vb, &p, request, 0) < 0) {
2697 REDEBUG2("No previous regex capture");
2698 talloc_free(vb);
2699 return XLAT_ACTION_FAIL;
2700 }
2701
2702 fr_assert(p);
2703 fr_value_box_bstrdup_buffer_shallow(NULL, vb, NULL, p, false);
2705
2706 return XLAT_ACTION_DONE;
2707 }
2708
2709 switch (arg->type) {
2710 /*
2711 * If the input is an integer value then get an
2712 * arbitrary subcapture index.
2713 */
2714 case FR_TYPE_NUMERIC:
2715 {
2716 fr_value_box_t idx;
2717 fr_value_box_t *vb;
2718 char *p;
2719
2720 if (fr_value_box_list_next(in, in_head)) {
2721 REDEBUG("Only one subcapture argument allowed");
2722 return XLAT_ACTION_FAIL;
2723 }
2724
2725 if (fr_value_box_cast(NULL, &idx, FR_TYPE_UINT32, NULL, arg) < 0) {
2726 RPEDEBUG("Bad subcapture index");
2727 return XLAT_ACTION_FAIL;
2728 }
2729
2730 MEM(vb = fr_value_box_alloc_null(ctx));
2731 if (regex_request_to_sub(vb, &p, request, idx.vb_uint32) < 0) {
2732 REDEBUG2("No previous numbered regex capture group");
2733 talloc_free(vb);
2734 return XLAT_ACTION_FAIL;
2735 }
2736 fr_assert(p);
2737 fr_value_box_bstrdup_buffer_shallow(NULL, vb, NULL, p, false);
2739
2740 return XLAT_ACTION_DONE;
2741 }
2742
2743 default:
2744 {
2745 fr_value_box_t *vb;
2746 char *p;
2747
2748 /*
2749 * Concatenate all input
2750 */
2752 arg, &in_head->vb_group, FR_TYPE_STRING,
2754 SIZE_MAX) < 0) {
2755 RPEDEBUG("Failed concatenating input");
2756 return XLAT_ACTION_FAIL;
2757 }
2758
2759 MEM(vb = fr_value_box_alloc_null(ctx));
2760 if (regex_request_to_sub_named(vb, &p, request, arg->vb_strvalue) < 0) {
2761 REDEBUG2("No previous named regex capture group");
2762 talloc_free(vb);
2763 return XLAT_ACTION_FAIL;
2764 }
2765
2766 fr_assert(p);
2767 fr_value_box_bstrdup_buffer_shallow(NULL, vb, NULL, p, false);
2769
2770 return XLAT_ACTION_DONE;
2771 }
2772 }
2773}
2774#endif
2775
2777 { .concat = true, .type = FR_TYPE_OCTETS },
2779};
2780
2781/** Calculate the SHA1 hash of a string or attribute.
2782 *
2783 * Example:
2784@verbatim
2785%sha1(foo) == "0beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a33"
2786@endverbatim
2787 *
2788 * @ingroup xlat_functions
2789 */
2791 UNUSED xlat_ctx_t const *xctx,
2792 UNUSED request_t *request, fr_value_box_list_t *args)
2793{
2795 fr_sha1_ctx sha1_ctx;
2796 fr_value_box_t *vb;
2797 fr_value_box_t *in_head;
2798
2799 XLAT_ARGS(args, &in_head);
2800
2801 fr_sha1_init(&sha1_ctx);
2802 if (in_head) {
2803 fr_sha1_update(&sha1_ctx, in_head->vb_octets, in_head->vb_length);
2804 } else {
2805 /* sha1 of empty string */
2806 fr_sha1_update(&sha1_ctx, NULL, 0);
2807 }
2808 fr_sha1_final(digest, &sha1_ctx);
2809
2810 MEM(vb = fr_value_box_alloc_null(ctx));
2811 fr_value_box_memdup(vb, vb, NULL, digest, sizeof(digest), false);
2812
2814
2815 return XLAT_ACTION_DONE;
2816}
2817
2818/** Calculate any digest supported by OpenSSL EVP_MD
2819 *
2820 * Example:
2821@verbatim
2822%sha2_256(foo) == "0beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a33"
2823@endverbatim
2824 *
2825 * @ingroup xlat_functions
2826 */
2827#ifdef HAVE_OPENSSL_EVP_H
2828static xlat_action_t xlat_evp_md(TALLOC_CTX *ctx, fr_dcursor_t *out,
2829 UNUSED xlat_ctx_t const *xctx,
2830 UNUSED request_t *request, fr_value_box_list_t *args, EVP_MD const *md)
2831{
2832 uint8_t digest[EVP_MAX_MD_SIZE];
2833 unsigned int digestlen;
2834 EVP_MD_CTX *md_ctx;
2835 fr_value_box_t *vb;
2836 fr_value_box_t *in_head;
2837
2838 XLAT_ARGS(args, &in_head);
2839
2840 md_ctx = EVP_MD_CTX_create();
2841 EVP_DigestInit_ex(md_ctx, md, NULL);
2842 if (in_head) {
2843 EVP_DigestUpdate(md_ctx, in_head->vb_octets, in_head->vb_length);
2844 } else {
2845 EVP_DigestUpdate(md_ctx, NULL, 0);
2846 }
2847 EVP_DigestFinal_ex(md_ctx, digest, &digestlen);
2848 EVP_MD_CTX_destroy(md_ctx);
2849
2850 MEM(vb = fr_value_box_alloc_null(ctx));
2851 fr_value_box_memdup(vb, vb, NULL, digest, digestlen, false);
2852
2854
2855 return XLAT_ACTION_DONE;
2856}
2857
2858# define EVP_MD_XLAT(_md, _md_func) \
2859static xlat_action_t xlat_func_##_md(TALLOC_CTX *ctx, fr_dcursor_t *out,\
2860 xlat_ctx_t const *xctx, \
2861 request_t *request,\
2862 fr_value_box_list_t *in)\
2863{\
2864 return xlat_evp_md(ctx, out, xctx, request, in, EVP_##_md_func());\
2865}
2866
2867EVP_MD_XLAT(sha2_224, sha224)
2868EVP_MD_XLAT(sha2_256, sha256)
2869EVP_MD_XLAT(sha2_384, sha384)
2870EVP_MD_XLAT(sha2_512, sha512)
2871
2872/*
2873 * OpenWRT's OpenSSL library doesn't contain these by default
2874 */
2875#ifdef HAVE_EVP_BLAKE2S256
2876EVP_MD_XLAT(blake2s_256, blake2s256)
2877#endif
2878
2879#ifdef HAVE_EVP_BLAKE2B512
2880EVP_MD_XLAT(blake2b_512, blake2b512)
2881#endif
2882
2883EVP_MD_XLAT(sha3_224, sha3_224)
2884EVP_MD_XLAT(sha3_256, sha3_256)
2885EVP_MD_XLAT(sha3_384, sha3_384)
2886EVP_MD_XLAT(sha3_512, sha3_512)
2887#endif
2888
2889
2891 { .required = true, .concat = true, .type = FR_TYPE_STRING },
2893};
2894
2895/** Print data as string, if possible.
2896 *
2897 * Concat and cast one or more input boxes to a single output box string.
2898 *
2899 * @ingroup xlat_functions
2900 */
2902 UNUSED xlat_ctx_t const *xctx,
2903 UNUSED request_t *request, fr_value_box_list_t *in)
2904{
2905 fr_value_box_t *in_head = fr_value_box_list_pop_head(in);
2906
2907 /*
2908 * Casting and concat is done by arg processing
2909 * so just move the value box to the output
2910 */
2911 fr_dcursor_append(out, in_head);
2912
2913 return XLAT_ACTION_DONE;
2914}
2915
2916
2918 { .concat = true, .type = FR_TYPE_STRING },
2920};
2921
2922/** Print length of given string
2923 *
2924 * Example:
2925@verbatim
2926%strlen(foo) == 3
2927@endverbatim
2928 *
2929 * @see #xlat_func_length
2930 *
2931 * @ingroup xlat_functions
2932 */
2934 UNUSED xlat_ctx_t const *xctx,
2935 UNUSED request_t *request, fr_value_box_list_t *args)
2936{
2937 fr_value_box_t *vb;
2938 fr_value_box_t *in_head;
2939
2940 XLAT_ARGS(args, &in_head);
2941
2942 MEM(vb = fr_value_box_alloc(ctx, FR_TYPE_SIZE, NULL));
2943
2944 if (!in_head) {
2945 vb->vb_size = 0;
2946 } else {
2947 vb->vb_size = strlen(in_head->vb_strvalue);
2948 }
2949
2951
2952 return XLAT_ACTION_DONE;
2953}
2954
2956 { .concat = true, .type = FR_TYPE_STRING },
2957 { .single = true, .type = FR_TYPE_BOOL },
2959};
2960
2961/** Return whether a string has only printable chars
2962 *
2963 * This function returns true if the input string contains UTF8 sequences and printable chars.
2964 *
2965 * @note "\t" and " " are considered unprintable chars, unless the second argument(relaxed) is true.
2966 *
2967 * Example:
2968@verbatim
2969%str.printable("🍉abcdef🍓") == true
2970%str.printable("\000\n\r\t") == false
2971%str.printable("\t abcd", yes) == true
2972@endverbatim
2973 *
2974 * @ingroup xlat_functions
2975 */
2977 UNUSED xlat_ctx_t const *xctx,
2978 UNUSED request_t *request, fr_value_box_list_t *args)
2979{
2980 fr_value_box_t *vb;
2981 fr_value_box_t *str;
2982 fr_value_box_t *relaxed_vb;
2983 uint8_t const *p, *end;
2984 bool relaxed = false;
2985
2986 XLAT_ARGS(args, &str, &relaxed_vb);
2987
2988 if (relaxed_vb) relaxed = relaxed_vb->vb_bool;
2989
2990 p = (uint8_t const *)str->vb_strvalue;
2991 end = p + str->vb_length;
2992
2993 MEM(vb = fr_value_box_alloc(ctx, FR_TYPE_BOOL, NULL));
2995 vb->vb_bool = false;
2996
2997 do {
2998 size_t clen;
2999
3000 if ((*p < '!') &&
3001 (!relaxed || ((*p != '\t') && (*p != ' ')))) return XLAT_ACTION_DONE;
3002
3003 if (*p == 0x7f) return XLAT_ACTION_DONE;
3004
3005 clen = fr_utf8_char(p, end - p);
3006 if (clen == 0) return XLAT_ACTION_DONE;
3007 p += clen;
3008 } while (p < end);
3009
3010 vb->vb_bool = true;
3011
3012 return XLAT_ACTION_DONE;
3013}
3014
3016 { .concat = true, .type = FR_TYPE_STRING },
3018};
3019
3020/** Return whether a string is valid UTF-8
3021 *
3022 * This function returns true if the input string is valid UTF-8, false otherwise.
3023 *
3024 * Example:
3025@verbatim
3026%str.utf8(🍉🥝🍓) == true
3027%str.utf8(🍉\xff🍓) == false
3028@endverbatim
3029 *
3030 * @ingroup xlat_functions
3031 */
3033 UNUSED xlat_ctx_t const *xctx,
3034 UNUSED request_t *request, fr_value_box_list_t *args)
3035{
3036 fr_value_box_t *vb;
3037 fr_value_box_t *in_head;
3038
3039 XLAT_ARGS(args, &in_head);
3040
3041 MEM(vb = fr_value_box_alloc(ctx, FR_TYPE_BOOL, NULL));
3042 vb->vb_bool = (fr_utf8_str((uint8_t const *)in_head->vb_strvalue,
3043 in_head->vb_length) >= 0);
3044
3046
3047 return XLAT_ACTION_DONE;
3048}
3049
3051 { .single = true, .required = true, .type = FR_TYPE_VOID },
3052 { .single = true, .required = true, .type = FR_TYPE_INT32 },
3053 { .single = true, .type = FR_TYPE_INT32 },
3055};
3056
3057/** Extract a substring from string / octets data
3058 *
3059 * Non string / octets data is cast to a string.
3060 *
3061 * Second parameter is start position, optional third parameter is length
3062 * Negative start / length count from RHS of data.
3063 *
3064 * Example: (User-Name = "hello")
3065@verbatim
3066%substr(&User-Name, 1, 3) == 'ell'
3067@endverbatim
3068 *
3069 * @ingroup xlat_functions
3070 */
3071static xlat_action_t xlat_func_substr(TALLOC_CTX *ctx, fr_dcursor_t *out, UNUSED xlat_ctx_t const *xctx,
3072 request_t *request, fr_value_box_list_t *args)
3073{
3074 fr_value_box_t *in = NULL, *start_vb, *len_vb, *vb;
3075 int32_t start, end, len;
3076
3077 XLAT_ARGS(args, &in, &start_vb, &len_vb);
3078
3079 switch (in->type) {
3080 case FR_TYPE_OCTETS:
3081 case FR_TYPE_STRING:
3082 break;
3083
3084 default:
3086 RPEDEBUG("Failed casting value to string");
3087 return XLAT_ACTION_FAIL;
3088 }
3089 break;
3090 }
3091
3092 if (start_vb->vb_int32 > (int32_t)in->vb_length) return XLAT_ACTION_DONE;
3093
3094 if (start_vb->vb_int32 < 0) {
3095 start = in->vb_length + start_vb->vb_int32;
3096 if (start < 0) start = 0;
3097 } else {
3098 start = start_vb->vb_int32;
3099 }
3100
3101 if (len_vb) {
3102 if (len_vb->vb_int32 < 0) {
3103 end = in->vb_length + len_vb->vb_int32;
3104 if (end < 0) return XLAT_ACTION_DONE;
3105 } else {
3106 end = start + len_vb->vb_int32;
3107 if (end > (int32_t)in->vb_length) end = in->vb_length;
3108 }
3109 } else {
3110 end = in->vb_length;
3111 }
3112
3113 if (start >= end) return XLAT_ACTION_DONE;
3114
3115 MEM(vb = fr_value_box_alloc(ctx, in->type, NULL));
3116
3117 len = end - start;
3118 switch (in->type) {
3119 case FR_TYPE_STRING:
3120 fr_value_box_bstrndup(vb, vb, NULL, &in->vb_strvalue[start], len, in->tainted);
3121 break;
3122 case FR_TYPE_OCTETS:
3123 {
3124 uint8_t *buf;
3125 fr_value_box_mem_alloc(vb, &buf, vb, NULL, len, in->tainted);
3126 memcpy(buf, &in->vb_octets[start], len);
3127 }
3128 break;
3129 default:
3130 fr_assert(0);
3131 }
3133
3134 return XLAT_ACTION_DONE;
3135}
3136
3137#ifdef HAVE_REGEX_PCRE2
3138/** Cache statically compiled expressions
3139 */
3140typedef struct {
3141 regex_t *pattern;
3142 fr_regex_flags_t flags;
3143} xlat_subst_regex_inst_t;
3144
3145/** Pre-compile regexes where possible
3146 */
3147static int xlat_instantiate_subst_regex(xlat_inst_ctx_t const *xctx)
3148{
3149 xlat_subst_regex_inst_t *inst = talloc_get_type_abort(xctx->inst, xlat_subst_regex_inst_t);
3150 xlat_exp_t *patt_exp;
3151 fr_sbuff_t sbuff;
3152 fr_sbuff_marker_t start_m, end_m;
3153
3154 /* args #2 (pattern) */
3155 patt_exp = fr_dlist_next(&xctx->ex->call.args->dlist, fr_dlist_head(&xctx->ex->call.args->dlist));
3156 fr_assert(patt_exp && patt_exp->type == XLAT_GROUP); /* args must be groups */
3157
3158 /* If there are dynamic expansions, we can't pre-compile */
3159 if (!xlat_is_literal(patt_exp->group)) return 0;
3160 fr_assert(fr_dlist_num_elements(&patt_exp->group->dlist) == 1);
3161
3162 patt_exp = fr_dlist_head(&patt_exp->group->dlist);
3163
3164 /* We can only pre-compile strings */
3165 if (!fr_type_is_string(patt_exp->data.type)) return 0;
3166
3167 sbuff = FR_SBUFF_IN(patt_exp->data.vb_strvalue, patt_exp->data.vb_length);
3168
3169 /* skip any whitesapce */
3170 fr_sbuff_adv_past_whitespace(&sbuff, SIZE_MAX, 0);
3171
3172 /* Is the next char a forward slash? */
3173 if (fr_sbuff_next_if_char(&sbuff, '/')) {
3174 fr_slen_t slen;
3175
3176 fr_sbuff_marker(&start_m, &sbuff);
3177
3178 if (!fr_sbuff_adv_to_chr(&sbuff, SIZE_MAX, '/')) return 0; /* Not a regex */
3179
3180 fr_sbuff_marker(&end_m, &sbuff);
3181 fr_sbuff_next(&sbuff); /* skip trailing slash */
3182
3183 if (fr_sbuff_remaining(&sbuff)) {
3184 slen = regex_flags_parse(NULL, &inst->flags,
3185 &sbuff,
3186 NULL, true);
3187 if (slen < 0) {
3188 PERROR("Failed parsing regex flags in \"%s\"", patt_exp->data.vb_strvalue);
3189 return -1;
3190 }
3191 }
3192
3193 if (regex_compile(inst, &inst->pattern,
3194 fr_sbuff_current(&start_m), fr_sbuff_current(&end_m) - fr_sbuff_current(&start_m),
3195 &inst->flags, true, false) <= 0) {
3196 PERROR("Failed compiling regex \"%s\"", patt_exp->data.vb_strvalue);
3197 return -1;
3198 }
3199 }
3200 /* No... then it's not a regex */
3201
3202 return 0;
3203}
3204
3205/** Perform regex substitution TODO CHECK
3206 *
3207 * Called when %subst() pattern begins with "/"
3208 *
3209@verbatim
3210%subst(<subject>, /<regex>/[flags], <replace>)
3211@endverbatim
3212 *
3213 * Example: (User-Name = "foo")
3214@verbatim
3215%subst(%{User-Name}, /oo.*$/, 'un') == "fun"
3216@endverbatim
3217 *
3218 * @note References can be specified in the replacement string with $<ref>
3219 *
3220 * @see #xlat_func_subst
3221 *
3222 * @ingroup xlat_functions
3223 */
3224static int xlat_func_subst_regex(TALLOC_CTX *ctx, fr_dcursor_t *out,
3225 xlat_ctx_t const *xctx, request_t *request,
3226 fr_value_box_list_t *args)
3227{
3228 xlat_subst_regex_inst_t const *inst = talloc_get_type_abort_const(xctx->inst, xlat_subst_regex_inst_t);
3229 fr_sbuff_t sbuff;
3230 fr_sbuff_marker_t start_m, end_m;
3231 char *buff;
3232 ssize_t slen;
3233 regex_t *pattern, *our_pattern = NULL;
3234 fr_regex_flags_t const *flags;
3235 fr_regex_flags_t our_flags = {};
3236 fr_value_box_t *vb;
3237 fr_value_box_t *subject_vb;
3238 fr_value_box_t *regex_vb;
3239 fr_value_box_t *rep_vb;
3240
3241 XLAT_ARGS(args, &subject_vb, &regex_vb, &rep_vb);
3242
3243 /*
3244 * Was not pre-compiled, so we need to compile it now
3245 */
3246 if (!inst->pattern) {
3247 sbuff = FR_SBUFF_IN(regex_vb->vb_strvalue, regex_vb->vb_length);
3248 if (fr_sbuff_len(&sbuff) == 0) {
3249 REDEBUG("Regex must not be empty");
3250 return XLAT_ACTION_FAIL;
3251 }
3252
3253 fr_sbuff_next(&sbuff); /* skip leading slash */
3254 fr_sbuff_marker(&start_m, &sbuff);
3255
3256 if (!fr_sbuff_adv_to_chr(&sbuff, SIZE_MAX, '/')) return 1; /* Not a regex */
3257
3258 fr_sbuff_marker(&end_m, &sbuff);
3259 fr_sbuff_next(&sbuff); /* skip trailing slash */
3260
3261 slen = regex_flags_parse(NULL, &our_flags, &sbuff, NULL, true);
3262 if (slen < 0) {
3263 RPEDEBUG("Failed parsing regex flags");
3264 return -1;
3265 }
3266
3267 /*
3268 * Process the substitution
3269 */
3270 if (regex_compile(NULL, &our_pattern,
3271 fr_sbuff_current(&start_m), fr_sbuff_current(&end_m) - fr_sbuff_current(&start_m),
3272 &our_flags, true, true) <= 0) {
3273 RPEDEBUG("Failed compiling regex");
3274 return -1;
3275 }
3276 pattern = our_pattern;
3277 flags = &our_flags;
3278 } else {
3279 pattern = inst->pattern;
3280 flags = &inst->flags;
3281 }
3282
3283 MEM(vb = fr_value_box_alloc_null(ctx));
3284 if (regex_substitute(vb, &buff, 0, pattern, flags,
3285 subject_vb->vb_strvalue, subject_vb->vb_length,
3286 rep_vb->vb_strvalue, rep_vb->vb_length, NULL) < 0) {
3287 RPEDEBUG("Failed performing substitution");
3288 talloc_free(vb);
3289 talloc_free(pattern);
3290 return -1;
3291 }
3292 fr_value_box_bstrdup_buffer_shallow(NULL, vb, NULL, buff, subject_vb->tainted);
3294
3296
3297 talloc_free(our_pattern);
3298
3299 return 0;
3300}
3301#endif
3302
3304 { .required = true, .concat = true, .type = FR_TYPE_STRING },
3305 { .required = true, .concat = true, .type = FR_TYPE_STRING },
3306 { .required = true, .concat = true, .type = FR_TYPE_STRING },
3308};
3309
3310/** Perform regex substitution
3311 *
3312@verbatim
3313%subst(<subject>, <pattern>, <replace>)
3314@endverbatim
3315 *
3316 * Example: (User-Name = "foobar")
3317@verbatim
3318%subst(%{User-Name}, 'oo', 'un') == "funbar"
3319@endverbatim
3320 *
3321 * @see xlat_func_subst_regex
3322 *
3323 * @ingroup xlat_functions
3324 */
3326#ifdef HAVE_REGEX_PCRE2
3327 xlat_ctx_t const *xctx,
3328#else
3329 UNUSED xlat_ctx_t const *xctx,
3330#endif
3331 request_t *request, fr_value_box_list_t *args)
3332{
3333 char const *p, *q, *end;
3334 char *vb_str;
3335
3336 char const *pattern, *rep;
3337 size_t pattern_len, rep_len;
3338
3339 fr_value_box_t *rep_vb, *vb;
3340 fr_value_box_t *subject_vb;
3341 fr_value_box_t *pattern_vb;
3342
3343 XLAT_ARGS(args, &subject_vb, &pattern_vb, &rep_vb);
3344
3345 /* coverity[dereference] */
3346 pattern = pattern_vb->vb_strvalue;
3347 if (*pattern == '/') {
3348#ifdef HAVE_REGEX_PCRE2
3349 switch (xlat_func_subst_regex(ctx, out, xctx, request, args)) {
3350 case 0:
3351 return XLAT_ACTION_DONE;
3352
3353 case 1:
3354 /* Not a regex, fall through */
3355 break;
3356
3357 case -1:
3358 return XLAT_ACTION_FAIL;
3359 }
3360#else
3361 if (memchr(pattern, '/', pattern_vb->vb_length - 1)) {
3362 REDEBUG("regex based substitutions require libpcre2. "
3363 "Check ${features.regex-pcre2} to determine support");
3364 }
3365 return XLAT_ACTION_FAIL;
3366#endif
3367 }
3368
3369 /*
3370 * Check for empty pattern
3371 */
3372 pattern_len = pattern_vb->vb_length;
3373 if (pattern_len == 0) {
3374 REDEBUG("Empty pattern");
3375 return XLAT_ACTION_FAIL;
3376 }
3377
3378 rep = rep_vb->vb_strvalue;
3379 rep_len = rep_vb->vb_length;
3380
3381 p = subject_vb->vb_strvalue;
3382 end = p + subject_vb->vb_length;
3383
3384 MEM(vb = fr_value_box_alloc_null(ctx));
3385 vb_str = talloc_bstrndup(vb, "", 0);
3386
3387 while (p < end) {
3388 q = memmem(p, end - p, pattern, pattern_len);
3389 if (!q) {
3390 MEM(vb_str = talloc_bstr_append(vb, vb_str, p, end - p));
3391 break;
3392 }
3393
3394 if (q > p) MEM(vb_str = talloc_bstr_append(vb, vb_str, p, q - p));
3395 if (rep_len) MEM(vb_str = talloc_bstr_append(vb, vb_str, rep, rep_len));
3396 p = q + pattern_len;
3397 }
3398
3399 if (fr_value_box_bstrdup_buffer_shallow(NULL, vb, NULL, vb_str, subject_vb->tainted) < 0) {
3400 RPEDEBUG("Failed creating output box");
3401 talloc_free(vb);
3402 return XLAT_ACTION_FAIL;
3403 }
3404
3405 fr_assert(vb && (vb->type != FR_TYPE_NULL));
3408
3409 return XLAT_ACTION_DONE;
3410}
3411
3413 { .required = false, .single = true, .type = FR_TYPE_STRING },
3415};
3416
3417/** Return the time as a #FR_TYPE_DATE
3418 *
3419 * Note that all operations are UTC.
3420 *
3421@verbatim
3422%time()
3423@endverbatim
3424 *
3425 * Example:
3426@verbatim
3427update reply {
3428 &Reply-Message := "%{%time(now) - %time(request)}"
3429}
3430@endverbatim
3431 *
3432 * @ingroup xlat_functions
3433 */
3435 UNUSED xlat_ctx_t const *xctx,
3436 request_t *request, fr_value_box_list_t *args)
3437{
3438 fr_value_box_t *arg;
3439 fr_value_box_t *vb;
3441
3442 XLAT_ARGS(args, &arg);
3443
3444 if (!arg || (strcmp(arg->vb_strvalue, "now") == 0)) {
3446
3447 } else if (strcmp(arg->vb_strvalue, "request") == 0) {
3448 value = fr_time_to_unix_time(request->packet->timestamp);
3449
3450 } else if (strcmp(arg->vb_strvalue, "offset") == 0) {
3451 MEM(vb = fr_value_box_alloc(ctx, FR_TYPE_TIME_DELTA, NULL));
3452 vb->vb_time_delta = fr_time_gmtoff();
3453 goto append;
3454
3455 } else if (strcmp(arg->vb_strvalue, "dst") == 0) {
3456 MEM(vb = fr_value_box_alloc(ctx, FR_TYPE_BOOL, NULL));
3457 vb->vb_bool = fr_time_is_dst();
3458 goto append;
3459
3460 } else if (strcmp(arg->vb_strvalue, "mday_offset") == 0) {
3461 struct tm tm;
3462 fr_unix_time_t unix_time = fr_time_to_unix_time(request->packet->timestamp);
3463 time_t when = fr_unix_time_to_sec(unix_time);
3464 int64_t nsec;
3465
3466 gmtime_r(&when, &tm);
3467
3468 nsec = (int64_t) 86400 * (tm.tm_mday - 1);
3469 nsec += when % 86400;
3470 nsec *= NSEC;
3471 nsec += fr_unix_time_unwrap(unix_time) % NSEC;
3472
3473 MEM(vb = fr_value_box_alloc(ctx, FR_TYPE_TIME_DELTA, NULL));
3474 vb->vb_time_delta = fr_time_delta_wrap(nsec);
3475 goto append;
3476
3477 } else if (strcmp(arg->vb_strvalue, "wday_offset") == 0) {
3478 struct tm tm;
3479 fr_unix_time_t unix_time = fr_time_to_unix_time(request->packet->timestamp);
3480 time_t when = fr_unix_time_to_sec(unix_time);
3481 int64_t nsec;
3482
3483 gmtime_r(&when, &tm);
3484
3485 nsec = (int64_t) 86400 * tm.tm_wday;
3486 nsec += when % 86400;
3487 nsec *= NSEC;
3488 nsec += fr_unix_time_unwrap(unix_time) % NSEC;
3489
3490 MEM(vb = fr_value_box_alloc(ctx, FR_TYPE_TIME_DELTA, NULL));
3491 vb->vb_time_delta = fr_time_delta_wrap(nsec);
3492
3493 MEM(vb = fr_value_box_alloc(ctx, FR_TYPE_TIME_DELTA, NULL));
3494 vb->vb_time_delta = fr_time_delta_wrap(nsec);
3495 goto append;
3496
3497 } else if (fr_unix_time_from_str(&value, arg->vb_strvalue, FR_TIME_RES_SEC) < 0) {
3498 REDEBUG("Invalid time specification '%s'", arg->vb_strvalue);
3499 return XLAT_ACTION_FAIL;
3500 }
3501
3502 MEM(vb = fr_value_box_alloc(ctx, FR_TYPE_DATE, NULL));
3503 vb->vb_date = value;
3504
3505append:
3507
3508 return XLAT_ACTION_DONE;
3509}
3510
3511
3512/** Change case of a string
3513 *
3514 * If upper is true, change to uppercase, otherwise, change to lowercase
3515 */
3517 UNUSED request_t *request, fr_value_box_list_t *args, bool upper)
3518{
3519 char *p;
3520 char const *end;
3521 fr_value_box_t *vb;
3522
3523 XLAT_ARGS(args, &vb);
3524
3525 p = UNCONST(char *, vb->vb_strvalue);
3526 end = p + vb->vb_length;
3527
3528 while (p < end) {
3529 *(p) = upper ? toupper ((int) *(p)) : tolower((uint8_t) *(p));
3530 p++;
3531 }
3532
3533 fr_value_box_list_remove(args, vb); /* Can't leave it in both lists */
3535
3536 return XLAT_ACTION_DONE;
3537}
3538
3540 { .required = true, .concat = true, .type = FR_TYPE_STRING },
3542};
3543
3544
3545/** Convert a string to lowercase
3546 *
3547 * Example:
3548@verbatim
3549%tolower("Bar") == "bar"
3550@endverbatim
3551 *
3552 * Probably only works for ASCII
3553 *
3554 * @ingroup xlat_functions
3555 */
3557 UNUSED xlat_ctx_t const *xctx,
3558 request_t *request, fr_value_box_list_t *in)
3559{
3560 return xlat_change_case(ctx, out, request, in, false);
3561}
3562
3563
3564/** Convert a string to uppercase
3565 *
3566 * Example:
3567@verbatim
3568%toupper("Foo") == "FOO"
3569@endverbatim
3570 *
3571 * Probably only works for ASCII
3572 *
3573 * @ingroup xlat_functions
3574 */
3576 UNUSED xlat_ctx_t const *xctx,
3577 request_t *request, fr_value_box_list_t *in)
3578{
3579 return xlat_change_case(ctx, out, request, in, true);
3580}
3581
3582
3584 { .required = true, .concat = true, .type = FR_TYPE_STRING },
3586};
3587
3588/** URLencode special characters
3589 *
3590 * Example:
3591@verbatim
3592%urlquote("http://example.org/") == "http%3A%47%47example.org%47"
3593@endverbatim
3594 *
3595 * @ingroup xlat_functions
3596 */
3598 UNUSED xlat_ctx_t const *xctx,
3599 UNUSED request_t *request, fr_value_box_list_t *args)
3600{
3601 char const *p, *end;
3602 char *buff_p;
3603 size_t outlen = 0;
3604 fr_value_box_t *vb;
3605 fr_value_box_t *in_head;
3606
3607 XLAT_ARGS(args, &in_head);
3608
3609 p = in_head->vb_strvalue;
3610 end = p + in_head->vb_length;
3611
3612 /*
3613 * Calculate size of output
3614 */
3615 while (p < end) {
3616 if (isalnum(*p) ||
3617 *p == '-' ||
3618 *p == '_' ||
3619 *p == '.' ||
3620 *p == '~') {
3621 outlen++;
3622 } else {
3623 outlen += 3;
3624 }
3625 p++;
3626 }
3627
3628 MEM(vb = fr_value_box_alloc_null(ctx));
3629 MEM(fr_value_box_bstr_alloc(vb, &buff_p, vb, NULL, outlen, false) == 0);
3631
3632 /* Reset p to start position */
3633 p = in_head->vb_strvalue;
3634
3635 while (p < end) {
3636 if (isalnum(*p)) {
3637 *buff_p++ = *p++;
3638 continue;
3639 }
3640
3641 switch (*p) {
3642 case '-':
3643 case '_':
3644 case '.':
3645 case '~':
3646 *buff_p++ = *p++;
3647 break;
3648
3649 default:
3650 /* MUST be upper case hex to be compliant */
3651 snprintf(buff_p, 4, "%%%02X", (uint8_t) *p++); /* %XX */
3652
3653 buff_p += 3;
3654 }
3655 }
3656
3657 *buff_p = '\0';
3658
3660
3661 return XLAT_ACTION_DONE;
3662}
3663
3664
3666 { .required = true, .concat = true, .type = FR_TYPE_STRING },
3668};
3669
3670/** URLdecode special characters
3671 *
3672 * @note Remember to escape % with %% in strings, else xlat will try to parse it.
3673 *
3674 * Example:
3675@verbatim
3676%urlunquote("http%%3A%%47%%47example.org%%47") == "http://example.org/"
3677@endverbatim
3678 *
3679 * @ingroup xlat_functions
3680 */
3682 UNUSED xlat_ctx_t const *xctx,
3683 request_t *request, fr_value_box_list_t *args)
3684{
3685 char const *p, *end;
3686 char *buff_p;
3687 char *c1, *c2;
3688 size_t outlen = 0;
3689 fr_value_box_t *vb;
3690 fr_value_box_t *in_head;
3691
3692 XLAT_ARGS(args, &in_head);
3693
3694 p = in_head->vb_strvalue;
3695 end = p + in_head->vb_length;
3696
3697 /*
3698 * Calculate size of output
3699 */
3700 while (p < end) {
3701 if (*p == '%') {
3702 p += 3;
3703 } else {
3704 p++;
3705 }
3706 outlen++;
3707 }
3708
3709 MEM(vb = fr_value_box_alloc_null(ctx));
3710 MEM(fr_value_box_bstr_alloc(vb, &buff_p, vb, NULL, outlen, false) == 0);
3712
3713 /* Reset p to start position */
3714 p = in_head->vb_strvalue;
3715
3716 while (p < end) {
3717 if (*p != '%') {
3718 *buff_p++ = *p++;
3719 continue;
3720 }
3721 /* Is a % char */
3722
3723 /* Don't need \0 check, as it won't be in the hextab */
3724 if (!(c1 = memchr(hextab, tolower((uint8_t) *++p), 16)) ||
3725 !(c2 = memchr(hextab, tolower((uint8_t) *++p), 16))) {
3726 REMARKER(in_head->vb_strvalue, p - in_head->vb_strvalue, "Non-hex char in %% sequence");
3727 talloc_free(vb);
3728
3729 return XLAT_ACTION_FAIL;
3730 }
3731 p++;
3732 *buff_p++ = ((c1 - hextab) << 4) + (c2 - hextab);
3733 }
3734
3735 *buff_p = '\0';
3737
3738 return XLAT_ACTION_DONE;
3739}
3740
3745
3746/** Decode any protocol attribute / options
3747 *
3748 * Creates protocol-specific attributes based on the given binary option data
3749 *
3750 * Example:
3751@verbatim
3752%dhcpv4.decode(%{Tmp-Octets-0})
3753@endverbatim
3754 *
3755 * @ingroup xlat_functions
3756 */
3758 xlat_ctx_t const *xctx,
3759 request_t *request, fr_value_box_list_t *in)
3760{
3761 int decoded;
3762 fr_value_box_t *vb;
3763 void *decode_ctx = NULL;
3764 fr_test_point_pair_decode_t const *tp_decode = *(void * const *)xctx->inst;
3765
3766 if (tp_decode->test_ctx) {
3767 if (tp_decode->test_ctx(&decode_ctx, ctx, request->dict) < 0) {
3768 return XLAT_ACTION_FAIL;
3769 }
3770 }
3771
3772 decoded = xlat_decode_value_box_list(request->request_ctx, &request->request_pairs,
3773 request, decode_ctx, tp_decode->func, in);
3774 if (decoded <= 0) {
3775 talloc_free(decode_ctx);
3776 RPERROR("Protocol decoding failed");
3777 return XLAT_ACTION_FAIL;
3778 }
3779
3780 /*
3781 * Create a value box to hold the decoded count, and add
3782 * it to the output list.
3783 */
3784 MEM(vb = fr_value_box_alloc(ctx, FR_TYPE_UINT32, NULL));
3785 vb->vb_uint32 = decoded;
3787
3788 talloc_free(decode_ctx);
3789 return XLAT_ACTION_DONE;
3790}
3791
3793 { .required = true, .single = true, .type = FR_TYPE_IPV4_PREFIX },
3795};
3796
3797/** Calculate the subnet mask from a IPv4 prefix
3798 *
3799 * Example:
3800@verbatim
3801%ip.v4.netmask(%{Network-Prefix})
3802@endverbatim
3803 *
3804 * @ingroup xlat_functions
3805 */
3807 UNUSED request_t *request, fr_value_box_list_t *args)
3808{
3809 fr_value_box_t *subnet, *vb;
3810 XLAT_ARGS(args, &subnet);
3811
3812 MEM(vb = fr_value_box_alloc(ctx, FR_TYPE_IPV4_ADDR, NULL));
3813 vb->vb_ip.addr.v4.s_addr = htonl((uint32_t)0xffffffff << (32 - subnet->vb_ip.prefix));
3815
3816 return XLAT_ACTION_DONE;
3817}
3818
3819/** Calculate the broadcast address from a IPv4 prefix
3820 *
3821 * Example:
3822@verbatim
3823%ip.v4.broadcast(%{Network-Prefix})
3824@endverbatim
3825 *
3826 * @ingroup xlat_functions
3827 */
3829 UNUSED request_t *request, fr_value_box_list_t *args)
3830{
3831 fr_value_box_t *subnet, *vb;
3832 XLAT_ARGS(args, &subnet);
3833
3834 MEM(vb = fr_value_box_alloc(ctx, FR_TYPE_IPV4_ADDR, NULL));
3835 vb->vb_ip.addr.v4.s_addr = htonl( ntohl(subnet->vb_ip.addr.v4.s_addr) | (uint32_t)0xffffffff >> subnet->vb_ip.prefix);
3837
3838 return XLAT_ACTION_DONE;
3839}
3840
3842{
3843 *(void **) mctx->inst = mctx->uctx;
3844 return 0;
3845}
3846
3848 { .required = true, .single = true, .type = FR_TYPE_STRING },
3850};
3851
3852/** Encode protocol attributes / options
3853 *
3854 * Returns octet string created from the provided pairs
3855 *
3856 * Example:
3857@verbatim
3858%dhcpv4.encode(&request[*])
3859@endverbatim
3860 *
3861 * @ingroup xlat_functions
3862 */
3864 xlat_ctx_t const *xctx,
3865 request_t *request, fr_value_box_list_t *args)
3866{
3867 tmpl_t *vpt;
3868 fr_pair_t *vp;
3869 fr_dcursor_t cursor;
3871 bool tainted = false;
3872 fr_value_box_t *encoded;
3873
3874 uint8_t binbuf[2048];
3875 uint8_t *p = binbuf, *end = p + sizeof(binbuf);
3876 ssize_t len = 0;
3877 fr_value_box_t *in_head;
3878 void *encode_ctx = NULL;
3879 fr_test_point_pair_encode_t const *tp_encode;
3880
3881 XLAT_ARGS(args, &in_head);
3882
3883 memcpy(&tp_encode, xctx->inst, sizeof(tp_encode)); /* const issues */
3884
3885 if (tmpl_afrom_attr_str(ctx, NULL, &vpt, in_head->vb_strvalue,
3886 &(tmpl_rules_t){
3887 .attr = {
3888 .dict_def = request->dict,
3889 .list_def = request_attr_request,
3890 .allow_wildcard = true,
3891 .prefix = TMPL_ATTR_REF_PREFIX_AUTO
3892 }
3893 }) <= 0) {
3894 RPEDEBUG("Failed parsing attribute reference");
3895 return XLAT_ACTION_FAIL;
3896 }
3897
3898 /*
3899 * Create the encoding context.
3900 */
3901 if (tp_encode->test_ctx) {
3902 if (tp_encode->test_ctx(&encode_ctx, vpt, request->dict) < 0) {
3904 return XLAT_ACTION_FAIL;
3905 }
3906 }
3907
3908 /*
3909 * Loop over the attributes, encoding them.
3910 */
3911 for (vp = tmpl_dcursor_init(NULL, NULL, &cc, &cursor, request, vpt);
3912 vp != NULL;
3913 vp = fr_dcursor_next(&cursor)) {
3914 if (vp->da->flags.internal) continue;
3915
3916 /*
3917 * Don't check the dictionaries. By definition,
3918 * vp->da->dict==request->dict, OR else we're
3919 * using the internal encoder and encoding a real
3920 * protocol.
3921 *
3922 * However, we likely still want a
3923 * dictionary-specific "is encodable" function,
3924 * as AKA/SIM and DHCPv6 encode "bool"s only if
3925 * their value is true.
3926 */
3927
3928 len = tp_encode->func(&FR_DBUFF_TMP(p, end), &cursor, encode_ctx);
3929 if (len < 0) {
3930 RPEDEBUG("Protocol encoding failed");
3931 tmpl_dcursor_clear(&cc);
3933 return XLAT_ACTION_FAIL;
3934 }
3935
3936 tainted |= vp->vp_tainted;
3937 p += len;
3938 }
3939
3940 tmpl_dcursor_clear(&cc);
3942
3943 /*
3944 * Pass the options string back to the caller.
3945 */
3946 MEM(encoded = fr_value_box_alloc_null(ctx));
3947 fr_value_box_memdup(encoded, encoded, NULL, binbuf, (size_t)len, tainted);
3948 fr_dcursor_append(out, encoded);
3949
3950 return XLAT_ACTION_DONE;
3951}
3952
3954{
3955 fr_test_point_pair_decode_t *tp_decode;
3956 fr_test_point_pair_encode_t *tp_encode;
3957 xlat_t *xlat;
3958 char buffer[256+32];
3959
3960 /*
3961 * See if there's a decode function for it.
3962 */
3963 snprintf(buffer, sizeof(buffer), "%s_tp_decode_pair", name);
3964 tp_decode = dlsym(dl->handle, buffer);
3965 if (tp_decode) {
3966 snprintf(buffer, sizeof(buffer), "%s.decode", name);
3967
3968 /* May be called multiple times, so just skip protocols we've already registered */
3969 if (xlat_func_find(buffer, -1)) return 1;
3970
3971 if (unlikely((xlat = xlat_func_register(NULL, buffer, protocol_decode_xlat, FR_TYPE_UINT32)) == NULL)) return -1;
3973 /* coverity[suspicious_sizeof] */
3976 }
3977
3978 /*
3979 * See if there's an encode function for it.
3980 */
3981 snprintf(buffer, sizeof(buffer), "%s_tp_encode_pair", name);
3982 tp_encode = dlsym(dl->handle, buffer);
3983 if (tp_encode) {
3984 snprintf(buffer, sizeof(buffer), "%s.encode", name);
3985
3986 if (xlat_func_find(buffer, -1)) return 1;
3987
3988 if (unlikely((xlat = xlat_func_register(NULL, buffer, protocol_encode_xlat, FR_TYPE_OCTETS)) == NULL)) return -1;
3990 /* coverity[suspicious_sizeof] */
3993 }
3994
3995 return 0;
3996}
3997
3998static int xlat_protocol_register(fr_dict_t const *dict)
3999{
4000 dl_t *dl = fr_dict_dl(dict);
4001 char *p, name[256];
4002
4003 /*
4004 * No library for this protocol, skip it.
4005 *
4006 * Protocol TEST has no libfreeradius-test, so that's OK.
4007 */
4008 if (!dl) return 0;
4009
4010 strlcpy(name, fr_dict_root(dict)->name, sizeof(name));
4011 for (p = name; *p != '\0'; p++) {
4012 *p = tolower((uint8_t) *p);
4013 }
4014
4016}
4017
4019
4021{
4022 dl_t *dl;
4023
4024 cbor_loader = dl_loader_init(NULL, NULL, false, false);
4025 if (!cbor_loader) return 0;
4026
4027 dl = dl_by_name(cbor_loader, "libfreeradius-cbor", NULL, false);
4028 if (!dl) return 0;
4029
4030 if (xlat_protocol_register_by_name(dl, "cbor") < 0) return -1;
4031
4032 return 0;
4033}
4034
4035
4036/** Register xlats for any loaded dictionaries
4037 */
4039{
4040 fr_dict_t *dict;
4042
4043 for (dict = fr_dict_global_ctx_iter_init(&iter);
4044 dict != NULL;
4045 dict = fr_dict_global_ctx_iter_next(&iter)) {
4046 if (xlat_protocol_register(dict) < 0) return -1;
4047 }
4048
4049 /*
4050 * And the internal protocol, too.
4051 */
4052 if (xlat_protocol_register(fr_dict_internal()) < 0) return -1;
4053
4054 /*
4055 * And cbor stuff
4056 */
4057 if (xlat_protocol_register_cbor() < 0) return -1;
4058
4059 return 0;
4060}
4061
4062/** De-register all xlat functions we created
4063 *
4064 */
4065static int _xlat_global_free(UNUSED void *uctx)
4066{
4067 TALLOC_FREE(xlat_ctx);
4071
4072 return 0;
4073}
4074
4075/** Global initialisation for xlat
4076 *
4077 * @note Free memory with #xlat_free
4078 *
4079 * @return
4080 * - 0 on success.
4081 * - -1 on failure.
4082 *
4083 * @hidecallgraph
4084 */
4085static int _xlat_global_init(UNUSED void *uctx)
4086{
4087 xlat_t *xlat;
4088
4089 xlat_ctx = talloc_init("xlat");
4090 if (!xlat_ctx) return -1;
4091
4092 if (xlat_func_init() < 0) return -1;
4093
4094 /*
4095 * Lookup attributes used by virtual xlat expansions.
4096 */
4097 if (xlat_eval_init() < 0) return -1;
4098
4099 /*
4100 * Registers async xlat operations in the `unlang` interpreter.
4101 */
4103
4104 /*
4105 * These are all "pure" functions.
4106 */
4107#define XLAT_REGISTER_ARGS(_xlat, _func, _return_type, _args) \
4108do { \
4109 if (unlikely((xlat = xlat_func_register(xlat_ctx, _xlat, _func, _return_type)) == NULL)) return -1; \
4110 xlat_func_args_set(xlat, _args); \
4111 xlat_func_flags_set(xlat, XLAT_FUNC_FLAG_PURE | XLAT_FUNC_FLAG_INTERNAL); \
4112} while (0)
4113
4134
4135 /*
4136 * The inputs to these functions are variable.
4137 */
4138#undef XLAT_REGISTER_ARGS
4139#define XLAT_REGISTER_ARGS(_xlat, _func, _return_type, _args) \
4140do { \
4141 if (unlikely((xlat = xlat_func_register(xlat_ctx, _xlat, _func, _return_type)) == NULL)) return -1; \
4142 xlat_func_args_set(xlat, _args); \
4143 xlat_func_flags_set(xlat, XLAT_FUNC_FLAG_INTERNAL); \
4144} while (0)
4145
4157#ifdef HAVE_REGEX_PCRE2
4158 xlat_func_instantiate_set(xlat, xlat_instantiate_subst_regex, xlat_subst_regex_inst_t, NULL, NULL);
4159#endif
4166
4167 if (unlikely((xlat = xlat_func_register(xlat_ctx, "untaint", xlat_func_untaint, FR_TYPE_VOID)) == NULL)) return -1;
4169
4170 if (unlikely((xlat = xlat_func_register(xlat_ctx, "taint", xlat_func_taint, FR_TYPE_VOID)) == NULL)) return -1;
4172
4173 /*
4174 * All of these functions are pure.
4175 */
4176#define XLAT_REGISTER_PURE(_xlat, _func, _return_type, _arg) \
4177do { \
4178 if (unlikely((xlat = xlat_func_register(xlat_ctx, _xlat, _func, _return_type)) == NULL)) return -1; \
4179 xlat_func_args_set(xlat, _arg); \
4180 xlat_func_flags_set(xlat, XLAT_FUNC_FLAG_PURE | XLAT_FUNC_FLAG_INTERNAL); \
4181} while (0)
4182
4188#if defined(HAVE_REGEX_PCRE) || defined(HAVE_REGEX_PCRE2)
4189 if (unlikely((xlat = xlat_func_register(xlat_ctx, "regex", xlat_func_regex, FR_TYPE_STRING)) == NULL)) return -1;
4191#endif
4192
4193 {
4194 static xlat_arg_parser_t const xlat_regex_safe_args[] = {
4195 { .type = FR_TYPE_STRING, .variadic = true, .concat = true },
4197 };
4198
4199 if (unlikely((xlat = xlat_func_register(xlat_ctx, "regex.safe",
4200 xlat_transparent, FR_TYPE_STRING)) == NULL)) return -1;
4202 xlat_func_args_set(xlat, xlat_regex_safe_args);
4203 xlat_func_safe_for_set(xlat, FR_REGEX_SAFE_FOR);
4204 }
4205
4207
4208#ifdef HAVE_OPENSSL_EVP_H
4209 XLAT_REGISTER_PURE("sha2_224", xlat_func_sha2_224, FR_TYPE_OCTETS, xlat_func_sha_arg);
4210 XLAT_REGISTER_PURE("sha2_256", xlat_func_sha2_256, FR_TYPE_OCTETS, xlat_func_sha_arg);
4211 XLAT_REGISTER_PURE("sha2_384", xlat_func_sha2_384, FR_TYPE_OCTETS, xlat_func_sha_arg);
4212 XLAT_REGISTER_PURE("sha2_512", xlat_func_sha2_512, FR_TYPE_OCTETS, xlat_func_sha_arg);
4213
4214# ifdef HAVE_EVP_BLAKE2S256
4215 XLAT_REGISTER_PURE("blake2s_256", xlat_func_blake2s_256, FR_TYPE_OCTETS, xlat_func_sha_arg);
4216# endif
4217# ifdef HAVE_EVP_BLAKE2B512
4218 XLAT_REGISTER_PURE("blake2b_512", xlat_func_blake2b_512, FR_TYPE_OCTETS, xlat_func_sha_arg);
4219# endif
4220
4221 XLAT_REGISTER_PURE("sha3_224", xlat_func_sha3_224, FR_TYPE_OCTETS, xlat_func_sha_arg);
4222 XLAT_REGISTER_PURE("sha3_256", xlat_func_sha3_256, FR_TYPE_OCTETS, xlat_func_sha_arg);
4223 XLAT_REGISTER_PURE("sha3_384", xlat_func_sha3_384, FR_TYPE_OCTETS, xlat_func_sha_arg);
4224 XLAT_REGISTER_PURE("sha3_512", xlat_func_sha3_512, FR_TYPE_OCTETS, xlat_func_sha_arg);
4225#endif
4226
4237
4239}
4240
4242{
4243 int ret;
4244 fr_atexit_global_once_ret(&ret, _xlat_global_init, _xlat_global_free, NULL);
4245 return ret;
4246}
static int const char char buffer[256]
Definition acutest.h:576
int const char * file
Definition acutest.h:702
int n
Definition acutest.h:577
va_list args
Definition acutest.h:770
static int const char * fmt
Definition acutest.h:573
#define fr_base16_encode(_out, _in)
Definition base16.h:57
#define fr_base16_decode(_err, _out, _in, _no_trailing)
Definition base16.h:95
#define fr_base64_encode(_out, _in, _add_padding)
Definition base64.h:74
#define fr_base64_decode(_out, _in, _expect_padding, _no_trailing)
Definition base64.h:81
#define FR_BASE64_DEC_LENGTH(_inlen)
Definition base64.h:44
#define FR_BASE64_ENC_LENGTH(_inlen)
Encode/decode binary data using printable characters (base64 format)
Definition base64.h:43
static dl_t * dl
Definition fuzzer.c:42
static bool stop
Definition radmin.c:70
#define UNCONST(_type, _ptr)
Remove const qualification from a pointer.
Definition build.h:167
#define RCSID(id)
Definition build.h:483
#define unlikely(_x)
Definition build.h:381
#define UNUSED
Definition build.h:315
#define FR_DBUFF_TMP(_start, _len_or_end)
Creates a compound literal to pass into functions which accept a dbuff.
Definition dbuff.h:514
static void * fr_dcursor_next(fr_dcursor_t *cursor)
Advanced the cursor to the next item.
Definition dcursor.h:288
static int fr_dcursor_append(fr_dcursor_t *cursor, void *v)
Insert a single item at the end of the list.
Definition dcursor.h:406
#define MEM(x)
Definition debug.h:36
fr_dict_t * fr_dict_global_ctx_iter_next(fr_dict_global_ctx_iter_t *iter)
Definition dict_util.c:4574
char const * name
Vendor name.
Definition dict.h:250
static fr_slen_t err
Definition dict.h:824
fr_dict_t * fr_dict_global_ctx_iter_init(fr_dict_global_ctx_iter_t *iter)
Iterate protocols by name.
Definition dict_util.c:4567
fr_dict_attr_t const * fr_dict_root(fr_dict_t const *dict)
Return the root attribute of a dictionary.
Definition dict_util.c:2400
dl_t * fr_dict_dl(fr_dict_t const *dict)
Definition dict_util.c:2410
uint32_t pen
Private enterprise number.
Definition dict.h:246
fr_dict_t const * fr_dict_internal(void)
Definition dict_util.c:4610
static fr_slen_t in
Definition dict.h:824
fr_dict_vendor_t const * fr_dict_vendor_by_da(fr_dict_attr_t const *da)
Look up a vendor by one of its child attributes.
Definition dict_util.c:2635
Private enterprise.
Definition dict.h:245
Test enumeration values.
Definition dict_test.h:92
dl_loader_t * dl_loader_init(TALLOC_CTX *ctx, void *uctx, bool uctx_free, bool defer_symbol_init)
Initialise structures needed by the dynamic linker.
Definition dl.c:885
dl_t * dl_by_name(dl_loader_t *dl_loader, char const *name, void *uctx, bool uctx_free)
Search for a dl's shared object in various locations.
Definition dl.c:470
A dynamic loader.
Definition dl.c:81
void * handle
Handle returned by dlopen.
Definition dl.h:62
Module handle.
Definition dl.h:58
static void * fr_dlist_head(fr_dlist_head_t const *list_head)
Return the HEAD item of a list or NULL if the list is empty.
Definition dlist.h:486
static unsigned int fr_dlist_num_elements(fr_dlist_head_t const *head)
Return the number of elements in the dlist.
Definition dlist.h:939
static void * fr_dlist_next(fr_dlist_head_t const *list_head, void const *ptr)
Get the next item in a list.
Definition dlist.h:555
fr_bio_shutdown & my
Definition fd_errno.h:59
static xlat_action_t xlat_func_next_time(TALLOC_CTX *ctx, fr_dcursor_t *out, UNUSED xlat_ctx_t const *xctx, request_t *request, fr_value_box_list_t *args)
Calculate number of seconds until the next n hour(s), day(s), week(s), year(s).
static xlat_action_t xlat_func_eval(TALLOC_CTX *ctx, fr_dcursor_t *out, xlat_ctx_t const *xctx, request_t *request, fr_value_box_list_t *args)
Dynamically evaluate an expansion string.
static xlat_action_t xlat_func_lpad(UNUSED TALLOC_CTX *ctx, fr_dcursor_t *out, UNUSED xlat_ctx_t const *xctx, request_t *request, fr_value_box_list_t *args)
lpad a string
static xlat_action_t xlat_func_bin(TALLOC_CTX *ctx, fr_dcursor_t *out, UNUSED xlat_ctx_t const *xctx, request_t *request, fr_value_box_list_t *args)
Convert hex string to binary.
static xlat_action_t xlat_func_subst(TALLOC_CTX *ctx, fr_dcursor_t *out, UNUSED xlat_ctx_t const *xctx, request_t *request, fr_value_box_list_t *args)
Perform regex substitution.
static xlat_action_t xlat_func_urlunquote(TALLOC_CTX *ctx, fr_dcursor_t *out, UNUSED xlat_ctx_t const *xctx, request_t *request, fr_value_box_list_t *args)
URLdecode special characters.
static xlat_action_t xlat_func_base64_decode(TALLOC_CTX *ctx, fr_dcursor_t *out, UNUSED xlat_ctx_t const *xctx, request_t *request, fr_value_box_list_t *args)
Decode base64 string.
static xlat_action_t xlat_func_hmac_md5(TALLOC_CTX *ctx, fr_dcursor_t *out, UNUSED xlat_ctx_t const *xctx, UNUSED request_t *request, fr_value_box_list_t *in)
Generate the HMAC-MD5 of a string or attribute.
static xlat_action_t xlat_func_base64_encode(TALLOC_CTX *ctx, fr_dcursor_t *out, UNUSED xlat_ctx_t const *xctx, request_t *request, fr_value_box_list_t *args)
Encode string or attribute as base64.
static xlat_action_t xlat_func_log_info(UNUSED TALLOC_CTX *ctx, UNUSED fr_dcursor_t *out, UNUSED xlat_ctx_t const *xctx, request_t *request, fr_value_box_list_t *args)
Log something at INFO level.
static xlat_action_t xlat_func_log_warn(UNUSED TALLOC_CTX *ctx, UNUSED fr_dcursor_t *out, UNUSED xlat_ctx_t const *xctx, request_t *request, fr_value_box_list_t *args)
Log something at WARN level.
static xlat_action_t xlat_func_map(TALLOC_CTX *ctx, fr_dcursor_t *out, UNUSED xlat_ctx_t const *xctx, request_t *request, fr_value_box_list_t *args)
Processes fmt as a map string and applies it to the current request.
static xlat_action_t protocol_decode_xlat(TALLOC_CTX *ctx, fr_dcursor_t *out, xlat_ctx_t const *xctx, request_t *request, fr_value_box_list_t *in)
Decode any protocol attribute / options.
static xlat_action_t xlat_func_debug(TALLOC_CTX *ctx, fr_dcursor_t *out, UNUSED xlat_ctx_t const *xctx, request_t *request, fr_value_box_list_t *args)
Dynamically change the debugging level for the current request.
static xlat_action_t xlat_func_log_debug(UNUSED TALLOC_CTX *ctx, UNUSED fr_dcursor_t *out, UNUSED xlat_ctx_t const *xctx, request_t *request, fr_value_box_list_t *args)
Log something at DEBUG level.
static xlat_action_t xlat_func_log_dst(UNUSED TALLOC_CTX *ctx, UNUSED fr_dcursor_t *out, UNUSED xlat_ctx_t const *xctx, request_t *request, fr_value_box_list_t *args)
Change the log destination to the named one.
static xlat_arg_parser_t const xlat_func_string_arg[]
Calculate any digest supported by OpenSSL EVP_MD.
static xlat_action_t xlat_func_concat(TALLOC_CTX *ctx, fr_dcursor_t *out, UNUSED xlat_ctx_t const *xctx, request_t *request, fr_value_box_list_t *args)
Concatenate string representation of values of given attributes using separator.
static xlat_action_t xlat_func_urlquote(TALLOC_CTX *ctx, fr_dcursor_t *out, UNUSED xlat_ctx_t const *xctx, UNUSED request_t *request, fr_value_box_list_t *args)
URLencode special characters.
static xlat_action_t xlat_func_rpad(UNUSED TALLOC_CTX *ctx, fr_dcursor_t *out, UNUSED xlat_ctx_t const *xctx, request_t *request, fr_value_box_list_t *args)
Right pad a string.
static xlat_action_t xlat_func_md4(TALLOC_CTX *ctx, fr_dcursor_t *out, UNUSED xlat_ctx_t const *xctx, UNUSED request_t *request, fr_value_box_list_t *args)
Calculate the MD4 hash of a string or attribute.
static xlat_action_t xlat_func_explode(TALLOC_CTX *ctx, fr_dcursor_t *out, UNUSED xlat_ctx_t const *xctx, request_t *request, fr_value_box_list_t *args)
Split a string into multiple new strings based on a delimiter.
static xlat_action_t xlat_func_string(UNUSED TALLOC_CTX *ctx, fr_dcursor_t *out, UNUSED xlat_ctx_t const *xctx, UNUSED request_t *request, fr_value_box_list_t *in)
Print data as string, if possible.
static xlat_action_t xlat_func_substr(TALLOC_CTX *ctx, fr_dcursor_t *out, UNUSED xlat_ctx_t const *xctx, request_t *request, fr_value_box_list_t *args)
Extract a substring from string / octets data.
static xlat_action_t xlat_func_length(TALLOC_CTX *ctx, fr_dcursor_t *out, UNUSED xlat_ctx_t const *xctx, UNUSED request_t *request, fr_value_box_list_t *in)
Return the on-the-wire size of the boxes in bytes.
static xlat_action_t xlat_func_immutable_attr(UNUSED TALLOC_CTX *ctx, UNUSED fr_dcursor_t *out, UNUSED xlat_ctx_t const *xctx, request_t *request, fr_value_box_list_t *args)
Mark one or more attributes as immutable.
static xlat_action_t xlat_func_rand(TALLOC_CTX *ctx, fr_dcursor_t *out, UNUSED xlat_ctx_t const *xctx, UNUSED request_t *request, fr_value_box_list_t *in)
Generate a random integer value.
static xlat_action_t xlat_func_log_err(UNUSED TALLOC_CTX *ctx, UNUSED fr_dcursor_t *out, UNUSED xlat_ctx_t const *xctx, request_t *request, fr_value_box_list_t *args)
Log something at DEBUG level.
static xlat_action_t xlat_func_hmac_sha1(TALLOC_CTX *ctx, fr_dcursor_t *out, UNUSED xlat_ctx_t const *xctx, UNUSED request_t *request, fr_value_box_list_t *in)
Generate the HMAC-SHA1 of a string or attribute.
static xlat_action_t xlat_func_integer(TALLOC_CTX *ctx, fr_dcursor_t *out, UNUSED xlat_ctx_t const *xctx, request_t *request, fr_value_box_list_t *args)
Print data as integer, not as VALUE.
static xlat_action_t xlat_func_time(TALLOC_CTX *ctx, fr_dcursor_t *out, UNUSED xlat_ctx_t const *xctx, request_t *request, fr_value_box_list_t *args)
Return the time as a FR_TYPE_DATE.
static xlat_action_t xlat_func_toupper(TALLOC_CTX *ctx, fr_dcursor_t *out, UNUSED xlat_ctx_t const *xctx, request_t *request, fr_value_box_list_t *in)
Convert a string to uppercase.
static xlat_action_t xlat_func_cast(TALLOC_CTX *ctx, fr_dcursor_t *out, UNUSED xlat_ctx_t const *xctx, request_t *request, fr_value_box_list_t *args)
Cast one or more output value-boxes to the given type.
static xlat_action_t xlat_func_hex(UNUSED TALLOC_CTX *ctx, fr_dcursor_t *out, UNUSED xlat_ctx_t const *xctx, UNUSED request_t *request, fr_value_box_list_t *args)
Print data as hex, not as VALUE.
static xlat_action_t xlat_func_md5(TALLOC_CTX *ctx, fr_dcursor_t *out, UNUSED xlat_ctx_t const *xctx, UNUSED request_t *request, fr_value_box_list_t *args)
Calculate the MD5 hash of a string or attribute.
static xlat_action_t xlat_func_subnet_netmask(TALLOC_CTX *ctx, fr_dcursor_t *out, UNUSED xlat_ctx_t const *xctx, UNUSED request_t *request, fr_value_box_list_t *args)
Calculate the subnet mask from a IPv4 prefix.
static xlat_action_t xlat_func_sha1(TALLOC_CTX *ctx, fr_dcursor_t *out, UNUSED xlat_ctx_t const *xctx, UNUSED request_t *request, fr_value_box_list_t *args)
Calculate the SHA1 hash of a string or attribute.
static xlat_action_t xlat_func_str_printable(TALLOC_CTX *ctx, fr_dcursor_t *out, UNUSED xlat_ctx_t const *xctx, UNUSED request_t *request, fr_value_box_list_t *args)
Return whether a string has only printable chars.
static xlat_action_t xlat_func_randstr(TALLOC_CTX *ctx, fr_dcursor_t *out, UNUSED xlat_ctx_t const *xctx, request_t *request, fr_value_box_list_t *args)
Generate a string of random chars.
static xlat_action_t xlat_func_tolower(TALLOC_CTX *ctx, fr_dcursor_t *out, UNUSED xlat_ctx_t const *xctx, request_t *request, fr_value_box_list_t *in)
Convert a string to lowercase.
static xlat_action_t xlat_func_debug_attr(UNUSED TALLOC_CTX *ctx, UNUSED fr_dcursor_t *out, UNUSED xlat_ctx_t const *xctx, request_t *request, fr_value_box_list_t *args)
Print out attribute info.
static xlat_action_t xlat_func_subnet_broadcast(TALLOC_CTX *ctx, fr_dcursor_t *out, UNUSED xlat_ctx_t const *xctx, UNUSED request_t *request, fr_value_box_list_t *args)
Calculate the broadcast address from a IPv4 prefix.
static xlat_action_t xlat_func_pairs(TALLOC_CTX *ctx, fr_dcursor_t *out, UNUSED xlat_ctx_t const *xctx, request_t *request, fr_value_box_list_t *args)
Encode attributes as a series of string attribute/value pairs.
static xlat_action_t xlat_func_str_utf8(TALLOC_CTX *ctx, fr_dcursor_t *out, UNUSED xlat_ctx_t const *xctx, UNUSED request_t *request, fr_value_box_list_t *args)
Return whether a string is valid UTF-8.
static xlat_action_t xlat_func_strlen(TALLOC_CTX *ctx, fr_dcursor_t *out, UNUSED xlat_ctx_t const *xctx, UNUSED request_t *request, fr_value_box_list_t *args)
Print length of given string.
static xlat_action_t protocol_encode_xlat(TALLOC_CTX *ctx, fr_dcursor_t *out, xlat_ctx_t const *xctx, request_t *request, fr_value_box_list_t *args)
Encode protocol attributes / options.
Stores the state of the current iteration operation.
Definition hash.h:41
int fr_hmac_md5(uint8_t digest[MD5_DIGEST_LENGTH], uint8_t const *in, size_t inlen, uint8_t const *key, size_t key_len)
Calculate HMAC using internal MD5 implementation.
Definition hmac_md5.c:119
int fr_hmac_sha1(uint8_t digest[static SHA1_DIGEST_LENGTH], uint8_t const *in, size_t inlen, uint8_t const *key, size_t key_len)
Calculate HMAC using internal SHA1 implementation.
Definition hmac_sha1.c:124
TALLOC_CTX * unlang_interpret_frame_talloc_ctx(request_t *request)
Get a talloc_ctx which is valid only for this frame.
Definition interpret.c:1403
fr_event_list_t * unlang_interpret_event_list(request_t *request)
Get the event list for the current interpreter.
Definition interpret.c:1764
#define UNLANG_SUB_FRAME
Definition interpret.h:36
fr_log_t * log_dst_by_name(char const *name)
Get a logging destination by name.
Definition log.c:1074
#define PERROR(_fmt,...)
Definition log.h:228
#define REXDENT()
Exdent (unindent) R* messages by one level.
Definition log.h:443
#define RWDEBUG(fmt,...)
Definition log.h:361
#define RDEBUG_ENABLED3
True if request debug level 1-3 messages are enabled.
Definition log.h:335
#define REDEBUG3(fmt,...)
Definition log.h:373
#define RPERROR(fmt,...)
Definition log.h:302
#define REMARKER(_str, _marker_idx, _marker,...)
Output string with error marker, showing where format error occurred.
Definition log.h:498
#define RINFO(fmt,...)
Definition log.h:296
#define RMARKER(_type, _lvl, _str, _marker_idx, _marker,...)
Output string with error marker, showing where format error occurred.
Definition log.h:469
#define RPEDEBUG(fmt,...)
Definition log.h:376
#define RDEBUG4(fmt,...)
Definition log.h:344
#define RDEBUG_ENABLED4
True if request debug level 1-4 messages are enabled.
Definition log.h:336
#define RIDEBUG2(fmt,...)
Definition log.h:352
#define REDEBUG2(fmt,...)
Definition log.h:372
#define RIDEBUG3(fmt,...)
Definition log.h:353
#define RINDENT()
Indent R* messages by one level.
Definition log.h:430
int map_to_vp(TALLOC_CTX *ctx, fr_pair_list_t *out, request_t *request, map_t const *map, UNUSED void *uctx)
Convert a map to a fr_pair_t.
Definition map.c:1487
int map_to_request(request_t *request, map_t const *map, radius_map_getvalue_t func, void *ctx)
Convert map_t to fr_pair_t (s) and add them to a request_t.
Definition map.c:1781
int map_afrom_attr_str(TALLOC_CTX *ctx, map_t **out, char const *vp_str, tmpl_rules_t const *lhs_rules, tmpl_rules_t const *rhs_rules)
Convert a value pair string to valuepair map.
Definition map.c:1319
talloc_free(reap)
@ L_DST_NULL
Discard log messages.
Definition log.h:83
@ L_DST_FILES
Log to a file on disk.
Definition log.h:79
@ L_DBG_LVL_DISABLE
Don't print messages.
Definition log.h:68
@ L_DBG_LVL_2
2nd highest priority debug messages (-xx | -X).
Definition log.h:71
@ L_DBG_LVL_MAX
Lowest priority debug messages (-xxxxx | -Xxxx).
Definition log.h:74
@ L_WARN
Warning.
Definition log.h:57
void fr_md4_calc(uint8_t out[static MD4_DIGEST_LENGTH], uint8_t const *in, size_t inlen)
Calculate the MD4 hash of the contents of a buffer.
Definition md4.c:482
#define MD4_DIGEST_LENGTH
Definition md4.h:25
@ TMPL_ATTR_REF_PREFIX_AUTO
Attribute refs may have a '&' prefix.
#define MD5_DIGEST_LENGTH
unsigned short uint16_t
fr_type_t
@ FR_TYPE_TIME_DELTA
A period of time measured in nanoseconds.
@ FR_TYPE_IPV4_ADDR
32 Bit IPv4 Address.
@ FR_TYPE_INT8
8 Bit signed integer.
@ FR_TYPE_ETHERNET
48 Bit Mac-Address.
@ FR_TYPE_IPV6_PREFIX
IPv6 Prefix.
@ FR_TYPE_STRING
String of printable characters.
@ FR_TYPE_NULL
Invalid (uninitialised) attribute type.
@ FR_TYPE_UINT16
16 Bit unsigned integer.
@ FR_TYPE_INT64
64 Bit signed integer.
@ FR_TYPE_DATE
Unix time stamp, always has value >2^31.
@ FR_TYPE_UINT8
8 Bit unsigned integer.
@ FR_TYPE_UINT32
32 Bit unsigned integer.
@ FR_TYPE_INT32
32 Bit signed integer.
@ FR_TYPE_UINT64
64 Bit unsigned integer.
@ FR_TYPE_IPV6_ADDR
128 Bit IPv6 Address.
@ FR_TYPE_IPV4_PREFIX
IPv4 Prefix.
@ FR_TYPE_VOID
User data.
@ FR_TYPE_BOOL
A truth value.
@ FR_TYPE_SIZE
Unsigned integer capable of representing any memory address on the local system.
@ FR_TYPE_OCTETS
Raw octets.
@ FR_TYPE_GROUP
A grouping of other attributes.
unsigned int uint32_t
long int ssize_t
void fr_md5_calc(uint8_t out[static MD5_DIGEST_LENGTH], uint8_t const *in, size_t inlen)
Perform a single digest operation on a single input buffer.
unsigned char uint8_t
ssize_t fr_slen_t
long long int off_t
unsigned long int size_t
fr_sbuff_parse_error_t
size_t fr_snprint_uint128(char *out, size_t outlen, uint128_t const num)
Write 128bit unsigned integer to buffer.
Definition misc.c:369
struct tm * gmtime_r(time_t const *l_clock, struct tm *result)
Definition missing.c:201
struct tm * localtime_r(time_t const *l_clock, struct tm *result)
Definition missing.c:163
fr_slen_t fr_utf8_str(uint8_t const *str, ssize_t inlen)
Validate a complete UTF8 string.
Definition print.c:143
size_t fr_utf8_char(uint8_t const *str, ssize_t inlen)
Checks for utf-8, taken from http://www.w3.org/International/questions/qa-forms-utf-8.
Definition print.c:39
static fr_internal_encode_ctx_t encode_ctx
#define fr_assert(_expr)
Definition rad_assert.h:38
static bool done
Definition radclient.c:80
#define REDEBUG(fmt,...)
Definition radclient.h:52
#define RDEBUG_ENABLED2()
Definition radclient.h:50
#define RDEBUG(fmt,...)
Definition radclient.h:53
#define RIDEBUG(fmt,...)
Definition radsniff.h:65
#define fill(_expr)
uint32_t fr_rand(void)
Return a 32-bit random number.
Definition rand.c:105
fr_dict_attr_t const * request_attr_request
Definition request.c:45
void request_log_prepend(request_t *request, fr_log_t *log_dst, fr_log_lvl_t lvl)
Prepend another logging destination to the list.
Definition request.c:100
#define RAD_REQUEST_LVL_NONE
No debug messages should be printed.
Definition request.h:279
static char const * name
ssize_t fr_sbuff_in_escape(fr_sbuff_t *sbuff, char const *in, size_t inlen, fr_sbuff_escape_rules_t const *e_rules)
Print an escaped string to an sbuff.
Definition sbuff.c:1620
char * fr_sbuff_adv_to_str(fr_sbuff_t *sbuff, size_t len, char const *needle, size_t needle_len)
Wind position to the first instance of the specified needle.
Definition sbuff.c:1992
char * fr_sbuff_adv_to_chr(fr_sbuff_t *sbuff, size_t len, char c)
Wind position to first instance of specified char.
Definition sbuff.c:1956
ssize_t fr_sbuff_in_bstrncpy(fr_sbuff_t *sbuff, char const *str, size_t len)
Copy bytes into the sbuff up to the first \0.
Definition sbuff.c:1480
bool fr_sbuff_next_if_char(fr_sbuff_t *sbuff, char c)
Return true if the current char matches, and if it does, advance.
Definition sbuff.c:2088
#define fr_sbuff_start(_sbuff_or_marker)
#define fr_sbuff_set(_dst, _src)
#define FR_SBUFF_IN(_start, _len_or_end)
#define fr_sbuff_adv_past_whitespace(_sbuff, _len, _tt)
#define fr_sbuff_current(_sbuff_or_marker)
char const * name
Name for rule set to aid we debugging.
Definition sbuff.h:202
#define FR_SBUFF_SET_RETURN(_dst, _src)
#define FR_SBUFF(_sbuff_or_marker)
#define fr_sbuff_advance(_sbuff_or_marker, _len)
#define fr_sbuff_init_in(_out, _start, _len_or_end)
#define fr_sbuff_remaining(_sbuff_or_marker)
#define fr_sbuff_len(_sbuff_or_marker)
#define FR_SBUFF_OUT(_start, _len_or_end)
#define fr_sbuff_move(_out, _in, _len)
#define fr_sbuff_used(_sbuff_or_marker)
#define fr_sbuff_behind(_sbuff_or_marker)
#define fr_sbuff_ahead(_sbuff_or_marker)
#define fr_sbuff_in_char(_sbuff,...)
#define FR_SBUFF_TALLOC_THREAD_LOCAL(_out, _init, _max)
Set of parsing rules for *unescape_until functions.
static char const * tmpl_type_to_str(tmpl_type_t type)
Return a static string containing the type name.
Definition tmpl.h:645
static fr_slen_t vpt
Definition tmpl.h:1272
static fr_dict_attr_t const * tmpl_list(tmpl_t const *vpt)
Definition tmpl.h:915
ssize_t tmpl_afrom_attr_str(TALLOC_CTX *ctx, tmpl_attr_error_t *err, tmpl_t **out, char const *name, tmpl_rules_t const *rules))
Parse a string into a TMPL_TYPE_ATTR_* type tmpl_t.
@ TMPL_TYPE_ATTR
Reference to one or more attributes.
Definition tmpl.h:146
@ TMPL_TYPE_XLAT
Pre-parsed xlat expansion.
Definition tmpl.h:150
@ TMPL_TYPE_EXEC
Callout to an external script or program.
Definition tmpl.h:154
@ TMPL_TYPE_REGEX_XLAT_UNRESOLVED
A regular expression with unresolved xlat functions or attribute references.
Definition tmpl.h:201
@ TMPL_TYPE_DATA
Value in native boxed format.
Definition tmpl.h:142
@ TMPL_TYPE_DATA_UNRESOLVED
Unparsed literal string.
Definition tmpl.h:183
tmpl_attr_rules_t attr
Rules/data for parsing attribute references.
Definition tmpl.h:344
static char const * tmpl_list_name(fr_dict_attr_t const *list, char const *def)
Return the name of a tmpl list or def if list not provided.
Definition tmpl.h:926
Optional arguments passed to vp_tmpl functions.
Definition tmpl.h:341
void fr_sha1_init(fr_sha1_ctx *context)
Definition sha1.c:93
void fr_sha1_final(uint8_t digest[static SHA1_DIGEST_LENGTH], fr_sha1_ctx *context)
Definition sha1.c:141
void fr_sha1_update(fr_sha1_ctx *context, uint8_t const *in, size_t len)
Definition sha1.c:105
#define SHA1_DIGEST_LENGTH
Definition sha1.h:29
static char buff[sizeof("18446744073709551615")+3]
Definition size_tests.c:41
PUBLIC int snprintf(char *string, size_t length, char *format, va_alist)
Definition snprintf.c:689
PRIVATE void strings()
return count
Definition module.c:163
eap_aka_sim_process_conf_t * inst
fr_aka_sim_id_type_t type
fr_pair_t * vp
#define fr_time()
Allow us to arbitrarily manipulate time.
Definition state_test.c:8
size_t strlcpy(char *dst, char const *src, size_t siz)
Definition strlcpy.c:34
Definition log.h:96
fr_log_t * parent
Log destination this was cloned from.
Definition log.h:121
fr_log_dst_t dst
Log destination.
Definition log.h:97
int fd
File descriptor to write messages to.
Definition log.h:112
char const * file
Path to log file.
Definition log.h:113
Value pair map.
Definition map.h:77
tmpl_t * lhs
Typically describes the attribute to add, modify or compare.
Definition map.h:78
tmpl_t * rhs
Typically describes a literal value or a src attribute to copy or compare.
Definition map.h:79
fr_dict_t const * dict_def
Default dictionary to use with unqualified attribute references.
Definition tmpl.h:285
Stores an attribute, a value and various bits of other data.
Definition pair.h:68
fr_dict_attr_t const *_CONST da
Dictionary attribute defines the attribute number, vendor and type of the pair.
Definition pair.h:69
char const * fr_syserror(int num)
Guaranteed to be thread-safe version of strerror.
Definition syserror.c:243
#define fr_table_value_by_str(_table, _name, _def)
Convert a string to a value using a sorted or ordered table.
Definition table.h:653
An element in an arbitrarily ordered array of name to num mappings.
Definition table.h:57
char * talloc_bstrndup(TALLOC_CTX *ctx, char const *in, size_t inlen)
Binary safe strndup function.
Definition talloc.c:564
char * talloc_bstr_append(TALLOC_CTX *ctx, char *to, char const *from, size_t from_len)
Append a bstr to a bstr.
Definition talloc.c:592
#define talloc_get_type_abort_const
Definition talloc.h:282
fr_test_point_ctx_alloc_t test_ctx
Allocate a test ctx for the encoder.
Definition test_point.h:85
fr_pair_decode_t func
Decoder for pairs.
Definition test_point.h:86
Entry point for pair decoders.
Definition test_point.h:84
Entry point for pair encoders.
Definition test_point.h:92
bool fr_time_is_dst(void)
Whether or not we're daylight savings.
Definition time.c:1243
int fr_unix_time_from_str(fr_unix_time_t *date, char const *date_str, fr_time_res_t hint)
Convert string in various formats to a fr_unix_time_t.
Definition time.c:831
fr_time_delta_t fr_time_gmtoff(void)
Get the offset to gmt.
Definition time.c:1235
static int64_t fr_unix_time_to_sec(fr_unix_time_t delta)
Definition time.h:506
#define fr_time_delta_wrap(_time)
Definition time.h:152
@ FR_TIME_RES_SEC
Definition time.h:50
#define NSEC
Definition time.h:379
static uint64_t fr_unix_time_unwrap(fr_unix_time_t time)
Definition time.h:161
static fr_unix_time_t fr_time_to_unix_time(fr_time_t when)
Convert an fr_time_t (internal time) to our version of unix time (wallclock time)
Definition time.h:688
"Unix" time.
Definition time.h:95
void tmpl_dcursor_clear(tmpl_dcursor_ctx_t *cc)
Clear any temporary state allocations.
#define tmpl_dcursor_init(_err, _ctx, _cc, _cursor, _request, _vpt)
Maintains state between cursor calls.
xlat_arg_parser_t const trigger_xlat_args[]
Definition trigger.c:65
xlat_action_t trigger_xlat(TALLOC_CTX *ctx, fr_dcursor_t *out, UNUSED xlat_ctx_t const *xctx, request_t *request, fr_value_box_list_t *in)
Retrieve attributes from a special trigger list.
Definition trigger.c:73
close(uq->fd)
xlat_action_t unlang_xlat_yield(request_t *request, xlat_func_t resume, xlat_func_signal_t signal, fr_signal_t sigmask, void *rctx)
Yield a request back to the interpreter from within a module.
Definition xlat.c:573
int unlang_xlat_push(TALLOC_CTX *ctx, bool *p_success, fr_value_box_list_t *out, request_t *request, xlat_exp_head_t const *xlat, bool top_frame)
Push a pre-compiled xlat onto the stack for evaluation.
Definition xlat.c:286
void unlang_xlat_init(void)
Register xlat operation with the interpreter.
Definition xlat.c:827
fr_type_t type
Type to cast argument to.
Definition xlat.h:155
bool xlat_is_literal(xlat_exp_head_t const *head)
Check to see if the expansion consists entirely of value-box elements.
bool required
Argument must be present, and non-empty.
Definition xlat.h:148
@ XLAT_ARG_VARIADIC_EMPTY_KEEP
Empty argument groups are left alone, and either passed through as empty groups or null boxes.
Definition xlat.h:139
@ XLAT_ARG_VARIADIC_EMPTY_SQUASH
Empty argument groups are removed.
Definition xlat.h:138
bool concat
Concat boxes together.
Definition xlat.h:149
bool single
Argument must only contain a single box.
Definition xlat.h:150
#define XLAT_ARGS(_list,...)
Populate local variables with value boxes from the input list.
Definition xlat.h:381
int xlat_resolve(xlat_exp_head_t *head, xlat_res_rules_t const *xr_rules)
Walk over an xlat tree recursively, resolving any unresolved functions or references.
#define XLAT_ARG_PARSER_TERMINATOR
Definition xlat.h:168
xlat_action_t
Definition xlat.h:37
@ XLAT_ACTION_FAIL
An xlat function failed.
Definition xlat.h:44
@ XLAT_ACTION_YIELD
An xlat function pushed a resume frame onto the stack.
Definition xlat.h:42
@ XLAT_ACTION_PUSH_UNLANG
An xlat function pushed an unlang frame onto the unlang stack.
Definition xlat.h:39
fr_slen_t xlat_tokenize_expression(TALLOC_CTX *ctx, xlat_exp_head_t **head, fr_sbuff_t *in, fr_sbuff_parse_rules_t const *p_rules, tmpl_rules_t const *t_rules)
Definition xlat_expr.c:3165
Definition for a single argument consumend by an xlat function.
Definition xlat.h:147
static fr_slen_t fr_pair_aprint(TALLOC_CTX *ctx, char **out, fr_dict_attr_t const *parent, fr_pair_t const *vp) 1(fr_pair_print
fr_pair_t * fr_pair_list_next(fr_pair_list_t const *list, fr_pair_t const *item))
Get the next item in a valuepair list after a specific entry.
Definition pair_inline.c:70
static void fr_pair_set_immutable(fr_pair_t *vp)
Definition pair.h:688
void fr_strerror_clear(void)
Clears all pending messages from the talloc pools.
Definition strerror.c:577
#define fr_strerror_printf(_fmt,...)
Log to thread local error buffer.
Definition strerror.h:64
fr_table_num_ordered_t const fr_type_table[]
Map data types to names representing those types.
Definition types.c:31
size_t fr_type_table_len
Definition types.c:84
#define fr_type_is_string(_x)
Definition types.h:327
#define fr_type_is_numeric(_x)
Definition types.h:361
#define FR_TYPE_STRUCTURAL
Definition types.h:296
#define fr_type_is_null(_x)
Definition types.h:326
#define fr_type_is_leaf(_x)
Definition types.h:372
static char const * fr_type_to_str(fr_type_t type)
Return a static string containing the type name.
Definition types.h:433
#define fr_type_is_integer(_x)
Definition types.h:360
#define FR_TYPE_LEAF
Definition types.h:297
#define FR_TYPE_NUMERIC
Definition types.h:286
size_t fr_value_box_network_length(fr_value_box_t const *value)
Get the size of the value held by the fr_value_box_t.
Definition value.c:1325
ssize_t fr_value_box_print(fr_sbuff_t *out, fr_value_box_t const *data, fr_sbuff_escape_rules_t const *e_rules)
Print one boxed value to a string.
Definition value.c:5352
int fr_value_box_mem_alloc(TALLOC_CTX *ctx, uint8_t **out, fr_value_box_t *dst, fr_dict_attr_t const *enumv, size_t len, bool tainted)
Pre-allocate an octets buffer for filling by the caller.
Definition value.c:4371
int fr_value_box_cast(TALLOC_CTX *ctx, fr_value_box_t *dst, fr_type_t dst_type, fr_dict_attr_t const *dst_enumv, fr_value_box_t const *src)
Convert one type of fr_value_box_t to another.
Definition value.c:3352
char * fr_value_box_list_aprint(TALLOC_CTX *ctx, fr_value_box_list_t const *list, char const *delim, fr_sbuff_escape_rules_t const *e_rules)
Concatenate the string representations of a list of value boxes together.
Definition value.c:6028
int fr_value_box_mem_realloc(TALLOC_CTX *ctx, uint8_t **out, fr_value_box_t *dst, size_t len)
Change the length of a buffer already allocated to a value box.
Definition value.c:4404
void fr_value_box_list_untaint(fr_value_box_list_t *head)
Untaint every list member (and their children)
Definition value.c:6216
int fr_value_box_cast_in_place(TALLOC_CTX *ctx, fr_value_box_t *vb, fr_type_t dst_type, fr_dict_attr_t const *dst_enumv)
Convert one type of fr_value_box_t to another in place.
Definition value.c:3572
void fr_value_box_clear_value(fr_value_box_t *data)
Clear/free any existing value.
Definition value.c:3681
int fr_value_box_strdup(TALLOC_CTX *ctx, fr_value_box_t *dst, fr_dict_attr_t const *enumv, char const *src, bool tainted)
Copy a nul terminated string to a fr_value_box_t.
Definition value.c:3927
void fr_value_box_strdup_shallow(fr_value_box_t *dst, fr_dict_attr_t const *enumv, char const *src, bool tainted)
Assign a buffer containing a nul terminated string to a box, but don't copy it.
Definition value.c:4036
int fr_value_box_bstr_alloc(TALLOC_CTX *ctx, char **out, fr_value_box_t *dst, fr_dict_attr_t const *enumv, size_t len, bool tainted)
Alloc and assign an empty \0 terminated string to a fr_value_box_t.
Definition value.c:4071
ssize_t fr_value_box_list_concat_as_string(bool *tainted, bool *secret, fr_sbuff_t *sbuff, fr_value_box_list_t *list, char const *sep, size_t sep_len, fr_sbuff_escape_rules_t const *e_rules, fr_value_box_list_action_t proc_action, fr_value_box_safe_for_t safe_for, bool flatten)
Concatenate a list of value boxes together.
Definition value.c:5584
bool fr_value_box_list_tainted(fr_value_box_list_t const *head)
Check to see if any list members (or their children) are tainted.
Definition value.c:6186
int fr_value_box_bstr_realloc(TALLOC_CTX *ctx, char **out, fr_value_box_t *dst, size_t len)
Change the length of a buffer already allocated to a value box.
Definition value.c:4104
int fr_value_box_bstrndup(TALLOC_CTX *ctx, fr_value_box_t *dst, fr_dict_attr_t const *enumv, char const *src, size_t len, bool tainted)
Copy a string to to a fr_value_box_t.
Definition value.c:4148
int fr_value_box_bstrdup_buffer_shallow(TALLOC_CTX *ctx, fr_value_box_t *dst, fr_dict_attr_t const *enumv, char const *src, bool tainted)
Assign a talloced buffer containing a nul terminated string to a box, but don't copy it.
Definition value.c:4253
int fr_value_box_memdup(TALLOC_CTX *ctx, fr_value_box_t *dst, fr_dict_attr_t const *enumv, uint8_t const *src, size_t len, bool tainted)
Copy a buffer to a fr_value_box_t.
Definition value.c:4468
int fr_value_box_list_concat_in_place(TALLOC_CTX *ctx, fr_value_box_t *out, fr_value_box_list_t *list, fr_type_t type, fr_value_box_list_action_t proc_action, bool flatten, size_t max_size)
Concatenate a list of value boxes.
Definition value.c:5777
@ FR_VALUE_BOX_LIST_FREE
Definition value.h:221
@ FR_VALUE_BOX_LIST_FREE_BOX
Free each processed box.
Definition value.h:218
#define fr_value_box_list_foreach_safe(_list_head, _iter)
Definition value.h:207
#define fr_value_box_alloc(_ctx, _type, _enumv)
Allocate a value box of a specific type.
Definition value.h:621
static bool fr_value_box_is_secret(fr_value_box_t const *box)
Definition value.h:1060
static fr_slen_t data
Definition value.h:1265
static void fr_value_box_set_secret(fr_value_box_t *box, bool secret)
Definition value.h:1080
#define fr_box_is_variable_size(_x)
Definition value.h:441
#define VALUE_BOX_LIST_VERIFY(_x)
Definition value.h:1296
#define fr_value_box_alloc_null(_ctx)
Allocate a value box for later use with a value assignment function.
Definition value.h:632
#define fr_value_box_list_foreach(_list_head, _iter)
Definition value.h:206
static size_t char ** out
Definition value.h:997
#define fr_box_bool(_val)
Definition value.h:308
static xlat_arg_parser_t const xlat_func_bin_arg[]
static int xlat_protocol_register_cbor(void)
static xlat_arg_parser_t const xlat_func_map_arg[]
static xlat_arg_parser_t const xlat_func_log_dst_args[]
static xlat_arg_parser_t const protocol_decode_xlat_args[]
static xlat_arg_parser_t const xlat_func_time_args[]
static xlat_arg_parser_t const xlat_func_base64_encode_arg[]
static xlat_arg_parser_t const xlat_func_debug_attr_args[]
static int _log_dst_free(fr_log_t *log)
static xlat_action_t xlat_func_file_size(TALLOC_CTX *ctx, fr_dcursor_t *out, UNUSED xlat_ctx_t const *xctx, request_t *request, fr_value_box_list_t *in)
static xlat_action_t xlat_hmac(TALLOC_CTX *ctx, fr_dcursor_t *out, fr_value_box_list_t *args, uint8_t *digest, int digest_len, hmac_type type)
static xlat_arg_parser_t const xlat_func_log_arg[]
static xlat_arg_parser_t const xlat_func_sha_arg[]
static xlat_arg_parser_t const xlat_func_cast_args[]
hmac_type
@ HMAC_MD5
@ HMAC_SHA1
xlat_action_t xlat_transparent(UNUSED TALLOC_CTX *ctx, fr_dcursor_t *out, UNUSED xlat_ctx_t const *xctx, UNUSED request_t *request, fr_value_box_list_t *args)
Common function to move boxes form input list to output list.
static xlat_arg_parser_t const xlat_func_hex_arg[]
static xlat_arg_parser_t const xlat_func_pairs_args[]
static ssize_t xlat_file_escape_path(fr_sbuff_t *in, fr_value_box_t *vb)
Escape the paths as necessary.
static xlat_arg_parser_t const xlat_func_substr_args[]
static xlat_action_t xlat_func_file_exists(TALLOC_CTX *ctx, fr_dcursor_t *out, UNUSED xlat_ctx_t const *xctx, UNUSED request_t *request, fr_value_box_list_t *args)
static xlat_action_t xlat_func_join(UNUSED TALLOC_CTX *ctx, fr_dcursor_t *out, UNUSED xlat_ctx_t const *xctx, UNUSED request_t *request, fr_value_box_list_t *in)
Join a series of arguments to form a single list.
static xlat_arg_parser_t const xlat_func_subnet_args[]
#define XLAT_REGISTER_PURE(_xlat, _func, _return_type, _arg)
static xlat_arg_parser_t const xlat_func_str_printable_arg[]
static xlat_arg_parser_t const xlat_func_randstr_arg[]
static xlat_arg_parser_t const xlat_func_eval_arg[]
static xlat_arg_parser_t const xlat_func_subst_args[]
static xlat_arg_parser_t const xlat_func_explode_args[]
int xlat_protocols_register(void)
Register xlats for any loaded dictionaries.
static xlat_arg_parser_t const xlat_func_str_utf8_arg[]
#define REPETITION_MAX
void xlat_debug_attr_list(request_t *request, fr_pair_list_t const *list)
static const fr_sbuff_escape_rules_t xlat_filename_escape_dots
static dl_loader_t * cbor_loader
static xlat_arg_parser_t const xlat_change_case_arg[]
static xlat_arg_parser_t const xlat_func_strlen_arg[]
static int xlat_protocol_register(fr_dict_t const *dict)
static xlat_arg_parser_t const xlat_func_md5_arg[]
int xlat_global_init(void)
static xlat_arg_parser_t const xlat_func_urlquote_arg[]
static void ungroup(fr_dcursor_t *out, fr_value_box_list_t *in)
static xlat_arg_parser_t const xlat_func_md4_arg[]
static xlat_arg_parser_t const xlat_func_immutable_attr_args[]
static xlat_arg_parser_t const xlat_func_join_args[]
static xlat_action_t xlat_eval_resume(UNUSED TALLOC_CTX *ctx, UNUSED fr_dcursor_t *out, xlat_ctx_t const *xctx, UNUSED request_t *request, UNUSED fr_value_box_list_t *in)
Just serves to push the result up the stack.
static xlat_action_t xlat_func_taint(UNUSED TALLOC_CTX *ctx, fr_dcursor_t *out, UNUSED xlat_ctx_t const *xctx, UNUSED request_t *request, fr_value_box_list_t *in)
static xlat_arg_parser_t const xlat_func_debug_args[]
static char const hextab[]
static xlat_action_t xlat_func_file_head(TALLOC_CTX *ctx, fr_dcursor_t *out, UNUSED xlat_ctx_t const *xctx, request_t *request, fr_value_box_list_t *in)
static xlat_arg_parser_t const xlat_func_pad_args[]
static xlat_arg_parser_t const protocol_encode_xlat_args[]
static int xlat_eval_instantiate(xlat_inst_ctx_t const *xctx)
static xlat_arg_parser_t const xlat_func_urlunquote_arg[]
static xlat_action_t xlat_func_file_escape(TALLOC_CTX *ctx, fr_dcursor_t *out, UNUSED xlat_ctx_t const *xctx, UNUSED request_t *request, fr_value_box_list_t *args)
static int xlat_protocol_register_by_name(dl_t *dl, char const *name)
void xlat_debug_attr_vp(request_t *request, fr_pair_t *vp, tmpl_t const *vpt)
static xlat_arg_parser_t const xlat_func_rand_arg[]
static xlat_action_t xlat_func_file_tail(TALLOC_CTX *ctx, fr_dcursor_t *out, UNUSED xlat_ctx_t const *xctx, request_t *request, fr_value_box_list_t *in)
static xlat_arg_parser_t const xlat_func_concat_args[]
static xlat_arg_parser_t const xlat_func_file_name_count_args[]
static xlat_arg_parser_t const xlat_func_integer_args[]
static int _xlat_global_init(UNUSED void *uctx)
Global initialisation for xlat.
#define XLAT_REGISTER_ARGS(_xlat, _func, _return_type, _args)
static xlat_action_t xlat_func_untaint(UNUSED TALLOC_CTX *ctx, fr_dcursor_t *out, UNUSED xlat_ctx_t const *xctx, UNUSED request_t *request, fr_value_box_list_t *in)
xlat_exp_head_t * ex
static xlat_arg_parser_t const xlat_func_length_args[]
static const char * xlat_file_name(fr_value_box_t *vb)
static xlat_action_t xlat_change_case(UNUSED TALLOC_CTX *ctx, fr_dcursor_t *out, UNUSED request_t *request, fr_value_box_list_t *args, bool upper)
Change case of a string.
static xlat_action_t xlat_func_ungroup(UNUSED TALLOC_CTX *ctx, fr_dcursor_t *out, UNUSED xlat_ctx_t const *xctx, UNUSED request_t *request, fr_value_box_list_t *in)
Ungroups all of its arguments into one flat list.
static int protocol_xlat_instantiate(xlat_inst_ctx_t const *mctx)
return XLAT_ACTION_DONE
static xlat_arg_parser_t const xlat_func_file_name_args[]
static TALLOC_CTX * xlat_ctx
static xlat_arg_parser_t const xlat_func_next_time_args[]
static int _xlat_global_free(UNUSED void *uctx)
De-register all xlat functions we created.
static const fr_sbuff_escape_rules_t xlat_filename_escape
static xlat_arg_parser_t const xlat_func_base64_decode_arg[]
static xlat_arg_parser_t const xlat_hmac_args[]
static xlat_action_t xlat_func_file_rm(TALLOC_CTX *ctx, fr_dcursor_t *out, UNUSED xlat_ctx_t const *xctx, request_t *request, fr_value_box_list_t *in)
void * rctx
Resume context.
Definition xlat_ctx.h:54
xlat_exp_t * ex
Tokenized expression to use in expansion.
Definition xlat_ctx.h:63
void const * inst
xlat instance data.
Definition xlat_ctx.h:50
void * uctx
Passed to the registration function.
Definition xlat_ctx.h:65
void * inst
xlat instance data to populate.
Definition xlat_ctx.h:62
An xlat calling ctx.
Definition xlat_ctx.h:49
An xlat instantiation ctx.
Definition xlat_ctx.h:61
fr_dict_attr_t const * xlat_time_res_attr(char const *res)
Definition xlat_eval.c:134
int xlat_eval_init(void)
Definition xlat_eval.c:1723
void xlat_eval_free(void)
Definition xlat_eval.c:1748
int xlat_register_expressions(void)
Definition xlat_expr.c:1877
void xlat_func_free(void)
Definition xlat_func.c:566
void xlat_func_flags_set(xlat_t *x, xlat_func_flags_t flags)
Specify flags that alter the xlat's behaviour.
Definition xlat_func.c:402
int xlat_func_args_set(xlat_t *x, xlat_arg_parser_t const args[])
Register the arguments of an xlat.
Definition xlat_func.c:365
xlat_t * xlat_func_register(TALLOC_CTX *ctx, char const *name, xlat_func_t func, fr_type_t return_type)
Register an xlat function.
Definition xlat_func.c:218
int xlat_func_init(void)
Definition xlat_func.c:550
xlat_t * xlat_func_find(char const *in, ssize_t inlen)
Definition xlat_func.c:79
#define xlat_func_instantiate_set(_xlat, _instantiate, _inst_struct, _detach, _uctx)
Set a callback for global instantiation of xlat functions.
Definition xlat_func.h:93
#define xlat_func_safe_for_set(_xlat, _escaped)
Set the escaped values for output boxes.
Definition xlat_func.h:82
@ XLAT_FUNC_FLAG_PURE
Definition xlat_func.h:38
@ XLAT_FUNC_FLAG_INTERNAL
Definition xlat_func.h:39
int xlat_decode_value_box_list(TALLOC_CTX *ctx, fr_pair_list_t *out, request_t *request, void *decode_ctx, fr_pair_decode_t decode, fr_value_box_list_t *in)
Decode all of the value boxes into the output cursor.
Definition xlat_pair.c:90
@ XLAT_GROUP
encapsulated string of xlats
Definition xlat_priv.h:117
xlat_type_t _CONST type
type of this expansion.
Definition xlat_priv.h:158
An xlat expansion node.
Definition xlat_priv.h:151