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; }
static ssize_t xlat_tokenize_expansion(TALLOC_CTX *ctx, char *fmt, xlat_exp_t **head, char const **error) { ssize_t slen; char *p, *q; char *start; xlat_exp_t *node; #ifdef HAVE_REGEX long num; #endif 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); node->fmt = start = talloc_typed_strdup(node, fmt + 2); node->len = 0; #ifdef HAVE_REGEX /* * Handle regex's specially. */ p = start; num = strtol(p, &q, 10); if (p != q && (*q == '}')) { XLAT_DEBUG("REGEX <-- %s", fmt); *q = '\0'; if ((num > REQUEST_MAX_REGEX) || (num < 0)) { talloc_free(node); *error = "Invalid regex reference. Must be in range 0-" STRINGIFY(REQUEST_MAX_REGEX); return -2; /* error */ } node->regex_index = num; node->type = XLAT_REGEX; *head = node; node->len = (q - start); MEM(start = talloc_realloc_bstr(start, node->len)); q++; /* Skip closing brace */ return 2 + (q - start); } #endif /* HAVE_REGEX */ /* * %{Attr-Name} * %{Attr-Name[#]} * %{Tunnel-Password:1} * %{Tunnel-Password:1[#]} * %{request:Attr-Name} * %{request:Tunnel-Password:1} * %{request:Tunnel-Password:1[#]} * %{mod:foo} */ /* * This is for efficiency, so we don't search for an xlat, * when what's being referenced is obviously an attribute. */ p = start; for (q = p; *q != '\0'; q++) { if (*q == ':') break; if (isspace((int) *q)) break; if (*q == '[') continue; if (*q == '}') break; } /* * Check for empty expressions %{} */ if ((*q == '}') && (q == p)) { talloc_free(node); *error = "Empty expression is invalid"; return (-(p - start)) - 2; /* error */ } /* * Might be a module name reference. * * If it's not, it's an attribute or parse error. */ if (*q == ':') { *q = '\0'; node->xlat = xlat_func_find(node->fmt); if (node->xlat) { /* * %{mod:foo} */ node->type = XLAT_FUNC; p = q + 1; XLAT_DEBUG("MOD <-- %s ... %s", node->fmt, p); slen = xlat_tokenize_literal(node, p, &node->child, true, error); if (slen < 0) { talloc_free(node); return (slen - (p - start)) - 2; /* error */ } p += slen; node->async_safe = (node->xlat->async_safe && node->child->async_safe); *head = node; rad_assert(node->next == NULL); node->len = p - start; MEM(start = talloc_realloc_bstr(start, node->len)); return 2 + node->len; } *q = ':'; /* Avoids a talloc_strdup */ } /* * The first token ends with: * - '[' - Which is an attribute index, so it must be an attribute. * - '}' - The end of the expansion, which means it was a bareword. */ slen = tmpl_afrom_attr_substr(node, &node->attr, p, &(vp_tmpl_rules_t){ .allow_undefined = true, .allow_unknown = true });