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