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