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