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