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: 0e9f9fc023a161da1958b383f9b80f0adf5804aa $
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: 0e9f9fc023a161da1958b383f9b80f0adf5804aa $")
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 { .required = true, .type = FR_TYPE_VOID },
730 { .variadic = XLAT_ARG_VARIADIC_EMPTY_KEEP, .type = FR_TYPE_VOID },
732};
733
735 UNUSED xlat_ctx_t const *xctx,
736 UNUSED request_t *request, fr_value_box_list_t *in)
737{
738 fr_value_box_t *vb;
739
741 while ((vb = fr_value_box_list_pop_head(in)) != NULL) {
743 }
744
745 return XLAT_ACTION_DONE;
746}
747
749 UNUSED xlat_ctx_t const *xctx,
750 UNUSED request_t *request, fr_value_box_list_t *in)
751{
752 fr_value_box_t *vb;
753
754 while ((vb = fr_value_box_list_pop_head(in)) != NULL) {
755 fr_value_box_t *child;
756
757 fr_assert(vb->type == FR_TYPE_GROUP);
758
759 while ((child = fr_value_box_list_pop_head(&vb->vb_group)) != NULL) {
760 child->tainted = true;
761
762 fr_dcursor_append(out, child);
763 }
764 }
765
766 return XLAT_ACTION_DONE;
767}
768
770 { .required = true, .type = FR_TYPE_STRING },
771 { .required = true, .concat = true, .type = FR_TYPE_STRING },
773};
774
775/** Split a string into multiple new strings based on a delimiter
776 *
777@verbatim
778%explode(<string>, <delim>)
779@endverbatim
780 *
781 * Example:
782@verbatim
783update request {
784 &Tmp-String-1 := "a,b,c"
785}
786"%concat(%explode(%{Tmp-String-1}, ','), '|')" == "a|b|c"g
787@endverbatim
788 *
789 * @ingroup xlat_functions
790 */
792 UNUSED xlat_ctx_t const *xctx,
793 request_t *request, fr_value_box_list_t *args)
794{
796 fr_value_box_list_t *list;
797 fr_value_box_t *delim_vb;
798 ssize_t delim_len;
799 char const *delim;
800 fr_value_box_t *string, *vb;
801
802 XLAT_ARGS(args, &strings, &delim_vb);
803
804 list = &strings->vb_group;
805
806 /* coverity[dereference] */
807 if (delim_vb->vb_length == 0) {
808 REDEBUG("Delimiter must be greater than zero characters");
809 return XLAT_ACTION_FAIL;
810 }
811
812 delim = delim_vb->vb_strvalue;
813 delim_len = delim_vb->vb_length;
814
815 while((string = fr_value_box_list_pop_head(list))) {
816 fr_sbuff_t sbuff = FR_SBUFF_IN(string->vb_strvalue, string->vb_length);
817 fr_sbuff_marker_t m_start;
818
819 /*
820 * If the delimiter is not in the string, just move to the output
821 */
822 if (!fr_sbuff_adv_to_str(&sbuff, SIZE_MAX, delim, delim_len)) {
823 fr_dcursor_append(out, string);
824 continue;
825 }
826
827 fr_sbuff_set_to_start(&sbuff);
828 fr_sbuff_marker(&m_start, &sbuff);
829
830 while (fr_sbuff_remaining(&sbuff)) {
831 if (fr_sbuff_adv_to_str(&sbuff, SIZE_MAX, delim, delim_len)) {
832 /*
833 * If there's nothing before the delimiter skip
834 */
835 if (fr_sbuff_behind(&m_start) == 0) goto advance;
836
837 MEM(vb = fr_value_box_alloc_null(ctx));
838 fr_value_box_bstrndup(vb, vb, NULL, fr_sbuff_current(&m_start),
839 fr_sbuff_behind(&m_start), string->tainted);
841
842 advance:
843 fr_sbuff_advance(&sbuff, delim_len);
844 fr_sbuff_set(&m_start, &sbuff);
845 continue;
846 }
847 fr_sbuff_set_to_end(&sbuff);
848 MEM(vb = fr_value_box_alloc_null(ctx));
849 fr_value_box_bstrndup(vb, vb, NULL, fr_sbuff_current(&m_start),
850 fr_sbuff_behind(&m_start), string->tainted);
852 break;
853 }
854 talloc_free(string);
855 }
856
857 return XLAT_ACTION_DONE;
858}
859
861 { .required = true, .single = true, .type = FR_TYPE_STRING },
863};
864
865/** Mark one or more attributes as immutable
866 *
867 * Example:
868@verbatim
869%immutable(&request.State[*])
870@endverbatim
871 *
872 * @ingroup xlat_functions
873 */
875 UNUSED xlat_ctx_t const *xctx,
876 request_t *request, fr_value_box_list_t *args)
877{
878 fr_pair_t *vp;
879 fr_dcursor_t cursor;
881 tmpl_t *vpt;
882 fr_value_box_t *attr;
883 char const *fmt;
884
885 XLAT_ARGS(args, &attr);
886
887 fmt = attr->vb_strvalue;
888
889 if (tmpl_afrom_attr_str(request, NULL, &vpt, fmt,
890 &(tmpl_rules_t){
891 .attr = {
892 .dict_def = request->dict,
893 .list_def = request_attr_request,
894 .allow_wildcard = true,
896 }
897 }) <= 0) {
898 RPEDEBUG("Invalid input");
899 return XLAT_ACTION_FAIL;
900 }
901
902 RIDEBUG("Attributes matching \"%s\"", fmt);
903
904 RINDENT();
905 for (vp = tmpl_dcursor_init(NULL, NULL, &cc, &cursor, request, vpt);
906 vp;
907 vp = fr_dcursor_next(&cursor)) {
909 }
911 REXDENT();
912
914
915 return XLAT_ACTION_DONE;
916}
917
919 { .required = true, .single = true, .type = FR_TYPE_VOID },
921};
922
923/** Print data as integer, not as VALUE.
924 *
925 * Example:
926@verbatim
927update request {
928 &Tmp-IP-Address-0 := "127.0.0.5"
929}
930%integer(%{Tmp-IP-Address-0}) == 2130706437
931@endverbatim
932 * @ingroup xlat_functions
933 */
935 UNUSED xlat_ctx_t const *xctx,
936 request_t *request, fr_value_box_list_t *args)
937{
938 fr_value_box_t *in_vb;
939 char const *p;
940
941 XLAT_ARGS(args, &in_vb);
942
943 fr_strerror_clear(); /* Make sure we don't print old errors */
944
945 fr_value_box_list_remove(args, in_vb);
946
947 switch (in_vb->type) {
948 default:
949 error:
950 RPEDEBUG("Failed converting %pV (%s) to an integer", in_vb,
951 fr_type_to_str(in_vb->type));
952 talloc_free(in_vb);
953 return XLAT_ACTION_FAIL;
954
955 case FR_TYPE_NUMERIC:
956 /*
957 * Ensure enumeration is NULL so that the integer
958 * version of a box is returned
959 */
960 in_vb->enumv = NULL;
961
962 /*
963 * FR_TYPE_DATE and FR_TYPE_TIME_DELTA need to be cast
964 * to int64_t so that they're printed in a
965 * numeric format.
966 */
967 if ((in_vb->type == FR_TYPE_DATE) || (in_vb->type == FR_TYPE_TIME_DELTA)) {
968 if (fr_value_box_cast_in_place(ctx, in_vb, FR_TYPE_INT64, NULL) < 0) goto error;
969 }
970 break;
971
972 case FR_TYPE_STRING:
973 /*
974 * Strings are always zero terminated. They may
975 * also have zeros in the middle, but if that
976 * happens, the caller will only get the part up
977 * to the first zero.
978 *
979 * We check for negative numbers, just to be
980 * nice.
981 */
982 for (p = in_vb->vb_strvalue; *p != '\0'; p++) {
983 if (*p == '-') break;
984 }
985
986 if (*p == '-') {
987 if (fr_value_box_cast_in_place(ctx, in_vb, FR_TYPE_INT64, NULL) < 0) goto error;
988 } else {
989 if (fr_value_box_cast_in_place(ctx, in_vb, FR_TYPE_UINT64, NULL) < 0) goto error;
990 }
991 break;
992
993 case FR_TYPE_OCTETS:
994 if (in_vb->vb_length > sizeof(uint64_t)) {
995 fr_strerror_printf("Expected octets length <= %zu, got %zu", sizeof(uint64_t), in_vb->vb_length);
996 goto error;
997 }
998
999 if (in_vb->vb_length > sizeof(uint32_t)) {
1000 if (unlikely(fr_value_box_cast_in_place(ctx, in_vb, FR_TYPE_UINT64, NULL) < 0)) goto error;
1001 } else if (in_vb->vb_length > sizeof(uint16_t)) {
1002 if (unlikely(fr_value_box_cast_in_place(ctx, in_vb, FR_TYPE_UINT32, NULL) < 0)) goto error;
1003 } else if (in_vb->vb_length > sizeof(uint8_t)) {
1004 if (unlikely(fr_value_box_cast_in_place(ctx, in_vb, FR_TYPE_UINT16, NULL) < 0)) goto error;
1005 } else {
1006 if (unlikely(fr_value_box_cast_in_place(ctx, in_vb, FR_TYPE_UINT8, NULL) < 0)) goto error;
1007 }
1008
1009 break;
1010
1011 case FR_TYPE_IPV4_ADDR:
1013 if (fr_value_box_cast_in_place(ctx, in_vb, FR_TYPE_UINT32, NULL) < 0) goto error;
1014 break;
1015
1016 case FR_TYPE_ETHERNET:
1017 if (fr_value_box_cast_in_place(ctx, in_vb, FR_TYPE_UINT64, NULL) < 0) goto error;
1018 break;
1019
1020 case FR_TYPE_IPV6_ADDR:
1022 {
1023 uint128_t ipv6int;
1024 char buff[40];
1025 fr_value_box_t *vb;
1026
1027 /*
1028 * Needed for correct alignment (as flagged by ubsan)
1029 */
1030 memcpy(&ipv6int, &in_vb->vb_ip.addr.v6.s6_addr, sizeof(ipv6int));
1031
1032 fr_snprint_uint128(buff, sizeof(buff), ntohlll(ipv6int));
1033
1034 MEM(vb = fr_value_box_alloc_null(ctx));
1035 fr_value_box_bstrndup(vb, vb, NULL, buff, strlen(buff), false);
1037 talloc_free(in_vb);
1038 return XLAT_ACTION_DONE;
1039 }
1040 }
1041
1042 fr_dcursor_append(out, in_vb);
1043
1044 return XLAT_ACTION_DONE;
1045}
1046
1048 { .concat = true, .type = FR_TYPE_STRING },
1050};
1051
1052/** Log something at INFO level.
1053 *
1054 * Example:
1055@verbatim
1056%log("This is an informational message")
1057@endverbatim
1058 *
1059 * @ingroup xlat_functions
1060 */
1062 UNUSED xlat_ctx_t const *xctx,
1063 request_t *request, fr_value_box_list_t *args)
1064{
1065 fr_value_box_t *vb;
1066
1067 XLAT_ARGS(args, &vb);
1068
1069 if (!vb) return XLAT_ACTION_DONE;
1070
1071 RINFO("%s", vb->vb_strvalue);
1072
1073 return XLAT_ACTION_DONE;
1074}
1075
1076
1077/** Log something at DEBUG level.
1078 *
1079 * Example:
1080@verbatim
1081%log.debug("This is a message")
1082@endverbatim
1083 *
1084 * @ingroup xlat_functions
1085 */
1087 UNUSED xlat_ctx_t const *xctx,
1088 request_t *request, fr_value_box_list_t *args)
1089{
1090 fr_value_box_t *vb;
1091
1092 XLAT_ARGS(args, &vb);
1093
1094 if (!vb) return XLAT_ACTION_DONE;
1095
1096 RDEBUG("%s", vb->vb_strvalue);
1097
1098 return XLAT_ACTION_DONE;
1099}
1100
1101
1102/** Log something at DEBUG level.
1103 *
1104 * Example:
1105@verbatim
1106%log.err("Big error here")
1107@endverbatim
1108 *
1109 * @ingroup xlat_functions
1110 */
1112 UNUSED xlat_ctx_t const *xctx,
1113 request_t *request, fr_value_box_list_t *args)
1114{
1115 fr_value_box_t *vb;
1116
1117 XLAT_ARGS(args, &vb);
1118
1119 if (!vb) return XLAT_ACTION_DONE;
1120
1121 REDEBUG("%s", vb->vb_strvalue);
1122
1123 return XLAT_ACTION_DONE;
1124}
1125
1126
1127/** Log something at WARN level.
1128 *
1129 * Example:
1130@verbatim
1131%log.warn("Maybe something bad happened")
1132@endverbatim
1133 *
1134 * @ingroup xlat_functions
1135 */
1137 UNUSED xlat_ctx_t const *xctx,
1138 request_t *request, fr_value_box_list_t *args)
1139{
1140 fr_value_box_t *vb;
1141
1142 XLAT_ARGS(args, &vb);
1143
1144 if (!vb) return XLAT_ACTION_DONE;
1145
1146 RWDEBUG("%s", vb->vb_strvalue);
1147
1148 return XLAT_ACTION_DONE;
1149}
1150
1151static int _log_dst_free(fr_log_t *log)
1152{
1153 close(log->fd);
1154 return 0;
1155}
1156
1158 { .required = false, .type = FR_TYPE_STRING, .concat = true },
1159 { .required = false, .type = FR_TYPE_UINT32, .single = true },
1160 { .required = false, .type = FR_TYPE_STRING, .concat = true },
1162};
1163
1164/** Change the log destination to the named one
1165 *
1166 * Example:
1167@verbatim
1168%log.destination('foo')
1169@endverbatim
1170 *
1171 * @ingroup xlat_functions
1172 */
1174 UNUSED xlat_ctx_t const *xctx,
1175 request_t *request, fr_value_box_list_t *args)
1176{
1177 fr_value_box_t *dst, *lvl, *file;
1178 fr_log_t *log, *dbg;
1179 uint32_t level = 2;
1180
1181 XLAT_ARGS(args, &dst, &lvl, &file);
1182
1183 if (!dst || !*dst->vb_strvalue) {
1184 request_log_prepend(request, NULL, L_DBG_LVL_DISABLE);
1185 return XLAT_ACTION_DONE;
1186 }
1187
1188 log = log_dst_by_name(dst->vb_strvalue);
1189 if (!log) return XLAT_ACTION_FAIL;
1190
1191 if (lvl) level = lvl->vb_uint32;
1192
1193 if (!file || ((log->dst != L_DST_NULL) && (log->dst != L_DST_FILES))) {
1194 request_log_prepend(request, log, level);
1195 return XLAT_ACTION_DONE;
1196 }
1197
1198 /*
1199 * Clone it.
1200 */
1201 MEM(dbg = talloc_memdup(request, log, sizeof(*log)));
1202 dbg->parent = log;
1203
1204 /*
1205 * Open the new filename.
1206 */
1207 dbg->dst = L_DST_FILES;
1208 dbg->file = talloc_strdup(dbg, file->vb_strvalue);
1209 dbg->fd = open(dbg->file, O_WRONLY | O_CREAT | O_CLOEXEC, 0600);
1210 if (dbg->fd < 0) {
1211 REDEBUG("Failed opening %s - %s", dbg->file, fr_syserror(errno));
1212 talloc_free(dbg);
1213 return XLAT_ACTION_DONE;
1214 }
1215
1216 /*
1217 * Ensure that we close the file handle when done.
1218 */
1219 talloc_set_destructor(dbg, _log_dst_free);
1220
1221 request_log_prepend(request, dbg, level);
1222 return XLAT_ACTION_DONE;
1223}
1224
1225
1227 { .required = true, .concat = true, .type = FR_TYPE_STRING },
1229};
1230
1231/** Processes fmt as a map string and applies it to the current request
1232 *
1233 * e.g.
1234@verbatim
1235%map("User-Name := 'foo'")
1236@endverbatim
1237 *
1238 * Allows sets of modifications to be cached and then applied.
1239 * Useful for processing generic attributes from LDAP.
1240 *
1241 * @ingroup xlat_functions
1242 */
1244 UNUSED xlat_ctx_t const *xctx,
1245 request_t *request, fr_value_box_list_t *args)
1246{
1247 map_t *map = NULL;
1248 int ret;
1249 fr_value_box_t *fmt_vb;
1250 fr_value_box_t *vb;
1251
1252 tmpl_rules_t attr_rules = {
1253 .attr = {
1254 .dict_def = request->dict,
1255 .list_def = request_attr_request,
1257 },
1258 .xlat = {
1259 .runtime_el = unlang_interpret_event_list(request)
1260 }
1261 };
1262
1263 XLAT_ARGS(args, &fmt_vb);
1264
1265 if (map_afrom_attr_str(request, &map, fmt_vb->vb_strvalue, &attr_rules, &attr_rules) < 0) {
1266 RPEDEBUG("Failed parsing \"%s\" as map", fmt_vb->vb_strvalue);
1267 return XLAT_ACTION_FAIL;
1268 }
1269
1270 MEM(vb = fr_value_box_alloc(ctx, FR_TYPE_INT8, NULL));
1271 vb->vb_int8 = 0; /* Default fail value - changed to 1 on success */
1273
1274 switch (map->lhs->type) {
1275 case TMPL_TYPE_ATTR:
1276 case TMPL_TYPE_XLAT:
1277 break;
1278
1279 default:
1280 REDEBUG("Unexpected type %s in left hand side of expression",
1281 tmpl_type_to_str(map->lhs->type));
1282 return XLAT_ACTION_FAIL;
1283 }
1284
1285 switch (map->rhs->type) {
1286 case TMPL_TYPE_ATTR:
1287 case TMPL_TYPE_EXEC:
1288 case TMPL_TYPE_DATA:
1291 case TMPL_TYPE_XLAT:
1292 break;
1293
1294 default:
1295 REDEBUG("Unexpected type %s in right hand side of expression",
1296 tmpl_type_to_str(map->rhs->type));
1297 return XLAT_ACTION_FAIL;
1298 }
1299
1300 RINDENT();
1301 ret = map_to_request(request, map, map_to_vp, NULL);
1302 REXDENT();
1303 talloc_free(map);
1304 if (ret < 0) return XLAT_ACTION_FAIL;
1305
1306 vb->vb_int8 = 1;
1307 return XLAT_ACTION_DONE;
1308}
1309
1310
1312 { .required = true, .concat = true, .type = FR_TYPE_STRING },
1314};
1315
1316/** Calculate number of seconds until the next n hour(s), day(s), week(s), year(s).
1317 *
1318 * For example, if it were 16:18 %nexttime(1h) would expand to 2520.
1319 *
1320 * The envisaged usage for this function is to limit sessions so that they don't
1321 * cross billing periods. The output of the xlat should be combined with %rand() to create
1322 * some jitter, unless the desired effect is every subscriber on the network
1323 * re-authenticating at the same time.
1324 *
1325 * @ingroup xlat_functions
1326 */
1328 UNUSED xlat_ctx_t const *xctx,
1329 request_t *request, fr_value_box_list_t *args)
1330{
1331 long num;
1332
1333 char const *p;
1334 char *q;
1335 time_t now;
1336 struct tm *local, local_buff;
1337 fr_value_box_t *in_head;
1338 fr_value_box_t *vb;
1339
1340 XLAT_ARGS(args, &in_head);
1341
1342 /*
1343 * We want to limit based on _now_, not on when they logged in.
1344 */
1345 now = time(NULL);
1346 local = localtime_r(&now, &local_buff);
1347
1348 p = in_head->vb_strvalue;
1349
1350 num = strtoul(p, &q, 10);
1351 if (!q || *q == '\0') {
1352 REDEBUG("nexttime: <int> must be followed by period specifier (h|d|w|m|y)");
1353 return XLAT_ACTION_FAIL;
1354 }
1355
1356 if (p == q) {
1357 num = 1;
1358 } else {
1359 p += q - p;
1360 }
1361
1362 local->tm_sec = 0;
1363 local->tm_min = 0;
1364
1365 switch (*p) {
1366 case 'h':
1367 local->tm_hour += num;
1368 break;
1369
1370 case 'd':
1371 local->tm_hour = 0;
1372 local->tm_mday += num;
1373 break;
1374
1375 case 'w':
1376 local->tm_hour = 0;
1377 local->tm_mday += (7 - local->tm_wday) + (7 * (num-1));
1378 break;
1379
1380 case 'm':
1381 local->tm_hour = 0;
1382 local->tm_mday = 1;
1383 local->tm_mon += num;
1384 break;
1385
1386 case 'y':
1387 local->tm_hour = 0;
1388 local->tm_mday = 1;
1389 local->tm_mon = 0;
1390 local->tm_year += num;
1391 break;
1392
1393 default:
1394 REDEBUG("nexttime: Invalid period specifier '%c', must be h|d|w|m|y", *p);
1395 return XLAT_ACTION_FAIL;
1396 }
1397
1398 MEM(vb = fr_value_box_alloc_null(ctx));
1399 fr_value_box_uint64(vb, NULL, (uint64_t)(mktime(local) - now), false);
1401 return XLAT_ACTION_DONE;
1402}
1403
1408
1409/** Just serves to push the result up the stack
1410 *
1411 */
1413 xlat_ctx_t const *xctx,
1414 UNUSED request_t *request, UNUSED fr_value_box_list_t *in)
1415{
1416 xlat_eval_rctx_t *rctx = talloc_get_type_abort(xctx->rctx, xlat_eval_rctx_t);
1418
1419 talloc_free(rctx);
1420
1421 return xa;
1422}
1423
1424typedef struct {
1425 fr_dict_t const *namespace; //!< Namespace we use for evaluating runtime expansions
1427
1429{
1430 xlat_eval_inst_t *inst = talloc_get_type_abort(xctx->inst, xlat_eval_inst_t);
1431
1432 inst->namespace = xctx->ex->call.dict;
1433
1434 return 0;
1435}
1436
1438 { .required = true, .concat = true, .type = FR_TYPE_STRING },
1440};
1441
1442/** Dynamically evaluate an expansion string
1443 *
1444 * @ingroup xlat_functions
1445 */
1447 xlat_ctx_t const *xctx,
1448 request_t *request, fr_value_box_list_t *args)
1449{
1451
1452 /*
1453 * These are escaping rules applied to the
1454 * input string. They're mostly here to
1455 * allow \% and \\ to work.
1456 *
1457 * Everything else should be passed in as
1458 * unescaped data.
1459 */
1460 static fr_sbuff_unescape_rules_t const escape_rules = {
1461 .name = "xlat",
1462 .chr = '\\',
1463 .subs = {
1464 ['%'] = '%',
1465 ['\\'] = '\\',
1466 },
1467 .do_hex = false,
1468 .do_oct = false
1469 };
1470
1471 xlat_eval_rctx_t *rctx;
1472 fr_value_box_t *arg = fr_value_box_list_head(args);
1473
1474 XLAT_ARGS(args, &arg);
1475
1476 MEM(rctx = talloc_zero(unlang_interpret_frame_talloc_ctx(request), xlat_eval_rctx_t));
1477
1478 /*
1479 * Parse the input as a literal expansion
1480 */
1481 if (xlat_tokenize_expression(rctx,
1482 &rctx->ex,
1483 &FR_SBUFF_IN(arg->vb_strvalue, arg->vb_length),
1484 &(fr_sbuff_parse_rules_t){
1485 .escapes = &escape_rules
1486 },
1487 &(tmpl_rules_t){
1488 .attr = {
1489 .dict_def = inst->namespace,
1490 .list_def = request_attr_request,
1491 .allow_unknown = false,
1492 .allow_unresolved = false,
1493 .allow_foreign = false,
1494 },
1495 .xlat = {
1496 .runtime_el = unlang_interpret_event_list(request),
1497 },
1498 .at_runtime = true
1499 }) < 0) {
1500 RPEDEBUG("Failed parsing expansion");
1501 error:
1502 talloc_free(rctx);
1503 return XLAT_ACTION_FAIL;
1504 }
1505
1506 /*
1507 * Call the resolution function so we produce
1508 * good errors about what function was
1509 * unresolved.
1510 */
1511 if (rctx->ex->flags.needs_resolving &&
1512 (xlat_resolve(rctx->ex, &(xlat_res_rules_t){ .allow_unresolved = false }) < 0)) {
1513 RPEDEBUG("Unresolved expansion functions in expansion");
1514 goto error;
1515
1516 }
1517
1518 if (unlang_xlat_yield(request, xlat_eval_resume, NULL, 0, rctx) != XLAT_ACTION_YIELD) goto error;
1519
1520 if (unlang_xlat_push(ctx, &rctx->last_success, (fr_value_box_list_t *)out->dlist,
1521 request, rctx->ex, UNLANG_SUB_FRAME) < 0) goto error;
1522
1524}
1525
1527 { .required = true, .type = FR_TYPE_STRING },
1528 { .required = true, .single = true, .type = FR_TYPE_UINT64 },
1529 { .concat = true, .type = FR_TYPE_STRING },
1531};
1532
1533/** lpad a string
1534 *
1535@verbatim
1536%lpad(%{Attribute-Name}, <length> [, <fill>])
1537@endverbatim
1538 *
1539 * Example: (User-Name = "foo")
1540@verbatim
1541%lpad(%{User-Name}, 5 'x') == "xxfoo"
1542@endverbatim
1543 *
1544 * @ingroup xlat_functions
1545 */
1547 UNUSED xlat_ctx_t const *xctx,
1548 request_t *request, fr_value_box_list_t *args)
1549{
1550 fr_value_box_t *values;
1551 fr_value_box_t *pad;
1553
1554 fr_value_box_list_t *list;
1555
1556 size_t pad_len;
1557
1558 char const *fill_str = NULL;
1559 size_t fill_len = 0;
1560
1561 fr_value_box_t *in = NULL;
1562
1563 XLAT_ARGS(args, &values, &pad, &fill);
1564
1565 /* coverity[dereference] */
1566 list = &values->vb_group;
1567 /* coverity[dereference] */
1568 pad_len = (size_t)pad->vb_uint64;
1569
1570 /*
1571 * Fill is optional
1572 */
1573 if (fill) {
1574 fill_str = fill->vb_strvalue;
1575 fill_len = talloc_array_length(fill_str) - 1;
1576 }
1577
1578 if (fill_len == 0) {
1579 fill_str = " ";
1580 fill_len = 1;
1581 }
1582
1583 while ((in = fr_value_box_list_pop_head(list))) {
1584 size_t len = talloc_array_length(in->vb_strvalue) - 1;
1585 size_t remaining;
1586 char *buff;
1587 fr_sbuff_t sbuff;
1588 fr_sbuff_marker_t m_data;
1589
1591
1592 if (len >= pad_len) continue;
1593
1594 if (fr_value_box_bstr_realloc(in, &buff, in, pad_len) < 0) {
1595 RPEDEBUG("Failed reallocing input data");
1596 return XLAT_ACTION_FAIL;
1597 }
1598
1599 fr_sbuff_init_in(&sbuff, buff, pad_len);
1600 fr_sbuff_marker(&m_data, &sbuff);
1601
1602 /*
1603 * ...nothing to move if the input
1604 * string is empty.
1605 */
1606 if (len > 0) {
1607 fr_sbuff_advance(&m_data, pad_len - len); /* Mark where we want the data to go */
1608 fr_sbuff_move(&FR_SBUFF(&m_data), &FR_SBUFF(&sbuff), len); /* Shift the data */
1609 }
1610
1611 if (fill_len == 1) {
1612 memset(fr_sbuff_current(&sbuff), *fill_str, fr_sbuff_ahead(&m_data));
1613 continue;
1614 }
1615
1616 /*
1617 * Copy fill as a repeating pattern
1618 */
1619 while ((remaining = fr_sbuff_ahead(&m_data))) {
1620 size_t to_copy = remaining >= fill_len ? fill_len : remaining;
1621 memcpy(fr_sbuff_current(&sbuff), fill_str, to_copy); /* avoid \0 termination */
1622 fr_sbuff_advance(&sbuff, to_copy);
1623 }
1624 fr_sbuff_set_to_end(&sbuff);
1625 fr_sbuff_terminate(&sbuff); /* Move doesn't re-terminate */
1626 }
1627
1628 return XLAT_ACTION_DONE;
1629}
1630
1631/** Right pad a string
1632 *
1633@verbatim
1634%rpad(%{Attribute-Name}, <length> [, <fill>])
1635@endverbatim
1636 *
1637 * Example: (User-Name = "foo")
1638@verbatim
1639%rpad(%{User-Name}, 5 'x') == "fooxx"
1640@endverbatim
1641 *
1642 * @ingroup xlat_functions
1643 */
1645 UNUSED xlat_ctx_t const *xctx,
1646 request_t *request, fr_value_box_list_t *args)
1647{
1648 fr_value_box_t *values;
1649 fr_value_box_list_t *list;
1650 fr_value_box_t *pad;
1651 /* coverity[dereference] */
1652 size_t pad_len;
1654 char const *fill_str = NULL;
1655 size_t fill_len = 0;
1656
1657 fr_value_box_t *in = NULL;
1658
1659 XLAT_ARGS(args, &values, &pad, &fill);
1660
1661 list = &values->vb_group;
1662 pad_len = (size_t)pad->vb_uint64;
1663
1664 /*
1665 * Fill is optional
1666 */
1667 if (fill) {
1668 fill_str = fill->vb_strvalue;
1669 fill_len = talloc_array_length(fill_str) - 1;
1670 }
1671
1672 if (fill_len == 0) {
1673 fill_str = " ";
1674 fill_len = 1;
1675 }
1676
1677 while ((in = fr_value_box_list_pop_head(list))) {
1678 size_t len = talloc_array_length(in->vb_strvalue) - 1;
1679 size_t remaining;
1680 char *buff;
1681 fr_sbuff_t sbuff;
1682
1684
1685 if (len >= pad_len) continue;
1686
1687 if (fr_value_box_bstr_realloc(in, &buff, in, pad_len) < 0) {
1688 fail:
1689 RPEDEBUG("Failed reallocing input data");
1690 return XLAT_ACTION_FAIL;
1691 }
1692
1693 fr_sbuff_init_in(&sbuff, buff, pad_len);
1694 fr_sbuff_advance(&sbuff, len);
1695
1696 if (fill_len == 1) {
1697 memset(fr_sbuff_current(&sbuff), *fill_str, fr_sbuff_remaining(&sbuff));
1698 continue;
1699 }
1700
1701 /*
1702 * Copy fill as a repeating pattern
1703 */
1704 while ((remaining = fr_sbuff_remaining(&sbuff))) {
1705 if (fr_sbuff_in_bstrncpy(&sbuff, fill_str, remaining >= fill_len ? fill_len : remaining) < 0) {
1706 goto fail;
1707 }
1708 }
1709 }
1710
1711 return XLAT_ACTION_DONE;
1712}
1713
1715 { .required = true, .concat = true, .type = FR_TYPE_OCTETS },
1717};
1718
1719/** Encode string or attribute as base64
1720 *
1721 * Example:
1722@verbatim
1723%base64.encode("foo") == "Zm9v"
1724@endverbatim
1725 *
1726 * @ingroup xlat_functions
1727 */
1729 UNUSED xlat_ctx_t const *xctx,
1730 request_t *request, fr_value_box_list_t *args)
1731{
1732 size_t alen;
1733 ssize_t elen;
1734 char *buff;
1735 fr_value_box_t *vb;
1737
1738 XLAT_ARGS(args, &in);
1739
1740 alen = FR_BASE64_ENC_LENGTH(in->vb_length);
1741
1742 MEM(vb = fr_value_box_alloc_null(ctx));
1743 if (fr_value_box_bstr_alloc(vb, &buff, vb, NULL, alen, false) < 0) {
1744 talloc_free(vb);
1745 return XLAT_ACTION_FAIL;
1746 }
1747
1748 elen = fr_base64_encode(&FR_SBUFF_OUT(buff, talloc_array_length(buff)),
1749 &FR_DBUFF_TMP(in->vb_octets, in->vb_length), true);
1750 if (elen < 0) {
1751 RPEDEBUG("Base64 encoding failed");
1752 talloc_free(vb);
1753 return XLAT_ACTION_FAIL;
1754 }
1755 fr_assert((size_t)elen <= alen);
1756 vb->tainted = in->tainted;
1759
1760 return XLAT_ACTION_DONE;
1761}
1762
1764 { .required = true, .concat = true, .type = FR_TYPE_OCTETS },
1766};
1767
1768/** Decode base64 string
1769 *
1770 * Example:
1771@verbatim
1772%base64.decode("Zm9v") == "foo"
1773@endverbatim
1774 *
1775 * @ingroup xlat_functions
1776 */
1778 UNUSED xlat_ctx_t const *xctx,
1779 request_t *request, fr_value_box_list_t *args)
1780{
1781 size_t alen;
1782 ssize_t declen = 0;
1783 uint8_t *decbuf;
1784 fr_value_box_t *vb;
1786
1787 XLAT_ARGS(args, &in);
1788
1789 /*
1790 * Pass empty arguments through
1791 *
1792 * FR_BASE64_DEC_LENGTH produces 2 for empty strings...
1793 */
1794 if (in->vb_length == 0) {
1795 fr_value_box_list_remove(args, in);
1797 return XLAT_ACTION_DONE;
1798 }
1799
1800 alen = FR_BASE64_DEC_LENGTH(in->vb_length);
1801 MEM(vb = fr_value_box_alloc_null(ctx));
1802 if (alen > 0) {
1803 MEM(fr_value_box_mem_alloc(vb, &decbuf, vb, NULL, alen, in->tainted) == 0);
1804 declen = fr_base64_decode(&FR_DBUFF_TMP(decbuf, alen),
1805 &FR_SBUFF_IN(in->vb_strvalue, in->vb_length), true, true);
1806 if (declen < 0) {
1807 RPEDEBUG("Base64 string invalid");
1808 talloc_free(vb);
1809 return XLAT_ACTION_FAIL;
1810 }
1811
1812 MEM(fr_value_box_mem_realloc(vb, NULL, vb, declen) == 0);
1813 }
1814
1815 vb->tainted = in->tainted;
1818
1819 return XLAT_ACTION_DONE;
1820}
1821
1823 { .required = true, .type = FR_TYPE_STRING },
1825};
1826
1827/** Convert hex string to binary
1828 *
1829 * Example:
1830@verbatim
1831%bin("666f6f626172") == "foobar"
1832@endverbatim
1833 *
1834 * @see #xlat_func_hex
1835 *
1836 * @ingroup xlat_functions
1837 */
1839 UNUSED xlat_ctx_t const *xctx,
1840 request_t *request, fr_value_box_list_t *args)
1841{
1842 fr_value_box_t *result;
1843 char const *p, *end;
1844 uint8_t *bin;
1845 size_t len, outlen;
1847 fr_value_box_t *list, *hex;
1848
1849 XLAT_ARGS(args, &list);
1850
1851 while ((hex = fr_value_box_list_pop_head(&list->vb_group))) {
1852 len = hex->vb_length;
1853 if ((len > 1) && (len & 0x01)) {
1854 REDEBUG("Input data length must be >1 and even, got %zu", len);
1855 return XLAT_ACTION_FAIL;
1856 }
1857
1858 p = hex->vb_strvalue;
1859 end = p + len;
1860
1861 /*
1862 * Look for 0x at the start of the string
1863 */
1864 if ((p[0] == '0') && (p[1] == 'x')) {
1865 p += 2;
1866 len -=2;
1867 }
1868
1869 /*
1870 * Zero length octets string
1871 */
1872 if (p == end) continue;
1873
1874 outlen = len / 2;
1875
1876 MEM(result = fr_value_box_alloc_null(ctx));
1877 MEM(fr_value_box_mem_alloc(result, &bin, result, NULL, outlen, fr_value_box_list_tainted(args)) == 0);
1878 fr_base16_decode(&err, &FR_DBUFF_TMP(bin, outlen), &FR_SBUFF_IN(p, end - p), true);
1879 if (err) {
1880 REDEBUG2("Invalid hex string");
1881 talloc_free(result);
1882 return XLAT_ACTION_FAIL;
1883 }
1884
1885 fr_dcursor_append(out, result);
1886 }
1887
1888 return XLAT_ACTION_DONE;
1889}
1890
1892 { .required = true, .single = true, .type = FR_TYPE_VOID },
1893 { .type = FR_TYPE_VOID },
1894 { .variadic = XLAT_ARG_VARIADIC_EMPTY_KEEP, .type = FR_TYPE_VOID },
1896};
1897
1898/** Cast one or more output value-boxes to the given type
1899 *
1900 * First argument of is type to cast to.
1901 *
1902 * Example:
1903@verbatim
1904%cast('string', %{request[*]}) results in all of the input boxes being cast to string/
1905@endverbatim
1906 *
1907 * @ingroup xlat_functions
1908 */
1910 UNUSED xlat_ctx_t const *xctx,
1911 request_t *request, fr_value_box_list_t *args)
1912{
1914 fr_value_box_t *arg;
1916 fr_dict_attr_t const *time_res = NULL;
1917
1918 XLAT_ARGS(args, &name);
1919
1920 /*
1921 * Get the type, which can be in one of a few formats.
1922 */
1923 if (fr_type_is_numeric(name->type)) {
1925 RPEDEBUG("Failed parsing '%pV' as a numerical data type", name);
1926 return XLAT_ACTION_FAIL;
1927 }
1928 type = name->vb_uint8;
1929
1930 } else {
1931 if (name->type != FR_TYPE_STRING) {
1933 RPEDEBUG("Failed parsing '%pV' as a string data type", name);
1934 return XLAT_ACTION_FAIL;
1935 }
1936 }
1937
1939 if (type == FR_TYPE_NULL) {
1940 if ((time_res = xlat_time_res_attr(name->vb_strvalue)) == NULL) {
1941 RDEBUG("Unknown data type '%s'", name->vb_strvalue);
1942 return XLAT_ACTION_FAIL;
1943 }
1944
1946 }
1947 }
1948
1949 (void) fr_value_box_list_pop_head(args);
1950
1951 /*
1952 * When we cast nothing to a string / octets, the result is an empty string/octets.
1953 */
1954 if (unlikely(!fr_value_box_list_head(args))) {
1955 if ((type == FR_TYPE_STRING) || (type == FR_TYPE_OCTETS)) {
1956 fr_value_box_t *dst;
1957
1958 MEM(dst = fr_value_box_alloc(ctx, type, NULL));
1959 fr_dcursor_append(out, dst);
1960 VALUE_BOX_LIST_VERIFY((fr_value_box_list_t *)out->dlist);
1961
1962 return XLAT_ACTION_DONE;
1963 }
1964
1965 RDEBUG("No data for cast to '%s'", fr_type_to_str(type));
1966 return XLAT_ACTION_FAIL;
1967 }
1968
1969 /*
1970 * Cast to string means *print* to string.
1971 */
1972 if (type == FR_TYPE_STRING) {
1973 fr_sbuff_t *agg;
1974 fr_value_box_t *dst;
1975
1977
1978 FR_SBUFF_TALLOC_THREAD_LOCAL(&agg, 256, SIZE_MAX);
1979
1980 MEM(dst = fr_value_box_alloc_null(ctx));
1981 if (fr_value_box_list_concat_as_string(NULL, NULL, agg, args, NULL, 0, NULL,
1982 FR_VALUE_BOX_LIST_FREE_BOX, 0, true) < 0) {
1983 RPEDEBUG("Failed concatenating string");
1984 return XLAT_ACTION_FAIL;
1985 }
1986
1987 fr_value_box_bstrndup(dst, dst, NULL, fr_sbuff_start(agg), fr_sbuff_used(agg), false);
1988 fr_dcursor_append(out, dst);
1989 VALUE_BOX_LIST_VERIFY((fr_value_box_list_t *)out->dlist);
1990
1991 return XLAT_ACTION_DONE;
1992 }
1993
1994 /*
1995 * Copy inputs to outputs, casting them along the way.
1996 */
1997 arg = NULL;
1998 while ((arg = fr_value_box_list_next(args, arg)) != NULL) {
1999 fr_value_box_t *vb, *p;
2000
2001 fr_assert(arg->type == FR_TYPE_GROUP);
2002
2003 vb = fr_value_box_list_head(&arg->vb_group);
2004 while (vb) {
2005 p = fr_value_box_list_remove(&arg->vb_group, vb);
2006
2007 if (fr_value_box_cast_in_place(vb, vb, type, time_res) < 0) {
2008 RPEDEBUG("Failed casting %pV to data type '%s'", vb, fr_type_to_str(type));
2009 return XLAT_ACTION_FAIL;
2010 }
2012 vb = fr_value_box_list_next(&arg->vb_group, p);
2013 }
2014 }
2015 VALUE_BOX_LIST_VERIFY((fr_value_box_list_t *)out->dlist);
2016
2017 return XLAT_ACTION_DONE;
2018}
2019
2021 { .required = true, .type = FR_TYPE_VOID },
2022 { .concat = true, .type = FR_TYPE_STRING },
2024};
2025
2026/** Concatenate string representation of values of given attributes using separator
2027 *
2028 * First argument of is the list of attributes to concatenate, followed
2029 * by an optional separator
2030 *
2031 * Example:
2032@verbatim
2033%concat(%{request.[*]}, ',') == "<attr1value>,<attr2value>,<attr3value>,..."
2034%concat(%{Tmp-String-0[*]}, '. ') == "<str1value>. <str2value>. <str3value>. ..."
2035%concat(%join(%{User-Name}, %{Calling-Station-Id}), ', ') == "bob, aa:bb:cc:dd:ee:ff"
2036@endverbatim
2037 *
2038 * @ingroup xlat_functions
2039 */
2041 UNUSED xlat_ctx_t const *xctx,
2042 request_t *request, fr_value_box_list_t *args)
2043{
2044 fr_value_box_t *result;
2045 fr_value_box_t *list;
2046 fr_value_box_t *separator;
2047 fr_value_box_list_t *to_concat;
2048 char *buff;
2049 char const *sep;
2050
2051 XLAT_ARGS(args, &list, &separator);
2052
2053 sep = (separator) ? separator->vb_strvalue : "";
2054 to_concat = &list->vb_group;
2055
2056 result = fr_value_box_alloc(ctx, FR_TYPE_STRING, NULL);
2057 if (!result) {
2058 error:
2059 RPEDEBUG("Failed concatenating input");
2060 return XLAT_ACTION_FAIL;
2061 }
2062
2063 buff = fr_value_box_list_aprint(result, to_concat, sep, NULL);
2064 if (!buff) goto error;
2065
2067
2068 fr_dcursor_append(out, result);
2069
2070 return XLAT_ACTION_DONE;
2071}
2072
2074 { .required = true, .type = FR_TYPE_OCTETS },
2076};
2077
2078/** Print data as hex, not as VALUE.
2079 *
2080 * Example:
2081@verbatim
2082%hex("foobar") == "666f6f626172"
2083@endverbatim
2084 *
2085 * @see #xlat_func_bin
2086 *
2087 * @ingroup xlat_functions
2088 */
2090 UNUSED xlat_ctx_t const *xctx,
2091 UNUSED request_t *request, fr_value_box_list_t *args)
2092{
2093 char *new_buff;
2094 fr_value_box_t *list, *bin;
2095
2096 XLAT_ARGS(args, &list);
2097
2098 while ((bin = fr_value_box_list_pop_head(&list->vb_group))) {
2099 /*
2100 * Use existing box, but with new buffer
2101 */
2102 MEM(new_buff = talloc_zero_array(bin, char, (bin->vb_length * 2) + 1));
2103 if (bin->vb_length) {
2104 fr_base16_encode(&FR_SBUFF_OUT(new_buff, (bin->vb_length * 2) + 1),
2105 &FR_DBUFF_TMP(bin->vb_octets, bin->vb_length));
2107 fr_value_box_strdup_shallow(bin, NULL, new_buff, bin->tainted);
2108 /*
2109 * Zero length binary > zero length hex string
2110 */
2111 } else {
2113 fr_value_box_strdup(bin, bin, NULL, "", bin->tainted);
2114 }
2115 fr_dcursor_append(out, bin);
2116 }
2117
2118 return XLAT_ACTION_DONE;
2119}
2120
2125
2126static xlat_action_t xlat_hmac(TALLOC_CTX *ctx, fr_dcursor_t *out,
2127 fr_value_box_list_t *args, uint8_t *digest, int digest_len, hmac_type type)
2128{
2129 fr_value_box_t *vb, *data, *key;
2130
2131 XLAT_ARGS(args, &data, &key);
2132
2133 if (type == HMAC_MD5) {
2134 /* coverity[dereference] */
2135 fr_hmac_md5(digest, data->vb_octets, data->vb_length, key->vb_octets, key->vb_length);
2136 } else if (type == HMAC_SHA1) {
2137 /* coverity[dereference] */
2138 fr_hmac_sha1(digest, data->vb_octets, data->vb_length, key->vb_octets, key->vb_length);
2139 }
2140
2141 MEM(vb = fr_value_box_alloc_null(ctx));
2142 fr_value_box_memdup(vb, vb, NULL, digest, digest_len, false);
2143
2145
2146 return XLAT_ACTION_DONE;
2147}
2148
2150 { .required = true, .concat = true, .type = FR_TYPE_STRING },
2151 { .required = true, .concat = true, .type = FR_TYPE_STRING },
2153};
2154
2155/** Generate the HMAC-MD5 of a string or attribute
2156 *
2157 * Example:
2158@verbatim
2159%hmacmd5('foo', 'bar') == "0x31b6db9e5eb4addb42f1a6ca07367adc"
2160@endverbatim
2161 *
2162 * @ingroup xlat_functions
2163 */
2165 UNUSED xlat_ctx_t const *xctx,
2166 UNUSED request_t *request, fr_value_box_list_t *in)
2167{
2168 uint8_t digest[MD5_DIGEST_LENGTH];
2169 return xlat_hmac(ctx, out, in, digest, MD5_DIGEST_LENGTH, HMAC_MD5);
2170}
2171
2172
2173/** Generate the HMAC-SHA1 of a string or attribute
2174 *
2175 * Example:
2176@verbatim
2177%hmacsha1('foo', 'bar') == "0x85d155c55ed286a300bd1cf124de08d87e914f3a"
2178@endverbatim
2179 *
2180 * @ingroup xlat_functions
2181 */
2183 UNUSED xlat_ctx_t const *xctx,
2184 UNUSED request_t *request, fr_value_box_list_t *in)
2185{
2187 return xlat_hmac(ctx, out, in, digest, SHA1_DIGEST_LENGTH, HMAC_SHA1);
2188}
2189
2191 { .required = true, .type = FR_TYPE_VOID },
2192 { .variadic = XLAT_ARG_VARIADIC_EMPTY_SQUASH, .type = FR_TYPE_VOID },
2194};
2195
2196/** Join a series of arguments to form a single list
2197 *
2198 * null boxes are not preserved.
2199 */
2201 UNUSED xlat_ctx_t const *xctx,
2202 UNUSED request_t *request, fr_value_box_list_t *in)
2203{
2205 fr_assert(arg->type == FR_TYPE_GROUP);
2206
2207 fr_value_box_list_foreach_safe(&arg->vb_group, vb) {
2208 fr_value_box_list_remove(&arg->vb_group, vb);
2210 }}
2211 }
2212 return XLAT_ACTION_DONE;
2213}
2214
2215static void ungroup(fr_dcursor_t *out, fr_value_box_list_t *in)
2216{
2217 fr_value_box_t *vb;
2218
2219 while ((vb = fr_value_box_list_pop_head(in)) != NULL) {
2220 if (vb->type != FR_TYPE_GROUP) {
2222 continue;
2223 }
2224 talloc_free(vb);
2225 }
2226}
2227
2228/** Ungroups all of its arguments into one flat list.
2229 *
2230 */
2232 UNUSED xlat_ctx_t const *xctx,
2233 UNUSED request_t *request, fr_value_box_list_t *in)
2234{
2235 fr_value_box_t *arg = NULL;
2236
2237 while ((arg = fr_value_box_list_next(in, arg)) != NULL) {
2238 fr_assert(arg->type == FR_TYPE_GROUP);
2239
2240 ungroup(out, &arg->vb_group);
2241 }
2242 return XLAT_ACTION_DONE;
2243}
2244
2246 { .single = true, .variadic = XLAT_ARG_VARIADIC_EMPTY_KEEP, .type = FR_TYPE_VOID },
2248};
2249
2250/** Return the on-the-wire size of the boxes in bytes
2251 *
2252 * skips null values
2253 *
2254 * Example:
2255@verbatim
2256%length(foobar) == 6
2257%length(%bin("0102030005060708")) == 8
2258@endverbatim
2259 *
2260 * @see #xlat_func_strlen
2261 *
2262 * @ingroup xlat_functions
2263 */
2265 UNUSED xlat_ctx_t const *xctx,
2266 UNUSED request_t *request, fr_value_box_list_t *in)
2267
2268{
2271
2272 MEM(my = fr_value_box_alloc(ctx, FR_TYPE_SIZE, NULL));
2273 if (!fr_type_is_null(vb->type)) my->vb_size = fr_value_box_network_length(vb);
2275 }
2276
2277 return XLAT_ACTION_DONE;
2278}
2279
2280
2282 { .concat = true, .type = FR_TYPE_OCTETS },
2284};
2285
2286/** Calculate the MD4 hash of a string or attribute.
2287 *
2288 * Example:
2289@verbatim
2290%md4("foo") == "0ac6700c491d70fb8650940b1ca1e4b2"
2291@endverbatim
2292 *
2293 * @ingroup xlat_functions
2294 */
2296 UNUSED xlat_ctx_t const *xctx,
2297 UNUSED request_t *request, fr_value_box_list_t *args)
2298{
2299 uint8_t digest[MD4_DIGEST_LENGTH];
2300 fr_value_box_t *vb;
2301 fr_value_box_t *in_head;
2302
2303 XLAT_ARGS(args, &in_head);
2304
2305 if (in_head) {
2306 fr_md4_calc(digest, in_head->vb_octets, in_head->vb_length);
2307 } else {
2308 /* Digest of empty string */
2309 fr_md4_calc(digest, NULL, 0);
2310 }
2311
2312 MEM(vb = fr_value_box_alloc_null(ctx));
2313 fr_value_box_memdup(vb, vb, NULL, digest, sizeof(digest), false);
2314
2316 VALUE_BOX_LIST_VERIFY((fr_value_box_list_t *)out->dlist);
2317
2318 return XLAT_ACTION_DONE;
2319}
2320
2322 { .concat = true, .type = FR_TYPE_OCTETS },
2324};
2325
2326/** Calculate the MD5 hash of a string or attribute.
2327 *
2328 * Example:
2329@verbatim
2330%md5("foo") == "acbd18db4cc2f85cedef654fccc4a4d8"
2331@endverbatim
2332 *
2333 * @ingroup xlat_functions
2334 */
2336 UNUSED xlat_ctx_t const *xctx,
2337 UNUSED request_t *request, fr_value_box_list_t *args)
2338{
2339 uint8_t digest[MD5_DIGEST_LENGTH];
2340 fr_value_box_t *vb;
2341 fr_value_box_t *in_head;
2342
2343 XLAT_ARGS(args, &in_head);
2344
2345 if (in_head) {
2346 fr_md5_calc(digest, in_head->vb_octets, in_head->vb_length);
2347 } else {
2348 /* Digest of empty string */
2349 fr_md5_calc(digest, NULL, 0);
2350 }
2351
2352 MEM(vb = fr_value_box_alloc_null(ctx));
2353 fr_value_box_memdup(vb, vb, NULL, digest, sizeof(digest), false);
2354
2356
2357 return XLAT_ACTION_DONE;
2358}
2359
2360
2362 { .required = true, .single = true, .type = FR_TYPE_STRING },
2364};
2365
2366/** Encode attributes as a series of string attribute/value pairs
2367 *
2368 * This is intended to serialize one or more attributes as a comma
2369 * delimited string.
2370 *
2371 * Example:
2372@verbatim
2373%pairs(request.[*]) == 'User-Name = "foo"User-Password = "bar"'
2374%concat(%pairs(request.[*]), ', ') == 'User-Name = "foo", User-Password = "bar"'
2375@endverbatim
2376 *
2377 * @see #xlat_func_concat
2378 *
2379 * @ingroup xlat_functions
2380 */
2382 UNUSED xlat_ctx_t const *xctx,
2383 request_t *request, fr_value_box_list_t *args)
2384{
2385 tmpl_t *vpt = NULL;
2386 fr_dcursor_t cursor;
2388 fr_value_box_t *vb;
2389 fr_value_box_t *in_head;
2390
2391 fr_pair_t *vp;
2392
2393 XLAT_ARGS(args, &in_head);
2394
2395 if (tmpl_afrom_attr_str(ctx, NULL, &vpt, in_head->vb_strvalue,
2396 &(tmpl_rules_t){
2397 .attr = {
2398 .dict_def = request->dict,
2399 .list_def = request_attr_request,
2400 .allow_wildcard = true,
2401 .prefix = TMPL_ATTR_REF_PREFIX_AUTO
2402 }
2403 }) <= 0) {
2404 RPEDEBUG("Invalid input");
2405 return XLAT_ACTION_FAIL;
2406 }
2407
2408 for (vp = tmpl_dcursor_init(NULL, NULL, &cc, &cursor, request, vpt);
2409 vp;
2410 vp = fr_dcursor_next(&cursor)) {
2411 char *buff;
2412
2413 MEM(vb = fr_value_box_alloc_null(ctx));
2414 if (unlikely(fr_pair_aprint(vb, &buff, NULL, vp) < 0)) {
2415 RPEDEBUG("Failed printing pair");
2416 talloc_free(vb);
2417 tmpl_dcursor_clear(&cc);
2418 return XLAT_ACTION_FAIL;
2419 }
2420
2421 fr_value_box_bstrdup_buffer_shallow(NULL, vb, NULL, buff, false);
2423 }
2424 tmpl_dcursor_clear(&cc);
2426
2427 return XLAT_ACTION_DONE;
2428}
2429
2431 { .required = true, .single = true, .type = FR_TYPE_UINT32 },
2433};
2434
2435/** Generate a random integer value
2436 *
2437 * For "N = %rand(MAX)", 0 <= N < MAX
2438 *
2439 * Example:
2440@verbatim
2441%rand(100) == 42
2442@endverbatim
2443 *
2444 * @ingroup xlat_functions
2445 */
2447 UNUSED xlat_ctx_t const *xctx,
2448 UNUSED request_t *request, fr_value_box_list_t *in)
2449{
2450 int64_t result;
2451 fr_value_box_t *vb;
2452 fr_value_box_t *in_head = fr_value_box_list_head(in);
2453
2454 result = in_head->vb_uint32;
2455
2456 /* Make sure it isn't too big */
2457 if (result > (1 << 30)) result = (1 << 30);
2458
2459 result *= fr_rand(); /* 0..2^32-1 */
2460 result >>= 32;
2461
2462 MEM(vb = fr_value_box_alloc(ctx, FR_TYPE_UINT64, NULL));
2463 vb->vb_uint64 = result;
2464
2466
2467 return XLAT_ACTION_DONE;
2468}
2469
2471 { .required = true, .concat = true, .type = FR_TYPE_STRING },
2473};
2474
2475/** Generate a string of random chars
2476 *
2477 * Build strings of random chars, useful for generating tokens and passcodes
2478 * Format similar to String::Random.
2479 *
2480 * Format characters may include the following, and may be
2481 * preceded by a repetition count:
2482 * - "c" lowercase letters
2483 * - "C" uppercase letters
2484 * - "n" numbers
2485 * - "a" alphanumeric
2486 * - "!" punctuation
2487 * - "." alphanumeric + punctuation
2488 * - "s" alphanumeric + "./"
2489 * - "o" characters suitable for OTP (easily confused removed)
2490 * - "b" binary data
2491 *
2492 * Example:
2493@verbatim
2494%randstr("CCCC!!cccnnn") == "IPFL>{saf874"
2495%randstr("42o") == "yHdupUwVbdHprKCJRYfGbaWzVwJwUXG9zPabdGAhM9"
2496%hex(%randstr("bbbb")) == "a9ce04f3"
2497%hex(%randstr("8b")) == "fe165529f9f66839"
2498@endverbatim
2499 * @ingroup xlat_functions
2500 */
2502 UNUSED xlat_ctx_t const *xctx,
2503 request_t *request, fr_value_box_list_t *args)
2504{
2505 /*
2506 * Lookup tables for randstr char classes
2507 */
2508 static char randstr_punc[] = "!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~";
2509 static char randstr_salt[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmopqrstuvwxyz/.";
2510
2511 /*
2512 * Characters humans rarely confuse. Reduces char set considerably
2513 * should only be used for things such as one time passwords.
2514 */
2515 static char randstr_otp[] = "469ACGHJKLMNPQRUVWXYabdfhijkprstuvwxyz";
2516
2517 char const *p, *start, *end;
2518 char *endptr;
2519 char *buff_p;
2520 unsigned int result;
2521 unsigned int reps;
2522 size_t outlen = 0;
2523 fr_value_box_t* vb;
2524 fr_value_box_t *in_head;
2525
2526 XLAT_ARGS(args, &in_head);
2527
2528 /** Max repetitions of a single character class
2529 *
2530 */
2531#define REPETITION_MAX 1024
2532
2533 start = p = in_head->vb_strvalue;
2534 end = p + in_head->vb_length;
2535
2536 /*
2537 * Calculate size of output
2538 */
2539 while (p < end) {
2540 /*
2541 * Repetition modifiers.
2542 *
2543 * We limit it to REPETITION_MAX, because we don't want
2544 * utter stupidity.
2545 */
2546 if (isdigit((uint8_t) *p)) {
2547 reps = strtol(p, &endptr, 10);
2548 if (reps > REPETITION_MAX) reps = REPETITION_MAX;
2549 outlen += reps;
2550 p = endptr;
2551 } else {
2552 outlen++;
2553 }
2554 p++;
2555 }
2556
2557 MEM(vb = fr_value_box_alloc_null(ctx));
2558 MEM(fr_value_box_bstr_alloc(vb, &buff_p, vb, NULL, outlen, false) == 0);
2559
2560 /* Reset p to start position */
2561 p = start;
2562
2563 while (p < end) {
2564 size_t i;
2565
2566 if (isdigit((uint8_t) *p)) {
2567 reps = strtol(p, &endptr, 10);
2568 if (reps > REPETITION_MAX) {
2569 reps = REPETITION_MAX;
2570 RMARKER(L_WARN, L_DBG_LVL_2, start, start - p,
2571 "Forcing repetition to %u", (unsigned int)REPETITION_MAX);
2572 }
2573 p = endptr;
2574 } else {
2575 reps = 1;
2576 }
2577
2578 for (i = 0; i < reps; i++) {
2579 result = fr_rand();
2580 switch (*p) {
2581 /*
2582 * Lowercase letters
2583 */
2584 case 'c':
2585 *buff_p++ = 'a' + (result % 26);
2586 break;
2587
2588 /*
2589 * Uppercase letters
2590 */
2591 case 'C':
2592 *buff_p++ = 'A' + (result % 26);
2593 break;
2594
2595 /*
2596 * Numbers
2597 */
2598 case 'n':
2599 *buff_p++ = '0' + (result % 10);
2600 break;
2601
2602 /*
2603 * Alpha numeric
2604 */
2605 case 'a':
2606 *buff_p++ = randstr_salt[result % (sizeof(randstr_salt) - 3)];
2607 break;
2608
2609 /*
2610 * Punctuation
2611 */
2612 case '!':
2613 *buff_p++ = randstr_punc[result % (sizeof(randstr_punc) - 1)];
2614 break;
2615
2616 /*
2617 * Alpha numeric + punctuation
2618 */
2619 case '.':
2620 *buff_p++ = '!' + (result % 95);
2621 break;
2622
2623 /*
2624 * Alpha numeric + salt chars './'
2625 */
2626 case 's':
2627 *buff_p++ = randstr_salt[result % (sizeof(randstr_salt) - 1)];
2628 break;
2629
2630 /*
2631 * Chars suitable for One Time Password tokens.
2632 * Alpha numeric with easily confused char pairs removed.
2633 */
2634 case 'o':
2635 *buff_p++ = randstr_otp[result % (sizeof(randstr_otp) - 1)];
2636 break;
2637
2638 /*
2639 * Binary data - Copy between 1-4 bytes at a time
2640 */
2641 case 'b':
2642 {
2643 size_t copy = (reps - i) > sizeof(result) ? sizeof(result) : reps - i;
2644
2645 memcpy(buff_p, (uint8_t *)&result, copy);
2646 buff_p += copy;
2647 i += (copy - 1); /* Loop +1 */
2648 }
2649 break;
2650
2651 default:
2652 REDEBUG("Invalid character class '%c'", *p);
2653 talloc_free(vb);
2654
2655 return XLAT_ACTION_FAIL;
2656 }
2657 }
2658
2659 p++;
2660 }
2661
2662 *buff_p++ = '\0';
2663
2665
2666 return XLAT_ACTION_DONE;
2667}
2668
2669
2671 { .required = true, .type = FR_TYPE_UINT64 },
2672 { .required = false, .type = FR_TYPE_UINT64 },
2673 { .required = false, .type = FR_TYPE_UINT64 },
2675};
2676
2677/** Generate a range of uint64 numbers
2678 *
2679 * Example:
2680@verbatim
2681%range(end) - 0..end
2682%rang(start, end)
2683%range(start,end, step)
2684@endverbatim
2685 * @ingroup xlat_functions
2686 */
2688 UNUSED xlat_ctx_t const *xctx,
2689 request_t *request, fr_value_box_list_t *args)
2690{
2691 fr_value_box_t *start_vb, *end_vb, *step_vb;
2692 fr_value_box_t *dst;
2693 uint64_t i, start, end, step;
2694
2695 XLAT_ARGS(args, &start_vb, &end_vb, &step_vb);
2696
2697 if (step_vb) {
2698 start = fr_value_box_list_head(&start_vb->vb_group)->vb_uint64;
2699 end = fr_value_box_list_head(&end_vb->vb_group)->vb_uint64;
2700 step = fr_value_box_list_head(&step_vb->vb_group)->vb_uint64;
2701
2702 } else if (end_vb) {
2703 start = fr_value_box_list_head(&start_vb->vb_group)->vb_uint64;
2704 end = fr_value_box_list_head(&end_vb->vb_group)->vb_uint64;
2705 step = 1;
2706
2707 } else {
2708 start = 0;
2709 fr_value_box_debug(start_vb);
2710 end = fr_value_box_list_head(&start_vb->vb_group)->vb_uint64;
2711 step = 1;
2712 }
2713
2714 if (end <= start) {
2715 REDEBUG("Invalid range - 'start' must be less than 'end'");
2716 return XLAT_ACTION_FAIL;
2717 }
2718
2719 if (!step) {
2720 REDEBUG("Invalid range - 'step' must be greater than zero");
2721 return XLAT_ACTION_FAIL;
2722 }
2723
2724 if (step > (end - start)) {
2725 REDEBUG("Invalid range - 'step' must allow for at least one result");
2726 return XLAT_ACTION_FAIL;
2727 }
2728
2729 if (((end - start) / step) > 1000) {
2730 REDEBUG("Invalid range - Too many results");
2731 return XLAT_ACTION_FAIL;
2732 }
2733
2734 for (i = start; i < end; i += step) {
2735 MEM(dst = fr_value_box_alloc(ctx, FR_TYPE_UINT64, NULL));
2736 dst->vb_uint64 = i;
2737 fr_dcursor_append(out, dst);
2738 }
2739
2740 return XLAT_ACTION_DONE;
2741}
2742
2743#if defined(HAVE_REGEX_PCRE) || defined(HAVE_REGEX_PCRE2)
2744static xlat_arg_parser_t const xlat_func_regex_args[] = {
2747};
2748
2749
2750/** Get named subcapture value from previous regex
2751 *
2752 * Example:
2753@verbatim
2754if ("foo" =~ /^(?<name>.*)/) {
2755 noop
2756}
2757%regex(name) == "foo"
2758@endverbatim
2759 *
2760 * @ingroup xlat_functions
2761 */
2762static xlat_action_t xlat_func_regex(TALLOC_CTX *ctx, fr_dcursor_t *out,
2763 UNUSED xlat_ctx_t const *xctx,
2764 request_t *request, fr_value_box_list_t *in)
2765{
2766 fr_value_box_t *in_head = fr_value_box_list_head(in);
2767
2768 /*
2769 * Find the first child of the first argument group
2770 */
2771 fr_value_box_t *arg = fr_value_box_list_head(&in_head->vb_group);
2772
2773 /*
2774 * Return the complete capture if no other capture is specified
2775 */
2776 if (!arg) {
2777 fr_value_box_t *vb;
2778 char *p;
2779
2780 MEM(vb = fr_value_box_alloc_null(ctx));
2781 if (regex_request_to_sub(vb, &p, request, 0) < 0) {
2782 REDEBUG2("No previous regex capture");
2783 talloc_free(vb);
2784 return XLAT_ACTION_FAIL;
2785 }
2786
2787 fr_assert(p);
2788 fr_value_box_bstrdup_buffer_shallow(NULL, vb, NULL, p, false);
2790
2791 return XLAT_ACTION_DONE;
2792 }
2793
2794 switch (arg->type) {
2795 /*
2796 * If the input is an integer value then get an
2797 * arbitrary subcapture index.
2798 */
2799 case FR_TYPE_NUMERIC:
2800 {
2801 fr_value_box_t idx;
2802 fr_value_box_t *vb;
2803 char *p;
2804
2805 if (fr_value_box_list_next(in, in_head)) {
2806 REDEBUG("Only one subcapture argument allowed");
2807 return XLAT_ACTION_FAIL;
2808 }
2809
2810 if (fr_value_box_cast(NULL, &idx, FR_TYPE_UINT32, NULL, arg) < 0) {
2811 RPEDEBUG("Bad subcapture index");
2812 return XLAT_ACTION_FAIL;
2813 }
2814
2815 MEM(vb = fr_value_box_alloc_null(ctx));
2816 if (regex_request_to_sub(vb, &p, request, idx.vb_uint32) < 0) {
2817 REDEBUG2("No previous numbered regex capture group");
2818 talloc_free(vb);
2819 return XLAT_ACTION_FAIL;
2820 }
2821 fr_assert(p);
2822 fr_value_box_bstrdup_buffer_shallow(NULL, vb, NULL, p, false);
2824
2825 return XLAT_ACTION_DONE;
2826 }
2827
2828 default:
2829 {
2830 fr_value_box_t *vb;
2831 char *p;
2832
2833 /*
2834 * Concatenate all input
2835 */
2837 arg, &in_head->vb_group, FR_TYPE_STRING,
2839 SIZE_MAX) < 0) {
2840 RPEDEBUG("Failed concatenating input");
2841 return XLAT_ACTION_FAIL;
2842 }
2843
2844 MEM(vb = fr_value_box_alloc_null(ctx));
2845 if (regex_request_to_sub_named(vb, &p, request, arg->vb_strvalue) < 0) {
2846 REDEBUG2("No previous named regex capture group");
2847 talloc_free(vb);
2848 return XLAT_ACTION_FAIL;
2849 }
2850
2851 fr_assert(p);
2852 fr_value_box_bstrdup_buffer_shallow(NULL, vb, NULL, p, false);
2854
2855 return XLAT_ACTION_DONE;
2856 }
2857 }
2858}
2859#endif
2860
2862 { .concat = true, .type = FR_TYPE_OCTETS },
2864};
2865
2866/** Calculate the SHA1 hash of a string or attribute.
2867 *
2868 * Example:
2869@verbatim
2870%sha1(foo) == "0beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a33"
2871@endverbatim
2872 *
2873 * @ingroup xlat_functions
2874 */
2876 UNUSED xlat_ctx_t const *xctx,
2877 UNUSED request_t *request, fr_value_box_list_t *args)
2878{
2880 fr_sha1_ctx sha1_ctx;
2881 fr_value_box_t *vb;
2882 fr_value_box_t *in_head;
2883
2884 XLAT_ARGS(args, &in_head);
2885
2886 fr_sha1_init(&sha1_ctx);
2887 if (in_head) {
2888 fr_sha1_update(&sha1_ctx, in_head->vb_octets, in_head->vb_length);
2889 } else {
2890 /* sha1 of empty string */
2891 fr_sha1_update(&sha1_ctx, NULL, 0);
2892 }
2893 fr_sha1_final(digest, &sha1_ctx);
2894
2895 MEM(vb = fr_value_box_alloc_null(ctx));
2896 fr_value_box_memdup(vb, vb, NULL, digest, sizeof(digest), false);
2897
2899
2900 return XLAT_ACTION_DONE;
2901}
2902
2903/** Calculate any digest supported by OpenSSL EVP_MD
2904 *
2905 * Example:
2906@verbatim
2907%sha2_256(foo) == "0beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a33"
2908@endverbatim
2909 *
2910 * @ingroup xlat_functions
2911 */
2912#ifdef HAVE_OPENSSL_EVP_H
2913static xlat_action_t xlat_evp_md(TALLOC_CTX *ctx, fr_dcursor_t *out,
2914 UNUSED xlat_ctx_t const *xctx,
2915 UNUSED request_t *request, fr_value_box_list_t *args, EVP_MD const *md)
2916{
2917 uint8_t digest[EVP_MAX_MD_SIZE];
2918 unsigned int digestlen;
2919 EVP_MD_CTX *md_ctx;
2920 fr_value_box_t *vb;
2921 fr_value_box_t *in_head;
2922
2923 XLAT_ARGS(args, &in_head);
2924
2925 md_ctx = EVP_MD_CTX_create();
2926 EVP_DigestInit_ex(md_ctx, md, NULL);
2927 if (in_head) {
2928 EVP_DigestUpdate(md_ctx, in_head->vb_octets, in_head->vb_length);
2929 } else {
2930 EVP_DigestUpdate(md_ctx, NULL, 0);
2931 }
2932 EVP_DigestFinal_ex(md_ctx, digest, &digestlen);
2933 EVP_MD_CTX_destroy(md_ctx);
2934
2935 MEM(vb = fr_value_box_alloc_null(ctx));
2936 fr_value_box_memdup(vb, vb, NULL, digest, digestlen, false);
2937
2939
2940 return XLAT_ACTION_DONE;
2941}
2942
2943# define EVP_MD_XLAT(_md, _md_func) \
2944static xlat_action_t xlat_func_##_md(TALLOC_CTX *ctx, fr_dcursor_t *out,\
2945 xlat_ctx_t const *xctx, \
2946 request_t *request,\
2947 fr_value_box_list_t *in)\
2948{\
2949 return xlat_evp_md(ctx, out, xctx, request, in, EVP_##_md_func());\
2950}
2951
2952EVP_MD_XLAT(sha2_224, sha224)
2953EVP_MD_XLAT(sha2_256, sha256)
2954EVP_MD_XLAT(sha2_384, sha384)
2955EVP_MD_XLAT(sha2_512, sha512)
2956
2957/*
2958 * OpenWRT's OpenSSL library doesn't contain these by default
2959 */
2960#ifdef HAVE_EVP_BLAKE2S256
2961EVP_MD_XLAT(blake2s_256, blake2s256)
2962#endif
2963
2964#ifdef HAVE_EVP_BLAKE2B512
2965EVP_MD_XLAT(blake2b_512, blake2b512)
2966#endif
2967
2968EVP_MD_XLAT(sha3_224, sha3_224)
2969EVP_MD_XLAT(sha3_256, sha3_256)
2970EVP_MD_XLAT(sha3_384, sha3_384)
2971EVP_MD_XLAT(sha3_512, sha3_512)
2972#endif
2973
2974
2976 { .required = true, .concat = true, .type = FR_TYPE_STRING },
2978};
2979
2980/** Print data as string, if possible.
2981 *
2982 * Concat and cast one or more input boxes to a single output box string.
2983 *
2984 * @ingroup xlat_functions
2985 */
2987 UNUSED xlat_ctx_t const *xctx,
2988 UNUSED request_t *request, fr_value_box_list_t *in)
2989{
2990 fr_value_box_t *in_head = fr_value_box_list_pop_head(in);
2991
2992 /*
2993 * Casting and concat is done by arg processing
2994 * so just move the value box to the output
2995 */
2996 fr_dcursor_append(out, in_head);
2997
2998 return XLAT_ACTION_DONE;
2999}
3000
3001
3003 { .concat = true, .type = FR_TYPE_STRING },
3005};
3006
3007/** Print length of given string
3008 *
3009 * Example:
3010@verbatim
3011%strlen(foo) == 3
3012@endverbatim
3013 *
3014 * @see #xlat_func_length
3015 *
3016 * @ingroup xlat_functions
3017 */
3019 UNUSED xlat_ctx_t const *xctx,
3020 UNUSED request_t *request, fr_value_box_list_t *args)
3021{
3022 fr_value_box_t *vb;
3023 fr_value_box_t *in_head;
3024
3025 XLAT_ARGS(args, &in_head);
3026
3027 MEM(vb = fr_value_box_alloc(ctx, FR_TYPE_SIZE, NULL));
3028
3029 if (!in_head) {
3030 vb->vb_size = 0;
3031 } else {
3032 vb->vb_size = strlen(in_head->vb_strvalue);
3033 }
3034
3036
3037 return XLAT_ACTION_DONE;
3038}
3039
3041 { .concat = true, .type = FR_TYPE_STRING },
3042 { .single = true, .type = FR_TYPE_BOOL },
3044};
3045
3046/** Return whether a string has only printable chars
3047 *
3048 * This function returns true if the input string contains UTF8 sequences and printable chars.
3049 *
3050 * @note "\t" and " " are considered unprintable chars, unless the second argument(relaxed) is true.
3051 *
3052 * Example:
3053@verbatim
3054%str.printable("🍉abcdef🍓") == true
3055%str.printable("\000\n\r\t") == false
3056%str.printable("\t abcd", yes) == true
3057@endverbatim
3058 *
3059 * @ingroup xlat_functions
3060 */
3062 UNUSED xlat_ctx_t const *xctx,
3063 UNUSED request_t *request, fr_value_box_list_t *args)
3064{
3065 fr_value_box_t *vb;
3066 fr_value_box_t *str;
3067 fr_value_box_t *relaxed_vb;
3068 uint8_t const *p, *end;
3069 bool relaxed = false;
3070
3071 XLAT_ARGS(args, &str, &relaxed_vb);
3072
3073 if (relaxed_vb) relaxed = relaxed_vb->vb_bool;
3074
3075 p = (uint8_t const *)str->vb_strvalue;
3076 end = p + str->vb_length;
3077
3078 MEM(vb = fr_value_box_alloc(ctx, FR_TYPE_BOOL, NULL));
3080 vb->vb_bool = false;
3081
3082 do {
3083 size_t clen;
3084
3085 if ((*p < '!') &&
3086 (!relaxed || ((*p != '\t') && (*p != ' ')))) return XLAT_ACTION_DONE;
3087
3088 if (*p == 0x7f) return XLAT_ACTION_DONE;
3089
3090 clen = fr_utf8_char(p, end - p);
3091 if (clen == 0) return XLAT_ACTION_DONE;
3092 p += clen;
3093 } while (p < end);
3094
3095 vb->vb_bool = true;
3096
3097 return XLAT_ACTION_DONE;
3098}
3099
3101 { .concat = true, .type = FR_TYPE_STRING },
3103};
3104
3105/** Return whether a string is valid UTF-8
3106 *
3107 * This function returns true if the input string is valid UTF-8, false otherwise.
3108 *
3109 * Example:
3110@verbatim
3111%str.utf8(🍉🥝🍓) == true
3112%str.utf8(🍉\xff🍓) == false
3113@endverbatim
3114 *
3115 * @ingroup xlat_functions
3116 */
3118 UNUSED xlat_ctx_t const *xctx,
3119 UNUSED request_t *request, fr_value_box_list_t *args)
3120{
3121 fr_value_box_t *vb;
3122 fr_value_box_t *in_head;
3123
3124 XLAT_ARGS(args, &in_head);
3125
3126 MEM(vb = fr_value_box_alloc(ctx, FR_TYPE_BOOL, NULL));
3127 vb->vb_bool = (fr_utf8_str((uint8_t const *)in_head->vb_strvalue,
3128 in_head->vb_length) >= 0);
3129
3131
3132 return XLAT_ACTION_DONE;
3133}
3134
3136 { .single = true, .required = true, .type = FR_TYPE_VOID },
3137 { .single = true, .required = true, .type = FR_TYPE_INT32 },
3138 { .single = true, .type = FR_TYPE_INT32 },
3140};
3141
3142/** Extract a substring from string / octets data
3143 *
3144 * Non string / octets data is cast to a string.
3145 *
3146 * Second parameter is start position, optional third parameter is length
3147 * Negative start / length count from RHS of data.
3148 *
3149 * Example: (User-Name = "hello")
3150@verbatim
3151%substr(&User-Name, 1, 3) == 'ell'
3152@endverbatim
3153 *
3154 * @ingroup xlat_functions
3155 */
3156static xlat_action_t xlat_func_substr(TALLOC_CTX *ctx, fr_dcursor_t *out, UNUSED xlat_ctx_t const *xctx,
3157 request_t *request, fr_value_box_list_t *args)
3158{
3159 fr_value_box_t *in = NULL, *start_vb, *len_vb, *vb;
3160 int32_t start, end, len;
3161
3162 XLAT_ARGS(args, &in, &start_vb, &len_vb);
3163
3164 switch (in->type) {
3165 case FR_TYPE_OCTETS:
3166 case FR_TYPE_STRING:
3167 break;
3168
3169 default:
3171 RPEDEBUG("Failed casting value to string");
3172 return XLAT_ACTION_FAIL;
3173 }
3174 break;
3175 }
3176
3177 if (start_vb->vb_int32 > (int32_t)in->vb_length) return XLAT_ACTION_DONE;
3178
3179 if (start_vb->vb_int32 < 0) {
3180 start = in->vb_length + start_vb->vb_int32;
3181 if (start < 0) start = 0;
3182 } else {
3183 start = start_vb->vb_int32;
3184 }
3185
3186 if (len_vb) {
3187 if (len_vb->vb_int32 < 0) {
3188 end = in->vb_length + len_vb->vb_int32;
3189 if (end < 0) return XLAT_ACTION_DONE;
3190 } else {
3191 end = start + len_vb->vb_int32;
3192 if (end > (int32_t)in->vb_length) end = in->vb_length;
3193 }
3194 } else {
3195 end = in->vb_length;
3196 }
3197
3198 if (start >= end) return XLAT_ACTION_DONE;
3199
3200 MEM(vb = fr_value_box_alloc(ctx, in->type, NULL));
3201
3202 len = end - start;
3203 switch (in->type) {
3204 case FR_TYPE_STRING:
3205 fr_value_box_bstrndup(vb, vb, NULL, &in->vb_strvalue[start], len, in->tainted);
3206 break;
3207 case FR_TYPE_OCTETS:
3208 {
3209 uint8_t *buf;
3210 fr_value_box_mem_alloc(vb, &buf, vb, NULL, len, in->tainted);
3211 memcpy(buf, &in->vb_octets[start], len);
3212 }
3213 break;
3214 default:
3215 fr_assert(0);
3216 }
3218
3219 return XLAT_ACTION_DONE;
3220}
3221
3222#ifdef HAVE_REGEX_PCRE2
3223/** Cache statically compiled expressions
3224 */
3225typedef struct {
3226 regex_t *pattern;
3227 fr_regex_flags_t flags;
3228} xlat_subst_regex_inst_t;
3229
3230/** Pre-compile regexes where possible
3231 */
3232static int xlat_instantiate_subst_regex(xlat_inst_ctx_t const *xctx)
3233{
3234 xlat_subst_regex_inst_t *inst = talloc_get_type_abort(xctx->inst, xlat_subst_regex_inst_t);
3235 xlat_exp_t *patt_exp;
3236 fr_sbuff_t sbuff;
3237 fr_sbuff_marker_t start_m, end_m;
3238
3239 /* args #2 (pattern) */
3240 patt_exp = fr_dlist_next(&xctx->ex->call.args->dlist, fr_dlist_head(&xctx->ex->call.args->dlist));
3241 fr_assert(patt_exp && patt_exp->type == XLAT_GROUP); /* args must be groups */
3242
3243 /* If there are dynamic expansions, we can't pre-compile */
3244 if (!xlat_is_literal(patt_exp->group)) return 0;
3245 fr_assert(fr_dlist_num_elements(&patt_exp->group->dlist) == 1);
3246
3247 patt_exp = fr_dlist_head(&patt_exp->group->dlist);
3248
3249 /* We can only pre-compile strings */
3250 if (!fr_type_is_string(patt_exp->data.type)) return 0;
3251
3252 sbuff = FR_SBUFF_IN(patt_exp->data.vb_strvalue, patt_exp->data.vb_length);
3253
3254 /* skip any whitesapce */
3255 fr_sbuff_adv_past_whitespace(&sbuff, SIZE_MAX, 0);
3256
3257 /* Is the next char a forward slash? */
3258 if (fr_sbuff_next_if_char(&sbuff, '/')) {
3259 fr_slen_t slen;
3260
3261 fr_sbuff_marker(&start_m, &sbuff);
3262
3263 if (!fr_sbuff_adv_to_chr(&sbuff, SIZE_MAX, '/')) return 0; /* Not a regex */
3264
3265 fr_sbuff_marker(&end_m, &sbuff);
3266 fr_sbuff_next(&sbuff); /* skip trailing slash */
3267
3268 if (fr_sbuff_remaining(&sbuff)) {
3269 slen = regex_flags_parse(NULL, &inst->flags,
3270 &sbuff,
3271 NULL, true);
3272 if (slen < 0) {
3273 PERROR("Failed parsing regex flags in \"%s\"", patt_exp->data.vb_strvalue);
3274 return -1;
3275 }
3276 }
3277
3278 if (regex_compile(inst, &inst->pattern,
3279 fr_sbuff_current(&start_m), fr_sbuff_current(&end_m) - fr_sbuff_current(&start_m),
3280 &inst->flags, true, false) <= 0) {
3281 PERROR("Failed compiling regex \"%s\"", patt_exp->data.vb_strvalue);
3282 return -1;
3283 }
3284 }
3285 /* No... then it's not a regex */
3286
3287 return 0;
3288}
3289
3290/** Perform regex substitution TODO CHECK
3291 *
3292 * Called when %subst() pattern begins with "/"
3293 *
3294@verbatim
3295%subst(<subject>, /<regex>/[flags], <replace>)
3296@endverbatim
3297 *
3298 * Example: (User-Name = "foo")
3299@verbatim
3300%subst(%{User-Name}, /oo.*$/, 'un') == "fun"
3301@endverbatim
3302 *
3303 * @note References can be specified in the replacement string with $<ref>
3304 *
3305 * @see #xlat_func_subst
3306 *
3307 * @ingroup xlat_functions
3308 */
3309static int xlat_func_subst_regex(TALLOC_CTX *ctx, fr_dcursor_t *out,
3310 xlat_ctx_t const *xctx, request_t *request,
3311 fr_value_box_list_t *args)
3312{
3313 xlat_subst_regex_inst_t const *inst = talloc_get_type_abort_const(xctx->inst, xlat_subst_regex_inst_t);
3314 fr_sbuff_t sbuff;
3315 fr_sbuff_marker_t start_m, end_m;
3316 char *buff;
3317 ssize_t slen;
3318 regex_t *pattern, *our_pattern = NULL;
3319 fr_regex_flags_t const *flags;
3320 fr_regex_flags_t our_flags = {};
3321 fr_value_box_t *vb;
3322 fr_value_box_t *subject_vb;
3323 fr_value_box_t *regex_vb;
3324 fr_value_box_t *rep_vb;
3325
3326 XLAT_ARGS(args, &subject_vb, &regex_vb, &rep_vb);
3327
3328 /*
3329 * Was not pre-compiled, so we need to compile it now
3330 */
3331 if (!inst->pattern) {
3332 sbuff = FR_SBUFF_IN(regex_vb->vb_strvalue, regex_vb->vb_length);
3333 if (fr_sbuff_len(&sbuff) == 0) {
3334 REDEBUG("Regex must not be empty");
3335 return XLAT_ACTION_FAIL;
3336 }
3337
3338 fr_sbuff_next(&sbuff); /* skip leading slash */
3339 fr_sbuff_marker(&start_m, &sbuff);
3340
3341 if (!fr_sbuff_adv_to_chr(&sbuff, SIZE_MAX, '/')) return 1; /* Not a regex */
3342
3343 fr_sbuff_marker(&end_m, &sbuff);
3344 fr_sbuff_next(&sbuff); /* skip trailing slash */
3345
3346 slen = regex_flags_parse(NULL, &our_flags, &sbuff, NULL, true);
3347 if (slen < 0) {
3348 RPEDEBUG("Failed parsing regex flags");
3349 return -1;
3350 }
3351
3352 /*
3353 * Process the substitution
3354 */
3355 if (regex_compile(NULL, &our_pattern,
3356 fr_sbuff_current(&start_m), fr_sbuff_current(&end_m) - fr_sbuff_current(&start_m),
3357 &our_flags, true, true) <= 0) {
3358 RPEDEBUG("Failed compiling regex");
3359 return -1;
3360 }
3361 pattern = our_pattern;
3362 flags = &our_flags;
3363 } else {
3364 pattern = inst->pattern;
3365 flags = &inst->flags;
3366 }
3367
3368 MEM(vb = fr_value_box_alloc_null(ctx));
3369 if (regex_substitute(vb, &buff, 0, pattern, flags,
3370 subject_vb->vb_strvalue, subject_vb->vb_length,
3371 rep_vb->vb_strvalue, rep_vb->vb_length, NULL) < 0) {
3372 RPEDEBUG("Failed performing substitution");
3373 talloc_free(vb);
3374 talloc_free(pattern);
3375 return -1;
3376 }
3377 fr_value_box_bstrdup_buffer_shallow(NULL, vb, NULL, buff, subject_vb->tainted | rep_vb->tainted);
3379
3381
3382 talloc_free(our_pattern);
3383
3384 return 0;
3385}
3386#endif
3387
3389 { .required = true, .concat = true, .type = FR_TYPE_STRING },
3390 { .required = true, .concat = true, .type = FR_TYPE_STRING },
3391 { .required = true, .concat = true, .type = FR_TYPE_STRING },
3393};
3394
3395/** Perform regex substitution
3396 *
3397@verbatim
3398%subst(<subject>, <pattern>, <replace>)
3399@endverbatim
3400 *
3401 * Example: (User-Name = "foobar")
3402@verbatim
3403%subst(%{User-Name}, 'oo', 'un') == "funbar"
3404@endverbatim
3405 *
3406 * @see xlat_func_subst_regex
3407 *
3408 * @ingroup xlat_functions
3409 */
3411#ifdef HAVE_REGEX_PCRE2
3412 xlat_ctx_t const *xctx,
3413#else
3414 UNUSED xlat_ctx_t const *xctx,
3415#endif
3416 request_t *request, fr_value_box_list_t *args)
3417{
3418 char const *p, *q, *end;
3419 char *vb_str;
3420
3421 char const *pattern, *rep;
3422 size_t pattern_len, rep_len;
3423
3424 fr_value_box_t *rep_vb, *vb;
3425 fr_value_box_t *subject_vb;
3426 fr_value_box_t *pattern_vb;
3427
3428 XLAT_ARGS(args, &subject_vb, &pattern_vb, &rep_vb);
3429
3430 /* coverity[dereference] */
3431 pattern = pattern_vb->vb_strvalue;
3432 if (*pattern == '/') {
3433#ifdef HAVE_REGEX_PCRE2
3434 switch (xlat_func_subst_regex(ctx, out, xctx, request, args)) {
3435 case 0:
3436 return XLAT_ACTION_DONE;
3437
3438 case 1:
3439 /* Not a regex, fall through */
3440 break;
3441
3442 case -1:
3443 return XLAT_ACTION_FAIL;
3444 }
3445#else
3446 if (memchr(pattern, '/', pattern_vb->vb_length - 1)) {
3447 REDEBUG("regex based substitutions require libpcre2. "
3448 "Check ${features.regex-pcre2} to determine support");
3449 }
3450 return XLAT_ACTION_FAIL;
3451#endif
3452 }
3453
3454 /*
3455 * Check for empty pattern
3456 */
3457 pattern_len = pattern_vb->vb_length;
3458 if (pattern_len == 0) {
3459 REDEBUG("Empty pattern");
3460 return XLAT_ACTION_FAIL;
3461 }
3462
3463 rep = rep_vb->vb_strvalue;
3464 rep_len = rep_vb->vb_length;
3465
3466 p = subject_vb->vb_strvalue;
3467 end = p + subject_vb->vb_length;
3468
3469 MEM(vb = fr_value_box_alloc_null(ctx));
3470 vb_str = talloc_bstrndup(vb, "", 0);
3471
3472 while (p < end) {
3473 q = memmem(p, end - p, pattern, pattern_len);
3474 if (!q) {
3475 MEM(vb_str = talloc_bstr_append(vb, vb_str, p, end - p));
3476 break;
3477 }
3478
3479 if (q > p) MEM(vb_str = talloc_bstr_append(vb, vb_str, p, q - p));
3480 if (rep_len) MEM(vb_str = talloc_bstr_append(vb, vb_str, rep, rep_len));
3481 p = q + pattern_len;
3482 }
3483
3484 if (fr_value_box_bstrdup_buffer_shallow(NULL, vb, NULL, vb_str, subject_vb->tainted | rep_vb->tainted) < 0) {
3485 RPEDEBUG("Failed creating output box");
3486 talloc_free(vb);
3487 return XLAT_ACTION_FAIL;
3488 }
3489
3490 fr_assert(vb && (vb->type != FR_TYPE_NULL));
3493
3494 return XLAT_ACTION_DONE;
3495}
3496
3498 { .required = false, .single = true, .type = FR_TYPE_STRING },
3500};
3501
3502/** Return the time as a #FR_TYPE_DATE
3503 *
3504 * Note that all operations are UTC.
3505 *
3506@verbatim
3507%time()
3508@endverbatim
3509 *
3510 * Example:
3511@verbatim
3512update reply {
3513 &Reply-Message := "%{%time(now) - %time(request)}"
3514}
3515@endverbatim
3516 *
3517 * @ingroup xlat_functions
3518 */
3520 UNUSED xlat_ctx_t const *xctx,
3521 request_t *request, fr_value_box_list_t *args)
3522{
3523 fr_value_box_t *arg;
3524 fr_value_box_t *vb;
3526
3527 XLAT_ARGS(args, &arg);
3528
3529 if (!arg || (strcmp(arg->vb_strvalue, "now") == 0)) {
3531
3532 } else if (strcmp(arg->vb_strvalue, "request") == 0) {
3533 value = fr_time_to_unix_time(request->packet->timestamp);
3534
3535 } else if (strcmp(arg->vb_strvalue, "offset") == 0) {
3536 MEM(vb = fr_value_box_alloc(ctx, FR_TYPE_TIME_DELTA, NULL));
3537 vb->vb_time_delta = fr_time_gmtoff();
3538 goto append;
3539
3540 } else if (strcmp(arg->vb_strvalue, "dst") == 0) {
3541 MEM(vb = fr_value_box_alloc(ctx, FR_TYPE_BOOL, NULL));
3542 vb->vb_bool = fr_time_is_dst();
3543 goto append;
3544
3545 } else if (strcmp(arg->vb_strvalue, "mday_offset") == 0) {
3546 struct tm tm;
3547 fr_unix_time_t unix_time = fr_time_to_unix_time(request->packet->timestamp);
3548 time_t when = fr_unix_time_to_sec(unix_time);
3549 int64_t nsec;
3550
3551 gmtime_r(&when, &tm);
3552
3553 nsec = (int64_t) 86400 * (tm.tm_mday - 1);
3554 nsec += when % 86400;
3555 nsec *= NSEC;
3556 nsec += fr_unix_time_unwrap(unix_time) % NSEC;
3557
3558 MEM(vb = fr_value_box_alloc(ctx, FR_TYPE_TIME_DELTA, NULL));
3559 vb->vb_time_delta = fr_time_delta_wrap(nsec);
3560 goto append;
3561
3562 } else if (strcmp(arg->vb_strvalue, "wday_offset") == 0) {
3563 struct tm tm;
3564 fr_unix_time_t unix_time = fr_time_to_unix_time(request->packet->timestamp);
3565 time_t when = fr_unix_time_to_sec(unix_time);
3566 int64_t nsec;
3567
3568 gmtime_r(&when, &tm);
3569
3570 nsec = (int64_t) 86400 * tm.tm_wday;
3571 nsec += when % 86400;
3572 nsec *= NSEC;
3573 nsec += fr_unix_time_unwrap(unix_time) % NSEC;
3574
3575 MEM(vb = fr_value_box_alloc(ctx, FR_TYPE_TIME_DELTA, NULL));
3576 vb->vb_time_delta = fr_time_delta_wrap(nsec);
3577
3578 MEM(vb = fr_value_box_alloc(ctx, FR_TYPE_TIME_DELTA, NULL));
3579 vb->vb_time_delta = fr_time_delta_wrap(nsec);
3580 goto append;
3581
3582 } else if (fr_unix_time_from_str(&value, arg->vb_strvalue, FR_TIME_RES_SEC) < 0) {
3583 REDEBUG("Invalid time specification '%s'", arg->vb_strvalue);
3584 return XLAT_ACTION_FAIL;
3585 }
3586
3587 MEM(vb = fr_value_box_alloc(ctx, FR_TYPE_DATE, NULL));
3588 vb->vb_date = value;
3589
3590append:
3592
3593 return XLAT_ACTION_DONE;
3594}
3595
3596
3597/** Change case of a string
3598 *
3599 * If upper is true, change to uppercase, otherwise, change to lowercase
3600 */
3602 UNUSED request_t *request, fr_value_box_list_t *args, bool upper)
3603{
3604 char *p;
3605 char const *end;
3606 fr_value_box_t *vb;
3607
3608 XLAT_ARGS(args, &vb);
3609
3610 p = UNCONST(char *, vb->vb_strvalue);
3611 end = p + vb->vb_length;
3612
3613 while (p < end) {
3614 *(p) = upper ? toupper ((int) *(p)) : tolower((uint8_t) *(p));
3615 p++;
3616 }
3617
3618 fr_value_box_list_remove(args, vb); /* Can't leave it in both lists */
3620
3621 return XLAT_ACTION_DONE;
3622}
3623
3625 { .required = true, .concat = true, .type = FR_TYPE_STRING },
3627};
3628
3629
3630/** Convert a string to lowercase
3631 *
3632 * Example:
3633@verbatim
3634%tolower("Bar") == "bar"
3635@endverbatim
3636 *
3637 * Probably only works for ASCII
3638 *
3639 * @ingroup xlat_functions
3640 */
3642 UNUSED xlat_ctx_t const *xctx,
3643 request_t *request, fr_value_box_list_t *in)
3644{
3645 return xlat_change_case(ctx, out, request, in, false);
3646}
3647
3648
3649/** Convert a string to uppercase
3650 *
3651 * Example:
3652@verbatim
3653%toupper("Foo") == "FOO"
3654@endverbatim
3655 *
3656 * Probably only works for ASCII
3657 *
3658 * @ingroup xlat_functions
3659 */
3661 UNUSED xlat_ctx_t const *xctx,
3662 request_t *request, fr_value_box_list_t *in)
3663{
3664 return xlat_change_case(ctx, out, request, in, true);
3665}
3666
3667
3669 { .required = true, .concat = true, .type = FR_TYPE_STRING },
3671};
3672
3673/** URLencode special characters
3674 *
3675 * Example:
3676@verbatim
3677%urlquote("http://example.org/") == "http%3A%47%47example.org%47"
3678@endverbatim
3679 *
3680 * @ingroup xlat_functions
3681 */
3683 UNUSED xlat_ctx_t const *xctx,
3684 UNUSED request_t *request, fr_value_box_list_t *args)
3685{
3686 char const *p, *end;
3687 char *buff_p;
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 (isalnum(*p) ||
3702 *p == '-' ||
3703 *p == '_' ||
3704 *p == '.' ||
3705 *p == '~') {
3706 outlen++;
3707 } else {
3708 outlen += 3;
3709 }
3710 p++;
3711 }
3712
3713 MEM(vb = fr_value_box_alloc_null(ctx));
3714 MEM(fr_value_box_bstr_alloc(vb, &buff_p, vb, NULL, outlen, false) == 0);
3716
3717 /* Reset p to start position */
3718 p = in_head->vb_strvalue;
3719
3720 while (p < end) {
3721 if (isalnum(*p)) {
3722 *buff_p++ = *p++;
3723 continue;
3724 }
3725
3726 switch (*p) {
3727 case '-':
3728 case '_':
3729 case '.':
3730 case '~':
3731 *buff_p++ = *p++;
3732 break;
3733
3734 default:
3735 /* MUST be upper case hex to be compliant */
3736 snprintf(buff_p, 4, "%%%02X", (uint8_t) *p++); /* %XX */
3737
3738 buff_p += 3;
3739 }
3740 }
3741
3742 *buff_p = '\0';
3743
3745
3746 return XLAT_ACTION_DONE;
3747}
3748
3749
3751 { .required = true, .concat = true, .type = FR_TYPE_STRING },
3753};
3754
3755/** URLdecode special characters
3756 *
3757 * @note Remember to escape % with %% in strings, else xlat will try to parse it.
3758 *
3759 * Example:
3760@verbatim
3761%urlunquote("http%%3A%%47%%47example.org%%47") == "http://example.org/"
3762@endverbatim
3763 *
3764 * @ingroup xlat_functions
3765 */
3767 UNUSED xlat_ctx_t const *xctx,
3768 request_t *request, fr_value_box_list_t *args)
3769{
3770 char const *p, *end;
3771 char *buff_p;
3772 char *c1, *c2;
3773 size_t outlen = 0;
3774 fr_value_box_t *vb;
3775 fr_value_box_t *in_head;
3776
3777 XLAT_ARGS(args, &in_head);
3778
3779 p = in_head->vb_strvalue;
3780 end = p + in_head->vb_length;
3781
3782 /*
3783 * Calculate size of output
3784 */
3785 while (p < end) {
3786 if (*p == '%') {
3787 p += 3;
3788 } else {
3789 p++;
3790 }
3791 outlen++;
3792 }
3793
3794 MEM(vb = fr_value_box_alloc_null(ctx));
3795 MEM(fr_value_box_bstr_alloc(vb, &buff_p, vb, NULL, outlen, false) == 0);
3797
3798 /* Reset p to start position */
3799 p = in_head->vb_strvalue;
3800
3801 while (p < end) {
3802 if (*p != '%') {
3803 *buff_p++ = *p++;
3804 continue;
3805 }
3806 /* Is a % char */
3807
3808 /* Don't need \0 check, as it won't be in the hextab */
3809 if (!(c1 = memchr(hextab, tolower((uint8_t) *++p), 16)) ||
3810 !(c2 = memchr(hextab, tolower((uint8_t) *++p), 16))) {
3811 REMARKER(in_head->vb_strvalue, p - in_head->vb_strvalue, "Non-hex char in %% sequence");
3812 talloc_free(vb);
3813
3814 return XLAT_ACTION_FAIL;
3815 }
3816 p++;
3817 *buff_p++ = ((c1 - hextab) << 4) + (c2 - hextab);
3818 }
3819
3820 *buff_p = '\0';
3822
3823 return XLAT_ACTION_DONE;
3824}
3825
3830
3831/** Decode any protocol attribute / options
3832 *
3833 * Creates protocol-specific attributes based on the given binary option data
3834 *
3835 * Example:
3836@verbatim
3837%dhcpv4.decode(%{Tmp-Octets-0})
3838@endverbatim
3839 *
3840 * @ingroup xlat_functions
3841 */
3843 xlat_ctx_t const *xctx,
3844 request_t *request, fr_value_box_list_t *in)
3845{
3846 int decoded;
3847 fr_value_box_t *vb;
3848 void *decode_ctx = NULL;
3849 fr_test_point_pair_decode_t const *tp_decode = *(void * const *)xctx->inst;
3850
3851 if (tp_decode->test_ctx) {
3852 if (tp_decode->test_ctx(&decode_ctx, ctx, request->dict) < 0) {
3853 return XLAT_ACTION_FAIL;
3854 }
3855 }
3856
3857 decoded = xlat_decode_value_box_list(request->request_ctx, &request->request_pairs,
3858 request, decode_ctx, tp_decode->func, in);
3859 if (decoded <= 0) {
3860 talloc_free(decode_ctx);
3861 RPERROR("Protocol decoding failed");
3862 return XLAT_ACTION_FAIL;
3863 }
3864
3865 /*
3866 * Create a value box to hold the decoded count, and add
3867 * it to the output list.
3868 */
3869 MEM(vb = fr_value_box_alloc(ctx, FR_TYPE_UINT32, NULL));
3870 vb->vb_uint32 = decoded;
3872
3873 talloc_free(decode_ctx);
3874 return XLAT_ACTION_DONE;
3875}
3876
3878 { .required = true, .single = true, .type = FR_TYPE_IPV4_PREFIX },
3880};
3881
3882/** Calculate the subnet mask from a IPv4 prefix
3883 *
3884 * Example:
3885@verbatim
3886%ip.v4.netmask(%{Network-Prefix})
3887@endverbatim
3888 *
3889 * @ingroup xlat_functions
3890 */
3892 UNUSED request_t *request, fr_value_box_list_t *args)
3893{
3894 fr_value_box_t *subnet, *vb;
3895 XLAT_ARGS(args, &subnet);
3896
3897 MEM(vb = fr_value_box_alloc(ctx, FR_TYPE_IPV4_ADDR, NULL));
3898 vb->vb_ip.addr.v4.s_addr = htonl((uint32_t)0xffffffff << (32 - subnet->vb_ip.prefix));
3900
3901 return XLAT_ACTION_DONE;
3902}
3903
3904/** Calculate the broadcast address from a IPv4 prefix
3905 *
3906 * Example:
3907@verbatim
3908%ip.v4.broadcast(%{Network-Prefix})
3909@endverbatim
3910 *
3911 * @ingroup xlat_functions
3912 */
3914 UNUSED request_t *request, fr_value_box_list_t *args)
3915{
3916 fr_value_box_t *subnet, *vb;
3917 XLAT_ARGS(args, &subnet);
3918
3919 MEM(vb = fr_value_box_alloc(ctx, FR_TYPE_IPV4_ADDR, NULL));
3920 vb->vb_ip.addr.v4.s_addr = htonl( ntohl(subnet->vb_ip.addr.v4.s_addr) | (uint32_t)0xffffffff >> subnet->vb_ip.prefix);
3922
3923 return XLAT_ACTION_DONE;
3924}
3925
3927{
3928 *(void **) mctx->inst = mctx->uctx;
3929 return 0;
3930}
3931
3933 { .required = true, .single = true, .type = FR_TYPE_STRING },
3935};
3936
3937/** Encode protocol attributes / options
3938 *
3939 * Returns octet string created from the provided pairs
3940 *
3941 * Example:
3942@verbatim
3943%dhcpv4.encode(&request[*])
3944@endverbatim
3945 *
3946 * @ingroup xlat_functions
3947 */
3949 xlat_ctx_t const *xctx,
3950 request_t *request, fr_value_box_list_t *args)
3951{
3952 tmpl_t *vpt;
3953 fr_pair_t *vp;
3954 fr_dcursor_t cursor;
3956 bool tainted = false;
3957 fr_value_box_t *encoded;
3958
3959 uint8_t binbuf[2048];
3960 uint8_t *p = binbuf, *end = p + sizeof(binbuf);
3961 ssize_t len = 0;
3962 fr_value_box_t *in_head;
3963 void *encode_ctx = NULL;
3964 fr_test_point_pair_encode_t const *tp_encode;
3965
3966 XLAT_ARGS(args, &in_head);
3967
3968 memcpy(&tp_encode, xctx->inst, sizeof(tp_encode)); /* const issues */
3969
3970 if (tmpl_afrom_attr_str(ctx, NULL, &vpt, in_head->vb_strvalue,
3971 &(tmpl_rules_t){
3972 .attr = {
3973 .dict_def = request->dict,
3974 .list_def = request_attr_request,
3975 .allow_wildcard = true,
3976 .prefix = TMPL_ATTR_REF_PREFIX_AUTO
3977 }
3978 }) <= 0) {
3979 RPEDEBUG("Failed parsing attribute reference");
3980 return XLAT_ACTION_FAIL;
3981 }
3982
3983 /*
3984 * Create the encoding context.
3985 */
3986 if (tp_encode->test_ctx) {
3987 if (tp_encode->test_ctx(&encode_ctx, vpt, request->dict) < 0) {
3989 return XLAT_ACTION_FAIL;
3990 }
3991 }
3992
3993 /*
3994 * Loop over the attributes, encoding them.
3995 */
3996 for (vp = tmpl_dcursor_init(NULL, NULL, &cc, &cursor, request, vpt);
3997 vp != NULL;
3998 vp = fr_dcursor_next(&cursor)) {
3999 if (vp->da->flags.internal) continue;
4000
4001 /*
4002 * Don't check the dictionaries. By definition,
4003 * vp->da->dict==request->dict, OR else we're
4004 * using the internal encoder and encoding a real
4005 * protocol.
4006 *
4007 * However, we likely still want a
4008 * dictionary-specific "is encodable" function,
4009 * as AKA/SIM and DHCPv6 encode "bool"s only if
4010 * their value is true.
4011 */
4012
4013 len = tp_encode->func(&FR_DBUFF_TMP(p, end), &cursor, encode_ctx);
4014 if (len < 0) {
4015 RPEDEBUG("Protocol encoding failed");
4016 tmpl_dcursor_clear(&cc);
4018 return XLAT_ACTION_FAIL;
4019 }
4020
4021 tainted |= vp->vp_tainted;
4022 p += len;
4023 }
4024
4025 tmpl_dcursor_clear(&cc);
4027
4028 /*
4029 * Pass the options string back to the caller.
4030 */
4031 MEM(encoded = fr_value_box_alloc_null(ctx));
4032 fr_value_box_memdup(encoded, encoded, NULL, binbuf, (size_t)len, tainted);
4033 fr_dcursor_append(out, encoded);
4034
4035 return XLAT_ACTION_DONE;
4036}
4037
4039{
4040 fr_test_point_pair_decode_t *tp_decode;
4041 fr_test_point_pair_encode_t *tp_encode;
4042 xlat_t *xlat;
4043 char buffer[256+32];
4044
4045 /*
4046 * See if there's a decode function for it.
4047 */
4048 snprintf(buffer, sizeof(buffer), "%s_tp_decode_pair", name);
4049 tp_decode = dlsym(dl->handle, buffer);
4050 if (tp_decode) {
4051 snprintf(buffer, sizeof(buffer), "%s.decode", name);
4052
4053 /* May be called multiple times, so just skip protocols we've already registered */
4054 if (xlat_func_find(buffer, -1)) return 1;
4055
4056 if (unlikely((xlat = xlat_func_register(NULL, buffer, protocol_decode_xlat, FR_TYPE_UINT32)) == NULL)) return -1;
4058 /* coverity[suspicious_sizeof] */
4061 }
4062
4063 /*
4064 * See if there's an encode function for it.
4065 */
4066 snprintf(buffer, sizeof(buffer), "%s_tp_encode_pair", name);
4067 tp_encode = dlsym(dl->handle, buffer);
4068 if (tp_encode) {
4069 snprintf(buffer, sizeof(buffer), "%s.encode", name);
4070
4071 if (xlat_func_find(buffer, -1)) return 1;
4072
4073 if (unlikely((xlat = xlat_func_register(NULL, buffer, protocol_encode_xlat, FR_TYPE_OCTETS)) == NULL)) return -1;
4075 /* coverity[suspicious_sizeof] */
4078 }
4079
4080 return 0;
4081}
4082
4083static int xlat_protocol_register(fr_dict_t const *dict)
4084{
4085 dl_t *dl = fr_dict_dl(dict);
4086 char *p, name[256];
4087
4088 /*
4089 * No library for this protocol, skip it.
4090 *
4091 * Protocol TEST has no libfreeradius-test, so that's OK.
4092 */
4093 if (!dl) return 0;
4094
4095 strlcpy(name, fr_dict_root(dict)->name, sizeof(name));
4096 for (p = name; *p != '\0'; p++) {
4097 *p = tolower((uint8_t) *p);
4098 }
4099
4101}
4102
4104
4106{
4107 dl_t *dl;
4108
4109 cbor_loader = dl_loader_init(NULL, NULL, false, false);
4110 if (!cbor_loader) return 0;
4111
4112 dl = dl_by_name(cbor_loader, "libfreeradius-cbor", NULL, false);
4113 if (!dl) return 0;
4114
4115 if (xlat_protocol_register_by_name(dl, "cbor") < 0) return -1;
4116
4117 return 0;
4118}
4119
4120
4121/** Register xlats for any loaded dictionaries
4122 */
4124{
4125 fr_dict_t *dict;
4127
4128 for (dict = fr_dict_global_ctx_iter_init(&iter);
4129 dict != NULL;
4130 dict = fr_dict_global_ctx_iter_next(&iter)) {
4131 if (xlat_protocol_register(dict) < 0) return -1;
4132 }
4133
4134 /*
4135 * And the internal protocol, too.
4136 */
4137 if (xlat_protocol_register(fr_dict_internal()) < 0) return -1;
4138
4139 /*
4140 * And cbor stuff
4141 */
4142 if (xlat_protocol_register_cbor() < 0) return -1;
4143
4144 return 0;
4145}
4146
4147/** De-register all xlat functions we created
4148 *
4149 */
4150static int _xlat_global_free(UNUSED void *uctx)
4151{
4152 TALLOC_FREE(xlat_ctx);
4156
4157 return 0;
4158}
4159
4160/** Global initialisation for xlat
4161 *
4162 * @note Free memory with #xlat_free
4163 *
4164 * @return
4165 * - 0 on success.
4166 * - -1 on failure.
4167 *
4168 * @hidecallgraph
4169 */
4170static int _xlat_global_init(UNUSED void *uctx)
4171{
4172 xlat_t *xlat;
4173
4174 xlat_ctx = talloc_init("xlat");
4175 if (!xlat_ctx) return -1;
4176
4177 if (xlat_func_init() < 0) return -1;
4178
4179 /*
4180 * Lookup attributes used by virtual xlat expansions.
4181 */
4182 if (xlat_eval_init() < 0) return -1;
4183
4184 /*
4185 * Registers async xlat operations in the `unlang` interpreter.
4186 */
4188
4189 /*
4190 * These are all "pure" functions.
4191 */
4192#define XLAT_REGISTER_ARGS(_xlat, _func, _return_type, _args) \
4193do { \
4194 if (unlikely((xlat = xlat_func_register(xlat_ctx, _xlat, _func, _return_type)) == NULL)) return -1; \
4195 xlat_func_args_set(xlat, _args); \
4196 xlat_func_flags_set(xlat, XLAT_FUNC_FLAG_PURE | XLAT_FUNC_FLAG_INTERNAL); \
4197} while (0)
4198
4214
4215 /*
4216 * The inputs to these functions are variable.
4217 */
4218#undef XLAT_REGISTER_ARGS
4219#define XLAT_REGISTER_ARGS(_xlat, _func, _return_type, _args) \
4220do { \
4221 if (unlikely((xlat = xlat_func_register(xlat_ctx, _xlat, _func, _return_type)) == NULL)) return -1; \
4222 xlat_func_args_set(xlat, _args); \
4223 xlat_func_flags_set(xlat, XLAT_FUNC_FLAG_INTERNAL); \
4224} while (0)
4225
4242#ifdef HAVE_REGEX_PCRE2
4243 xlat_func_instantiate_set(xlat, xlat_instantiate_subst_regex, xlat_subst_regex_inst_t, NULL, NULL);
4244#endif
4252
4253 if (unlikely((xlat = xlat_func_register(xlat_ctx, "untaint", xlat_func_untaint, FR_TYPE_VOID)) == NULL)) return -1;
4256
4257 if (unlikely((xlat = xlat_func_register(xlat_ctx, "taint", xlat_func_taint, FR_TYPE_VOID)) == NULL)) return -1;
4260
4261 /*
4262 * All of these functions are pure.
4263 */
4264#define XLAT_REGISTER_PURE(_xlat, _func, _return_type, _arg) \
4265do { \
4266 if (unlikely((xlat = xlat_func_register(xlat_ctx, _xlat, _func, _return_type)) == NULL)) return -1; \
4267 xlat_func_args_set(xlat, _arg); \
4268 xlat_func_flags_set(xlat, XLAT_FUNC_FLAG_PURE | XLAT_FUNC_FLAG_INTERNAL); \
4269} while (0)
4270
4276#if defined(HAVE_REGEX_PCRE) || defined(HAVE_REGEX_PCRE2)
4277 if (unlikely((xlat = xlat_func_register(xlat_ctx, "regex", xlat_func_regex, FR_TYPE_STRING)) == NULL)) return -1;
4278 xlat_func_args_set(xlat, xlat_func_regex_args);
4280#endif
4281
4282 {
4283 static xlat_arg_parser_t const xlat_regex_safe_args[] = {
4284 { .type = FR_TYPE_STRING, .variadic = true, .concat = true },
4286 };
4287
4288 if (unlikely((xlat = xlat_func_register(xlat_ctx, "regex.safe",
4289 xlat_transparent, FR_TYPE_STRING)) == NULL)) return -1;
4291 xlat_func_args_set(xlat, xlat_regex_safe_args);
4292 xlat_func_safe_for_set(xlat, FR_REGEX_SAFE_FOR);
4293 }
4294
4296
4297#ifdef HAVE_OPENSSL_EVP_H
4298 XLAT_REGISTER_PURE("sha2_224", xlat_func_sha2_224, FR_TYPE_OCTETS, xlat_func_sha_arg);
4299 XLAT_REGISTER_PURE("sha2_256", xlat_func_sha2_256, FR_TYPE_OCTETS, xlat_func_sha_arg);
4300 XLAT_REGISTER_PURE("sha2_384", xlat_func_sha2_384, FR_TYPE_OCTETS, xlat_func_sha_arg);
4301 XLAT_REGISTER_PURE("sha2_512", xlat_func_sha2_512, FR_TYPE_OCTETS, xlat_func_sha_arg);
4302
4303# ifdef HAVE_EVP_BLAKE2S256
4304 XLAT_REGISTER_PURE("blake2s_256", xlat_func_blake2s_256, FR_TYPE_OCTETS, xlat_func_sha_arg);
4305# endif
4306# ifdef HAVE_EVP_BLAKE2B512
4307 XLAT_REGISTER_PURE("blake2b_512", xlat_func_blake2b_512, FR_TYPE_OCTETS, xlat_func_sha_arg);
4308# endif
4309
4310 XLAT_REGISTER_PURE("sha3_224", xlat_func_sha3_224, FR_TYPE_OCTETS, xlat_func_sha_arg);
4311 XLAT_REGISTER_PURE("sha3_256", xlat_func_sha3_256, FR_TYPE_OCTETS, xlat_func_sha_arg);
4312 XLAT_REGISTER_PURE("sha3_384", xlat_func_sha3_384, FR_TYPE_OCTETS, xlat_func_sha_arg);
4313 XLAT_REGISTER_PURE("sha3_512", xlat_func_sha3_512, FR_TYPE_OCTETS, xlat_func_sha_arg);
4314#endif
4315
4326
4328}
4329
4331{
4332 int ret;
4333 fr_atexit_global_once_ret(&ret, _xlat_global_init, _xlat_global_free, NULL);
4334 return ret;
4335}
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:4581
char const * name
Vendor name.
Definition dict.h:252
static fr_slen_t err
Definition dict.h:827
fr_dict_t * fr_dict_global_ctx_iter_init(fr_dict_global_ctx_iter_t *iter)
Iterate protocols by name.
Definition dict_util.c:4574
fr_dict_attr_t const * fr_dict_root(fr_dict_t const *dict)
Return the root attribute of a dictionary.
Definition dict_util.c:2407
dl_t * fr_dict_dl(fr_dict_t const *dict)
Definition dict_util.c:2417
uint32_t pen
Private enterprise number.
Definition dict.h:248
fr_dict_t const * fr_dict_internal(void)
Definition dict_util.c:4617
static fr_slen_t in
Definition dict.h:827
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:2642
Private enterprise.
Definition dict.h:247
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_range(TALLOC_CTX *ctx, fr_dcursor_t *out, UNUSED xlat_ctx_t const *xctx, request_t *request, fr_value_box_list_t *args)
Generate a range of uint64 numbers.
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:1418
fr_event_list_t * unlang_interpret_event_list(request_t *request)
Get the event list for the current interpreter.
Definition interpret.c:1779
#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:1567
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:1861
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:1399
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:651
static fr_slen_t vpt
Definition tmpl.h:1276
static fr_dict_attr_t const * tmpl_list(tmpl_t const *vpt)
Definition tmpl.h:921
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:350
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:932
Optional arguments passed to vp_tmpl functions.
Definition tmpl.h:347
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:287
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:1251
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:838
fr_time_delta_t fr_time_gmtoff(void)
Get the offset to gmt.
Definition time.c:1243
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
xlat_arg_parser_variadic_t variadic
All additional boxes should be processed using this definition.
Definition xlat.h:151
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:3295
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
void fr_value_box_debug(fr_value_box_t const *vb)
Print the value of a box as info messages.
Definition value.c:6470
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:5408
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:4381
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:6095
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:4414
void fr_value_box_list_untaint(fr_value_box_list_t *head)
Untaint every list member (and their children)
Definition value.c:6283
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:3937
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:4046
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:4081
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:5640
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:6253
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:4114
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:4158
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:4263
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:4478
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:5833
@ 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 xlat_func_taint_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_range_arg[]
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:131
int xlat_eval_init(void)
Definition xlat_eval.c:1669
void xlat_eval_free(void)
Definition xlat_eval.c:1694
int xlat_register_expressions(void)
Definition xlat_expr.c:1857
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:115
xlat_type_t _CONST type
type of this expansion.
Definition xlat_priv.h:156
An xlat expansion node.
Definition xlat_priv.h:149