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: 998751f9db377a0f6397b8c56e6abef4af69a620 $
19 *
20 * @file xlat.c
21 * @brief String expansion ("translation"). Implements %Attribute -> value
22 *
23 * @copyright 2000,2006 The FreeRADIUS server project
24 * @copyright 2000 Alan DeKok <aland@ox.org>
25 */
26
27 RCSID("$Id: 998751f9db377a0f6397b8c56e6abef4af69a620 $")
28
29 #include <freeradius-devel/radiusd.h>
30 #include <freeradius-devel/parser.h>
31 #include <freeradius-devel/rad_assert.h>
32 #include <freeradius-devel/base64.h>
33
34 #include <ctype.h>
35
36 typedef struct xlat_t {
37 char name[MAX_STRING_LEN]; //!< Name of the xlat expansion.
38 int length; //!< Length of name.
39 void *instance; //!< Module instance passed to xlat and escape functions.
40 xlat_func_t func; //!< xlat function.
41 xlat_escape_t escape; //!< Escape function to apply to dynamic input to func.
42 bool internal; //!< If true, cannot be redefined.
43 } xlat_t;
44
45 typedef enum {
46 XLAT_LITERAL, //!< Literal string
47 XLAT_PERCENT, //!< Literal string with %v
48 XLAT_MODULE, //!< xlat module
49 XLAT_VIRTUAL, //!< virtual attribute
50 XLAT_ATTRIBUTE, //!< xlat attribute
51 #ifdef HAVE_REGEX
52 XLAT_REGEX, //!< regex reference
53 #endif
54 XLAT_ALTERNATE //!< xlat conditional syntax :-
55 } xlat_state_t;
56
57 struct xlat_exp {
58 char const *fmt; //!< The format string.
59 size_t len; //!< Length of the format string.
60
61 xlat_state_t type; //!< type of this expansion.
62 xlat_exp_t *next; //!< Next in the list.
63
64 xlat_exp_t *child; //!< Nested expansion.
65 xlat_exp_t *alternate; //!< Alternative expansion if this one expanded to a zero length string.
66
67 vp_tmpl_t attr; //!< An attribute template.
68 xlat_t const *xlat; //!< The xlat expansion to expand format with.
69 };
70
71 static rbtree_t *xlat_root = NULL;
72
73 #ifdef WITH_UNLANG
74 static char const * const xlat_foreach_names[] = {"Foreach-Variable-0",
75 "Foreach-Variable-1",
76 "Foreach-Variable-2",
77 "Foreach-Variable-3",
78 "Foreach-Variable-4",
79 "Foreach-Variable-5",
80 "Foreach-Variable-6",
81 "Foreach-Variable-7",
82 "Foreach-Variable-8",
83 "Foreach-Variable-9",
84 NULL};
85 #endif
86
87
88 static int xlat_inst[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; /* up to 10 for foreach */
89
90 /** Print length of its RHS.
91 *
92 */
xlat_strlen(UNUSED void * instance,UNUSED REQUEST * request,char const * fmt,char * out,size_t outlen)93 static ssize_t xlat_strlen(UNUSED void *instance, UNUSED REQUEST *request,
94 char const *fmt, char *out, size_t outlen)
95 {
96 snprintf(out, outlen, "%u", (unsigned int) strlen(fmt));
97 return strlen(out);
98 }
99
100 /** Print the size of the attribute in bytes.
101 *
102 */
xlat_length(UNUSED void * instance,REQUEST * request,char const * fmt,char * out,size_t outlen)103 static ssize_t xlat_length(UNUSED void *instance, REQUEST *request,
104 char const *fmt, char *out, size_t outlen)
105 {
106 VALUE_PAIR *vp;
107 while (isspace((int) *fmt)) fmt++;
108
109 if ((radius_get_vp(&vp, request, fmt) < 0) || !vp) {
110 *out = '\0';
111 return 0;
112 }
113
114 snprintf(out, outlen, "%zu", vp->vp_length);
115 return strlen(out);
116 }
117
118 /** Print data as integer, not as VALUE.
119 *
120 */
xlat_integer(UNUSED void * instance,REQUEST * request,char const * fmt,char * out,size_t outlen)121 static ssize_t xlat_integer(UNUSED void *instance, REQUEST *request,
122 char const *fmt, char *out, size_t outlen)
123 {
124 VALUE_PAIR *vp;
125
126 uint64_t int64 = 0; /* Needs to be initialised to zero */
127 uint32_t int32 = 0; /* Needs to be initialised to zero */
128
129 while (isspace((int) *fmt)) fmt++;
130
131 if ((radius_get_vp(&vp, request, fmt) < 0) || !vp) {
132 *out = '\0';
133 return 0;
134 }
135
136 switch (vp->da->type) {
137 case PW_TYPE_OCTETS:
138 case PW_TYPE_STRING:
139 if (vp->vp_length > 8) {
140 break;
141 }
142
143 if (vp->vp_length > 4) {
144 memcpy(&int64, vp->vp_octets, vp->vp_length);
145 return snprintf(out, outlen, "%" PRIu64, htonll(int64));
146 }
147
148 memcpy(&int32, vp->vp_octets, vp->vp_length);
149 return snprintf(out, outlen, "%i", htonl(int32));
150
151 case PW_TYPE_INTEGER64:
152 return snprintf(out, outlen, "%" PRIu64, vp->vp_integer64);
153
154 /*
155 * IP addresses are treated specially, as parsing functions assume the value
156 * is bigendian and will convert it for us.
157 */
158 case PW_TYPE_IPV4_ADDR:
159 return snprintf(out, outlen, "%u", htonl(vp->vp_ipaddr));
160
161 case PW_TYPE_IPV4_PREFIX:
162 return snprintf(out, outlen, "%u", htonl((*(uint32_t *)(vp->vp_ipv4prefix + 2))));
163
164 case PW_TYPE_INTEGER:
165 return snprintf(out, outlen, "%u", vp->vp_integer);
166
167 case PW_TYPE_DATE:
168 return snprintf(out, outlen, "%u", vp->vp_date);
169
170 case PW_TYPE_BYTE:
171 return snprintf(out, outlen, "%u", (unsigned int) vp->vp_byte);
172
173 case PW_TYPE_SHORT:
174 return snprintf(out, outlen, "%u", (unsigned int) vp->vp_short);
175
176 /*
177 * Ethernet is weird... It's network related, so we assume to it should be
178 * bigendian.
179 */
180 case PW_TYPE_ETHERNET:
181 memcpy(&int64, vp->vp_ether, vp->vp_length);
182 return snprintf(out, outlen, "%" PRIu64, htonll(int64));
183
184 case PW_TYPE_SIGNED:
185 return snprintf(out, outlen, "%i", vp->vp_signed);
186
187 case PW_TYPE_IPV6_ADDR:
188 return fr_prints_uint128(out, outlen, ntohlll(*(uint128_t const *) &vp->vp_ipv6addr));
189
190 case PW_TYPE_IPV6_PREFIX:
191 return fr_prints_uint128(out, outlen, ntohlll(*(uint128_t const *) &vp->vp_ipv6prefix[2]));
192
193 default:
194 break;
195 }
196
197 REDEBUG("Type '%s' of length %zu cannot be converted to integer",
198 fr_int2str(dict_attr_types, vp->da->type, "???"), vp->vp_length);
199 *out = '\0';
200
201 return -1;
202 }
203
204 /** Print data as hex, not as VALUE.
205 *
206 */
xlat_hex(UNUSED void * instance,REQUEST * request,char const * fmt,char * out,size_t outlen)207 static ssize_t xlat_hex(UNUSED void *instance, REQUEST *request,
208 char const *fmt, char *out, size_t outlen)
209 {
210 size_t i;
211 VALUE_PAIR *vp;
212 uint8_t const *p;
213 ssize_t ret;
214 size_t len;
215 value_data_t dst;
216 uint8_t const *buff = NULL;
217
218 while (isspace((int) *fmt)) fmt++;
219
220 if ((radius_get_vp(&vp, request, fmt) < 0) || !vp) {
221 error:
222 *out = '\0';
223 return -1;
224 }
225
226 /*
227 * The easy case.
228 */
229 if (vp->da->type == PW_TYPE_OCTETS) {
230 p = vp->vp_octets;
231 len = vp->vp_length;
232 /*
233 * Cast the value_data_t of the VP to an octets string and
234 * print that as hex.
235 */
236 } else {
237 ret = value_data_cast(request, &dst, PW_TYPE_OCTETS, NULL, vp->da->type,
238 NULL, &vp->data, vp->vp_length);
239 if (ret < 0) {
240 REDEBUG("%s", fr_strerror());
241 goto error;
242 }
243 len = (size_t) ret;
244 p = buff = dst.octets;
245 }
246
247 rad_assert(p);
248
249 /*
250 * Don't truncate the data.
251 */
252 if (outlen < (len * 2)) {
253 rad_const_free(buff);
254 goto error;
255 }
256
257 for (i = 0; i < len; i++) {
258 snprintf(out + 2*i, 3, "%02x", p[i]);
259 }
260 rad_const_free(buff);
261
262 return len * 2;
263 }
264
265 /** Return the tag of an attribute reference
266 *
267 */
xlat_tag(UNUSED void * instance,REQUEST * request,char const * fmt,char * out,size_t outlen)268 static ssize_t xlat_tag(UNUSED void *instance, REQUEST *request,
269 char const *fmt, char *out, size_t outlen)
270 {
271 VALUE_PAIR *vp;
272
273 while (isspace((int) *fmt)) fmt++;
274
275 if ((radius_get_vp(&vp, request, fmt) < 0) || !vp) {
276 *out = '\0';
277 return 0;
278 }
279
280 if (!vp->da->flags.has_tag || !TAG_VALID(vp->tag)) {
281 *out = '\0';
282 return 0;
283 }
284
285 return snprintf(out, outlen, "%u", vp->tag);
286 }
287
288 /** Return the vendor of an attribute reference
289 *
290 */
xlat_vendor(UNUSED void * instance,REQUEST * request,char const * fmt,char * out,size_t outlen)291 static ssize_t xlat_vendor(UNUSED void *instance, REQUEST *request,
292 char const *fmt, char *out, size_t outlen)
293 {
294 VALUE_PAIR *vp;
295 DICT_VENDOR *vendor;
296
297 while (isspace((int) *fmt)) fmt++;
298
299 if ((radius_get_vp(&vp, request, fmt) < 0) || !vp) {
300 *out = '\0';
301 return 0;
302 }
303
304 vendor = dict_vendorbyvalue(vp->da->vendor);
305 if (!vendor) {
306 *out = '\0';
307 return 0;
308 }
309 strlcpy(out, vendor->name, outlen);
310
311 return vendor->length;
312 }
313
314 /** Return the vendor number of an attribute reference
315 *
316 */
xlat_vendor_num(UNUSED void * instance,REQUEST * request,char const * fmt,char * out,size_t outlen)317 static ssize_t xlat_vendor_num(UNUSED void *instance, REQUEST *request,
318 char const *fmt, char *out, size_t outlen)
319 {
320 VALUE_PAIR *vp;
321
322 while (isspace((int) *fmt)) fmt++;
323
324 if ((radius_get_vp(&vp, request, fmt) < 0) || !vp) {
325 *out = '\0';
326 return 0;
327 }
328
329 return snprintf(out, outlen, "%u", vp->da->vendor);
330 }
331
332 /** Return the attribute name of an attribute reference
333 *
334 */
xlat_attr(UNUSED void * instance,REQUEST * request,char const * fmt,char * out,size_t outlen)335 static ssize_t xlat_attr(UNUSED void *instance, REQUEST *request,
336 char const *fmt, char *out, size_t outlen)
337 {
338 VALUE_PAIR *vp;
339
340 while (isspace((int) *fmt)) fmt++;
341
342 if ((radius_get_vp(&vp, request, fmt) < 0) || !vp) {
343 *out = '\0';
344 return 0;
345 }
346 strlcpy(out, vp->da->name, outlen);
347
348 return strlen(vp->da->name);
349 }
350
351 /** Return the attribute number of an attribute reference
352 *
353 */
xlat_attr_num(UNUSED void * instance,REQUEST * request,char const * fmt,char * out,size_t outlen)354 static ssize_t xlat_attr_num(UNUSED void *instance, REQUEST *request,
355 char const *fmt, char *out, size_t outlen)
356 {
357 VALUE_PAIR *vp;
358
359 while (isspace((int) *fmt)) fmt++;
360
361 if ((radius_get_vp(&vp, request, fmt) < 0) || !vp) {
362 *out = '\0';
363 return 0;
364 }
365
366 return snprintf(out, outlen, "%u", vp->da->attr);
367 }
368
369 /** Print out attribute info
370 *
371 * Prints out all instances of a current attribute, or all attributes in a list.
372 *
373 * At higher debugging levels, also prints out alternative decodings of the same
374 * value. This is helpful to determine types for unknown attributes of long
375 * passed vendors, or just crazy/broken NAS.
376 *
377 * This expands to a zero length string.
378 */
xlat_debug_attr(UNUSED void * instance,REQUEST * request,char const * fmt,char * out,UNUSED size_t outlen)379 static ssize_t xlat_debug_attr(UNUSED void *instance, REQUEST *request, char const *fmt,
380 char *out, UNUSED size_t outlen)
381 {
382 VALUE_PAIR *vp;
383 vp_cursor_t cursor;
384
385 vp_tmpl_t vpt;
386
387 if (!RDEBUG_ENABLED2) {
388 *out = '\0';
389 return -1;
390 }
391
392 while (isspace((int) *fmt)) fmt++;
393
394 if (tmpl_from_attr_str(&vpt, fmt, REQUEST_CURRENT, PAIR_LIST_REQUEST, false, false) <= 0) {
395 RDEBUG("%s", fr_strerror());
396 return -1;
397 }
398
399 RIDEBUG("Attributes matching \"%s\"", fmt);
400
401 RINDENT();
402 for (vp = tmpl_cursor_init(NULL, &cursor, request, &vpt);
403 vp;
404 vp = tmpl_cursor_next(&cursor, &vpt)) {
405 FR_NAME_NUMBER const *type;
406 char *value;
407
408 value = vp_aprints_value(vp, vp, '\'');
409 if (vp->da->flags.has_tag) {
410 RIDEBUG2("&%s:%s:%i %s %s",
411 fr_int2str(pair_lists, vpt.tmpl_list, "<INVALID>"),
412 vp->da->name,
413 vp->tag,
414 fr_int2str(fr_tokens, vp->op, "<INVALID>"),
415 value);
416 } else {
417 RIDEBUG2("&%s:%s %s %s",
418 fr_int2str(pair_lists, vpt.tmpl_list, "<INVALID>"),
419 vp->da->name,
420 fr_int2str(fr_tokens, vp->op, "<INVALID>"),
421 value);
422 }
423 talloc_free(value);
424
425 if (!RDEBUG_ENABLED3) continue;
426
427 if (vp->da->vendor) {
428 DICT_VENDOR *dv;
429
430 dv = dict_vendorbyvalue(vp->da->vendor);
431 RIDEBUG2("Vendor : %i (%s)", vp->da->vendor, dv ? dv->name : "unknown");
432 }
433 RIDEBUG2("Type : %s", fr_int2str(dict_attr_types, vp->da->type, "<INVALID>"));
434 RIDEBUG2("Length : %zu", vp->vp_length);
435
436 if (!RDEBUG_ENABLED4) continue;
437
438 type = dict_attr_types;
439 while (type->name) {
440 int pad;
441
442 value_data_t *dst = NULL;
443
444 ssize_t ret;
445
446 if ((PW_TYPE) type->number == vp->da->type) {
447 goto next_type;
448 }
449
450 switch (type->number) {
451 case PW_TYPE_INVALID: /* Not real type */
452 case PW_TYPE_MAX: /* Not real type */
453 case PW_TYPE_EXTENDED: /* Not safe/appropriate */
454 case PW_TYPE_LONG_EXTENDED: /* Not safe/appropriate */
455 case PW_TYPE_TLV: /* Not safe/appropriate */
456 case PW_TYPE_EVS: /* Not safe/appropriate */
457 case PW_TYPE_VSA: /* @fixme We need special behaviour for these */
458 case PW_TYPE_COMBO_IP_ADDR: /* Covered by IPv4 address IPv6 address */
459 case PW_TYPE_COMBO_IP_PREFIX: /* Covered by IPv4 address IPv6 address */
460 case PW_TYPE_TIMEVAL: /* Not a VALUE_PAIR type */
461 goto next_type;
462
463 default:
464 break;
465 }
466
467 dst = talloc_zero(vp, value_data_t);
468 ret = value_data_cast(dst, dst, type->number, NULL, vp->da->type, vp->da,
469 &vp->data, vp->vp_length);
470 if (ret < 0) goto next_type; /* We expect some to fail */
471
472 value = value_data_aprints(dst, type->number, NULL, dst, (size_t)ret, '\'');
473 if (!value) goto next_type;
474
475 if ((pad = (11 - strlen(type->name))) < 0) {
476 pad = 0;
477 }
478
479 RINDENT();
480 RDEBUG2("as %s%*s: %s", type->name, pad, " ", value);
481 REXDENT();
482 talloc_free(value);
483
484 next_type:
485 talloc_free(dst);
486 type++;
487 }
488 }
489 REXDENT();
490
491 *out = '\0';
492 return 0;
493 }
494
495 /** Processes fmt as a map string and applies it to the current request
496 *
497 * e.g. "%{map:&User-Name := 'foo'}"
498 *
499 * Allows sets of modifications to be cached and then applied.
500 * Useful for processing generic attributes from LDAP.
501 */
xlat_map(UNUSED void * instance,REQUEST * request,char const * fmt,char * out,size_t outlen)502 static ssize_t xlat_map(UNUSED void *instance, REQUEST *request,
503 char const *fmt, char *out, size_t outlen)
504 {
505 vp_map_t *map = NULL;
506 int ret;
507
508 if (map_afrom_attr_str(request, &map, fmt,
509 REQUEST_CURRENT, PAIR_LIST_REQUEST,
510 REQUEST_CURRENT, PAIR_LIST_REQUEST) < 0) {
511 REDEBUG("Failed parsing \"%s\" as map: %s", fmt, fr_strerror());
512 return -1;
513 }
514
515 RINDENT();
516 ret = map_to_request(request, map, map_to_vp, NULL);
517 REXDENT();
518 talloc_free(map);
519 if (ret < 0) return strlcpy(out, "0", outlen);
520
521 return strlcpy(out, "1", outlen);
522 }
523
524 /** Prints the current module processing the request
525 *
526 */
xlat_module(UNUSED void * instance,REQUEST * request,UNUSED char const * fmt,char * out,size_t outlen)527 static ssize_t xlat_module(UNUSED void *instance, REQUEST *request,
528 UNUSED char const *fmt, char *out, size_t outlen)
529 {
530 strlcpy(out, request->module, outlen);
531
532 return strlen(out);
533 }
534
535 #if defined(HAVE_REGEX) && defined(HAVE_PCRE)
xlat_regex(UNUSED void * instance,REQUEST * request,char const * fmt,char * out,size_t outlen)536 static ssize_t xlat_regex(UNUSED void *instance, REQUEST *request,
537 char const *fmt, char *out, size_t outlen)
538 {
539 char *p;
540 size_t len;
541
542 if (regex_request_to_sub_named(request, &p, request, fmt) < 0) {
543 *out = '\0';
544 return 0;
545 }
546
547 len = talloc_array_length(p);
548 if (len > outlen) {
549 RDEBUG("Insufficient buffer space to write subcapture value, needed %zu bytes, have %zu bytes",
550 len, outlen);
551 return -1;
552 }
553 strlcpy(out, p, outlen);
554
555 return len - 1; /* - \0 */
556 }
557 #endif
558
559 #ifdef WITH_UNLANG
560 /** Implements the Foreach-Variable-X
561 *
562 * @see modcall()
563 */
xlat_foreach(void * instance,REQUEST * request,UNUSED char const * fmt,char * out,size_t outlen)564 static ssize_t xlat_foreach(void *instance, REQUEST *request,
565 UNUSED char const *fmt, char *out, size_t outlen)
566 {
567 VALUE_PAIR **pvp;
568 size_t len;
569
570 /*
571 * See modcall, "FOREACH" for how this works.
572 */
573 pvp = (VALUE_PAIR **) request_data_reference(request, (void *)radius_get_vp, *(int*) instance);
574 if (!pvp || !*pvp) {
575 *out = '\0';
576 return 0;
577 }
578
579 len = vp_prints_value(out, outlen, *pvp, 0);
580 if (is_truncated(len, outlen)) {
581 RDEBUG("Insufficient buffer space to write foreach value");
582 return -1;
583 }
584
585 return len;
586 }
587 #endif
588
589 /** Print data as string, if possible.
590 *
591 * If attribute "Foo" is defined as "octets" it will normally
592 * be printed as 0x0a0a0a. The xlat "%{string:Foo}" will instead
593 * expand to "\n\n\n"
594 */
xlat_string(UNUSED void * instance,REQUEST * request,char const * fmt,char * out,size_t outlen)595 static ssize_t xlat_string(UNUSED void *instance, REQUEST *request,
596 char const *fmt, char *out, size_t outlen)
597 {
598 size_t len;
599 ssize_t ret;
600 VALUE_PAIR *vp;
601 uint8_t const *p;
602
603 while (isspace((int) *fmt)) fmt++;
604
605 if (outlen < 3) {
606 nothing:
607 *out = '\0';
608 return 0;
609 }
610
611 if ((radius_get_vp(&vp, request, fmt) < 0) || !vp) goto nothing;
612
613 ret = rad_vp2data(&p, vp);
614 if (ret < 0) {
615 return ret;
616 }
617
618 switch (vp->da->type) {
619 case PW_TYPE_OCTETS:
620 len = fr_prints(out, outlen, (char const *) p, vp->vp_length, '"');
621 break;
622
623 /*
624 * Note that "%{string:...}" is NOT binary safe!
625 * It is explicitly used to get rid of embedded zeros.
626 */
627 case PW_TYPE_STRING:
628 len = strlcpy(out, vp->vp_strvalue, outlen);
629 break;
630
631 default:
632 len = fr_prints(out, outlen, (char const *) p, ret, '\0');
633 break;
634 }
635
636 return len;
637 }
638
639 /** xlat expand string attribute value
640 *
641 */
xlat_xlat(UNUSED void * instance,REQUEST * request,char const * fmt,char * out,size_t outlen)642 static ssize_t xlat_xlat(UNUSED void *instance, REQUEST *request,
643 char const *fmt, char *out, size_t outlen)
644 {
645 VALUE_PAIR *vp;
646
647 while (isspace((int) *fmt)) fmt++;
648
649 if (outlen < 3) {
650 nothing:
651 *out = '\0';
652 return 0;
653 }
654
655 if ((radius_get_vp(&vp, request, fmt) < 0) || !vp) goto nothing;
656
657 if (vp->da->type != PW_TYPE_STRING) goto nothing;
658
659 return radius_xlat(out, outlen, request, vp->vp_strvalue, NULL, NULL);
660 }
661
662 /** Dynamically change the debugging level for the current request
663 *
664 * Example %{debug:3}
665 */
xlat_debug(UNUSED void * instance,REQUEST * request,char const * fmt,char * out,size_t outlen)666 static ssize_t xlat_debug(UNUSED void *instance, REQUEST *request,
667 char const *fmt, char *out, size_t outlen)
668 {
669 int level = 0;
670
671 /*
672 * Expand to previous (or current) level
673 */
674 snprintf(out, outlen, "%d", request->log.lvl);
675
676 /*
677 * Assume we just want to get the current value and NOT set it to 0
678 */
679 if (!*fmt)
680 goto done;
681
682 level = atoi(fmt);
683 if (level == 0) {
684 request->log.lvl = RAD_REQUEST_LVL_NONE;
685 request->log.func = NULL;
686 } else {
687 if (level > 4) level = 4;
688
689 request->log.lvl = level;
690 request->log.func = vradlog_request;
691 }
692
693 done:
694 return strlen(out);
695 }
696
697 /*
698 * Compare two xlat_t structs, based ONLY on the module name.
699 */
xlat_cmp(void const * one,void const * two)700 static int xlat_cmp(void const *one, void const *two)
701 {
702 xlat_t const *a = one;
703 xlat_t const *b = two;
704
705 if (a->length != b->length) {
706 return a->length - b->length;
707 }
708
709 return memcmp(a->name, b->name, a->length);
710 }
711
712
713 /*
714 * find the appropriate registered xlat function.
715 */
xlat_find(char const * name)716 static xlat_t *xlat_find(char const *name)
717 {
718 xlat_t my_xlat;
719
720 strlcpy(my_xlat.name, name, sizeof(my_xlat.name));
721 my_xlat.length = strlen(my_xlat.name);
722
723 return rbtree_finddata(xlat_root, &my_xlat);
724 }
725
726
727 /** Register an xlat function.
728 *
729 * @param[in] name xlat name.
730 * @param[in] func xlat function to be called.
731 * @param[in] escape function to sanitize any sub expansions passed to the xlat function.
732 * @param[in] instance of module that's registering the xlat function.
733 * @return 0 on success, -1 on failure
734 */
xlat_register(char const * name,xlat_func_t func,xlat_escape_t escape,void * instance)735 int xlat_register(char const *name, xlat_func_t func, xlat_escape_t escape, void *instance)
736 {
737 xlat_t *c;
738 xlat_t my_xlat;
739 rbnode_t *node;
740
741 if (!name || !*name) {
742 DEBUG("xlat_register: Invalid xlat name");
743 return -1;
744 }
745
746 /*
747 * First time around, build up the tree...
748 *
749 * FIXME: This code should be hoisted out of this function,
750 * and into a global "initialization". But it isn't critical...
751 */
752 if (!xlat_root) {
753 #ifdef WITH_UNLANG
754 int i;
755 #endif
756
757 xlat_root = rbtree_create(NULL, xlat_cmp, NULL, RBTREE_FLAG_REPLACE);
758 if (!xlat_root) {
759 DEBUG("xlat_register: Failed to create tree");
760 return -1;
761 }
762
763 #ifdef WITH_UNLANG
764 for (i = 0; xlat_foreach_names[i] != NULL; i++) {
765 xlat_register(xlat_foreach_names[i],
766 xlat_foreach, NULL, &xlat_inst[i]);
767 c = xlat_find(xlat_foreach_names[i]);
768 rad_assert(c != NULL);
769 c->internal = true;
770 }
771 #endif
772
773 #define XLAT_REGISTER(_x) xlat_register(STRINGIFY(_x), xlat_ ## _x, NULL, NULL); \
774 c = xlat_find(STRINGIFY(_x)); \
775 rad_assert(c != NULL); \
776 c->internal = true
777
778 XLAT_REGISTER(integer);
779 XLAT_REGISTER(strlen);
780 XLAT_REGISTER(length);
781 XLAT_REGISTER(hex);
782 XLAT_REGISTER(tag);
783 XLAT_REGISTER(vendor);
784 XLAT_REGISTER(vendor_num);
785 XLAT_REGISTER(attr);
786 XLAT_REGISTER(attr_num);
787 XLAT_REGISTER(string);
788 XLAT_REGISTER(xlat);
789 XLAT_REGISTER(map);
790 XLAT_REGISTER(module);
791 XLAT_REGISTER(debug_attr);
792 #if defined(HAVE_REGEX) && defined(HAVE_PCRE)
793 XLAT_REGISTER(regex);
794 #endif
795
796 xlat_register("debug", xlat_debug, NULL, &xlat_inst[0]);
797 c = xlat_find("debug");
798 rad_assert(c != NULL);
799 c->internal = true;
800 }
801
802 /*
803 * If it already exists, replace the instance.
804 */
805 strlcpy(my_xlat.name, name, sizeof(my_xlat.name));
806 my_xlat.length = strlen(my_xlat.name);
807 c = rbtree_finddata(xlat_root, &my_xlat);
808 if (c) {
809 if (c->internal) {
810 DEBUG("xlat_register: Cannot re-define internal xlat");
811 return -1;
812 }
813
814 c->func = func;
815 c->escape = escape;
816 c->instance = instance;
817 return 0;
818 }
819
820 /*
821 * Doesn't exist. Create it.
822 */
823 c = talloc_zero(xlat_root, xlat_t);
824
825 c->func = func;
826 c->escape = escape;
827 strlcpy(c->name, name, sizeof(c->name));
828 c->length = strlen(c->name);
829 c->instance = instance;
830
831 node = rbtree_insert_node(xlat_root, c);
832 if (!node) {
833 talloc_free(c);
834 return -1;
835 }
836
837 /*
838 * Ensure that the data is deleted when the node is
839 * deleted.
840 *
841 * @todo: Maybe this should be the other way around...
842 * when a thing IN the tree is deleted, it's automatically
843 * removed from the tree. But for now, this works.
844 */
845 (void) talloc_steal(node, c);
846 return 0;
847 }
848
849 /** Unregister an xlat function
850 *
851 * We can only have one function to call per name, so the passing of "func"
852 * here is extraneous.
853 *
854 * @param[in] name xlat to unregister.
855 * @param[in] func unused.
856 * @param[in] instance data.
857 */
xlat_unregister(char const * name,UNUSED xlat_func_t func,void * instance)858 void xlat_unregister(char const *name, UNUSED xlat_func_t func, void *instance)
859 {
860 xlat_t *c;
861 xlat_t my_xlat;
862
863 if (!name || !xlat_root) return;
864
865 strlcpy(my_xlat.name, name, sizeof(my_xlat.name));
866 my_xlat.length = strlen(my_xlat.name);
867
868 c = rbtree_finddata(xlat_root, &my_xlat);
869 if (!c) return;
870
871 if (c->instance != instance) return;
872
873 rbtree_deletebydata(xlat_root, c);
874 }
875
xlat_unregister_callback(void * instance,void * data)876 static int xlat_unregister_callback(void *instance, void *data)
877 {
878 xlat_t *c = (xlat_t *) data;
879
880 if (c->instance != instance) return 0; /* keep walking */
881
882 return 2; /* delete it */
883 }
884
xlat_unregister_module(void * instance)885 void xlat_unregister_module(void *instance)
886 {
887 rbtree_walk(xlat_root, RBTREE_DELETE_ORDER, xlat_unregister_callback, instance);
888 }
889
890 /*
891 * Internal redundant handler for xlats
892 */
893 typedef enum xlat_redundant_type_t {
894 XLAT_INVALID = 0,
895 XLAT_REDUNDANT,
896 XLAT_LOAD_BALANCE,
897 XLAT_REDUNDANT_LOAD_BALANCE,
898 } xlat_redundant_type_t;
899
900 typedef struct xlat_redundant_t {
901 xlat_redundant_type_t type;
902 uint32_t count;
903 CONF_SECTION *cs;
904 } xlat_redundant_t;
905
906
xlat_redundant(void * instance,REQUEST * request,char const * fmt,char * out,size_t outlen)907 static ssize_t xlat_redundant(void *instance, REQUEST *request,
908 char const *fmt, char *out, size_t outlen)
909 {
910 xlat_redundant_t *xr = instance;
911 CONF_ITEM *ci;
912 char const *name;
913 xlat_t *xlat;
914
915 rad_assert(xr->type == XLAT_REDUNDANT);
916
917 /*
918 * Pick the first xlat which succeeds
919 */
920 for (ci = cf_item_find_next(xr->cs, NULL);
921 ci != NULL;
922 ci = cf_item_find_next(xr->cs, ci)) {
923 ssize_t rcode;
924
925 if (!cf_item_is_pair(ci)) continue;
926
927 name = cf_pair_attr(cf_item_to_pair(ci));
928 rad_assert(name != NULL);
929
930 xlat = xlat_find(name);
931 if (!xlat) continue;
932
933 rcode = xlat->func(xlat->instance, request, fmt, out, outlen);
934 if (rcode <= 0) continue;
935 return rcode;
936 }
937
938 /*
939 * Everything failed. Oh well.
940 */
941 *out = 0;
942 return 0;
943 }
944
945
xlat_load_balance(void * instance,REQUEST * request,char const * fmt,char * out,size_t outlen)946 static ssize_t xlat_load_balance(void *instance, REQUEST *request,
947 char const *fmt, char *out, size_t outlen)
948 {
949 uint32_t count = 0;
950 xlat_redundant_t *xr = instance;
951 CONF_ITEM *ci;
952 CONF_ITEM *found = NULL;
953 char const *name;
954 xlat_t *xlat;
955
956 /*
957 * Choose a child at random.
958 */
959 for (ci = cf_item_find_next(xr->cs, NULL);
960 ci != NULL;
961 ci = cf_item_find_next(xr->cs, ci)) {
962 if (!cf_item_is_pair(ci)) continue;
963 count++;
964
965 /*
966 * Replace the previously found one with a random
967 * new one.
968 */
969 if ((count * (fr_rand() & 0xffff)) < (uint32_t) 0x10000) {
970 found = ci;
971 }
972 }
973
974 /*
975 * Plain load balancing: do one child, and only one child.
976 */
977 if (xr->type == XLAT_LOAD_BALANCE) {
978 name = cf_pair_attr(cf_item_to_pair(found));
979 rad_assert(name != NULL);
980
981 xlat = xlat_find(name);
982 if (!xlat) return -1;
983
984 return xlat->func(xlat->instance, request, fmt, out, outlen);
985 }
986
987 rad_assert(xr->type == XLAT_REDUNDANT_LOAD_BALANCE);
988
989 /*
990 * Try the random one we found. If it fails, keep going
991 * through the rest of the children.
992 */
993 ci = found;
994 do {
995 name = cf_pair_attr(cf_item_to_pair(ci));
996 rad_assert(name != NULL);
997
998 xlat = xlat_find(name);
999 if (xlat) {
1000 ssize_t rcode;
1001
1002 rcode = xlat->func(xlat->instance, request, fmt, out, outlen);
1003 if (rcode > 0) return rcode;
1004 }
1005
1006 /*
1007 * Go to the next one, wrapping around at the end.
1008 */
1009 ci = cf_item_find_next(xr->cs, ci);
1010 if (!ci) ci = cf_item_find_next(xr->cs, NULL);
1011 } while (ci != found);
1012
1013 return -1;
1014 }
1015
1016
xlat_register_redundant(CONF_SECTION * cs)1017 bool xlat_register_redundant(CONF_SECTION *cs)
1018 {
1019 char const *name1, *name2;
1020 xlat_redundant_t *xr;
1021
1022 name1 = cf_section_name1(cs);
1023 name2 = cf_section_name2(cs);
1024
1025 if (!name2) return false;
1026
1027 if (xlat_find(name2)) {
1028 cf_log_err_cs(cs, "An expansion is already registered for this name");
1029 return false;
1030 }
1031
1032 xr = talloc_zero(cs, xlat_redundant_t);
1033 if (!xr) return false;
1034
1035 if (strcmp(name1, "redundant") == 0) {
1036 xr->type = XLAT_REDUNDANT;
1037
1038 } else if (strcmp(name1, "redundant-load-balance") == 0) {
1039 xr->type = XLAT_REDUNDANT_LOAD_BALANCE;
1040
1041 } else if (strcmp(name1, "load-balance") == 0) {
1042 xr->type = XLAT_LOAD_BALANCE;
1043
1044 } else {
1045 return false;
1046 }
1047
1048 xr->cs = cs;
1049
1050 /*
1051 * Get the number of children for load balancing.
1052 */
1053 if (xr->type == XLAT_REDUNDANT) {
1054 if (xlat_register(name2, xlat_redundant, NULL, xr) < 0) {
1055 talloc_free(xr);
1056 return false;
1057 }
1058
1059 } else {
1060 CONF_ITEM *ci;
1061
1062 for (ci = cf_item_find_next(cs, NULL);
1063 ci != NULL;
1064 ci = cf_item_find_next(cs, ci)) {
1065 if (!cf_item_is_pair(ci)) continue;
1066
1067 if (!xlat_find(cf_pair_attr(cf_item_to_pair(ci)))) {
1068 talloc_free(xr);
1069 return false;
1070 }
1071
1072 xr->count++;
1073 }
1074
1075 if (xlat_register(name2, xlat_load_balance, NULL, xr) < 0) {
1076 talloc_free(xr);
1077 return false;
1078 }
1079 }
1080
1081 return true;
1082 }
1083
1084
1085 /** Crappy temporary function to add attribute ref support to xlats
1086 *
1087 * This needs to die, and hopefully will die, when xlat functions accept
1088 * xlat node structures.
1089 *
1090 * Provides either a pointer to a buffer which contains the value of the reference VALUE_PAIR
1091 * in an architecture independent format. Or a pointer to the start of the fmt string.
1092 *
1093 * The pointer is only guaranteed to be valid between calls to xlat_fmt_to_ref,
1094 * and so long as the source VALUE_PAIR is not freed.
1095 *
1096 * @param out where to write a pointer to the buffer to the data the xlat function needs to work on.
1097 * @param request current request.
1098 * @param fmt string.
1099 * @returns the length of the data or -1 on error.
1100 */
xlat_fmt_to_ref(uint8_t const ** out,REQUEST * request,char const * fmt)1101 ssize_t xlat_fmt_to_ref(uint8_t const **out, REQUEST *request, char const *fmt)
1102 {
1103 VALUE_PAIR *vp;
1104
1105 while (isspace((int) *fmt)) fmt++;
1106
1107 if (fmt[0] == '&') {
1108 if ((radius_get_vp(&vp, request, fmt) < 0) || !vp) {
1109 *out = NULL;
1110 return -1;
1111 }
1112
1113 return rad_vp2data(out, vp);
1114 }
1115
1116 *out = (uint8_t const *)fmt;
1117 return strlen(fmt);
1118 }
1119
1120 /** De-register all xlat functions, used mainly for debugging.
1121 *
1122 */
xlat_free(void)1123 void xlat_free(void)
1124 {
1125 rbtree_free(xlat_root);
1126 }
1127
1128 #ifdef DEBUG_XLAT
1129 # define XLAT_DEBUG DEBUG3
1130 #else
1131 # define XLAT_DEBUG(...)
1132 #endif
1133
1134 static ssize_t xlat_tokenize_expansion(TALLOC_CTX *ctx, char *fmt, xlat_exp_t **head,
1135 char const **error);
1136 static ssize_t xlat_tokenize_literal(TALLOC_CTX *ctx, char *fmt, xlat_exp_t **head,
1137 bool brace, char const **error);
1138 static size_t xlat_process(char **out, REQUEST *request, xlat_exp_t const * const head,
1139 xlat_escape_t escape, void *escape_ctx);
1140
xlat_tokenize_alternation(TALLOC_CTX * ctx,char * fmt,xlat_exp_t ** head,char const ** error)1141 static ssize_t xlat_tokenize_alternation(TALLOC_CTX *ctx, char *fmt, xlat_exp_t **head,
1142 char const **error)
1143 {
1144 ssize_t slen;
1145 char *p;
1146 xlat_exp_t *node;
1147
1148 rad_assert(fmt[0] == '%');
1149 rad_assert(fmt[1] == '{');
1150 rad_assert(fmt[2] == '%');
1151 rad_assert(fmt[3] == '{');
1152
1153 XLAT_DEBUG("ALTERNATE <-- %s", fmt);
1154
1155 node = talloc_zero(ctx, xlat_exp_t);
1156 node->type = XLAT_ALTERNATE;
1157
1158 p = fmt + 2;
1159 slen = xlat_tokenize_expansion(node, p, &node->child, error);
1160 if (slen <= 0) {
1161 talloc_free(node);
1162 return slen - (p - fmt);
1163 }
1164 p += slen;
1165
1166 if (p[0] != ':') {
1167 talloc_free(node);
1168 *error = "Expected ':' after first expansion";
1169 return -(p - fmt);
1170 }
1171 p++;
1172
1173 if (p[0] != '-') {
1174 talloc_free(node);
1175 *error = "Expected '-' after ':'";
1176 return -(p - fmt);
1177 }
1178 p++;
1179
1180 /*
1181 * Allow the RHS to be empty as a special case.
1182 */
1183 if (*p == '}') {
1184 /*
1185 * Hack up an empty string.
1186 */
1187 node->alternate = talloc_zero(node, xlat_exp_t);
1188 node->alternate->type = XLAT_LITERAL;
1189 node->alternate->fmt = talloc_typed_strdup(node->alternate, "");
1190 *(p++) = '\0';
1191
1192 } else {
1193 slen = xlat_tokenize_literal(node, p, &node->alternate, true, error);
1194 if (slen <= 0) {
1195 talloc_free(node);
1196 return slen - (p - fmt);
1197 }
1198
1199 if (!node->alternate) {
1200 talloc_free(node);
1201 *error = "Empty expansion is invalid";
1202 return -(p - fmt);
1203 }
1204 p += slen;
1205 }
1206
1207 *head = node;
1208 return p - fmt;
1209 }
1210
xlat_tokenize_expansion(TALLOC_CTX * ctx,char * fmt,xlat_exp_t ** head,char const ** error)1211 static ssize_t xlat_tokenize_expansion(TALLOC_CTX *ctx, char *fmt, xlat_exp_t **head,
1212 char const **error)
1213 {
1214 ssize_t slen;
1215 char *p, *q;
1216 xlat_exp_t *node;
1217 long num;
1218
1219 rad_assert(fmt[0] == '%');
1220 rad_assert(fmt[1] == '{');
1221
1222 /*
1223 * %{%{...}:-bar}
1224 */
1225 if ((fmt[2] == '%') && (fmt[3] == '{')) return xlat_tokenize_alternation(ctx, fmt, head, error);
1226
1227 XLAT_DEBUG("EXPANSION <-- %s", fmt);
1228 node = talloc_zero(ctx, xlat_exp_t);
1229 node->fmt = fmt + 2;
1230 node->len = 0;
1231
1232 #ifdef HAVE_REGEX
1233 /*
1234 * Handle regex's specially.
1235 */
1236 p = fmt + 2;
1237 num = strtol(p, &q, 10);
1238 if (p != q && (*q == '}')) {
1239 XLAT_DEBUG("REGEX <-- %s", fmt);
1240 *q = '\0';
1241
1242 if ((num > REQUEST_MAX_REGEX) || (num < 0)) {
1243 talloc_free(node);
1244 *error = "Invalid regex reference. Must be in range 0-" STRINGIFY(REQUEST_MAX_REGEX);
1245 return -2;
1246 }
1247 node->attr.tmpl_num = num;
1248
1249 node->type = XLAT_REGEX;
1250 *head = node;
1251
1252 return (q - fmt) + 1;
1253 }
1254 #endif /* HAVE_REGEX */
1255
1256 /*
1257 * %{Attr-Name}
1258 * %{Attr-Name[#]}
1259 * %{Tunnel-Password:1}
1260 * %{Tunnel-Password:1[#]}
1261 * %{request:Attr-Name}
1262 * %{request:Tunnel-Password:1}
1263 * %{request:Tunnel-Password:1[#]}
1264 * %{mod:foo}
1265 */
1266
1267 /*
1268 * This is for efficiency, so we don't search for an xlat,
1269 * when what's being referenced is obviously an attribute.
1270 */
1271 p = fmt + 2;
1272 for (q = p; *q != '\0'; q++) {
1273 if (*q == ':') break;
1274
1275 if (isspace((int) *q)) break;
1276
1277 if (*q == '[') continue;
1278
1279 if (*q == '}') break;
1280 }
1281
1282 /*
1283 * Check for empty expressions %{}
1284 */
1285 if ((*q == '}') && (q == p)) {
1286 talloc_free(node);
1287 *error = "Empty expression is invalid";
1288 return -(p - fmt);
1289 }
1290
1291 /*
1292 * Might be a module name reference.
1293 *
1294 * If it's not, it's an attribute or parse error.
1295 */
1296 if (*q == ':') {
1297 *q = '\0';
1298 node->xlat = xlat_find(node->fmt);
1299 if (node->xlat) {
1300 /*
1301 * %{mod:foo}
1302 */
1303 node->type = XLAT_MODULE;
1304
1305 p = q + 1;
1306 XLAT_DEBUG("MOD <-- %s ... %s", node->fmt, p);
1307
1308 slen = xlat_tokenize_literal(node, p, &node->child, true, error);
1309 if (slen < 0) {
1310 talloc_free(node);
1311 return slen - (p - fmt);
1312 }
1313 p += slen;
1314
1315 *head = node;
1316 rad_assert(node->next == NULL);
1317
1318 return p - fmt;
1319 }
1320 *q = ':'; /* Avoids a strdup */
1321 }
1322
1323 /*
1324 * The first token ends with:
1325 * - '[' - Which is an attribute index, so it must be an attribute.
1326 * - '}' - The end of the expansion, which means it was a bareword.
1327 */
1328 slen = tmpl_from_attr_substr(&node->attr, p, REQUEST_CURRENT, PAIR_LIST_REQUEST, true, true);
1329 if (slen <= 0) {
1330 /*
1331 * If the parse error occurred before the ':'
1332 * then the error is changed to 'Unknown module',
1333 * as it was more likely to be a bad module name,
1334 * than a request qualifier.
1335 */
1336 if ((*q == ':') && ((p + (slen * -1)) < q)) {
1337 *error = "Unknown module";
1338 } else {
1339 *error = fr_strerror();
1340 }
1341
1342 talloc_free(node);
1343 return slen - (p - fmt);
1344 }
1345
1346 /*
1347 * Might be a virtual XLAT attribute
1348 */
1349 if (node->attr.type == TMPL_TYPE_ATTR_UNDEFINED) {
1350 node->xlat = xlat_find(node->attr.tmpl_unknown_name);
1351 if (node->xlat && node->xlat->instance && !node->xlat->internal) {
1352 talloc_free(node);
1353 *error = "Missing content in expansion";
1354 return -(p - fmt) - slen;
1355 }
1356
1357 if (node->xlat) {
1358 node->type = XLAT_VIRTUAL;
1359 node->fmt = node->attr.tmpl_unknown_name;
1360
1361 XLAT_DEBUG("VIRTUAL <-- %s", node->fmt);
1362 *head = node;
1363 rad_assert(node->next == NULL);
1364 q++;
1365 return q - fmt;
1366 }
1367
1368 talloc_free(node);
1369 *error = "Unknown attribute";
1370 return -(p - fmt);
1371 }
1372
1373 /*
1374 * Might be a list, too...
1375 */
1376 node->type = XLAT_ATTRIBUTE;
1377 p += slen;
1378
1379 if (*p != '}') {
1380 talloc_free(node);
1381 *error = "No matching closing brace";
1382 return -1; /* second character of format string */
1383 }
1384 *p++ = '\0';
1385 *head = node;
1386 rad_assert(node->next == NULL);
1387
1388 return p - fmt;
1389 }
1390
1391
xlat_tokenize_literal(TALLOC_CTX * ctx,char * fmt,xlat_exp_t ** head,bool brace,char const ** error)1392 static ssize_t xlat_tokenize_literal(TALLOC_CTX *ctx, char *fmt, xlat_exp_t **head,
1393 bool brace, char const **error)
1394 {
1395 char *p;
1396 xlat_exp_t *node;
1397
1398 if (!*fmt) return 0;
1399
1400 XLAT_DEBUG("LITERAL <-- %s", fmt);
1401
1402 node = talloc_zero(ctx, xlat_exp_t);
1403 node->fmt = fmt;
1404 node->len = 0;
1405 node->type = XLAT_LITERAL;
1406
1407 p = fmt;
1408
1409 while (*p) {
1410 if (*p == '\\') {
1411 if (!p[1]) {
1412 talloc_free(node);
1413 *error = "Invalid escape at end of string";
1414 return -(p - fmt);
1415 }
1416
1417 p += 2;
1418 node->len += 2;
1419 continue;
1420 }
1421
1422 /*
1423 * Process the expansion.
1424 */
1425 if ((p[0] == '%') && (p[1] == '{')) {
1426 ssize_t slen;
1427
1428 XLAT_DEBUG("EXPANSION-2 <-- %s", node->fmt);
1429
1430 slen = xlat_tokenize_expansion(node, p, &node->next, error);
1431 if (slen <= 0) {
1432 talloc_free(node);
1433 return slen - (p - fmt);
1434 }
1435 *p = '\0'; /* end the literal */
1436 p += slen;
1437
1438 rad_assert(node->next != NULL);
1439
1440 /*
1441 * Short-circuit the recursive call.
1442 * This saves another function call and
1443 * memory allocation.
1444 */
1445 if (!*p) break;
1446
1447 /*
1448 * "foo %{User-Name} bar"
1449 * LITERAL "foo "
1450 * EXPANSION User-Name
1451 * LITERAL " bar"
1452 */
1453 slen = xlat_tokenize_literal(node->next, p, &(node->next->next), brace, error);
1454 rad_assert(slen != 0);
1455 if (slen < 0) {
1456 talloc_free(node);
1457 return slen - (p - fmt);
1458 }
1459
1460 brace = false; /* it was found above, or else the above code errored out */
1461 p += slen;
1462 break; /* stop processing the string */
1463 }
1464
1465 /*
1466 * Check for valid single-character expansions.
1467 */
1468 if (p[0] == '%') {
1469 ssize_t slen;
1470 xlat_exp_t *next;
1471
1472 if (!p[1] || !strchr("%}cdelmntCDGHIMSTYv", p[1])) {
1473 talloc_free(node);
1474 *error = "Invalid variable expansion";
1475 p++;
1476 return - (p - fmt);
1477 }
1478
1479 next = talloc_zero(node, xlat_exp_t);
1480 next->len = 1;
1481
1482 switch (p[1]) {
1483 case '%':
1484 case '}':
1485 next->fmt = talloc_strndup(next, p + 1, 1);
1486
1487 XLAT_DEBUG("LITERAL-ESCAPED <-- %s", next->fmt);
1488 next->type = XLAT_LITERAL;
1489 break;
1490
1491 default:
1492 next->fmt = p + 1;
1493
1494 XLAT_DEBUG("PERCENT <-- %c", *next->fmt);
1495 next->type = XLAT_PERCENT;
1496 break;
1497 }
1498
1499 node->next = next;
1500 *p = '\0';
1501 p += 2;
1502
1503 if (!*p) break;
1504
1505 /*
1506 * And recurse.
1507 */
1508 slen = xlat_tokenize_literal(node->next, p, &(node->next->next), brace, error);
1509 rad_assert(slen != 0);
1510 if (slen < 0) {
1511 talloc_free(node);
1512 return slen - (p - fmt);
1513 }
1514
1515 brace = false; /* it was found above, or else the above code errored out */
1516 p += slen;
1517 break; /* stop processing the string */
1518 }
1519
1520 /*
1521 * If required, eat the brace.
1522 */
1523 if (brace && (*p == '}')) {
1524 brace = false;
1525 *p = '\0';
1526 p++;
1527 break;
1528 }
1529
1530 p++;
1531 node->len++;
1532 }
1533
1534 /*
1535 * We were told to look for a brace, but we ran off of
1536 * the end of the string before we found one.
1537 */
1538 if (brace) {
1539 *error = "Missing closing brace at end of string";
1540 return -(p - fmt);
1541 }
1542
1543 /*
1544 * Squash zero-width literals
1545 */
1546 if (node->len > 0) {
1547 *head = node;
1548
1549 } else {
1550 (void) talloc_steal(ctx, node->next);
1551 *head = node->next;
1552 talloc_free(node);
1553 }
1554
1555 return p - fmt;
1556 }
1557
1558
1559 static char const xlat_tabs[] = " ";
1560
xlat_tokenize_debug(xlat_exp_t const * node,int lvl)1561 static void xlat_tokenize_debug(xlat_exp_t const *node, int lvl)
1562 {
1563 rad_assert(node != NULL);
1564
1565 if (lvl >= (int) sizeof(xlat_tabs)) lvl = sizeof(xlat_tabs);
1566
1567 while (node) {
1568 switch (node->type) {
1569 case XLAT_LITERAL:
1570 DEBUG("%.*sliteral --> %s", lvl, xlat_tabs, node->fmt);
1571 break;
1572
1573 case XLAT_PERCENT:
1574 DEBUG("%.*spercent --> %c", lvl, xlat_tabs, node->fmt[0]);
1575 break;
1576
1577 case XLAT_ATTRIBUTE:
1578 rad_assert(node->attr.tmpl_da != NULL);
1579 DEBUG("%.*sattribute --> %s", lvl, xlat_tabs, node->attr.tmpl_da->name);
1580 rad_assert(node->child == NULL);
1581 if ((node->attr.tmpl_tag != TAG_ANY) || (node->attr.tmpl_num != NUM_ANY)) {
1582 DEBUG("%.*s{", lvl, xlat_tabs);
1583
1584 DEBUG("%.*sref %d", lvl + 1, xlat_tabs, node->attr.tmpl_request);
1585 DEBUG("%.*slist %d", lvl + 1, xlat_tabs, node->attr.tmpl_list);
1586
1587 if (node->attr.tmpl_tag != TAG_ANY) {
1588 DEBUG("%.*stag %d", lvl + 1, xlat_tabs, node->attr.tmpl_tag);
1589 }
1590 if (node->attr.tmpl_num != NUM_ANY) {
1591 if (node->attr.tmpl_num == NUM_COUNT) {
1592 DEBUG("%.*s[#]", lvl + 1, xlat_tabs);
1593 } else if (node->attr.tmpl_num == NUM_ALL) {
1594 DEBUG("%.*s[*]", lvl + 1, xlat_tabs);
1595 } else {
1596 DEBUG("%.*s[%d]", lvl + 1, xlat_tabs, node->attr.tmpl_num);
1597 }
1598 }
1599
1600 DEBUG("%.*s}", lvl, xlat_tabs);
1601 }
1602 break;
1603
1604 case XLAT_VIRTUAL:
1605 rad_assert(node->fmt != NULL);
1606 DEBUG("%.*svirtual --> %s", lvl, xlat_tabs, node->fmt);
1607 break;
1608
1609 case XLAT_MODULE:
1610 rad_assert(node->xlat != NULL);
1611 DEBUG("%.*sxlat --> %s", lvl, xlat_tabs, node->xlat->name);
1612 if (node->child) {
1613 DEBUG("%.*s{", lvl, xlat_tabs);
1614 xlat_tokenize_debug(node->child, lvl + 1);
1615 DEBUG("%.*s}", lvl, xlat_tabs);
1616 }
1617 break;
1618
1619 #ifdef HAVE_REGEX
1620 case XLAT_REGEX:
1621 DEBUG("%.*sregex-var --> %d", lvl, xlat_tabs, node->attr.tmpl_num);
1622 break;
1623 #endif
1624
1625 case XLAT_ALTERNATE:
1626 DEBUG("%.*sXLAT-IF {", lvl, xlat_tabs);
1627 xlat_tokenize_debug(node->child, lvl + 1);
1628 DEBUG("%.*s}", lvl, xlat_tabs);
1629 DEBUG("%.*sXLAT-ELSE {", lvl, xlat_tabs);
1630 xlat_tokenize_debug(node->alternate, lvl + 1);
1631 DEBUG("%.*s}", lvl, xlat_tabs);
1632 break;
1633 }
1634 node = node->next;
1635 }
1636 }
1637
xlat_sprint(char * buffer,size_t bufsize,xlat_exp_t const * node)1638 size_t xlat_sprint(char *buffer, size_t bufsize, xlat_exp_t const *node)
1639 {
1640 size_t len;
1641 char *p, *end;
1642
1643 if (!node) {
1644 *buffer = '\0';
1645 return 0;
1646 }
1647
1648 p = buffer;
1649 end = buffer + bufsize;
1650
1651 while (node) {
1652 switch (node->type) {
1653 case XLAT_LITERAL:
1654 strlcpy(p, node->fmt, end - p);
1655 p += strlen(p);
1656 break;
1657
1658 case XLAT_PERCENT:
1659 p[0] = '%';
1660 p[1] = node->fmt[0];
1661 p += 2;
1662 break;
1663
1664 case XLAT_ATTRIBUTE:
1665 *(p++) = '%';
1666 *(p++) = '{';
1667
1668 /*
1669 * The node MAY NOT be an attribute. It
1670 * may be a list.
1671 */
1672 tmpl_prints(p, end - p, &node->attr, NULL);
1673 if (*p == '&') {
1674 memmove(p, p + 1, strlen(p + 1) + 1);
1675 }
1676 p += strlen(p);
1677 *(p++) = '}';
1678 break;
1679 #ifdef HAVE_REGEX
1680 case XLAT_REGEX:
1681 snprintf(p, end - p, "%%{%i}", node->attr.tmpl_num);
1682 p += strlen(p);
1683 break;
1684 #endif
1685 case XLAT_VIRTUAL:
1686 *(p++) = '%';
1687 *(p++) = '{';
1688 strlcpy(p, node->fmt, end - p);
1689 p += strlen(p);
1690 *(p++) = '}';
1691 break;
1692
1693 case XLAT_MODULE:
1694 *(p++) = '%';
1695 *(p++) = '{';
1696 strlcpy(p, node->xlat->name, end - p);
1697 p += strlen(p);
1698 *(p++) = ':';
1699 rad_assert(node->child != NULL);
1700 len = xlat_sprint(p, end - p, node->child);
1701 p += len;
1702 *(p++) = '}';
1703 break;
1704
1705 case XLAT_ALTERNATE:
1706 *(p++) = '%';
1707 *(p++) = '{';
1708
1709 len = xlat_sprint(p, end - p, node->child);
1710 p += len;
1711
1712 *(p++) = ':';
1713 *(p++) = '-';
1714
1715 len = xlat_sprint(p, end - p, node->alternate);
1716 p += len;
1717
1718 *(p++) = '}';
1719 break;
1720 }
1721
1722
1723 if (p == end) break;
1724
1725 node = node->next;
1726 }
1727
1728 *p = '\0';
1729
1730 return p - buffer;
1731 }
1732
xlat_tokenize(TALLOC_CTX * ctx,char * fmt,xlat_exp_t ** head,char const ** error)1733 ssize_t xlat_tokenize(TALLOC_CTX *ctx, char *fmt, xlat_exp_t **head,
1734 char const **error)
1735 {
1736 return xlat_tokenize_literal(ctx, fmt, head, false, error);
1737 }
1738
1739
1740 /** Tokenize an xlat expansion
1741 *
1742 * @param[in] request the input request. Memory will be attached here.
1743 * @param[in] fmt the format string to expand
1744 * @param[out] head the head of the xlat list / tree structure.
1745 */
xlat_tokenize_request(REQUEST * request,char const * fmt,xlat_exp_t ** head)1746 static ssize_t xlat_tokenize_request(REQUEST *request, char const *fmt, xlat_exp_t **head)
1747 {
1748 ssize_t slen;
1749 char *tokens;
1750 char const *error = NULL;
1751
1752 *head = NULL;
1753
1754 /*
1755 * Copy the original format string to a buffer so that
1756 * the later functions can mangle it in-place, which is
1757 * much faster.
1758 */
1759 tokens = talloc_typed_strdup(request, fmt);
1760 if (!tokens) {
1761 error = "Out of memory";
1762 return -1;
1763 }
1764
1765 slen = xlat_tokenize_literal(request, tokens, head, false, &error);
1766
1767 /*
1768 * Zero length expansion, return a zero length node.
1769 */
1770 if (slen == 0) {
1771 *head = talloc_zero(request, xlat_exp_t);
1772 }
1773
1774 /*
1775 * Output something like:
1776 *
1777 * "format string"
1778 * " ^ error was here"
1779 */
1780 if (slen < 0) {
1781 talloc_free(tokens);
1782
1783 if (!error) error = "Unknown error";
1784
1785 REMARKER(fmt, -slen, error);
1786 return slen;
1787 }
1788
1789 if (*head && (rad_debug_lvl > 2)) {
1790 DEBUG("%s", fmt);
1791 DEBUG("Parsed xlat tree:");
1792 xlat_tokenize_debug(*head, 0);
1793 }
1794
1795 /*
1796 * All of the nodes point to offsets in the "tokens"
1797 * string. Let's ensure that free'ing head will free
1798 * "tokens", too.
1799 */
1800 (void) talloc_steal(*head, tokens);
1801
1802 return slen;
1803 }
1804
1805
xlat_getvp(TALLOC_CTX * ctx,REQUEST * request,vp_tmpl_t const * vpt,bool escape,bool return_null)1806 static char *xlat_getvp(TALLOC_CTX *ctx, REQUEST *request, vp_tmpl_t const *vpt,
1807 bool escape, bool return_null)
1808 {
1809 VALUE_PAIR *vp = NULL, *virtual = NULL;
1810 RADIUS_PACKET *packet = NULL;
1811 DICT_VALUE *dv;
1812 char *ret = NULL;
1813
1814 vp_cursor_t cursor;
1815 char quote = escape ? '"' : '\0';
1816
1817 rad_assert((vpt->type == TMPL_TYPE_ATTR) || (vpt->type == TMPL_TYPE_LIST));
1818
1819 /*
1820 * We only support count and concatenate operations on lists.
1821 */
1822 if (vpt->type == TMPL_TYPE_LIST) {
1823 vp = tmpl_cursor_init(NULL, &cursor, request, vpt);
1824 goto do_print;
1825 }
1826
1827 /*
1828 * See if we're dealing with an attribute in the request
1829 *
1830 * This allows users to manipulate virtual attributes as if
1831 * they were real ones.
1832 */
1833 vp = tmpl_cursor_init(NULL, &cursor, request, vpt);
1834 if (vp) goto do_print;
1835
1836 /*
1837 * We didn't find the VP in a list.
1838 * If it's not a virtual one, and we're not meant to
1839 * be counting it, return.
1840 */
1841 if (!vpt->tmpl_da->flags.virtual) {
1842 if (vpt->tmpl_num == NUM_COUNT) goto do_print;
1843 return NULL;
1844 }
1845
1846 /*
1847 * Switch out the request to the one specified by the template
1848 */
1849 if (radius_request(&request, vpt->tmpl_request) < 0) return NULL;
1850
1851 /*
1852 * Some non-packet expansions
1853 */
1854 switch (vpt->tmpl_da->attr) {
1855 default:
1856 break; /* ignore them */
1857
1858 case PW_CLIENT_SHORTNAME:
1859 if (vpt->tmpl_num == NUM_COUNT) goto count_virtual;
1860 if (request->client && request->client->shortname) {
1861 return talloc_typed_strdup(ctx, request->client->shortname);
1862 }
1863 return talloc_typed_strdup(ctx, "<UNKNOWN-CLIENT>");
1864
1865 case PW_REQUEST_PROCESSING_STAGE:
1866 if (vpt->tmpl_num == NUM_COUNT) goto count_virtual;
1867 if (request->component) {
1868 return talloc_typed_strdup(ctx, request->component);
1869 }
1870 return talloc_typed_strdup(ctx, "server_core");
1871
1872 case PW_VIRTUAL_SERVER:
1873 if (vpt->tmpl_num == NUM_COUNT) goto count_virtual;
1874 if (!request->server) return NULL;
1875 return talloc_typed_strdup(ctx, request->server);
1876
1877 case PW_MODULE_RETURN_CODE:
1878 if (vpt->tmpl_num == NUM_COUNT) goto count_virtual;
1879 if (!request->rcode) return NULL;
1880 return talloc_typed_strdup(ctx, fr_int2str(modreturn_table, request->rcode, ""));
1881 }
1882
1883 /*
1884 * All of the attributes must now refer to a packet.
1885 * If there's no packet, we can't print any attribute
1886 * referencing it.
1887 */
1888 packet = radius_packet(request, vpt->tmpl_list);
1889 if (!packet) {
1890 if (return_null) return NULL;
1891 return vp_aprints_type(ctx, vpt->tmpl_da->type);
1892 }
1893
1894 vp = NULL;
1895 switch (vpt->tmpl_da->attr) {
1896 default:
1897 break;
1898
1899 case PW_PACKET_TYPE:
1900 dv = dict_valbyattr(PW_PACKET_TYPE, 0, packet->code);
1901 if (dv) return talloc_typed_strdup(ctx, dv->name);
1902 return talloc_typed_asprintf(ctx, "%d", packet->code);
1903
1904 case PW_RESPONSE_PACKET_TYPE:
1905 {
1906 int code = 0;
1907
1908 #ifdef WITH_PROXY
1909 if (request->proxy_reply && (!request->reply || !request->reply->code)) {
1910 code = request->proxy_reply->code;
1911 } else
1912 #endif
1913 if (request->reply) {
1914 code = request->reply->code;
1915 }
1916
1917 if (!code) return NULL;
1918
1919 if (code >= FR_MAX_PACKET_CODE) {
1920 return talloc_typed_asprintf(ctx, "%d", packet->code);
1921 }
1922
1923 return talloc_typed_strdup(ctx, fr_packet_codes[code]);
1924 }
1925
1926 /*
1927 * Virtual attributes which require a temporary VALUE_PAIR
1928 * to be allocated. We can't use stack allocated memory
1929 * because of the talloc checks sprinkled throughout the
1930 * various VP functions.
1931 */
1932 case PW_PACKET_AUTHENTICATION_VECTOR:
1933 virtual = fr_pair_afrom_da(ctx, vpt->tmpl_da);
1934 fr_pair_value_memcpy(virtual, packet->vector, sizeof(packet->vector));
1935 vp = virtual;
1936 break;
1937
1938 case PW_CLIENT_IP_ADDRESS:
1939 case PW_PACKET_SRC_IP_ADDRESS:
1940 if (packet->src_ipaddr.af == AF_INET) {
1941 virtual = fr_pair_afrom_da(ctx, vpt->tmpl_da);
1942 virtual->vp_ipaddr = packet->src_ipaddr.ipaddr.ip4addr.s_addr;
1943 vp = virtual;
1944 }
1945 break;
1946
1947 case PW_PACKET_DST_IP_ADDRESS:
1948 if (packet->dst_ipaddr.af == AF_INET) {
1949 virtual = fr_pair_afrom_da(ctx, vpt->tmpl_da);
1950 virtual->vp_ipaddr = packet->dst_ipaddr.ipaddr.ip4addr.s_addr;
1951 vp = virtual;
1952 }
1953 break;
1954
1955 case PW_PACKET_SRC_IPV6_ADDRESS:
1956 if (packet->src_ipaddr.af == AF_INET6) {
1957 virtual = fr_pair_afrom_da(ctx, vpt->tmpl_da);
1958 memcpy(&virtual->vp_ipv6addr,
1959 &packet->src_ipaddr.ipaddr.ip6addr,
1960 sizeof(packet->src_ipaddr.ipaddr.ip6addr));
1961 vp = virtual;
1962 }
1963 break;
1964
1965 case PW_PACKET_DST_IPV6_ADDRESS:
1966 if (packet->dst_ipaddr.af == AF_INET6) {
1967 virtual = fr_pair_afrom_da(ctx, vpt->tmpl_da);
1968 memcpy(&virtual->vp_ipv6addr,
1969 &packet->dst_ipaddr.ipaddr.ip6addr,
1970 sizeof(packet->dst_ipaddr.ipaddr.ip6addr));
1971 vp = virtual;
1972 }
1973 break;
1974
1975 case PW_PACKET_SRC_PORT:
1976 virtual = fr_pair_afrom_da(ctx, vpt->tmpl_da);
1977 virtual->vp_integer = packet->src_port;
1978 vp = virtual;
1979 break;
1980
1981 case PW_PACKET_DST_PORT:
1982 virtual = fr_pair_afrom_da(ctx, vpt->tmpl_da);
1983 virtual->vp_integer = packet->dst_port;
1984 vp = virtual;
1985 break;
1986 }
1987
1988 /*
1989 * Fake various operations for virtual attributes.
1990 */
1991 if (virtual) {
1992 if (vpt->tmpl_num != NUM_ANY) switch (vpt->tmpl_num) {
1993 /*
1994 * [n] is NULL (we only have [0])
1995 */
1996 default:
1997 goto finish;
1998 /*
1999 * [*] means only one.
2000 */
2001 case NUM_ALL:
2002 break;
2003
2004 /*
2005 * [#] means 1 (as there's only one)
2006 */
2007 case NUM_COUNT:
2008 count_virtual:
2009 ret = talloc_strdup(ctx, "1");
2010 goto finish;
2011
2012 /*
2013 * [0] is fine (get the first instance)
2014 */
2015 case 0:
2016 break;
2017 }
2018 goto print;
2019 }
2020
2021 do_print:
2022 switch (vpt->tmpl_num) {
2023 /*
2024 * Return a count of the VPs.
2025 */
2026 case NUM_COUNT:
2027 {
2028 int count = 0;
2029
2030 for (vp = tmpl_cursor_init(NULL, &cursor, request, vpt);
2031 vp;
2032 vp = tmpl_cursor_next(&cursor, vpt)) count++;
2033
2034 return talloc_typed_asprintf(ctx, "%d", count);
2035 }
2036
2037
2038 /*
2039 * Concatenate all values together,
2040 * separated by commas.
2041 */
2042 case NUM_ALL:
2043 {
2044 char *p, *q;
2045
2046 if (!fr_cursor_current(&cursor)) return NULL;
2047 p = vp_aprints_value(ctx, vp, quote);
2048 if (!p) return NULL;
2049
2050 while ((vp = tmpl_cursor_next(&cursor, vpt)) != NULL) {
2051 q = vp_aprints_value(ctx, vp, quote);
2052 if (!q) return NULL;
2053 p = talloc_strdup_append(p, ",");
2054 p = talloc_strdup_append(p, q);
2055 }
2056
2057 return p;
2058 }
2059
2060 default:
2061 /*
2062 * The cursor was set to the correct
2063 * position above by tmpl_cursor_init.
2064 */
2065 vp = fr_cursor_current(&cursor);
2066 break;
2067 }
2068
2069 if (!vp) {
2070 if (return_null) return NULL;
2071 return vp_aprints_type(ctx, vpt->tmpl_da->type);
2072 }
2073
2074 print:
2075 ret = vp_aprints_value(ctx, vp, quote);
2076
2077 finish:
2078 talloc_free(virtual);
2079 return ret;
2080 }
2081
2082 #ifdef DEBUG_XLAT
2083 static const char xlat_spaces[] = " ";
2084 #endif
2085
xlat_aprint(TALLOC_CTX * ctx,REQUEST * request,xlat_exp_t const * const node,xlat_escape_t escape,void * escape_ctx,UNUSED int lvl)2086 static char *xlat_aprint(TALLOC_CTX *ctx, REQUEST *request, xlat_exp_t const * const node,
2087 xlat_escape_t escape, void *escape_ctx,
2088 #ifndef DEBUG_XLAT
2089 UNUSED
2090 #endif
2091 int lvl)
2092 {
2093 ssize_t rcode;
2094 char *str = NULL, *child;
2095 char const *p;
2096
2097 XLAT_DEBUG("%.*sxlat aprint %d %s", lvl, xlat_spaces, node->type, node->fmt);
2098
2099 switch (node->type) {
2100 /*
2101 * Don't escape this.
2102 */
2103 case XLAT_LITERAL:
2104 XLAT_DEBUG("%.*sxlat_aprint LITERAL", lvl, xlat_spaces);
2105 return talloc_typed_strdup(ctx, node->fmt);
2106
2107 /*
2108 * Do a one-character expansion.
2109 */
2110 case XLAT_PERCENT:
2111 {
2112 char *nl;
2113 size_t freespace = 256;
2114 struct tm ts;
2115 time_t when;
2116 int usec;
2117
2118 XLAT_DEBUG("%.*sxlat_aprint PERCENT", lvl, xlat_spaces);
2119
2120 str = talloc_array(ctx, char, freespace); /* @todo do better allocation */
2121 p = node->fmt;
2122
2123 when = request->timestamp;
2124 usec = 0;
2125 if (request->packet) {
2126 when = request->packet->timestamp.tv_sec;
2127 usec = request->packet->timestamp.tv_usec;
2128 }
2129
2130 switch (*p) {
2131 case '%':
2132 str[0] = '%';
2133 str[1] = '\0';
2134 break;
2135
2136 case 'c': /* current epoch time seconds */
2137 snprintf(str, freespace, "%" PRIu64, (uint64_t) time(NULL));
2138 break;
2139
2140 case 'd': /* request day */
2141 if (!localtime_r(&when, &ts)) goto error;
2142 strftime(str, freespace, "%d", &ts);
2143 break;
2144
2145 case 'e': /* request second */
2146 if (!localtime_r(&when, &ts)) goto error;
2147
2148 snprintf(str, freespace, "%d", ts.tm_sec);
2149 break;
2150
2151 case 'l': /* request timestamp */
2152 snprintf(str, freespace, "%lu",
2153 (unsigned long) when);
2154 break;
2155
2156 case 'm': /* request month */
2157 if (!localtime_r(&when, &ts)) goto error;
2158 strftime(str, freespace, "%m", &ts);
2159 break;
2160
2161 case 'n': /* Request Number*/
2162 snprintf(str, freespace, "%u", request->number);
2163 break;
2164
2165 case 't': /* request timestamp */
2166 CTIME_R(&when, str, freespace);
2167 nl = strchr(str, '\n');
2168 if (nl) *nl = '\0';
2169 break;
2170
2171 case 'C': /* current epoch time microseconds */
2172 {
2173 struct timeval tv;
2174
2175 gettimeofday(&tv, NULL);
2176
2177 snprintf(str, freespace, "%" PRIu64, (uint64_t) tv.tv_usec);
2178 }
2179 break;
2180
2181 case 'D': /* request date */
2182 if (!localtime_r(&when, &ts)) goto error;
2183 strftime(str, freespace, "%Y%m%d", &ts);
2184 break;
2185
2186 case 'G': /* request minute */
2187 if (!localtime_r(&when, &ts)) goto error;
2188 strftime(str, freespace, "%M", &ts);
2189 break;
2190
2191 case 'H': /* request hour */
2192 if (!localtime_r(&when, &ts)) goto error;
2193 strftime(str, freespace, "%H", &ts);
2194 break;
2195
2196 case 'I': /* Request ID */
2197 if (request->packet) {
2198 snprintf(str, freespace, "%i", request->packet->id);
2199 }
2200 break;
2201
2202 case 'M': /* request microsecond component */
2203 snprintf(str, freespace, "%06u", (unsigned int) usec);
2204 break;
2205
2206 case 'S': /* request timestamp in SQL format*/
2207 if (!localtime_r(&when, &ts)) goto error;
2208 strftime(str, freespace, "%Y-%m-%d %H:%M:%S", &ts);
2209 break;
2210
2211 case 'T': /* request timestamp */
2212 if (!localtime_r(&when, &ts)) goto error;
2213 nl = str + strftime(str, freespace, "%Y-%m-%d-%H.%M.%S", &ts);
2214 rad_assert(((str + freespace) - nl) >= 8);
2215 snprintf(nl, (str + freespace) - nl, ".%06d", usec);
2216 break;
2217
2218 case 'Y': /* request year */
2219 if (!localtime_r(&when, &ts)) {
2220 error:
2221 REDEBUG("Failed converting packet timestamp to localtime: %s", fr_syserror(errno));
2222 talloc_free(str);
2223 return NULL;
2224 }
2225 strftime(str, freespace, "%Y", &ts);
2226 break;
2227
2228 case 'v': /* Version of code */
2229 RWDEBUG("%%v is deprecated and will be removed. Use ${version.freeradius-server}");
2230 snprintf(str, freespace, "%s", radiusd_version_short);
2231 break;
2232
2233 default:
2234 rad_assert(0 == 1);
2235 break;
2236 }
2237 }
2238 break;
2239
2240 case XLAT_ATTRIBUTE:
2241 XLAT_DEBUG("%.*sxlat_aprint ATTRIBUTE", lvl, xlat_spaces);
2242
2243 /*
2244 * Some attributes are virtual <sigh>
2245 */
2246 str = xlat_getvp(ctx, request, &node->attr, escape ? false : true, true);
2247 if (str) {
2248 XLAT_DEBUG("%.*sEXPAND attr %s", lvl, xlat_spaces, node->attr.tmpl_da->name);
2249 XLAT_DEBUG("%.*s ---> %s", lvl ,xlat_spaces, str);
2250 }
2251 break;
2252
2253 case XLAT_VIRTUAL:
2254 XLAT_DEBUG("xlat_aprint VIRTUAL");
2255 str = talloc_array(ctx, char, 2048); /* FIXME: have the module call talloc_typed_asprintf */
2256 rcode = node->xlat->func(node->xlat->instance, request, NULL, str, 2048);
2257 if (rcode < 0) {
2258 talloc_free(str);
2259 return NULL;
2260 }
2261 RDEBUG2("EXPAND %s", node->xlat->name);
2262 RDEBUG2(" --> %s", str);
2263
2264 /*
2265 * Resize the buffer to the correct size.
2266 */
2267 if (rcode == 0) {
2268 talloc_free(str);
2269 str = talloc_strdup(ctx, "");
2270 } else if (rcode < 2047) {
2271 child = talloc_memdup(ctx, str, rcode + 1);
2272 talloc_free(str);
2273 str = child;
2274 }
2275 break;
2276
2277 case XLAT_MODULE:
2278 XLAT_DEBUG("xlat_aprint MODULE");
2279
2280 if (node->child) {
2281 if (xlat_process(&child, request, node->child, node->xlat->escape, node->xlat->instance) == 0) {
2282 return NULL;
2283 }
2284
2285 XLAT_DEBUG("%.*sEXPAND mod %s %s", lvl, xlat_spaces, node->fmt, node->child->fmt);
2286 } else {
2287 XLAT_DEBUG("%.*sEXPAND mod %s", lvl, xlat_spaces, node->fmt);
2288 child = talloc_typed_strdup(ctx, "");
2289 }
2290
2291 XLAT_DEBUG("%.*s ---> %s", lvl, xlat_spaces, child);
2292
2293 /*
2294 * Smash \n --> CR.
2295 *
2296 * The OUTPUT of xlat is a "raw" string. The INPUT is a printable string.
2297 *
2298 * This is really the reverse of fr_prints().
2299 */
2300 if (cf_new_escape && *child) {
2301 ssize_t slen;
2302 PW_TYPE type;
2303 value_data_t data;
2304
2305 type = PW_TYPE_STRING;
2306 slen = value_data_from_str(request, &data, &type, NULL, child, talloc_array_length(child) - 1, '"');
2307 if (slen <= 0) {
2308 talloc_free(child);
2309 return NULL;
2310 }
2311
2312 talloc_free(child);
2313 child = data.ptr;
2314
2315 } else {
2316 char *q;
2317
2318 p = q = child;
2319 while (*p) {
2320 if (*p == '\\') switch (p[1]) {
2321 default:
2322 *(q++) = p[1];
2323 p += 2;
2324 continue;
2325
2326 case 'n':
2327 *(q++) = '\n';
2328 p += 2;
2329 continue;
2330
2331 case 't':
2332 *(q++) = '\t';
2333 p += 2;
2334 continue;
2335 }
2336
2337 *(q++) = *(p++);
2338 }
2339 *q = '\0';
2340 }
2341
2342 str = talloc_array(ctx, char, 2048); /* FIXME: have the module call talloc_typed_asprintf */
2343 *str = '\0'; /* Be sure the string is NULL terminated, we now only free on error */
2344
2345 rcode = node->xlat->func(node->xlat->instance, request, child, str, 2048);
2346 talloc_free(child);
2347 if (rcode < 0) {
2348 talloc_free(str);
2349 return NULL;
2350 }
2351 break;
2352
2353 #ifdef HAVE_REGEX
2354 case XLAT_REGEX:
2355 XLAT_DEBUG("%.*sxlat_aprint REGEX", lvl, xlat_spaces);
2356 if (regex_request_to_sub(ctx, &str, request, node->attr.tmpl_num) < 0) return NULL;
2357
2358 break;
2359 #endif
2360
2361 case XLAT_ALTERNATE:
2362 XLAT_DEBUG("%.*sxlat_aprint ALTERNATE", lvl, xlat_spaces);
2363 rad_assert(node->child != NULL);
2364 rad_assert(node->alternate != NULL);
2365
2366 /*
2367 * Call xlat_process recursively. The child /
2368 * alternate nodes may have "next" pointers, and
2369 * those need to be expanded.
2370 */
2371 if (xlat_process(&str, request, node->child, escape, escape_ctx) > 0) {
2372 XLAT_DEBUG("%.*sALTERNATE got first string: %s", lvl, xlat_spaces, str);
2373 } else {
2374 (void) xlat_process(&str, request, node->alternate, escape, escape_ctx);
2375 XLAT_DEBUG("%.*sALTERNATE got alternate string %s", lvl, xlat_spaces, str);
2376 }
2377 break;
2378 }
2379
2380 /*
2381 * If there's no data, return that, instead of an empty string.
2382 */
2383 if (str && !str[0]) {
2384 talloc_free(str);
2385 return NULL;
2386 }
2387
2388 /*
2389 * Escape the non-literals we found above.
2390 */
2391 if (str && escape) {
2392 size_t len;
2393 char *escaped;
2394
2395 len = talloc_array_length(str) * 3;
2396
2397 escaped = talloc_array(ctx, char, len);
2398 escape(request, escaped, len, str, escape_ctx);
2399 talloc_free(str);
2400 str = escaped;
2401 }
2402
2403 return str;
2404 }
2405
2406
xlat_process(char ** out,REQUEST * request,xlat_exp_t const * const head,xlat_escape_t escape,void * escape_ctx)2407 static size_t xlat_process(char **out, REQUEST *request, xlat_exp_t const * const head,
2408 xlat_escape_t escape, void *escape_ctx)
2409 {
2410 int i, list;
2411 size_t total;
2412 char **array, *answer;
2413 xlat_exp_t const *node;
2414
2415 *out = NULL;
2416
2417 /*
2418 * There are no nodes to process, so the result is a zero
2419 * length string.
2420 */
2421 if (!head) {
2422 *out = talloc_zero_array(request, char, 1);
2423 return 0;
2424 }
2425
2426 /*
2427 * Hack for speed. If it's one expansion, just allocate
2428 * that and return, instead of allocating an intermediary
2429 * array.
2430 */
2431 if (!head->next) {
2432 /*
2433 * Pass the MAIN escape function. Recursive
2434 * calls will call node-specific escape
2435 * functions.
2436 */
2437 answer = xlat_aprint(request, request, head, escape, escape_ctx, 0);
2438 if (!answer) {
2439 *out = talloc_zero_array(request, char, 1);
2440 return 0;
2441 }
2442 *out = answer;
2443 return strlen(answer);
2444 }
2445
2446 list = 0; /* FIXME: calculate this once */
2447 for (node = head; node != NULL; node = node->next) {
2448 list++;
2449 }
2450
2451 array = talloc_array(request, char *, list);
2452 if (!array) return -1;
2453
2454 for (node = head, i = 0; node != NULL; node = node->next, i++) {
2455 array[i] = xlat_aprint(array, request, node, escape, escape_ctx, 0); /* may be NULL */
2456 }
2457
2458 total = 0;
2459 for (i = 0; i < list; i++) {
2460 if (array[i]) total += strlen(array[i]); /* FIXME: calculate strlen once */
2461 }
2462
2463 if (!total) {
2464 talloc_free(array);
2465 *out = talloc_zero_array(request, char, 1);
2466 return 0;
2467 }
2468
2469 answer = talloc_array(request, char, total + 1);
2470
2471 total = 0;
2472 for (i = 0; i < list; i++) {
2473 size_t len;
2474
2475 if (array[i]) {
2476 len = strlen(array[i]);
2477 memcpy(answer + total, array[i], len);
2478 total += len;
2479 }
2480 }
2481 answer[total] = '\0';
2482 talloc_free(array); /* and child entries */
2483
2484 *out = answer;
2485 return total;
2486 }
2487
2488
2489 /** Replace %whatever in a string.
2490 *
2491 * See 'doc/configuration/variables.rst' for more information.
2492 *
2493 * @param[out] out Where to write pointer to output buffer.
2494 * @param[in] outlen Size of out.
2495 * @param[in] request current request.
2496 * @param[in] node the xlat structure to expand
2497 * @param[in] escape function to escape final value e.g. SQL quoting.
2498 * @param[in] escape_ctx pointer to pass to escape function.
2499 * @return length of string written @bug should really have -1 for failure
2500 */
xlat_expand_struct(char ** out,size_t outlen,REQUEST * request,xlat_exp_t const * node,xlat_escape_t escape,void * escape_ctx)2501 static ssize_t xlat_expand_struct(char **out, size_t outlen, REQUEST *request, xlat_exp_t const *node,
2502 xlat_escape_t escape, void *escape_ctx)
2503 {
2504 char *buff;
2505 ssize_t len;
2506
2507 rad_assert(node != NULL);
2508
2509 len = xlat_process(&buff, request, node, escape, escape_ctx);
2510 if ((len < 0) || !buff) {
2511 rad_assert(buff == NULL);
2512 if (*out) *out[0] = '\0';
2513 return len;
2514 }
2515
2516 len = strlen(buff);
2517
2518 /*
2519 * If out doesn't point to an existing buffer
2520 * copy the pointer to our buffer over.
2521 */
2522 if (!*out) {
2523 *out = buff;
2524 return len;
2525 }
2526
2527 /*
2528 * Otherwise copy the malloced buffer to the fixed one.
2529 */
2530 strlcpy(*out, buff, outlen);
2531 talloc_free(buff);
2532 return len;
2533 }
2534
2535 static ssize_t xlat_expand(char **out, size_t outlen, REQUEST *request, char const *fmt,
2536 xlat_escape_t escape, void *escape_ctx) CC_HINT(nonnull (1, 3, 4));
2537
2538 /** Replace %whatever in a string.
2539 *
2540 * See 'doc/configuration/variables.rst' for more information.
2541 *
2542 * @param[out] out Where to write pointer to output buffer.
2543 * @param[in] outlen Size of out.
2544 * @param[in] request current request.
2545 * @param[in] fmt string to expand.
2546 * @param[in] escape function to escape final value e.g. SQL quoting.
2547 * @param[in] escape_ctx pointer to pass to escape function.
2548 * @return length of string written @bug should really have -1 for failure
2549 */
xlat_expand(char ** out,size_t outlen,REQUEST * request,char const * fmt,xlat_escape_t escape,void * escape_ctx)2550 static ssize_t xlat_expand(char **out, size_t outlen, REQUEST *request, char const *fmt,
2551 xlat_escape_t escape, void *escape_ctx)
2552 {
2553 ssize_t len;
2554 xlat_exp_t *node;
2555
2556 /*
2557 * Give better errors than the old code.
2558 */
2559 len = xlat_tokenize_request(request, fmt, &node);
2560 if (len == 0) {
2561 if (*out) {
2562 *out[0] = '\0';
2563 } else {
2564 *out = talloc_zero_array(request, char, 1);
2565 }
2566 return 0;
2567 }
2568
2569 if (len < 0) {
2570 if (*out) *out[0] = '\0';
2571 return -1;
2572 }
2573
2574 len = xlat_expand_struct(out, outlen, request, node, escape, escape_ctx);
2575 talloc_free(node);
2576
2577 RDEBUG2("EXPAND %s", fmt);
2578 RDEBUG2(" --> %s", *out);
2579
2580 return len;
2581 }
2582
2583 /** Try to convert an xlat to a tmpl for efficiency
2584 *
2585 * @param ctx to allocate new vp_tmpl_t in.
2586 * @param node to convert.
2587 * @return NULL if unable to convert (not necessarily error), or a new vp_tmpl_t.
2588 */
xlat_to_tmpl_attr(TALLOC_CTX * ctx,xlat_exp_t * node)2589 vp_tmpl_t *xlat_to_tmpl_attr(TALLOC_CTX *ctx, xlat_exp_t *node)
2590 {
2591 vp_tmpl_t *vpt;
2592
2593 if (node->next || (node->type != XLAT_ATTRIBUTE) || (node->attr.type != TMPL_TYPE_ATTR)) return NULL;
2594
2595 /*
2596 * Concat means something completely different as an attribute reference
2597 * Count isn't implemented.
2598 */
2599 if ((node->attr.tmpl_num == NUM_COUNT) || (node->attr.tmpl_num == NUM_ALL)) return NULL;
2600
2601 vpt = tmpl_alloc(ctx, TMPL_TYPE_ATTR, node->fmt, -1);
2602 if (!vpt) return NULL;
2603 memcpy(&vpt->data, &node->attr.data, sizeof(vpt->data));
2604
2605 VERIFY_TMPL(vpt);
2606
2607 return vpt;
2608 }
2609
2610 /** Try to convert attr tmpl to an xlat for &attr[*] and artificially constructing expansions
2611 *
2612 * @param ctx to allocate new xlat_expt_t in.
2613 * @param vpt to convert.
2614 * @return NULL if unable to convert (not necessarily error), or a new vp_tmpl_t.
2615 */
xlat_from_tmpl_attr(TALLOC_CTX * ctx,vp_tmpl_t * vpt)2616 xlat_exp_t *xlat_from_tmpl_attr(TALLOC_CTX *ctx, vp_tmpl_t *vpt)
2617 {
2618 xlat_exp_t *node;
2619
2620 if (vpt->type != TMPL_TYPE_ATTR) return NULL;
2621
2622 node = talloc_zero(ctx, xlat_exp_t);
2623 node->type = XLAT_ATTRIBUTE;
2624 node->fmt = talloc_bstrndup(node, vpt->name, vpt->len);
2625 tmpl_init(&node->attr, TMPL_TYPE_ATTR, node->fmt, talloc_array_length(node->fmt) - 1);
2626 memcpy(&node->attr.data, &vpt->data, sizeof(vpt->data));
2627
2628 return node;
2629 }
2630
radius_xlat(char * out,size_t outlen,REQUEST * request,char const * fmt,xlat_escape_t escape,void * ctx)2631 ssize_t radius_xlat(char *out, size_t outlen, REQUEST *request, char const *fmt, xlat_escape_t escape, void *ctx)
2632 {
2633 return xlat_expand(&out, outlen, request, fmt, escape, ctx);
2634 }
2635
radius_xlat_struct(char * out,size_t outlen,REQUEST * request,xlat_exp_t const * xlat,xlat_escape_t escape,void * ctx)2636 ssize_t radius_xlat_struct(char *out, size_t outlen, REQUEST *request, xlat_exp_t const *xlat, xlat_escape_t escape, void *ctx)
2637 {
2638 return xlat_expand_struct(&out, outlen, request, xlat, escape, ctx);
2639 }
2640
radius_axlat(char ** out,REQUEST * request,char const * fmt,xlat_escape_t escape,void * ctx)2641 ssize_t radius_axlat(char **out, REQUEST *request, char const *fmt, xlat_escape_t escape, void *ctx)
2642 {
2643 *out = NULL;
2644 return xlat_expand(out, 0, request, fmt, escape, ctx);
2645 }
2646
radius_axlat_struct(char ** out,REQUEST * request,xlat_exp_t const * xlat,xlat_escape_t escape,void * ctx)2647 ssize_t radius_axlat_struct(char **out, REQUEST *request, xlat_exp_t const *xlat, xlat_escape_t escape, void *ctx)
2648 {
2649 *out = NULL;
2650 return xlat_expand_struct(out, 0, request, xlat, escape, ctx);
2651 }
2652