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