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: 7ddd7257787fc2006c60d85cf982b08027ab50b7 $
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: 7ddd7257787fc2006c60d85cf982b08027ab50b7 $")
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
39#include <freeradius-devel/unlang/xlat.h>
40#include <freeradius-devel/io/test_point.h>
41
42#include <freeradius-devel/util/base16.h>
43#include <freeradius-devel/util/table.h>
44
45#ifdef HAVE_OPENSSL_EVP_H
46# include <freeradius-devel/tls/openssl_user_macros.h>
47# include <openssl/evp.h>
48#endif
49
50#include <sys/stat.h>
51#include <fcntl.h>
52
53static char const hextab[] = "0123456789abcdef";
54static TALLOC_CTX *xlat_ctx;
55
56typedef struct {
58 fr_dict_t const *dict; //!< Restrict xlat to this namespace
60
61/*
62 * Regular xlat functions
63 */
65 { .single = true, .type = FR_TYPE_INT8 },
67};
68
69/** Dynamically change the debugging level for the current request
70 *
71 * Example:
72@verbatim
73%debug(3)
74@endverbatim
75 *
76 * @ingroup xlat_functions
77 */
79 UNUSED xlat_ctx_t const *xctx,
80 request_t *request, fr_value_box_list_t *args)
81{
82 int level = 0;
83 fr_value_box_t *vb, *lvl_vb;
84
85 XLAT_ARGS(args, &lvl_vb);
86
87 /*
88 * Expand to previous (or current) level
89 */
90 MEM(vb = fr_value_box_alloc(ctx, FR_TYPE_INT8, NULL));
91 vb->vb_int8 = request->log.lvl;
93
94 /*
95 * Assume we just want to get the current value and NOT set it to 0
96 */
97 if (!lvl_vb) goto done;
98
99 level = lvl_vb->vb_int8;
100 if (level == 0) {
101 request->log.lvl = RAD_REQUEST_LVL_NONE;
102 } else {
103 if (level > L_DBG_LVL_MAX) level = L_DBG_LVL_MAX;
104 request->log.lvl = level;
105 }
106
107done:
108 return XLAT_ACTION_DONE;
109}
110
111
116
118{
119 fr_dict_vendor_t const *vendor;
121 size_t i;
122
123 switch (vp->vp_type) {
125 if (vpt) {
126 RIDEBUG2("%s.%s = {",
127 tmpl_list_name(tmpl_list(vpt), "<INVALID>"),
128 vp->da->name);
129 } else {
130 RIDEBUG2("%s = {", vp->da->name);
131 }
132 RINDENT();
133 xlat_debug_attr_list(request, &vp->vp_group);
134 REXDENT();
135 RIDEBUG2("}");
136 break;
137
138 default:
139 if (vpt) {
140 RIDEBUG2("%s.%s = %pV",
141 tmpl_list_name(tmpl_list(vpt), "<INVALID>"),
142 vp->da->name,
143 &vp->data);
144 } else {
145 RIDEBUG2("%s = %pV", vp->da->name, &vp->data);
146 }
147 }
148
149 if (!RDEBUG_ENABLED3) return;
150
151 RINDENT();
152 RIDEBUG3("da : %p", vp->da);
153 RIDEBUG3("is_raw : %pV", fr_box_bool(vp->vp_raw));
154 RIDEBUG3("is_unknown : %pV", fr_box_bool(vp->da->flags.is_unknown));
155
156 if (RDEBUG_ENABLED3) {
157 RIDEBUG3("parent : %s (%p)", vp->da->parent->name, vp->da->parent);
158 } else {
159 RIDEBUG2("parent : %s", vp->da->parent->name);
160 }
161 RIDEBUG3("attr : %u", vp->da->attr);
162 vendor = fr_dict_vendor_by_da(vp->da);
163 if (vendor) RIDEBUG2("vendor : %u (%s)", vendor->pen, vendor->name);
164 RIDEBUG3("type : %s", fr_type_to_str(vp->vp_type));
165
166 switch (vp->vp_type) {
167 case FR_TYPE_LEAF:
168 if (fr_box_is_variable_size(&vp->data)) {
169 RIDEBUG3("length : %zu", vp->vp_length);
170 }
171 RIDEBUG3("tainted : %pV", fr_box_bool(vp->data.tainted));
172 break;
173 default:
174 break;
175 }
176
177 if (!RDEBUG_ENABLED4) {
178 REXDENT();
179 return;
180 }
181
182 for (i = 0; i < fr_type_table_len; i++) {
183 int pad;
184
185 fr_value_box_t *dst = NULL;
186
187 type = &fr_type_table[i];
188
189 if ((fr_type_t) type->value == vp->vp_type) goto next_type;
190
191 /*
192 * Don't cast TO structural, or FROM structural types.
193 */
194 if (!fr_type_is_leaf(type->value) || !fr_type_is_leaf(vp->vp_type)) goto next_type;
195
197 /* We expect some to fail */
198 if (fr_value_box_cast(dst, dst, type->value, NULL, &vp->data) < 0) {
199 goto next_type;
200 }
201
202 if ((pad = (11 - type->name.len)) < 0) pad = 0;
203
204 RINDENT();
205 RDEBUG4("as %s%*s: %pV", type->name.str, pad, " ", dst);
206 REXDENT();
207
208 next_type:
209 talloc_free(dst);
210 }
211
212 REXDENT();
213}
214
216{
217 fr_pair_t *vp;
218
219 for (vp = fr_pair_list_next(list, NULL);
220 vp != NULL;
221 vp = fr_pair_list_next(list, vp)) {
222 xlat_debug_attr_vp(request, vp, NULL);
223 }
224}
225
226/** Common function to move boxes from input list to output list
227 *
228 * This can be used to implement safe_for functions, as the xlat framework
229 * can be used for concatenation, casting, and marking up output boxes as
230 * safe_for.
231 */
233 UNUSED xlat_ctx_t const *xctx,
234 UNUSED request_t *request, fr_value_box_list_t *args)
235{
237 fr_value_box_list_remove(args, vb);
239 }}
240
242}
243
244/** Print out attribute info
245 *
246 * Prints out all instances of a current attribute, or all attributes in a list.
247 *
248 * At higher debugging levels, also prints out alternative decodings of the same
249 * value. This is helpful to determine types for unknown attributes of long
250 * passed vendors, or just crazy/broken NAS.
251 *
252 * This expands to a zero length string.
253 *
254 * Example:
255@verbatim
256%debug.attr(&request)
257@endverbatim
258 *
259 * @ingroup xlat_functions
260 */
262 UNUSED xlat_ctx_t const *xctx,
263 request_t *request, fr_value_box_list_t *args)
264{
265 fr_pair_t *vp;
266 fr_dcursor_t *cursor;
267 fr_value_box_t *in_head;
268
269 XLAT_ARGS(args, &in_head);
270
271 if (!RDEBUG_ENABLED2) return XLAT_ACTION_DONE; /* NOOP if debugging isn't enabled */
272
273 cursor = fr_value_box_get_cursor(in_head);
274
275 RDEBUG("Attributes matching \"%s\"", in_head->vb_cursor_name);
276
277 RINDENT();
278 for (vp = fr_dcursor_current(cursor);
279 vp;
280 vp = fr_dcursor_next(cursor)) {
281 xlat_debug_attr_vp(request, vp, NULL); /* @todo - pass in vpt, too, via the vb_cursor stuff */
282 }
283 REXDENT();
284
285 return XLAT_ACTION_DONE;
286}
287
288#ifdef __clang__
289#pragma clang diagnostic ignored "-Wgnu-designator"
290#endif
291
293 .name = "filename",
294 .chr = '_',
295 .do_utf8 = true,
296 .do_hex = true,
297
298 .esc = {
299 [ 0x00 ... 0x2d ] = true, // special characters, but not '.'
300 [ 0x2f ] = true, // /
301 [ 0x3A ... 0x3f ] = true, // :;<=>?, but not "@"
302 [ 0x5b ... 0x5e ] = true, // [\]^
303 [ 0x60 ] = true, // back-tick
304 [ 0x7b ... 0xff ] = true, // {|}, and all chars which have high bit set, but aren't UTF-8
305 },
306};
307
309 .name = "filename",
310 .chr = '_',
311 .do_utf8 = true,
312 .do_hex = true,
313
314 .esc = {
315 [ 0x00 ... 0x2f ] = true, // special characters, '.', '/', etc.
316 [ 0x3A ... 0x3f ] = true, // :;<=>?, but not "@"
317 [ 0x5b ... 0x5e ] = true, // [\]^
318 [ 0x60 ] = true, // back-tick
319 [ 0x7b ... 0xff ] = true, // {|}, and all chars which have high bit set, but aren't UTF-8
320 },
321};
322
323#define FR_FILENAME_SAFE_FOR ((uintptr_t) filename_xlat_escape)
324
325static int CC_HINT(nonnull(2,3)) filename_xlat_escape(UNUSED request_t *request, fr_value_box_t *vb, UNUSED void *uctx)
326{
327 fr_sbuff_t *out = NULL;
328 fr_value_box_entry_t entry;
329
331
332 /*
333 * Integers are just numbers, so they don't need to be escaped.
334 *
335 * Except that FR_TYPE_INTEGER includes 'date' and 'time_delta', which is annoying.
336 *
337 * 'octets' get printed as hex, so they don't need to be escaped.
338 */
339 switch (vb->type) {
340 case FR_TYPE_BOOL:
341 case FR_TYPE_UINT8:
342 case FR_TYPE_UINT16:
343 case FR_TYPE_UINT32:
344 case FR_TYPE_UINT64:
345 case FR_TYPE_INT8:
346 case FR_TYPE_INT16:
347 case FR_TYPE_INT32:
348 case FR_TYPE_INT64:
349 case FR_TYPE_SIZE:
350 case FR_TYPE_OCTETS:
351 return 0;
352
353 case FR_TYPE_NON_LEAF:
354 fr_assert(0);
355 return -1;
356
357 case FR_TYPE_DATE:
359 case FR_TYPE_IFID:
360 case FR_TYPE_ETHERNET:
361 case FR_TYPE_FLOAT32:
362 case FR_TYPE_FLOAT64:
369 case FR_TYPE_ATTR:
370 /*
371 * Printing prefixes etc. does NOT result in the escape function being called! So
372 * instead, we cast the results to a string, and then escape the string.
373 */
374 if (fr_value_box_cast_in_place(vb, vb, FR_TYPE_STRING, NULL) < 0) return -1;
375
377 break;
378
379 case FR_TYPE_STRING:
380 /*
381 * Note that we set ".always_escape" in the function arguments, so that we get called for
382 * IP addresses. Otherwise, the xlat evaluator and/or the list_concat_as_string
383 * functions won't call us. And the expansion will return IP addresses with '/' in them.
384 * Which is not what we want.
385 */
387
388 /*
389 * If the tainted string has a leading '.', then escape _all_ periods in it. This is so that we
390 * don't accidentally allow a "safe" value to end with '/', and then an "unsafe" value contains
391 * "..", and we now have a directory traversal attack.
392 *
393 * The escape rules will escape '/' in unsafe strings, so there's no possibility for an unsafe
394 * string to either end with a '/', or to contain "/.." itself.
395 *
396 * Allowing '.' in the middle of the string means we can have filenames based on realms, such as
397 * "log/aland@freeradius.org".
398 */
399 if (vb->vb_strvalue[0] == '.') {
401 } else {
403 }
404
405 break;
406 }
407
408 entry = vb->entry;
410 (void) fr_value_box_bstrndup(vb, vb, NULL, fr_sbuff_start(out), fr_sbuff_used(out), false);
411 vb->entry = entry;
412
413 return 0;
414}
415
417 { .required = true, .concat = true, .type = FR_TYPE_STRING,
418 .func = filename_xlat_escape, .safe_for = FR_FILENAME_SAFE_FOR, .always_escape = true },
420};
421
423 { .required = true, .concat = true, .type = FR_TYPE_STRING,
424 .func = filename_xlat_escape, .safe_for = FR_FILENAME_SAFE_FOR, .always_escape = true },
425 { .required = false, .type = FR_TYPE_UINT32 },
427};
428
429
431 UNUSED xlat_ctx_t const *xctx,
432 UNUSED request_t *request, fr_value_box_list_t *args)
433{
434 fr_value_box_t *dst, *vb;
435 char const *filename;
436 struct stat buf;
437
438 XLAT_ARGS(args, &vb);
439 fr_assert(vb->type == FR_TYPE_STRING);
440 filename = vb->vb_strvalue;
441
442 MEM(dst = fr_value_box_alloc(ctx, FR_TYPE_BOOL, NULL));
444
445 dst->vb_bool = (stat(filename, &buf) == 0);
446
447 return XLAT_ACTION_DONE;
448}
449
450
452 UNUSED xlat_ctx_t const *xctx,
453 request_t *request, fr_value_box_list_t *args)
454{
455 fr_value_box_t *dst, *vb;
456 char const *filename;
457 ssize_t len;
458 int fd;
459 char *p, buffer[256];
460
461 XLAT_ARGS(args, &vb);
462 fr_assert(vb->type == FR_TYPE_STRING);
463 filename = vb->vb_strvalue;
464
465 fd = open(filename, O_RDONLY);
466 if (fd < 0) {
467 REDEBUG3("Failed opening file %s - %s", filename, fr_syserror(errno));
468 return XLAT_ACTION_FAIL;
469 }
470
471 len = read(fd, buffer, sizeof(buffer));
472 if (len < 0) {
473 REDEBUG3("Failed reading file %s - %s", filename, fr_syserror(errno));
474 close(fd);
475 return XLAT_ACTION_FAIL;
476 }
477
478 /*
479 * Find the first CR/LF, but bail if we get any weird characters.
480 */
481 for (p = buffer; p < (buffer + len); p++) {
482 if ((*p == '\r') || (*p == '\n')) {
483 break;
484 }
485
486 if ((*p < ' ') && (*p != '\t')) {
487 invalid:
488 REDEBUG("Invalid text in file %s", filename);
489 close(fd);
490 return XLAT_ACTION_FAIL;
491 }
492 }
493
494 if ((p - buffer) > len) goto invalid;
495 close(fd);
496
497 MEM(dst = fr_value_box_alloc(ctx, FR_TYPE_STRING, NULL));
498 if (fr_value_box_bstrndup(dst, dst, NULL, buffer, p - buffer, false) < 0) {
499 talloc_free(dst);
500 return XLAT_ACTION_FAIL;
501 }
502
504
505 return XLAT_ACTION_DONE;
506}
507
508
510 UNUSED xlat_ctx_t const *xctx,
511 request_t *request, fr_value_box_list_t *args)
512{
513 fr_value_box_t *dst, *vb;
514 char const *filename;
515 struct stat buf;
516
517 XLAT_ARGS(args, &vb);
518 fr_assert(vb->type == FR_TYPE_STRING);
519 filename = vb->vb_strvalue;
520
521 if (stat(filename, &buf) < 0) {
522 REDEBUG3("Failed checking file %s - %s", filename, fr_syserror(errno));
523 return XLAT_ACTION_FAIL;
524 }
525
526 MEM(dst = fr_value_box_alloc(ctx, FR_TYPE_UINT64, NULL)); /* off_t is signed, but file sizes shouldn't be negative */
528
529 dst->vb_uint64 = buf.st_size;
530
531 return XLAT_ACTION_DONE;
532}
533
534
536 UNUSED xlat_ctx_t const *xctx,
537 request_t *request, fr_value_box_list_t *args)
538{
539 fr_value_box_t *dst, *vb, *num = NULL;
540 char const *filename;
541 ssize_t len;
542 off_t offset;
543 int fd;
544 int crlf, stop = 1;
545 char *p, *end, *found, buffer[256];
546
547 XLAT_ARGS(args, &vb, &num);
548 fr_assert(vb->type == FR_TYPE_STRING);
549 filename = vb->vb_strvalue;
550
551 fd = open(filename, O_RDONLY);
552 if (fd < 0) {
553 REDEBUG3("Failed opening file %s - %s", filename, fr_syserror(errno));
554 return XLAT_ACTION_FAIL;
555 }
556
557 offset = lseek(fd, 0, SEEK_END);
558 if (offset < 0) {
559 REDEBUG3("Failed seeking to end of file %s - %s", filename, fr_syserror(errno));
560 goto fail;
561 }
562
563 if (offset > (off_t) sizeof(buffer)) {
564 offset -= sizeof(buffer);
565 } else {
566 offset = 0;
567 }
568
569 if (lseek(fd, offset, SEEK_SET) < 0) {
570 REDEBUG3("Failed seeking backwards from end of file %s - %s", filename, fr_syserror(errno));
571 goto fail;
572 }
573
574 len = read(fd, buffer, sizeof(buffer));
575 if (len < 0) {
576 fail:
577 REDEBUG3("Failed reading file %s - %s", filename, fr_syserror(errno));
578 close(fd);
579 return XLAT_ACTION_FAIL;
580 }
581 close(fd);
582
583 found = buffer;
584 end = buffer + len;
585
586 /*
587 * No data, OR just one CR / LF, we print it all out.
588 */
589 if (len <= 1) goto done;
590
591 /*
592 * Clamp number of lines to a reasonable value. They
593 * still all have to fit into 256 characters, though.
594 *
595 * @todo - have a large thread-local temporary buffer for this stuff.
596 */
597 if (num) {
598 fr_assert(num->type == FR_TYPE_GROUP);
599 fr_assert(fr_value_box_list_num_elements(&num->vb_group) == 1);
600
601 num = fr_value_box_list_head(&num->vb_group);
602 fr_assert(num->type == FR_TYPE_UINT32);
603
604 if (!num->vb_uint32) {
605 stop = 1;
606
607 } else if (num->vb_uint32 <= 16) {
608 stop = num->vb_uint64;
609
610 } else {
611 stop = 16;
612 }
613 } else {
614 stop = 1;
615 }
616
617 p = end - 1;
618 crlf = 0;
619
620 /*
621 * Skip any trailing CRLF first.
622 */
623 while (p > buffer) {
624 /*
625 * Could be CRLF, or just LF.
626 */
627 if (*p == '\n') {
628 end = p;
629 p--;
630 if (p == buffer) {
631 goto done;
632 }
633 if (*p >= ' ') {
634 break;
635 }
636 }
637
638 if (*p == '\r') {
639 end = p;
640 p--;
641 break;
642 }
643
644 /*
645 * We've found CR, LF, or CRLF. The previous
646 * thing is either raw text, or is another CR/LF.
647 */
648 break;
649 }
650
651 found = p;
652
653 while (p > buffer) {
654 crlf++;
655
656 /*
657 * If the current line is empty, we can stop.
658 */
659 if ((crlf == stop) && (*found < ' ')) {
660 found++;
661 goto done;
662 }
663
664 while (*p >= ' ') {
665 found = p;
666 p--;
667 if (p == buffer) {
668 found = buffer;
669 goto done;
670 }
671 }
672 if (crlf == stop) {
673 break;
674 }
675
676 /*
677 * Check again for CRLF.
678 */
679 if (*p == '\n') {
680 p--;
681 if (p == buffer) {
682 break;
683 }
684 if (*p >= ' ') {
685 continue;
686 }
687 }
688
689 if (*p == '\r') {
690 p--;
691 if (p == buffer) {
692 break;
693 }
694 continue;
695 }
696 }
697
698done:
699
700 /*
701 * @todo - return a _list_ of value-boxes, one for each line in the file.
702 * Which means chopping off each CRLF in the file
703 */
704
705 MEM(dst = fr_value_box_alloc(ctx, FR_TYPE_STRING, NULL));
706 if (fr_value_box_bstrndup(dst, dst, NULL, found, (size_t) (end - found), false) < 0) {
707 talloc_free(dst);
708 return XLAT_ACTION_FAIL;
709 }
710
712
713 return XLAT_ACTION_DONE;
714}
715
717 { .required = true, .concat = true, .type = FR_TYPE_STRING,
718 .func = filename_xlat_escape, .safe_for = FR_FILENAME_SAFE_FOR, .always_escape = true },
719 { .required = true, .type = FR_TYPE_SIZE, .single = true },
721};
722
724 UNUSED xlat_ctx_t const *xctx,
725 request_t *request, fr_value_box_list_t *args)
726{
727 fr_value_box_t *dst, *vb, *max_size;
728 char const *filename;
729 ssize_t len;
730 int fd;
731 struct stat buf;
733
734 XLAT_ARGS(args, &vb, &max_size);
735 fr_assert(vb->type == FR_TYPE_STRING);
736 filename = vb->vb_strvalue;
737
738 fd = open(filename, O_RDONLY);
739 if (fd < 0) {
740 RPERROR("Failed opening file %s - %s", filename, fr_syserror(errno));
741 return XLAT_ACTION_FAIL;
742 }
743
744 if (fstat(fd, &buf) < 0) {
745 RPERROR("Failed checking file %s - %s", filename, fr_syserror(errno));
746 fail:
747 close(fd);
748 return XLAT_ACTION_FAIL;
749 }
750
751 if ((size_t)buf.st_size > max_size->vb_size) {
752 RPERROR("File larger than specified maximum (%"PRIu64" vs %zu)", buf.st_size, max_size->vb_size);
753 goto fail;
754 }
755
756 MEM(dst = fr_value_box_alloc(ctx, FR_TYPE_OCTETS, false));
757 fr_value_box_mem_alloc(dst, &buffer, dst, NULL, buf.st_size, true);
758
759 len = read(fd, buffer, buf.st_size);
760 if (len < 0) {
761 RPERROR("Failed reading file %s - %s", filename, fr_syserror(errno));
762 talloc_free(dst);
763 goto fail;
764 }
765 close(fd);
766
768
769 return XLAT_ACTION_DONE;
770}
771
773 UNUSED xlat_ctx_t const *xctx,
774 request_t *request, fr_value_box_list_t *args)
775{
776 fr_value_box_t *dst, *vb;
777 char const *filename;
778
779 XLAT_ARGS(args, &vb);
780 fr_assert(vb->type == FR_TYPE_STRING);
781 filename = vb->vb_strvalue;
782
783 MEM(dst = fr_value_box_alloc(ctx, FR_TYPE_BOOL, NULL));
785
786 dst->vb_bool = (unlink(filename) == 0);
787 if (!dst->vb_bool) {
788 REDEBUG3("Failed unlinking file %s - %s", filename, fr_syserror(errno));
789 }
790
791 return XLAT_ACTION_DONE;
792}
793
794
796 { .required = true, .type = FR_TYPE_VOID },
797 { .variadic = XLAT_ARG_VARIADIC_EMPTY_KEEP, .type = FR_TYPE_VOID },
799};
800
802 UNUSED xlat_ctx_t const *xctx,
803 UNUSED request_t *request, fr_value_box_list_t *in)
804{
805 fr_value_box_t *vb;
806
808 while ((vb = fr_value_box_list_pop_head(in)) != NULL) {
810 }
811
812 return XLAT_ACTION_DONE;
813}
814
816 UNUSED xlat_ctx_t const *xctx,
817 UNUSED request_t *request, fr_value_box_list_t *in)
818{
819 fr_value_box_t *vb;
820
821 while ((vb = fr_value_box_list_pop_head(in)) != NULL) {
822 fr_value_box_t *child;
823
824 fr_assert(vb->type == FR_TYPE_GROUP);
825
826 while ((child = fr_value_box_list_pop_head(&vb->vb_group)) != NULL) {
827 child->tainted = true;
829
830 fr_dcursor_append(out, child);
831 }
832 }
833
834 return XLAT_ACTION_DONE;
835}
836
838 { .required = true, .type = FR_TYPE_STRING },
839 { .required = true, .concat = true, .type = FR_TYPE_STRING },
841};
842
843/** Split a string into multiple new strings based on a delimiter
844 *
845@verbatim
846%explode(<string>, <delim>)
847@endverbatim
848 *
849 * Example:
850@verbatim
851update request {
852 &Tmp-String-1 := "a,b,c"
853}
854"%concat(%explode(%{Tmp-String-1}, ','), '|')" == "a|b|c"g
855@endverbatim
856 *
857 * @ingroup xlat_functions
858 */
860 UNUSED xlat_ctx_t const *xctx,
861 request_t *request, fr_value_box_list_t *args)
862{
864 fr_value_box_list_t *list;
865 fr_value_box_t *delim_vb;
866 ssize_t delim_len;
867 char const *delim;
868 fr_value_box_t *string, *vb;
869
870 XLAT_ARGS(args, &strings, &delim_vb);
871
872 list = &strings->vb_group;
873
874 /* coverity[dereference] */
875 if (delim_vb->vb_length == 0) {
876 REDEBUG("Delimiter must be greater than zero characters");
877 return XLAT_ACTION_FAIL;
878 }
879
880 delim = delim_vb->vb_strvalue;
881 delim_len = delim_vb->vb_length;
882
883 while ((string = fr_value_box_list_pop_head(list))) {
884 fr_sbuff_t sbuff = FR_SBUFF_IN(string->vb_strvalue, string->vb_length);
885 fr_sbuff_marker_t m_start;
886
887 /*
888 * If the delimiter is not in the string, just move to the output
889 */
890 if (!fr_sbuff_adv_to_str(&sbuff, SIZE_MAX, delim, delim_len)) {
891 fr_dcursor_append(out, string);
892 continue;
893 }
894
895 fr_sbuff_set_to_start(&sbuff);
896 fr_sbuff_marker(&m_start, &sbuff);
897
898 while (fr_sbuff_remaining(&sbuff)) {
899 if (fr_sbuff_adv_to_str(&sbuff, SIZE_MAX, delim, delim_len)) {
900 /*
901 * If there's nothing before the delimiter skip
902 */
903 if (fr_sbuff_behind(&m_start) == 0) goto advance;
904
905 MEM(vb = fr_value_box_alloc_null(ctx));
906 fr_value_box_bstrndup(vb, vb, NULL, fr_sbuff_current(&m_start),
907 fr_sbuff_behind(&m_start), false);
908 fr_value_box_safety_copy(vb, string);
910
911 advance:
912 fr_sbuff_advance(&sbuff, delim_len);
913 fr_sbuff_set(&m_start, &sbuff);
914 continue;
915 }
916
917 fr_sbuff_set_to_end(&sbuff);
918 MEM(vb = fr_value_box_alloc_null(ctx));
919 fr_value_box_bstrndup(vb, vb, NULL, fr_sbuff_current(&m_start),
920 fr_sbuff_behind(&m_start), false);
921
922 fr_value_box_safety_copy(vb, string);
924 break;
925 }
926 talloc_free(string);
927 }
928
929 return XLAT_ACTION_DONE;
930}
931
932/** Mark one or more attributes as immutable
933 *
934 * Example:
935@verbatim
936%pairs.immutable(request.State[*])
937@endverbatim
938 *
939 * @ingroup xlat_functions
940 */
942 UNUSED xlat_ctx_t const *xctx,
943 request_t *request, fr_value_box_list_t *args)
944{
945 fr_pair_t *vp;
946 fr_dcursor_t *cursor;
947 fr_value_box_t *in_head;
948
949 XLAT_ARGS(args, &in_head);
950
951 cursor = fr_value_box_get_cursor(in_head);
952
953 RDEBUG("Attributes matching \"%s\"", in_head->vb_cursor_name);
954
955 RINDENT();
956 for (vp = fr_dcursor_current(cursor);
957 vp;
958 vp = fr_dcursor_next(cursor)) {
960 }
961 REXDENT();
962
963 return XLAT_ACTION_DONE;
964}
965
967 { .required = true, .single = true, .type = FR_TYPE_VOID },
969};
970
971/** Print data as integer, not as VALUE.
972 *
973 * Example:
974@verbatim
975update request {
976 &Tmp-IP-Address-0 := "127.0.0.5"
977}
978%integer(%{Tmp-IP-Address-0}) == 2130706437
979@endverbatim
980 * @ingroup xlat_functions
981 */
983 UNUSED xlat_ctx_t const *xctx,
984 request_t *request, fr_value_box_list_t *args)
985{
986 fr_value_box_t *in_vb;
987 char const *p;
988
989 XLAT_ARGS(args, &in_vb);
990
991 fr_strerror_clear(); /* Make sure we don't print old errors */
992
993 fr_value_box_list_remove(args, in_vb);
994
995 switch (in_vb->type) {
996 default:
997 error:
998 RPEDEBUG("Failed converting %pV (%s) to an integer", in_vb,
999 fr_type_to_str(in_vb->type));
1000 talloc_free(in_vb);
1001 return XLAT_ACTION_FAIL;
1002
1003 case FR_TYPE_NUMERIC:
1004 /*
1005 * Ensure enumeration is NULL so that the integer
1006 * version of a box is returned
1007 */
1008 in_vb->enumv = NULL;
1009
1010 /*
1011 * FR_TYPE_DATE and FR_TYPE_TIME_DELTA need to be cast
1012 * to int64_t so that they're printed in a
1013 * numeric format.
1014 */
1015 if ((in_vb->type == FR_TYPE_DATE) || (in_vb->type == FR_TYPE_TIME_DELTA)) {
1016 if (fr_value_box_cast_in_place(ctx, in_vb, FR_TYPE_INT64, NULL) < 0) goto error;
1017 }
1018 break;
1019
1020 case FR_TYPE_STRING:
1021 /*
1022 * Strings are always zero terminated. They may
1023 * also have zeros in the middle, but if that
1024 * happens, the caller will only get the part up
1025 * to the first zero.
1026 *
1027 * We check for negative numbers, just to be
1028 * nice.
1029 */
1030 for (p = in_vb->vb_strvalue; *p != '\0'; p++) {
1031 if (*p == '-') break;
1032 }
1033
1034 if (*p == '-') {
1035 if (fr_value_box_cast_in_place(ctx, in_vb, FR_TYPE_INT64, NULL) < 0) goto error;
1036 } else {
1037 if (fr_value_box_cast_in_place(ctx, in_vb, FR_TYPE_UINT64, NULL) < 0) goto error;
1038 }
1039 break;
1040
1041 case FR_TYPE_OCTETS:
1042 if (in_vb->vb_length > sizeof(uint64_t)) {
1043 fr_strerror_printf("Expected octets length <= %zu, got %zu", sizeof(uint64_t), in_vb->vb_length);
1044 goto error;
1045 }
1046
1047 if (in_vb->vb_length > sizeof(uint32_t)) {
1048 if (unlikely(fr_value_box_cast_in_place(ctx, in_vb, FR_TYPE_UINT64, NULL) < 0)) goto error;
1049 } else if (in_vb->vb_length > sizeof(uint16_t)) {
1050 if (unlikely(fr_value_box_cast_in_place(ctx, in_vb, FR_TYPE_UINT32, NULL) < 0)) goto error;
1051 } else if (in_vb->vb_length > sizeof(uint8_t)) {
1052 if (unlikely(fr_value_box_cast_in_place(ctx, in_vb, FR_TYPE_UINT16, NULL) < 0)) goto error;
1053 } else {
1054 if (unlikely(fr_value_box_cast_in_place(ctx, in_vb, FR_TYPE_UINT8, NULL) < 0)) goto error;
1055 }
1056
1057 break;
1058
1059 case FR_TYPE_IPV4_ADDR:
1061 if (fr_value_box_cast_in_place(ctx, in_vb, FR_TYPE_UINT32, NULL) < 0) goto error;
1062 break;
1063
1064 case FR_TYPE_ETHERNET:
1065 if (fr_value_box_cast_in_place(ctx, in_vb, FR_TYPE_UINT64, NULL) < 0) goto error;
1066 break;
1067
1068 case FR_TYPE_IPV6_ADDR:
1070 {
1071 uint128_t ipv6int;
1072 char buff[40];
1073 fr_value_box_t *vb;
1074
1075 /*
1076 * Needed for correct alignment (as flagged by ubsan)
1077 */
1078 memcpy(&ipv6int, &in_vb->vb_ip.addr.v6.s6_addr, sizeof(ipv6int));
1079
1080 fr_snprint_uint128(buff, sizeof(buff), ntohlll(ipv6int));
1081
1082 MEM(vb = fr_value_box_alloc_null(ctx));
1083 fr_value_box_bstrndup(vb, vb, NULL, buff, strlen(buff), false);
1085 talloc_free(in_vb);
1086 return XLAT_ACTION_DONE;
1087 }
1088 }
1089
1090 fr_dcursor_append(out, in_vb);
1091
1092 return XLAT_ACTION_DONE;
1093}
1094
1096 { .concat = true, .type = FR_TYPE_STRING },
1098};
1099
1100/** Log something at INFO level.
1101 *
1102 * Example:
1103@verbatim
1104%log("This is an informational message")
1105@endverbatim
1106 *
1107 * @ingroup xlat_functions
1108 */
1110 UNUSED xlat_ctx_t const *xctx,
1111 request_t *request, fr_value_box_list_t *args)
1112{
1113 fr_value_box_t *vb;
1114
1115 XLAT_ARGS(args, &vb);
1116
1117 if (!vb) return XLAT_ACTION_DONE;
1118
1119 RINFO("%s", vb->vb_strvalue);
1120
1121 return XLAT_ACTION_DONE;
1122}
1123
1124
1125/** Log something at DEBUG level.
1126 *
1127 * Example:
1128@verbatim
1129%log.debug("This is a message")
1130@endverbatim
1131 *
1132 * @ingroup xlat_functions
1133 */
1135 UNUSED xlat_ctx_t const *xctx,
1136 request_t *request, fr_value_box_list_t *args)
1137{
1138 fr_value_box_t *vb;
1139
1140 XLAT_ARGS(args, &vb);
1141
1142 if (!vb) return XLAT_ACTION_DONE;
1143
1144 RDEBUG("%s", vb->vb_strvalue);
1145
1146 return XLAT_ACTION_DONE;
1147}
1148
1149
1150/** Log something at DEBUG level.
1151 *
1152 * Example:
1153@verbatim
1154%log.err("Big error here")
1155@endverbatim
1156 *
1157 * @ingroup xlat_functions
1158 */
1160 UNUSED xlat_ctx_t const *xctx,
1161 request_t *request, fr_value_box_list_t *args)
1162{
1163 fr_value_box_t *vb;
1164
1165 XLAT_ARGS(args, &vb);
1166
1167 if (!vb) return XLAT_ACTION_DONE;
1168
1169 REDEBUG("%s", vb->vb_strvalue);
1170
1171 return XLAT_ACTION_DONE;
1172}
1173
1174
1175/** Log something at WARN level.
1176 *
1177 * Example:
1178@verbatim
1179%log.warn("Maybe something bad happened")
1180@endverbatim
1181 *
1182 * @ingroup xlat_functions
1183 */
1185 UNUSED xlat_ctx_t const *xctx,
1186 request_t *request, fr_value_box_list_t *args)
1187{
1188 fr_value_box_t *vb;
1189
1190 XLAT_ARGS(args, &vb);
1191
1192 if (!vb) return XLAT_ACTION_DONE;
1193
1194 RWDEBUG("%s", vb->vb_strvalue);
1195
1196 return XLAT_ACTION_DONE;
1197}
1198
1199static int _log_dst_free(fr_log_t *log)
1200{
1201 close(log->fd);
1202 return 0;
1203}
1204
1206 { .required = false, .type = FR_TYPE_STRING, .concat = true },
1207 { .required = false, .type = FR_TYPE_UINT32, .single = true },
1208 { .required = false, .type = FR_TYPE_STRING, .concat = true },
1210};
1211
1212/** Change the log destination to the named one
1213 *
1214 * Example:
1215@verbatim
1216%log.destination('foo')
1217@endverbatim
1218 *
1219 * @ingroup xlat_functions
1220 */
1222 UNUSED xlat_ctx_t const *xctx,
1223 request_t *request, fr_value_box_list_t *args)
1224{
1225 fr_value_box_t *dst, *lvl, *file;
1226 fr_log_t *log, *dbg;
1227 uint32_t level = 2;
1228
1229 XLAT_ARGS(args, &dst, &lvl, &file);
1230
1231 if (!dst || !*dst->vb_strvalue) {
1232 request_log_prepend(request, NULL, L_DBG_LVL_DISABLE);
1233 return XLAT_ACTION_DONE;
1234 }
1235
1236 log = log_dst_by_name(dst->vb_strvalue);
1237 if (!log) return XLAT_ACTION_FAIL;
1238
1239 if (lvl) level = lvl->vb_uint32;
1240
1241 if (!file || ((log->dst != L_DST_NULL) && (log->dst != L_DST_FILES))) {
1242 request_log_prepend(request, log, level);
1243 return XLAT_ACTION_DONE;
1244 }
1245
1246 /*
1247 * Clone it.
1248 */
1249 MEM(dbg = talloc_memdup(request, log, sizeof(*log)));
1250 dbg->parent = log;
1251
1252 /*
1253 * Open the new filename.
1254 */
1255 dbg->dst = L_DST_FILES;
1256 dbg->file = talloc_strdup(dbg, file->vb_strvalue);
1257 dbg->fd = open(dbg->file, O_WRONLY | O_CREAT | O_CLOEXEC, 0600);
1258 if (dbg->fd < 0) {
1259 REDEBUG("Failed opening %s - %s", dbg->file, fr_syserror(errno));
1260 talloc_free(dbg);
1261 return XLAT_ACTION_DONE;
1262 }
1263
1264 /*
1265 * Ensure that we close the file handle when done.
1266 */
1267 talloc_set_destructor(dbg, _log_dst_free);
1268
1269 request_log_prepend(request, dbg, level);
1270 return XLAT_ACTION_DONE;
1271}
1272
1273
1275 { .required = true, .concat = true, .type = FR_TYPE_STRING },
1277};
1278
1279/** Processes fmt as a map string and applies it to the current request
1280 *
1281 * e.g.
1282@verbatim
1283%map("User-Name := 'foo'")
1284@endverbatim
1285 *
1286 * Allows sets of modifications to be cached and then applied.
1287 * Useful for processing generic attributes from LDAP.
1288 *
1289 * @ingroup xlat_functions
1290 */
1292 UNUSED xlat_ctx_t const *xctx,
1293 request_t *request, fr_value_box_list_t *args)
1294{
1295 map_t *map = NULL;
1296 int ret;
1297 fr_value_box_t *fmt_vb;
1298 fr_value_box_t *vb;
1299
1300 tmpl_rules_t attr_rules = {
1301 .attr = {
1302 .dict_def = request->local_dict,
1303 .list_def = request_attr_request,
1304 },
1305 .xlat = {
1306 .runtime_el = unlang_interpret_event_list(request)
1307 }
1308 };
1309
1310 XLAT_ARGS(args, &fmt_vb);
1311
1312 if (map_afrom_attr_str(request, &map, fmt_vb->vb_strvalue, &attr_rules, &attr_rules) < 0) {
1313 RPEDEBUG("Failed parsing \"%s\" as map", fmt_vb->vb_strvalue);
1314 return XLAT_ACTION_FAIL;
1315 }
1316
1317 MEM(vb = fr_value_box_alloc(ctx, FR_TYPE_BOOL, NULL));
1318 vb->vb_bool = false; /* Default fail value - changed to true on success */
1320
1321 switch (map->lhs->type) {
1322 case TMPL_TYPE_ATTR:
1323 case TMPL_TYPE_XLAT:
1324 break;
1325
1326 default:
1327 REDEBUG("Unexpected type %s in left hand side of expression",
1328 tmpl_type_to_str(map->lhs->type));
1329 return XLAT_ACTION_FAIL;
1330 }
1331
1332 switch (map->rhs->type) {
1333 case TMPL_TYPE_ATTR:
1334 case TMPL_TYPE_EXEC:
1335 case TMPL_TYPE_DATA:
1338 case TMPL_TYPE_XLAT:
1339 break;
1340
1341 default:
1342 REDEBUG("Unexpected type %s in right hand side of expression",
1343 tmpl_type_to_str(map->rhs->type));
1344 return XLAT_ACTION_FAIL;
1345 }
1346
1347 RINDENT();
1348 ret = map_to_request(request, map, map_to_vp, NULL);
1349 REXDENT();
1350 talloc_free(map);
1351 if (ret < 0) return XLAT_ACTION_FAIL;
1352
1353 vb->vb_bool = true;
1354 return XLAT_ACTION_DONE;
1355}
1356
1357
1359 { .required = true, .concat = true, .type = FR_TYPE_STRING },
1361};
1362
1363/** Calculate number of seconds until the next n hour(s), day(s), week(s), year(s).
1364 *
1365 * For example, if it were 16:18 %time.next(1h) would expand to 2520.
1366 *
1367 * The envisaged usage for this function is to limit sessions so that they don't
1368 * cross billing periods. The output of the xlat should be combined with %rand() to create
1369 * some jitter, unless the desired effect is every subscriber on the network
1370 * re-authenticating at the same time.
1371 *
1372 * @ingroup xlat_functions
1373 */
1375 UNUSED xlat_ctx_t const *xctx,
1376 request_t *request, fr_value_box_list_t *args)
1377{
1378 long num;
1379
1380 char const *p;
1381 char *q;
1382 time_t now;
1383 struct tm *local, local_buff;
1384 fr_value_box_t *in_head;
1385 fr_value_box_t *vb;
1386
1387 XLAT_ARGS(args, &in_head);
1388
1389 /*
1390 * We want to limit based on _now_, not on when they logged in.
1391 */
1392 now = time(NULL);
1393 local = localtime_r(&now, &local_buff);
1394
1395 p = in_head->vb_strvalue;
1396
1397 num = strtoul(p, &q, 10);
1398 if (!q || *q == '\0') {
1399 REDEBUG("<int> must be followed by time period (h|d|w|m|y)");
1400 return XLAT_ACTION_FAIL;
1401 }
1402
1403 if (p == q) {
1404 num = 1;
1405 } else {
1406 p += q - p;
1407 }
1408
1409 local->tm_sec = 0;
1410 local->tm_min = 0;
1411
1412 switch (*p) {
1413 case 'h':
1414 local->tm_hour += num;
1415 break;
1416
1417 case 'd':
1418 local->tm_hour = 0;
1419 local->tm_mday += num;
1420 break;
1421
1422 case 'w':
1423 local->tm_hour = 0;
1424 local->tm_mday += (7 - local->tm_wday) + (7 * (num-1));
1425 break;
1426
1427 case 'm':
1428 local->tm_hour = 0;
1429 local->tm_mday = 1;
1430 local->tm_mon += num;
1431 break;
1432
1433 case 'y':
1434 local->tm_hour = 0;
1435 local->tm_mday = 1;
1436 local->tm_mon = 0;
1437 local->tm_year += num;
1438 break;
1439
1440 default:
1441 REDEBUG("Invalid time period '%c', must be h|d|w|m|y", *p);
1442 return XLAT_ACTION_FAIL;
1443 }
1444
1445 MEM(vb = fr_value_box_alloc_null(ctx));
1446 fr_value_box_uint64(vb, NULL, (uint64_t)(mktime(local) - now), false);
1448 return XLAT_ACTION_DONE;
1449}
1450
1455
1456/** Just serves to push the result up the stack
1457 *
1458 */
1460 xlat_ctx_t const *xctx,
1461 UNUSED request_t *request, UNUSED fr_value_box_list_t *in)
1462{
1463 xlat_eval_rctx_t *rctx = talloc_get_type_abort(xctx->rctx, xlat_eval_rctx_t);
1465
1466 talloc_free(rctx);
1467
1468 return xa;
1469}
1470
1472 { .required = true, .concat = true, .type = FR_TYPE_STRING },
1474};
1475
1476/** Dynamically evaluate an expansion string
1477 *
1478 * @ingroup xlat_functions
1479 */
1481 UNUSED xlat_ctx_t const *xctx,
1482 request_t *request, fr_value_box_list_t *args)
1483{
1484 /*
1485 * These are escaping rules applied to the
1486 * input string. They're mostly here to
1487 * allow \% and \\ to work.
1488 *
1489 * Everything else should be passed in as
1490 * unescaped data.
1491 */
1492 static fr_sbuff_unescape_rules_t const escape_rules = {
1493 .name = "xlat",
1494 .chr = '\\',
1495 .subs = {
1496 ['%'] = '%',
1497 ['\\'] = '\\',
1498 },
1499 .do_hex = false,
1500 .do_oct = false
1501 };
1502
1503 xlat_eval_rctx_t *rctx;
1504 fr_value_box_t *arg = fr_value_box_list_head(args);
1505
1506 XLAT_ARGS(args, &arg);
1507
1508 MEM(rctx = talloc_zero(unlang_interpret_frame_talloc_ctx(request), xlat_eval_rctx_t));
1509
1510 /*
1511 * Parse the input as a literal expansion
1512 */
1513 if (xlat_tokenize_expression(rctx,
1514 &rctx->ex,
1515 &FR_SBUFF_IN(arg->vb_strvalue, arg->vb_length),
1516 &(fr_sbuff_parse_rules_t){
1517 .escapes = &escape_rules
1518 },
1519 &(tmpl_rules_t){
1520 .attr = {
1521 .dict_def = request->local_dict,
1522 .list_def = request_attr_request,
1523 .allow_unknown = false,
1524 .allow_unresolved = false,
1525 .allow_foreign = false,
1526 },
1527 .xlat = {
1528 .runtime_el = unlang_interpret_event_list(request),
1529 },
1530 .at_runtime = true
1531 }) < 0) {
1532 RPEDEBUG("Failed parsing expansion");
1533 error:
1534 talloc_free(rctx);
1535 return XLAT_ACTION_FAIL;
1536 }
1537
1538 /*
1539 * Call the resolution function so we produce
1540 * good errors about what function was
1541 * unresolved.
1542 */
1543 if (rctx->ex->flags.needs_resolving &&
1544 (xlat_resolve(rctx->ex, &(xlat_res_rules_t){ .allow_unresolved = false }) < 0)) {
1545 RPEDEBUG("Unresolved expansion functions in expansion");
1546 goto error;
1547
1548 }
1549
1550 if (unlang_xlat_yield(request, xlat_eval_resume, NULL, 0, rctx) != XLAT_ACTION_YIELD) goto error;
1551
1552 if (unlang_xlat_push(ctx, &rctx->last_result, (fr_value_box_list_t *)out->dlist,
1553 request, rctx->ex, UNLANG_SUB_FRAME) < 0) goto error;
1554
1556}
1557
1559 { .required = true, .type = FR_TYPE_STRING },
1560 { .required = true, .single = true, .type = FR_TYPE_UINT64 },
1561 { .concat = true, .type = FR_TYPE_STRING },
1563};
1564
1565/** lpad a string
1566 *
1567@verbatim
1568%lpad(%{Attribute-Name}, <length> [, <fill>])
1569@endverbatim
1570 *
1571 * Example: (User-Name = "foo")
1572@verbatim
1573%lpad(%{User-Name}, 5 'x') == "xxfoo"
1574@endverbatim
1575 *
1576 * @ingroup xlat_functions
1577 */
1579 UNUSED xlat_ctx_t const *xctx,
1580 request_t *request, fr_value_box_list_t *args)
1581{
1582 fr_value_box_t *values;
1583 fr_value_box_t *pad;
1585
1586 fr_value_box_list_t *list;
1587
1588 size_t pad_len;
1589
1590 char const *fill_str = NULL;
1591 size_t fill_len = 0;
1592
1593 fr_value_box_t *in = NULL;
1594
1595 XLAT_ARGS(args, &values, &pad, &fill);
1596
1597 /* coverity[dereference] */
1598 list = &values->vb_group;
1599 /* coverity[dereference] */
1600 pad_len = (size_t)pad->vb_uint64;
1601
1602 /*
1603 * Fill is optional
1604 */
1605 if (fill) {
1606 fill_str = fill->vb_strvalue;
1607 fill_len = talloc_array_length(fill_str) - 1;
1608 }
1609
1610 if (fill_len == 0) {
1611 fill_str = " ";
1612 fill_len = 1;
1613 }
1614
1615 while ((in = fr_value_box_list_pop_head(list))) {
1616 size_t len = talloc_array_length(in->vb_strvalue) - 1;
1617 size_t remaining;
1618 char *buff;
1619 fr_sbuff_t sbuff;
1620 fr_sbuff_marker_t m_data;
1621
1623
1624 if (len >= pad_len) continue;
1625
1626 if (fr_value_box_bstr_realloc(in, &buff, in, pad_len) < 0) {
1627 RPEDEBUG("Failed reallocing input data");
1628 return XLAT_ACTION_FAIL;
1629 }
1630
1631 fr_sbuff_init_in(&sbuff, buff, pad_len);
1632 fr_sbuff_marker(&m_data, &sbuff);
1633
1634 /*
1635 * ...nothing to move if the input
1636 * string is empty.
1637 */
1638 if (len > 0) {
1639 fr_sbuff_advance(&m_data, pad_len - len); /* Mark where we want the data to go */
1640 fr_sbuff_move(&FR_SBUFF(&m_data), &FR_SBUFF(&sbuff), len); /* Shift the data */
1641 }
1642
1643 if (fill_len == 1) {
1644 memset(fr_sbuff_current(&sbuff), *fill_str, fr_sbuff_ahead(&m_data));
1645 continue;
1646 }
1647
1648 /*
1649 * Copy fill as a repeating pattern
1650 */
1651 while ((remaining = fr_sbuff_ahead(&m_data))) {
1652 size_t to_copy = remaining >= fill_len ? fill_len : remaining;
1653 memcpy(fr_sbuff_current(&sbuff), fill_str, to_copy); /* avoid \0 termination */
1654 fr_sbuff_advance(&sbuff, to_copy);
1655 }
1656 fr_sbuff_set_to_end(&sbuff);
1657 fr_sbuff_terminate(&sbuff); /* Move doesn't re-terminate */
1658 }
1659
1660 return XLAT_ACTION_DONE;
1661}
1662
1663/** Right pad a string
1664 *
1665@verbatim
1666%rpad(%{Attribute-Name}, <length> [, <fill>])
1667@endverbatim
1668 *
1669 * Example: (User-Name = "foo")
1670@verbatim
1671%rpad(%{User-Name}, 5 'x') == "fooxx"
1672@endverbatim
1673 *
1674 * @ingroup xlat_functions
1675 */
1677 UNUSED xlat_ctx_t const *xctx,
1678 request_t *request, fr_value_box_list_t *args)
1679{
1680 fr_value_box_t *values;
1681 fr_value_box_list_t *list;
1682 fr_value_box_t *pad;
1683 /* coverity[dereference] */
1684 size_t pad_len;
1686 char const *fill_str = NULL;
1687 size_t fill_len = 0;
1688
1689 fr_value_box_t *in = NULL;
1690
1691 XLAT_ARGS(args, &values, &pad, &fill);
1692
1693 list = &values->vb_group;
1694 pad_len = (size_t)pad->vb_uint64;
1695
1696 /*
1697 * Fill is optional
1698 */
1699 if (fill) {
1700 fill_str = fill->vb_strvalue;
1701 fill_len = talloc_array_length(fill_str) - 1;
1702 }
1703
1704 if (fill_len == 0) {
1705 fill_str = " ";
1706 fill_len = 1;
1707 }
1708
1709 while ((in = fr_value_box_list_pop_head(list))) {
1710 size_t len = talloc_array_length(in->vb_strvalue) - 1;
1711 size_t remaining;
1712 char *buff;
1713 fr_sbuff_t sbuff;
1714
1716
1717 if (len >= pad_len) continue;
1718
1719 if (fr_value_box_bstr_realloc(in, &buff, in, pad_len) < 0) {
1720 fail:
1721 RPEDEBUG("Failed reallocing input data");
1722 return XLAT_ACTION_FAIL;
1723 }
1724
1725 fr_sbuff_init_in(&sbuff, buff, pad_len);
1726 fr_sbuff_advance(&sbuff, len);
1727
1728 if (fill_len == 1) {
1729 memset(fr_sbuff_current(&sbuff), *fill_str, fr_sbuff_remaining(&sbuff));
1730 continue;
1731 }
1732
1733 /*
1734 * Copy fill as a repeating pattern
1735 */
1736 while ((remaining = fr_sbuff_remaining(&sbuff))) {
1737 if (fr_sbuff_in_bstrncpy(&sbuff, fill_str, remaining >= fill_len ? fill_len : remaining) < 0) {
1738 goto fail;
1739 }
1740 }
1741 }
1742
1743 return XLAT_ACTION_DONE;
1744}
1745
1747 { .required = true, .concat = true, .type = FR_TYPE_OCTETS },
1749};
1750
1751/** Encode string or attribute as base64
1752 *
1753 * Example:
1754@verbatim
1755%base64.encode("foo") == "Zm9v"
1756@endverbatim
1757 *
1758 * @ingroup xlat_functions
1759 */
1761 UNUSED xlat_ctx_t const *xctx,
1762 request_t *request, fr_value_box_list_t *args)
1763{
1764 size_t alen;
1765 ssize_t elen;
1766 char *buff;
1767 fr_value_box_t *vb;
1769
1770 XLAT_ARGS(args, &in);
1771
1772 alen = FR_BASE64_ENC_LENGTH(in->vb_length);
1773
1774 MEM(vb = fr_value_box_alloc_null(ctx));
1775 if (fr_value_box_bstr_alloc(vb, &buff, vb, NULL, alen, false) < 0) {
1776 talloc_free(vb);
1777 return XLAT_ACTION_FAIL;
1778 }
1779
1780 elen = fr_base64_encode(&FR_SBUFF_OUT(buff, talloc_array_length(buff)),
1781 &FR_DBUFF_TMP(in->vb_octets, in->vb_length), true);
1782 if (elen < 0) {
1783 RPEDEBUG("Base64 encoding failed");
1784 talloc_free(vb);
1785 return XLAT_ACTION_FAIL;
1786 }
1787 fr_assert((size_t)elen <= alen);
1790
1791 return XLAT_ACTION_DONE;
1792}
1793
1795 { .required = true, .concat = true, .type = FR_TYPE_OCTETS },
1797};
1798
1799/** Decode base64 string
1800 *
1801 * Example:
1802@verbatim
1803%base64.decode("Zm9v") == "foo"
1804@endverbatim
1805 *
1806 * @ingroup xlat_functions
1807 */
1809 UNUSED xlat_ctx_t const *xctx,
1810 request_t *request, fr_value_box_list_t *args)
1811{
1812 size_t alen;
1813 ssize_t declen = 0;
1814 uint8_t *decbuf;
1815 fr_value_box_t *vb;
1817
1818 XLAT_ARGS(args, &in);
1819
1820 /*
1821 * Pass empty arguments through
1822 *
1823 * FR_BASE64_DEC_LENGTH produces 2 for empty strings...
1824 */
1825 if (in->vb_length == 0) {
1826 fr_value_box_list_remove(args, in);
1828 return XLAT_ACTION_DONE;
1829 }
1830
1831 alen = FR_BASE64_DEC_LENGTH(in->vb_length);
1832 MEM(vb = fr_value_box_alloc_null(ctx));
1833 if (alen > 0) {
1834 MEM(fr_value_box_mem_alloc(vb, &decbuf, vb, NULL, alen, false) == 0);
1835 declen = fr_base64_decode(&FR_DBUFF_TMP(decbuf, alen),
1836 &FR_SBUFF_IN(in->vb_strvalue, in->vb_length), true, true);
1837 if (declen < 0) {
1838 RPEDEBUG("Base64 string invalid");
1839 talloc_free(vb);
1840 return XLAT_ACTION_FAIL;
1841 }
1842
1843 MEM(fr_value_box_mem_realloc(vb, NULL, vb, declen) == 0);
1844 }
1845
1848
1849 return XLAT_ACTION_DONE;
1850}
1851
1853 { .required = true, .type = FR_TYPE_STRING },
1855};
1856
1857/** Convert hex string to binary
1858 *
1859 * Example:
1860@verbatim
1861%bin("666f6f626172") == "foobar"
1862@endverbatim
1863 *
1864 * @see #xlat_func_hex
1865 *
1866 * @ingroup xlat_functions
1867 */
1869 UNUSED xlat_ctx_t const *xctx,
1870 request_t *request, fr_value_box_list_t *args)
1871{
1872 fr_value_box_t *result;
1873 char const *p, *end;
1874 uint8_t *bin;
1875 size_t len, outlen;
1877 fr_value_box_t *list, *hex;
1878
1879 XLAT_ARGS(args, &list);
1880
1881 while ((hex = fr_value_box_list_pop_head(&list->vb_group))) {
1882 len = hex->vb_length;
1883 if ((len > 1) && (len & 0x01)) {
1884 REDEBUG("Input data length must be >1 and even, got %zu", len);
1885 return XLAT_ACTION_FAIL;
1886 }
1887
1888 p = hex->vb_strvalue;
1889 end = p + len;
1890
1891 /*
1892 * Look for 0x at the start of the string, and ignore if we see it.
1893 */
1894 if ((p[0] == '0') && (p[1] == 'x')) {
1895 p += 2;
1896 len -=2;
1897 }
1898
1899 /*
1900 * Zero length octets string
1901 */
1902 if (p == end) continue;
1903
1904 outlen = len / 2;
1905
1906 MEM(result = fr_value_box_alloc_null(ctx));
1907 MEM(fr_value_box_mem_alloc(result, &bin, result, NULL, outlen, false) == 0);
1908 fr_base16_decode(&err, &FR_DBUFF_TMP(bin, outlen), &FR_SBUFF_IN(p, end - p), true);
1909 if (err) {
1910 REDEBUG2("Invalid hex string");
1911 talloc_free(result);
1912 return XLAT_ACTION_FAIL;
1913 }
1914
1916 fr_dcursor_append(out, result);
1917 }
1918
1919 return XLAT_ACTION_DONE;
1920}
1921
1923 { .required = true, .single = true, .type = FR_TYPE_VOID },
1924 { .type = FR_TYPE_VOID },
1925 { .variadic = XLAT_ARG_VARIADIC_EMPTY_KEEP, .type = FR_TYPE_VOID },
1927};
1928
1929/** Cast one or more output value-boxes to the given type
1930 *
1931 * First argument of is type to cast to.
1932 *
1933 * Example:
1934@verbatim
1935%cast('string', %{request[*]}) results in all of the input boxes being cast to string/
1936@endverbatim
1937 *
1938 * @ingroup xlat_functions
1939 */
1941 UNUSED xlat_ctx_t const *xctx,
1942 request_t *request, fr_value_box_list_t *args)
1943{
1945 fr_value_box_t *arg;
1947 fr_dict_attr_t const *time_res = NULL;
1948
1949 XLAT_ARGS(args, &name);
1950
1951 /*
1952 * Get the type, which can be in one of a few formats.
1953 */
1954 if (fr_type_is_numeric(name->type)) {
1956 RPEDEBUG("Failed parsing '%pV' as a numerical data type", name);
1957 return XLAT_ACTION_FAIL;
1958 }
1959 type = name->vb_uint8;
1960
1961 } else {
1962 if (name->type != FR_TYPE_STRING) {
1964 RPEDEBUG("Failed parsing '%pV' as a string data type", name);
1965 return XLAT_ACTION_FAIL;
1966 }
1967 }
1968
1970 if (type == FR_TYPE_NULL) {
1971 if ((time_res = xlat_time_res_attr(name->vb_strvalue)) == NULL) {
1972 RDEBUG("Unknown data type '%s'", name->vb_strvalue);
1973 return XLAT_ACTION_FAIL;
1974 }
1975
1977 }
1978 }
1979
1980 (void) fr_value_box_list_pop_head(args);
1981
1982 /*
1983 * When we cast nothing to a string / octets, the result is an empty string/octets.
1984 */
1985 if (unlikely(!fr_value_box_list_head(args))) {
1986 if ((type == FR_TYPE_STRING) || (type == FR_TYPE_OCTETS)) {
1987 fr_value_box_t *dst;
1988
1989 MEM(dst = fr_value_box_alloc(ctx, type, NULL));
1990 fr_dcursor_append(out, dst);
1991 VALUE_BOX_LIST_VERIFY((fr_value_box_list_t *)out->dlist);
1992
1993 return XLAT_ACTION_DONE;
1994 }
1995
1996 RDEBUG("No data for cast to '%s'", fr_type_to_str(type));
1997 return XLAT_ACTION_FAIL;
1998 }
1999
2000 /*
2001 * Cast to string means *print* to string.
2002 */
2003 if (type == FR_TYPE_STRING) {
2004 fr_sbuff_t *agg;
2005 fr_value_box_t *dst;
2006
2008
2009 FR_SBUFF_TALLOC_THREAD_LOCAL(&agg, 256, SIZE_MAX);
2010
2011 MEM(dst = fr_value_box_alloc_null(ctx));
2013
2014 if (fr_value_box_list_concat_as_string(dst, agg, args, NULL, 0, NULL,
2016 RPEDEBUG("Failed concatenating string");
2017 return XLAT_ACTION_FAIL;
2018 }
2019
2020 fr_value_box_bstrndup(dst, dst, NULL, fr_sbuff_start(agg), fr_sbuff_used(agg), false);
2021 fr_dcursor_append(out, dst);
2022 VALUE_BOX_LIST_VERIFY((fr_value_box_list_t *)out->dlist);
2023
2024 return XLAT_ACTION_DONE;
2025 }
2026
2027 /*
2028 * Copy inputs to outputs, casting them along the way.
2029 */
2030 arg = NULL;
2031 while ((arg = fr_value_box_list_next(args, arg)) != NULL) {
2032 fr_value_box_t *vb, *p;
2033
2034 fr_assert(arg->type == FR_TYPE_GROUP);
2035
2036 vb = fr_value_box_list_head(&arg->vb_group);
2037 while (vb) {
2038 p = fr_value_box_list_remove(&arg->vb_group, vb);
2039
2040 if (fr_value_box_cast_in_place(vb, vb, type, time_res) < 0) {
2041 RPEDEBUG("Failed casting %pV to data type '%s'", vb, fr_type_to_str(type));
2042 return XLAT_ACTION_FAIL;
2043 }
2045 vb = fr_value_box_list_next(&arg->vb_group, p);
2046 }
2047 }
2048 VALUE_BOX_LIST_VERIFY((fr_value_box_list_t *)out->dlist);
2049
2050 return XLAT_ACTION_DONE;
2051}
2052
2054 { .required = true, .type = FR_TYPE_VOID },
2055 { .concat = true, .type = FR_TYPE_STRING },
2057};
2058
2059/** Concatenate string representation of values of given attributes using separator
2060 *
2061 * First argument of is the list of attributes to concatenate, followed
2062 * by an optional separator
2063 *
2064 * Example:
2065@verbatim
2066%concat(%{request.[*]}, ',') == "<attr1value>,<attr2value>,<attr3value>,..."
2067%concat(%{Tmp-String-0[*]}, '. ') == "<str1value>. <str2value>. <str3value>. ..."
2068%concat(%join(%{User-Name}, %{Calling-Station-Id}), ', ') == "bob, aa:bb:cc:dd:ee:ff"
2069@endverbatim
2070 *
2071 * @ingroup xlat_functions
2072 */
2074 UNUSED xlat_ctx_t const *xctx,
2075 request_t *request, fr_value_box_list_t *args)
2076{
2077 fr_value_box_t *result;
2078 fr_value_box_t *list;
2079 fr_value_box_t *separator;
2080 fr_value_box_list_t *to_concat;
2081 char *buff;
2082 char const *sep;
2083
2084 XLAT_ARGS(args, &list, &separator);
2085
2086 sep = (separator) ? separator->vb_strvalue : "";
2087 to_concat = &list->vb_group;
2088
2089 result = fr_value_box_alloc(ctx, FR_TYPE_STRING, NULL);
2090 if (!result) {
2091 error:
2092 RPEDEBUG("Failed concatenating input");
2093 return XLAT_ACTION_FAIL;
2094 }
2095
2096 buff = fr_value_box_list_aprint(result, to_concat, sep, NULL);
2097 if (!buff) goto error;
2098
2100
2101 fr_dcursor_append(out, result);
2102
2103 return XLAT_ACTION_DONE;
2104}
2105
2107 { .required = true, .type = FR_TYPE_OCTETS },
2109};
2110
2111/** Print data as hex, not as VALUE.
2112 *
2113 * Example:
2114@verbatim
2115%hex("foobar") == "666f6f626172"
2116@endverbatim
2117 *
2118 * @see #xlat_func_bin
2119 *
2120 * @ingroup xlat_functions
2121 */
2123 UNUSED xlat_ctx_t const *xctx,
2124 UNUSED request_t *request, fr_value_box_list_t *args)
2125{
2126 char *new_buff;
2127 fr_value_box_t *list, *bin;
2128 fr_value_box_t safety;
2129
2130 XLAT_ARGS(args, &list);
2131
2132 while ((bin = fr_value_box_list_pop_head(&list->vb_group))) {
2133 fr_value_box_safety_copy(&safety, bin);
2134
2135 /*
2136 * Use existing box, but with new buffer
2137 */
2138 MEM(new_buff = talloc_zero_array(bin, char, (bin->vb_length * 2) + 1));
2139 if (bin->vb_length) {
2140 fr_base16_encode(&FR_SBUFF_OUT(new_buff, (bin->vb_length * 2) + 1),
2141 &FR_DBUFF_TMP(bin->vb_octets, bin->vb_length));
2143 fr_value_box_strdup_shallow(bin, NULL, new_buff, false);
2144 /*
2145 * Zero length binary > zero length hex string
2146 */
2147 } else {
2149 fr_value_box_strdup(bin, bin, NULL, "", false);
2150 }
2151
2152 fr_value_box_safety_copy(bin, &safety);
2153 fr_dcursor_append(out, bin);
2154 }
2155
2156 return XLAT_ACTION_DONE;
2157}
2158
2163
2164static xlat_action_t xlat_hmac(TALLOC_CTX *ctx, fr_dcursor_t *out,
2165 fr_value_box_list_t *args, uint8_t *digest, int digest_len, hmac_type type)
2166{
2167 fr_value_box_t *vb, *data, *key;
2168
2169 XLAT_ARGS(args, &data, &key);
2170
2171 if (type == HMAC_MD5) {
2172 /* coverity[dereference] */
2173 fr_hmac_md5(digest, data->vb_octets, data->vb_length, key->vb_octets, key->vb_length);
2174 } else if (type == HMAC_SHA1) {
2175 /* coverity[dereference] */
2176 fr_hmac_sha1(digest, data->vb_octets, data->vb_length, key->vb_octets, key->vb_length);
2177 }
2178
2179 MEM(vb = fr_value_box_alloc_null(ctx));
2180 fr_value_box_memdup(vb, vb, NULL, digest, digest_len, false);
2181
2183
2184 return XLAT_ACTION_DONE;
2185}
2186
2188 { .required = true, .concat = true, .type = FR_TYPE_STRING },
2189 { .required = true, .concat = true, .type = FR_TYPE_STRING },
2191};
2192
2193/** Generate the HMAC-MD5 of a string or attribute
2194 *
2195 * Example:
2196@verbatim
2197%hmacmd5('foo', 'bar') == "0x31b6db9e5eb4addb42f1a6ca07367adc"
2198@endverbatim
2199 *
2200 * @ingroup xlat_functions
2201 */
2203 UNUSED xlat_ctx_t const *xctx,
2204 UNUSED request_t *request, fr_value_box_list_t *in)
2205{
2206 uint8_t digest[MD5_DIGEST_LENGTH];
2207 return xlat_hmac(ctx, out, in, digest, MD5_DIGEST_LENGTH, HMAC_MD5);
2208}
2209
2210
2211/** Generate the HMAC-SHA1 of a string or attribute
2212 *
2213 * Example:
2214@verbatim
2215%hmacsha1('foo', 'bar') == "0x85d155c55ed286a300bd1cf124de08d87e914f3a"
2216@endverbatim
2217 *
2218 * @ingroup xlat_functions
2219 */
2221 UNUSED xlat_ctx_t const *xctx,
2222 UNUSED request_t *request, fr_value_box_list_t *in)
2223{
2225 return xlat_hmac(ctx, out, in, digest, SHA1_DIGEST_LENGTH, HMAC_SHA1);
2226}
2227
2229 { .required = true, .type = FR_TYPE_VOID },
2230 { .variadic = XLAT_ARG_VARIADIC_EMPTY_SQUASH, .type = FR_TYPE_VOID },
2232};
2233
2234/** Join a series of arguments to form a single list
2235 *
2236 * null boxes are not preserved.
2237 */
2239 UNUSED xlat_ctx_t const *xctx,
2240 UNUSED request_t *request, fr_value_box_list_t *in)
2241{
2243 fr_assert(arg->type == FR_TYPE_GROUP);
2244
2245 fr_value_box_list_foreach_safe(&arg->vb_group, vb) {
2246 fr_value_box_list_remove(&arg->vb_group, vb);
2248 }}
2249 }
2250 return XLAT_ACTION_DONE;
2251}
2252
2253static void ungroup(fr_dcursor_t *out, fr_value_box_list_t *in)
2254{
2255 fr_value_box_t *vb;
2256
2257 while ((vb = fr_value_box_list_pop_head(in)) != NULL) {
2258 if (vb->type != FR_TYPE_GROUP) {
2260 continue;
2261 }
2262 talloc_free(vb);
2263 }
2264}
2265
2266/** Ungroups all of its arguments into one flat list.
2267 *
2268 */
2270 UNUSED xlat_ctx_t const *xctx,
2271 UNUSED request_t *request, fr_value_box_list_t *in)
2272{
2273 fr_value_box_t *arg = NULL;
2274
2275 while ((arg = fr_value_box_list_next(in, arg)) != NULL) {
2276 fr_assert(arg->type == FR_TYPE_GROUP);
2277
2278 ungroup(out, &arg->vb_group);
2279 }
2280 return XLAT_ACTION_DONE;
2281}
2282
2284 { .single = true, .variadic = XLAT_ARG_VARIADIC_EMPTY_KEEP, .type = FR_TYPE_VOID },
2286};
2287
2288/** Return the on-the-wire size of the boxes in bytes
2289 *
2290 * skips null values
2291 *
2292 * Example:
2293@verbatim
2294%length(foobar) == 6
2295%length(%bin("0102030005060708")) == 8
2296@endverbatim
2297 *
2298 * @see #xlat_func_strlen
2299 *
2300 * @ingroup xlat_functions
2301 */
2303 UNUSED xlat_ctx_t const *xctx,
2304 UNUSED request_t *request, fr_value_box_list_t *in)
2305
2306{
2309
2310 MEM(my = fr_value_box_alloc(ctx, FR_TYPE_SIZE, NULL));
2311 if (!fr_type_is_null(vb->type)) my->vb_size = fr_value_box_network_length(vb);
2313 }
2314
2315 return XLAT_ACTION_DONE;
2316}
2317
2318
2320 { .concat = true, .type = FR_TYPE_OCTETS },
2322};
2323
2324/** Calculate the MD4 hash of a string or attribute.
2325 *
2326 * Example:
2327@verbatim
2328%md4("foo") == "0ac6700c491d70fb8650940b1ca1e4b2"
2329@endverbatim
2330 *
2331 * @ingroup xlat_functions
2332 */
2334 UNUSED xlat_ctx_t const *xctx,
2335 UNUSED request_t *request, fr_value_box_list_t *args)
2336{
2337 uint8_t digest[MD4_DIGEST_LENGTH];
2338 fr_value_box_t *vb;
2339 fr_value_box_t *in_head;
2340
2341 XLAT_ARGS(args, &in_head);
2342
2343 if (in_head) {
2344 fr_md4_calc(digest, in_head->vb_octets, in_head->vb_length);
2345 } else {
2346 /* Digest of empty string */
2347 fr_md4_calc(digest, NULL, 0);
2348 }
2349
2350 MEM(vb = fr_value_box_alloc_null(ctx));
2351 fr_value_box_memdup(vb, vb, NULL, digest, sizeof(digest), false);
2352
2354 VALUE_BOX_LIST_VERIFY((fr_value_box_list_t *)out->dlist);
2355
2356 return XLAT_ACTION_DONE;
2357}
2358
2360 { .concat = true, .type = FR_TYPE_OCTETS },
2362};
2363
2364/** Calculate the MD5 hash of a string or attribute.
2365 *
2366 * Example:
2367@verbatim
2368%md5("foo") == "acbd18db4cc2f85cedef654fccc4a4d8"
2369@endverbatim
2370 *
2371 * @ingroup xlat_functions
2372 */
2374 UNUSED xlat_ctx_t const *xctx,
2375 UNUSED request_t *request, fr_value_box_list_t *args)
2376{
2377 uint8_t digest[MD5_DIGEST_LENGTH];
2378 fr_value_box_t *vb;
2379 fr_value_box_t *in_head;
2380
2381 XLAT_ARGS(args, &in_head);
2382
2383 if (in_head) {
2384 fr_md5_calc(digest, in_head->vb_octets, in_head->vb_length);
2385 } else {
2386 /* Digest of empty string */
2387 fr_md5_calc(digest, NULL, 0);
2388 }
2389
2390 MEM(vb = fr_value_box_alloc_null(ctx));
2391 fr_value_box_memdup(vb, vb, NULL, digest, sizeof(digest), false);
2392
2394
2395 return XLAT_ACTION_DONE;
2396}
2397
2398
2399/** Encode attributes as a series of string attribute/value pairs
2400 *
2401 * This is intended to serialize one or more attributes as a comma
2402 * delimited string.
2403 *
2404 * Example:
2405@verbatim
2406%pairs.print(request.[*]) == 'User-Name = "foo"User-Password = "bar"'
2407%concat(%pairs.print.print(request.[*]), ', ') == 'User-Name = "foo", User-Password = "bar"'
2408@endverbatim
2409 *
2410 * @see #xlat_func_concat
2411 *
2412 * @ingroup xlat_functions
2413 */
2415 UNUSED xlat_ctx_t const *xctx,
2416 request_t *request, fr_value_box_list_t *args)
2417{
2418 fr_pair_t *vp;
2419 fr_dcursor_t *cursor;
2420 fr_value_box_t *vb;
2421 fr_value_box_t *in_head;
2422
2423 XLAT_ARGS(args, &in_head);
2424
2425 cursor = fr_value_box_get_cursor(in_head);
2426
2427 for (vp = fr_dcursor_current(cursor);
2428 vp;
2429 vp = fr_dcursor_next(cursor)) {
2430 char *buff;
2431
2432 MEM(vb = fr_value_box_alloc_null(ctx));
2433 if (unlikely(fr_pair_aprint(vb, &buff, NULL, vp) < 0)) {
2434 RPEDEBUG("Failed printing pair");
2435 talloc_free(vb);
2436 return XLAT_ACTION_FAIL;
2437 }
2438
2439 fr_value_box_bstrdup_buffer_shallow(NULL, vb, NULL, buff, false);
2441
2442 VALUE_BOX_VERIFY(vb);
2443 }
2444
2445 return XLAT_ACTION_DONE;
2446}
2447
2449 { .required = true, .single = true, .type = FR_TYPE_UINT32 },
2451};
2452
2453/** Generate a random integer value
2454 *
2455 * For "N = %rand(MAX)", 0 <= N < MAX
2456 *
2457 * Example:
2458@verbatim
2459%rand(100) == 42
2460@endverbatim
2461 *
2462 * @ingroup xlat_functions
2463 */
2465 UNUSED xlat_ctx_t const *xctx,
2466 UNUSED request_t *request, fr_value_box_list_t *in)
2467{
2468 int64_t result;
2469 fr_value_box_t *vb;
2470 fr_value_box_t *in_head = fr_value_box_list_head(in);
2471
2472 result = in_head->vb_uint32;
2473
2474 /* Make sure it isn't too big */
2475 if (result > (1 << 30)) result = (1 << 30);
2476
2477 result *= fr_rand(); /* 0..2^32-1 */
2478 result >>= 32;
2479
2480 MEM(vb = fr_value_box_alloc(ctx, FR_TYPE_UINT64, NULL));
2481 vb->vb_uint64 = result;
2482
2484
2485 return XLAT_ACTION_DONE;
2486}
2487
2489 { .required = true, .concat = true, .type = FR_TYPE_STRING },
2491};
2492
2493/** Generate a string of random chars
2494 *
2495 * Build strings of random chars, useful for generating tokens and passcodes
2496 * Format similar to String::Random.
2497 *
2498 * Format characters may include the following, and may be
2499 * preceded by a repetition count:
2500 * - "c" lowercase letters
2501 * - "C" uppercase letters
2502 * - "n" numbers
2503 * - "a" alphanumeric
2504 * - "!" punctuation
2505 * - "." alphanumeric + punctuation
2506 * - "s" alphanumeric + "./"
2507 * - "o" characters suitable for OTP (easily confused removed)
2508 * - "b" binary data
2509 *
2510 * Example:
2511@verbatim
2512%randstr("CCCC!!cccnnn") == "IPFL>{saf874"
2513%randstr("42o") == "yHdupUwVbdHprKCJRYfGbaWzVwJwUXG9zPabdGAhM9"
2514%hex(%randstr("bbbb")) == "a9ce04f3"
2515%hex(%randstr("8b")) == "fe165529f9f66839"
2516@endverbatim
2517 * @ingroup xlat_functions
2518 */
2520 UNUSED xlat_ctx_t const *xctx,
2521 request_t *request, fr_value_box_list_t *args)
2522{
2523 /*
2524 * Lookup tables for randstr char classes
2525 */
2526 static char randstr_punc[] = "!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~";
2527 static char randstr_salt[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmopqrstuvwxyz/.";
2528
2529 /*
2530 * Characters humans rarely confuse. Reduces char set considerably
2531 * should only be used for things such as one time passwords.
2532 */
2533 static char randstr_otp[] = "469ACGHJKLMNPQRUVWXYabdfhijkprstuvwxyz";
2534
2535 char const *p, *start, *end;
2536 char *endptr;
2537 char *buff_p;
2538 unsigned int result;
2539 unsigned int reps;
2540 size_t outlen = 0;
2541 fr_value_box_t* vb;
2542 fr_value_box_t *in_head;
2543
2544 XLAT_ARGS(args, &in_head);
2545
2546 /** Max repetitions of a single character class
2547 *
2548 */
2549#define REPETITION_MAX 1024
2550
2551 start = p = in_head->vb_strvalue;
2552 end = p + in_head->vb_length;
2553
2554 /*
2555 * Calculate size of output
2556 */
2557 while (p < end) {
2558 /*
2559 * Repetition modifiers.
2560 *
2561 * We limit it to REPETITION_MAX, because we don't want
2562 * utter stupidity.
2563 */
2564 if (isdigit((uint8_t) *p)) {
2565 reps = strtol(p, &endptr, 10);
2566 if (reps > REPETITION_MAX) reps = REPETITION_MAX;
2567 outlen += reps;
2568 p = endptr;
2569 } else {
2570 outlen++;
2571 }
2572 p++;
2573 }
2574
2575 MEM(vb = fr_value_box_alloc_null(ctx));
2576 MEM(fr_value_box_bstr_alloc(vb, &buff_p, vb, NULL, outlen, false) == 0);
2577
2578 /* Reset p to start position */
2579 p = start;
2580
2581 while (p < end) {
2582 size_t i;
2583
2584 if (isdigit((uint8_t) *p)) {
2585 reps = strtol(p, &endptr, 10);
2586 if (reps > REPETITION_MAX) {
2587 reps = REPETITION_MAX;
2588 RMARKER(L_WARN, L_DBG_LVL_2, start, start - p,
2589 "Forcing repetition to %u", (unsigned int)REPETITION_MAX);
2590 }
2591 p = endptr;
2592 } else {
2593 reps = 1;
2594 }
2595
2596 for (i = 0; i < reps; i++) {
2597 result = fr_rand();
2598 switch (*p) {
2599 /*
2600 * Lowercase letters
2601 */
2602 case 'c':
2603 *buff_p++ = 'a' + (result % 26);
2604 break;
2605
2606 /*
2607 * Uppercase letters
2608 */
2609 case 'C':
2610 *buff_p++ = 'A' + (result % 26);
2611 break;
2612
2613 /*
2614 * Numbers
2615 */
2616 case 'n':
2617 *buff_p++ = '0' + (result % 10);
2618 break;
2619
2620 /*
2621 * Alpha numeric
2622 */
2623 case 'a':
2624 *buff_p++ = randstr_salt[result % (sizeof(randstr_salt) - 3)];
2625 break;
2626
2627 /*
2628 * Punctuation
2629 */
2630 case '!':
2631 *buff_p++ = randstr_punc[result % (sizeof(randstr_punc) - 1)];
2632 break;
2633
2634 /*
2635 * Alpha numeric + punctuation
2636 */
2637 case '.':
2638 *buff_p++ = '!' + (result % 95);
2639 break;
2640
2641 /*
2642 * Alpha numeric + salt chars './'
2643 */
2644 case 's':
2645 *buff_p++ = randstr_salt[result % (sizeof(randstr_salt) - 1)];
2646 break;
2647
2648 /*
2649 * Chars suitable for One Time Password tokens.
2650 * Alpha numeric with easily confused char pairs removed.
2651 */
2652 case 'o':
2653 *buff_p++ = randstr_otp[result % (sizeof(randstr_otp) - 1)];
2654 break;
2655
2656 /*
2657 * Binary data - Copy between 1-4 bytes at a time
2658 */
2659 case 'b':
2660 {
2661 size_t copy = (reps - i) > sizeof(result) ? sizeof(result) : reps - i;
2662
2663 memcpy(buff_p, (uint8_t *)&result, copy);
2664 buff_p += copy;
2665 i += (copy - 1); /* Loop +1 */
2666 }
2667 break;
2668
2669 default:
2670 REDEBUG("Invalid character class '%c'", *p);
2671 talloc_free(vb);
2672
2673 return XLAT_ACTION_FAIL;
2674 }
2675 }
2676
2677 p++;
2678 }
2679
2680 *buff_p++ = '\0';
2681
2683
2684 return XLAT_ACTION_DONE;
2685}
2686
2687
2689 { .required = true, .type = FR_TYPE_UINT64 },
2690 { .required = false, .type = FR_TYPE_UINT64 },
2691 { .required = false, .type = FR_TYPE_UINT64 },
2693};
2694
2695/** Generate a range of uint64 numbers
2696 *
2697 * Example:
2698@verbatim
2699%range(end) - 0..end
2700%rang(start, end)
2701%range(start,end, step)
2702@endverbatim
2703 * @ingroup xlat_functions
2704 */
2706 UNUSED xlat_ctx_t const *xctx,
2707 request_t *request, fr_value_box_list_t *args)
2708{
2709 fr_value_box_t *start_vb, *end_vb, *step_vb;
2710 fr_value_box_t *dst;
2711 uint64_t i, start, end, step;
2712
2713 XLAT_ARGS(args, &start_vb, &end_vb, &step_vb);
2714
2715 if (step_vb) {
2716 start = fr_value_box_list_head(&start_vb->vb_group)->vb_uint64;
2717 end = fr_value_box_list_head(&end_vb->vb_group)->vb_uint64;
2718 step = fr_value_box_list_head(&step_vb->vb_group)->vb_uint64;
2719
2720 } else if (end_vb) {
2721 start = fr_value_box_list_head(&start_vb->vb_group)->vb_uint64;
2722 end = fr_value_box_list_head(&end_vb->vb_group)->vb_uint64;
2723 step = 1;
2724
2725 } else {
2726 start = 0;
2727 end = fr_value_box_list_head(&start_vb->vb_group)->vb_uint64;
2728 step = 1;
2729 }
2730
2731 if (end <= start) {
2732 REDEBUG("Invalid range - 'start' must be less than 'end'");
2733 return XLAT_ACTION_FAIL;
2734 }
2735
2736 if (!step) {
2737 REDEBUG("Invalid range - 'step' must be greater than zero");
2738 return XLAT_ACTION_FAIL;
2739 }
2740
2741 if (step > (end - start)) {
2742 REDEBUG("Invalid range - 'step' must allow for at least one result");
2743 return XLAT_ACTION_FAIL;
2744 }
2745
2746 if (((end - start) / step) > 1000) {
2747 REDEBUG("Invalid range - Too many results");
2748 return XLAT_ACTION_FAIL;
2749 }
2750
2751 for (i = start; i < end; i += step) {
2752 MEM(dst = fr_value_box_alloc(ctx, FR_TYPE_UINT64, NULL));
2753 dst->vb_uint64 = i;
2754 fr_dcursor_append(out, dst);
2755 }
2756
2757 return XLAT_ACTION_DONE;
2758}
2759
2760static int CC_HINT(nonnull(2,3)) regex_xlat_escape(UNUSED request_t *request, fr_value_box_t *vb, UNUSED void *uctx)
2761{
2762 ssize_t slen;
2763 fr_sbuff_t *out = NULL;
2764 fr_value_box_entry_t entry;
2765
2766 FR_SBUFF_TALLOC_THREAD_LOCAL(&out, 256, 4096);
2767
2768 slen = fr_value_box_print(out, vb, &regex_escape_rules);
2769 if (slen < 0) return -1;
2770
2771 entry = vb->entry;
2773 (void) fr_value_box_bstrndup(vb, vb, NULL, fr_sbuff_start(out), fr_sbuff_used(out), false);
2774 vb->entry = entry;
2775
2776 return 0;
2777}
2778
2783
2784
2785/** Get named subcapture value from previous regex
2786 *
2787 * Example:
2788@verbatim
2789if ("foo" =~ /^(?<name>.*)/) {
2790 noop
2791}
2792%regex.match(name) == "foo"
2793@endverbatim
2794 *
2795 * @ingroup xlat_functions
2796 */
2798 UNUSED xlat_ctx_t const *xctx,
2799 request_t *request, fr_value_box_list_t *in)
2800{
2801 fr_value_box_t *in_head = fr_value_box_list_head(in);
2802
2803 /*
2804 * Find the first child of the first argument group
2805 */
2806 fr_value_box_t *arg = fr_value_box_list_head(&in_head->vb_group);
2807
2808 /*
2809 * Return the complete capture if no other capture is specified
2810 */
2811 if (!arg) {
2812 fr_value_box_t *vb;
2813
2814 MEM(vb = fr_value_box_alloc_null(ctx));
2815 if (regex_request_to_sub(vb, vb, request, 0) < 0) {
2816 REDEBUG2("No previous regex capture");
2817 talloc_free(vb);
2818 return XLAT_ACTION_FAIL;
2819 }
2820
2822
2823 return XLAT_ACTION_DONE;
2824 }
2825
2826 switch (arg->type) {
2827 /*
2828 * If the input is an integer value then get an
2829 * arbitrary subcapture index.
2830 */
2831 case FR_TYPE_NUMERIC:
2832 {
2833 fr_value_box_t idx;
2834 fr_value_box_t *vb;
2835
2836 if (fr_value_box_list_next(in, in_head)) {
2837 REDEBUG("Only one subcapture argument allowed");
2838 return XLAT_ACTION_FAIL;
2839 }
2840
2841 if (fr_value_box_cast(NULL, &idx, FR_TYPE_UINT32, NULL, arg) < 0) {
2842 RPEDEBUG("Bad subcapture index");
2843 return XLAT_ACTION_FAIL;
2844 }
2845
2846 MEM(vb = fr_value_box_alloc_null(ctx));
2847 if (regex_request_to_sub(vb, vb, request, idx.vb_uint32) < 0) {
2848 REDEBUG2("No previous numbered regex capture group '%u'", idx.vb_uint32);
2849 talloc_free(vb);
2850 return XLAT_ACTION_DONE;
2851 }
2853
2854 return XLAT_ACTION_DONE;
2855 }
2856
2857 default:
2858#if defined(HAVE_REGEX_PCRE) || defined(HAVE_REGEX_PCRE2)
2859 {
2860 fr_value_box_t *vb;
2861
2862 /*
2863 * Concatenate all input
2864 */
2866 arg, &in_head->vb_group, FR_TYPE_STRING,
2868 SIZE_MAX) < 0) {
2869 RPEDEBUG("Failed concatenating input");
2870 return XLAT_ACTION_FAIL;
2871 }
2872
2873 MEM(vb = fr_value_box_alloc_null(ctx));
2874 if (regex_request_to_sub_named(vb, vb, request, arg->vb_strvalue) < 0) {
2875 REDEBUG2("No previous named regex capture group '%s'", arg->vb_strvalue);
2876 talloc_free(vb);
2877 return XLAT_ACTION_DONE; /* NOT an error, just an empty result */
2878 }
2880
2881 return XLAT_ACTION_DONE;
2882 }
2883#else
2884 RDEBUG("Named regex captures are not supported (they require libpcre2)");
2885 return XLAT_ACTION_FAIL;
2886#endif
2887 }
2888}
2889
2891 { .concat = true, .type = FR_TYPE_OCTETS },
2893};
2894
2895/** Calculate the SHA1 hash of a string or attribute.
2896 *
2897 * Example:
2898@verbatim
2899%sha1(foo) == "0beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a33"
2900@endverbatim
2901 *
2902 * @ingroup xlat_functions
2903 */
2905 UNUSED xlat_ctx_t const *xctx,
2906 UNUSED request_t *request, fr_value_box_list_t *args)
2907{
2909 fr_sha1_ctx sha1_ctx;
2910 fr_value_box_t *vb;
2911 fr_value_box_t *in_head;
2912
2913 XLAT_ARGS(args, &in_head);
2914
2915 fr_sha1_init(&sha1_ctx);
2916 if (in_head) {
2917 fr_sha1_update(&sha1_ctx, in_head->vb_octets, in_head->vb_length);
2918 } else {
2919 /* sha1 of empty string */
2920 fr_sha1_update(&sha1_ctx, NULL, 0);
2921 }
2922 fr_sha1_final(digest, &sha1_ctx);
2923
2924 MEM(vb = fr_value_box_alloc_null(ctx));
2925 fr_value_box_memdup(vb, vb, NULL, digest, sizeof(digest), false);
2926
2928
2929 return XLAT_ACTION_DONE;
2930}
2931
2932/** Calculate any digest supported by OpenSSL EVP_MD
2933 *
2934 * Example:
2935@verbatim
2936%sha2_256(foo) == "0beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a33"
2937@endverbatim
2938 *
2939 * @ingroup xlat_functions
2940 */
2941#ifdef HAVE_OPENSSL_EVP_H
2942static xlat_action_t xlat_evp_md(TALLOC_CTX *ctx, fr_dcursor_t *out,
2943 UNUSED xlat_ctx_t const *xctx,
2944 UNUSED request_t *request, fr_value_box_list_t *args, EVP_MD const *md)
2945{
2946 uint8_t digest[EVP_MAX_MD_SIZE];
2947 unsigned int digestlen;
2948 EVP_MD_CTX *md_ctx;
2949 fr_value_box_t *vb;
2950 fr_value_box_t *in_head;
2951
2952 XLAT_ARGS(args, &in_head);
2953
2954 md_ctx = EVP_MD_CTX_create();
2955 EVP_DigestInit_ex(md_ctx, md, NULL);
2956 if (in_head) {
2957 EVP_DigestUpdate(md_ctx, in_head->vb_octets, in_head->vb_length);
2958 } else {
2959 EVP_DigestUpdate(md_ctx, NULL, 0);
2960 }
2961 EVP_DigestFinal_ex(md_ctx, digest, &digestlen);
2962 EVP_MD_CTX_destroy(md_ctx);
2963
2964 MEM(vb = fr_value_box_alloc_null(ctx));
2965 fr_value_box_memdup(vb, vb, NULL, digest, digestlen, false);
2966
2968
2969 return XLAT_ACTION_DONE;
2970}
2971
2972# define EVP_MD_XLAT(_md, _md_func) \
2973static xlat_action_t xlat_func_##_md(TALLOC_CTX *ctx, fr_dcursor_t *out,\
2974 xlat_ctx_t const *xctx, \
2975 request_t *request,\
2976 fr_value_box_list_t *in)\
2977{\
2978 return xlat_evp_md(ctx, out, xctx, request, in, EVP_##_md_func());\
2979}
2980
2981EVP_MD_XLAT(sha2_224, sha224)
2982EVP_MD_XLAT(sha2_256, sha256)
2983EVP_MD_XLAT(sha2_384, sha384)
2984EVP_MD_XLAT(sha2_512, sha512)
2985
2986/*
2987 * OpenWRT's OpenSSL library doesn't contain these by default
2988 */
2989#ifdef HAVE_EVP_BLAKE2S256
2990EVP_MD_XLAT(blake2s_256, blake2s256)
2991#endif
2992
2993#ifdef HAVE_EVP_BLAKE2B512
2994EVP_MD_XLAT(blake2b_512, blake2b512)
2995#endif
2996
2997EVP_MD_XLAT(sha3_224, sha3_224)
2998EVP_MD_XLAT(sha3_256, sha3_256)
2999EVP_MD_XLAT(sha3_384, sha3_384)
3000EVP_MD_XLAT(sha3_512, sha3_512)
3001#endif
3002
3003
3005 { .required = true, .concat = true, .type = FR_TYPE_STRING },
3007};
3008
3010 { .concat = true, .type = FR_TYPE_STRING },
3012};
3013
3014/** Print length of given string
3015 *
3016 * Example:
3017@verbatim
3018%strlen(foo) == 3
3019@endverbatim
3020 *
3021 * @see #xlat_func_length
3022 *
3023 * @ingroup xlat_functions
3024 */
3026 UNUSED xlat_ctx_t const *xctx,
3027 UNUSED request_t *request, fr_value_box_list_t *args)
3028{
3029 fr_value_box_t *vb;
3030 fr_value_box_t *in_head;
3031
3032 XLAT_ARGS(args, &in_head);
3033
3034 MEM(vb = fr_value_box_alloc(ctx, FR_TYPE_SIZE, NULL));
3035
3036 if (!in_head) {
3037 vb->vb_size = 0;
3038 } else {
3039 vb->vb_size = strlen(in_head->vb_strvalue);
3040 }
3041
3043
3044 return XLAT_ACTION_DONE;
3045}
3046
3048 { .concat = true, .type = FR_TYPE_STRING },
3049 { .single = true, .type = FR_TYPE_BOOL },
3051};
3052
3053/** Return whether a string has only printable chars
3054 *
3055 * This function returns true if the input string contains UTF8 sequences and printable chars.
3056 *
3057 * @note "\t" and " " are considered unprintable chars, unless the second argument(relaxed) is true.
3058 *
3059 * Example:
3060@verbatim
3061%str.printable("🍉abcdef🍓") == true
3062%str.printable("\000\n\r\t") == false
3063%str.printable("\t abcd", yes) == true
3064@endverbatim
3065 *
3066 * @ingroup xlat_functions
3067 */
3069 UNUSED xlat_ctx_t const *xctx,
3070 UNUSED request_t *request, fr_value_box_list_t *args)
3071{
3072 fr_value_box_t *vb;
3073 fr_value_box_t *str;
3074 fr_value_box_t *relaxed_vb;
3075 uint8_t const *p, *end;
3076 bool relaxed = false;
3077
3078 XLAT_ARGS(args, &str, &relaxed_vb);
3079
3080 if (relaxed_vb) relaxed = relaxed_vb->vb_bool;
3081
3082 p = (uint8_t const *)str->vb_strvalue;
3083 end = p + str->vb_length;
3084
3085 MEM(vb = fr_value_box_alloc(ctx, FR_TYPE_BOOL, NULL));
3087 vb->vb_bool = false;
3088
3089 do {
3090 size_t clen;
3091
3092 if ((*p < '!') &&
3093 (!relaxed || ((*p != '\t') && (*p != ' ')))) return XLAT_ACTION_DONE;
3094
3095 if (*p == 0x7f) return XLAT_ACTION_DONE;
3096
3097 clen = fr_utf8_char(p, end - p);
3098 if (clen == 0) return XLAT_ACTION_DONE;
3099 p += clen;
3100 } while (p < end);
3101
3102 vb->vb_bool = true;
3103
3104 return XLAT_ACTION_DONE;
3105}
3106
3108 { .concat = true, .type = FR_TYPE_STRING },
3110};
3111
3112/** Return whether a string is valid UTF-8
3113 *
3114 * This function returns true if the input string is valid UTF-8, false otherwise.
3115 *
3116 * Example:
3117@verbatim
3118%str.utf8(🍉🥝🍓) == true
3119%str.utf8(🍉\xff🍓) == false
3120@endverbatim
3121 *
3122 * @ingroup xlat_functions
3123 */
3125 UNUSED xlat_ctx_t const *xctx,
3126 UNUSED request_t *request, fr_value_box_list_t *args)
3127{
3128 fr_value_box_t *vb;
3129 fr_value_box_t *in_head;
3130
3131 XLAT_ARGS(args, &in_head);
3132
3133 MEM(vb = fr_value_box_alloc(ctx, FR_TYPE_BOOL, NULL));
3134 vb->vb_bool = (fr_utf8_str((uint8_t const *)in_head->vb_strvalue,
3135 in_head->vb_length) >= 0);
3136
3138
3139 return XLAT_ACTION_DONE;
3140}
3141
3143 { .single = true, .required = true, .type = FR_TYPE_VOID },
3144 { .single = true, .required = true, .type = FR_TYPE_INT32 },
3145 { .single = true, .type = FR_TYPE_INT32 },
3147};
3148
3149/** Extract a substring from string / octets data
3150 *
3151 * Non string / octets data is cast to a string.
3152 *
3153 * Second parameter is start position, optional third parameter is length
3154 * Negative start / length count from RHS of data.
3155 *
3156 * Example: (User-Name = "hello")
3157@verbatim
3158%substr(&User-Name, 1, 3) == 'ell'
3159@endverbatim
3160 *
3161 * @ingroup xlat_functions
3162 */
3163static xlat_action_t xlat_func_substr(TALLOC_CTX *ctx, fr_dcursor_t *out, UNUSED xlat_ctx_t const *xctx,
3164 request_t *request, fr_value_box_list_t *args)
3165{
3166 fr_value_box_t *in = NULL, *start_vb, *len_vb, *vb;
3167 int32_t start, end, len;
3168
3169 XLAT_ARGS(args, &in, &start_vb, &len_vb);
3170
3171 switch (in->type) {
3172 case FR_TYPE_OCTETS:
3173 case FR_TYPE_STRING:
3174 break;
3175
3176 default:
3178 RPEDEBUG("Failed casting value to string");
3179 return XLAT_ACTION_FAIL;
3180 }
3181 break;
3182 }
3183
3184 if (start_vb->vb_int32 > (int32_t)in->vb_length) return XLAT_ACTION_DONE;
3185
3186 if (start_vb->vb_int32 < 0) {
3187 start = in->vb_length + start_vb->vb_int32;
3188 if (start < 0) start = 0;
3189 } else {
3190 start = start_vb->vb_int32;
3191 }
3192
3193 if (len_vb) {
3194 if (len_vb->vb_int32 < 0) {
3195 end = in->vb_length + len_vb->vb_int32;
3196 if (end < 0) return XLAT_ACTION_DONE;
3197 } else {
3198 end = start + len_vb->vb_int32;
3199 if (end > (int32_t)in->vb_length) end = in->vb_length;
3200 }
3201 } else {
3202 end = in->vb_length;
3203 }
3204
3205 if (start >= end) return XLAT_ACTION_DONE;
3206
3207 MEM(vb = fr_value_box_alloc(ctx, in->type, NULL));
3208
3209 len = end - start;
3210 switch (in->type) {
3211 case FR_TYPE_STRING:
3212 fr_value_box_bstrndup(vb, vb, NULL, &in->vb_strvalue[start], len, false);
3213 break;
3214 case FR_TYPE_OCTETS:
3215 {
3216 uint8_t *buf;
3217 fr_value_box_mem_alloc(vb, &buf, vb, NULL, len, false);
3218 memcpy(buf, &in->vb_octets[start], len);
3219 }
3220 break;
3221 default:
3222 fr_assert(0);
3223 }
3224
3227
3228 return XLAT_ACTION_DONE;
3229}
3230
3231#ifdef HAVE_REGEX_PCRE2
3232/** Cache statically compiled expressions
3233 */
3234typedef struct {
3235 regex_t *pattern;
3236 fr_regex_flags_t flags;
3237} xlat_subst_regex_inst_t;
3238
3239/** Pre-compile regexes where possible
3240 */
3241static int xlat_instantiate_subst_regex(xlat_inst_ctx_t const *xctx)
3242{
3243 xlat_subst_regex_inst_t *inst = talloc_get_type_abort(xctx->inst, xlat_subst_regex_inst_t);
3244 xlat_exp_t *patt_exp;
3245 fr_sbuff_t sbuff;
3246 fr_sbuff_marker_t start_m, end_m;
3247
3248 /* args #2 (pattern) */
3249 patt_exp = fr_dlist_next(&xctx->ex->call.args->dlist, fr_dlist_head(&xctx->ex->call.args->dlist));
3250 fr_assert(patt_exp && patt_exp->type == XLAT_GROUP); /* args must be groups */
3251
3252 /* If there are dynamic expansions, we can't pre-compile */
3253 if (!xlat_is_literal(patt_exp->group)) return 0;
3254 fr_assert(fr_dlist_num_elements(&patt_exp->group->dlist) == 1);
3255
3256 patt_exp = fr_dlist_head(&patt_exp->group->dlist);
3257
3258 /* We can only pre-compile strings */
3259 if (!fr_type_is_string(patt_exp->data.type)) return 0;
3260
3261 sbuff = FR_SBUFF_IN(patt_exp->data.vb_strvalue, patt_exp->data.vb_length);
3262
3263 /* skip any whitesapce */
3264 fr_sbuff_adv_past_whitespace(&sbuff, SIZE_MAX, 0);
3265
3266 /* Is the next char a forward slash? */
3267 if (fr_sbuff_next_if_char(&sbuff, '/')) {
3268 fr_slen_t slen;
3269
3270 fr_sbuff_marker(&start_m, &sbuff);
3271
3272 if (!fr_sbuff_adv_to_chr(&sbuff, SIZE_MAX, '/')) return 0; /* Not a regex */
3273
3274 fr_sbuff_marker(&end_m, &sbuff);
3275 fr_sbuff_next(&sbuff); /* skip trailing slash */
3276
3277 if (fr_sbuff_remaining(&sbuff)) {
3278 slen = regex_flags_parse(NULL, &inst->flags,
3279 &sbuff,
3280 NULL, true);
3281 if (slen < 0) {
3282 PERROR("Failed parsing regex flags in \"%s\"", patt_exp->data.vb_strvalue);
3283 return -1;
3284 }
3285 }
3286
3287 if (regex_compile(inst, &inst->pattern,
3288 fr_sbuff_current(&start_m), fr_sbuff_current(&end_m) - fr_sbuff_current(&start_m),
3289 &inst->flags, true, false) <= 0) {
3290 PERROR("Failed compiling regex \"%s\"", patt_exp->data.vb_strvalue);
3291 return -1;
3292 }
3293 }
3294 /* No... then it's not a regex */
3295
3296 return 0;
3297}
3298
3299/** Perform regex substitution TODO CHECK
3300 *
3301 * Called when %subst() pattern begins with "/"
3302 *
3303@verbatim
3304%subst(<subject>, /<regex>/[flags], <replace>)
3305@endverbatim
3306 *
3307 * Example: (User-Name = "foo")
3308@verbatim
3309%subst(%{User-Name}, /oo.*$/, 'un') == "fun"
3310@endverbatim
3311 *
3312 * @note References can be specified in the replacement string with $<ref>
3313 *
3314 * @see #xlat_func_subst
3315 *
3316 * @ingroup xlat_functions
3317 */
3318static int xlat_func_subst_regex(TALLOC_CTX *ctx, fr_dcursor_t *out,
3319 xlat_ctx_t const *xctx, request_t *request,
3320 fr_value_box_list_t *args)
3321{
3322 xlat_subst_regex_inst_t const *inst = talloc_get_type_abort_const(xctx->inst, xlat_subst_regex_inst_t);
3323 fr_sbuff_t sbuff;
3324 fr_sbuff_marker_t start_m, end_m;
3325 char *buff;
3326 ssize_t slen;
3327 regex_t *pattern, *our_pattern = NULL;
3328 fr_regex_flags_t const *flags;
3329 fr_regex_flags_t our_flags = {};
3330 fr_value_box_t *vb;
3331 fr_value_box_t *subject_vb;
3332 fr_value_box_t *regex_vb;
3333 fr_value_box_t *rep_vb;
3334
3335 XLAT_ARGS(args, &subject_vb, &regex_vb, &rep_vb);
3336
3337 /*
3338 * Was not pre-compiled, so we need to compile it now
3339 */
3340 if (!inst->pattern) {
3341 sbuff = FR_SBUFF_IN(regex_vb->vb_strvalue, regex_vb->vb_length);
3342 if (fr_sbuff_len(&sbuff) == 0) {
3343 REDEBUG("Regex must not be empty");
3344 return XLAT_ACTION_FAIL;
3345 }
3346
3347 fr_sbuff_next(&sbuff); /* skip leading slash */
3348 fr_sbuff_marker(&start_m, &sbuff);
3349
3350 if (!fr_sbuff_adv_to_chr(&sbuff, SIZE_MAX, '/')) return 1; /* Not a regex */
3351
3352 fr_sbuff_marker(&end_m, &sbuff);
3353 fr_sbuff_next(&sbuff); /* skip trailing slash */
3354
3355 slen = regex_flags_parse(NULL, &our_flags, &sbuff, NULL, true);
3356 if (slen < 0) {
3357 RPEDEBUG("Failed parsing regex flags");
3358 return -1;
3359 }
3360
3361 /*
3362 * Process the substitution
3363 */
3364 if (regex_compile(NULL, &our_pattern,
3365 fr_sbuff_current(&start_m), fr_sbuff_current(&end_m) - fr_sbuff_current(&start_m),
3366 &our_flags, true, true) <= 0) {
3367 RPEDEBUG("Failed compiling regex");
3368 return -1;
3369 }
3370 pattern = our_pattern;
3371 flags = &our_flags;
3372 } else {
3373 pattern = inst->pattern;
3374 flags = &inst->flags;
3375 }
3376
3377 MEM(vb = fr_value_box_alloc_null(ctx));
3378 if (regex_substitute(vb, &buff, 0, pattern, flags,
3379 subject_vb->vb_strvalue, subject_vb->vb_length,
3380 rep_vb->vb_strvalue, rep_vb->vb_length, NULL) < 0) {
3381 RPEDEBUG("Failed performing substitution");
3382 talloc_free(vb);
3383 talloc_free(pattern);
3384 return -1;
3385 }
3386 fr_value_box_bstrdup_buffer_shallow(NULL, vb, NULL, buff, false);
3387
3388 fr_value_box_safety_copy(vb, subject_vb);
3389 fr_value_box_safety_merge(vb, rep_vb);
3390
3392
3393 talloc_free(our_pattern);
3394
3395 return 0;
3396}
3397#endif
3398
3400 { .required = true, .concat = true, .type = FR_TYPE_STRING },
3401 { .required = true, .concat = true, .type = FR_TYPE_STRING },
3402 { .required = true, .concat = true, .type = FR_TYPE_STRING },
3404};
3405
3406/** Perform regex substitution
3407 *
3408@verbatim
3409%subst(<subject>, <pattern>, <replace>)
3410@endverbatim
3411 *
3412 * Example: (User-Name = "foobar")
3413@verbatim
3414%subst(%{User-Name}, 'oo', 'un') == "funbar"
3415@endverbatim
3416 *
3417 * @see xlat_func_subst_regex
3418 *
3419 * @ingroup xlat_functions
3420 */
3422#ifdef HAVE_REGEX_PCRE2
3423 xlat_ctx_t const *xctx,
3424#else
3425 UNUSED xlat_ctx_t const *xctx,
3426#endif
3427 request_t *request, fr_value_box_list_t *args)
3428{
3429 char const *p, *q, *end;
3430 char *vb_str;
3431
3432 char const *pattern, *rep;
3433 size_t pattern_len, rep_len;
3434
3435 fr_value_box_t *rep_vb, *vb;
3436 fr_value_box_t *subject_vb;
3437 fr_value_box_t *pattern_vb;
3438
3439 XLAT_ARGS(args, &subject_vb, &pattern_vb, &rep_vb);
3440
3441 /* coverity[dereference] */
3442 pattern = pattern_vb->vb_strvalue;
3443 if (*pattern == '/') {
3444#ifdef HAVE_REGEX_PCRE2
3445 switch (xlat_func_subst_regex(ctx, out, xctx, request, args)) {
3446 case 0:
3447 return XLAT_ACTION_DONE;
3448
3449 case 1:
3450 /* Not a regex, fall through */
3451 break;
3452
3453 case -1:
3454 return XLAT_ACTION_FAIL;
3455 }
3456#else
3457 if (memchr(pattern, '/', pattern_vb->vb_length - 1)) {
3458 REDEBUG("regex based substitutions require libpcre2. "
3459 "Check ${features.regex-pcre2} to determine support");
3460 }
3461 return XLAT_ACTION_FAIL;
3462#endif
3463 }
3464
3465 /*
3466 * Check for empty pattern
3467 */
3468 pattern_len = pattern_vb->vb_length;
3469 if (pattern_len == 0) {
3470 REDEBUG("Empty pattern");
3471 return XLAT_ACTION_FAIL;
3472 }
3473
3474 rep = rep_vb->vb_strvalue;
3475 rep_len = rep_vb->vb_length;
3476
3477 p = subject_vb->vb_strvalue;
3478 end = p + subject_vb->vb_length;
3479
3480 MEM(vb = fr_value_box_alloc_null(ctx));
3481 vb_str = talloc_bstrndup(vb, "", 0);
3482
3483 while (p < end) {
3484 q = memmem(p, end - p, pattern, pattern_len);
3485 if (!q) {
3486 MEM(vb_str = talloc_bstr_append(vb, vb_str, p, end - p));
3487 break;
3488 }
3489
3490 if (q > p) MEM(vb_str = talloc_bstr_append(vb, vb_str, p, q - p));
3491 if (rep_len) MEM(vb_str = talloc_bstr_append(vb, vb_str, rep, rep_len));
3492 p = q + pattern_len;
3493 }
3494
3495 if (fr_value_box_bstrdup_buffer_shallow(NULL, vb, NULL, vb_str, false) < 0) {
3496 RPEDEBUG("Failed creating output box");
3497 talloc_free(vb);
3498 return XLAT_ACTION_FAIL;
3499 }
3500
3501 fr_value_box_safety_copy(vb, subject_vb);
3502 fr_value_box_safety_merge(vb, rep_vb);
3503
3505
3506 return XLAT_ACTION_DONE;
3507}
3508
3509/*
3510 * Debug builds only, we don't want to allow unsanitised inputs to crash the server
3511 */
3512#ifndef NDEBUG
3514 { .single = true, .required = true, .type = FR_TYPE_STRING },
3516};
3517
3519 UNUSED xlat_ctx_t const *xctx, request_t *request,
3520 fr_value_box_list_t *args)
3521{
3522 static fr_table_num_sorted_t const signal_table[] = {
3523 { L("break"), SIGTRAP }, /* Save flailing at the keyboard */
3524 { L("BREAK"), SIGTRAP },
3525 { L("SIGABRT"), SIGABRT },
3526 { L("SIGALRM"), SIGALRM },
3527#ifdef SIGBUS
3528 { L("SIGBUS"), SIGBUS },
3529#endif
3530 { L("SIGCHLD"), SIGCHLD },
3531 { L("SIGCONT"), SIGCONT },
3532 { L("SIGFPE"), SIGFPE },
3533 { L("SIGHUP"), SIGHUP },
3534 { L("SIGILL"), SIGILL },
3535 { L("SIGINT"), SIGINT },
3536 { L("SIGKILL"), SIGKILL },
3537 { L("SIGPIPE"), SIGPIPE },
3538#ifdef SIGPOLL
3539 { L("SIGPOLL"), SIGPOLL },
3540#endif
3541 { L("SIGPROF"), SIGPROF },
3542 { L("SIGQUIT"), SIGQUIT },
3543 { L("SIGSEGV"), SIGSEGV },
3544 { L("SIGSTOP"), SIGSTOP },
3545#ifdef SIGSYS
3546 { L("SIGSYS"), SIGSYS },
3547#endif
3548 { L("SIGTERM"), SIGTERM },
3549#ifdef SIGTRAP
3550 { L("SIGTRAP"), SIGTRAP },
3551#endif
3552 { L("SIGTSTP"), SIGTSTP },
3553 { L("SIGTTIN"), SIGTTIN },
3554 { L("SIGTTOU"), SIGTTOU },
3555 { L("SIGURG"), SIGURG },
3556 { L("SIGUSR1"), SIGUSR1 },
3557 { L("SIGUSR2"), SIGUSR2 },
3558 { L("SIGVTALRM"), SIGVTALRM },
3559 { L("SIGXCPU"), SIGXCPU },
3560 { L("SIGXFSZ"), SIGXFSZ }
3561 };
3562 static size_t signal_table_len = NUM_ELEMENTS(signal_table);
3563
3564 fr_value_box_t *signal_vb;
3565 int signal;
3566
3567 XLAT_ARGS(args, &signal_vb);
3568
3569 signal = fr_table_value_by_substr(signal_table, signal_vb->vb_strvalue, signal_vb->vb_length, -1);
3570 if (signal < 0) {
3571 RERROR("Invalid signal \"%pV\"", signal_vb);
3572 return XLAT_ACTION_FAIL;
3573 }
3574 if (raise(signal) < 0) {
3575 RERROR("Failed raising signal %d: %s", signal, strerror(errno));
3576 return XLAT_ACTION_FAIL;
3577 }
3578 return XLAT_ACTION_DONE;
3579}
3580#endif
3581
3583 { .required = false, .single = true, .type = FR_TYPE_STRING },
3585};
3586
3587/** Return the time as a #FR_TYPE_DATE
3588 *
3589 * Note that all operations are UTC.
3590 *
3591@verbatim
3592%time()
3593@endverbatim
3594 *
3595 * Example:
3596@verbatim
3597update reply {
3598 &Reply-Message := "%{%time(now) - %time(request)}"
3599}
3600@endverbatim
3601 *
3602 * @ingroup xlat_functions
3603 */
3605 UNUSED xlat_ctx_t const *xctx,
3606 request_t *request, fr_value_box_list_t *args)
3607{
3608 fr_value_box_t *arg;
3609 fr_value_box_t *vb;
3611
3612 XLAT_ARGS(args, &arg);
3613
3614 if (!arg || (strcmp(arg->vb_strvalue, "now") == 0)) {
3616
3617 } else if (strcmp(arg->vb_strvalue, "request") == 0) {
3618 value = fr_time_to_unix_time(request->packet->timestamp);
3619
3620 } else if (strcmp(arg->vb_strvalue, "offset") == 0) {
3621 MEM(vb = fr_value_box_alloc(ctx, FR_TYPE_TIME_DELTA, NULL));
3622 vb->vb_time_delta = fr_time_gmtoff();
3623 goto append;
3624
3625 } else if (strcmp(arg->vb_strvalue, "dst") == 0) {
3626 MEM(vb = fr_value_box_alloc(ctx, FR_TYPE_BOOL, NULL));
3627 vb->vb_bool = fr_time_is_dst();
3628 goto append;
3629
3630 } else if (strcmp(arg->vb_strvalue, "mday_offset") == 0) {
3631 struct tm tm;
3632 fr_unix_time_t unix_time = fr_time_to_unix_time(request->packet->timestamp);
3633 time_t when = fr_unix_time_to_sec(unix_time);
3634 int64_t nsec;
3635
3636 gmtime_r(&when, &tm);
3637
3638 nsec = (int64_t) 86400 * (tm.tm_mday - 1);
3639 nsec += when % 86400;
3640 nsec *= NSEC;
3641 nsec += fr_unix_time_unwrap(unix_time) % NSEC;
3642
3643 MEM(vb = fr_value_box_alloc(ctx, FR_TYPE_TIME_DELTA, NULL));
3644 vb->vb_time_delta = fr_time_delta_wrap(nsec);
3645 goto append;
3646
3647 } else if (strcmp(arg->vb_strvalue, "wday_offset") == 0) {
3648 struct tm tm;
3649 fr_unix_time_t unix_time = fr_time_to_unix_time(request->packet->timestamp);
3650 time_t when = fr_unix_time_to_sec(unix_time);
3651 int64_t nsec;
3652
3653 gmtime_r(&when, &tm);
3654
3655 nsec = (int64_t) 86400 * tm.tm_wday;
3656 nsec += when % 86400;
3657 nsec *= NSEC;
3658 nsec += fr_unix_time_unwrap(unix_time) % NSEC;
3659
3660 MEM(vb = fr_value_box_alloc(ctx, FR_TYPE_TIME_DELTA, NULL));
3661 vb->vb_time_delta = fr_time_delta_wrap(nsec);
3662
3663 MEM(vb = fr_value_box_alloc(ctx, FR_TYPE_TIME_DELTA, NULL));
3664 vb->vb_time_delta = fr_time_delta_wrap(nsec);
3665 goto append;
3666
3667 } else if (fr_unix_time_from_str(&value, arg->vb_strvalue, FR_TIME_RES_SEC) < 0) {
3668 REDEBUG("Invalid time specification '%s'", arg->vb_strvalue);
3669 return XLAT_ACTION_FAIL;
3670 }
3671
3672 MEM(vb = fr_value_box_alloc(ctx, FR_TYPE_DATE, NULL));
3673 vb->vb_date = value;
3674
3675append:
3677
3678 return XLAT_ACTION_DONE;
3679}
3680
3681/** Return the current time as a #FR_TYPE_DATE
3682 *
3683 * Note that all operations are UTC.
3684 *
3685@verbatim
3686%time.now()
3687@endverbatim
3688 *
3689 * Example:
3690@verbatim
3691update reply {
3692 &Reply-Message := "%{%time.now() - %time.request()}"
3693}
3694@endverbatim
3695 *
3696 * @ingroup xlat_functions
3697 */
3699 UNUSED xlat_ctx_t const *xctx,
3700 UNUSED request_t *request, UNUSED fr_value_box_list_t *args)
3701{
3702 fr_value_box_t *vb;
3703
3704 MEM(vb = fr_value_box_alloc(ctx, FR_TYPE_DATE, NULL));
3705 vb->vb_date = fr_time_to_unix_time(fr_time());
3706
3708
3709 return XLAT_ACTION_DONE;
3710}
3711
3712/** Return the request receive time as a #FR_TYPE_DATE
3713 *
3714 * Note that all operations are UTC.
3715 *
3716@verbatim
3717%time.request()
3718@endverbatim
3719 *
3720 * Example:
3721@verbatim
3722update reply {
3723 &Reply-Message := "%{%time.now() - %time.request()}"
3724}
3725@endverbatim
3726 *
3727 * @ingroup xlat_functions
3728 */
3730 UNUSED xlat_ctx_t const *xctx,
3731 request_t *request, UNUSED fr_value_box_list_t *args)
3732{
3733 fr_value_box_t *vb;
3734
3735 MEM(vb = fr_value_box_alloc(ctx, FR_TYPE_DATE, NULL));
3736 vb->vb_date = fr_time_to_unix_time(request->packet->timestamp);
3737
3739
3740 return XLAT_ACTION_DONE;
3741}
3742
3743
3744/** Return the current time offset from gmt
3745 *
3746 * @ingroup xlat_functions
3747 */
3749 UNUSED xlat_ctx_t const *xctx,
3750 UNUSED request_t *request, UNUSED fr_value_box_list_t *args)
3751{
3752 fr_value_box_t *vb;
3753
3754 MEM(vb = fr_value_box_alloc(ctx, FR_TYPE_TIME_DELTA, NULL));
3755 vb->vb_time_delta = fr_time_gmtoff();
3756
3758
3759 return XLAT_ACTION_DONE;
3760}
3761
3762
3763/** Return whether we are in daylight savings or not
3764 *
3765 * @ingroup xlat_functions
3766 */
3768 UNUSED xlat_ctx_t const *xctx,
3769 UNUSED request_t *request, UNUSED fr_value_box_list_t *args)
3770{
3771 fr_value_box_t *vb;
3772
3773 MEM(vb = fr_value_box_alloc(ctx, FR_TYPE_BOOL, NULL));
3774 vb->vb_bool = fr_time_is_dst();
3775
3777
3778 return XLAT_ACTION_DONE;
3779}
3780
3781
3782/** Change case of a string
3783 *
3784 * If upper is true, change to uppercase, otherwise, change to lowercase
3785 */
3787 UNUSED request_t *request, fr_value_box_list_t *args, bool upper)
3788{
3789 char *p;
3790 char const *end;
3791 fr_value_box_t *vb;
3792
3793 XLAT_ARGS(args, &vb);
3794
3795 p = UNCONST(char *, vb->vb_strvalue);
3796 end = p + vb->vb_length;
3797
3798 while (p < end) {
3799 *(p) = upper ? toupper ((int) *(p)) : tolower((uint8_t) *(p));
3800 p++;
3801 }
3802
3803 fr_value_box_list_remove(args, vb); /* Can't leave it in both lists */
3805
3806 return XLAT_ACTION_DONE;
3807}
3808
3810 { .required = true, .concat = true, .type = FR_TYPE_STRING },
3812};
3813
3814
3815/** Convert a string to lowercase
3816 *
3817 * Example:
3818@verbatim
3819%tolower("Bar") == "bar"
3820@endverbatim
3821 *
3822 * Probably only works for ASCII
3823 *
3824 * @ingroup xlat_functions
3825 */
3827 UNUSED xlat_ctx_t const *xctx,
3828 request_t *request, fr_value_box_list_t *in)
3829{
3830 return xlat_change_case(ctx, out, request, in, false);
3831}
3832
3833
3834/** Convert a string to uppercase
3835 *
3836 * Example:
3837@verbatim
3838%toupper("Foo") == "FOO"
3839@endverbatim
3840 *
3841 * Probably only works for ASCII
3842 *
3843 * @ingroup xlat_functions
3844 */
3846 UNUSED xlat_ctx_t const *xctx,
3847 request_t *request, fr_value_box_list_t *in)
3848{
3849 return xlat_change_case(ctx, out, request, in, true);
3850}
3851
3852
3854 { .required = true, .concat = true, .type = FR_TYPE_STRING },
3856};
3857
3858/** URLencode special characters
3859 *
3860 * Example:
3861@verbatim
3862%urlquote("http://example.org/") == "http%3A%47%47example.org%47"
3863@endverbatim
3864 *
3865 * @ingroup xlat_functions
3866 */
3868 UNUSED xlat_ctx_t const *xctx,
3869 UNUSED request_t *request, fr_value_box_list_t *args)
3870{
3871 char const *p, *end;
3872 char *buff_p;
3873 size_t outlen = 0;
3874 fr_value_box_t *vb;
3875 fr_value_box_t *in_head;
3876
3877 XLAT_ARGS(args, &in_head);
3878
3879 p = in_head->vb_strvalue;
3880 end = p + in_head->vb_length;
3881
3882 /*
3883 * Calculate size of output
3884 */
3885 while (p < end) {
3886 if (isalnum(*p) ||
3887 *p == '-' ||
3888 *p == '_' ||
3889 *p == '.' ||
3890 *p == '~') {
3891 outlen++;
3892 } else {
3893 outlen += 3;
3894 }
3895 p++;
3896 }
3897
3898 MEM(vb = fr_value_box_alloc_null(ctx));
3899 MEM(fr_value_box_bstr_alloc(vb, &buff_p, vb, NULL, outlen, false) == 0);
3900 fr_value_box_safety_copy(vb, in_head);
3901
3902 /* Reset p to start position */
3903 p = in_head->vb_strvalue;
3904
3905 while (p < end) {
3906 if (isalnum(*p)) {
3907 *buff_p++ = *p++;
3908 continue;
3909 }
3910
3911 switch (*p) {
3912 case '-':
3913 case '_':
3914 case '.':
3915 case '~':
3916 *buff_p++ = *p++;
3917 break;
3918
3919 default:
3920 /* MUST be upper case hex to be compliant */
3921 snprintf(buff_p, 4, "%%%02X", (uint8_t) *p++); /* %XX */
3922
3923 buff_p += 3;
3924 }
3925 }
3926
3927 *buff_p = '\0';
3928
3929 // @todo - mark as safe for URL?
3931
3932 return XLAT_ACTION_DONE;
3933}
3934
3935
3937 { .required = true, .concat = true, .type = FR_TYPE_STRING },
3939};
3940
3941/** URLdecode special characters
3942 *
3943 * @note Remember to escape % with %% in strings, else xlat will try to parse it.
3944 *
3945 * Example:
3946@verbatim
3947%urlunquote("http%%3A%%47%%47example.org%%47") == "http://example.org/"
3948@endverbatim
3949 *
3950 * @ingroup xlat_functions
3951 */
3953 UNUSED xlat_ctx_t const *xctx,
3954 request_t *request, fr_value_box_list_t *args)
3955{
3956 char const *p, *end;
3957 char *buff_p;
3958 char *c1, *c2;
3959 size_t outlen = 0;
3960 fr_value_box_t *vb;
3961 fr_value_box_t *in_head;
3962
3963 XLAT_ARGS(args, &in_head);
3964
3965 p = in_head->vb_strvalue;
3966 end = p + in_head->vb_length;
3967
3968 /*
3969 * Calculate size of output
3970 */
3971 while (p < end) {
3972 if (*p == '%') {
3973 p += 3;
3974 } else {
3975 p++;
3976 }
3977 outlen++;
3978 }
3979
3980 MEM(vb = fr_value_box_alloc_null(ctx));
3981 MEM(fr_value_box_bstr_alloc(vb, &buff_p, vb, NULL, outlen, false) == 0);
3982 fr_value_box_safety_copy(vb, in_head);
3983
3984 /* Reset p to start position */
3985 p = in_head->vb_strvalue;
3986
3987 while (p < end) {
3988 if (*p != '%') {
3989 *buff_p++ = *p++;
3990 continue;
3991 }
3992 /* Is a % char */
3993
3994 /* Don't need \0 check, as it won't be in the hextab */
3995 if (!(c1 = memchr(hextab, tolower((uint8_t) *++p), 16)) ||
3996 !(c2 = memchr(hextab, tolower((uint8_t) *++p), 16))) {
3997 REMARKER(in_head->vb_strvalue, p - in_head->vb_strvalue, "Non-hex char in %% sequence");
3998 talloc_free(vb);
3999
4000 return XLAT_ACTION_FAIL;
4001 }
4002 p++;
4003 *buff_p++ = ((c1 - hextab) << 4) + (c2 - hextab);
4004 }
4005
4006 *buff_p = '\0';
4008
4009 return XLAT_ACTION_DONE;
4010}
4011
4016
4017/** Decode any protocol attribute / options
4018 *
4019 * Creates protocol-specific attributes based on the given binary option data
4020 *
4021 * Example:
4022@verbatim
4023%dhcpv4.decode(%{Tmp-Octets-0})
4024@endverbatim
4025 *
4026 * @ingroup xlat_functions
4027 */
4029 xlat_ctx_t const *xctx,
4030 request_t *request, fr_value_box_list_t *in)
4031{
4032 int decoded;
4033 fr_value_box_t *vb;
4034 void *decode_ctx = NULL;
4035 protocol_decode_xlat_uctx_t const *decode_uctx = talloc_get_type_abort(*(void * const *)xctx->inst, protocol_decode_xlat_uctx_t);
4036 fr_test_point_pair_decode_t const *tp_decode = decode_uctx->tp_decode;
4037
4038 if (decode_uctx->dict && decode_uctx->dict != request->proto_dict) {
4039 REDEBUG2("Can't call %%%s() when in %s namespace", xctx->ex->call.func->name,
4040 fr_dict_root(request->proto_dict)->name);
4041 return XLAT_ACTION_FAIL;
4042 }
4043
4044 if (tp_decode->test_ctx) {
4045 if (tp_decode->test_ctx(&decode_ctx, ctx, request->proto_dict) < 0) {
4046 return XLAT_ACTION_FAIL;
4047 }
4048 }
4049
4050 decoded = xlat_decode_value_box_list(request->request_ctx, &request->request_pairs,
4051 request, decode_ctx, tp_decode->func, in);
4052 if (decoded <= 0) {
4053 talloc_free(decode_ctx);
4054 RPERROR("Protocol decoding failed");
4055 return XLAT_ACTION_FAIL;
4056 }
4057
4058 /*
4059 * Create a value box to hold the decoded count, and add
4060 * it to the output list.
4061 */
4062 MEM(vb = fr_value_box_alloc(ctx, FR_TYPE_UINT32, NULL));
4063 vb->vb_uint32 = decoded;
4065
4066 talloc_free(decode_ctx);
4067 return XLAT_ACTION_DONE;
4068}
4069
4071 { .required = true, .single = true, .type = FR_TYPE_IPV4_PREFIX },
4073};
4074
4075/** Calculate the subnet mask from a IPv4 prefix
4076 *
4077 * Example:
4078@verbatim
4079%ip.v4.netmask(%{Network-Prefix})
4080@endverbatim
4081 *
4082 * @ingroup xlat_functions
4083 */
4085 UNUSED request_t *request, fr_value_box_list_t *args)
4086{
4087 fr_value_box_t *subnet, *vb;
4088 XLAT_ARGS(args, &subnet);
4089
4090 MEM(vb = fr_value_box_alloc(ctx, FR_TYPE_IPV4_ADDR, NULL));
4091 vb->vb_ip.addr.v4.s_addr = htonl((uint32_t)0xffffffff << (32 - subnet->vb_ip.prefix));
4093
4094 return XLAT_ACTION_DONE;
4095}
4096
4097/** Calculate the broadcast address from a IPv4 prefix
4098 *
4099 * Example:
4100@verbatim
4101%ip.v4.broadcast(%{Network-Prefix})
4102@endverbatim
4103 *
4104 * @ingroup xlat_functions
4105 */
4107 UNUSED request_t *request, fr_value_box_list_t *args)
4108{
4109 fr_value_box_t *subnet, *vb;
4110 XLAT_ARGS(args, &subnet);
4111
4112 MEM(vb = fr_value_box_alloc(ctx, FR_TYPE_IPV4_ADDR, NULL));
4113 vb->vb_ip.addr.v4.s_addr = htonl( ntohl(subnet->vb_ip.addr.v4.s_addr) | (uint32_t)0xffffffff >> subnet->vb_ip.prefix);
4115
4116 return XLAT_ACTION_DONE;
4117}
4118
4120{
4121 *(void **) mctx->inst = mctx->uctx;
4122 return 0;
4123}
4124
4125/** Encode protocol attributes / options
4126 *
4127 * Returns octet string created from the provided pairs
4128 *
4129 * Example:
4130@verbatim
4131%dhcpv4.encode(&request[*])
4132@endverbatim
4133 *
4134 * @ingroup xlat_functions
4135 */
4137 xlat_ctx_t const *xctx,
4138 request_t *request, fr_value_box_list_t *args)
4139{
4140 fr_pair_t *vp;
4141 fr_dcursor_t *cursor;
4142 bool tainted = false;
4143 fr_value_box_t *encoded;
4144
4145 uint8_t binbuf[2048];
4146 uint8_t *p = binbuf, *end = p + sizeof(binbuf);
4147 ssize_t len = 0;
4148 fr_value_box_t *in_head;
4149 void *encode_ctx = NULL;
4150 fr_test_point_pair_encode_t const *tp_encode;
4151
4152 XLAT_ARGS(args, &in_head);
4153
4154 memcpy(&tp_encode, xctx->inst, sizeof(tp_encode)); /* const issues */
4155
4156 cursor = fr_value_box_get_cursor(in_head);
4157
4158 /*
4159 * Create the encoding context.
4160 */
4161 if (tp_encode->test_ctx) {
4162 if (tp_encode->test_ctx(&encode_ctx, cursor, request->proto_dict) < 0) {
4163 return XLAT_ACTION_FAIL;
4164 }
4165 }
4166
4167 /*
4168 * Loop over the attributes, encoding them.
4169 */
4170 for (vp = fr_dcursor_current(cursor);
4171 vp != NULL;
4172 vp = fr_dcursor_next(cursor)) {
4173 if (vp->da->flags.internal) continue;
4174
4175 /*
4176 * Don't check the dictionaries. By definition,
4177 * vp->da->dict==request->proto_dict, OR else we're
4178 * using the internal encoder and encoding a real
4179 * protocol.
4180 *
4181 * However, we likely still want a
4182 * dictionary-specific "is encodable" function,
4183 * as AKA/SIM and DHCPv6 encode "bool"s only if
4184 * their value is true.
4185 */
4186
4187 len = tp_encode->func(&FR_DBUFF_TMP(p, end), cursor, encode_ctx);
4188 if (len < 0) {
4189 RPEDEBUG("Protocol encoding failed");
4190 return XLAT_ACTION_FAIL;
4191 }
4192
4193 tainted |= vp->vp_tainted;
4194 p += len;
4195 }
4196
4197 /*
4198 * Pass the options string back to the caller.
4199 */
4200 MEM(encoded = fr_value_box_alloc_null(ctx));
4201 fr_value_box_memdup(encoded, encoded, NULL, binbuf, (size_t)len, tainted);
4202 fr_dcursor_append(out, encoded);
4203
4204 return XLAT_ACTION_DONE;
4205}
4206
4207static int xlat_protocol_register_by_name(dl_t *dl, char const *name, fr_dict_t const *dict)
4208{
4209 fr_test_point_pair_decode_t *tp_decode;
4210 fr_test_point_pair_encode_t *tp_encode;
4211 protocol_decode_xlat_uctx_t *decode_uctx;
4212 xlat_t *xlat;
4213 char buffer[256+32];
4214
4215 /*
4216 * See if there's a decode function for it.
4217 */
4218 snprintf(buffer, sizeof(buffer), "%s_tp_decode_pair", name);
4219 tp_decode = dlsym(dl->handle, buffer);
4220 if (tp_decode) {
4221 snprintf(buffer, sizeof(buffer), "%s.decode", name);
4222
4223 /* May be called multiple times, so just skip protocols we've already registered */
4224 if (xlat_func_find(buffer, -1)) return 1;
4225
4226 if (unlikely((xlat = xlat_func_register(NULL, buffer, protocol_decode_xlat, FR_TYPE_UINT32)) == NULL)) return -1;
4228 decode_uctx = talloc(xlat, protocol_decode_xlat_uctx_t);
4229 decode_uctx->tp_decode = tp_decode;
4230 decode_uctx->dict = dict;
4231 /* coverity[suspicious_sizeof] */
4234 }
4235
4236 /*
4237 * See if there's an encode function for it.
4238 */
4239 snprintf(buffer, sizeof(buffer), "%s_tp_encode_pair", name);
4240 tp_encode = dlsym(dl->handle, buffer);
4241 if (tp_encode) {
4242 snprintf(buffer, sizeof(buffer), "%s.encode", name);
4243
4244 if (xlat_func_find(buffer, -1)) return 1;
4245
4246 if (unlikely((xlat = xlat_func_register(NULL, buffer, protocol_encode_xlat, FR_TYPE_OCTETS)) == NULL)) return -1;
4248 /* coverity[suspicious_sizeof] */
4251 }
4252
4253 return 0;
4254}
4255
4256static int xlat_protocol_register(fr_dict_t const *dict)
4257{
4258 dl_t *dl = fr_dict_dl(dict);
4259 char *p, name[256];
4260
4261 /*
4262 * No library for this protocol, skip it.
4263 *
4264 * Protocol TEST has no libfreeradius-test, so that's OK.
4265 */
4266 if (!dl) return 0;
4267
4268 strlcpy(name, fr_dict_root(dict)->name, sizeof(name));
4269 for (p = name; *p != '\0'; p++) {
4270 *p = tolower((uint8_t) *p);
4271 }
4272
4273 return xlat_protocol_register_by_name(dl, name, dict != fr_dict_internal() ? dict : NULL);
4274}
4275
4277
4279{
4280 dl_t *dl;
4281
4282 cbor_loader = dl_loader_init(NULL, NULL, false, false);
4283 if (!cbor_loader) return 0;
4284
4285 dl = dl_by_name(cbor_loader, "libfreeradius-cbor", NULL, false);
4286 if (!dl) return 0;
4287
4288 if (xlat_protocol_register_by_name(dl, "cbor", NULL) < 0) return -1;
4289
4290 return 0;
4291}
4292
4293
4294/** Register xlats for any loaded dictionaries
4295 */
4297{
4298 fr_dict_t *dict;
4300
4301 for (dict = fr_dict_global_ctx_iter_init(&iter);
4302 dict != NULL;
4303 dict = fr_dict_global_ctx_iter_next(&iter)) {
4304 if (xlat_protocol_register(dict) < 0) return -1;
4305 }
4306
4307 /*
4308 * And the internal protocol, too.
4309 */
4310 if (xlat_protocol_register(fr_dict_internal()) < 0) return -1;
4311
4312 /*
4313 * And cbor stuff
4314 */
4315 if (xlat_protocol_register_cbor() < 0) return -1;
4316
4317 return 0;
4318}
4319
4320/** De-register all xlat functions we created
4321 *
4322 */
4323static int _xlat_global_free(UNUSED void *uctx)
4324{
4325 TALLOC_FREE(xlat_ctx);
4329
4330 return 0;
4331}
4332
4333/** Global initialisation for xlat
4334 *
4335 * @note Free memory with #xlat_free
4336 *
4337 * @return
4338 * - 0 on success.
4339 * - -1 on failure.
4340 *
4341 * @hidecallgraph
4342 */
4343static int _xlat_global_init(UNUSED void *uctx)
4344{
4345 xlat_t *xlat;
4346
4347 xlat_ctx = talloc_init("xlat");
4348 if (!xlat_ctx) return -1;
4349
4350 if (xlat_func_init() < 0) return -1;
4351
4352 /*
4353 * Lookup attributes used by virtual xlat expansions.
4354 */
4355 if (xlat_eval_init() < 0) return -1;
4356
4357 /*
4358 * Registers async xlat operations in the `unlang` interpreter.
4359 */
4361
4362 /*
4363 * These are all "pure" functions.
4364 */
4365#define XLAT_REGISTER_ARGS(_xlat, _func, _return_type, _args) \
4366do { \
4367 if (unlikely((xlat = xlat_func_register(xlat_ctx, _xlat, _func, _return_type)) == NULL)) return -1; \
4368 xlat_func_args_set(xlat, _args); \
4369 xlat_func_flags_set(xlat, XLAT_FUNC_FLAG_PURE | XLAT_FUNC_FLAG_INTERNAL); \
4370} while (0)
4371
4372#define XLAT_NEW(_x) xlat->replaced_with = _x
4373
4375
4378 XLAT_NEW("str.concat");
4379
4382 XLAT_NEW("str.split");
4383
4385
4388 XLAT_NEW("hmac.md5");
4389
4392 XLAT_NEW("hmac.sha1");
4393
4395 xlat->deprecated = true;
4396
4399 xlat->deprecated = true;
4400
4402
4405 XLAT_NEW("str.lpad");
4406
4409 XLAT_NEW("str.rpad");
4410
4413 XLAT_NEW("str.substr");
4414
4417
4418 /*
4419 * The inputs to these functions are variable.
4420 */
4421#undef XLAT_REGISTER_ARGS
4422#define XLAT_REGISTER_ARGS(_xlat, _func, _return_type, _args) \
4423do { \
4424 if (unlikely((xlat = xlat_func_register(xlat_ctx, _xlat, _func, _return_type)) == NULL)) return -1; \
4425 xlat_func_args_set(xlat, _args); \
4426 xlat_func_flags_set(xlat, XLAT_FUNC_FLAG_INTERNAL); \
4427} while (0)
4428
4429#undef XLAT_REGISTER_VOID
4430#define XLAT_REGISTER_VOID(_xlat, _func, _return_type) \
4431do { \
4432 if (unlikely((xlat = xlat_func_register(xlat_ctx, _xlat, _func, _return_type)) == NULL)) return -1; \
4433 xlat_func_flags_set(xlat, XLAT_FUNC_FLAG_INTERNAL); \
4434} while (0)
4435
4438 XLAT_NEW("pairs.debug");
4439
4446
4448 XLAT_NEW("pairs.immutable");
4450
4456
4458 XLAT_NEW("time.next");
4460
4462 XLAT_NEW("pairs.print");
4464
4466
4468#ifdef HAVE_REGEX_PCRE2
4469 xlat_func_instantiate_set(xlat, xlat_instantiate_subst_regex, xlat_subst_regex_inst_t, NULL, NULL);
4470#endif
4472 XLAT_NEW("str.subst");
4473#ifdef HAVE_REGEX_PCRE2
4474 xlat_func_instantiate_set(xlat, xlat_instantiate_subst_regex, xlat_subst_regex_inst_t, NULL, NULL);
4475#endif
4476
4477#ifndef NDEBUG
4479#endif
4480
4486
4490
4493 XLAT_NEW("str.rand");
4494
4496
4497 if (unlikely((xlat = xlat_func_register(xlat_ctx, "untaint", xlat_func_untaint, FR_TYPE_VOID)) == NULL)) return -1;
4500
4501 if (unlikely((xlat = xlat_func_register(xlat_ctx, "taint", xlat_func_taint, FR_TYPE_VOID)) == NULL)) return -1;
4504
4505 /*
4506 * All of these functions are pure.
4507 */
4508#define XLAT_REGISTER_PURE(_xlat, _func, _return_type, _arg) \
4509do { \
4510 if (unlikely((xlat = xlat_func_register(xlat_ctx, _xlat, _func, _return_type)) == NULL)) return -1; \
4511 xlat_func_args_set(xlat, _arg); \
4512 xlat_func_flags_set(xlat, XLAT_FUNC_FLAG_PURE | XLAT_FUNC_FLAG_INTERNAL); \
4513} while (0)
4514
4520 XLAT_NEW("hash.md4");
4521
4524 XLAT_NEW("hash.md4");
4525
4526 if (unlikely((xlat = xlat_func_register(xlat_ctx, "regex.match", xlat_func_regex, FR_TYPE_STRING)) == NULL)) return -1;
4529 if (unlikely((xlat = xlat_func_register(xlat_ctx, "regex", xlat_func_regex, FR_TYPE_STRING)) == NULL)) return -1;
4532 XLAT_NEW("regex.match");
4533
4534 {
4535 static xlat_arg_parser_t const xlat_regex_safe_args[] = {
4536 { .type = FR_TYPE_STRING, .variadic = true, .concat = true },
4538 };
4539
4540 static xlat_arg_parser_t const xlat_regex_escape_args[] = {
4541 { .type = FR_TYPE_STRING,
4542 .func = regex_xlat_escape, .safe_for = FR_REGEX_SAFE_FOR, .always_escape = true,
4543 .variadic = true, .concat = true },
4545 };
4546
4547 if (unlikely((xlat = xlat_func_register(xlat_ctx, "regex.safe",
4548 xlat_transparent, FR_TYPE_STRING)) == NULL)) return -1;
4550 xlat_func_args_set(xlat, xlat_regex_safe_args);
4551 xlat_func_safe_for_set(xlat, FR_REGEX_SAFE_FOR);
4552
4553 if (unlikely((xlat = xlat_func_register(xlat_ctx, "regex.escape",
4554 xlat_transparent, FR_TYPE_STRING)) == NULL)) return -1;
4556 xlat_func_args_set(xlat, xlat_regex_escape_args);
4557 xlat_func_safe_for_set(xlat, FR_REGEX_SAFE_FOR);
4558 }
4559
4560#define XLAT_REGISTER_HASH(_name, _func) do { \
4561 XLAT_REGISTER_PURE("hash." _name, _func, FR_TYPE_OCTETS, xlat_func_sha_arg); \
4562 XLAT_REGISTER_PURE(_name, _func, FR_TYPE_OCTETS, xlat_func_sha_arg); \
4563 XLAT_NEW("hash." _name); \
4564 } while (0)
4565
4567
4568#ifdef HAVE_OPENSSL_EVP_H
4569 XLAT_REGISTER_HASH("sha2_224", xlat_func_sha2_224);
4570 XLAT_REGISTER_HASH("sha2_256", xlat_func_sha2_256);
4571 XLAT_REGISTER_HASH("sha2_384", xlat_func_sha2_384);
4572 XLAT_REGISTER_HASH("sha2_512", xlat_func_sha2_512);
4573
4574# ifdef HAVE_EVP_BLAKE2S256
4575 XLAT_REGISTER_HASH("blake2s_256", xlat_func_blake2s_256);
4576# endif
4577# ifdef HAVE_EVP_BLAKE2B512
4578 XLAT_REGISTER_HASH("blake2b_512", xlat_func_blake2b_512);
4579# endif
4580
4581 XLAT_REGISTER_HASH("sha3_224", xlat_func_sha3_224);
4582 XLAT_REGISTER_HASH("sha3_256", xlat_func_sha3_256);
4583 XLAT_REGISTER_HASH("sha3_384", xlat_func_sha3_384);
4584 XLAT_REGISTER_HASH("sha3_512", xlat_func_sha3_512);
4585#endif
4586
4588 xlat->deprecated = true;
4590 XLAT_NEW("length");
4591
4594
4597 XLAT_NEW("str.lower");
4598
4601 XLAT_NEW("str.upper");
4602
4605 XLAT_NEW("url.quote");
4606
4609 XLAT_NEW("url.unquote");
4610
4612
4614}
4615
4617{
4618 int ret;
4619 fr_atexit_global_once_ret(&ret, _xlat_global_init, _xlat_global_free, NULL);
4620 return ret;
4621}
static int const char char buffer[256]
Definition acutest.h:576
int const char * file
Definition acutest.h:702
va_list args
Definition acutest.h:770
#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:485
#define L(_str)
Helper for initialising arrays of string literals.
Definition build.h:209
#define unlikely(_x)
Definition build.h:383
#define UNUSED
Definition build.h:317
#define NUM_ELEMENTS(_t)
Definition build.h:339
#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:290
static int fr_dcursor_append(fr_dcursor_t *cursor, void *v)
Insert a single item at the end of the list.
Definition dcursor.h:408
static void * fr_dcursor_current(fr_dcursor_t *cursor)
Return the item the cursor current points to.
Definition dcursor.h:339
#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:4620
char const * name
Vendor name.
Definition dict.h:256
static fr_slen_t err
Definition dict.h:841
fr_dict_t * fr_dict_global_ctx_iter_init(fr_dict_global_ctx_iter_t *iter)
Iterate protocols by name.
Definition dict_util.c:4613
fr_dict_attr_t const * fr_dict_root(fr_dict_t const *dict)
Return the root attribute of a dictionary.
Definition dict_util.c:2403
dl_t * fr_dict_dl(fr_dict_t const *dict)
Definition dict_util.c:2413
uint32_t pen
Private enterprise number.
Definition dict.h:252
fr_dict_t const * fr_dict_internal(void)
Definition dict_util.c:4656
static fr_slen_t in
Definition dict.h:841
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:2638
Private enterprise.
Definition dict.h:251
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:70
static xlat_action_t xlat_func_time_now(TALLOC_CTX *ctx, fr_dcursor_t *out, UNUSED xlat_ctx_t const *xctx, UNUSED request_t *request, UNUSED fr_value_box_list_t *args)
Return the current time as a FR_TYPE_DATE.
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_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_pairs_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)
Print out attribute info.
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_pairs_print(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_time_request(TALLOC_CTX *ctx, fr_dcursor_t *out, UNUSED xlat_ctx_t const *xctx, request_t *request, UNUSED fr_value_box_list_t *args)
Return the request receive time as a FR_TYPE_DATE.
static xlat_action_t xlat_func_regex(TALLOC_CTX *ctx, fr_dcursor_t *out, UNUSED xlat_ctx_t const *xctx, request_t *request, fr_value_box_list_t *in)
Get named subcapture value from previous regex.
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_eval(TALLOC_CTX *ctx, fr_dcursor_t *out, UNUSED 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_time_is_dst(TALLOC_CTX *ctx, fr_dcursor_t *out, UNUSED xlat_ctx_t const *xctx, UNUSED request_t *request, UNUSED fr_value_box_list_t *args)
Return whether we are in daylight savings or not.
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_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_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_time_offset(TALLOC_CTX *ctx, fr_dcursor_t *out, UNUSED xlat_ctx_t const *xctx, UNUSED request_t *request, UNUSED fr_value_box_list_t *args)
Return the current time offset from gmt.
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:1657
fr_event_list_t * unlang_interpret_event_list(request_t *request)
Get the event list for the current interpreter.
Definition interpret.c:2009
#define UNLANG_SUB_FRAME
Definition interpret.h:37
fr_log_t * log_dst_by_name(char const *name)
Get a logging destination by name.
Definition log.c:1072
#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 RERROR(fmt,...)
Definition log.h:298
#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:1593
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:1873
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:1428
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
#define MD5_DIGEST_LENGTH
unsigned short uint16_t
fr_type_t
@ FR_TYPE_TIME_DELTA
A period of time measured in nanoseconds.
@ FR_TYPE_FLOAT32
Single precision floating point.
@ 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_INT16
16 Bit signed integer.
@ FR_TYPE_DATE
Unix time stamp, always has value >2^31.
@ FR_TYPE_COMBO_IP_PREFIX
IPv4 or IPv6 address prefix depending on length.
@ 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_COMBO_IP_ADDR
IPv4 or IPv6 address depending on length.
@ FR_TYPE_IFID
Interface ID.
@ FR_TYPE_OCTETS
Raw octets.
@ FR_TYPE_GROUP
A grouping of other attributes.
@ FR_TYPE_FLOAT64
Double precision floating point.
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:370
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:81
#define REDEBUG(fmt,...)
Definition radclient.h:52
#define RDEBUG_ENABLED2()
Definition radclient.h:50
#define RDEBUG(fmt,...)
Definition radclient.h:53
#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:43
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:92
#define RAD_REQUEST_LVL_NONE
No debug messages should be printed.
Definition request.h:312
static char const * name
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:2025
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:1989
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:1482
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:2121
#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(_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_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:634
static fr_dict_attr_t const * tmpl_list(tmpl_t const *vpt)
Definition tmpl.h:904
@ TMPL_TYPE_ATTR
Reference to one or more attributes.
Definition tmpl.h:142
@ TMPL_TYPE_XLAT
Pre-parsed xlat expansion.
Definition tmpl.h:146
@ TMPL_TYPE_EXEC
Callout to an external script or program.
Definition tmpl.h:150
@ TMPL_TYPE_REGEX_XLAT_UNRESOLVED
A regular expression with unresolved xlat functions or attribute references.
Definition tmpl.h:197
@ TMPL_TYPE_DATA
Value in native boxed format.
Definition tmpl.h:138
@ TMPL_TYPE_DATA_UNRESOLVED
Unparsed literal string.
Definition tmpl.h:179
static fr_slen_t vpt
Definition tmpl.h:1269
tmpl_attr_rules_t attr
Rules/data for parsing attribute references.
Definition tmpl.h:335
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:915
Optional arguments passed to vp_tmpl functions.
Definition tmpl.h:332
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()
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:273
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
#define fr_table_value_by_substr(_table, _name, _name_len, _def)
Convert a partial string to a value using an ordered or sorted table.
Definition table.h:693
An element in an arbitrarily ordered array of name to num mappings.
Definition table.h:57
An element in a lexicographically sorted array of name to num mappings.
Definition table.h:49
char * talloc_bstrndup(TALLOC_CTX *ctx, char const *in, size_t inlen)
Binary safe strndup function.
Definition talloc.c:586
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:614
#define talloc_get_type_abort_const
Definition talloc.h:287
fr_test_point_ctx_alloc_t test_ctx
Allocate a test ctx for the encoder.
Definition test_point.h:85
fr_test_point_ctx_alloc_t test_ctx
Allocate a test ctx for the encoder.
Definition test_point.h:93
fr_pair_decode_t func
Decoder for pairs.
Definition test_point.h:86
fr_pair_encode_t func
Encoder for pairs.
Definition test_point.h:94
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:1207
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:794
fr_time_delta_t fr_time_gmtoff(void)
Get the offset to gmt.
Definition time.c:1199
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
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:548
int unlang_xlat_push(TALLOC_CTX *ctx, unlang_result_t *p_result, 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:270
void unlang_xlat_init(void)
Register xlat operation with the interpreter.
Definition xlat.c:810
fr_type_t type
Type to cast argument to.
Definition xlat.h:155
uint8_t single
Argument must only contain a single box.
Definition xlat.h:148
bool xlat_is_literal(xlat_exp_head_t const *head)
Check to see if the expansion consists entirely of value-box elements.
#define XLAT_ARG_PARSER_CURSOR
Definition xlat.h:162
@ XLAT_ARG_VARIADIC_EMPTY_KEEP
Empty argument groups are left alone, and either passed through as empty groups or null boxes.
Definition xlat.h:137
@ XLAT_ARG_VARIADIC_EMPTY_SQUASH
Empty argument groups are removed.
Definition xlat.h:136
xlat_arg_parser_variadic_t variadic
All additional boxes should be processed using this definition.
Definition xlat.h:153
#define XLAT_RESULT_SUCCESS(_p_result)
Definition xlat.h:503
uint8_t required
Argument must be present, and non-empty.
Definition xlat.h:146
#define XLAT_ARGS(_list,...)
Populate local variables with value boxes from the input list.
Definition xlat.h:383
uint8_t concat
Concat boxes together.
Definition xlat.h:147
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:170
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:3135
Definition for a single argument consumend by an xlat function.
Definition xlat.h:145
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:69
static void fr_pair_set_immutable(fr_pair_t *vp)
Definition pair.h:684
void fr_strerror_clear(void)
Clears all pending messages from the talloc pools.
Definition strerror.c:576
#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:86
@ FR_TYPE_ATTR
A contains an attribute reference.
Definition types.h:83
#define FR_TYPE_NON_LEAF
Definition types.h:316
#define fr_type_is_string(_x)
Definition types.h:346
#define fr_type_is_numeric(_x)
Definition types.h:380
#define FR_TYPE_STRUCTURAL
Definition types.h:314
#define fr_type_is_null(_x)
Definition types.h:345
#define fr_type_is_leaf(_x)
Definition types.h:391
static char const * fr_type_to_str(fr_type_t type)
Return a static string containing the type name.
Definition types.h:452
#define FR_TYPE_LEAF
Definition types.h:315
#define FR_TYPE_NUMERIC
Definition types.h:304
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:1428
void fr_value_box_mark_unsafe(fr_value_box_t *vb)
Mark a value-box as "unsafe".
Definition value.c:6804
ssize_t fr_value_box_list_concat_as_string(fr_value_box_t *safety, 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:6020
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:5754
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:4742
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:3741
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:6505
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:4775
void fr_value_box_list_untaint(fr_value_box_list_t *head)
Untaint every list member (and their children)
Definition value.c:6702
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:3984
void fr_value_box_clear_value(fr_value_box_t *data)
Clear/free any existing value.
Definition value.c:4093
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:4377
void fr_value_box_safety_copy_changed(fr_value_box_t *out, fr_value_box_t const *in)
Copy the safety values from one box to another.
Definition value.c:6847
void fr_value_box_safety_merge(fr_value_box_t *out, fr_value_box_t const *in)
Merge safety results.
Definition value.c:6856
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:4486
void fr_value_box_safety_copy(fr_value_box_t *out, fr_value_box_t const *in)
Copy the safety values from one box to another.
Definition value.c:6834
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:4521
void fr_value_box_clear(fr_value_box_t *data)
Clear/free any existing value and metadata.
Definition value.c:4139
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:6671
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:4554
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:4598
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:4703
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:4839
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:6236
@ FR_VALUE_BOX_LIST_FREE
Definition value.h:239
@ FR_VALUE_BOX_LIST_FREE_BOX
Free each processed box.
Definition value.h:236
#define fr_value_box_list_foreach_safe(_list_head, _iter)
Definition value.h:225
#define fr_value_box_alloc(_ctx, _type, _enumv)
Allocate a value box of a specific type.
Definition value.h:643
#define fr_value_box_mark_safe_for(_box, _safe_for)
Definition value.h:1074
static fr_slen_t data
Definition value.h:1291
#define fr_value_box_is_safe_for(_box, _safe_for)
Definition value.h:1081
#define fr_box_is_variable_size(_x)
Definition value.h:463
#define fr_value_box_get_cursor(_dst)
Definition value.h:1216
#define VALUE_BOX_VERIFY(_x)
Definition value.h:1321
#define VALUE_BOX_LIST_VERIFY(_x)
Definition value.h:1322
int nonnull(2, 5))
#define fr_value_box_alloc_null(_ctx)
Allocate a value box for later use with a value assignment function.
Definition value.h:654
#define fr_value_box_list_foreach(_list_head, _iter)
Definition value.h:224
static size_t char ** out
Definition value.h:1023
#define fr_box_bool(_val)
Definition value.h:330
#define FR_VALUE_BOX_SAFE_FOR_ANY
Definition value.h:173
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_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 *args)
#define XLAT_REGISTER_VOID(_xlat, _func, _return_type)
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[]
unlang_result_t last_result
static int _log_dst_free(fr_log_t *log)
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_signal_raise_args[]
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 from input list to output list.
static xlat_arg_parser_t const xlat_func_hex_arg[]
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_file_head(TALLOC_CTX *ctx, fr_dcursor_t *out, UNUSED xlat_ctx_t const *xctx, 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 xlat_arg_parser_t const xlat_pair_cursor_args[]
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 *args)
static void ungroup(fr_dcursor_t *out, fr_value_box_list_t *in)
static xlat_arg_parser_t const xlat_func_md4_arg[]
static int regex_xlat_escape(UNUSED request_t *request, fr_value_box_t *vb, UNUSED void *uctx)
static xlat_arg_parser_t const xlat_func_join_args[]
#define XLAT_NEW(_x)
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)
#define XLAT_REGISTER_HASH(_name, _func)
static xlat_arg_parser_t const xlat_func_debug_args[]
static char const hextab[]
#define FR_FILENAME_SAFE_FOR
static xlat_action_t xlat_func_signal_raise(UNUSED TALLOC_CTX *ctx, UNUSED fr_dcursor_t *out, UNUSED xlat_ctx_t const *xctx, request_t *request, fr_value_box_list_t *args)
static xlat_arg_parser_t const xlat_func_pad_args[]
fr_dict_t const * dict
Restrict xlat to this namespace.
static xlat_arg_parser_t const xlat_func_urlunquote_arg[]
fr_test_point_pair_decode_t * tp_decode
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 int filename_xlat_escape(UNUSED request_t *request, fr_value_box_t *vb, UNUSED void *uctx)
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)
static xlat_action_t xlat_func_file_cat(TALLOC_CTX *ctx, fr_dcursor_t *out, UNUSED xlat_ctx_t const *xctx, request_t *request, fr_value_box_list_t *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 *args)
xlat_exp_head_t * ex
static xlat_arg_parser_t const xlat_func_length_args[]
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 xlat_protocol_register_by_name(dl_t *dl, char const *name, fr_dict_t const *dict)
static xlat_arg_parser_t const xlat_func_file_cat_args[]
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_arg_parser_t const xlat_func_regex_args[]
void * rctx
Resume context.
Definition xlat_ctx.h:54
xlat_exp_t const * ex
Tokenized expression.
Definition xlat_ctx.h:55
xlat_exp_t * ex
Tokenized expression to use in expansion.
Definition xlat_ctx.h:64
void const * inst
xlat instance data.
Definition xlat_ctx.h:50
void * uctx
Passed to the registration function.
Definition xlat_ctx.h:66
void * inst
xlat instance data to populate.
Definition xlat_ctx.h:63
An xlat calling ctx.
Definition xlat_ctx.h:49
An xlat instantiation ctx.
Definition xlat_ctx.h:62
fr_dict_attr_t const * xlat_time_res_attr(char const *res)
Definition xlat_eval.c:129
int xlat_eval_init(void)
Definition xlat_eval.c:1958
void xlat_eval_free(void)
Definition xlat_eval.c:1983
int xlat_register_expressions(void)
Definition xlat_expr.c:1858
void xlat_func_free(void)
Definition xlat_func.c:563
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:399
int xlat_func_args_set(xlat_t *x, xlat_arg_parser_t const args[])
Register the arguments of an xlat.
Definition xlat_func.c:363
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:216
int xlat_func_init(void)
Definition xlat_func.c:547
xlat_t * xlat_func_find(char const *in, ssize_t inlen)
Definition xlat_func.c:77
#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:116
bool deprecated
this function was deprecated
Definition xlat_priv.h:68
xlat_type_t _CONST type
type of this expansion.
Definition xlat_priv.h:155
An xlat expansion node.
Definition xlat_priv.h:148