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