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