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.
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.
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
21 * @brief String expansion ("translation"). Implements %Attribute -> value
23 * @copyright 2000,2006 The FreeRADIUS server project
24 * @copyright 2000 Alan DeKok <aland@ox.org>
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>
35 typedef struct xlat_t {
36 char name[MAX_STRING_LEN]; //!< Name of the xlat expansion.
37 int length; //!< Length of name.
38 void *instance; //!< Module instance passed to xlat and escape functions.
39 RAD_XLAT_FUNC func; //!< xlat function.
40 RADIUS_ESCAPE_STRING escape; //!< Escape function to apply to dynamic input to func.
41 bool internal; //!< If true, cannot be redefined.
45 XLAT_LITERAL, //!< Literal string
46 XLAT_PERCENT, //!< Literal string with %v
47 XLAT_MODULE, //!< xlat module
48 XLAT_VIRTUAL, //!< virtual attribute
49 XLAT_ATTRIBUTE, //!< xlat attribute
51 XLAT_REGEX, //!< regex reference
53 XLAT_ALTERNATE //!< xlat conditional syntax :-
57 char const *fmt; //!< The format string.
58 size_t len; //!< Length of the format string.
60 DICT_ATTR const *da; //!< the name of the dictionary attribute
62 int num; //!< attribute number
63 int8_t tag; //!< attribute tag
64 pair_lists_t list; //!< list of which attribute
65 request_refs_t ref; //!< outer / this / ...
67 xlat_state_t type; //!< type of this expansion
68 xlat_exp_t *next; //!< Next in the list.
70 xlat_exp_t *child; //!< Nested expansion.
71 xlat_exp_t *alternate; //!< Alternative expansion if this one expanded to a zero length string.
73 xlat_t const *xlat; //!< The xlat expansion to expand format with.
76 typedef struct xlat_out {
77 char const *out; //!< Output data.
78 size_t len; //!< Length of the output string.
81 static rbtree_t *xlat_root = NULL;
84 static char const * const xlat_foreach_names[] = {"Foreach-Variable-0",
97 #if REQUEST_MAX_REGEX > 8
98 #error Please fix the following line
100 static int xlat_inst[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8 }; /* up to 8 for regex */
102 char const *radiusd_short_version = RADIUSD_VERSION_STRING;
104 /** Print length of its RHS.
107 static ssize_t xlat_strlen(UNUSED void *instance, UNUSED REQUEST *request,
108 char const *fmt, char *out, size_t outlen)
110 snprintf(out, outlen, "%u", (unsigned int) strlen(fmt));
114 /** Print the size of the attribute in bytes.
117 static ssize_t xlat_length(UNUSED void *instance, UNUSED REQUEST *request,
118 char const *fmt, char *out, size_t outlen)
121 while (isspace((int) *fmt)) fmt++;
123 if ((radius_get_vp(&vp, request, fmt) < 0) || !vp) {
128 snprintf(out, outlen, "%zu", vp->length);
132 /** Print data as integer, not as VALUE.
135 static ssize_t xlat_integer(UNUSED void *instance, REQUEST *request,
136 char const *fmt, char *out, size_t outlen)
140 uint64_t int64 = 0; /* Needs to be initialised to zero */
141 uint32_t int32 = 0; /* Needs to be initialised to zero */
143 while (isspace((int) *fmt)) fmt++;
145 if ((radius_get_vp(&vp, request, fmt) < 0) || !vp) {
150 switch (vp->da->type) {
153 if (vp->length > 8) {
157 if (vp->length > 4) {
158 memcpy(&int64, vp->vp_octets, vp->length);
159 return snprintf(out, outlen, "%" PRIu64, htonll(int64));
162 memcpy(&int32, vp->vp_octets, vp->length);
163 return snprintf(out, outlen, "%i", htonl(int32));
165 case PW_TYPE_INTEGER64:
166 return snprintf(out, outlen, "%" PRIu64, vp->vp_integer64);
169 * IP addresses are treated specially, as parsing functions assume the value
170 * is bigendian and will convert it for us.
172 case PW_TYPE_IPV4_ADDR:
173 return snprintf(out, outlen, "%u", htonl(vp->vp_ipaddr));
175 case PW_TYPE_IPV4_PREFIX:
176 return snprintf(out, outlen, "%u", htonl((*(uint32_t *)(vp->vp_ipv4prefix + 2))));
178 case PW_TYPE_INTEGER:
182 return snprintf(out, outlen, "%u", vp->vp_integer);
185 * Ethernet is weird... It's network related, so we assume to it should be
188 case PW_TYPE_ETHERNET:
189 memcpy(&int64, &vp->vp_ether, vp->length);
190 return snprintf(out, outlen, "%" PRIu64, htonll(int64));
193 return snprintf(out, outlen, "%i", vp->vp_signed);
195 case PW_TYPE_IPV6_ADDR:
196 return fr_prints_uint128(out, outlen, ntohlll(*(uint128_t const *) &vp->vp_ipv6addr));
198 case PW_TYPE_IPV6_PREFIX:
199 return fr_prints_uint128(out, outlen, ntohlll(*(uint128_t const *) &(vp->vp_ipv6prefix[2])));
205 REDEBUG("Type '%s' of length %zu cannot be converted to integer",
206 fr_int2str(dict_attr_types, vp->da->type, "???"), vp->length);
212 /** Print data as hex, not as VALUE.
215 static ssize_t xlat_hex(UNUSED void *instance, REQUEST *request,
216 char const *fmt, char *out, size_t outlen)
224 while (isspace((int) *fmt)) fmt++;
226 if ((radius_get_vp(&vp, request, fmt) < 0) || !vp) {
231 ret = rad_vp2data(&p, vp);
238 * Don't truncate the data.
240 if ((ret < 0 ) || (outlen < (len * 2))) {
245 for (i = 0; i < len; i++) {
246 snprintf(out + 2*i, 3, "%02x", p[i]);
252 /** Return the tag of an attribute reference
255 static ssize_t xlat_tag(UNUSED void *instance, REQUEST *request,
256 char const *fmt, char *out, size_t outlen)
260 while (isspace((int) *fmt)) fmt++;
262 if ((radius_get_vp(&vp, request, fmt) < 0) || !vp) {
267 if (!vp->da->flags.has_tag || !TAG_VALID(vp->tag)) {
272 return snprintf(out, outlen, "%u", vp->tag);
275 /** Print out attribute info
277 * Prints out all instances of a current attribute, or all attributes in a list.
279 * At higher debugging levels, also prints out alternative decodings of the same
280 * value. This is helpful to determine types for unknown attributes of long
281 * passed vendors, or just crazy/broken NAS.
283 * It's also useful for exposing issues in the packet decoding functions, as in
284 * some cases they get fed random garbage data.
286 * This expands to a zero length string.
288 static ssize_t xlat_debug_attr(UNUSED void *instance, REQUEST *request, char const *fmt,
289 char *out, UNUSED size_t outlen)
291 VALUE_PAIR *vp, **vps;
293 value_pair_tmpl_t vpt;
297 if (!RDEBUG_ENABLED2) {
302 while (isspace((int) *fmt)) fmt++;
304 if (radius_parse_attr(&vpt, fmt, REQUEST_CURRENT, PAIR_LIST_REQUEST) < 0) {
305 RDEBUG("%s", fr_strerror());
310 if (radius_request(¤t, vpt.vpt_request) < 0) return -2;
312 vps = radius_list(current, vpt.vpt_list);
317 RIDEBUG("Attributes matching \"%s\"", fmt);
318 vp = fr_cursor_init(&cursor, vps);
321 vp = fr_cursor_next_by_da(&cursor, vpt.vpt_da, TAG_ANY);
324 DICT_ATTR *dac = NULL;
327 vp_prints_value(buffer, sizeof(buffer), vp, '\'');
329 if (vp->da->flags.has_tag) {
330 RIDEBUG2("\t%s:%s:%i %s %s",
331 fr_int2str(pair_lists, vpt.vpt_list, "<INVALID>"),
334 fr_int2str(fr_tokens, vp->op, "<INVALID>"),
337 RIDEBUG2("\t%s:%s %s %s",
338 fr_int2str(pair_lists, vpt.vpt_list, "<INVALID>"),
340 fr_int2str(fr_tokens, vp->op, "<INVALID>"),
344 if (!RDEBUG_ENABLED3) {
348 if (vp->da->vendor) {
349 dv = dict_vendorbyvalue(vp->da->vendor);
350 RDEBUG3("\t\tvendor : %i (%s)", vp->da->vendor, dv ? dv->name : "unknown");
352 RDEBUG3("\t\ttype : %s", fr_int2str(dict_attr_types, vp->da->type, "<INVALID>"));
353 RDEBUG3("\t\tlength : %zu", vp->length);
355 dac = talloc_memdup(request, vp->da, sizeof(DICT_ATTR));
357 talloc_set_type(dac, DICT_ATTR);
358 dac->flags.vp_free = 0;
364 vp = fr_cursor_next_by_da(&cursor, vpt.vpt_da, TAG_ANY);
366 vp = fr_cursor_next(&cursor);
374 /** Prints the current module processing the request
377 static ssize_t xlat_module(UNUSED void *instance, REQUEST *request,
378 UNUSED char const *fmt, char *out, size_t outlen)
380 strlcpy(out, request->module, outlen);
386 /** Implements the Foreach-Variable-X
390 static ssize_t xlat_foreach(void *instance, REQUEST *request,
391 UNUSED char const *fmt, char *out, size_t outlen)
397 * See modcall, "FOREACH" for how this works.
399 pvp = (VALUE_PAIR **) request_data_reference(request, radius_get_vp, *(int*) instance);
405 len = vp_prints_value(out, outlen, *pvp, 0);
406 if (is_truncated(len, outlen)) {
407 RDEBUG("Insufficient buffer space to write foreach value");
415 /** Print data as string, if possible.
417 * If attribute "Foo" is defined as "octets" it will normally
418 * be printed as 0x0a0a0a. The xlat "%{string:Foo}" will instead
421 static ssize_t xlat_string(UNUSED void *instance, REQUEST *request,
422 char const *fmt, char *out, size_t outlen)
429 while (isspace((int) *fmt)) fmt++;
437 if ((radius_get_vp(&vp, request, fmt) < 0) || !vp) goto nothing;
439 ret = rad_vp2data(&p, vp);
444 switch (vp->da->type) {
446 len = fr_print_string((char const *) p, vp->length, out, outlen);
450 len = strlcpy(out, vp->vp_strvalue, outlen);
454 len = fr_print_string((char const *) p, ret, out, outlen);
461 /** xlat expand string attribute value
464 static ssize_t xlat_xlat(UNUSED void *instance, REQUEST *request,
465 char const *fmt, char *out, size_t outlen)
469 while (isspace((int) *fmt)) fmt++;
477 if ((radius_get_vp(&vp, request, fmt) < 0) || !vp) goto nothing;
479 return radius_xlat(out, outlen, request, vp->vp_strvalue, NULL, NULL);
482 /** Dynamically change the debugging level for the current request
486 static ssize_t xlat_debug(UNUSED void *instance, REQUEST *request,
487 char const *fmt, char *out, size_t outlen)
492 * Expand to previous (or current) level
494 snprintf(out, outlen, "%d", request->log.lvl & RAD_REQUEST_OPTION_DEBUG4);
497 * Assume we just want to get the current value and NOT set it to 0
504 request->log.lvl = RAD_REQUEST_OPTION_NONE;
505 request->log.func = NULL;
507 if (level > 4) level = 4;
509 request->log.lvl = level;
510 request->log.func = vradlog_request;
518 * Compare two xlat_t structs, based ONLY on the module name.
520 static int xlat_cmp(void const *one, void const *two)
522 xlat_t const *a = one;
523 xlat_t const *b = two;
525 if (a->length != b->length) {
526 return a->length - b->length;
529 return memcmp(a->name, b->name, a->length);
534 * find the appropriate registered xlat function.
536 static xlat_t *xlat_find(char const *name)
540 strlcpy(my_xlat.name, name, sizeof(my_xlat.name));
541 my_xlat.length = strlen(my_xlat.name);
543 return rbtree_finddata(xlat_root, &my_xlat);
547 /** Register an xlat function.
549 * @param[in] name xlat name.
550 * @param[in] func xlat function to be called.
551 * @param[in] escape function to sanitize any sub expansions passed to the xlat function.
552 * @param[in] instance of module that's registering the xlat function.
553 * @return 0 on success, -1 on failure
555 int xlat_register(char const *name, RAD_XLAT_FUNC func, RADIUS_ESCAPE_STRING escape, void *instance)
561 if (!name || !*name) {
562 DEBUG("xlat_register: Invalid xlat name");
567 * First time around, build up the tree...
569 * FIXME: This code should be hoisted out of this function,
570 * and into a global "initialization". But it isn't critical...
577 xlat_root = rbtree_create(xlat_cmp, free, 0);
579 DEBUG("xlat_register: Failed to create tree");
584 for (i = 0; xlat_foreach_names[i] != NULL; i++) {
585 xlat_register(xlat_foreach_names[i],
586 xlat_foreach, NULL, &xlat_inst[i]);
587 c = xlat_find(xlat_foreach_names[i]);
588 rad_assert(c != NULL);
593 #define XLAT_REGISTER(_x) xlat_register(STRINGIFY(_x), xlat_ ## _x, NULL, NULL); \
594 c = xlat_find(STRINGIFY(_x)); \
595 rad_assert(c != NULL); \
598 XLAT_REGISTER(integer);
599 XLAT_REGISTER(strlen);
600 XLAT_REGISTER(length);
603 XLAT_REGISTER(string);
605 XLAT_REGISTER(module);
606 XLAT_REGISTER(debug_attr);
608 xlat_register("debug", xlat_debug, NULL, &xlat_inst[0]);
609 c = xlat_find("debug");
610 rad_assert(c != NULL);
615 * If it already exists, replace the instance.
617 strlcpy(my_xlat.name, name, sizeof(my_xlat.name));
618 my_xlat.length = strlen(my_xlat.name);
619 c = rbtree_finddata(xlat_root, &my_xlat);
622 DEBUG("xlat_register: Cannot re-define internal xlat");
628 c->instance = instance;
633 * Doesn't exist. Create it.
635 c = rad_malloc(sizeof(*c));
636 memset(c, 0, sizeof(*c));
640 strlcpy(c->name, name, sizeof(c->name));
641 c->length = strlen(c->name);
642 c->instance = instance;
644 node = rbtree_insert_node(xlat_root, c);
653 /** Unregister an xlat function
655 * We can only have one function to call per name, so the passing of "func"
656 * here is extraneous.
658 * @param[in] name xlat to unregister.
659 * @param[in] func unused.
660 * @param[in] instance data.
662 void xlat_unregister(char const *name, UNUSED RAD_XLAT_FUNC func, void *instance)
669 strlcpy(my_xlat.name, name, sizeof(my_xlat.name));
670 my_xlat.length = strlen(my_xlat.name);
672 c = rbtree_finddata(xlat_root, &my_xlat);
675 if (c->instance != instance) return;
677 rbtree_deletebydata(xlat_root, c);
681 /** Crappy temporary function to add attribute ref support to xlats
683 * This needs to die, and hopefully will die, when xlat functions accept
684 * xlat node structures.
686 * Provides either a pointer to a buffer which contains the value of the reference VALUE_PAIR
687 * in an architecture independent format. Or a pointer to the start of the fmt string.
689 * The pointer is only guaranteed to be valid between calls to xlat_fmt_to_ref,
690 * and so long as the source VALUE_PAIR is not freed.
692 * @param out where to write a pointer to the buffer to the data the xlat function needs to work on.
693 * @param request current request.
695 * @returns the length of the data or -1 on error.
697 ssize_t xlat_fmt_to_ref(uint8_t const **out, REQUEST *request, char const *fmt)
701 while (isspace((int) *fmt)) fmt++;
704 if ((radius_get_vp(&vp, request, fmt) < 0) || !vp) {
709 return rad_vp2data(out, vp);
712 *out = (uint8_t const *)fmt;
716 /** De-register all xlat functions, used mainly for debugging.
721 rbtree_free(xlat_root);
726 #define XLAT_DEBUG DEBUG3
728 #define XLAT_DEBUG(...)
731 static ssize_t xlat_tokenize_expansion(TALLOC_CTX *ctx, char *fmt, xlat_exp_t **head,
733 static ssize_t xlat_tokenize_literal(TALLOC_CTX *ctx, char *fmt, xlat_exp_t **head,
734 int brace, char const **error);
735 static size_t xlat_process(char **out, REQUEST *request, xlat_exp_t const * const head,
736 RADIUS_ESCAPE_STRING escape, void *escape_ctx);
738 static ssize_t xlat_tokenize_alternation(TALLOC_CTX *ctx, char *fmt, xlat_exp_t **head,
745 rad_assert(fmt[0] == '%');
746 rad_assert(fmt[1] == '{');
747 rad_assert(fmt[2] == '%');
748 rad_assert(fmt[3] == '{');
750 XLAT_DEBUG("ALTERNATE <-- %s", fmt);
752 node = talloc_zero(ctx, xlat_exp_t);
753 node->type = XLAT_ALTERNATE;
756 slen = xlat_tokenize_expansion(node, p, &node->child, error);
759 return slen - (p - fmt);
765 *error = "Expected ':' after first expansion";
772 *error = "Expected '-' after ':'";
778 * Allow the RHS to be empty as a special case.
782 * Hack up an empty string.
784 node->alternate = talloc_zero(node, xlat_exp_t);
785 node->alternate->type = XLAT_LITERAL;
786 node->alternate->fmt = talloc_typed_strdup(node->alternate, "");
790 slen = xlat_tokenize_literal(node, p, &node->alternate, true, error);
793 return slen - (p - fmt);
796 if (!node->alternate) {
798 *error = "Empty expansion is invalid";
808 static ssize_t xlat_tokenize_expansion(TALLOC_CTX *ctx, char *fmt, xlat_exp_t **head,
813 char const *attrname;
816 rad_assert(fmt[0] == '%');
817 rad_assert(fmt[1] == '{');
822 if ((fmt[2] == '%') && (fmt[3] == '{')) {
823 return xlat_tokenize_alternation(ctx, fmt, head, error);
826 XLAT_DEBUG("EXPANSION <-- %s", fmt);
827 node = talloc_zero(ctx, xlat_exp_t);
828 attrname = node->fmt = fmt + 2;
833 * Handle regex's specially.
835 if (isdigit((int) fmt[2]) && (fmt[3] == '}')) {
838 *error = "Invalid regex reference";
842 XLAT_DEBUG("REGEX <-- %s", fmt);
844 node->num = fmt[2] - '0'; /* ASCII */
846 node->type = XLAT_REGEX;
850 #endif /* HAVE_REGEX_H */
855 * %{Tunnel-Password:1}
856 * %{Tunnel-Password:1[#]}
857 * %{request:Attr-Name}
858 * %{request:Tunnel-Password:1}
859 * %{request:Tunnel-Password:1[#]}
863 for (p = fmt + 2; *p != '\0'; p++) {
864 if (*p == ':') break;
866 if (isspace((int) *p)) break;
868 if (*p == '[') break;
870 if (*p == '}') break;
873 if (*p != ':') p = NULL;
876 * Might be a module name reference.
884 node->xlat = xlat_find(node->fmt);
886 node->type = XLAT_MODULE;
888 XLAT_DEBUG("MOD <-- %s ... %s", node->fmt, p + 1);
890 slen = xlat_tokenize_literal(node, p + 1, &node->child, true, error);
893 return slen - (p - fmt);
898 rad_assert(node->next == NULL);
903 * Modules can have '}' in their RHS, so we
904 * didn't check for that until now.
906 * As of now, node->fmt MUST be a reference to an
907 * attribute, however complicated. So it MUST have a closing brace.
909 brace = strchr(p + 1, '}');
910 if (!brace) goto no_brace;
916 * %{Tunnel-Password:1}
917 * %{request:Tunnel-Password:1}
919 * <sigh> The syntax is fairly poor.
921 XLAT_DEBUG("Looking for list in '%s'", attrname);
924 * Not a module. Has to be an attribute
927 * As of v3, we've removed %{request: ..>} as
928 * internally registered xlats.
931 node->ref = radius_request_name(&attrname, REQUEST_CURRENT);
932 rad_assert(node->ref != REQUEST_UNKNOWN);
934 node->list = radius_list_name(&attrname, PAIR_LIST_REQUEST);
935 if (node->list == PAIR_LIST_UNKNOWN) {
937 *error = "Unknown module";
942 * Check for a trailing tag.
944 p = strchr(attrname, ':');
948 brace = strchr(attrname, '}');
952 *error = "No matching closing brace";
953 return -1; /* second character of format string */
957 node->ref = REQUEST_CURRENT;
958 node->list = PAIR_LIST_REQUEST;
963 XLAT_DEBUG("Looking for attribute name in %s", attrname);
966 * Allow for an array reference. They come AFTER the
967 * tag, if the tag exists. Otherwise, they come after
968 * the attribute name.
971 q = strchr(p + 1, '[');
973 q = strchr(attrname, '[');
975 if (q) *(q++) = '\0';
979 *error = "Empty expression is invalid";
980 return -(attrname - fmt);
984 * It's either an attribute name, or a Tunnel-Password:TAG
985 * with the ':' already set to NULL.
987 node->da = dict_attrbyname(attrname);
990 * Foreach. Maybe other stuff, too.
992 node->xlat = xlat_find(attrname);
994 node->type = XLAT_VIRTUAL;
995 node->fmt = attrname;
997 XLAT_DEBUG("VIRTUAL <-- %s", node->fmt);
999 rad_assert(node->next == NULL);
1005 *error = "Unknown attribute";
1006 return -(attrname - fmt);
1016 if (!node->da->flags.has_tag) {
1018 *error = "Attribute cannot have a tag";
1022 tag = strtoul(p + 1, &end, 10);
1025 if (tag == ULONG_MAX) {
1027 *error = "Invalid tag value";
1036 *error = "Unexpected text after tag";
1041 node->tag = TAG_ANY;
1046 * Check for array reference
1054 node->num = NUM_COUNT;
1057 } else if (*p == '*') {
1058 node->num = NUM_JOIN;
1061 } else if (isdigit((int) *p)) {
1062 num = strtoul(p, &end, 10);
1065 *error = "Invalid array reference";
1074 *error = "Invalid array reference";
1080 *error = "Expected ']'";
1087 *error = "Unexpected text after array reference";
1091 node->num = NUM_ANY;
1094 rad_assert(!p || (p == brace));
1096 node->type = XLAT_ATTRIBUTE;
1100 rad_assert(node->next == NULL);
1105 static ssize_t xlat_tokenize_literal(TALLOC_CTX *ctx, char *fmt, xlat_exp_t **head,
1106 int brace, char const **error)
1111 if (!*fmt) return 0;
1113 XLAT_DEBUG("LITERAL <-- %s", fmt);
1115 node = talloc_zero(ctx, xlat_exp_t);
1118 node->type = XLAT_LITERAL;
1125 * Convert \n to it's literal representation.
1127 if (p[0] == '\\') switch (p[1]) {
1142 if (!p[0] || !p[1]) {
1144 *error = "Hex expansion requires two hex digits";
1148 if (!fr_hex2bin((uint8_t *) q, 1, p, 2)) {
1150 *error = "Invalid hex characters";
1155 * Don't let people shoot themselves in the foot.
1156 * \x00 is forbidden.
1160 *error = "Cannot add zero byte to printable string";
1177 * Process the expansion.
1179 if ((p[0] == '%') && (p[1] == '{')) {
1182 XLAT_DEBUG("LITERAL <-- %s", node->fmt);
1184 slen = xlat_tokenize_expansion(node, p, &node->next, error);
1187 return slen - (p - fmt);
1189 *p = '\0'; /* end the literal */
1192 rad_assert(node->next != NULL);
1195 * Short-circuit the recursive call.
1196 * This saves another function call and
1197 * memory allocation.
1202 * "foo %{User-Name} bar"
1204 * EXPANSION User-Name
1207 slen = xlat_tokenize_literal(node->next, p, &(node->next->next), brace, error);
1208 rad_assert(slen != 0);
1211 return slen - (p - fmt);
1215 break; /* stop processing the string */
1219 * Check for valid single-character expansions.
1225 if (!p[1] || !strchr("%dlmtDGHISTYv", p[1])) {
1227 *error = "Invalid variable expansion";
1232 next = talloc_zero(node, xlat_exp_t);
1236 next->fmt = talloc_typed_strdup(next, "%");
1238 XLAT_DEBUG("LITERAL <-- %s", next->fmt);
1239 next->type = XLAT_LITERAL;
1244 XLAT_DEBUG("PERCENT <-- %c", *next->fmt);
1245 next->type = XLAT_PERCENT;
1257 slen = xlat_tokenize_literal(node->next, p, &(node->next->next), brace, error);
1258 rad_assert(slen != 0);
1261 return slen - (p - fmt);
1265 break; /* stop processing the string */
1269 * If required, eat the brace.
1271 if (brace && (*p == '}')) {
1282 * Squash zero-width literals
1284 if (node->len > 0) {
1287 *head = talloc_steal(ctx, node->next);
1295 static char const xlat_tabs[] = " ";
1297 static void xlat_tokenize_debug(xlat_exp_t const *node, int lvl)
1299 rad_assert(node != NULL);
1301 if (lvl >= (int) sizeof(xlat_tabs)) lvl = sizeof(xlat_tabs);
1304 switch (node->type) {
1306 DEBUG("%.*sliteral --> %s", lvl, xlat_tabs, node->fmt);
1310 DEBUG("%.*spercent --> %c", lvl, xlat_tabs, node->fmt[0]);
1313 case XLAT_ATTRIBUTE:
1314 rad_assert(node->da != NULL);
1315 DEBUG("%.*sattribute --> %s", lvl, xlat_tabs, node->da->name);
1316 rad_assert(node->child == NULL);
1317 if ((node->tag != TAG_ANY) || (node->num != NUM_ANY)) {
1318 DEBUG("%.*s{", lvl, xlat_tabs);
1320 DEBUG("%.*sref %d", lvl + 1, xlat_tabs, node->ref);
1321 DEBUG("%.*slist %d", lvl + 1, xlat_tabs, node->list);
1323 if (node->tag != TAG_ANY) {
1324 DEBUG("%.*stag %d", lvl + 1, xlat_tabs, node->tag);
1326 if (node->num != NUM_ANY) {
1327 if (node->num == NUM_COUNT) {
1328 DEBUG("%.*s[#]", lvl + 1, xlat_tabs);
1329 } else if (node->num == NUM_JOIN) {
1330 DEBUG("%.*s[*]", lvl + 1, xlat_tabs);
1332 DEBUG("%.*s[%d]", lvl + 1, xlat_tabs, node->num);
1336 DEBUG("%.*s}", lvl, xlat_tabs);
1341 rad_assert(node->fmt != NULL);
1342 DEBUG("%.*svirtual --> %s", lvl, xlat_tabs, node->fmt);
1346 rad_assert(node->xlat != NULL);
1347 DEBUG("%.*sxlat --> %s", lvl, xlat_tabs, node->xlat->name);
1349 DEBUG("%.*s{", lvl, xlat_tabs);
1350 xlat_tokenize_debug(node->child, lvl + 1);
1351 DEBUG("%.*s}", lvl, xlat_tabs);
1357 DEBUG("%.*sregex-var --> %d", lvl, xlat_tabs, node->num);
1361 case XLAT_ALTERNATE:
1362 DEBUG("%.*sif {", lvl, xlat_tabs);
1363 xlat_tokenize_debug(node->child, lvl + 1);
1364 DEBUG("%.*s}", lvl, xlat_tabs);
1365 DEBUG("%.*selse {", lvl, xlat_tabs);
1366 xlat_tokenize_debug(node->alternate, lvl + 1);
1367 DEBUG("%.*s}", lvl, xlat_tabs);
1374 size_t xlat_sprint(char *buffer, size_t bufsize, xlat_exp_t const *node)
1385 end = buffer + bufsize;
1388 switch (node->type) {
1390 strlcpy(p, node->fmt, end - p);
1396 p[1] = node->fmt[0];
1400 case XLAT_ATTRIBUTE:
1404 if (node->ref != REQUEST_CURRENT) {
1405 strlcpy(p, fr_int2str(request_refs, node->ref, "??"), end - p);
1410 if ((node->ref != REQUEST_CURRENT) ||
1411 (node->list != PAIR_LIST_REQUEST)) {
1412 strlcpy(p, fr_int2str(pair_lists, node->list, "??"), end - p);
1417 strlcpy(p, node->da->name, end - p);
1420 if (node->tag != TAG_ANY) {
1422 snprintf(p, end - p, "%u", node->tag);
1426 if (node->num != NUM_ANY) {
1428 switch (node->num) {
1438 snprintf(p, end - p, "%u", node->num);
1447 snprintf(p, end - p, "%%{%u}", node->num);
1454 strlcpy(p, node->fmt, end - p);
1462 strlcpy(p, node->xlat->name, end - p);
1465 rad_assert(node->child != NULL);
1466 len = xlat_sprint(p, end - p, node->child);
1471 case XLAT_ALTERNATE:
1475 len = xlat_sprint(p, end - p, node->child);
1481 len = xlat_sprint(p, end - p, node->alternate);
1489 if (p == end) break;
1499 ssize_t xlat_tokenize(TALLOC_CTX *ctx, char *fmt, xlat_exp_t **head,
1502 return xlat_tokenize_literal(ctx, fmt, head, false, error);
1506 /** Tokenize an xlat expansion
1508 * @param[in] request the input request. Memory will be attached here.
1509 * @param[in] fmt the format string to expand
1510 * @param[out] head the head of the xlat list / tree structure.
1512 static ssize_t xlat_tokenize_request(REQUEST *request, char const *fmt, xlat_exp_t **head)
1521 * Copy the original format string to a buffer so that
1522 * the later functions can mangle it in-place, which is
1525 tokens = talloc_typed_strdup(request, fmt);
1526 if (!tokens) return -1;
1528 slen = xlat_tokenize_literal(request, tokens, head, false, &error);
1531 * Zero length expansion, return a zero length node.
1534 *head = talloc_zero(request, xlat_exp_t);
1538 * Output something like:
1541 * " ^ error was here"
1544 talloc_free(tokens);
1545 rad_assert(error != NULL);
1547 REMARKER(fmt, -slen, error);
1551 if (*head && (debug_flag > 2)) {
1553 DEBUG("Parsed xlat tree:");
1554 xlat_tokenize_debug(*head, 0);
1558 * All of the nodes point to offsets in the "tokens"
1559 * string. Let's ensure that free'ing head will free
1562 (void) talloc_steal(*head, tokens);
1568 static char *xlat_getvp(TALLOC_CTX *ctx, REQUEST *request, pair_lists_t list, DICT_ATTR const *da,
1569 int8_t tag, int num, bool return_null)
1571 VALUE_PAIR *vp, *vps = NULL, *myvp = NULL;
1572 RADIUS_PACKET *packet = NULL;
1577 * Arg. Too much abstraction is annoying.
1581 if (return_null) return NULL;
1582 return vp_aprint_type(ctx, da->type);
1584 case PAIR_LIST_CONTROL:
1585 vps = request->config_items;
1588 case PAIR_LIST_REQUEST:
1589 packet = request->packet;
1590 if (packet) vps = packet->vps;
1593 case PAIR_LIST_REPLY:
1594 packet = request->reply;
1595 if (packet) vps = packet->vps;
1599 case PAIR_LIST_PROXY_REQUEST:
1600 packet = request->proxy;
1601 if (packet) vps = packet->vps;
1604 case PAIR_LIST_PROXY_REPLY:
1605 packet = request->proxy_reply;
1606 if (packet) vps = packet->vps;
1613 if (request->coa) packet = request->coa->packet;
1614 if (packet) vps = packet->vps;
1617 case PAIR_LIST_COA_REPLY:
1618 case PAIR_LIST_DM_REPLY:
1619 if (request->coa) packet = request->coa->reply;
1620 if (packet) vps = packet->vps;
1627 * Now we have the list, check to see if we have an attribute in
1628 * the request, if we do, it takes precedence over the virtual
1631 * This allows users to manipulate virtual attributes as if they
1634 vp = pairfind(vps, da->attr, da->vendor, tag);
1635 if (vp) goto do_print;
1638 * Some non-packet expansions
1642 break; /* ignore them */
1644 case PW_CLIENT_SHORTNAME:
1645 if (num == NUM_COUNT) goto count;
1646 if (request->client && request->client->shortname) {
1647 return talloc_typed_strdup(ctx, request->client->shortname);
1649 return talloc_typed_strdup(ctx, "<UNKNOWN-CLIENT>");
1651 case PW_REQUEST_PROCESSING_STAGE:
1652 if (num == NUM_COUNT) goto count;
1653 if (request->component) {
1654 return talloc_typed_strdup(ctx, request->component);
1656 return talloc_typed_strdup(ctx, "server_core");
1658 case PW_VIRTUAL_SERVER:
1659 if (num == NUM_COUNT) goto count;
1660 if (!request->server) return NULL;
1661 return talloc_typed_strdup(ctx, request->server);
1663 case PW_MODULE_RETURN_CODE:
1664 if (num == NUM_COUNT) goto count;
1665 if (!request->rcode) return NULL;
1666 return talloc_typed_strdup(ctx, fr_int2str(modreturn_table, request->rcode, ""));
1670 * All of the attributes must now refer to a packet.
1671 * If there's no packet, we can't print any attribute
1675 if (return_null) return NULL;
1676 return vp_aprint_type(ctx, da->type);
1684 case PW_PACKET_TYPE:
1685 dv = dict_valbyattr(PW_PACKET_TYPE, 0, packet->code);
1686 if (dv) return talloc_typed_strdup(ctx, dv->name);
1687 return talloc_typed_asprintf(ctx, "%d", packet->code);
1689 case PW_RESPONSE_PACKET_TYPE:
1694 if (request->proxy_reply && (!request->reply || !request->reply->code)) {
1695 code = request->proxy_reply->code;
1698 if (request->reply) {
1699 code = request->reply->code;
1702 return talloc_typed_strdup(ctx, fr_packet_codes[code]);
1706 * Virtual attributes which require a temporary VALUE_PAIR
1707 * to be allocated. We can't use stack allocated memory
1708 * because of the talloc checks sprinkled throughout the
1709 * various VP functions.
1711 case PW_PACKET_AUTHENTICATION_VECTOR:
1712 myvp = pairalloc(ctx, da);
1713 pairmemcpy(myvp, packet->vector, sizeof(packet->vector));
1717 case PW_CLIENT_IP_ADDRESS:
1718 case PW_PACKET_SRC_IP_ADDRESS:
1719 if (packet->src_ipaddr.af == AF_INET) {
1720 myvp = pairalloc(ctx, da);
1721 myvp->vp_ipaddr = packet->src_ipaddr.ipaddr.ip4addr.s_addr;
1726 case PW_PACKET_DST_IP_ADDRESS:
1727 if (packet->dst_ipaddr.af == AF_INET) {
1728 myvp = pairalloc(ctx, da);
1729 myvp->vp_ipaddr = packet->dst_ipaddr.ipaddr.ip4addr.s_addr;
1734 case PW_PACKET_SRC_IPV6_ADDRESS:
1735 if (packet->src_ipaddr.af == AF_INET6) {
1736 myvp = pairalloc(ctx, da);
1737 memcpy(&myvp->vp_ipv6addr,
1738 &packet->src_ipaddr.ipaddr.ip6addr,
1739 sizeof(packet->src_ipaddr.ipaddr.ip6addr));
1744 case PW_PACKET_DST_IPV6_ADDRESS:
1745 if (packet->dst_ipaddr.af == AF_INET6) {
1746 myvp = pairalloc(ctx, da);
1747 memcpy(&myvp->vp_ipv6addr,
1748 &packet->dst_ipaddr.ipaddr.ip6addr,
1749 sizeof(packet->dst_ipaddr.ipaddr.ip6addr));
1754 case PW_PACKET_SRC_PORT:
1755 myvp = pairalloc(ctx, da);
1756 myvp->vp_integer = packet->src_port;
1760 case PW_PACKET_DST_PORT:
1761 myvp = pairalloc(ctx, da);
1762 myvp->vp_integer = packet->dst_port;
1768 * Fake various operations for virtual attributes.
1771 if (num != NUM_ANY) switch (num) {
1773 * [n] is NULL (we only have [0])
1778 * [*] means only one.
1784 * [#] means 1 (as there's only one)
1788 ret = talloc_strdup(ctx, "1");
1792 * [0] is fine (get the first instance)
1802 * We want the N'th VP.
1804 if (num != NUM_ANY) {
1810 * Return a count of the VPs.
1813 fr_cursor_init(&cursor, &vp);
1814 while (fr_cursor_next_by_da(&cursor, da, tag) != NULL) {
1817 return talloc_typed_asprintf(ctx, "%d", count);
1820 * Ugly, but working.
1826 (void) fr_cursor_init(&cursor, &vp);
1827 vp = fr_cursor_next_by_da(&cursor, da, tag);
1828 if (!vp) return NULL;
1830 p = vp_aprint_value(ctx, vp);
1831 if (!p) return NULL;
1832 while ((vp = fr_cursor_next_by_da(&cursor, da, tag)) != NULL) {
1833 q = vp_aprint_value(ctx, vp);
1834 if (!q) return NULL;
1835 p = talloc_strdup_append(p, ",");
1836 p = talloc_strdup_append(p, q);
1843 fr_cursor_init(&cursor, &vp);
1844 while ((vp = fr_cursor_next_by_da(&cursor, da, tag)) != NULL) {
1845 if (count++ == num) break;
1852 if (return_null) return NULL;
1853 return vp_aprint_type(ctx, da->type);
1857 ret = vp_aprint_value(ctx, vp);
1865 static const char *xlat_spaces = " ";
1868 static char *xlat_aprint(TALLOC_CTX *ctx, REQUEST *request, xlat_exp_t const * const node,
1869 RADIUS_ESCAPE_STRING escape, void *escape_ctx, int lvl)
1872 char *str = NULL, *child;
1875 XLAT_DEBUG("%.*sxlat aprint %d", lvl, xlat_spaces, node->type);
1877 switch (node->type) {
1879 * Don't escape this.
1882 XLAT_DEBUG("xlat_aprint LITERAL");
1883 return talloc_typed_strdup(ctx, node->fmt);
1886 * Do a one-character expansion.
1892 size_t freespace = 256;
1896 XLAT_DEBUG("xlat_aprint PERCENT");
1898 str = talloc_array(ctx, char, freespace); /* @todo do better allocation */
1901 when = request->timestamp;
1902 if (request->packet) {
1903 when = request->packet->timestamp.tv_sec;
1912 case 'd': /* request day */
1913 (void) localtime_r(&when, &ts);
1914 strftime(str, freespace, "%d", &ts);
1917 case 'l': /* request timestamp */
1918 snprintf(str, freespace, "%lu",
1919 (unsigned long) when);
1922 case 'm': /* request month */
1923 (void) localtime_r(&when, &ts);
1924 strftime(str, freespace, "%m", &ts);
1927 case 'n': /* Request Number*/
1928 snprintf(str, freespace, "%i", request->number);
1931 case 't': /* request timestamp */
1932 CTIME_R(&when, str, freespace);
1933 nl = strchr(str, '\n');
1937 case 'D': /* request date */
1938 (void) localtime_r(&when, &ts);
1939 strftime(str, freespace, "%Y%m%d", &ts);
1942 case 'G': /* request minute */
1943 (void) localtime_r(&when, &ts);
1944 strftime(str, freespace, "%M", &ts);
1947 case 'H': /* request hour */
1948 (void) localtime_r(&when, &ts);
1949 strftime(str, freespace, "%H", &ts);
1952 case 'I': /* Request ID */
1953 if (request->packet) {
1954 snprintf(str, freespace, "%i", request->packet->id);
1958 case 'S': /* request timestamp in SQL format*/
1959 (void) localtime_r(&when, &ts);
1960 strftime(str, freespace, "%Y-%m-%d %H:%M:%S", &ts);
1963 case 'T': /* request timestamp */
1964 (void) localtime_r(&when, &ts);
1965 strftime(str, freespace, "%Y-%m-%d-%H.%M.%S.000000", &ts);
1968 case 'Y': /* request year */
1969 (void) localtime_r(&when, &ts);
1970 strftime(str, freespace, "%Y", &ts);
1973 case 'v': /* Version of code */
1974 snprintf(str, freespace, "%s", radiusd_short_version);
1984 case XLAT_ATTRIBUTE:
1985 XLAT_DEBUG("xlat_aprint ATTRIBUTE");
1987 if (radius_request(&ref, node->ref) < 0) {
1992 * Some attributes are virtual <sigh>
1994 str = xlat_getvp(ctx, ref, node->list, node->da, node->tag, node->num, true);
1996 XLAT_DEBUG("EXPAND attr %s", node->da->name);
1997 XLAT_DEBUG(" ---> %s", str);
2002 XLAT_DEBUG("xlat_aprint VIRTUAL");
2003 str = talloc_array(ctx, char, 1024); /* FIXME: have the module call talloc_typed_asprintf */
2004 rcode = node->xlat->func(node->xlat->instance, request, NULL, str, 1024);
2012 XLAT_DEBUG("xlat_aprint MODULE");
2013 if (xlat_process(&child, request, node->child, node->xlat->escape, node->xlat->instance) == 0) {
2017 XLAT_DEBUG("%.*sEXPAND mod %s %s", lvl, xlat_spaces, node->fmt, node->child->fmt);
2018 XLAT_DEBUG("%.*s ---> %s", lvl, xlat_spaces, child);
2020 str = talloc_array(ctx, char, 1024); /* FIXME: have the module call talloc_typed_asprintf */
2021 *str = '\0'; /* Be sure the string is NULL terminated, we now only free on error */
2023 rcode = node->xlat->func(node->xlat->instance, request, child, str, 1024);
2033 XLAT_DEBUG("xlat_aprint REGEX");
2034 child = request_data_reference(request, request,
2035 REQUEST_DATA_REGEX | node->num);
2036 if (!child) return NULL;
2038 str = talloc_typed_strdup(ctx, child);
2042 case XLAT_ALTERNATE:
2043 XLAT_DEBUG("xlat_aprint ALTERNATE");
2044 rad_assert(node->child != NULL);
2045 rad_assert(node->alternate != NULL);
2047 str = xlat_aprint(ctx, request, node->child, escape, escape_ctx, lvl);
2050 str = xlat_aprint(ctx, request, node->alternate, escape, escape_ctx, lvl);
2056 * Escape the non-literals we found above.
2058 if (str && escape) {
2061 escaped = talloc_array(ctx, char, 1024); /* FIXME: do something intelligent */
2062 escape(request, escaped, 1024, str, escape_ctx);
2071 static size_t xlat_process(char **out, REQUEST *request, xlat_exp_t const * const head,
2072 RADIUS_ESCAPE_STRING escape, void *escape_ctx)
2076 char **array, *answer;
2077 xlat_exp_t const *node;
2082 * There are no nodes to process, so the result is a zero
2086 *out = talloc_zero_array(request, char, 1);
2091 * Hack for speed. If it's one expansion, just allocate
2092 * that and return, instead of allocating an intermediary
2097 * Pass the MAIN escape function. Recursive
2098 * calls will call node-specific escape
2101 answer = xlat_aprint(request, request, head, escape, escape_ctx, 0);
2103 *out = talloc_zero_array(request, char, 1);
2107 return strlen(answer);
2110 list = 0; /* FIXME: calculate this once */
2111 for (node = head; node != NULL; node = node->next) {
2115 array = talloc_array(request, char *, list);
2116 if (!array) return -1;
2118 for (node = head, i = 0; node != NULL; node = node->next, i++) {
2119 array[i] = xlat_aprint(array, request, node, escape, escape_ctx, 0); /* may be NULL */
2123 for (i = 0; i < list; i++) {
2124 if (array[i]) total += strlen(array[i]); /* FIXME: calculate strlen once */
2129 *out = talloc_zero_array(request, char, 1);
2133 answer = talloc_array(request, char, total + 1);
2136 for (i = 0; i < list; i++) {
2140 len = strlen(array[i]);
2141 memcpy(answer + total, array[i], len);
2145 answer[total] = '\0';
2146 talloc_free(array); /* and child entries */
2153 /** Replace %whatever in a string.
2155 * See 'doc/variables.txt' for more information.
2157 * @param[out] out Where to write pointer to output buffer.
2158 * @param[in] outlen Size of out.
2159 * @param[in] request current request.
2160 * @param[in] node the xlat structure to expand
2161 * @param[in] escape function to escape final value e.g. SQL quoting.
2162 * @param[in] escape_ctx pointer to pass to escape function.
2163 * @return length of string written @bug should really have -1 for failure
2165 static ssize_t xlat_expand_struct(char **out, size_t outlen, REQUEST *request, xlat_exp_t const *node,
2166 RADIUS_ESCAPE_STRING escape, void *escape_ctx)
2171 rad_assert(node != NULL);
2173 len = xlat_process(&buff, request, node, escape, escape_ctx);
2175 if ((len < 0) || !buff) {
2176 rad_assert(buff == NULL);
2177 if (*out) *out[0] = '\0';
2184 strlcpy(*out, buff, outlen);
2188 return strlen(*out);
2191 static ssize_t xlat_expand(char **out, size_t outlen, REQUEST *request, char const *fmt,
2192 RADIUS_ESCAPE_STRING escape, void *escape_ctx) CC_HINT(nonnull (1, 3, 4));
2194 /** Replace %whatever in a string.
2196 * See 'doc/variables.txt' for more information.
2198 * @param[out] out Where to write pointer to output buffer.
2199 * @param[in] outlen Size of out.
2200 * @param[in] request current request.
2201 * @param[in] fmt string to expand.
2202 * @param[in] escape function to escape final value e.g. SQL quoting.
2203 * @param[in] escape_ctx pointer to pass to escape function.
2204 * @return length of string written @bug should really have -1 for failure
2206 static ssize_t CC_HINT(nonnull (1, 3, 4)) xlat_expand(char **out, size_t outlen, REQUEST *request, char const *fmt,
2207 RADIUS_ESCAPE_STRING escape, void *escape_ctx)
2213 * Give better errors than the old code.
2215 len = xlat_tokenize_request(request, fmt, &node);
2220 *out = talloc_zero_array(request, char, 1);
2226 if (*out) *out[0] = '\0';
2230 len = xlat_expand_struct(out, outlen, request, node, escape, escape_ctx);
2233 RDEBUG2("EXPAND %s", fmt);
2234 RDEBUG2(" --> %s", *out);
2240 * Try to convert an xlat to a tmpl for efficiency
2242 value_pair_tmpl_t *radius_xlat2tmpl(TALLOC_CTX *ctx, xlat_exp_t *xlat)
2244 value_pair_tmpl_t *vpt;
2246 if (xlat->next || (xlat->type != XLAT_ATTRIBUTE)) return NULL;
2249 * @todo it should be possible to emulate the concat and count operations in the
2252 if ((xlat->num == NUM_COUNT) || (xlat->num == NUM_JOIN)) return NULL;
2254 vpt = talloc(ctx, value_pair_tmpl_t);
2255 if (!vpt) return NULL;
2257 vpt->type = VPT_TYPE_ATTR;
2258 vpt->name = talloc_strdup(vpt, xlat->fmt);
2259 vpt->vpt_request = xlat->ref;
2260 vpt->vpt_list = xlat->list;
2261 vpt->vpt_da = xlat->da;
2262 vpt->vpt_num = xlat->num;
2263 vpt->vpt_tag = xlat->tag;
2268 ssize_t radius_xlat(char *out, size_t outlen, REQUEST *request, char const *fmt, RADIUS_ESCAPE_STRING escape, void *ctx)
2270 return xlat_expand(&out, outlen, request, fmt, escape, ctx);
2273 ssize_t radius_axlat(char **out, REQUEST *request, char const *fmt, RADIUS_ESCAPE_STRING escape, void *ctx)
2275 return xlat_expand(out, 0, request, fmt, escape, ctx);
2278 ssize_t radius_axlat_struct(char **out, REQUEST *request, xlat_exp_t const *xlat, RADIUS_ESCAPE_STRING escape, void *ctx)
2280 return xlat_expand_struct(out, 0, request, xlat, escape, ctx);