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: 76dd746c0634cfebc3b3f33147fc2f8a961134bf $
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 */
26RCSID("$Id: 76dd746c0634cfebc3b3f33147fc2f8a961134bf $")
27
28/**
29 * @defgroup xlat_functions xlat expansion functions
30 */
31#include <freeradius-devel/server/base.h>
32#include <freeradius-devel/server/tmpl_dcursor.h>
33#include <freeradius-devel/unlang/interpret.h>
34#include <freeradius-devel/unlang/xlat_priv.h>
35
36#include <freeradius-devel/unlang/xlat.h>
37#include <freeradius-devel/io/test_point.h>
38
39#include <freeradius-devel/util/base16.h>
40#include <freeradius-devel/util/dbuff.h>
41#include <freeradius-devel/util/dcursor.h>
42#include <freeradius-devel/util/pair.h>
43#include <freeradius-devel/util/table.h>
44
45#ifdef HAVE_OPENSSL_EVP_H
46# include <freeradius-devel/tls/openssl_user_macros.h>
47# include <openssl/evp.h>
48#endif
49
50#include <sys/stat.h>
51#include <fcntl.h>
52
53static char const hextab[] = "0123456789abcdef";
54static TALLOC_CTX *xlat_ctx;
55
56typedef struct {
58 fr_dict_t const *dict; //!< Restrict xlat to this namespace
60
61/** Copy an argument from the input list to the output cursor.
62 *
63 * For now we just move it. This utility function will let us have
64 * value-box cursors as input arguments.
65 *
66 * @param[in] ctx talloc ctx
67 * @param[out] out where the value-box will be stored
68 * @param[in] in input value-box list
69 * @param[in] vb the argument to copy
70 */
71void xlat_arg_copy_out(TALLOC_CTX *ctx, fr_dcursor_t *out, fr_value_box_list_t *in, fr_value_box_t *vb)
72{
73 fr_value_box_list_remove(in, vb);
74 if (talloc_parent(vb) != ctx) {
75 (void) talloc_steal(ctx, vb);
76 }
78}
79
80/*
81 * Regular xlat functions
82 */
84 { .single = true, .type = FR_TYPE_INT8 },
86};
87
88/** Dynamically change the debugging level for the current request
89 *
90 * Example:
91@verbatim
92%debug(3)
93@endverbatim
94 *
95 * @ingroup xlat_functions
96 */
98 UNUSED xlat_ctx_t const *xctx,
99 request_t *request, fr_value_box_list_t *args)
100{
101 int level = 0;
102 fr_value_box_t *vb, *lvl_vb;
103
104 XLAT_ARGS(args, &lvl_vb);
105
106 /*
107 * Expand to previous (or current) level
108 */
109 MEM(vb = fr_value_box_alloc(ctx, FR_TYPE_INT8, NULL));
110 vb->vb_int8 = request->log.lvl;
112
113 /*
114 * Assume we just want to get the current value and NOT set it to 0
115 */
116 if (!lvl_vb) goto done;
117
118 level = lvl_vb->vb_int8;
119 if (level == 0) {
120 request->log.lvl = RAD_REQUEST_LVL_NONE;
121 } else {
122 if (level > L_DBG_LVL_MAX) level = L_DBG_LVL_MAX;
123 request->log.lvl = level;
124 }
125
126done:
127 return XLAT_ACTION_DONE;
128}
129
130
131static void xlat_debug_attr_vp(request_t *request, fr_pair_t const *vp,
132 fr_dict_attr_t const *da);
133
134static void xlat_debug_attr_list(request_t *request, fr_pair_list_t const *list,
135 fr_dict_attr_t const *parent)
136{
137 fr_pair_t *vp;
138
139 for (vp = fr_pair_list_next(list, NULL);
140 vp != NULL;
141 vp = fr_pair_list_next(list, vp)) {
142 xlat_debug_attr_vp(request, vp, parent);
143 }
144}
145
146
151
152static void xlat_debug_attr_vp(request_t *request, fr_pair_t const *vp,
153 fr_dict_attr_t const *parent)
154{
155 fr_dict_vendor_t const *vendor;
157 size_t i;
158 ssize_t slen;
159 fr_sbuff_t sbuff;
160 char buffer[1024];
161
162 sbuff = FR_SBUFF_OUT(buffer, sizeof(buffer));
163
164 /*
165 * Squash the names down if necessary.
166 */
167 if (!RDEBUG_ENABLED3) {
168 slen = fr_pair_print_name(&sbuff, parent, &vp);
169 } else {
170 slen = fr_sbuff_in_sprintf(&sbuff, "%s %s ", vp->da->name, fr_tokens[vp->op]);
171 }
172 if (slen <= 0) return;
173
174 switch (vp->vp_type) {
176 RIDEBUG2("%s{", buffer);
177 RINDENT();
178 xlat_debug_attr_list(request, &vp->vp_group, vp->da);
179 REXDENT();
180 RIDEBUG2("}");
181 break;
182
183 default:
184 RIDEBUG2("%s%pV", buffer, &vp->data);
185 }
186
187 if (!RDEBUG_ENABLED3) return;
188
189 RINDENT();
190 RIDEBUG3("da : %p", vp->da);
191 RIDEBUG3("is_raw : %pV", fr_box_bool(vp->vp_raw));
192 RIDEBUG3("is_unknown : %pV", fr_box_bool(vp->da->flags.is_unknown));
193
194 if (RDEBUG_ENABLED3) {
195 RIDEBUG3("parent : %s (%p)", vp->da->parent->name, vp->da->parent);
196 } else {
197 RIDEBUG2("parent : %s", vp->da->parent->name);
198 }
199 RIDEBUG3("attr : %u", vp->da->attr);
200 vendor = fr_dict_vendor_by_da(vp->da);
201 if (vendor) RIDEBUG2("vendor : %u (%s)", vendor->pen, vendor->name);
202 RIDEBUG3("type : %s", fr_type_to_str(vp->vp_type));
203
204 switch (vp->vp_type) {
205 case FR_TYPE_LEAF:
206 if (fr_box_is_variable_size(&vp->data)) {
207 RIDEBUG3("length : %zu", vp->vp_length);
208 }
209 RIDEBUG3("tainted : %pV", fr_box_bool(vp->data.tainted));
210 break;
211 default:
212 break;
213 }
214
215 if (!RDEBUG_ENABLED4) {
216 REXDENT();
217 return;
218 }
219
220 for (i = 0; i < fr_type_table_len; i++) {
221 int pad;
222
223 fr_value_box_t *dst = NULL;
224
225 type = &fr_type_table[i];
226
227 if ((fr_type_t) type->value == vp->vp_type) goto next_type;
228
229 /*
230 * Don't cast TO structural, or FROM structural types.
231 */
232 if (!fr_type_is_leaf(type->value) || !fr_type_is_leaf(vp->vp_type)) goto next_type;
233
234 MEM(dst = fr_value_box_acopy(NULL, &vp->data));
235
236 /* We expect some to fail */
237 if (fr_value_box_cast_in_place(dst, dst, type->value, NULL) < 0) {
238 goto next_type;
239 }
240
241 if ((pad = (11 - type->name.len)) < 0) pad = 0;
242
243 RINDENT();
244 RDEBUG4("as %s%*s: %pV", type->name.str, pad, " ", dst);
245 REXDENT();
246
247 next_type:
248 talloc_free(dst);
249 }
250
251 REXDENT();
252}
253
254/** Common function to move boxes from input list to output list
255 *
256 * This can be used to implement safe_for functions, as the xlat framework
257 * can be used for concatenation, casting, and marking up output boxes as
258 * safe_for.
259 */
261 UNUSED xlat_ctx_t const *xctx,
262 UNUSED request_t *request, fr_value_box_list_t *args)
263{
265 xlat_arg_copy_out(ctx, out, args, vb);
266 }
267
268 return XLAT_ACTION_DONE;
269}
270
271/** Print out attribute info
272 *
273 * Prints out all instances of a current attribute, or all attributes in a list.
274 *
275 * At higher debugging levels, also prints out alternative decodings of the same
276 * value. This is helpful to determine types for unknown attributes of long
277 * passed vendors, or just crazy/broken NAS.
278 *
279 * This expands to a zero length string.
280 *
281 * Example:
282@verbatim
283%pairs.debug(&request)
284@endverbatim
285 *
286 * @ingroup xlat_functions
287 */
289 UNUSED xlat_ctx_t const *xctx,
290 request_t *request, fr_value_box_list_t *args)
291{
292 fr_pair_t *vp;
293 fr_dcursor_t *cursor;
294 fr_value_box_t *in_head;
295
296 XLAT_ARGS(args, &in_head);
297
298 if (!RDEBUG_ENABLED2) return XLAT_ACTION_DONE; /* NOOP if debugging isn't enabled */
299
300 cursor = fr_value_box_get_cursor(in_head);
301
302 RDEBUG("Attributes matching \"%s\"", in_head->vb_cursor_name);
303
304 RINDENT();
305 for (vp = fr_dcursor_current(cursor);
306 vp;
307 vp = fr_dcursor_next(cursor)) {
308 xlat_debug_attr_vp(request, vp, NULL);
309 }
310 REXDENT();
311
312 return XLAT_ACTION_DONE;
313}
314
315#ifdef __clang__
316#pragma clang diagnostic ignored "-Wgnu-designator"
317#endif
318
320 .name = "filename",
321 .chr = '_',
322 .do_utf8 = true,
323 .do_hex = true,
324
325 .esc = {
326 [ 0x00 ... 0x2d ] = true, // special characters, but not '.'
327 [ 0x2f ] = true, // /
328 [ 0x3A ... 0x3f ] = true, // :;<=>?, but not "@"
329 [ 0x5b ... 0x5e ] = true, // [\]^
330 [ 0x60 ] = true, // back-tick
331 [ 0x7b ... 0xff ] = true, // {|}, and all chars which have high bit set, but aren't UTF-8
332 },
333};
334
336 .name = "filename",
337 .chr = '_',
338 .do_utf8 = true,
339 .do_hex = true,
340
341 .esc = {
342 [ 0x00 ... 0x2f ] = true, // special characters, '.', '/', etc.
343 [ 0x3A ... 0x3f ] = true, // :;<=>?, but not "@"
344 [ 0x5b ... 0x5e ] = true, // [\]^
345 [ 0x60 ] = true, // back-tick
346 [ 0x7b ... 0xff ] = true, // {|}, and all chars which have high bit set, but aren't UTF-8
347 },
348};
349
350#define FR_FILENAME_SAFE_FOR ((uintptr_t) filename_xlat_escape)
351
352static int CC_HINT(nonnull(2,3)) filename_xlat_escape(UNUSED request_t *request, fr_value_box_t *vb, UNUSED void *uctx)
353{
354 fr_sbuff_t *out = NULL;
355 fr_value_box_entry_t entry;
356
358
359 /*
360 * Integers are just numbers, so they don't need to be escaped.
361 *
362 * Except that FR_TYPE_INTEGER includes 'date' and 'time_delta', which is annoying.
363 *
364 * 'octets' get printed as hex, so they don't need to be escaped.
365 */
366 switch (vb->type) {
367 case FR_TYPE_BOOL:
368 case FR_TYPE_UINT8:
369 case FR_TYPE_UINT16:
370 case FR_TYPE_UINT32:
371 case FR_TYPE_UINT64:
372 case FR_TYPE_INT8:
373 case FR_TYPE_INT16:
374 case FR_TYPE_INT32:
375 case FR_TYPE_INT64:
376 case FR_TYPE_SIZE:
377 case FR_TYPE_OCTETS:
378 return 0;
379
380 case FR_TYPE_NON_LEAF:
381 fr_assert(0);
382 return -1;
383
384 case FR_TYPE_DATE:
386 case FR_TYPE_IFID:
387 case FR_TYPE_ETHERNET:
388 case FR_TYPE_FLOAT32:
389 case FR_TYPE_FLOAT64:
396 case FR_TYPE_ATTR:
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_uint32;
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
744 { .required = true, .concat = true, .type = FR_TYPE_STRING,
745 .func = filename_xlat_escape, .safe_for = FR_FILENAME_SAFE_FOR, .always_escape = true },
746 { .required = true, .type = FR_TYPE_SIZE, .single = true },
748};
749
751 UNUSED xlat_ctx_t const *xctx,
752 request_t *request, fr_value_box_list_t *args)
753{
754 fr_value_box_t *dst, *vb, *max_size;
755 char const *filename;
756 ssize_t len;
757 int fd;
758 struct stat buf;
760
761 XLAT_ARGS(args, &vb, &max_size);
762 fr_assert(vb->type == FR_TYPE_STRING);
763 filename = vb->vb_strvalue;
764
765 fd = open(filename, O_RDONLY);
766 if (fd < 0) {
767 RPERROR("Failed opening file %s - %s", filename, fr_syserror(errno));
768 return XLAT_ACTION_FAIL;
769 }
770
771 if (fstat(fd, &buf) < 0) {
772 RPERROR("Failed checking file %s - %s", filename, fr_syserror(errno));
773 fail:
774 close(fd);
775 return XLAT_ACTION_FAIL;
776 }
777
778 if ((size_t)buf.st_size > max_size->vb_size) {
779 RPERROR("File larger than specified maximum (%"PRIu64" vs %zu)", buf.st_size, max_size->vb_size);
780 goto fail;
781 }
782
783 MEM(dst = fr_value_box_alloc(ctx, FR_TYPE_OCTETS, NULL));
784 fr_value_box_mem_alloc(dst, &buffer, dst, NULL, buf.st_size, true);
785
786 len = read(fd, buffer, buf.st_size);
787 if (len < 0) {
788 RPERROR("Failed reading file %s - %s", filename, fr_syserror(errno));
789 talloc_free(dst);
790 goto fail;
791 }
792 close(fd);
793
794 if (len < buf.st_size) {
795 RPERROR("Failed reading all of file %s", filename);
796 talloc_free(dst);
797 return XLAT_ACTION_FAIL;
798 }
799
801
802 return XLAT_ACTION_DONE;
803}
804
806 UNUSED xlat_ctx_t const *xctx,
807 request_t *request, fr_value_box_list_t *args)
808{
809 fr_value_box_t *dst, *vb;
810 char const *filename;
811
812 XLAT_ARGS(args, &vb);
813 fr_assert(vb->type == FR_TYPE_STRING);
814 filename = vb->vb_strvalue;
815
816 MEM(dst = fr_value_box_alloc(ctx, FR_TYPE_BOOL, NULL));
818
819 dst->vb_bool = (unlink(filename) == 0);
820 if (!dst->vb_bool) {
821 REDEBUG3("Failed unlinking file %s - %s", filename, fr_syserror(errno));
822 }
823
824 return XLAT_ACTION_DONE;
825}
826
828 request_t *request, fr_value_box_list_t *args)
829{
830 fr_value_box_t *dst, *vb;
831 char const *filename;
832 int fd;
833
834 XLAT_ARGS(args, &vb);
835 fr_assert(vb->type == FR_TYPE_STRING);
836 filename = vb->vb_strvalue;
837
838 MEM(dst = fr_value_box_alloc(ctx, FR_TYPE_BOOL, NULL));
840
841 fd = open(filename, O_CREAT | O_WRONLY, 0600);
842 if (fd < 0) {
843 dst->vb_bool = false;
844 REDEBUG3("Failed touching file %s - %s", filename, fr_syserror(errno));
845 return XLAT_ACTION_DONE;
846 }
847 dst->vb_bool = true;
848
849 close(fd);
850
851 return XLAT_ACTION_DONE;
852}
853
855 request_t *request, fr_value_box_list_t *args)
856{
857 fr_value_box_t *dst, *vb;
858 char const *dirname;
859
860 XLAT_ARGS(args, &vb);
861 fr_assert(vb->type == FR_TYPE_STRING);
862 dirname = vb->vb_strvalue;
863
864 MEM(dst = fr_value_box_alloc(ctx, FR_TYPE_BOOL, NULL));
866
867 dst->vb_bool = (fr_mkdir(NULL, dirname, -1, 0700, NULL, NULL) == 0);
868 if (!dst->vb_bool) {
869 REDEBUG3("Failed creating directory %s - %s", dirname, fr_syserror(errno));
870 }
871
872 return XLAT_ACTION_DONE;
873}
874
876 request_t *request, fr_value_box_list_t *args)
877{
878 fr_value_box_t *dst, *vb;
879 char const *dirname;
880
881 XLAT_ARGS(args, &vb);
882 fr_assert(vb->type == FR_TYPE_STRING);
883 dirname = vb->vb_strvalue;
884
885 MEM(dst = fr_value_box_alloc(ctx, FR_TYPE_BOOL, NULL));
887
888 dst->vb_bool = (rmdir(dirname) == 0);
889 if (!dst->vb_bool) {
890 REDEBUG3("Failed removing directory %s - %s", dirname, fr_syserror(errno));
891 }
892
893 return XLAT_ACTION_DONE;
894}
895
897 { .required = true, .type = FR_TYPE_VOID },
898 { .variadic = XLAT_ARG_VARIADIC_EMPTY_KEEP, .type = FR_TYPE_VOID },
900};
901
903 UNUSED xlat_ctx_t const *xctx,
904 UNUSED request_t *request, fr_value_box_list_t *in)
905{
906 fr_value_box_t *vb;
907
909 while ((vb = fr_value_box_list_pop_head(in)) != NULL) {
911 }
912
913 return XLAT_ACTION_DONE;
914}
915
917 UNUSED xlat_ctx_t const *xctx,
918 UNUSED request_t *request, fr_value_box_list_t *in)
919{
920 fr_value_box_t *vb;
921
922 while ((vb = fr_value_box_list_pop_head(in)) != NULL) {
923 fr_value_box_t *child;
924
925 fr_assert(vb->type == FR_TYPE_GROUP);
926
927 while ((child = fr_value_box_list_pop_head(&vb->vb_group)) != NULL) {
928 child->tainted = true;
930
931 fr_dcursor_append(out, child);
932 }
933 }
934
935 return XLAT_ACTION_DONE;
936}
937
939 { .required = true, .type = FR_TYPE_STRING },
940 { .required = true, .concat = true, .type = FR_TYPE_STRING },
942};
943
944/** Split a string into multiple new strings based on a delimiter
945 *
946@verbatim
947%explode(<string>, <delim>)
948@endverbatim
949 *
950 * Example:
951@verbatim
952update request {
953 &Tmp-String-1 := "a,b,c"
954}
955"%concat(%explode(%{Tmp-String-1}, ','), '|')" == "a|b|c"g
956@endverbatim
957 *
958 * @ingroup xlat_functions
959 */
961 UNUSED xlat_ctx_t const *xctx,
962 request_t *request, fr_value_box_list_t *args)
963{
965 fr_value_box_list_t *list;
966 fr_value_box_t *delim_vb;
967 ssize_t delim_len;
968 char const *delim;
969 fr_value_box_t *string, *vb;
970
971 XLAT_ARGS(args, &strings, &delim_vb);
972
973 list = &strings->vb_group;
974
975 /* coverity[dereference] */
976 if (delim_vb->vb_length == 0) {
977 REDEBUG("Delimiter must be greater than zero characters");
978 return XLAT_ACTION_FAIL;
979 }
980
981 delim = delim_vb->vb_strvalue;
982 delim_len = delim_vb->vb_length;
983
984 while ((string = fr_value_box_list_pop_head(list))) {
985 fr_sbuff_t sbuff = FR_SBUFF_IN(string->vb_strvalue, string->vb_length);
986 fr_sbuff_marker_t m_start;
987
988 /*
989 * If the delimiter is not in the string, just move to the output
990 */
991 if (!fr_sbuff_adv_to_str(&sbuff, SIZE_MAX, delim, delim_len)) {
992 fr_dcursor_append(out, string);
993 continue;
994 }
995
996 fr_sbuff_set_to_start(&sbuff);
997 fr_sbuff_marker(&m_start, &sbuff);
998
999 while (fr_sbuff_remaining(&sbuff)) {
1000 if (fr_sbuff_adv_to_str(&sbuff, SIZE_MAX, delim, delim_len)) {
1001 /*
1002 * If there's nothing before the delimiter skip
1003 */
1004 if (fr_sbuff_behind(&m_start) == 0) goto advance;
1005
1006 MEM(vb = fr_value_box_alloc_null(ctx));
1007 fr_value_box_bstrndup(vb, vb, NULL, fr_sbuff_current(&m_start),
1008 fr_sbuff_behind(&m_start), false);
1009 fr_value_box_safety_copy(vb, string);
1011
1012 advance:
1013 fr_sbuff_advance(&sbuff, delim_len);
1014 fr_sbuff_set(&m_start, &sbuff);
1015 continue;
1016 }
1017
1018 fr_sbuff_set_to_end(&sbuff);
1019 MEM(vb = fr_value_box_alloc_null(ctx));
1020 fr_value_box_bstrndup(vb, vb, NULL, fr_sbuff_current(&m_start),
1021 fr_sbuff_behind(&m_start), false);
1022
1023 fr_value_box_safety_copy(vb, string);
1025 break;
1026 }
1027 talloc_free(string);
1028 }
1029
1030 return XLAT_ACTION_DONE;
1031}
1032
1033/** Mark one or more attributes as immutable
1034 *
1035 * Example:
1036@verbatim
1037%pairs.immutable(request.State[*])
1038@endverbatim
1039 *
1040 * @ingroup xlat_functions
1041 */
1043 UNUSED xlat_ctx_t const *xctx,
1044 request_t *request, fr_value_box_list_t *args)
1045{
1046 fr_pair_t *vp;
1047 fr_dcursor_t *cursor;
1048 fr_value_box_t *in_head;
1049
1050 XLAT_ARGS(args, &in_head);
1051
1052 cursor = fr_value_box_get_cursor(in_head);
1053
1054 RDEBUG("Attributes matching \"%s\"", in_head->vb_cursor_name);
1055
1056 RINDENT();
1057 for (vp = fr_dcursor_current(cursor);
1058 vp;
1059 vp = fr_dcursor_next(cursor)) {
1061 }
1062 REXDENT();
1063
1064 return XLAT_ACTION_DONE;
1065}
1066
1068 { .required = true, .single = true, .type = FR_TYPE_VOID },
1070};
1071
1072/** Print data as integer, not as VALUE.
1073 *
1074 * Example:
1075@verbatim
1076update request {
1077 &Tmp-IP-Address-0 := "127.0.0.5"
1078}
1079%integer(%{Tmp-IP-Address-0}) == 2130706437
1080@endverbatim
1081 * @ingroup xlat_functions
1082 */
1084 UNUSED xlat_ctx_t const *xctx,
1085 request_t *request, fr_value_box_list_t *args)
1086{
1087 fr_value_box_t *in_vb;
1088 char const *p;
1089
1090 XLAT_ARGS(args, &in_vb);
1091
1092 fr_strerror_clear(); /* Make sure we don't print old errors */
1093
1094 fr_value_box_list_remove(args, in_vb);
1095
1096 switch (in_vb->type) {
1097 default:
1098 error:
1099 RPEDEBUG("Failed converting %pR (%s) to an integer", in_vb,
1100 fr_type_to_str(in_vb->type));
1101 talloc_free(in_vb);
1102 return XLAT_ACTION_FAIL;
1103
1104 case FR_TYPE_NUMERIC:
1105 /*
1106 * Ensure enumeration is NULL so that the integer
1107 * version of a box is returned
1108 */
1109 in_vb->enumv = NULL;
1110
1111 /*
1112 * FR_TYPE_DATE and FR_TYPE_TIME_DELTA need to be cast
1113 * to int64_t so that they're printed in a
1114 * numeric format.
1115 */
1116 if ((in_vb->type == FR_TYPE_DATE) || (in_vb->type == FR_TYPE_TIME_DELTA)) {
1117 if (fr_value_box_cast_in_place(ctx, in_vb, FR_TYPE_INT64, NULL) < 0) goto error;
1118 }
1119 break;
1120
1121 case FR_TYPE_STRING:
1122 /*
1123 * Strings are always zero terminated. They may
1124 * also have zeros in the middle, but if that
1125 * happens, the caller will only get the part up
1126 * to the first zero.
1127 *
1128 * We check for negative numbers, just to be
1129 * nice.
1130 */
1131 for (p = in_vb->vb_strvalue; *p != '\0'; p++) {
1132 if (*p == '-') break;
1133 }
1134
1135 if (*p == '-') {
1136 if (fr_value_box_cast_in_place(ctx, in_vb, FR_TYPE_INT64, NULL) < 0) goto error;
1137 } else {
1138 if (fr_value_box_cast_in_place(ctx, in_vb, FR_TYPE_UINT64, NULL) < 0) goto error;
1139 }
1140 break;
1141
1142 case FR_TYPE_OCTETS:
1143 if (in_vb->vb_length > sizeof(uint64_t)) {
1144 fr_strerror_printf("Expected octets length <= %zu, got %zu", sizeof(uint64_t), in_vb->vb_length);
1145 goto error;
1146 }
1147
1148 if (in_vb->vb_length > sizeof(uint32_t)) {
1149 if (unlikely(fr_value_box_cast_in_place(ctx, in_vb, FR_TYPE_UINT64, NULL) < 0)) goto error;
1150 } else if (in_vb->vb_length > sizeof(uint16_t)) {
1151 if (unlikely(fr_value_box_cast_in_place(ctx, in_vb, FR_TYPE_UINT32, NULL) < 0)) goto error;
1152 } else if (in_vb->vb_length > sizeof(uint8_t)) {
1153 if (unlikely(fr_value_box_cast_in_place(ctx, in_vb, FR_TYPE_UINT16, NULL) < 0)) goto error;
1154 } else {
1155 if (unlikely(fr_value_box_cast_in_place(ctx, in_vb, FR_TYPE_UINT8, NULL) < 0)) goto error;
1156 }
1157
1158 break;
1159
1160 case FR_TYPE_IPV4_ADDR:
1162 if (fr_value_box_cast_in_place(ctx, in_vb, FR_TYPE_UINT32, NULL) < 0) goto error;
1163 break;
1164
1165 case FR_TYPE_ETHERNET:
1166 if (fr_value_box_cast_in_place(ctx, in_vb, FR_TYPE_UINT64, NULL) < 0) goto error;
1167 break;
1168
1169 case FR_TYPE_IPV6_ADDR:
1171 {
1172 uint128_t ipv6int;
1173 char buff[40];
1174 fr_value_box_t *vb;
1175
1176 /*
1177 * Needed for correct alignment (as flagged by ubsan)
1178 */
1179 memcpy(&ipv6int, &in_vb->vb_ipv6addr, sizeof(ipv6int));
1180
1181 fr_snprint_uint128(buff, sizeof(buff), ntohlll(ipv6int));
1182
1183 MEM(vb = fr_value_box_alloc_null(ctx));
1184 fr_value_box_bstrndup(vb, vb, NULL, buff, strlen(buff), false);
1186 talloc_free(in_vb);
1187 return XLAT_ACTION_DONE;
1188 }
1189 }
1190
1191 fr_dcursor_append(out, in_vb);
1192
1193 return XLAT_ACTION_DONE;
1194}
1195
1197 { .concat = true, .type = FR_TYPE_STRING },
1199};
1200
1201/** Log something at INFO level.
1202 *
1203 * Example:
1204@verbatim
1205%log("This is an informational message")
1206@endverbatim
1207 *
1208 * @ingroup xlat_functions
1209 */
1211 UNUSED xlat_ctx_t const *xctx,
1212 request_t *request, fr_value_box_list_t *args)
1213{
1214 fr_value_box_t *vb;
1215
1216 XLAT_ARGS(args, &vb);
1217
1218 if (!vb) return XLAT_ACTION_DONE;
1219
1220 RINFO("%s", vb->vb_strvalue);
1221
1222 return XLAT_ACTION_DONE;
1223}
1224
1225
1226/** Log something at DEBUG level.
1227 *
1228 * Example:
1229@verbatim
1230%log.debug("This is a message")
1231@endverbatim
1232 *
1233 * @ingroup xlat_functions
1234 */
1236 UNUSED xlat_ctx_t const *xctx,
1237 request_t *request, fr_value_box_list_t *args)
1238{
1239 fr_value_box_t *vb;
1240
1241 XLAT_ARGS(args, &vb);
1242
1243 if (!vb) return XLAT_ACTION_DONE;
1244
1245 RDEBUG("%s", vb->vb_strvalue);
1246
1247 return XLAT_ACTION_DONE;
1248}
1249
1250
1251/** Log something at DEBUG level.
1252 *
1253 * Example:
1254@verbatim
1255%log.err("Big error here")
1256@endverbatim
1257 *
1258 * @ingroup xlat_functions
1259 */
1261 UNUSED xlat_ctx_t const *xctx,
1262 request_t *request, fr_value_box_list_t *args)
1263{
1264 fr_value_box_t *vb;
1265
1266 XLAT_ARGS(args, &vb);
1267
1268 if (!vb) return XLAT_ACTION_DONE;
1269
1270 REDEBUG("%s", vb->vb_strvalue);
1271
1272 return XLAT_ACTION_DONE;
1273}
1274
1275
1276/** Log something at WARN level.
1277 *
1278 * Example:
1279@verbatim
1280%log.warn("Maybe something bad happened")
1281@endverbatim
1282 *
1283 * @ingroup xlat_functions
1284 */
1286 UNUSED xlat_ctx_t const *xctx,
1287 request_t *request, fr_value_box_list_t *args)
1288{
1289 fr_value_box_t *vb;
1290
1291 XLAT_ARGS(args, &vb);
1292
1293 if (!vb) return XLAT_ACTION_DONE;
1294
1295 RWDEBUG("%s", vb->vb_strvalue);
1296
1297 return XLAT_ACTION_DONE;
1298}
1299
1300static int _log_dst_free(fr_log_t *log)
1301{
1302 close(log->fd);
1303 return 0;
1304}
1305
1307 { .required = false, .type = FR_TYPE_STRING, .concat = true },
1308 { .required = false, .type = FR_TYPE_UINT32, .single = true },
1309 { .required = false, .type = FR_TYPE_STRING, .concat = true },
1311};
1312
1313/** Change the log destination to the named one
1314 *
1315 * Example:
1316@verbatim
1317%log.destination('foo')
1318@endverbatim
1319 *
1320 * @ingroup xlat_functions
1321 */
1323 UNUSED xlat_ctx_t const *xctx,
1324 request_t *request, fr_value_box_list_t *args)
1325{
1326 fr_value_box_t *dst, *lvl, *file;
1327 fr_log_t *log, *dbg;
1328 uint32_t level = 2;
1329
1330 XLAT_ARGS(args, &dst, &lvl, &file);
1331
1332 if (!dst || !*dst->vb_strvalue) {
1333 request_log_prepend(request, NULL, L_DBG_LVL_DISABLE);
1334 return XLAT_ACTION_DONE;
1335 }
1336
1337 log = log_dst_by_name(dst->vb_strvalue);
1338 if (!log) return XLAT_ACTION_FAIL;
1339
1340 if (lvl) level = lvl->vb_uint32;
1341
1342 if (!file || ((log->dst != L_DST_NULL) && (log->dst != L_DST_FILES))) {
1343 request_log_prepend(request, log, level);
1344 return XLAT_ACTION_DONE;
1345 }
1346
1347 /*
1348 * Clone it.
1349 */
1350 MEM(dbg = talloc_memdup(request, log, sizeof(*log)));
1351 dbg->parent = log;
1352
1353 /*
1354 * Open the new filename.
1355 */
1356 dbg->dst = L_DST_FILES;
1357 dbg->file = talloc_strdup(dbg, file->vb_strvalue);
1358 dbg->fd = open(dbg->file, O_WRONLY | O_CREAT | O_CLOEXEC, 0600);
1359 if (dbg->fd < 0) {
1360 REDEBUG("Failed opening %s - %s", dbg->file, fr_syserror(errno));
1361 talloc_free(dbg);
1362 return XLAT_ACTION_DONE;
1363 }
1364
1365 /*
1366 * Ensure that we close the file handle when done.
1367 */
1368 talloc_set_destructor(dbg, _log_dst_free);
1369
1370 request_log_prepend(request, dbg, level);
1371 return XLAT_ACTION_DONE;
1372}
1373
1374
1376 { .required = true, .type = FR_TYPE_STRING },
1378};
1379
1380/** Processes fmt as a map string and applies it to the current request
1381 *
1382 * e.g.
1383@verbatim
1384%map("User-Name := 'foo'")
1385@endverbatim
1386 *
1387 * Allows sets of modifications to be cached and then applied.
1388 * Useful for processing generic attributes from LDAP.
1389 *
1390 * @ingroup xlat_functions
1391 */
1393 UNUSED xlat_ctx_t const *xctx,
1394 request_t *request, fr_value_box_list_t *args)
1395{
1396 map_t *map = NULL;
1397 int ret;
1398 fr_value_box_t *fmt_vb;
1399 fr_value_box_t *vb;
1400
1401 tmpl_rules_t attr_rules = {
1402 .attr = {
1403 .dict_def = request->local_dict,
1404 .list_def = request_attr_request,
1405 },
1406 .xlat = {
1407 .runtime_el = unlang_interpret_event_list(request)
1408 }
1409 };
1410
1411 XLAT_ARGS(args, &fmt_vb);
1412
1413 MEM(vb = fr_value_box_alloc(ctx, FR_TYPE_BOOL, NULL));
1414 vb->vb_bool = false; /* Default fail value - changed to true on success */
1416
1417 fr_value_box_list_foreach(&fmt_vb->vb_group, fmt) {
1418 if (map_afrom_attr_str(request, &map, fmt->vb_strvalue, &attr_rules, &attr_rules) < 0) {
1419 RPEDEBUG("Failed parsing \"%s\" as map", fmt_vb->vb_strvalue);
1420 return XLAT_ACTION_FAIL;
1421 }
1422
1423 switch (map->lhs->type) {
1424 case TMPL_TYPE_ATTR:
1425 case TMPL_TYPE_XLAT:
1426 break;
1427
1428 default:
1429 REDEBUG("Unexpected type %s in left hand side of expression",
1430 tmpl_type_to_str(map->lhs->type));
1431 return XLAT_ACTION_FAIL;
1432 }
1433
1434 switch (map->rhs->type) {
1435 case TMPL_TYPE_ATTR:
1436 case TMPL_TYPE_EXEC:
1437 case TMPL_TYPE_DATA:
1440 case TMPL_TYPE_XLAT:
1441 break;
1442
1443 default:
1444 REDEBUG("Unexpected type %s in right hand side of expression",
1445 tmpl_type_to_str(map->rhs->type));
1446 return XLAT_ACTION_FAIL;
1447 }
1448
1449 RINDENT();
1450 ret = map_to_request(request, map, map_to_vp, NULL);
1451 REXDENT();
1452 talloc_free(map);
1453 if (ret < 0) return XLAT_ACTION_FAIL;
1454 }
1455
1456 vb->vb_bool = true;
1457 return XLAT_ACTION_DONE;
1458}
1459
1460
1462 { .required = true, .concat = true, .type = FR_TYPE_STRING },
1464};
1465
1466/** Calculate number of seconds until the next n hour(s), day(s), week(s), year(s).
1467 *
1468 * For example, if it were 16:18 %time.next(1h) would expand to 2520.
1469 *
1470 * The envisaged usage for this function is to limit sessions so that they don't
1471 * cross billing periods. The output of the xlat should be combined with %rand() to create
1472 * some jitter, unless the desired effect is every subscriber on the network
1473 * re-authenticating at the same time.
1474 *
1475 * @ingroup xlat_functions
1476 */
1478 UNUSED xlat_ctx_t const *xctx,
1479 request_t *request, fr_value_box_list_t *args)
1480{
1481 unsigned long num;
1482
1483 char const *p;
1484 char *q;
1485 time_t now;
1486 struct tm *local, local_buff;
1487 fr_value_box_t *in_head;
1488 fr_value_box_t *vb;
1489
1490 XLAT_ARGS(args, &in_head);
1491
1492 /*
1493 * We want to limit based on _now_, not on when they logged in.
1494 */
1495 now = time(NULL);
1496 local = localtime_r(&now, &local_buff);
1497
1498 p = in_head->vb_strvalue;
1499
1500 num = strtoul(p, &q, 10);
1501 if ((num == ULONG_MAX) || !q || *q == '\0') {
1502 REDEBUG("<int> must be followed by time period (h|d|w|m|y)");
1503 return XLAT_ACTION_FAIL;
1504 }
1505 if (num == 0) {
1506 REDEBUG("<int> must be greater than zero");
1507 return XLAT_ACTION_FAIL;
1508 }
1509
1510 if (p == q) {
1511 num = 1;
1512 } else {
1513 p += q - p;
1514 }
1515
1516 local->tm_sec = 0;
1517 local->tm_min = 0;
1518
1519 switch (*p) {
1520 case 'h':
1521 local->tm_hour += num;
1522 break;
1523
1524 case 'd':
1525 local->tm_hour = 0;
1526 local->tm_mday += num;
1527 break;
1528
1529 case 'w':
1530 local->tm_hour = 0;
1531 local->tm_mday += (7 - local->tm_wday) + (7 * (num-1));
1532 break;
1533
1534 case 'm':
1535 local->tm_hour = 0;
1536 local->tm_mday = 1;
1537 local->tm_mon += num;
1538 break;
1539
1540 case 'y':
1541 local->tm_hour = 0;
1542 local->tm_mday = 1;
1543 local->tm_mon = 0;
1544 local->tm_year += num;
1545 break;
1546
1547 default:
1548 REDEBUG("Invalid time period '%c', must be h|d|w|m|y", *p);
1549 return XLAT_ACTION_FAIL;
1550 }
1551
1552 MEM(vb = fr_value_box_alloc_null(ctx));
1553 fr_value_box_uint64(vb, NULL, (uint64_t)(mktime(local) - now), false);
1555 return XLAT_ACTION_DONE;
1556}
1557
1562
1563/** Just serves to push the result up the stack
1564 *
1565 */
1567 xlat_ctx_t const *xctx,
1568 UNUSED request_t *request, UNUSED fr_value_box_list_t *in)
1569{
1570 xlat_eval_rctx_t *rctx = talloc_get_type_abort(xctx->rctx, xlat_eval_rctx_t);
1572
1573 talloc_free(rctx);
1574
1575 return xa;
1576}
1577
1579 { .required = true, .concat = true, .type = FR_TYPE_STRING },
1581};
1582
1583/** Dynamically evaluate an expansion string
1584 *
1585 * @ingroup xlat_functions
1586 */
1588 UNUSED xlat_ctx_t const *xctx,
1589 request_t *request, fr_value_box_list_t *args)
1590{
1591 /*
1592 * These are escaping rules applied to the
1593 * input string. They're mostly here to
1594 * allow \% and \\ to work.
1595 *
1596 * Everything else should be passed in as
1597 * unescaped data.
1598 */
1599 static fr_sbuff_unescape_rules_t const escape_rules = {
1600 .name = "xlat",
1601 .chr = '\\',
1602 .subs = {
1603 ['%'] = '%',
1604 ['\\'] = '\\',
1605 },
1606 .do_hex = false,
1607 .do_oct = false
1608 };
1609
1610 xlat_eval_rctx_t *rctx;
1611 fr_value_box_t *arg = fr_value_box_list_head(args);
1612
1613 XLAT_ARGS(args, &arg);
1614
1615 MEM(rctx = talloc_zero(unlang_interpret_frame_talloc_ctx(request), xlat_eval_rctx_t));
1616
1617 /*
1618 * Parse the input as a literal expansion
1619 */
1620 if (xlat_tokenize_expression(rctx,
1621 &rctx->ex,
1622 &FR_SBUFF_IN(arg->vb_strvalue, arg->vb_length),
1623 &(fr_sbuff_parse_rules_t){
1624 .escapes = &escape_rules
1625 },
1626 &(tmpl_rules_t){
1627 .attr = {
1628 .dict_def = request->local_dict,
1629 .list_def = request_attr_request,
1630 .allow_unknown = false,
1631 .allow_unresolved = false,
1632 .allow_foreign = false,
1633 },
1634 .xlat = {
1635 .runtime_el = unlang_interpret_event_list(request),
1636 },
1637 .at_runtime = true
1638 }) < 0) {
1639 RPEDEBUG("Failed parsing expansion");
1640 error:
1641 talloc_free(rctx);
1642 return XLAT_ACTION_FAIL;
1643 }
1644
1645 /*
1646 * Call the resolution function so we produce
1647 * good errors about what function was
1648 * unresolved.
1649 */
1650 if (rctx->ex->flags.needs_resolving &&
1651 (xlat_resolve(rctx->ex, &(xlat_res_rules_t){ .allow_unresolved = false }) < 0)) {
1652 RPEDEBUG("Unresolved expansion functions in expansion");
1653 goto error;
1654
1655 }
1656
1657 if (unlang_xlat_yield(request, xlat_eval_resume, NULL, 0, rctx) != XLAT_ACTION_YIELD) goto error;
1658
1659 if (unlang_xlat_push(ctx, &rctx->last_result, (fr_value_box_list_t *)out->dlist,
1660 request, rctx->ex, UNLANG_SUB_FRAME) < 0) goto error;
1661
1663}
1664
1666 { .required = true, .type = FR_TYPE_STRING },
1667 { .required = true, .single = true, .type = FR_TYPE_UINT64 },
1668 { .concat = true, .type = FR_TYPE_STRING },
1670};
1671
1672/** lpad a string
1673 *
1674@verbatim
1675%lpad(%{Attribute-Name}, <length> [, <fill>])
1676@endverbatim
1677 *
1678 * Example: (User-Name = "foo")
1679@verbatim
1680%lpad(%{User-Name}, 5 'x') == "xxfoo"
1681@endverbatim
1682 *
1683 * @ingroup xlat_functions
1684 */
1686 UNUSED xlat_ctx_t const *xctx,
1687 request_t *request, fr_value_box_list_t *args)
1688{
1689 fr_value_box_t *values;
1690 fr_value_box_t *pad;
1692
1693 fr_value_box_list_t *list;
1694
1695 size_t pad_len;
1696
1697 char const *fill_str = NULL;
1698 size_t fill_len = 0;
1699
1700 fr_value_box_t *in = NULL;
1701
1702 XLAT_ARGS(args, &values, &pad, &fill);
1703
1704 /* coverity[dereference] */
1705 list = &values->vb_group;
1706 /* coverity[dereference] */
1707 pad_len = (size_t)pad->vb_uint64;
1708
1709 /*
1710 * Fill is optional
1711 */
1712 if (fill) {
1713 fill_str = fill->vb_strvalue;
1714 fill_len = talloc_strlen(fill_str);
1715 }
1716
1717 if (fill_len == 0) {
1718 fill_str = " ";
1719 fill_len = 1;
1720 }
1721
1722 while ((in = fr_value_box_list_pop_head(list))) {
1723 size_t len = talloc_strlen(in->vb_strvalue);
1724 size_t remaining;
1725 char *buff;
1726 fr_sbuff_t sbuff;
1727 fr_sbuff_marker_t m_data;
1728
1730
1731 if (len >= pad_len) continue;
1732
1733 if (fr_value_box_bstr_realloc(in, &buff, in, pad_len) < 0) {
1734 RPEDEBUG("Failed reallocing input data");
1735 return XLAT_ACTION_FAIL;
1736 }
1737
1738 fr_sbuff_init_in(&sbuff, buff, pad_len);
1739 fr_sbuff_marker(&m_data, &sbuff);
1740
1741 /*
1742 * ...nothing to move if the input
1743 * string is empty.
1744 */
1745 if (len > 0) {
1746 fr_sbuff_advance(&m_data, pad_len - len); /* Mark where we want the data to go */
1747 fr_sbuff_move(&FR_SBUFF(&m_data), &FR_SBUFF(&sbuff), len); /* Shift the data */
1748 }
1749
1750 if (fill_len == 1) {
1751 memset(fr_sbuff_current(&sbuff), *fill_str, fr_sbuff_ahead(&m_data));
1752 continue;
1753 }
1754
1755 /*
1756 * Copy fill as a repeating pattern
1757 */
1758 while ((remaining = fr_sbuff_ahead(&m_data))) {
1759 size_t to_copy = remaining >= fill_len ? fill_len : remaining;
1760 memcpy(fr_sbuff_current(&sbuff), fill_str, to_copy); /* avoid \0 termination */
1761 fr_sbuff_advance(&sbuff, to_copy);
1762 }
1763 fr_sbuff_set_to_end(&sbuff);
1764 fr_sbuff_terminate(&sbuff); /* Move doesn't re-terminate */
1765 }
1766
1767 return XLAT_ACTION_DONE;
1768}
1769
1770/** Right pad a string
1771 *
1772@verbatim
1773%rpad(%{Attribute-Name}, <length> [, <fill>])
1774@endverbatim
1775 *
1776 * Example: (User-Name = "foo")
1777@verbatim
1778%rpad(%{User-Name}, 5 'x') == "fooxx"
1779@endverbatim
1780 *
1781 * @ingroup xlat_functions
1782 */
1784 UNUSED xlat_ctx_t const *xctx,
1785 request_t *request, fr_value_box_list_t *args)
1786{
1787 fr_value_box_t *values;
1788 fr_value_box_list_t *list;
1789 fr_value_box_t *pad;
1790 /* coverity[dereference] */
1791 size_t pad_len;
1793 char const *fill_str = NULL;
1794 size_t fill_len = 0;
1795
1796 fr_value_box_t *in = NULL;
1797
1798 XLAT_ARGS(args, &values, &pad, &fill);
1799
1800 list = &values->vb_group;
1801 pad_len = (size_t)pad->vb_uint64;
1802
1803 /*
1804 * Fill is optional
1805 */
1806 if (fill) {
1807 fill_str = fill->vb_strvalue;
1808 fill_len = talloc_strlen(fill_str);
1809 }
1810
1811 if (fill_len == 0) {
1812 fill_str = " ";
1813 fill_len = 1;
1814 }
1815
1816 while ((in = fr_value_box_list_pop_head(list))) {
1817 size_t len = talloc_strlen(in->vb_strvalue);
1818 size_t remaining;
1819 char *buff;
1820 fr_sbuff_t sbuff;
1821
1823
1824 if (len >= pad_len) continue;
1825
1826 if (fr_value_box_bstr_realloc(in, &buff, in, pad_len) < 0) {
1827 fail:
1828 RPEDEBUG("Failed reallocing input data");
1829 return XLAT_ACTION_FAIL;
1830 }
1831
1832 fr_sbuff_init_in(&sbuff, buff, pad_len);
1833 fr_sbuff_advance(&sbuff, len);
1834
1835 if (fill_len == 1) {
1836 memset(fr_sbuff_current(&sbuff), *fill_str, fr_sbuff_remaining(&sbuff));
1837 continue;
1838 }
1839
1840 /*
1841 * Copy fill as a repeating pattern
1842 */
1843 while ((remaining = fr_sbuff_remaining(&sbuff))) {
1844 if (fr_sbuff_in_bstrncpy(&sbuff, fill_str, remaining >= fill_len ? fill_len : remaining) < 0) {
1845 goto fail;
1846 }
1847 }
1848 }
1849
1850 return XLAT_ACTION_DONE;
1851}
1852
1854 { .required = true, .concat = true, .type = FR_TYPE_OCTETS },
1856};
1857
1858/** Encode string or attribute as base64
1859 *
1860 * Example:
1861@verbatim
1862%base64.encode("foo") == "Zm9v"
1863@endverbatim
1864 *
1865 * @ingroup xlat_functions
1866 */
1868 UNUSED xlat_ctx_t const *xctx,
1869 request_t *request, fr_value_box_list_t *args)
1870{
1871 size_t alen;
1872 ssize_t elen;
1873 char *buff;
1874 fr_value_box_t *vb;
1876
1877 XLAT_ARGS(args, &in);
1878
1879 alen = FR_BASE64_ENC_LENGTH(in->vb_length);
1880
1881 MEM(vb = fr_value_box_alloc_null(ctx));
1882 if (fr_value_box_bstr_alloc(vb, &buff, vb, NULL, alen, false) < 0) {
1883 talloc_free(vb);
1884 return XLAT_ACTION_FAIL;
1885 }
1886
1887 elen = fr_base64_encode(&FR_SBUFF_OUT(buff, talloc_array_length(buff)),
1888 &FR_DBUFF_TMP(in->vb_octets, in->vb_length), true);
1889 if (elen < 0) {
1890 RPEDEBUG("Base64 encoding failed");
1891 talloc_free(vb);
1892 return XLAT_ACTION_FAIL;
1893 }
1894 fr_assert((size_t)elen <= alen);
1897
1898 return XLAT_ACTION_DONE;
1899}
1900
1902 { .required = true, .concat = true, .type = FR_TYPE_OCTETS },
1904};
1905
1906/** Decode base64 string
1907 *
1908 * Example:
1909@verbatim
1910%base64.decode("Zm9v") == "foo"
1911@endverbatim
1912 *
1913 * @ingroup xlat_functions
1914 */
1916 UNUSED xlat_ctx_t const *xctx,
1917 request_t *request, fr_value_box_list_t *args)
1918{
1919 size_t alen;
1920 ssize_t declen = 0;
1921 uint8_t *decbuf;
1922 fr_value_box_t *vb;
1924
1925 XLAT_ARGS(args, &in);
1926
1927 /*
1928 * Pass empty arguments through
1929 *
1930 * FR_BASE64_DEC_LENGTH produces 2 for empty strings...
1931 */
1932 if (in->vb_length == 0) {
1933 xlat_arg_copy_out(ctx, out, args, in);
1934 return XLAT_ACTION_DONE;
1935 }
1936
1937 alen = FR_BASE64_DEC_LENGTH(in->vb_length);
1938 MEM(vb = fr_value_box_alloc_null(ctx));
1939 if (alen > 0) {
1940 MEM(fr_value_box_mem_alloc(vb, &decbuf, vb, NULL, alen, false) == 0);
1941 declen = fr_base64_decode(&FR_DBUFF_TMP(decbuf, alen),
1942 &FR_SBUFF_IN(in->vb_strvalue, in->vb_length), true, true);
1943 if (declen < 0) {
1944 RPEDEBUG("Base64 string invalid");
1945 talloc_free(vb);
1946 return XLAT_ACTION_FAIL;
1947 }
1948
1949 MEM(fr_value_box_mem_realloc(vb, NULL, vb, declen) == 0);
1950 }
1951
1954
1955 return XLAT_ACTION_DONE;
1956}
1957
1959 { .required = true, .type = FR_TYPE_STRING },
1961};
1962
1963/** Convert hex string to binary
1964 *
1965 * Example:
1966@verbatim
1967%bin("666f6f626172") == "foobar"
1968@endverbatim
1969 *
1970 * @see #xlat_func_hex
1971 *
1972 * @ingroup xlat_functions
1973 */
1975 UNUSED xlat_ctx_t const *xctx,
1976 request_t *request, fr_value_box_list_t *args)
1977{
1978 fr_value_box_t *result;
1979 char const *p, *end;
1980 uint8_t *bin;
1981 size_t len, outlen;
1983 fr_value_box_t *list, *hex;
1984
1985 XLAT_ARGS(args, &list);
1986
1987 while ((hex = fr_value_box_list_pop_head(&list->vb_group))) {
1988 len = hex->vb_length;
1989 if ((len > 1) && (len & 0x01)) {
1990 REDEBUG("Input data length must be >1 and even, got %zu", len);
1991 return XLAT_ACTION_FAIL;
1992 }
1993
1994 p = hex->vb_strvalue;
1995 end = p + len;
1996
1997 /*
1998 * Look for 0x at the start of the string, and ignore if we see it.
1999 */
2000 if ((p[0] == '0') && (p[1] == 'x')) {
2001 p += 2;
2002 len -=2;
2003 }
2004
2005 /*
2006 * Zero length octets string
2007 */
2008 if (p == end) continue;
2009
2010 outlen = len / 2;
2011
2012 MEM(result = fr_value_box_alloc_null(ctx));
2013 MEM(fr_value_box_mem_alloc(result, &bin, result, NULL, outlen, false) == 0);
2014 fr_base16_decode(&err, &FR_DBUFF_TMP(bin, outlen), &FR_SBUFF_IN(p, end - p), true);
2015 if (err) {
2016 REDEBUG2("Invalid hex string");
2017 talloc_free(result);
2018 return XLAT_ACTION_FAIL;
2019 }
2020
2022 fr_dcursor_append(out, result);
2023 }
2024
2025 return XLAT_ACTION_DONE;
2026}
2027
2029 { .required = true, .single = true, .type = FR_TYPE_TIME_DELTA },
2031};
2032
2033/** Block for the specified duration
2034 *
2035 * This is for developer use only to simulate blocking, synchronous I/O.
2036 * For normal use, use the %delay() xlat instead.
2037 *
2038 * Example:
2039@verbatim
2040%block(1s)
2041@endverbatim
2042 *
2043 * @ingroup xlat_functions
2044 */
2046 UNUSED xlat_ctx_t const *xctx,
2047 UNUSED request_t *request, fr_value_box_list_t *args)
2048{
2049 fr_value_box_t *delay;
2050 fr_value_box_t *vb;
2051 struct timespec ts_in, ts_remain = {};
2052
2053 XLAT_ARGS(args, &delay);
2054
2055 ts_in = fr_time_delta_to_timespec(delay->vb_time_delta);
2056
2057 (void)nanosleep(&ts_in, &ts_remain);
2058
2059 MEM(vb = fr_value_box_alloc(ctx, FR_TYPE_TIME_DELTA, NULL));
2060 vb->vb_time_delta = fr_time_delta_sub(delay->vb_time_delta,
2061 fr_time_delta_from_timespec(&ts_remain));
2063
2064 return XLAT_ACTION_DONE;
2065}
2066
2068 { .required = true, .single = true, .type = FR_TYPE_VOID },
2069 { .type = FR_TYPE_VOID },
2070 { .variadic = XLAT_ARG_VARIADIC_EMPTY_KEEP, .type = FR_TYPE_VOID },
2072};
2073
2074/** Cast one or more output value-boxes to the given type
2075 *
2076 * First argument of is type to cast to.
2077 *
2078 * Example:
2079@verbatim
2080%cast('string', %{request[*]}) results in all of the input boxes being cast to string/
2081@endverbatim
2082 *
2083 * @ingroup xlat_functions
2084 */
2086 UNUSED xlat_ctx_t const *xctx,
2087 request_t *request, fr_value_box_list_t *args)
2088{
2090 fr_value_box_t *arg;
2092 fr_dict_attr_t const *time_res = NULL;
2093
2094 XLAT_ARGS(args, &name);
2095
2096 /*
2097 * Get the type, which can be in one of a few formats.
2098 */
2099 if (fr_type_is_numeric(name->type)) {
2101 RPEDEBUG("Failed parsing '%pV' as a numerical data type", name);
2102 return XLAT_ACTION_FAIL;
2103 }
2104 type = name->vb_uint8;
2105
2106 } else {
2107 if (name->type != FR_TYPE_STRING) {
2109 RPEDEBUG("Failed parsing '%pV' as a string data type", name);
2110 return XLAT_ACTION_FAIL;
2111 }
2112 }
2113
2115 if (type == FR_TYPE_NULL) {
2116 if ((time_res = xlat_time_res_attr(name->vb_strvalue)) == NULL) {
2117 RDEBUG("Unknown data type '%s'", name->vb_strvalue);
2118 return XLAT_ACTION_FAIL;
2119 }
2120
2122 }
2123 }
2124
2125 (void) fr_value_box_list_pop_head(args);
2126
2127 /*
2128 * When we cast nothing to a string / octets, the result is an empty string/octets.
2129 */
2130 if (unlikely(!fr_value_box_list_head(args))) {
2131 if ((type == FR_TYPE_STRING) || (type == FR_TYPE_OCTETS)) {
2132 fr_value_box_t *dst;
2133
2134 MEM(dst = fr_value_box_alloc(ctx, type, NULL));
2135 fr_dcursor_append(out, dst);
2136 VALUE_BOX_LIST_VERIFY((fr_value_box_list_t *)out->dlist);
2137
2138 return XLAT_ACTION_DONE;
2139 }
2140
2141 RDEBUG("No data for cast to '%s'", fr_type_to_str(type));
2142 return XLAT_ACTION_FAIL;
2143 }
2144
2145 /*
2146 * Cast to string means *print* to string.
2147 */
2148 if (type == FR_TYPE_STRING) {
2149 fr_sbuff_t *agg;
2150 fr_value_box_t *dst;
2151
2153
2154 FR_SBUFF_TALLOC_THREAD_LOCAL(&agg, 256, SIZE_MAX);
2155
2156 MEM(dst = fr_value_box_alloc_null(ctx));
2158
2159 if (fr_value_box_list_concat_as_string(dst, agg, args, NULL, 0, NULL,
2161 RPEDEBUG("Failed concatenating string");
2162 return XLAT_ACTION_FAIL;
2163 }
2164
2165 fr_value_box_bstrndup(dst, dst, NULL, fr_sbuff_start(agg), fr_sbuff_used(agg), false);
2166 fr_dcursor_append(out, dst);
2167 VALUE_BOX_LIST_VERIFY((fr_value_box_list_t *)out->dlist);
2168
2169 return XLAT_ACTION_DONE;
2170 }
2171
2172 /*
2173 * Copy inputs to outputs, casting them along the way.
2174 */
2175 arg = NULL;
2176 while ((arg = fr_value_box_list_next(args, arg)) != NULL) {
2177 fr_value_box_t *vb, *p;
2178
2179 fr_assert(arg->type == FR_TYPE_GROUP);
2180
2181 vb = fr_value_box_list_head(&arg->vb_group);
2182 while (vb) {
2183 p = fr_value_box_list_remove(&arg->vb_group, vb);
2184
2185 if (fr_value_box_cast_in_place(vb, vb, type, time_res) < 0) {
2186 RPEDEBUG("Failed casting %pV to data type '%s'", vb, fr_type_to_str(type));
2187 return XLAT_ACTION_FAIL;
2188 }
2190 vb = fr_value_box_list_next(&arg->vb_group, p);
2191 }
2192 }
2193 VALUE_BOX_LIST_VERIFY((fr_value_box_list_t *)out->dlist);
2194
2195 return XLAT_ACTION_DONE;
2196}
2197
2199 { .required = true, .type = FR_TYPE_VOID },
2200 { .concat = true, .type = FR_TYPE_STRING },
2202};
2203
2204/** Concatenate string representation of values of given attributes using separator
2205 *
2206 * First argument of is the list of attributes to concatenate, followed
2207 * by an optional separator
2208 *
2209 * Example:
2210@verbatim
2211%concat(%{request.[*]}, ',') == "<attr1value>,<attr2value>,<attr3value>,..."
2212%concat(%{Tmp-String-0[*]}, '. ') == "<str1value>. <str2value>. <str3value>. ..."
2213%concat(%join(%{User-Name}, %{Calling-Station-Id}), ', ') == "bob, aa:bb:cc:dd:ee:ff"
2214@endverbatim
2215 *
2216 * @ingroup xlat_functions
2217 */
2219 UNUSED xlat_ctx_t const *xctx,
2220 request_t *request, fr_value_box_list_t *args)
2221{
2222 fr_value_box_t *result;
2223 fr_value_box_t *list;
2224 fr_value_box_t *separator;
2225 fr_value_box_list_t *to_concat;
2226 char *buff;
2227 char const *sep;
2228
2229 XLAT_ARGS(args, &list, &separator);
2230
2231 sep = (separator) ? separator->vb_strvalue : "";
2232 to_concat = &list->vb_group;
2233
2234 result = fr_value_box_alloc(ctx, FR_TYPE_STRING, NULL);
2235 if (!result) {
2236 error:
2237 RPEDEBUG("Failed concatenating input");
2238 return XLAT_ACTION_FAIL;
2239 }
2240
2241 buff = fr_value_box_list_aprint(result, to_concat, sep, NULL);
2242 if (!buff) goto error;
2243
2245
2246 fr_dcursor_append(out, result);
2247
2248 return XLAT_ACTION_DONE;
2249}
2250
2252 { .required = true, .type = FR_TYPE_OCTETS },
2254};
2255
2256/** Print data as hex, not as VALUE.
2257 *
2258 * Example:
2259@verbatim
2260%hex("foobar") == "666f6f626172"
2261@endverbatim
2262 *
2263 * @see #xlat_func_bin
2264 *
2265 * @ingroup xlat_functions
2266 */
2268 UNUSED xlat_ctx_t const *xctx,
2269 UNUSED request_t *request, fr_value_box_list_t *args)
2270{
2271 char *new_buff;
2272 fr_value_box_t *list, *bin;
2273 fr_value_box_t safety;
2274
2275 XLAT_ARGS(args, &list);
2276
2277 while ((bin = fr_value_box_list_pop_head(&list->vb_group))) {
2278 fr_value_box_safety_copy(&safety, bin);
2279
2280 /*
2281 * Use existing box, but with new buffer
2282 */
2283 MEM(new_buff = talloc_zero_array(bin, char, (bin->vb_length * 2) + 1));
2284 if (bin->vb_length) {
2285 fr_base16_encode(&FR_SBUFF_OUT(new_buff, (bin->vb_length * 2) + 1),
2286 &FR_DBUFF_TMP(bin->vb_octets, bin->vb_length));
2288 fr_value_box_strdup_shallow(bin, NULL, new_buff, false);
2289 /*
2290 * Zero length binary > zero length hex string
2291 */
2292 } else {
2294 fr_value_box_strdup(bin, bin, NULL, "", false);
2295 }
2296
2297 fr_value_box_safety_copy(bin, &safety);
2298 fr_dcursor_append(out, bin);
2299 }
2300
2301 return XLAT_ACTION_DONE;
2302}
2303
2308
2309static xlat_action_t xlat_hmac(TALLOC_CTX *ctx, fr_dcursor_t *out,
2310 fr_value_box_list_t *args, uint8_t *digest, int digest_len, hmac_type type)
2311{
2312 fr_value_box_t *vb, *data, *key;
2313
2314 XLAT_ARGS(args, &data, &key);
2315
2316 if (type == HMAC_MD5) {
2317 /* coverity[dereference] */
2318 fr_hmac_md5(digest, data->vb_octets, data->vb_length, key->vb_octets, key->vb_length);
2319 } else if (type == HMAC_SHA1) {
2320 /* coverity[dereference] */
2321 fr_hmac_sha1(digest, data->vb_octets, data->vb_length, key->vb_octets, key->vb_length);
2322 }
2323
2324 MEM(vb = fr_value_box_alloc_null(ctx));
2325 fr_value_box_memdup(vb, vb, NULL, digest, digest_len, false);
2326
2328
2329 return XLAT_ACTION_DONE;
2330}
2331
2333 { .required = true, .concat = true, .type = FR_TYPE_STRING },
2334 { .required = true, .concat = true, .type = FR_TYPE_STRING },
2336};
2337
2338/** Generate the HMAC-MD5 of a string or attribute
2339 *
2340 * Example:
2341@verbatim
2342%hmacmd5('foo', 'bar') == "0x31b6db9e5eb4addb42f1a6ca07367adc"
2343@endverbatim
2344 *
2345 * @ingroup xlat_functions
2346 */
2348 UNUSED xlat_ctx_t const *xctx,
2349 UNUSED request_t *request, fr_value_box_list_t *in)
2350{
2351 uint8_t digest[MD5_DIGEST_LENGTH];
2352 return xlat_hmac(ctx, out, in, digest, MD5_DIGEST_LENGTH, HMAC_MD5);
2353}
2354
2355
2356/** Generate the HMAC-SHA1 of a string or attribute
2357 *
2358 * Example:
2359@verbatim
2360%hmacsha1('foo', 'bar') == "0x85d155c55ed286a300bd1cf124de08d87e914f3a"
2361@endverbatim
2362 *
2363 * @ingroup xlat_functions
2364 */
2366 UNUSED xlat_ctx_t const *xctx,
2367 UNUSED request_t *request, fr_value_box_list_t *in)
2368{
2370 return xlat_hmac(ctx, out, in, digest, SHA1_DIGEST_LENGTH, HMAC_SHA1);
2371}
2372
2374 { .required = true, .type = FR_TYPE_VOID },
2375 { .variadic = XLAT_ARG_VARIADIC_EMPTY_SQUASH, .type = FR_TYPE_VOID },
2377};
2378
2379/** Join a series of arguments to form a single list
2380 *
2381 * null boxes are not preserved.
2382 */
2384 UNUSED xlat_ctx_t const *xctx,
2385 UNUSED request_t *request, fr_value_box_list_t *in)
2386{
2388 fr_assert(arg->type == FR_TYPE_GROUP);
2389
2390 fr_value_box_list_foreach(&arg->vb_group, vb) {
2391 xlat_arg_copy_out(ctx, out, &arg->vb_group, vb);
2392 }
2393 }
2394 return XLAT_ACTION_DONE;
2395}
2396
2397static void ungroup(fr_dcursor_t *out, fr_value_box_list_t *in)
2398{
2399 fr_value_box_t *vb;
2400
2401 while ((vb = fr_value_box_list_pop_head(in)) != NULL) {
2402 if (vb->type != FR_TYPE_GROUP) {
2404 continue;
2405 }
2406 talloc_free(vb);
2407 }
2408}
2409
2410/** Ungroups all of its arguments into one flat list.
2411 *
2412 */
2414 UNUSED xlat_ctx_t const *xctx,
2415 UNUSED request_t *request, fr_value_box_list_t *in)
2416{
2417 fr_value_box_t *arg = NULL;
2418
2419 while ((arg = fr_value_box_list_next(in, arg)) != NULL) {
2420 fr_assert(arg->type == FR_TYPE_GROUP);
2421
2422 ungroup(out, &arg->vb_group);
2423 }
2424 return XLAT_ACTION_DONE;
2425}
2426
2428 { .single = true, .variadic = XLAT_ARG_VARIADIC_EMPTY_KEEP, .type = FR_TYPE_VOID },
2430};
2431
2432/** Return the on-the-wire size of the boxes in bytes
2433 *
2434 * skips null values
2435 *
2436 * Example:
2437@verbatim
2438%length(foobar) == 6
2439%length(%bin("0102030005060708")) == 8
2440@endverbatim
2441 *
2442 * @see #xlat_func_strlen
2443 *
2444 * @ingroup xlat_functions
2445 */
2447 UNUSED xlat_ctx_t const *xctx,
2448 UNUSED request_t *request, fr_value_box_list_t *in)
2449
2450{
2453
2454 MEM(my = fr_value_box_alloc(ctx, FR_TYPE_SIZE, NULL));
2455 if (!fr_type_is_null(vb->type)) my->vb_size = fr_value_box_network_length(vb);
2457 }
2458
2459 return XLAT_ACTION_DONE;
2460}
2461
2462
2464 { .concat = true, .type = FR_TYPE_OCTETS },
2466};
2467
2468/** Calculate the MD4 hash of a string or attribute.
2469 *
2470 * Example:
2471@verbatim
2472%md4("foo") == "0ac6700c491d70fb8650940b1ca1e4b2"
2473@endverbatim
2474 *
2475 * @ingroup xlat_functions
2476 */
2478 UNUSED xlat_ctx_t const *xctx,
2479 UNUSED request_t *request, fr_value_box_list_t *args)
2480{
2481 uint8_t digest[MD4_DIGEST_LENGTH];
2482 fr_value_box_t *vb;
2483 fr_value_box_t *in_head;
2484
2485 XLAT_ARGS(args, &in_head);
2486
2487 if (in_head) {
2488 fr_md4_calc(digest, in_head->vb_octets, in_head->vb_length);
2489 } else {
2490 /* Digest of empty string */
2491 fr_md4_calc(digest, NULL, 0);
2492 }
2493
2494 MEM(vb = fr_value_box_alloc_null(ctx));
2495 fr_value_box_memdup(vb, vb, NULL, digest, sizeof(digest), false);
2496
2498 VALUE_BOX_LIST_VERIFY((fr_value_box_list_t *)out->dlist);
2499
2500 return XLAT_ACTION_DONE;
2501}
2502
2504 { .concat = true, .type = FR_TYPE_OCTETS },
2506};
2507
2508/** Calculate the MD5 hash of a string or attribute.
2509 *
2510 * Example:
2511@verbatim
2512%md5("foo") == "acbd18db4cc2f85cedef654fccc4a4d8"
2513@endverbatim
2514 *
2515 * @ingroup xlat_functions
2516 */
2518 UNUSED xlat_ctx_t const *xctx,
2519 UNUSED request_t *request, fr_value_box_list_t *args)
2520{
2521 uint8_t digest[MD5_DIGEST_LENGTH];
2522 fr_value_box_t *vb;
2523 fr_value_box_t *in_head;
2524
2525 XLAT_ARGS(args, &in_head);
2526
2527 if (in_head) {
2528 fr_md5_calc(digest, in_head->vb_octets, in_head->vb_length);
2529 } else {
2530 /* Digest of empty string */
2531 fr_md5_calc(digest, NULL, 0);
2532 }
2533
2534 MEM(vb = fr_value_box_alloc_null(ctx));
2535 fr_value_box_memdup(vb, vb, NULL, digest, sizeof(digest), false);
2536
2538
2539 return XLAT_ACTION_DONE;
2540}
2541
2542
2543/** Encode attributes as a series of string attribute/value pairs
2544 *
2545 * This is intended to serialize one or more attributes as a comma
2546 * delimited string.
2547 *
2548 * Example:
2549@verbatim
2550%pairs.print(request.[*]) == 'User-Name = "foo"User-Password = "bar"'
2551%concat(%pairs.print.print(request.[*]), ', ') == 'User-Name = "foo", User-Password = "bar"'
2552@endverbatim
2553 *
2554 * @see #xlat_func_concat
2555 *
2556 * @ingroup xlat_functions
2557 */
2559 UNUSED xlat_ctx_t const *xctx,
2560 request_t *request, fr_value_box_list_t *args)
2561{
2562 fr_pair_t *vp;
2563 fr_dcursor_t *cursor;
2564 fr_value_box_t *vb;
2565 fr_value_box_t *in_head;
2566
2567 XLAT_ARGS(args, &in_head);
2568
2569 cursor = fr_value_box_get_cursor(in_head);
2570
2571 for (vp = fr_dcursor_current(cursor);
2572 vp;
2573 vp = fr_dcursor_next(cursor)) {
2574 char *buff;
2575
2576 MEM(vb = fr_value_box_alloc_null(ctx));
2577 if (unlikely(fr_pair_aprint(vb, &buff, NULL, vp) < 0)) {
2578 RPEDEBUG("Failed printing pair");
2579 talloc_free(vb);
2580 return XLAT_ACTION_FAIL;
2581 }
2582
2583 fr_value_box_bstrdup_buffer_shallow(NULL, vb, NULL, buff, false);
2585
2586 VALUE_BOX_VERIFY(vb);
2587 }
2588
2589 return XLAT_ACTION_DONE;
2590}
2591
2593 { .required = true, .single = true, .type = FR_TYPE_UINT32 },
2595};
2596
2597/** Generate a random integer value
2598 *
2599 * For "N = %rand(MAX)", 0 <= N < MAX
2600 *
2601 * Example:
2602@verbatim
2603%rand(100) == 42
2604@endverbatim
2605 *
2606 * @ingroup xlat_functions
2607 */
2609 UNUSED xlat_ctx_t const *xctx,
2610 UNUSED request_t *request, fr_value_box_list_t *in)
2611{
2612 int64_t result;
2613 fr_value_box_t *vb;
2614 fr_value_box_t *in_head = fr_value_box_list_head(in);
2615
2616 result = in_head->vb_uint32;
2617
2618 /* Make sure it isn't too big */
2619 if (result > (1 << 30)) result = (1 << 30);
2620
2621 result *= fr_rand(); /* 0..2^32-1 */
2622 result >>= 32;
2623
2624 MEM(vb = fr_value_box_alloc(ctx, FR_TYPE_UINT64, NULL));
2625 vb->vb_uint64 = result;
2626
2628
2629 return XLAT_ACTION_DONE;
2630}
2631
2633 { .required = true, .concat = true, .type = FR_TYPE_STRING },
2635};
2636
2637/** Generate a string of random chars
2638 *
2639 * Build strings of random chars, useful for generating tokens and passcodes
2640 * Format similar to String::Random.
2641 *
2642 * Format characters may include the following, and may be
2643 * preceded by a repetition count:
2644 * - "c" lowercase letters
2645 * - "C" uppercase letters
2646 * - "n" numbers
2647 * - "a" alphanumeric
2648 * - "!" punctuation
2649 * - "." alphanumeric + punctuation
2650 * - "s" alphanumeric + "./"
2651 * - "o" characters suitable for OTP (easily confused removed)
2652 * - "b" binary data
2653 *
2654 * Example:
2655@verbatim
2656%randstr("CCCC!!cccnnn") == "IPFL>{saf874"
2657%randstr("42o") == "yHdupUwVbdHprKCJRYfGbaWzVwJwUXG9zPabdGAhM9"
2658%hex(%randstr("bbbb")) == "a9ce04f3"
2659%hex(%randstr("8b")) == "fe165529f9f66839"
2660@endverbatim
2661 * @ingroup xlat_functions
2662 */
2664 UNUSED xlat_ctx_t const *xctx,
2665 request_t *request, fr_value_box_list_t *args)
2666{
2667 /*
2668 * Lookup tables for randstr char classes
2669 */
2670 static char randstr_punc[] = "!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~";
2671 static char randstr_salt[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmopqrstuvwxyz/.";
2672
2673 /*
2674 * Characters humans rarely confuse. Reduces char set considerably
2675 * should only be used for things such as one time passwords.
2676 */
2677 static char randstr_otp[] = "469ACGHJKLMNPQRUVWXYabdfhijkprstuvwxyz";
2678
2679 char const *p, *start, *end;
2680 char *endptr;
2681 char *buff_p;
2682 unsigned int result;
2683 unsigned int reps;
2684 size_t outlen = 0;
2685 fr_value_box_t* vb;
2686 fr_value_box_t *in_head;
2687
2688 XLAT_ARGS(args, &in_head);
2689
2690 /** Max repetitions of a single character class
2691 *
2692 */
2693#define REPETITION_MAX 1024
2694
2695 start = p = in_head->vb_strvalue;
2696 end = p + in_head->vb_length;
2697
2698 /*
2699 * Calculate size of output
2700 */
2701 while (p < end) {
2702 /*
2703 * Repetition modifiers.
2704 *
2705 * We limit it to REPETITION_MAX, because we don't want
2706 * utter stupidity.
2707 */
2708 if (isdigit((uint8_t) *p)) {
2709 reps = strtol(p, &endptr, 10);
2710 if (reps > REPETITION_MAX) reps = REPETITION_MAX;
2711 outlen += reps;
2712 p = endptr;
2713 } else {
2714 outlen++;
2715 }
2716 p++;
2717 }
2718
2719 MEM(vb = fr_value_box_alloc_null(ctx));
2720 MEM(fr_value_box_bstr_alloc(vb, &buff_p, vb, NULL, outlen, false) == 0);
2721
2722 /* Reset p to start position */
2723 p = start;
2724
2725 while (p < end) {
2726 size_t i;
2727
2728 if (isdigit((uint8_t) *p)) {
2729 reps = strtol(p, &endptr, 10);
2730 if (reps > REPETITION_MAX) {
2731 reps = REPETITION_MAX;
2732 RMARKER(L_WARN, L_DBG_LVL_2, start, p - start,
2733 "Forcing repetition to %u", (unsigned int)REPETITION_MAX);
2734 }
2735 p = endptr;
2736 } else {
2737 reps = 1;
2738 }
2739
2740 for (i = 0; i < reps; i++) {
2741 result = fr_rand();
2742 switch (*p) {
2743 /*
2744 * Lowercase letters
2745 */
2746 case 'c':
2747 *buff_p++ = 'a' + (result % 26);
2748 break;
2749
2750 /*
2751 * Uppercase letters
2752 */
2753 case 'C':
2754 *buff_p++ = 'A' + (result % 26);
2755 break;
2756
2757 /*
2758 * Numbers
2759 */
2760 case 'n':
2761 *buff_p++ = '0' + (result % 10);
2762 break;
2763
2764 /*
2765 * Alpha numeric
2766 */
2767 case 'a':
2768 *buff_p++ = randstr_salt[result % (sizeof(randstr_salt) - 3)];
2769 break;
2770
2771 /*
2772 * Punctuation
2773 */
2774 case '!':
2775 *buff_p++ = randstr_punc[result % (sizeof(randstr_punc) - 1)];
2776 break;
2777
2778 /*
2779 * Alpha numeric + punctuation
2780 */
2781 case '.':
2782 *buff_p++ = '!' + (result % 95);
2783 break;
2784
2785 /*
2786 * Alpha numeric + salt chars './'
2787 */
2788 case 's':
2789 *buff_p++ = randstr_salt[result % (sizeof(randstr_salt) - 1)];
2790 break;
2791
2792 /*
2793 * Chars suitable for One Time Password tokens.
2794 * Alpha numeric with easily confused char pairs removed.
2795 */
2796 case 'o':
2797 *buff_p++ = randstr_otp[result % (sizeof(randstr_otp) - 1)];
2798 break;
2799
2800 /*
2801 * Binary data - Copy between 1-4 bytes at a time
2802 */
2803 case 'b':
2804 {
2805 size_t copy = (reps - i) > sizeof(result) ? sizeof(result) : reps - i;
2806
2807 memcpy(buff_p, (uint8_t *)&result, copy);
2808 buff_p += copy;
2809 i += (copy - 1); /* Loop +1 */
2810 }
2811 break;
2812
2813 default:
2814 REDEBUG("Invalid character class '%c'", *p);
2815 talloc_free(vb);
2816
2817 return XLAT_ACTION_FAIL;
2818 }
2819 }
2820
2821 p++;
2822 }
2823
2824 *buff_p++ = '\0';
2825
2827
2828 return XLAT_ACTION_DONE;
2829}
2830
2831/** Convert a UUID in an array of uint32_t to the conventional string representation.
2832 */
2833static int uuid_print_vb(fr_value_box_t *vb, uint32_t vals[4])
2834{
2835 char buffer[36];
2836 int i, j = 0;
2837
2838#define UUID_CHARS(_v, _num) for (i = 0; i < _num; i++) { \
2839 buffer[j++] = fr_base16_alphabet_encode_lc[(uint8_t)((vals[_v] & 0xf0000000) >> 28)]; \
2840 vals[_v] = vals[_v] << 4; \
2841 }
2842
2843 UUID_CHARS(0, 8)
2844 buffer[j++] = '-';
2845 UUID_CHARS(1, 4)
2846 buffer[j++] = '-';
2847 UUID_CHARS(1, 4);
2848 buffer[j++] = '-';
2849 UUID_CHARS(2, 4);
2850 buffer[j++] = '-';
2851 UUID_CHARS(2, 4);
2852 UUID_CHARS(3, 8);
2853
2854 return fr_value_box_bstrndup(vb, vb, NULL, buffer, sizeof(buffer), false);
2855}
2856
2857static inline void uuid_set_version(uint32_t vals[4], uint8_t version)
2858{
2859 /*
2860 * The version is indicated by the upper 4 bits of byte 7 - the 3rd byte of vals[1]
2861 */
2862 vals[1] = (vals[1] & 0xffff0fff) | (((uint32_t)version & 0x0f) << 12);
2863}
2864
2865static inline void uuid_set_variant(uint32_t vals[4], uint8_t variant)
2866{
2867 /*
2868 * The variant is indicated by the first 1, 2 or 3 bits of byte 9
2869 * The number of bits is determined by the variant.
2870 */
2871 switch (variant) {
2872 case 0:
2873 vals[2] = vals[2] & 0x7fffffff;
2874 break;
2875
2876 case 1:
2877 vals[2] = (vals[2] & 0x3fffffff) | 0x80000000;
2878 break;
2879
2880 case 2:
2881 vals[2] = (vals[2] & 0x3fffffff) | 0xc0000000;
2882 break;
2883
2884 case 3:
2885 vals[2] = vals[2] | 0xe0000000;
2886 break;
2887 }
2888}
2889
2890/** Generate a version 4 UUID
2891 *
2892 * Version 4 UUIDs are all random except the version and variant fields
2893 *
2894 * Example:
2895@verbatim
2896%uuid.v4 == "cba48bda-641c-42ae-8173-d97aa04f888a"
2897@endverbatim
2898 * @ingroup xlat_functions
2899 */
2900static xlat_action_t xlat_func_uuid_v4(TALLOC_CTX *ctx, fr_dcursor_t *out, UNUSED xlat_ctx_t const *xctx,
2901 UNUSED request_t *request, UNUSED fr_value_box_list_t *args)
2902{
2903 fr_value_box_t *vb;
2904 uint32_t vals[4];
2905 int i;
2906
2907 MEM(vb = fr_value_box_alloc(ctx, FR_TYPE_STRING, NULL));
2908
2909 /*
2910 * A type 4 UUID is all random except a few bits.
2911 * Start with 128 bits of random.
2912 */
2913 for (i = 0; i < 4; i++) vals[i] = fr_rand();
2914
2915 /*
2916 * Set the version and variant fields
2917 */
2918 uuid_set_version(vals, 4);
2919 uuid_set_variant(vals, 1);
2920
2921 if (uuid_print_vb(vb, vals) < 0) {
2922 talloc_free(vb);
2923 return XLAT_ACTION_FAIL;
2924 }
2925
2927 return XLAT_ACTION_DONE;
2928}
2929
2930/** Generate a version 7 UUID
2931 *
2932 * Version 7 UUIDs use 48 bits of unix millisecond epoch and 74 bits of random
2933 *
2934 * Example:
2935@verbatim
2936%uuid.v7 == "019a58d8-8524-7342-aa07-c0fa2bba6a4e"
2937@endverbatim
2938 * @ingroup xlat_functions
2939 */
2940static xlat_action_t xlat_func_uuid_v7(TALLOC_CTX *ctx, fr_dcursor_t *out, UNUSED xlat_ctx_t const *xctx,
2941 UNUSED request_t *request, UNUSED fr_value_box_list_t *args)
2942{
2943 fr_value_box_t *vb;
2944 uint32_t vals[4];
2945 int i;
2946 uint64_t now;
2947
2948 MEM(vb = fr_value_box_alloc(ctx, FR_TYPE_STRING, NULL));
2949
2950 /*
2951 * A type 7 UUID has random data from bit 48
2952 * Start with random from bit 32 - since fr_rand is uint32
2953 */
2954 for (i = 1; i < 4; i++) vals[i] = fr_rand();
2955
2956 /*
2957 * The millisecond epoch fills the first 48 bits
2958 */
2959 now = fr_time_to_msec(fr_time());
2960 now = now << 16;
2961 vals[0] = now >> 32;
2962 vals[1] = (vals[1] & 0x0000ffff) | (now & 0xffff0000);
2963
2964 /*
2965 * Set the version and variant fields
2966 */
2967 uuid_set_version(vals, 7);
2968 uuid_set_variant(vals, 1);
2969
2970 if (uuid_print_vb(vb, vals) < 0) return XLAT_ACTION_FAIL;
2971
2973 return XLAT_ACTION_DONE;
2974}
2975
2977 { .required = true, .type = FR_TYPE_UINT64 },
2978 { .required = false, .type = FR_TYPE_UINT64 },
2979 { .required = false, .type = FR_TYPE_UINT64 },
2981};
2982
2983/** Generate a range of uint64 numbers
2984 *
2985 * Example:
2986@verbatim
2987%range(end) - 0..end
2988%rang(start, end)
2989%range(start,end, step)
2990@endverbatim
2991 * @ingroup xlat_functions
2992 */
2994 UNUSED xlat_ctx_t const *xctx,
2995 request_t *request, fr_value_box_list_t *args)
2996{
2997 fr_value_box_t *start_vb, *end_vb, *step_vb;
2998 fr_value_box_t *dst;
2999 uint64_t i, start, end, step;
3000
3001 XLAT_ARGS(args, &start_vb, &end_vb, &step_vb);
3002
3003 if (step_vb) {
3004 start = fr_value_box_list_head(&start_vb->vb_group)->vb_uint64;
3005 end = fr_value_box_list_head(&end_vb->vb_group)->vb_uint64;
3006 step = fr_value_box_list_head(&step_vb->vb_group)->vb_uint64;
3007
3008 } else if (end_vb) {
3009 start = fr_value_box_list_head(&start_vb->vb_group)->vb_uint64;
3010 end = fr_value_box_list_head(&end_vb->vb_group)->vb_uint64;
3011 step = 1;
3012
3013 } else {
3014 start = 0;
3015 end = fr_value_box_list_head(&start_vb->vb_group)->vb_uint64;
3016 step = 1;
3017 }
3018
3019 if (end <= start) {
3020 REDEBUG("Invalid range - 'start' must be less than 'end'");
3021 return XLAT_ACTION_FAIL;
3022 }
3023
3024 if (!step) {
3025 REDEBUG("Invalid range - 'step' must be greater than zero");
3026 return XLAT_ACTION_FAIL;
3027 }
3028
3029 if (step > (end - start)) {
3030 REDEBUG("Invalid range - 'step' must allow for at least one result");
3031 return XLAT_ACTION_FAIL;
3032 }
3033
3034 if (((end - start) / step) > 1000) {
3035 REDEBUG("Invalid range - Too many results");
3036 return XLAT_ACTION_FAIL;
3037 }
3038
3039 for (i = start; i < end; i += step) {
3040 MEM(dst = fr_value_box_alloc(ctx, FR_TYPE_UINT64, NULL));
3041 dst->vb_uint64 = i;
3042 fr_dcursor_append(out, dst);
3043 }
3044
3045 return XLAT_ACTION_DONE;
3046}
3047
3048static int CC_HINT(nonnull(2,3)) regex_xlat_escape(UNUSED request_t *request, fr_value_box_t *vb, UNUSED void *uctx)
3049{
3050 ssize_t slen;
3051 fr_sbuff_t *out = NULL;
3052 fr_value_box_entry_t entry;
3053
3054 FR_SBUFF_TALLOC_THREAD_LOCAL(&out, 256, 4096);
3055
3056 slen = fr_value_box_print(out, vb, &regex_escape_rules);
3057 if (slen < 0) return -1;
3058
3059 entry = vb->entry;
3061 (void) fr_value_box_bstrndup(vb, vb, NULL, fr_sbuff_start(out), fr_sbuff_used(out), false);
3062 vb->entry = entry;
3063
3064 return 0;
3065}
3066
3071
3072
3073/** Get named subcapture value from previous regex
3074 *
3075 * Example:
3076@verbatim
3077if ("foo" =~ /^(?<name>.*)/) {
3078 noop
3079}
3080%regex.match(name) == "foo"
3081@endverbatim
3082 *
3083 * @ingroup xlat_functions
3084 */
3086 UNUSED xlat_ctx_t const *xctx,
3087 request_t *request, fr_value_box_list_t *in)
3088{
3089 fr_value_box_t *in_head = fr_value_box_list_head(in);
3090
3091 /*
3092 * Find the first child of the first argument group
3093 */
3094 fr_value_box_t *arg = fr_value_box_list_head(&in_head->vb_group);
3095
3096 /*
3097 * Return the complete capture if no other capture is specified
3098 */
3099 if (!arg) {
3100 fr_value_box_t *vb;
3101
3102 MEM(vb = fr_value_box_alloc_null(ctx));
3103 if (regex_request_to_sub(vb, vb, request, 0) < 0) {
3104 REDEBUG2("No previous regex capture");
3105 talloc_free(vb);
3106 return XLAT_ACTION_FAIL;
3107 }
3108
3110
3111 return XLAT_ACTION_DONE;
3112 }
3113
3114 switch (arg->type) {
3115 /*
3116 * If the input is an integer value then get an
3117 * arbitrary subcapture index.
3118 */
3119 case FR_TYPE_NUMERIC:
3120 {
3121 fr_value_box_t idx;
3122 fr_value_box_t *vb;
3123
3124 if (fr_value_box_list_next(in, in_head)) {
3125 REDEBUG("Only one subcapture argument allowed");
3126 return XLAT_ACTION_FAIL;
3127 }
3128
3129 if (fr_value_box_cast(NULL, &idx, FR_TYPE_UINT32, NULL, arg) < 0) {
3130 RPEDEBUG("Bad subcapture index");
3131 return XLAT_ACTION_FAIL;
3132 }
3133
3134 MEM(vb = fr_value_box_alloc_null(ctx));
3135 if (regex_request_to_sub(vb, vb, request, idx.vb_uint32) < 0) {
3136 REDEBUG2("No previous numbered regex capture group '%u'", idx.vb_uint32);
3137 talloc_free(vb);
3138 return XLAT_ACTION_DONE;
3139 }
3141
3142 return XLAT_ACTION_DONE;
3143 }
3144
3145 default:
3146#if defined(HAVE_REGEX_PCRE) || defined(HAVE_REGEX_PCRE2)
3147 {
3148 fr_value_box_t *vb;
3149
3150 /*
3151 * Concatenate all input
3152 */
3154 arg, &in_head->vb_group, FR_TYPE_STRING,
3156 SIZE_MAX) < 0) {
3157 RPEDEBUG("Failed concatenating input");
3158 return XLAT_ACTION_FAIL;
3159 }
3160
3161 MEM(vb = fr_value_box_alloc_null(ctx));
3162 if (regex_request_to_sub_named(vb, vb, request, arg->vb_strvalue) < 0) {
3163 REDEBUG2("No previous named regex capture group '%s'", arg->vb_strvalue);
3164 talloc_free(vb);
3165 return XLAT_ACTION_DONE; /* NOT an error, just an empty result */
3166 }
3168
3169 return XLAT_ACTION_DONE;
3170 }
3171#else
3172 RDEBUG("Named regex captures are not supported (they require libpcre2)");
3173 return XLAT_ACTION_FAIL;
3174#endif
3175 }
3176}
3177
3179 { .concat = true, .type = FR_TYPE_OCTETS },
3181};
3182
3183/** Calculate the SHA1 hash of a string or attribute.
3184 *
3185 * Example:
3186@verbatim
3187%sha1(foo) == "0beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a33"
3188@endverbatim
3189 *
3190 * @ingroup xlat_functions
3191 */
3193 UNUSED xlat_ctx_t const *xctx,
3194 UNUSED request_t *request, fr_value_box_list_t *args)
3195{
3197 fr_sha1_ctx sha1_ctx;
3198 fr_value_box_t *vb;
3199 fr_value_box_t *in_head;
3200
3201 XLAT_ARGS(args, &in_head);
3202
3203 fr_sha1_init(&sha1_ctx);
3204 if (in_head) {
3205 fr_sha1_update(&sha1_ctx, in_head->vb_octets, in_head->vb_length);
3206 } else {
3207 /* sha1 of empty string */
3208 fr_sha1_update(&sha1_ctx, NULL, 0);
3209 }
3210 fr_sha1_final(digest, &sha1_ctx);
3211
3212 MEM(vb = fr_value_box_alloc_null(ctx));
3213 fr_value_box_memdup(vb, vb, NULL, digest, sizeof(digest), false);
3214
3216
3217 return XLAT_ACTION_DONE;
3218}
3219
3220/** Calculate any digest supported by OpenSSL EVP_MD
3221 *
3222 * Example:
3223@verbatim
3224%sha2_256(foo) == "0beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a33"
3225@endverbatim
3226 *
3227 * @ingroup xlat_functions
3228 */
3229#ifdef HAVE_OPENSSL_EVP_H
3230static xlat_action_t xlat_evp_md(TALLOC_CTX *ctx, fr_dcursor_t *out,
3231 UNUSED xlat_ctx_t const *xctx,
3232 UNUSED request_t *request, fr_value_box_list_t *args, EVP_MD const *md)
3233{
3234 uint8_t digest[EVP_MAX_MD_SIZE];
3235 unsigned int digestlen;
3236 EVP_MD_CTX *md_ctx;
3237 fr_value_box_t *vb;
3238 fr_value_box_t *in_head;
3239
3240 XLAT_ARGS(args, &in_head);
3241
3242 md_ctx = EVP_MD_CTX_create();
3243 EVP_DigestInit_ex(md_ctx, md, NULL);
3244 if (in_head) {
3245 EVP_DigestUpdate(md_ctx, in_head->vb_octets, in_head->vb_length);
3246 } else {
3247 EVP_DigestUpdate(md_ctx, NULL, 0);
3248 }
3249 EVP_DigestFinal_ex(md_ctx, digest, &digestlen);
3250 EVP_MD_CTX_destroy(md_ctx);
3251
3252 MEM(vb = fr_value_box_alloc_null(ctx));
3253 fr_value_box_memdup(vb, vb, NULL, digest, digestlen, false);
3254
3256
3257 return XLAT_ACTION_DONE;
3258}
3259
3260# define EVP_MD_XLAT(_md, _md_func) \
3261static xlat_action_t xlat_func_##_md(TALLOC_CTX *ctx, fr_dcursor_t *out,\
3262 xlat_ctx_t const *xctx, \
3263 request_t *request,\
3264 fr_value_box_list_t *in)\
3265{\
3266 return xlat_evp_md(ctx, out, xctx, request, in, EVP_##_md_func());\
3267}
3268
3269EVP_MD_XLAT(sha2_224, sha224)
3270EVP_MD_XLAT(sha2_256, sha256)
3271EVP_MD_XLAT(sha2_384, sha384)
3272EVP_MD_XLAT(sha2_512, sha512)
3273
3274/*
3275 * OpenWRT's OpenSSL library doesn't contain these by default
3276 */
3277#ifdef HAVE_EVP_BLAKE2S256
3278EVP_MD_XLAT(blake2s_256, blake2s256)
3279#endif
3280
3281#ifdef HAVE_EVP_BLAKE2B512
3282EVP_MD_XLAT(blake2b_512, blake2b512)
3283#endif
3284
3285EVP_MD_XLAT(sha3_224, sha3_224)
3286EVP_MD_XLAT(sha3_256, sha3_256)
3287EVP_MD_XLAT(sha3_384, sha3_384)
3288EVP_MD_XLAT(sha3_512, sha3_512)
3289#endif
3290
3291
3293 { .required = true, .concat = true, .type = FR_TYPE_STRING },
3295};
3296
3298 { .concat = true, .type = FR_TYPE_STRING },
3300};
3301
3302/** Print length of given string
3303 *
3304 * Example:
3305@verbatim
3306%strlen(foo) == 3
3307@endverbatim
3308 *
3309 * @see #xlat_func_length
3310 *
3311 * @ingroup xlat_functions
3312 */
3314 UNUSED xlat_ctx_t const *xctx,
3315 UNUSED request_t *request, fr_value_box_list_t *args)
3316{
3317 fr_value_box_t *vb;
3318 fr_value_box_t *in_head;
3319
3320 XLAT_ARGS(args, &in_head);
3321
3322 MEM(vb = fr_value_box_alloc(ctx, FR_TYPE_SIZE, NULL));
3323
3324 if (!in_head) {
3325 vb->vb_size = 0;
3326 } else {
3327 vb->vb_size = strlen(in_head->vb_strvalue);
3328 }
3329
3331
3332 return XLAT_ACTION_DONE;
3333}
3334
3336 { .concat = true, .type = FR_TYPE_STRING, .required = true, },
3337 { .single = true, .type = FR_TYPE_BOOL },
3339};
3340
3341/** Return whether a string has only printable chars
3342 *
3343 * This function returns true if the input string contains UTF8 sequences and printable chars.
3344 *
3345 * @note "\t" and " " are considered unprintable chars, unless the second argument(relaxed) is true.
3346 *
3347 * Example:
3348@verbatim
3349%str.printable("🍉abcdef🍓") == true
3350%str.printable("\000\n\r\t") == false
3351%str.printable("\t abcd", yes) == true
3352@endverbatim
3353 *
3354 * @ingroup xlat_functions
3355 */
3357 UNUSED xlat_ctx_t const *xctx,
3358 UNUSED request_t *request, fr_value_box_list_t *args)
3359{
3360 fr_value_box_t *vb;
3361 fr_value_box_t *str;
3362 fr_value_box_t *relaxed_vb;
3363 uint8_t const *p, *end;
3364 bool relaxed = false;
3365
3366 XLAT_ARGS(args, &str, &relaxed_vb);
3367
3368 if (relaxed_vb) relaxed = relaxed_vb->vb_bool;
3369
3370 p = (uint8_t const *)str->vb_strvalue;
3371 end = p + str->vb_length;
3372
3373 MEM(vb = fr_value_box_alloc(ctx, FR_TYPE_BOOL, NULL));
3375 vb->vb_bool = false;
3376
3377 do {
3378 size_t clen;
3379
3380 if ((*p < '!') &&
3381 (!relaxed || ((*p != '\t') && (*p != ' ')))) return XLAT_ACTION_DONE;
3382
3383 if (*p == 0x7f) return XLAT_ACTION_DONE;
3384
3385 clen = fr_utf8_char(p, end - p);
3386 if (clen == 0) return XLAT_ACTION_DONE;
3387 p += clen;
3388 } while (p < end);
3389
3390 vb->vb_bool = true;
3391
3392 return XLAT_ACTION_DONE;
3393}
3394
3396 { .concat = true, .type = FR_TYPE_STRING },
3398};
3399
3400/** Return whether a string is valid UTF-8
3401 *
3402 * This function returns true if the input string is valid UTF-8, false otherwise.
3403 *
3404 * Example:
3405@verbatim
3406%str.utf8(🍉🥝🍓) == true
3407%str.utf8(🍉\xff🍓) == false
3408@endverbatim
3409 *
3410 * @ingroup xlat_functions
3411 */
3413 UNUSED xlat_ctx_t const *xctx,
3414 UNUSED request_t *request, fr_value_box_list_t *args)
3415{
3416 fr_value_box_t *vb;
3417 fr_value_box_t *in_head;
3418
3419 XLAT_ARGS(args, &in_head);
3420
3421 MEM(vb = fr_value_box_alloc(ctx, FR_TYPE_BOOL, NULL));
3422 vb->vb_bool = (fr_utf8_str((uint8_t const *)in_head->vb_strvalue,
3423 in_head->vb_length) >= 0);
3424
3426
3427 return XLAT_ACTION_DONE;
3428}
3429
3431 { .single = true, .required = true, .type = FR_TYPE_VOID },
3432 { .single = true, .required = true, .type = FR_TYPE_INT32 },
3433 { .single = true, .type = FR_TYPE_INT32 },
3435};
3436
3437/** Extract a substring from string / octets data
3438 *
3439 * Non string / octets data is cast to a string.
3440 *
3441 * Second parameter is start position, optional third parameter is length
3442 * Negative start / length count from RHS of data.
3443 *
3444 * Example: (User-Name = "hello")
3445@verbatim
3446%substr(&User-Name, 1, 3) == 'ell'
3447@endverbatim
3448 *
3449 * @ingroup xlat_functions
3450 */
3451static xlat_action_t xlat_func_substr(TALLOC_CTX *ctx, fr_dcursor_t *out, UNUSED xlat_ctx_t const *xctx,
3452 request_t *request, fr_value_box_list_t *args)
3453{
3454 fr_value_box_t *in = NULL, *start_vb, *len_vb, *vb;
3455 int32_t start, end, len;
3456
3457 XLAT_ARGS(args, &in, &start_vb, &len_vb);
3458
3459 switch (in->type) {
3460 case FR_TYPE_OCTETS:
3461 case FR_TYPE_STRING:
3462 break;
3463
3464 default:
3466 RPEDEBUG("Failed casting value to string");
3467 return XLAT_ACTION_FAIL;
3468 }
3469 break;
3470 }
3471
3472 if (start_vb->vb_int32 > (int32_t)in->vb_length) return XLAT_ACTION_DONE;
3473
3474 if (start_vb->vb_int32 < 0) {
3475 start = in->vb_length + start_vb->vb_int32;
3476 if (start < 0) start = 0;
3477 } else {
3478 start = start_vb->vb_int32;
3479 }
3480
3481 if (len_vb) {
3482 if (len_vb->vb_int32 < 0) {
3483 end = in->vb_length + len_vb->vb_int32;
3484 if (end < 0) return XLAT_ACTION_DONE;
3485 } else {
3486 end = start + len_vb->vb_int32;
3487 if (end > (int32_t)in->vb_length) end = in->vb_length;
3488 }
3489 } else {
3490 end = in->vb_length;
3491 }
3492
3493 if (start >= end) return XLAT_ACTION_DONE;
3494
3495 MEM(vb = fr_value_box_alloc(ctx, in->type, NULL));
3496
3497 len = end - start;
3498 switch (in->type) {
3499 case FR_TYPE_STRING:
3500 fr_value_box_bstrndup(vb, vb, NULL, &in->vb_strvalue[start], len, false);
3501 break;
3502 case FR_TYPE_OCTETS:
3503 {
3504 uint8_t *buf;
3505 fr_value_box_mem_alloc(vb, &buf, vb, NULL, len, false);
3506 memcpy(buf, &in->vb_octets[start], len);
3507 }
3508 break;
3509 default:
3510 fr_assert(0);
3511 }
3512
3515
3516 return XLAT_ACTION_DONE;
3517}
3518
3519#ifdef HAVE_REGEX_PCRE2
3520/** Cache statically compiled expressions
3521 */
3522typedef struct {
3523 regex_t *pattern;
3524 fr_regex_flags_t flags;
3525} xlat_subst_regex_inst_t;
3526
3527/** Pre-compile regexes where possible
3528 */
3529static int xlat_instantiate_subst_regex(xlat_inst_ctx_t const *xctx)
3530{
3531 xlat_subst_regex_inst_t *inst = talloc_get_type_abort(xctx->inst, xlat_subst_regex_inst_t);
3532 xlat_exp_t *patt_exp;
3533 fr_sbuff_t sbuff;
3534 fr_sbuff_marker_t start_m, end_m;
3535
3536 /* args #2 (pattern) */
3537 patt_exp = fr_dlist_next(&xctx->ex->call.args->dlist, fr_dlist_head(&xctx->ex->call.args->dlist));
3538 fr_assert(patt_exp && patt_exp->type == XLAT_GROUP); /* args must be groups */
3539
3540 /* If there are dynamic expansions, we can't pre-compile */
3541 if (!xlat_is_literal(patt_exp->group)) return 0;
3542 fr_assert(fr_dlist_num_elements(&patt_exp->group->dlist) == 1);
3543
3544 patt_exp = fr_dlist_head(&patt_exp->group->dlist);
3545
3546 /* We can only pre-compile strings */
3547 if (!fr_type_is_string(patt_exp->data.type)) return 0;
3548
3549 sbuff = FR_SBUFF_IN(patt_exp->data.vb_strvalue, patt_exp->data.vb_length);
3550
3551 /* skip any whitesapce */
3552 fr_sbuff_adv_past_whitespace(&sbuff, SIZE_MAX, 0);
3553
3554 /* Is the next char a forward slash? */
3555 if (fr_sbuff_next_if_char(&sbuff, '/')) {
3556 fr_slen_t slen;
3557
3558 fr_sbuff_marker(&start_m, &sbuff);
3559
3560 if (!fr_sbuff_adv_to_chr(&sbuff, SIZE_MAX, '/')) return 0; /* Not a regex */
3561
3562 fr_sbuff_marker(&end_m, &sbuff);
3563 fr_sbuff_next(&sbuff); /* skip trailing slash */
3564
3565 if (fr_sbuff_remaining(&sbuff)) {
3566 slen = regex_flags_parse(NULL, &inst->flags,
3567 &sbuff,
3568 NULL, true);
3569 if (slen < 0) {
3570 PERROR("Failed parsing regex flags in \"%s\"", patt_exp->data.vb_strvalue);
3571 return -1;
3572 }
3573 }
3574
3575 if (regex_compile(inst, &inst->pattern,
3576 fr_sbuff_current(&start_m), fr_sbuff_current(&end_m) - fr_sbuff_current(&start_m),
3577 &inst->flags, true, false) <= 0) {
3578 PERROR("Failed compiling regex \"%s\"", patt_exp->data.vb_strvalue);
3579 return -1;
3580 }
3581 }
3582 /* No... then it's not a regex */
3583
3584 return 0;
3585}
3586
3587/** Perform regex substitution TODO CHECK
3588 *
3589 * Called when %subst() pattern begins with "/"
3590 *
3591@verbatim
3592%subst(<subject>, /<regex>/[flags], <replace>)
3593@endverbatim
3594 *
3595 * Example: (User-Name = "foo")
3596@verbatim
3597%subst(%{User-Name}, /oo.*$/, 'un') == "fun"
3598@endverbatim
3599 *
3600 * @note References can be specified in the replacement string with $<ref>
3601 *
3602 * @see #xlat_func_subst
3603 *
3604 * @ingroup xlat_functions
3605 */
3606static int xlat_func_subst_regex(TALLOC_CTX *ctx, fr_dcursor_t *out,
3607 xlat_ctx_t const *xctx, request_t *request,
3608 fr_value_box_list_t *args)
3609{
3610 xlat_subst_regex_inst_t const *inst = talloc_get_type_abort_const(xctx->inst, xlat_subst_regex_inst_t);
3611 fr_sbuff_t sbuff;
3612 fr_sbuff_marker_t start_m, end_m;
3613 char *buff;
3614 ssize_t slen;
3615 regex_t *pattern, *our_pattern = NULL;
3616 fr_regex_flags_t const *flags;
3617 fr_regex_flags_t our_flags = {};
3618 fr_value_box_t *vb;
3619 fr_value_box_t *subject_vb;
3620 fr_value_box_t *regex_vb;
3621 fr_value_box_t *rep_vb;
3622
3623 XLAT_ARGS(args, &subject_vb, &regex_vb, &rep_vb);
3624
3625 /*
3626 * Was not pre-compiled, so we need to compile it now
3627 */
3628 if (!inst->pattern) {
3629 sbuff = FR_SBUFF_IN(regex_vb->vb_strvalue, regex_vb->vb_length);
3630 if (fr_sbuff_len(&sbuff) == 0) {
3631 REDEBUG("Regex must not be empty");
3632 return XLAT_ACTION_FAIL;
3633 }
3634
3635 fr_sbuff_next(&sbuff); /* skip leading slash */
3636 fr_sbuff_marker(&start_m, &sbuff);
3637
3638 if (!fr_sbuff_adv_to_chr(&sbuff, SIZE_MAX, '/')) return 1; /* Not a regex */
3639
3640 fr_sbuff_marker(&end_m, &sbuff);
3641 fr_sbuff_next(&sbuff); /* skip trailing slash */
3642
3643 slen = regex_flags_parse(NULL, &our_flags, &sbuff, NULL, true);
3644 if (slen < 0) {
3645 RPEDEBUG("Failed parsing regex flags");
3646 return -1;
3647 }
3648
3649 /*
3650 * Process the substitution
3651 */
3652 if (regex_compile(NULL, &our_pattern,
3653 fr_sbuff_current(&start_m), fr_sbuff_current(&end_m) - fr_sbuff_current(&start_m),
3654 &our_flags, true, true) <= 0) {
3655 RPEDEBUG("Failed compiling regex");
3656 return -1;
3657 }
3658 pattern = our_pattern;
3659 flags = &our_flags;
3660 } else {
3661 pattern = inst->pattern;
3662 flags = &inst->flags;
3663 }
3664
3665 MEM(vb = fr_value_box_alloc_null(ctx));
3666 if (regex_substitute(vb, &buff, 0, pattern, flags,
3667 subject_vb->vb_strvalue, subject_vb->vb_length,
3668 rep_vb->vb_strvalue, rep_vb->vb_length, NULL) < 0) {
3669 RPEDEBUG("Failed performing substitution");
3670 talloc_free(vb);
3671 talloc_free(pattern);
3672 return -1;
3673 }
3674 fr_value_box_bstrdup_buffer_shallow(NULL, vb, NULL, buff, false);
3675
3676 fr_value_box_safety_copy(vb, subject_vb);
3677 fr_value_box_safety_merge(vb, rep_vb);
3678
3680
3681 talloc_free(our_pattern);
3682
3683 return 0;
3684}
3685#endif
3686
3688 { .required = true, .concat = true, .type = FR_TYPE_STRING },
3689 { .required = true, .concat = true, .type = FR_TYPE_STRING },
3690 { .required = true, .concat = true, .type = FR_TYPE_STRING },
3692};
3693
3694/** Perform regex substitution
3695 *
3696@verbatim
3697%subst(<subject>, <pattern>, <replace>)
3698@endverbatim
3699 *
3700 * Example: (User-Name = "foobar")
3701@verbatim
3702%subst(%{User-Name}, 'oo', 'un') == "funbar"
3703@endverbatim
3704 *
3705 * @see xlat_func_subst_regex
3706 *
3707 * @ingroup xlat_functions
3708 */
3710#ifdef HAVE_REGEX_PCRE2
3711 xlat_ctx_t const *xctx,
3712#else
3713 UNUSED xlat_ctx_t const *xctx,
3714#endif
3715 request_t *request, fr_value_box_list_t *args)
3716{
3717 char const *p, *q, *end;
3718 char *vb_str;
3719
3720 char const *pattern, *rep;
3721 size_t pattern_len, rep_len;
3722
3723 fr_value_box_t *rep_vb, *vb;
3724 fr_value_box_t *subject_vb;
3725 fr_value_box_t *pattern_vb;
3726
3727 XLAT_ARGS(args, &subject_vb, &pattern_vb, &rep_vb);
3728
3729 /* coverity[dereference] */
3730 pattern = pattern_vb->vb_strvalue;
3731 if (*pattern == '/') {
3732#ifdef HAVE_REGEX_PCRE2
3733 switch (xlat_func_subst_regex(ctx, out, xctx, request, args)) {
3734 case 0:
3735 return XLAT_ACTION_DONE;
3736
3737 case 1:
3738 /* Not a regex, fall through */
3739 break;
3740
3741 case -1:
3742 return XLAT_ACTION_FAIL;
3743 }
3744#else
3745 if (memchr(pattern, '/', pattern_vb->vb_length - 1)) {
3746 REDEBUG("regex based substitutions require libpcre2. "
3747 "Check ${features.regex-pcre2} to determine support");
3748 }
3749 return XLAT_ACTION_FAIL;
3750#endif
3751 }
3752
3753 /*
3754 * Check for empty pattern
3755 */
3756 pattern_len = pattern_vb->vb_length;
3757 if (pattern_len == 0) {
3758 REDEBUG("Empty pattern");
3759 return XLAT_ACTION_FAIL;
3760 }
3761
3762 rep = rep_vb->vb_strvalue;
3763 rep_len = rep_vb->vb_length;
3764
3765 p = subject_vb->vb_strvalue;
3766 end = p + subject_vb->vb_length;
3767
3768 MEM(vb = fr_value_box_alloc_null(ctx));
3769 vb_str = talloc_bstrndup(vb, "", 0);
3770
3771 while (p < end) {
3772 q = memmem(p, end - p, pattern, pattern_len);
3773 if (!q) {
3774 MEM(vb_str = talloc_bstr_append(vb, vb_str, p, end - p));
3775 break;
3776 }
3777
3778 if (q > p) MEM(vb_str = talloc_bstr_append(vb, vb_str, p, q - p));
3779 if (rep_len) MEM(vb_str = talloc_bstr_append(vb, vb_str, rep, rep_len));
3780 p = q + pattern_len;
3781 }
3782
3783 if (fr_value_box_bstrdup_buffer_shallow(NULL, vb, NULL, vb_str, false) < 0) {
3784 RPEDEBUG("Failed creating output box");
3785 talloc_free(vb);
3786 return XLAT_ACTION_FAIL;
3787 }
3788
3789 fr_value_box_safety_copy(vb, subject_vb);
3790 fr_value_box_safety_merge(vb, rep_vb);
3791
3793
3794 return XLAT_ACTION_DONE;
3795}
3796
3797/*
3798 * Debug builds only, we don't want to allow unsanitised inputs to crash the server
3799 */
3800#ifndef NDEBUG
3802 { .single = true, .required = true, .type = FR_TYPE_STRING },
3804};
3805
3807 UNUSED xlat_ctx_t const *xctx, request_t *request,
3808 fr_value_box_list_t *args)
3809{
3810 static fr_table_num_sorted_t const signal_table[] = {
3811 { L("break"), SIGTRAP }, /* Save flailing at the keyboard */
3812 { L("BREAK"), SIGTRAP },
3813 { L("SIGABRT"), SIGABRT },
3814 { L("SIGALRM"), SIGALRM },
3815#ifdef SIGBUS
3816 { L("SIGBUS"), SIGBUS },
3817#endif
3818 { L("SIGCHLD"), SIGCHLD },
3819 { L("SIGCONT"), SIGCONT },
3820 { L("SIGFPE"), SIGFPE },
3821 { L("SIGHUP"), SIGHUP },
3822 { L("SIGILL"), SIGILL },
3823 { L("SIGINT"), SIGINT },
3824 { L("SIGKILL"), SIGKILL },
3825 { L("SIGPIPE"), SIGPIPE },
3826#ifdef SIGPOLL
3827 { L("SIGPOLL"), SIGPOLL },
3828#endif
3829 { L("SIGPROF"), SIGPROF },
3830 { L("SIGQUIT"), SIGQUIT },
3831 { L("SIGSEGV"), SIGSEGV },
3832 { L("SIGSTOP"), SIGSTOP },
3833#ifdef SIGSYS
3834 { L("SIGSYS"), SIGSYS },
3835#endif
3836 { L("SIGTERM"), SIGTERM },
3837#ifdef SIGTRAP
3838 { L("SIGTRAP"), SIGTRAP },
3839#endif
3840 { L("SIGTSTP"), SIGTSTP },
3841 { L("SIGTTIN"), SIGTTIN },
3842 { L("SIGTTOU"), SIGTTOU },
3843 { L("SIGURG"), SIGURG },
3844 { L("SIGUSR1"), SIGUSR1 },
3845 { L("SIGUSR2"), SIGUSR2 },
3846 { L("SIGVTALRM"), SIGVTALRM },
3847 { L("SIGXCPU"), SIGXCPU },
3848 { L("SIGXFSZ"), SIGXFSZ }
3849 };
3850 static size_t signal_table_len = NUM_ELEMENTS(signal_table);
3851
3852 fr_value_box_t *signal_vb;
3853 int signal;
3854
3855 XLAT_ARGS(args, &signal_vb);
3856
3857 signal = fr_table_value_by_substr(signal_table, signal_vb->vb_strvalue, signal_vb->vb_length, -1);
3858 if (signal < 0) {
3859 RERROR("Invalid signal \"%pV\"", signal_vb);
3860 return XLAT_ACTION_FAIL;
3861 }
3862 if (raise(signal) < 0) {
3863 RERROR("Failed raising signal %d: %s", signal, strerror(errno));
3864 return XLAT_ACTION_FAIL;
3865 }
3866 return XLAT_ACTION_DONE;
3867}
3868#endif
3869
3871 { .required = false, .single = true, .type = FR_TYPE_STRING },
3873};
3874
3875/** Return the time as a #FR_TYPE_DATE
3876 *
3877 * Note that all operations are UTC.
3878 *
3879@verbatim
3880%time()
3881@endverbatim
3882 *
3883 * Example:
3884@verbatim
3885update reply {
3886 &Reply-Message := "%{%time(now) - %time(request)}"
3887}
3888@endverbatim
3889 *
3890 * @ingroup xlat_functions
3891 */
3893 UNUSED xlat_ctx_t const *xctx,
3894 request_t *request, fr_value_box_list_t *args)
3895{
3896 fr_value_box_t *arg;
3897 fr_value_box_t *vb;
3899
3900 XLAT_ARGS(args, &arg);
3901
3902 if (!arg || (strcmp(arg->vb_strvalue, "now") == 0)) {
3904
3905 } else if (strcmp(arg->vb_strvalue, "request") == 0) {
3906 value = fr_time_to_unix_time(request->packet->timestamp);
3907
3908 } else if (strcmp(arg->vb_strvalue, "offset") == 0) {
3909 MEM(vb = fr_value_box_alloc(ctx, FR_TYPE_TIME_DELTA, NULL));
3910 vb->vb_time_delta = fr_time_gmtoff();
3911 goto append;
3912
3913 } else if (strcmp(arg->vb_strvalue, "dst") == 0) {
3914 MEM(vb = fr_value_box_alloc(ctx, FR_TYPE_BOOL, NULL));
3915 vb->vb_bool = fr_time_is_dst();
3916 goto append;
3917
3918 } else if (strcmp(arg->vb_strvalue, "mday_offset") == 0) {
3919 struct tm tm;
3920 fr_unix_time_t unix_time = fr_time_to_unix_time(request->packet->timestamp);
3921 time_t when = fr_unix_time_to_sec(unix_time);
3922 int64_t nsec;
3923
3924 gmtime_r(&when, &tm);
3925
3926 nsec = (int64_t) 86400 * (tm.tm_mday - 1);
3927 nsec += when % 86400;
3928 nsec *= NSEC;
3929 nsec += fr_unix_time_unwrap(unix_time) % NSEC;
3930
3931 MEM(vb = fr_value_box_alloc(ctx, FR_TYPE_TIME_DELTA, NULL));
3932 vb->vb_time_delta = fr_time_delta_wrap(nsec);
3933 goto append;
3934
3935 } else if (strcmp(arg->vb_strvalue, "wday_offset") == 0) {
3936 struct tm tm;
3937 fr_unix_time_t unix_time = fr_time_to_unix_time(request->packet->timestamp);
3938 time_t when = fr_unix_time_to_sec(unix_time);
3939 int64_t nsec;
3940
3941 gmtime_r(&when, &tm);
3942
3943 nsec = (int64_t) 86400 * tm.tm_wday;
3944 nsec += when % 86400;
3945 nsec *= NSEC;
3946 nsec += fr_unix_time_unwrap(unix_time) % NSEC;
3947
3948 MEM(vb = fr_value_box_alloc(ctx, FR_TYPE_TIME_DELTA, NULL));
3949 vb->vb_time_delta = fr_time_delta_wrap(nsec);
3950 goto append;
3951
3952 } else if (fr_unix_time_from_str(&value, arg->vb_strvalue, FR_TIME_RES_SEC) < 0) {
3953 REDEBUG("Invalid time specification '%s'", arg->vb_strvalue);
3954 return XLAT_ACTION_FAIL;
3955 }
3956
3957 MEM(vb = fr_value_box_alloc(ctx, FR_TYPE_DATE, NULL));
3958 vb->vb_date = value;
3959
3960append:
3962
3963 return XLAT_ACTION_DONE;
3964}
3965
3966/** Return the current time as a #FR_TYPE_DATE
3967 *
3968 * Note that all operations are UTC.
3969 *
3970@verbatim
3971%time.now()
3972@endverbatim
3973 *
3974 * Example:
3975@verbatim
3976update reply {
3977 &Reply-Message := "%{%time.now() - %time.request()}"
3978}
3979@endverbatim
3980 *
3981 * @ingroup xlat_functions
3982 */
3984 UNUSED xlat_ctx_t const *xctx,
3985 UNUSED request_t *request, UNUSED fr_value_box_list_t *args)
3986{
3987 fr_value_box_t *vb;
3988
3989 MEM(vb = fr_value_box_alloc(ctx, FR_TYPE_DATE, NULL));
3990 vb->vb_date = fr_time_to_unix_time(fr_time());
3991
3993
3994 return XLAT_ACTION_DONE;
3995}
3996
3997/** Return the request receive time as a #FR_TYPE_DATE
3998 *
3999 * Note that all operations are UTC.
4000 *
4001@verbatim
4002%time.request()
4003@endverbatim
4004 *
4005 * Example:
4006@verbatim
4007update reply {
4008 &Reply-Message := "%{%time.now() - %time.request()}"
4009}
4010@endverbatim
4011 *
4012 * @ingroup xlat_functions
4013 */
4015 UNUSED xlat_ctx_t const *xctx,
4016 request_t *request, UNUSED fr_value_box_list_t *args)
4017{
4018 fr_value_box_t *vb;
4019
4020 MEM(vb = fr_value_box_alloc(ctx, FR_TYPE_DATE, NULL));
4021 vb->vb_date = fr_time_to_unix_time(request->packet->timestamp);
4022
4024
4025 return XLAT_ACTION_DONE;
4026}
4027
4028
4029/** Return the current time offset from gmt
4030 *
4031 * @ingroup xlat_functions
4032 */
4034 UNUSED xlat_ctx_t const *xctx,
4035 UNUSED request_t *request, UNUSED fr_value_box_list_t *args)
4036{
4037 fr_value_box_t *vb;
4038
4039 MEM(vb = fr_value_box_alloc(ctx, FR_TYPE_TIME_DELTA, NULL));
4040 vb->vb_time_delta = fr_time_gmtoff();
4041
4043
4044 return XLAT_ACTION_DONE;
4045}
4046
4047
4048/** Return whether we are in daylight savings or not
4049 *
4050 * @ingroup xlat_functions
4051 */
4053 UNUSED xlat_ctx_t const *xctx,
4054 UNUSED request_t *request, UNUSED fr_value_box_list_t *args)
4055{
4056 fr_value_box_t *vb;
4057
4058 MEM(vb = fr_value_box_alloc(ctx, FR_TYPE_BOOL, NULL));
4059 vb->vb_bool = fr_time_is_dst();
4060
4062
4063 return XLAT_ACTION_DONE;
4064}
4065
4066
4067/** Change case of a string
4068 *
4069 * If upper is true, change to uppercase, otherwise, change to lowercase
4070 */
4072 UNUSED request_t *request, fr_value_box_list_t *args, bool upper)
4073{
4074 char *p;
4075 char const *end;
4076 fr_value_box_t *vb;
4077
4078 XLAT_ARGS(args, &vb);
4079
4080 p = UNCONST(char *, vb->vb_strvalue);
4081 end = p + vb->vb_length;
4082
4083 while (p < end) {
4084 *(p) = upper ? toupper ((uint8_t) *(p)) : tolower((uint8_t) *(p));
4085 p++;
4086 }
4087
4088 xlat_arg_copy_out(ctx, out, args, vb);
4089
4090 return XLAT_ACTION_DONE;
4091}
4092
4094 { .required = true, .concat = true, .type = FR_TYPE_STRING },
4096};
4097
4098
4099/** Convert a string to lowercase
4100 *
4101 * Example:
4102@verbatim
4103%tolower("Bar") == "bar"
4104@endverbatim
4105 *
4106 * Probably only works for ASCII
4107 *
4108 * @ingroup xlat_functions
4109 */
4111 UNUSED xlat_ctx_t const *xctx,
4112 request_t *request, fr_value_box_list_t *in)
4113{
4114 return xlat_change_case(ctx, out, request, in, false);
4115}
4116
4117
4118/** Convert a string to uppercase
4119 *
4120 * Example:
4121@verbatim
4122%toupper("Foo") == "FOO"
4123@endverbatim
4124 *
4125 * Probably only works for ASCII
4126 *
4127 * @ingroup xlat_functions
4128 */
4130 UNUSED xlat_ctx_t const *xctx,
4131 request_t *request, fr_value_box_list_t *in)
4132{
4133 return xlat_change_case(ctx, out, request, in, true);
4134}
4135
4136
4138 { .required = true, .concat = true, .type = FR_TYPE_STRING },
4140};
4141
4142/** URLencode special characters
4143 *
4144 * Example:
4145@verbatim
4146%urlquote("http://example.org/") == "http%3A%47%47example.org%47"
4147@endverbatim
4148 *
4149 * @ingroup xlat_functions
4150 */
4152 UNUSED xlat_ctx_t const *xctx,
4153 UNUSED request_t *request, fr_value_box_list_t *args)
4154{
4155 char const *p, *end;
4156 char *buff_p;
4157 size_t outlen = 0;
4158 fr_value_box_t *vb;
4159 fr_value_box_t *in_head;
4160
4161 XLAT_ARGS(args, &in_head);
4162
4163 p = in_head->vb_strvalue;
4164 end = p + in_head->vb_length;
4165
4166 /*
4167 * Calculate size of output
4168 */
4169 while (p < end) {
4170 if (isalnum(*p) ||
4171 *p == '-' ||
4172 *p == '_' ||
4173 *p == '.' ||
4174 *p == '~') {
4175 outlen++;
4176 } else {
4177 outlen += 3;
4178 }
4179 p++;
4180 }
4181
4182 MEM(vb = fr_value_box_alloc_null(ctx));
4183 MEM(fr_value_box_bstr_alloc(vb, &buff_p, vb, NULL, outlen, false) == 0);
4184 fr_value_box_safety_copy(vb, in_head);
4185
4186 /* Reset p to start position */
4187 p = in_head->vb_strvalue;
4188
4189 while (p < end) {
4190 if (isalnum(*p)) {
4191 *buff_p++ = *p++;
4192 continue;
4193 }
4194
4195 switch (*p) {
4196 case '-':
4197 case '_':
4198 case '.':
4199 case '~':
4200 *buff_p++ = *p++;
4201 break;
4202
4203 default:
4204 /* MUST be upper case hex to be compliant */
4205 snprintf(buff_p, 4, "%%%02X", (uint8_t) *p++); /* %XX */
4206
4207 buff_p += 3;
4208 }
4209 }
4210
4211 *buff_p = '\0';
4212
4213 // @todo - mark as safe for URL?
4215
4216 return XLAT_ACTION_DONE;
4217}
4218
4219
4221 { .required = true, .concat = true, .type = FR_TYPE_STRING },
4223};
4224
4225/** URLdecode special characters
4226 *
4227 * @note Remember to escape % with %% in strings, else xlat will try to parse it.
4228 *
4229 * Example:
4230@verbatim
4231%urlunquote("http%%3A%%47%%47example.org%%47") == "http://example.org/"
4232@endverbatim
4233 *
4234 * @ingroup xlat_functions
4235 */
4237 UNUSED xlat_ctx_t const *xctx,
4238 request_t *request, fr_value_box_list_t *args)
4239{
4240 char const *p, *end;
4241 char *buff_p;
4242 char *c1, *c2;
4243 size_t outlen = 0;
4244 fr_value_box_t *vb;
4245 fr_value_box_t *in_head;
4246
4247 XLAT_ARGS(args, &in_head);
4248
4249 p = in_head->vb_strvalue;
4250 end = p + in_head->vb_length;
4251
4252 /*
4253 * Calculate size of output
4254 */
4255 while (p < end) {
4256 if (*p == '%') {
4257 if (!p[1] || !p[2]) {
4258 REMARKER(in_head->vb_strvalue, p - in_head->vb_strvalue, "Invalid %% sequence");
4259 return XLAT_ACTION_FAIL;
4260 }
4261 p += 3;
4262 } else {
4263 p++;
4264 }
4265 outlen++;
4266 }
4267
4268 MEM(vb = fr_value_box_alloc_null(ctx));
4269 MEM(fr_value_box_bstr_alloc(vb, &buff_p, vb, NULL, outlen, false) == 0);
4270 fr_value_box_safety_copy(vb, in_head);
4271
4272 /* Reset p to start position */
4273 p = in_head->vb_strvalue;
4274
4275 while (p < end) {
4276 if (*p != '%') {
4277 *buff_p++ = *p++;
4278 continue;
4279 }
4280 /* Is a % char */
4281
4282 /* Don't need \0 check, as it won't be in the hextab */
4283 if (!(c1 = memchr(hextab, tolower((uint8_t) *++p), 16)) ||
4284 !(c2 = memchr(hextab, tolower((uint8_t) *++p), 16))) {
4285 REMARKER(in_head->vb_strvalue, p - in_head->vb_strvalue, "Non-hex char in %% sequence");
4286 talloc_free(vb);
4287
4288 return XLAT_ACTION_FAIL;
4289 }
4290 p++;
4291 *buff_p++ = ((c1 - hextab) << 4) + (c2 - hextab);
4292 }
4293
4294 *buff_p = '\0';
4296
4297 return XLAT_ACTION_DONE;
4298}
4299
4301 { .required = true, .type = FR_TYPE_VOID },
4302 { .single = true, .type = FR_TYPE_ATTR },
4304};
4305
4306/** Decode any protocol attribute / options
4307 *
4308 * Creates protocol-specific attributes based on the given binary option data
4309 *
4310 * Example:
4311@verbatim
4312%dhcpv4.decode(%{Tmp-Octets-0})
4313@endverbatim
4314 *
4315 * @ingroup xlat_functions
4316 */
4318 xlat_ctx_t const *xctx,
4319 request_t *request, fr_value_box_list_t *in)
4320{
4321 int decoded;
4322 fr_value_box_t *vb, *in_head, *root_da;
4323 void *decode_ctx = NULL;
4324 xlat_pair_decode_uctx_t const *decode_uctx = talloc_get_type_abort(*(void * const *)xctx->inst, xlat_pair_decode_uctx_t);
4325 fr_test_point_pair_decode_t const *tp_decode = decode_uctx->tp_decode;
4326 fr_pair_t *vp = NULL;
4327 bool created = false;
4328
4329 XLAT_ARGS(in, &in_head, &root_da);
4330
4331 fr_assert(in_head->type == FR_TYPE_GROUP);
4332
4333 if (decode_uctx->dict && decode_uctx->dict != request->proto_dict) {
4334 REDEBUG2("Can't call %%%s() when in %s namespace", xctx->ex->call.func->name,
4335 fr_dict_root(request->proto_dict)->name);
4336 return XLAT_ACTION_FAIL;
4337 }
4338
4339 if (root_da) {
4340 int ret;
4341 if (!fr_type_is_structural(root_da->vb_attr->type)) {
4342 REDEBUG2("Decoding context must be a structural attribute reference");
4343 return XLAT_ACTION_FAIL;
4344 }
4345 ret = fr_pair_update_by_da_parent(fr_pair_list_parent(&request->request_pairs), &vp, root_da->vb_attr);
4346 if (ret < 0) {
4347 REDEBUG2("Failed creating decoding root pair");
4348 return XLAT_ACTION_FAIL;
4349 }
4350 if (ret == 0) created = true;
4351 }
4352
4353 if (tp_decode->test_ctx) {
4354 if (tp_decode->test_ctx(&decode_ctx, ctx, request->proto_dict, root_da ? root_da->vb_attr : NULL) < 0) {
4355 goto fail;
4356 }
4357 }
4358
4359 decoded = xlat_decode_value_box_list(root_da ? vp : request->request_ctx,
4360 root_da ? &vp->vp_group : &request->request_pairs,
4361 request, decode_ctx, tp_decode->func, &in_head->vb_group);
4362 if (decoded <= 0) {
4363 talloc_free(decode_ctx);
4364 RPERROR("Protocol decoding failed");
4365 fail:
4366 if (created) fr_pair_delete(&request->request_pairs, vp);
4367 return XLAT_ACTION_FAIL;
4368 }
4369
4370 /*
4371 * Create a value box to hold the decoded count, and add
4372 * it to the output list.
4373 */
4374 MEM(vb = fr_value_box_alloc(ctx, FR_TYPE_UINT32, NULL));
4375 vb->vb_uint32 = decoded;
4377
4378 talloc_free(decode_ctx);
4379 return XLAT_ACTION_DONE;
4380}
4381
4383 { .required = true, .single = true, .type = FR_TYPE_IPV4_PREFIX },
4385};
4386
4387/** Calculate the subnet mask from a IPv4 prefix
4388 *
4389 * Example:
4390@verbatim
4391%ip.v4.netmask(%{Network-Prefix})
4392@endverbatim
4393 *
4394 * @ingroup xlat_functions
4395 */
4397 UNUSED request_t *request, fr_value_box_list_t *args)
4398{
4399 fr_value_box_t *subnet, *vb;
4400 XLAT_ARGS(args, &subnet);
4401
4402 MEM(vb = fr_value_box_alloc(ctx, FR_TYPE_IPV4_ADDR, NULL));
4403
4404 switch (subnet->vb_ip.prefix) {
4405 case 0:
4406 vb->vb_ipv4addr = 0;
4407 break;
4408
4409 case 32:
4410 vb->vb_ipv4addr = 0xffffffff;
4411 break;
4412
4413 default:
4414 vb->vb_ipv4addr = htonl((uint32_t)0xffffffff << (32 - subnet->vb_ip.prefix));
4415 break;
4416 }
4417
4419
4420 return XLAT_ACTION_DONE;
4421}
4422
4423/** Calculate the broadcast address from a IPv4 prefix
4424 *
4425 * Example:
4426@verbatim
4427%ip.v4.broadcast(%{Network-Prefix})
4428@endverbatim
4429 *
4430 * @ingroup xlat_functions
4431 */
4433 UNUSED request_t *request, fr_value_box_list_t *args)
4434{
4435 fr_value_box_t *subnet, *vb;
4436 XLAT_ARGS(args, &subnet);
4437
4438 MEM(vb = fr_value_box_alloc(ctx, FR_TYPE_IPV4_ADDR, NULL));
4439 vb->vb_ipv4addr = htonl( ntohl(subnet->vb_ipv4addr) | ((uint32_t)0xffffffff >> subnet->vb_ip.prefix));
4441
4442 return XLAT_ACTION_DONE;
4443}
4444
4446{
4447 *(void **) mctx->inst = mctx->uctx;
4448 return 0;
4449}
4450
4456
4457/** Encode protocol attributes / options
4458 *
4459 * Returns octet string created from the provided pairs
4460 *
4461 * Example:
4462@verbatim
4463%dhcpv4.encode(&request[*])
4464@endverbatim
4465 *
4466 * @ingroup xlat_functions
4467 */
4469 xlat_ctx_t const *xctx,
4470 request_t *request, fr_value_box_list_t *args)
4471{
4472 fr_pair_t *vp;
4473 fr_dcursor_t *cursor;
4474 bool tainted = false, encode_children = false;
4475 fr_value_box_t *encoded;
4476
4477 fr_dbuff_t *dbuff;
4478 ssize_t len = 0;
4479 fr_value_box_t *in_head, *root_da;
4480 void *encode_ctx = NULL;
4481 fr_test_point_pair_encode_t const *tp_encode;
4482
4483 FR_DBUFF_TALLOC_THREAD_LOCAL(&dbuff, 2048, SIZE_MAX);
4484
4485 XLAT_ARGS(args, &in_head, &root_da);
4486
4487 memcpy(&tp_encode, xctx->inst, sizeof(tp_encode)); /* const issues */
4488
4489 cursor = fr_value_box_get_cursor(in_head);
4490
4491 /*
4492 * Create the encoding context.
4493 */
4494 if (tp_encode->test_ctx) {
4495 if (tp_encode->test_ctx(&encode_ctx, cursor, request->proto_dict, root_da ? root_da->vb_attr : NULL) < 0) {
4496 return XLAT_ACTION_FAIL;
4497 }
4498 }
4499
4500 if (root_da) {
4501 if (!fr_type_is_structural(root_da->vb_attr->type)) {
4502 REDEBUG2("Encoding context must be a structural attribute reference");
4503 return XLAT_ACTION_FAIL;
4504 }
4505 vp = fr_dcursor_current(cursor);
4506 if (!fr_dict_attr_common_parent(root_da->vb_attr, vp->da, true) && (root_da->vb_attr != vp->da)) {
4507 REDEBUG2("%s is not a child of %s", vp->da->name, root_da->vb_attr->name);
4508 return XLAT_ACTION_FAIL;
4509 }
4510 if (root_da->vb_attr == vp->da) encode_children = true;
4511 }
4512
4513 /*
4514 * Loop over the attributes, encoding them.
4515 */
4516 RDEBUG2("Encoding attributes");
4517
4518 if (RDEBUG_ENABLED2) {
4519 RINDENT();
4520 for (vp = fr_dcursor_current(cursor);
4521 vp != NULL;
4522 vp = fr_dcursor_next(cursor)) {
4523 RDEBUG2("%pP", vp);
4524 }
4525 REXDENT();
4526 }
4527
4528 /*
4529 * Encoders advance the cursor, so we just need to feed
4530 * in the next pair. This was originally so we could
4531 * extend the output buffer, but with dbuffs that's
4532 * no longer necessary... we might want to refactor this
4533 * in future.
4534 */
4535 for (vp = fr_dcursor_head(cursor);
4536 vp != NULL;
4537 vp = fr_dcursor_current(cursor)) {
4538 /*
4539 *
4540 * Don't check for internal attributes, the
4541 * encoders can skip them if they need to, and the
4542 * internal encoder can encode anything, as can
4543 * things like CBOR.
4544 *
4545 * Don't check the dictionaries. By definition,
4546 * vp->da->dict==request->proto_dict, OR else we're
4547 * using the internal encoder and encoding a real
4548 * protocol.
4549 *
4550 * However, we likely still want a
4551 * dictionary-specific "is encodable" function,
4552 * as AKA/SIM and DHCPv6 encode "bool"s only if
4553 * their value is true.
4554 */
4555 if (encode_children) {
4556 fr_dcursor_t child_cursor;
4557
4559
4560 /*
4561 * If we're given an encoding context which is the
4562 * same as the DA returned by the cursor, that means
4563 * encode the children.
4564 */
4565 fr_pair_dcursor_init(&child_cursor, &vp->vp_group);
4566 while (fr_dcursor_current(&child_cursor)) {
4567 len = tp_encode->func(dbuff, &child_cursor, encode_ctx);
4568 if (len < 0) break;
4569 }
4570 fr_dcursor_next(cursor);
4571 } else {
4572 len = tp_encode->func(dbuff, cursor, encode_ctx);
4573 }
4574 if (len < 0) {
4575 RPEDEBUG("Protocol encoding failed");
4576 return XLAT_ACTION_FAIL;
4577 }
4578
4579 tainted |= vp->vp_tainted;
4580 }
4581
4582 /*
4583 * Pass the options string back to the caller.
4584 */
4585 MEM(encoded = fr_value_box_alloc_null(ctx));
4586 fr_value_box_memdup(encoded, encoded, NULL, fr_dbuff_start(dbuff), fr_dbuff_used(dbuff), tainted);
4587 fr_dcursor_append(out, encoded);
4588
4589 return XLAT_ACTION_DONE;
4590}
4591
4592static int xlat_protocol_register_by_name(dl_t *dl, char const *name, fr_dict_t const *dict)
4593{
4594 fr_test_point_pair_decode_t *tp_decode;
4595 fr_test_point_pair_encode_t *tp_encode;
4596 xlat_pair_decode_uctx_t *decode_uctx;
4597 xlat_t *xlat;
4598 char buffer[256+32];
4599
4600 /*
4601 * See if there's a decode function for it.
4602 */
4603 snprintf(buffer, sizeof(buffer), "%s_tp_decode_pair", name);
4604 tp_decode = dlsym(dl->handle, buffer);
4605 if (tp_decode) {
4606 snprintf(buffer, sizeof(buffer), "%s.decode", name);
4607
4608 /* May be called multiple times, so just skip protocols we've already registered */
4609 if (xlat_func_find(buffer, -1)) return 1;
4610
4611 if (unlikely((xlat = xlat_func_register(NULL, buffer, xlat_pair_decode, FR_TYPE_UINT32)) == NULL)) return -1;
4613 decode_uctx = talloc(xlat, xlat_pair_decode_uctx_t);
4614 decode_uctx->tp_decode = tp_decode;
4615 decode_uctx->dict = dict;
4616 /* coverity[suspicious_sizeof] */
4619 }
4620
4621 /*
4622 * See if there's an encode function for it.
4623 */
4624 snprintf(buffer, sizeof(buffer), "%s_tp_encode_pair", name);
4625 tp_encode = dlsym(dl->handle, buffer);
4626 if (tp_encode) {
4627 snprintf(buffer, sizeof(buffer), "%s.encode", name);
4628
4629 if (xlat_func_find(buffer, -1)) return 1;
4630
4631 if (unlikely((xlat = xlat_func_register(NULL, buffer, xlat_pair_encode, FR_TYPE_OCTETS)) == NULL)) return -1;
4633 /* coverity[suspicious_sizeof] */
4636 }
4637
4638 return 0;
4639}
4640
4641static int xlat_protocol_register(fr_dict_t const *dict)
4642{
4643 dl_t *dl = fr_dict_dl(dict);
4644 char *p, name[256];
4645
4646 /*
4647 * No library for this protocol, skip it.
4648 *
4649 * Protocol TEST has no libfreeradius-test, so that's OK.
4650 */
4651 if (!dl) return 0;
4652
4653 strlcpy(name, fr_dict_root(dict)->name, sizeof(name));
4654 for (p = name; *p != '\0'; p++) {
4655 *p = tolower((uint8_t) *p);
4656 }
4657
4658 return xlat_protocol_register_by_name(dl, name, dict != fr_dict_internal() ? dict : NULL);
4659}
4660
4662
4664{
4665 dl_t *dl;
4666
4667 cbor_loader = dl_loader_init(NULL, NULL, false, false);
4668 if (!cbor_loader) return 0;
4669
4670 dl = dl_by_name(cbor_loader, "libfreeradius-cbor", NULL, false);
4671 if (!dl) return 0;
4672
4673 if (xlat_protocol_register_by_name(dl, "cbor", NULL) < 0) return -1;
4674
4675 return 0;
4676}
4677
4678
4679/** Register xlats for any loaded dictionaries
4680 */
4682{
4683 fr_dict_t *dict;
4685
4686 for (dict = fr_dict_global_ctx_iter_init(&iter);
4687 dict != NULL;
4688 dict = fr_dict_global_ctx_iter_next(&iter)) {
4689 if (xlat_protocol_register(dict) < 0) return -1;
4690 }
4691
4692 /*
4693 * And the internal protocol, too.
4694 */
4695 if (xlat_protocol_register(fr_dict_internal()) < 0) return -1;
4696
4697 /*
4698 * And cbor stuff
4699 */
4700 if (xlat_protocol_register_cbor() < 0) return -1;
4701
4702 return 0;
4703}
4704
4705/** De-register all xlat functions we created
4706 *
4707 */
4708static int _xlat_global_free(UNUSED void *uctx)
4709{
4710 TALLOC_FREE(xlat_ctx);
4714
4715 return 0;
4716}
4717
4718/** Global initialisation for xlat
4719 *
4720 * @note Free memory with #xlat_free
4721 *
4722 * @return
4723 * - 0 on success.
4724 * - -1 on failure.
4725 *
4726 * @hidecallgraph
4727 */
4728static int _xlat_global_init(UNUSED void *uctx)
4729{
4730 xlat_t *xlat;
4731
4732 xlat_ctx = talloc_init("xlat");
4733 if (!xlat_ctx) return -1;
4734
4735 if (xlat_func_init() < 0) return -1;
4736
4737 /*
4738 * Lookup attributes used by virtual xlat expansions.
4739 */
4740 if (xlat_eval_init() < 0) return -1;
4741
4742 /*
4743 * Registers async xlat operations in the `unlang` interpreter.
4744 */
4746
4747 /*
4748 * These are all "pure" functions.
4749 */
4750#define XLAT_REGISTER_ARGS(_xlat, _func, _return_type, _args) \
4751do { \
4752 if (unlikely((xlat = xlat_func_register(xlat_ctx, _xlat, _func, _return_type)) == NULL)) return -1; \
4753 xlat_func_args_set(xlat, _args); \
4754 xlat_func_flags_set(xlat, XLAT_FUNC_FLAG_PURE | XLAT_FUNC_FLAG_INTERNAL); \
4755} while (0)
4756
4757#define XLAT_NEW(_x) xlat->replaced_with = _x
4758
4760
4763 XLAT_NEW("str.concat");
4764
4767 XLAT_NEW("str.split");
4768
4770
4773 XLAT_NEW("hmac.md5");
4774
4777 XLAT_NEW("hmac.sha1");
4778
4780 xlat->deprecated = true;
4781
4784 xlat->deprecated = true;
4785
4787
4790 XLAT_NEW("str.lpad");
4791
4794 XLAT_NEW("str.rpad");
4795
4798 XLAT_NEW("str.substr");
4799
4802
4803 /*
4804 * The inputs to these functions are variable.
4805 */
4806#undef XLAT_REGISTER_ARGS
4807#define XLAT_REGISTER_ARGS(_xlat, _func, _return_type, _args) \
4808do { \
4809 if (unlikely((xlat = xlat_func_register(xlat_ctx, _xlat, _func, _return_type)) == NULL)) return -1; \
4810 xlat_func_args_set(xlat, _args); \
4811 xlat_func_flags_set(xlat, XLAT_FUNC_FLAG_INTERNAL); \
4812} while (0)
4813
4814#undef XLAT_REGISTER_VOID
4815#define XLAT_REGISTER_VOID(_xlat, _func, _return_type) \
4816do { \
4817 if (unlikely((xlat = xlat_func_register(xlat_ctx, _xlat, _func, _return_type)) == NULL)) return -1; \
4818 xlat_func_flags_set(xlat, XLAT_FUNC_FLAG_INTERNAL); \
4819} while (0)
4820
4824 XLAT_NEW("pairs.debug");
4825
4835
4837 XLAT_NEW("pairs.immutable");
4839
4845
4847 XLAT_NEW("time.next");
4849
4851 XLAT_NEW("pairs.print");
4853
4855
4857#ifdef HAVE_REGEX_PCRE2
4858 xlat_func_instantiate_set(xlat, xlat_instantiate_subst_regex, xlat_subst_regex_inst_t, NULL, NULL);
4859#endif
4861 XLAT_NEW("str.subst");
4862#ifdef HAVE_REGEX_PCRE2
4863 xlat_func_instantiate_set(xlat, xlat_instantiate_subst_regex, xlat_subst_regex_inst_t, NULL, NULL);
4864#endif
4865
4866#ifndef NDEBUG
4868#endif
4869
4875
4880
4883 XLAT_NEW("str.rand");
4884
4887
4889
4890 if (unlikely((xlat = xlat_func_register(xlat_ctx, "untaint", xlat_func_untaint, FR_TYPE_VOID)) == NULL)) return -1;
4893
4894 if (unlikely((xlat = xlat_func_register(xlat_ctx, "taint", xlat_func_taint, FR_TYPE_VOID)) == NULL)) return -1;
4897
4898 /*
4899 * All of these functions are pure.
4900 */
4901#define XLAT_REGISTER_PURE(_xlat, _func, _return_type, _arg) \
4902do { \
4903 if (unlikely((xlat = xlat_func_register(xlat_ctx, _xlat, _func, _return_type)) == NULL)) return -1; \
4904 xlat_func_args_set(xlat, _arg); \
4905 xlat_func_flags_set(xlat, XLAT_FUNC_FLAG_PURE | XLAT_FUNC_FLAG_INTERNAL); \
4906} while (0)
4907
4912 XLAT_NEW("hash.md4");
4913
4916 XLAT_NEW("hash.md4");
4917
4918 if (unlikely((xlat = xlat_func_register(xlat_ctx, "regex.match", xlat_func_regex, FR_TYPE_STRING)) == NULL)) return -1;
4921 if (unlikely((xlat = xlat_func_register(xlat_ctx, "regex", xlat_func_regex, FR_TYPE_STRING)) == NULL)) return -1;
4924 XLAT_NEW("regex.match");
4925
4926 {
4927 static xlat_arg_parser_t const xlat_regex_safe_args[] = {
4928 { .type = FR_TYPE_STRING, .variadic = true, .concat = true },
4930 };
4931
4932 static xlat_arg_parser_t const xlat_regex_escape_args[] = {
4933 { .type = FR_TYPE_STRING,
4934 .func = regex_xlat_escape, .safe_for = FR_REGEX_SAFE_FOR, .always_escape = true,
4935 .variadic = true, .concat = true },
4937 };
4938
4939 if (unlikely((xlat = xlat_func_register(xlat_ctx, "regex.safe",
4940 xlat_transparent, FR_TYPE_STRING)) == NULL)) return -1;
4942 xlat_func_args_set(xlat, xlat_regex_safe_args);
4943 xlat_func_safe_for_set(xlat, FR_REGEX_SAFE_FOR);
4944
4945 if (unlikely((xlat = xlat_func_register(xlat_ctx, "regex.escape",
4946 xlat_transparent, FR_TYPE_STRING)) == NULL)) return -1;
4948 xlat_func_args_set(xlat, xlat_regex_escape_args);
4949 xlat_func_safe_for_set(xlat, FR_REGEX_SAFE_FOR);
4950 }
4951
4952#define XLAT_REGISTER_HASH(_name, _func) do { \
4953 XLAT_REGISTER_PURE("hash." _name, _func, FR_TYPE_OCTETS, xlat_func_sha_arg); \
4954 XLAT_REGISTER_PURE(_name, _func, FR_TYPE_OCTETS, xlat_func_sha_arg); \
4955 XLAT_NEW("hash." _name); \
4956 } while (0)
4957
4959
4960#ifdef HAVE_OPENSSL_EVP_H
4961 XLAT_REGISTER_HASH("sha2_224", xlat_func_sha2_224);
4962 XLAT_REGISTER_HASH("sha2_256", xlat_func_sha2_256);
4963 XLAT_REGISTER_HASH("sha2_384", xlat_func_sha2_384);
4964 XLAT_REGISTER_HASH("sha2_512", xlat_func_sha2_512);
4965 XLAT_REGISTER_HASH("sha2", xlat_func_sha2_256);
4966
4967# ifdef HAVE_EVP_BLAKE2S256
4968 XLAT_REGISTER_HASH("blake2s_256", xlat_func_blake2s_256);
4969# endif
4970# ifdef HAVE_EVP_BLAKE2B512
4971 XLAT_REGISTER_HASH("blake2b_512", xlat_func_blake2b_512);
4972# endif
4973
4974 XLAT_REGISTER_HASH("sha3_224", xlat_func_sha3_224);
4975 XLAT_REGISTER_HASH("sha3_256", xlat_func_sha3_256);
4976 XLAT_REGISTER_HASH("sha3_384", xlat_func_sha3_384);
4977 XLAT_REGISTER_HASH("sha3_512", xlat_func_sha3_512);
4978 XLAT_REGISTER_HASH("sha3", xlat_func_sha3_256);
4979#endif
4980
4982 xlat->deprecated = true;
4984 XLAT_NEW("length");
4985
4988
4991 XLAT_NEW("str.lower");
4992
4995 XLAT_NEW("str.upper");
4996
4999 XLAT_NEW("url.quote");
5000
5003 XLAT_NEW("url.unquote");
5004
5006
5008}
5009
5011{
5012 int ret;
5013 fr_atexit_global_once_ret(&ret, _xlat_global_init, _xlat_global_free, NULL);
5014 return ret;
5015}
static int const char char buffer[256]
Definition acutest.h:578
int const char * file
Definition acutest.h:704
va_list args
Definition acutest.h:772
static int const char * fmt
Definition acutest.h:575
#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:43
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:487
#define L(_str)
Helper for initialising arrays of string literals.
Definition build.h:209
#define unlikely(_x)
Definition build.h:383
#define UNUSED
Definition build.h:317
#define NUM_ELEMENTS(_t)
Definition build.h:339
#define fr_dbuff_used(_dbuff_or_marker)
Return the number of bytes remaining between the start of the dbuff or marker and the current positio...
Definition dbuff.h:777
#define fr_dbuff_start(_dbuff_or_marker)
Return the 'start' position of a dbuff or marker.
Definition dbuff.h:908
#define FR_DBUFF_TALLOC_THREAD_LOCAL(_out, _init, _max)
Create a function local and thread local extensible dbuff.
Definition dbuff.h:566
#define FR_DBUFF_TMP(_start, _len_or_end)
Creates a compound literal to pass into functions which accept a dbuff.
Definition dbuff.h:524
static void * fr_dcursor_next(fr_dcursor_t *cursor)
Advanced the cursor to the next item.
Definition dcursor.h:290
static int fr_dcursor_append(fr_dcursor_t *cursor, void *v)
Insert a single item at the end of the list.
Definition dcursor.h:408
static void * fr_dcursor_current(fr_dcursor_t *cursor)
Return the item the cursor current points to.
Definition dcursor.h:339
static void * fr_dcursor_head(fr_dcursor_t *cursor)
Rewind cursor to the start of the list.
Definition dcursor.h:234
#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:4893
char const * name
Vendor name.
Definition dict.h:276
fr_dict_attr_t const * fr_dict_attr_common_parent(fr_dict_attr_t const *a, fr_dict_attr_t const *b, bool is_ancestor)
Find a common ancestor that two TLV type attributes share.
Definition dict_util.c:2314
static fr_slen_t err
Definition dict.h:884
fr_dict_t * fr_dict_global_ctx_iter_init(fr_dict_global_ctx_iter_t *iter)
Iterate protocols by name.
Definition dict_util.c:4886
fr_dict_attr_t const * fr_dict_root(fr_dict_t const *dict)
Return the root attribute of a dictionary.
Definition dict_util.c:2666
dl_t * fr_dict_dl(fr_dict_t const *dict)
Definition dict_util.c:2676
uint32_t pen
Private enterprise number.
Definition dict.h:272
fr_dict_t const * fr_dict_internal(void)
Definition dict_util.c:4929
static fr_slen_t in
Definition dict.h:884
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:2901
Private enterprise.
Definition dict.h:271
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:900
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:468
static unsigned int fr_dlist_num_elements(fr_dlist_head_t const *head)
Return the number of elements in the dlist.
Definition dlist.h:921
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:537
void fr_bio_shutdown & my
Definition fd_errno.h:70
static xlat_action_t xlat_func_time_now(TALLOC_CTX *ctx, fr_dcursor_t *out, UNUSED xlat_ctx_t const *xctx, UNUSED request_t *request, UNUSED fr_value_box_list_t *args)
Return the current time as a FR_TYPE_DATE.
static xlat_action_t xlat_func_next_time(TALLOC_CTX *ctx, fr_dcursor_t *out, UNUSED xlat_ctx_t const *xctx, request_t *request, fr_value_box_list_t *args)
Calculate number of seconds until the next n hour(s), day(s), week(s), year(s).
static xlat_action_t xlat_func_lpad(UNUSED TALLOC_CTX *ctx, fr_dcursor_t *out, UNUSED xlat_ctx_t const *xctx, request_t *request, fr_value_box_list_t *args)
lpad a string
static xlat_action_t xlat_func_bin(TALLOC_CTX *ctx, fr_dcursor_t *out, UNUSED xlat_ctx_t const *xctx, request_t *request, fr_value_box_list_t *args)
Convert hex string to binary.
static xlat_action_t xlat_func_pairs_debug(UNUSED TALLOC_CTX *ctx, UNUSED fr_dcursor_t *out, UNUSED xlat_ctx_t const *xctx, request_t *request, fr_value_box_list_t *args)
Print out attribute info.
static xlat_action_t xlat_func_subst(TALLOC_CTX *ctx, fr_dcursor_t *out, UNUSED xlat_ctx_t const *xctx, request_t *request, fr_value_box_list_t *args)
Perform regex substitution.
static xlat_action_t xlat_func_urlunquote(TALLOC_CTX *ctx, fr_dcursor_t *out, UNUSED xlat_ctx_t const *xctx, request_t *request, fr_value_box_list_t *args)
URLdecode special characters.
static xlat_action_t xlat_pair_decode(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_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 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_block(TALLOC_CTX *ctx, fr_dcursor_t *out, UNUSED xlat_ctx_t const *xctx, UNUSED request_t *request, fr_value_box_list_t *args)
Block for the specified duration.
static xlat_action_t xlat_func_concat(TALLOC_CTX *ctx, fr_dcursor_t *out, UNUSED xlat_ctx_t const *xctx, request_t *request, fr_value_box_list_t *args)
Concatenate string representation of values of given attributes using separator.
static xlat_action_t xlat_func_urlquote(TALLOC_CTX *ctx, fr_dcursor_t *out, UNUSED xlat_ctx_t const *xctx, UNUSED request_t *request, fr_value_box_list_t *args)
URLencode special characters.
static xlat_action_t xlat_func_rpad(UNUSED TALLOC_CTX *ctx, fr_dcursor_t *out, UNUSED xlat_ctx_t const *xctx, request_t *request, fr_value_box_list_t *args)
Right pad a string.
static xlat_action_t xlat_func_md4(TALLOC_CTX *ctx, fr_dcursor_t *out, UNUSED xlat_ctx_t const *xctx, UNUSED request_t *request, fr_value_box_list_t *args)
Calculate the MD4 hash of a string or attribute.
static xlat_action_t xlat_func_explode(TALLOC_CTX *ctx, fr_dcursor_t *out, UNUSED xlat_ctx_t const *xctx, request_t *request, fr_value_box_list_t *args)
Split a string into multiple new strings based on a delimiter.
static xlat_action_t xlat_func_pairs_print(TALLOC_CTX *ctx, fr_dcursor_t *out, UNUSED xlat_ctx_t const *xctx, request_t *request, fr_value_box_list_t *args)
Encode attributes as a series of string attribute/value pairs.
static xlat_action_t xlat_func_time_request(TALLOC_CTX *ctx, fr_dcursor_t *out, UNUSED xlat_ctx_t const *xctx, request_t *request, UNUSED fr_value_box_list_t *args)
Return the request receive time as a FR_TYPE_DATE.
static xlat_action_t xlat_func_regex(TALLOC_CTX *ctx, fr_dcursor_t *out, UNUSED xlat_ctx_t const *xctx, request_t *request, fr_value_box_list_t *in)
Get named subcapture value from previous regex.
static xlat_action_t xlat_func_substr(TALLOC_CTX *ctx, fr_dcursor_t *out, UNUSED xlat_ctx_t const *xctx, request_t *request, fr_value_box_list_t *args)
Extract a substring from string / octets data.
static xlat_action_t xlat_func_length(TALLOC_CTX *ctx, fr_dcursor_t *out, UNUSED xlat_ctx_t const *xctx, UNUSED request_t *request, fr_value_box_list_t *in)
Return the on-the-wire size of the boxes in bytes.
static xlat_action_t xlat_func_immutable_attr(UNUSED TALLOC_CTX *ctx, UNUSED fr_dcursor_t *out, UNUSED xlat_ctx_t const *xctx, request_t *request, fr_value_box_list_t *args)
Mark one or more attributes as immutable.
static xlat_action_t xlat_func_rand(TALLOC_CTX *ctx, fr_dcursor_t *out, UNUSED xlat_ctx_t const *xctx, UNUSED request_t *request, fr_value_box_list_t *in)
Generate a random integer value.
static xlat_action_t xlat_pair_encode(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.
static xlat_action_t xlat_func_log_err(UNUSED TALLOC_CTX *ctx, UNUSED fr_dcursor_t *out, UNUSED xlat_ctx_t const *xctx, request_t *request, fr_value_box_list_t *args)
Log something at DEBUG level.
static xlat_action_t xlat_func_hmac_sha1(TALLOC_CTX *ctx, fr_dcursor_t *out, UNUSED xlat_ctx_t const *xctx, UNUSED request_t *request, fr_value_box_list_t *in)
Generate the HMAC-SHA1 of a string or attribute.
static xlat_action_t xlat_func_eval(TALLOC_CTX *ctx, fr_dcursor_t *out, UNUSED xlat_ctx_t const *xctx, request_t *request, fr_value_box_list_t *args)
Dynamically evaluate an expansion string.
static xlat_action_t xlat_func_time_is_dst(TALLOC_CTX *ctx, fr_dcursor_t *out, UNUSED xlat_ctx_t const *xctx, UNUSED request_t *request, UNUSED fr_value_box_list_t *args)
Return whether we are in daylight savings or not.
static xlat_action_t xlat_func_integer(TALLOC_CTX *ctx, fr_dcursor_t *out, UNUSED xlat_ctx_t const *xctx, request_t *request, fr_value_box_list_t *args)
Print data as integer, not as VALUE.
static xlat_action_t xlat_func_time(TALLOC_CTX *ctx, fr_dcursor_t *out, UNUSED xlat_ctx_t const *xctx, request_t *request, fr_value_box_list_t *args)
Return the time as a FR_TYPE_DATE.
static xlat_action_t xlat_func_toupper(TALLOC_CTX *ctx, fr_dcursor_t *out, UNUSED xlat_ctx_t const *xctx, request_t *request, fr_value_box_list_t *in)
Convert a string to uppercase.
static xlat_action_t xlat_func_uuid_v7(TALLOC_CTX *ctx, fr_dcursor_t *out, UNUSED xlat_ctx_t const *xctx, UNUSED request_t *request, UNUSED fr_value_box_list_t *args)
Generate a version 7 UUID.
static xlat_action_t xlat_func_uuid_v4(TALLOC_CTX *ctx, fr_dcursor_t *out, UNUSED xlat_ctx_t const *xctx, UNUSED request_t *request, UNUSED fr_value_box_list_t *args)
Generate a version 4 UUID.
static xlat_action_t xlat_func_cast(TALLOC_CTX *ctx, fr_dcursor_t *out, UNUSED xlat_ctx_t const *xctx, request_t *request, fr_value_box_list_t *args)
Cast one or more output value-boxes to the given type.
static xlat_action_t xlat_func_hex(UNUSED TALLOC_CTX *ctx, fr_dcursor_t *out, UNUSED xlat_ctx_t const *xctx, UNUSED request_t *request, fr_value_box_list_t *args)
Print data as hex, not as VALUE.
static xlat_action_t xlat_func_md5(TALLOC_CTX *ctx, fr_dcursor_t *out, UNUSED xlat_ctx_t const *xctx, UNUSED request_t *request, fr_value_box_list_t *args)
Calculate the MD5 hash of a string or attribute.
static xlat_action_t xlat_func_subnet_netmask(TALLOC_CTX *ctx, fr_dcursor_t *out, UNUSED xlat_ctx_t const *xctx, UNUSED request_t *request, fr_value_box_list_t *args)
Calculate the subnet mask from a IPv4 prefix.
static xlat_action_t xlat_func_sha1(TALLOC_CTX *ctx, fr_dcursor_t *out, UNUSED xlat_ctx_t const *xctx, UNUSED request_t *request, fr_value_box_list_t *args)
Calculate the SHA1 hash of a string or attribute.
static xlat_action_t xlat_func_str_printable(TALLOC_CTX *ctx, fr_dcursor_t *out, UNUSED xlat_ctx_t const *xctx, UNUSED request_t *request, fr_value_box_list_t *args)
Return whether a string has only printable chars.
static xlat_action_t xlat_func_range(TALLOC_CTX *ctx, fr_dcursor_t *out, UNUSED xlat_ctx_t const *xctx, request_t *request, fr_value_box_list_t *args)
Generate a range of uint64 numbers.
static xlat_action_t xlat_func_randstr(TALLOC_CTX *ctx, fr_dcursor_t *out, UNUSED xlat_ctx_t const *xctx, request_t *request, fr_value_box_list_t *args)
Generate a string of random chars.
static xlat_action_t xlat_func_tolower(TALLOC_CTX *ctx, fr_dcursor_t *out, UNUSED xlat_ctx_t const *xctx, request_t *request, fr_value_box_list_t *in)
Convert a string to lowercase.
static xlat_action_t xlat_func_subnet_broadcast(TALLOC_CTX *ctx, fr_dcursor_t *out, UNUSED xlat_ctx_t const *xctx, UNUSED request_t *request, fr_value_box_list_t *args)
Calculate the broadcast address from a IPv4 prefix.
static xlat_action_t xlat_func_str_utf8(TALLOC_CTX *ctx, fr_dcursor_t *out, UNUSED xlat_ctx_t const *xctx, UNUSED request_t *request, fr_value_box_list_t *args)
Return whether a string is valid UTF-8.
static xlat_action_t xlat_func_time_offset(TALLOC_CTX *ctx, fr_dcursor_t *out, UNUSED xlat_ctx_t const *xctx, UNUSED request_t *request, UNUSED fr_value_box_list_t *args)
Return the current time offset from gmt.
static xlat_action_t xlat_func_strlen(TALLOC_CTX *ctx, fr_dcursor_t *out, UNUSED xlat_ctx_t const *xctx, UNUSED request_t *request, fr_value_box_list_t *args)
Print length of given string.
Stores the state of the current iteration operation.
Definition hash.h:41
talloc_free(hp)
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:1684
fr_event_list_t * unlang_interpret_event_list(request_t *request)
Get the event list for the current interpreter.
Definition interpret.c:2052
#define UNLANG_SUB_FRAME
Definition interpret.h:37
fr_log_t * log_dst_by_name(char const *name)
Get a logging destination by name.
Definition log.c:1064
#define PERROR(_fmt,...)
Definition log.h:228
#define REXDENT()
Exdent (unindent) R* messages by one level.
Definition log.h:455
#define RWDEBUG(fmt,...)
Definition log.h:373
#define RDEBUG_ENABLED3
True if request debug level 1-3 messages are enabled.
Definition log.h:347
#define REDEBUG3(fmt,...)
Definition log.h:385
#define RERROR(fmt,...)
Definition log.h:310
#define RPERROR(fmt,...)
Definition log.h:314
#define REMARKER(_str, _marker_idx, _marker,...)
Output string with error marker, showing where format error occurred.
Definition log.h:510
#define RINFO(fmt,...)
Definition log.h:308
#define RMARKER(_type, _lvl, _str, _marker_idx, _marker,...)
Output string with error marker, showing where format error occurred.
Definition log.h:481
#define RPEDEBUG(fmt,...)
Definition log.h:388
#define RDEBUG4(fmt,...)
Definition log.h:356
#define RDEBUG_ENABLED4
True if request debug level 1-4 messages are enabled.
Definition log.h:348
#define RIDEBUG2(fmt,...)
Definition log.h:364
#define REDEBUG2(fmt,...)
Definition log.h:384
#define RIDEBUG3(fmt,...)
Definition log.h:365
#define RINDENT()
Indent R* messages by one level.
Definition log.h:442
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:1601
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:1881
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:1436
ssize_t fr_mkdir(int *fd_out, char const *path, ssize_t len, mode_t mode, fr_mkdir_func_t func, void *uctx)
Create directories that are missing in the specified path.
Definition file.c:219
@ 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:471
#define MD4_DIGEST_LENGTH
Definition md4.h:25
#define MD5_DIGEST_LENGTH
unsigned short uint16_t
fr_type_t
@ FR_TYPE_TIME_DELTA
A period of time measured in nanoseconds.
@ FR_TYPE_FLOAT32
Single precision floating point.
@ FR_TYPE_IPV4_ADDR
32 Bit IPv4 Address.
@ FR_TYPE_INT8
8 Bit signed integer.
@ FR_TYPE_ETHERNET
48 Bit Mac-Address.
@ FR_TYPE_IPV6_PREFIX
IPv6 Prefix.
@ FR_TYPE_STRING
String of printable characters.
@ FR_TYPE_NULL
Invalid (uninitialised) attribute type.
@ FR_TYPE_UINT16
16 Bit unsigned integer.
@ FR_TYPE_INT64
64 Bit signed integer.
@ FR_TYPE_INT16
16 Bit signed integer.
@ FR_TYPE_DATE
Unix time stamp, always has value >2^31.
@ FR_TYPE_COMBO_IP_PREFIX
IPv4 or IPv6 address prefix depending on length.
@ FR_TYPE_UINT8
8 Bit unsigned integer.
@ FR_TYPE_UINT32
32 Bit unsigned integer.
@ FR_TYPE_INT32
32 Bit signed integer.
@ FR_TYPE_UINT64
64 Bit unsigned integer.
@ FR_TYPE_IPV6_ADDR
128 Bit IPv6 Address.
@ FR_TYPE_IPV4_PREFIX
IPv4 Prefix.
@ FR_TYPE_VOID
User data.
@ FR_TYPE_BOOL
A truth value.
@ FR_TYPE_SIZE
Unsigned integer capable of representing any memory address on the local system.
@ FR_TYPE_COMBO_IP_ADDR
IPv4 or IPv6 address depending on length.
@ FR_TYPE_IFID
Interface ID.
@ FR_TYPE_OCTETS
Raw octets.
@ FR_TYPE_GROUP
A grouping of other attributes.
@ FR_TYPE_FLOAT64
Double precision floating point.
unsigned int uint32_t
long int ssize_t
void fr_md5_calc(uint8_t out[static MD5_DIGEST_LENGTH], uint8_t const *in, size_t inlen)
Perform a single digest operation on a single input buffer.
unsigned char uint8_t
ssize_t fr_slen_t
long long int off_t
unsigned long int size_t
fr_sbuff_parse_error_t
size_t fr_snprint_uint128(char *out, size_t outlen, uint128_t const num)
Write 128bit unsigned integer to buffer.
Definition misc.c:402
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_pair_t * fr_pair_list_parent(fr_pair_list_t const *list)
Return a pointer to the parent pair which contains this list.
Definition pair.c:969
int fr_pair_update_by_da_parent(fr_pair_t *parent, fr_pair_t **out, fr_dict_attr_t const *da)
Return the first fr_pair_t matching the fr_dict_attr_t or alloc a new fr_pair_t and its subtree (and ...
Definition pair.c:1601
int fr_pair_delete(fr_pair_list_t *list, fr_pair_t *vp)
Remove fr_pair_t from a list and free.
Definition pair.c:1832
fr_slen_t fr_utf8_str(uint8_t const *str, ssize_t inlen)
Validate a complete UTF8 string.
Definition print.c:153
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
#define REDEBUG(fmt,...)
#define RDEBUG_ENABLED2()
#define RDEBUG2(fmt,...)
#define RDEBUG(fmt,...)
static bool done
Definition radclient.c:83
#define fill(_expr)
uint32_t fr_rand(void)
Return a 32-bit random number.
Definition rand.c:105
fr_dict_attr_t const * request_attr_request
Definition request.c:43
void request_log_prepend(request_t *request, fr_log_t *log_dst, fr_log_lvl_t lvl)
Prepend another logging destination to the list.
Definition request.c:92
#define RAD_REQUEST_LVL_NONE
No debug messages should be printed.
Definition request.h:312
static char const * name
char * fr_sbuff_adv_to_str(fr_sbuff_t *sbuff, size_t len, char const *needle, size_t needle_len)
Wind position to the first instance of the specified needle.
Definition sbuff.c:2025
char * fr_sbuff_adv_to_chr(fr_sbuff_t *sbuff, size_t len, char c)
Wind position to first instance of specified char.
Definition sbuff.c:1989
ssize_t fr_sbuff_in_bstrncpy(fr_sbuff_t *sbuff, char const *str, size_t len)
Copy bytes into the sbuff up to the first \0.
Definition sbuff.c:1488
ssize_t fr_sbuff_in_sprintf(fr_sbuff_t *sbuff, char const *fmt,...)
Print using a fmt string to an sbuff.
Definition sbuff.c:1597
bool fr_sbuff_next_if_char(fr_sbuff_t *sbuff, char c)
Return true if the current char matches, and if it does, advance.
Definition sbuff.c:2121
#define fr_sbuff_start(_sbuff_or_marker)
#define fr_sbuff_set(_dst, _src)
#define FR_SBUFF_IN(_start, _len_or_end)
#define fr_sbuff_adv_past_whitespace(_sbuff, _len, _tt)
#define fr_sbuff_current(_sbuff_or_marker)
char const * name
Name for rule set to aid we debugging.
Definition sbuff.h:212
#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:638
@ TMPL_TYPE_ATTR
Reference to one or more attributes.
Definition tmpl.h:142
@ TMPL_TYPE_XLAT
Pre-parsed xlat expansion.
Definition tmpl.h:146
@ TMPL_TYPE_EXEC
Callout to an external script or program.
Definition tmpl.h:150
@ TMPL_TYPE_REGEX_XLAT_UNRESOLVED
A regular expression with unresolved xlat functions or attribute references.
Definition tmpl.h:197
@ TMPL_TYPE_DATA
Value in native boxed format.
Definition tmpl.h:138
@ TMPL_TYPE_DATA_UNRESOLVED
Unparsed literal string.
Definition tmpl.h:179
tmpl_attr_rules_t attr
Rules/data for parsing attribute references.
Definition tmpl.h:339
Optional arguments passed to vp_tmpl functions.
Definition tmpl.h:336
void fr_sha1_init(fr_sha1_ctx *context)
Definition sha1.c:93
void fr_sha1_final(uint8_t digest[static SHA1_DIGEST_LENGTH], fr_sha1_ctx *context)
Definition sha1.c:141
void fr_sha1_update(fr_sha1_ctx *context, uint8_t const *in, size_t len)
Definition sha1.c:105
#define SHA1_DIGEST_LENGTH
Definition sha1.h:29
static char buff[sizeof("18446744073709551615")+3]
Definition size_tests.c:41
PUBLIC int snprintf(char *string, size_t length, char *format, va_alist)
Definition snprintf.c:689
PRIVATE void strings()
eap_aka_sim_process_conf_t * inst
fr_aka_sim_id_type_t type
fr_pair_t * vp
#define fr_time()
Allow us to arbitrarily manipulate time.
Definition state_test.c:8
size_t strlcpy(char *dst, char const *src, size_t siz)
Definition strlcpy.c:34
Definition log.h:96
fr_log_t * parent
Log destination this was cloned from.
Definition log.h:121
fr_log_dst_t dst
Log destination.
Definition log.h:97
int fd
File descriptor to write messages to.
Definition log.h:112
char const * file
Path to log file.
Definition log.h:113
Value pair map.
Definition map.h:77
tmpl_t * lhs
Typically describes the attribute to add, modify or compare.
Definition map.h:78
tmpl_t * rhs
Typically describes a literal value or a src attribute to copy or compare.
Definition map.h:79
fr_dict_t const * dict_def
Default dictionary to use with unqualified attribute references.
Definition tmpl.h:273
Stores an attribute, a value and various bits of other data.
Definition pair.h:68
fr_dict_attr_t const *_CONST da
Dictionary attribute defines the attribute number, vendor and type of the pair.
Definition pair.h:69
char const * fr_syserror(int num)
Guaranteed to be thread-safe version of strerror.
Definition syserror.c:243
#define fr_table_value_by_str(_table, _name, _def)
Convert a string to a value using a sorted or ordered table.
Definition table.h:653
#define fr_table_value_by_substr(_table, _name, _name_len, _def)
Convert a partial string to a value using an ordered or sorted table.
Definition table.h:693
An element in an arbitrarily ordered array of name to num mappings.
Definition table.h:57
An element in a lexicographically sorted array of name to num mappings.
Definition table.h:49
char * talloc_bstrndup(TALLOC_CTX *ctx, char const *in, size_t inlen)
Binary safe strndup function.
Definition talloc.c:617
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:645
#define talloc_get_type_abort_const
Definition talloc.h:113
#define talloc_strdup(_ctx, _str)
Definition talloc.h:145
static size_t talloc_strlen(char const *s)
Returns the length of a talloc array containing a string.
Definition talloc.h:139
fr_test_point_ctx_alloc_t test_ctx
Allocate a test ctx for the encoder.
Definition test_point.h:86
fr_test_point_ctx_alloc_t test_ctx
Allocate a test ctx for the encoder.
Definition test_point.h:94
fr_pair_decode_t func
Decoder for pairs.
Definition test_point.h:87
fr_pair_encode_t func
Encoder for pairs.
Definition test_point.h:95
Entry point for pair decoders.
Definition test_point.h:85
Entry point for pair encoders.
Definition test_point.h:93
bool fr_time_is_dst(void)
Whether or not we're daylight savings.
Definition time.c:1207
int fr_unix_time_from_str(fr_unix_time_t *date, char const *date_str, fr_time_res_t hint)
Convert string in various formats to a fr_unix_time_t.
Definition time.c:794
fr_time_delta_t fr_time_gmtoff(void)
Get the offset to gmt.
Definition time.c:1199
#define fr_time_delta_to_timespec(_delta)
Convert a delta to a timespec.
Definition time.h:666
static int64_t fr_time_to_msec(fr_time_t when)
Convert an fr_time_t (internal time) to number of msec since the unix epoch (wallclock time)
Definition time.h:711
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_time_delta_t fr_time_delta_sub(fr_time_delta_t a, fr_time_delta_t b)
Definition time.h:261
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
static fr_time_delta_t fr_time_delta_from_timespec(struct timespec const *ts)
Definition time.h:614
"Unix" time.
Definition time.h:95
char const * fr_tokens[T_TOKEN_LAST]
Definition token.c:81
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:544
int unlang_xlat_push(TALLOC_CTX *ctx, unlang_result_t *p_result, fr_value_box_list_t *out, request_t *request, xlat_exp_head_t const *xlat, bool top_frame)
Push a pre-compiled xlat onto the stack for evaluation.
Definition xlat.c:270
void unlang_xlat_init(void)
Register xlat operation with the interpreter.
Definition xlat.c:806
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.
#define XLAT_ARG_PARSER_CURSOR
Definition xlat.h:162
unsigned int concat
Concat boxes together.
Definition xlat.h:147
@ XLAT_ARG_VARIADIC_EMPTY_KEEP
Empty argument groups are left alone, and either passed through as empty groups or null boxes.
Definition xlat.h:137
@ XLAT_ARG_VARIADIC_EMPTY_SQUASH
Empty argument groups are removed.
Definition xlat.h:136
xlat_arg_parser_variadic_t variadic
All additional boxes should be processed using this definition.
Definition xlat.h:153
#define XLAT_RESULT_SUCCESS(_p_result)
Definition xlat.h:500
#define XLAT_ARGS(_list,...)
Populate local variables with value boxes from the input list.
Definition xlat.h:383
unsigned int required
Argument must be present, and non-empty.
Definition xlat.h:146
unsigned int single
Argument must only contain a single box.
Definition xlat.h:148
int xlat_resolve(xlat_exp_head_t *head, xlat_res_rules_t const *xr_rules)
Walk over an xlat tree recursively, resolving any unresolved functions or references.
#define XLAT_ARG_PARSER_TERMINATOR
Definition xlat.h:170
xlat_action_t
Definition xlat.h:37
@ XLAT_ACTION_FAIL
An xlat function failed.
Definition xlat.h:44
@ XLAT_ACTION_YIELD
An xlat function pushed a resume frame onto the stack.
Definition xlat.h:42
@ XLAT_ACTION_PUSH_UNLANG
An xlat function pushed an unlang frame onto the unlang stack.
Definition xlat.h:39
@ XLAT_ACTION_DONE
We're done evaluating this level of nesting.
Definition xlat.h:43
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:3165
Definition for a single argument consumend by an xlat function.
Definition xlat.h:145
static fr_slen_t fr_pair_aprint(TALLOC_CTX *ctx, char **out, fr_dict_attr_t const *parent, fr_pair_t const *vp) 1(fr_pair_print
fr_pair_t * fr_pair_list_next(fr_pair_list_t const *list, fr_pair_t const *item))
Get the next item in a valuepair list after a specific entry.
Definition pair_inline.c:69
static void fr_pair_set_immutable(fr_pair_t *vp)
Definition pair.h:699
static fr_slen_t quote ssize_t fr_pair_print_name(fr_sbuff_t *out, fr_dict_attr_t const *parent, fr_pair_t const **vp_p)
Print an attribute name.
Definition pair_print.c:136
#define fr_pair_dcursor_init(_cursor, _list)
Initialises a special dcursor with callbacks that will maintain the attr sublists correctly.
Definition pair.h:604
static fr_slen_t parent
Definition pair.h:858
void fr_strerror_clear(void)
Clears all pending messages from the talloc pools.
Definition strerror.c:576
#define fr_strerror_printf(_fmt,...)
Log to thread local error buffer.
Definition strerror.h:64
fr_table_num_ordered_t const fr_type_table[]
Map data types to names representing those types.
Definition types.c:31
size_t fr_type_table_len
Definition types.c:87
#define fr_type_is_structural(_x)
Definition types.h:393
@ FR_TYPE_ATTR
A contains an attribute reference.
Definition types.h:84
#define FR_TYPE_NON_LEAF
Definition types.h:319
#define fr_type_is_string(_x)
Definition types.h:349
#define fr_type_is_numeric(_x)
Definition types.h:383
#define FR_TYPE_STRUCTURAL
Definition types.h:317
#define fr_type_is_null(_x)
Definition types.h:348
#define fr_type_is_leaf(_x)
Definition types.h:394
static char const * fr_type_to_str(fr_type_t type)
Return a static string containing the type name.
Definition types.h:455
#define FR_TYPE_LEAF
Definition types.h:318
#define FR_TYPE_NUMERIC
Definition types.h:307
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:1439
void fr_value_box_mark_unsafe(fr_value_box_t *vb)
Mark a value-box as "unsafe".
Definition value.c:7197
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:6399
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:6116
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:5000
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:3961
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:6884
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:5033
void fr_value_box_list_untaint(fr_value_box_list_t *head)
Untaint every list member (and their children)
Definition value.c:7081
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:4211
void fr_value_box_clear_value(fr_value_box_t *data)
Clear/free any existing value.
Definition value.c:4346
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:4634
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:7240
void fr_value_box_safety_merge(fr_value_box_t *out, fr_value_box_t const *in)
Merge safety results.
Definition value.c:7249
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:4744
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:7227
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:4779
void fr_value_box_clear(fr_value_box_t *data)
Clear/free any existing value and metadata.
Definition value.c:4392
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:7050
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:4812
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:4853
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:4961
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:5094
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:6615
@ FR_VALUE_BOX_LIST_FREE
Definition value.h:238
@ FR_VALUE_BOX_LIST_FREE_BOX
Free each processed box.
Definition value.h:235
#define fr_value_box_alloc(_ctx, _type, _enumv)
Allocate a value box of a specific type.
Definition value.h:644
#define fr_value_box_mark_safe_for(_box, _safe_for)
Definition value.h:1087
static fr_slen_t data
Definition value.h:1334
static fr_value_box_t * fr_value_box_acopy(TALLOC_CTX *ctx, fr_value_box_t const *src)
Copy an existing box, allocating a new box to hold its contents.
Definition value.h:738
#define fr_value_box_is_safe_for(_box, _safe_for)
Definition value.h:1094
#define fr_box_is_variable_size(_x)
Definition value.h:464
#define fr_value_box_get_cursor(_dst)
Definition value.h:1255
#define VALUE_BOX_VERIFY(_x)
Definition value.h:1364
#define VALUE_BOX_LIST_VERIFY(_x)
Definition value.h:1365
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:655
#define fr_value_box_list_foreach(_list_head, _iter)
Definition value.h:224
static size_t char ** out
Definition value.h:1024
#define fr_box_bool(_val)
Definition value.h:331
#define FR_VALUE_BOX_SAFE_FOR_ANY
Definition value.h:173
static xlat_arg_parser_t const xlat_func_bin_arg[]
static int xlat_protocol_register_cbor(void)
static xlat_arg_parser_t const xlat_func_map_arg[]
static xlat_action_t xlat_func_file_tail(TALLOC_CTX *ctx, fr_dcursor_t *out, UNUSED xlat_ctx_t const *xctx, request_t *request, fr_value_box_list_t *args)
#define XLAT_REGISTER_VOID(_xlat, _func, _return_type)
static xlat_arg_parser_t const xlat_func_log_dst_args[]
static xlat_arg_parser_t const xlat_func_taint_args[]
static xlat_arg_parser_t const xlat_func_time_args[]
static xlat_arg_parser_t const xlat_func_base64_encode_arg[]
unlang_result_t last_result
static xlat_action_t xlat_change_case(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 int _log_dst_free(fr_log_t *log)
static xlat_arg_parser_t const xlat_pair_encode_args[]
static xlat_action_t xlat_hmac(TALLOC_CTX *ctx, fr_dcursor_t *out, fr_value_box_list_t *args, uint8_t *digest, int digest_len, hmac_type type)
static xlat_arg_parser_t const xlat_func_signal_raise_args[]
static void xlat_debug_attr_vp(request_t *request, fr_pair_t const *vp, fr_dict_attr_t const *da)
static xlat_arg_parser_t const xlat_func_log_arg[]
static xlat_action_t xlat_func_file_mkdir(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_sha_arg[]
static xlat_arg_parser_t const xlat_func_cast_args[]
static int xlat_pair_dencode_instantiate(xlat_inst_ctx_t const *mctx)
xlat_action_t xlat_transparent(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.
hmac_type
@ HMAC_MD5
@ HMAC_SHA1
static xlat_arg_parser_t const xlat_func_hex_arg[]
static xlat_arg_parser_t const xlat_func_substr_args[]
static xlat_action_t xlat_func_file_exists(TALLOC_CTX *ctx, fr_dcursor_t *out, UNUSED xlat_ctx_t const *xctx, UNUSED request_t *request, fr_value_box_list_t *args)
static xlat_action_t xlat_func_file_head(TALLOC_CTX *ctx, fr_dcursor_t *out, UNUSED xlat_ctx_t const *xctx, request_t *request, fr_value_box_list_t *args)
static xlat_arg_parser_t const xlat_func_block_args[]
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
static const fr_sbuff_escape_rules_t xlat_filename_escape_dots
static dl_loader_t * cbor_loader
static xlat_arg_parser_t const xlat_change_case_arg[]
static xlat_arg_parser_t const xlat_func_strlen_arg[]
static int xlat_protocol_register(fr_dict_t const *dict)
static xlat_arg_parser_t const xlat_func_md5_arg[]
int xlat_global_init(void)
static xlat_arg_parser_t const xlat_func_urlquote_arg[]
static xlat_arg_parser_t const xlat_pair_cursor_args[]
static xlat_action_t xlat_func_file_size(TALLOC_CTX *ctx, fr_dcursor_t *out, UNUSED xlat_ctx_t const *xctx, request_t *request, fr_value_box_list_t *args)
static void ungroup(fr_dcursor_t *out, fr_value_box_list_t *in)
static xlat_arg_parser_t const xlat_func_md4_arg[]
static int regex_xlat_escape(UNUSED request_t *request, fr_value_box_t *vb, UNUSED void *uctx)
static xlat_arg_parser_t const xlat_func_join_args[]
#define XLAT_NEW(_x)
static xlat_action_t xlat_eval_resume(UNUSED TALLOC_CTX *ctx, UNUSED fr_dcursor_t *out, xlat_ctx_t const *xctx, UNUSED request_t *request, UNUSED fr_value_box_list_t *in)
Just serves to push the result up the stack.
static xlat_action_t xlat_func_taint(UNUSED TALLOC_CTX *ctx, fr_dcursor_t *out, UNUSED xlat_ctx_t const *xctx, UNUSED request_t *request, fr_value_box_list_t *in)
#define XLAT_REGISTER_HASH(_name, _func)
static xlat_arg_parser_t const xlat_func_debug_args[]
static char const hextab[]
#define FR_FILENAME_SAFE_FOR
static xlat_action_t xlat_func_signal_raise(UNUSED TALLOC_CTX *ctx, UNUSED fr_dcursor_t *out, UNUSED xlat_ctx_t const *xctx, request_t *request, fr_value_box_list_t *args)
fr_test_point_pair_decode_t * tp_decode
static xlat_arg_parser_t const xlat_func_pad_args[]
static int uuid_print_vb(fr_value_box_t *vb, uint32_t vals[4])
Convert a UUID in an array of uint32_t to the conventional string representation.
static xlat_arg_parser_t const xlat_func_urlunquote_arg[]
static xlat_action_t xlat_func_file_touch(TALLOC_CTX *ctx, fr_dcursor_t *out, UNUSED xlat_ctx_t const *xctx, request_t *request, fr_value_box_list_t *args)
fr_dict_t const * dict
Restrict xlat to this namespace.
static xlat_arg_parser_t const xlat_pair_decode_args[]
static xlat_arg_parser_t const xlat_func_rand_arg[]
static void uuid_set_variant(uint32_t vals[4], uint8_t variant)
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_action_t xlat_func_join(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_file_name_count_args[]
void xlat_arg_copy_out(TALLOC_CTX *ctx, fr_dcursor_t *out, fr_value_box_list_t *in, fr_value_box_t *vb)
Copy an argument from the input list to the output cursor.
static xlat_arg_parser_t const xlat_func_range_arg[]
static xlat_arg_parser_t const xlat_func_integer_args[]
static int _xlat_global_init(UNUSED void *uctx)
Global initialisation for xlat.
#define XLAT_REGISTER_ARGS(_xlat, _func, _return_type, _args)
static xlat_action_t xlat_func_untaint(UNUSED TALLOC_CTX *ctx, fr_dcursor_t *out, UNUSED xlat_ctx_t const *xctx, UNUSED request_t *request, fr_value_box_list_t *in)
static xlat_action_t xlat_func_file_cat(TALLOC_CTX *ctx, fr_dcursor_t *out, UNUSED xlat_ctx_t const *xctx, request_t *request, fr_value_box_list_t *args)
static xlat_action_t xlat_func_file_rm(TALLOC_CTX *ctx, fr_dcursor_t *out, UNUSED xlat_ctx_t const *xctx, request_t *request, fr_value_box_list_t *args)
xlat_exp_head_t * ex
static xlat_arg_parser_t const xlat_func_length_args[]
static xlat_action_t xlat_func_ungroup(UNUSED TALLOC_CTX *ctx, fr_dcursor_t *out, UNUSED xlat_ctx_t const *xctx, UNUSED request_t *request, fr_value_box_list_t *in)
Ungroups all of its arguments into one flat list.
static int xlat_protocol_register_by_name(dl_t *dl, char const *name, fr_dict_t const *dict)
static xlat_arg_parser_t const xlat_func_file_cat_args[]
static void uuid_set_version(uint32_t vals[4], uint8_t version)
static xlat_action_t xlat_func_file_rmdir(TALLOC_CTX *ctx, fr_dcursor_t *out, UNUSED xlat_ctx_t const *xctx, request_t *request, fr_value_box_list_t *args)
#define UUID_CHARS(_v, _num)
static void xlat_debug_attr_list(request_t *request, fr_pair_list_t const *list, fr_dict_attr_t const *parent)
static xlat_arg_parser_t const xlat_func_file_name_args[]
static TALLOC_CTX * xlat_ctx
static xlat_arg_parser_t const xlat_func_next_time_args[]
static int _xlat_global_free(UNUSED void *uctx)
De-register all xlat functions we created.
static const fr_sbuff_escape_rules_t xlat_filename_escape
static xlat_arg_parser_t const xlat_func_base64_decode_arg[]
static xlat_arg_parser_t const xlat_hmac_args[]
static xlat_arg_parser_t const xlat_func_regex_args[]
void * rctx
Resume context.
Definition xlat_ctx.h:54
xlat_exp_t const * ex
Tokenized expression.
Definition xlat_ctx.h:55
xlat_exp_t * ex
Tokenized expression to use in expansion.
Definition xlat_ctx.h:64
void const * inst
xlat instance data.
Definition xlat_ctx.h:50
void * uctx
Passed to the registration function.
Definition xlat_ctx.h:66
void * inst
xlat instance data to populate.
Definition xlat_ctx.h:63
An xlat calling ctx.
Definition xlat_ctx.h:49
An xlat instantiation ctx.
Definition xlat_ctx.h:62
fr_dict_attr_t const * xlat_time_res_attr(char const *res)
Definition xlat_eval.c:129
int xlat_eval_init(void)
Definition xlat_eval.c:2010
void xlat_eval_free(void)
Definition xlat_eval.c:2032
int xlat_register_expressions(void)
Definition xlat_expr.c:1861
void xlat_func_free(void)
Definition xlat_func.c:563
void xlat_func_flags_set(xlat_t *x, xlat_func_flags_t flags)
Specify flags that alter the xlat's behaviour.
Definition xlat_func.c:399
int xlat_func_args_set(xlat_t *x, xlat_arg_parser_t const args[])
Register the arguments of an xlat.
Definition xlat_func.c:363
xlat_t * xlat_func_register(TALLOC_CTX *ctx, char const *name, xlat_func_t func, fr_type_t return_type)
Register an xlat function.
Definition xlat_func.c:216
int xlat_func_init(void)
Definition xlat_func.c:547
xlat_t * xlat_func_find(char const *in, ssize_t inlen)
Definition xlat_func.c:77
#define xlat_func_instantiate_set(_xlat, _instantiate, _inst_struct, _detach, _uctx)
Set a callback for global instantiation of xlat functions.
Definition xlat_func.h:93
#define xlat_func_safe_for_set(_xlat, _escaped)
Set the escaped values for output boxes.
Definition xlat_func.h:82
@ XLAT_FUNC_FLAG_PURE
Definition xlat_func.h:38
@ XLAT_FUNC_FLAG_INTERNAL
Definition xlat_func.h:39
int xlat_decode_value_box_list(TALLOC_CTX *ctx, fr_pair_list_t *out, request_t *request, void *decode_ctx, fr_pair_decode_t decode, fr_value_box_list_t *in)
Decode all of the value boxes into the output cursor.
Definition xlat_pair.c:90
@ XLAT_GROUP
encapsulated string of xlats
Definition xlat_priv.h:116
bool deprecated
this function was deprecated
Definition xlat_priv.h:68
xlat_type_t _CONST type
type of this expansion.
Definition xlat_priv.h:155
An xlat expansion node.
Definition xlat_priv.h:148