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