static ssize_t xlat_tokenize_expansion(TALLOC_CTX *ctx, char *fmt, xlat_exp_t **head, char const **error) { ssize_t slen; char *p, *q, *brace; char const *attrname; xlat_exp_t *node; rad_assert(fmt[0] == '%'); rad_assert(fmt[1] == '{'); /* * %{%{...}:-bar} */ if ((fmt[2] == '%') && (fmt[3] == '{')) { return xlat_tokenize_alternation(ctx, fmt, head, error); } XLAT_DEBUG("EXPANSION: %s", fmt); node = talloc_zero(ctx, xlat_exp_t); attrname = node->fmt = fmt + 2; node->len = 0; #ifdef HAVE_REGEX_H /* * Handle regex's specially. */ if (isdigit((int) fmt[2]) && (fmt[3] == '}')) { if (fmt[2] == '9') { talloc_free(node); *error = "Invalid regex reference"; return -2; } XLAT_DEBUG("REGEX: %s", fmt); fmt[3] = '\0'; node->num = fmt[2] - '0'; /* ASCII */ node->type = XLAT_REGEX; *head = node; return 4; } #endif /* HAVE_REGEX_H */ /* * %{Attr-Name} * %{Attr-Name[#]} * %{Tunnel-Password:1} * %{Tunnel-Password:1[#]} * %{request:Attr-Name} * %{request:Tunnel-Password:1} * %{request:Tunnel-Password:1[#]} * %{mod:foo} */ q = brace = NULL; for (p = fmt + 2; *p != '\0'; p++) { if (*p == ':') break; if (isspace((int) *p)) break; if (*p == '[') break; if (*p == '}') break; } if (*p != ':') p = NULL; /* * Might be a module name reference. */ if (p) { *p = '\0'; /* * %{mod:foo} */ node->xlat = xlat_find(node->fmt); if (node->xlat) { node->type = XLAT_MODULE; XLAT_DEBUG("MOD: %s --> %s", node->fmt, p); slen = xlat_tokenize_literal(node, p + 1, &node->child, true, error); if (slen <= 0) { talloc_free(node); return slen - (p - fmt); } p += slen + 1; *head = node; rad_assert(node->next == NULL); return p - fmt; } /* * Modules can have '}' in their RHS, so we * didn't check for that until now. * * As of now, node->fmt MUST be a reference to an * attribute, however complicated. So it MUST have a closing brace. */ brace = strchr(p + 1, '}'); if (!brace) goto no_brace; *brace = '\0'; /* * %{User-Name} * %{User-Name[1]} * %{Tunnel-Password:1} * %{request:Tunnel-Password:1} * * <sigh> The syntax is fairly poor. */ XLAT_DEBUG("Looking for list in '%s'", attrname); /* * Not a module. Has to be an attribute * reference. * * As of v3, we've removed %{request: ..>} as * internally registered xlats. */ *p = ':'; node->ref = radius_request_name(&attrname, REQUEST_CURRENT); rad_assert(node->ref != REQUEST_UNKNOWN); node->list = radius_list_name(&attrname, PAIR_LIST_REQUEST); if (node->list == PAIR_LIST_UNKNOWN) { talloc_free(node); *error = "Unknown module"; return -2; } /* * Check for a trailing tag. */ p = strchr(attrname, ':'); if (p) *p = '\0'; } else { brace = strchr(attrname, '}'); if (!brace) { no_brace: talloc_free(node); *error = "No matching closing brace"; return -1; /* second character of format string */ } *brace = '\0'; node->ref = REQUEST_CURRENT; node->list = PAIR_LIST_REQUEST; } *brace = '\0'; XLAT_DEBUG("Looking for attribute name in %s", attrname); /* * Allow for an array reference. They come AFTER the * tag, if the tag exists. Otherwise, they come after * the attribute name. */ if (p) { q = strchr(p + 1, '['); } else { q = strchr(attrname, '['); } if (q) *(q++) = '\0'; if (!*attrname) { talloc_free(node); *error = "Empty expression is invalid"; return -(attrname - fmt); } /* * It's either an attribute name, or a Tunnel-Password:TAG * with the ':' already set to NULL. */ node->da = dict_attrbyname(attrname); if (!node->da) { /* * Foreach. Maybe other stuff, too. */ node->xlat = xlat_find(attrname); if (node->xlat) { node->type = XLAT_VIRTUAL; node->fmt = attrname; XLAT_DEBUG("VIRTUAL: %s", node->fmt); *head = node; rad_assert(node->next == NULL); brace++; return brace - fmt; } talloc_free(node); *error = "Unknown attribute"; return -(attrname - fmt); } /* * Parse the tag. */ if (p) { unsigned long tag; char *end; if (!node->da->flags.has_tag) { talloc_free(node); *error = "Attribute cannot have a tag"; return - (p - fmt); } tag = strtoul(p + 1, &end, 10); p++; if (tag == ULONG_MAX) { talloc_free(node); *error = "Invalid tag value"; return - (p - fmt); } node->tag = tag; p = end; if (*p) { talloc_free(node); *error = "Unexpected text after tag"; return - (p - fmt); } } else { node->tag = TAG_ANY; /* leave p alone */ } /* * Check for array reference */ if (q) { unsigned long num; char *end; p = q; if (*p== '#') { node->num = 65536; p++; } else if (*p == '*') { node->num = 65537; p++; } else if (isdigit((int) *p)) { num = strtoul(p, &end, 10); if ((num == ULONG_MAX) || (num > 65535)) { talloc_free(node); *error = "Invalid number"; return - (p - fmt); } p = end; DEBUG("END %s", p); node->num = num; } else { talloc_free(node); *error = "Invalid array reference"; return - (p - fmt); } if (*p != ']') { talloc_free(node); *error = "Expected ']'"; return - (p - fmt); } p++; if (*p) { talloc_free(node); *error = "Unexpected text after array reference"; return - (p - fmt); } } rad_assert(!p || (p == brace)); node->type = XLAT_ATTRIBUTE; p = brace + 1; *head = node; rad_assert(node->next == NULL); return p - fmt; }
/** Register an xlat function. * * @param[in] name xlat name. * @param[in] func xlat function to be called. * @param[in] escape function to sanitize any sub expansions passed to the xlat function. * @param[in] instance of module that's registering the xlat function. * @return 0 on success, -1 on failure */ int xlat_register(char const *name, RAD_XLAT_FUNC func, RADIUS_ESCAPE_STRING escape, void *instance) { xlat_t *c; xlat_t my_xlat; rbnode_t *node; if (!name || !*name) { DEBUG("xlat_register: Invalid xlat name"); return -1; } /* * First time around, build up the tree... * * FIXME: This code should be hoisted out of this function, * and into a global "initialization". But it isn't critical... */ if (!xlat_root) { #ifdef WITH_UNLANG int i; #endif xlat_root = rbtree_create(xlat_cmp, NULL, 0); if (!xlat_root) { DEBUG("xlat_register: Failed to create tree"); return -1; } #ifdef WITH_UNLANG for (i = 0; xlat_foreach_names[i] != NULL; i++) { xlat_register(xlat_foreach_names[i], xlat_foreach, NULL, &xlat_inst[i]); c = xlat_find(xlat_foreach_names[i]); rad_assert(c != NULL); c->internal = true; } #endif #define XLAT_REGISTER(_x) xlat_register(STRINGIFY(_x), xlat_ ## _x, NULL, NULL); \ c = xlat_find(STRINGIFY(_x)); \ rad_assert(c != NULL); \ c->internal = true XLAT_REGISTER(integer); XLAT_REGISTER(strlen); XLAT_REGISTER(length); XLAT_REGISTER(hex); XLAT_REGISTER(string); XLAT_REGISTER(xlat); XLAT_REGISTER(module); XLAT_REGISTER(debug_attr); xlat_register("debug", xlat_debug, NULL, &xlat_inst[0]); c = xlat_find("debug"); rad_assert(c != NULL); c->internal = true; } /* * If it already exists, replace the instance. */ strlcpy(my_xlat.name, name, sizeof(my_xlat.name)); my_xlat.length = strlen(my_xlat.name); c = rbtree_finddata(xlat_root, &my_xlat); if (c) { if (c->internal) { DEBUG("xlat_register: Cannot re-define internal xlat"); return -1; } c->func = func; c->escape = escape; c->instance = instance; return 0; } /* * Doesn't exist. Create it. */ c = talloc_zero(xlat_root, xlat_t); c->func = func; c->escape = escape; strlcpy(c->name, name, sizeof(c->name)); c->length = strlen(c->name); c->instance = instance; node = rbtree_insert_node(xlat_root, c); if (!node) { talloc_free(c); return -1; } /* * Ensure that the data is deleted when the node is * deleted. * * @todo: Maybe this should be the other way around... * when a thing IN the tree is deleted, it's automatically * removed from the tree. But for now, this works. */ (void) talloc_steal(node, c); return 0; }
/** * @brief Register an xlat function. * * @param module xlat name * @param func xlat function to be called * @param instance argument to xlat function * @return 0 on success, -1 on failure */ int xlat_register(const char *module, RAD_XLAT_FUNC func, void *instance) { xlat_t *c; xlat_t my_xlat; if (!module || !*module) { DEBUG("xlat_register: Invalid module name"); return -1; } /* * First time around, build up the tree... * * FIXME: This code should be hoisted out of this function, * and into a global "initialization". But it isn't critical... */ if (!xlat_root) { int i; #ifdef HAVE_REGEX_H char buffer[2]; #endif xlat_root = rbtree_create(xlat_cmp, free, 0); if (!xlat_root) { DEBUG("xlat_register: Failed to create tree."); return -1; } /* * Register the internal packet xlat's. */ for (i = 0; internal_xlat[i] != NULL; i++) { xlat_register(internal_xlat[i], xlat_packet, &xlat_inst[i]); c = xlat_find(internal_xlat[i]); rad_assert(c != NULL); c->internal = TRUE; } #ifdef WITH_UNLANG for (i = 0; xlat_foreach_names[i] != NULL; i++) { xlat_register(xlat_foreach_names[i], xlat_foreach, &xlat_inst[i]); c = xlat_find(xlat_foreach_names[i]); rad_assert(c != NULL); c->internal = TRUE; } #endif /* * New name: "control" */ xlat_register("control", xlat_packet, &xlat_inst[0]); c = xlat_find("control"); rad_assert(c != NULL); c->internal = TRUE; #define XLAT_REGISTER(_x) xlat_register(Stringify(_x), xlat_ ## _x, NULL); \ c = xlat_find(Stringify(_x)); \ rad_assert(c != NULL); \ c->internal = TRUE XLAT_REGISTER(integer); XLAT_REGISTER(hex); XLAT_REGISTER(base64); XLAT_REGISTER(string); XLAT_REGISTER(module); #ifdef HAVE_REGEX_H /* * Register xlat's for regexes. */ buffer[1] = '\0'; for (i = 0; i <= REQUEST_MAX_REGEX; i++) { buffer[0] = '0' + i; xlat_register(buffer, xlat_regex, &xlat_inst[i]); c = xlat_find(buffer); rad_assert(c != NULL); c->internal = TRUE; } #endif /* HAVE_REGEX_H */ xlat_register("debug", xlat_debug, &xlat_inst[0]); c = xlat_find("debug"); rad_assert(c != NULL); c->internal = TRUE; } /* * If it already exists, replace the instance. */ strlcpy(my_xlat.module, module, sizeof(my_xlat.module)); my_xlat.length = strlen(my_xlat.module); c = rbtree_finddata(xlat_root, &my_xlat); if (c) { if (c->internal) { DEBUG("xlat_register: Cannot re-define internal xlat"); return -1; } c->do_xlat = func; c->instance = instance; return 0; } /* * Doesn't exist. Create it. */ c = rad_malloc(sizeof(*c)); memset(c, 0, sizeof(*c)); c->do_xlat = func; strlcpy(c->module, module, sizeof(c->module)); c->length = strlen(c->module); c->instance = instance; rbtree_insert(xlat_root, c); return 0; }
/** * @brief Decode an attribute name into a string. * * This expands the various formats: * - %{Name} * - %{xlat:name} * - %{Name:-Other} * * calls radius_xlat() to do most of the work * * @param from string to expand * @param to buffer for output * @param freespace space remaining in output buffer * @param request current server request * @param func optional function to escape output; passed to radius_xlat() * @return 0 on success, -1 on failure */ static int decode_attribute(const char **from, char **to, int freespace, REQUEST *request, RADIUS_ESCAPE_STRING func, void *funcarg) { int do_length = 0; const char *module_name, *xlat_str; char *p, *q, *l, *next = NULL; int retlen=0; const xlat_t *c; int varlen; char buffer[8192]; q = *to; *q = '\0'; /* * Copy the input string to an intermediate buffer where * we can mangle it. */ varlen = rad_copy_variable(buffer, *from); if (varlen < 0) { RDEBUG2("ERROR: Badly formatted variable: %s", *from); return -1; } *from += varlen; /* * Kill the %{} around the data we are looking for. */ p = buffer; p[varlen - 1] = '\0'; /* */ p += 2; if (*p == '#') { p++; do_length = 1; } /* * Handle %{%{foo}:-%{bar}}, which is useful, too. * * Did I mention that this parser is garbage? */ if ((p[0] == '%') && (p[1] == '{')) { int len1, len2; int expand2 = FALSE; /* * 'p' is after the start of 'buffer', so we can * safely do this. */ len1 = rad_copy_variable(buffer, p); if (len1 < 0) { RDEBUG2("ERROR: Badly formatted variable: %s", p); return -1; } /* * They did %{%{foo}}, which is stupid, but allowed. */ if (!p[len1]) { RDEBUG2("Improperly nested variable; %%{%s}", p); return -1; } /* * It SHOULD be %{%{foo}:-%{bar}}. If not, it's * an error. */ if ((p[len1] != ':') || (p[len1 + 1] != '-')) { RDEBUG2("No trailing :- after variable at %s", p); return -1; } /* * Parse the second bit. The second bit can be * either %{foo}, or a string "foo", or a string * 'foo', or just a bare word: foo */ p += len1 + 2; l = buffer + len1 + 1; if ((p[0] == '%') && (p[1] == '{')) { len2 = rad_copy_variable(l, p); if (len2 < 0) { RDEBUG2("ERROR: Invalid text after :- at %s", p); return -1; } p += len2; expand2 = TRUE; } else if ((p[0] == '"') || p[0] == '\'') { getstring((const char **) &p, l, strlen(l)); } else { l = p; } /* * Expand the first one. If we did, exit the * conditional. */ retlen = radius_xlat(q, freespace, buffer, request, func, funcarg); if (retlen) { q += retlen; goto done; } RDEBUG2("\t... expanding second conditional"); /* * Expand / copy the second string if required. */ if (expand2) { retlen = radius_xlat(q, freespace, l, request, func, funcarg); if (retlen) { q += retlen; } } else { strlcpy(q, l, freespace); q += strlen(q); } /* * Else the output is an empty string. */ goto done; } /* * See if we're supposed to expand a module name. */ module_name = NULL; for (l = p; *l != '\0'; l++) { /* * module:string */ if (*l == ':') { module_name = p; /* start of name */ *l = '\0'; p = l + 1; break; } /* * Module names can't have spaces. */ if ((*l == ' ') || (*l == '\t')) break; } /* * %{name} is a simple attribute reference, * or regex reference. */ if (!module_name) { if (isdigit(*p) && !p[1]) { /* regex 0..8 */ module_name = xlat_str = p; } else { xlat_str = p; } goto do_xlat; } /* * Maybe it's the old-style %{foo:-bar} */ if (*p == '-') { RDEBUG2("WARNING: Deprecated conditional expansion \":-\". See \"man unlang\" for details"); p++; xlat_str = module_name; next = p; goto do_xlat; } /* * FIXME: For backwards "WTF" compatibility, check for * {...}, (after the :), and copy that, too. */ /* module name, followed by (possibly) per-module string */ xlat_str = p; do_xlat: /* * Just "foo". Maybe it's a magic attr, which doesn't * really exist. * * If we can't find that, then assume it's a dictionary * attribute in the request. * * Else if it's module:foo, look for module, and pass it "foo". */ if (!module_name) { c = xlat_find(xlat_str); if (!c) c = xlat_find("request"); } else { c = xlat_find(module_name); } if (!c) { if (!module_name) { RDEBUG2("WARNING: Unknown Attribute \"%s\" in string expansion \"%%%s\"", xlat_str, *from); } else { RDEBUG2("WARNING: Unknown module \"%s\" in string expansion \"%%%s\"", module_name, *from); } return -1; } if (!c->internal) RDEBUG3("radius_xlat: Running registered xlat function of module %s for string \'%s\'", c->module, xlat_str); if (func) { /* xlat to a temporary buffer, then escape */ char tmpbuf[8192]; retlen = c->do_xlat(c->instance, request, xlat_str, tmpbuf, sizeof(tmpbuf)); if (retlen > 0) { retlen = func(request, q, freespace, tmpbuf, funcarg); if (retlen > 0) { RDEBUG2("string escaped from \'%s\' to \'%s\'", tmpbuf, q); } else if (retlen < 0) { RDEBUG2("string escape failed"); } } } else { retlen = c->do_xlat(c->instance, request, xlat_str, q, freespace); } if (retlen > 0) { if (do_length) { snprintf(q, freespace, "%d", retlen); retlen = strlen(q); } } else if (next) { /* * Expand the second bit. */ RDEBUG2("\t... expanding second conditional"); retlen = radius_xlat(q, freespace, next, request, func, funcarg); } q += retlen; done: *to = q; return 0; }
/** Register an xlat function. * * @param[in] name xlat name. * @param[in] func xlat function to be called. * @param[in] escape function to sanitize any sub expansions passed to the xlat function. * @param[in] instance of module that's registering the xlat function. * @return 0 on success, -1 on failure */ int xlat_register(char const *name, RAD_XLAT_FUNC func, RADIUS_ESCAPE_STRING escape, void *instance) { xlat_t *c; xlat_t my_xlat; if (!name || !*name) { DEBUG("xlat_register: Invalid xlat name"); return -1; } /* * First time around, build up the tree... * * FIXME: This code should be hoisted out of this function, * and into a global "initialization". But it isn't critical... */ if (!xlat_root) { int i; xlat_root = rbtree_create(xlat_cmp, free, 0); if (!xlat_root) { DEBUG("xlat_register: Failed to create tree"); return -1; } #ifdef WITH_UNLANG for (i = 0; xlat_foreach_names[i] != NULL; i++) { xlat_register(xlat_foreach_names[i], xlat_foreach, NULL, &xlat_inst[i]); c = xlat_find(xlat_foreach_names[i]); rad_assert(c != NULL); c->internal = true; } #endif #define XLAT_REGISTER(_x) xlat_register(STRINGIFY(_x), xlat_ ## _x, NULL, NULL); \ c = xlat_find(STRINGIFY(_x)); \ rad_assert(c != NULL); \ c->internal = true XLAT_REGISTER(integer); XLAT_REGISTER(strlen); XLAT_REGISTER(length); XLAT_REGISTER(hex); XLAT_REGISTER(base64); XLAT_REGISTER(string); XLAT_REGISTER(xlat); XLAT_REGISTER(module); XLAT_REGISTER(debug_attr); xlat_register("debug", xlat_debug, NULL, &xlat_inst[0]); c = xlat_find("debug"); rad_assert(c != NULL); c->internal = true; } /* * If it already exists, replace the instance. */ strlcpy(my_xlat.name, name, sizeof(my_xlat.name)); my_xlat.length = strlen(my_xlat.name); c = rbtree_finddata(xlat_root, &my_xlat); if (c) { if (c->internal) { DEBUG("xlat_register: Cannot re-define internal xlat"); return -1; } c->func = func; c->escape = escape; c->instance = instance; return 0; } /* * Doesn't exist. Create it. */ c = rad_malloc(sizeof(*c)); memset(c, 0, sizeof(*c)); c->func = func; c->escape = escape; strlcpy(c->name, name, sizeof(c->name)); c->length = strlen(c->name); c->instance = instance; rbtree_insert(xlat_root, c); return 0; }