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