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