/** Convert module specific attribute id to value_pair_tmpl_t. * * @param[in] ctx for talloc * @param[in] name string to convert. * @param[in] type Type of quoting around value. * @return pointer to new VPT. */ value_pair_tmpl_t *radius_str2tmpl(TALLOC_CTX *ctx, char const *name, FR_TOKEN type) { value_pair_tmpl_t *vpt; vpt = talloc_zero(ctx, value_pair_tmpl_t); vpt->name = talloc_strdup(vpt, name); switch (type) { case T_BARE_WORD: if (!isdigit((int) *name)) { request_refs_t ref; pair_lists_t list; const char *p = name; ref = radius_request_name(&p, REQUEST_CURRENT); list = radius_list_name(&p, PAIR_LIST_REQUEST); if ((p != name) && !*p) { vpt->type = VPT_TYPE_LIST; } else { const DICT_ATTR *da; da = dict_attrbyname(p); if (!da) { vpt->type = VPT_TYPE_LITERAL; break; } vpt->da = da; vpt->type = VPT_TYPE_ATTR; } vpt->request = ref; vpt->list = list; break; } /* FALL-THROUGH */ case T_SINGLE_QUOTED_STRING: vpt->type = VPT_TYPE_LITERAL; break; case T_DOUBLE_QUOTED_STRING: vpt->type = VPT_TYPE_XLAT; break; case T_BACK_QUOTED_STRING: vpt->type = VPT_TYPE_EXEC; break; case T_OP_REG_EQ: /* hack */ vpt->type = VPT_TYPE_REGEX; break; default: rad_assert(0); return NULL; } return vpt; }
/** Parse qualifiers to convert attrname into a value_pair_tmpl_t. * * VPTs are used in various places where we need to pre-parse configuration * sections into attribute mappings. * * Note: name field is just a copy of the input pointer, if you know that * string might be freed before you're done with the vpt use radius_attr2tmpl * instead. * * @param[in] name attribute name including qualifiers. * @param[out] vpt to modify. * @param[in] request_def The default request to insert unqualified * attributes into. * @param[in] list_def The default list to insert unqualified attributes into. * @return -1 on error or 0 on success. */ int radius_parse_attr(const char *name, value_pair_tmpl_t *vpt, request_refs_t request_def, pair_lists_t list_def) { char buffer[128]; const char *p; size_t len; vpt->name = name; p = name; vpt->request = radius_request_name(&p, request_def); len = p - name; if (vpt->request == REQUEST_UNKNOWN) { strlcpy(buffer, name, len < sizeof(buffer) ? len + 1 : sizeof(buffer)); radlog(L_ERR, "Invalid request qualifier \"%s\"", buffer); return -1; } name += len; vpt->list = radius_list_name(&p, list_def); if (vpt->list == PAIR_LIST_UNKNOWN) { len = p - name; strlcpy(buffer, name, len < sizeof(buffer) ? len + 1 : sizeof(buffer)); radlog(L_ERR, "Invalid list qualifier \"%s\"", buffer); return -1; } if (*p == '\0') { vpt->type = VPT_TYPE_LIST; return 0; } vpt->da = dict_attrbyname(p); if (!vpt->da) { radlog(L_ERR, "Attribute \"%s\" unknown", p); return -1; } vpt->type = VPT_TYPE_ATTR; return 0; }
/** Convert a valuepair string to VALUE_PAIR and insert it into a list * * Takes a valuepair string with list and request qualifiers, converts it into a VALUE_PAIR * and inserts it into the appropriate list. * * @param request Current request. * @param raw string to parse. * @param request_def to use if attribute isn't qualified. * @param list_def to use if attribute isn't qualified. * @return 0 on success, -1 on error. */ int radius_str2vp(REQUEST *request, char const *raw, request_refs_t request_def, pair_lists_t list_def) { char const *p; size_t len; request_refs_t req; pair_lists_t list; VALUE_PAIR *vp = NULL; VALUE_PAIR **vps; p = raw; req = radius_request_name(&p, request_def); len = p - raw; if (req == REQUEST_UNKNOWN) { REDEBUG("Invalid request qualifier \"%.*s\"", (int) len, raw); return -1; } raw += len; list = radius_list_name(&p, list_def); if (list == PAIR_LIST_UNKNOWN) { len = p - raw; REDEBUG("Invalid list qualifier \"%.*s\"", (int) len, raw); return -1; } raw += len; if (radius_request(&request, req) < 0) { return -1; } vps = radius_list(request, list); if (!vps) { return -1; } if (userparse(request, raw, &vp) == T_OP_INVALID) { return -1; } pairmove(request, vps, &vp); return 0; }
/** Parse qualifiers to convert attrname into a value_pair_tmpl_t. * * VPTs are used in various places where we need to pre-parse configuration * sections into attribute mappings. * * Note: name field is just a copy of the input pointer, if you know that * string might be freed before you're done with the vpt use radius_attr2tmpl * instead. * * @param[in] name attribute name including qualifiers. * @param[out] vpt to modify. * @param[in] request_def The default request to insert unqualified * attributes into. * @param[in] list_def The default list to insert unqualified attributes into. * @return -1 on error or 0 on success. */ int radius_parse_attr(char const *name, value_pair_tmpl_t *vpt, request_refs_t request_def, pair_lists_t list_def) { DICT_ATTR const *da; char const *p; size_t len; memset(vpt, 0, sizeof(*vpt)); vpt->name = name; p = name; vpt->request = radius_request_name(&p, request_def); len = p - name; if (vpt->request == REQUEST_UNKNOWN) { ERROR("Invalid request qualifier \"%.*s\"", (int) len, name); return -1; } name += len; vpt->list = radius_list_name(&p, list_def); if (vpt->list == PAIR_LIST_UNKNOWN) { len = p - name; ERROR("Invalid list qualifier \"%.*s\"", (int) len, name); return -1; } if (*p == '\0') { vpt->type = VPT_TYPE_LIST; return 0; } da = dict_attrbyname(p); if (!da) { da = dict_attrunknownbyname(p, false); if (!da) { ERROR("Unknown attribute \"%s\"", p); return -1; } } vpt->da = da; vpt->type = VPT_TYPE_ATTR; return 0; }
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; }
/** Convert an 'update' config section into an attribute map. * * Uses 'name2' of section to set default request and lists. * * @param[in] cs the update section * @param[out] head Where to store the head of the map. * @param[in] dst_list_def The default destination list, usually dictated by * the section the module is being called in. * @param[in] src_list_def The default source list, usually dictated by the * section the module is being called in. * @param[in] max number of mappings to process. * @return -1 on error, else 0. */ int radius_attrmap(CONF_SECTION *cs, value_pair_map_t **head, pair_lists_t dst_list_def, pair_lists_t src_list_def, unsigned int max) { char const *cs_list, *p; request_refs_t request_def = REQUEST_CURRENT; CONF_ITEM *ci; CONF_PAIR *cp; unsigned int total = 0; value_pair_map_t **tail, *map; TALLOC_CTX *ctx; *head = NULL; tail = head; if (!cs) return 0; /* * The first map has cs as the parent. * The rest have the previous map as the parent. */ ctx = cs; ci = cf_sectiontoitem(cs); cs_list = p = cf_section_name2(cs); if (cs_list) { request_def = radius_request_name(&p, REQUEST_CURRENT); if (request_def == REQUEST_UNKNOWN) { cf_log_err(ci, "Default request specified " "in mapping section is invalid"); return -1; } dst_list_def = fr_str2int(pair_lists, p, PAIR_LIST_UNKNOWN); if (dst_list_def == PAIR_LIST_UNKNOWN) { cf_log_err(ci, "Default list \"%s\" specified " "in mapping section is invalid", p); return -1; } } for (ci = cf_item_find_next(cs, NULL); ci != NULL; ci = cf_item_find_next(cs, ci)) { if (total++ == max) { cf_log_err(ci, "Map size exceeded"); goto error; } if (!cf_item_is_pair(ci)) { cf_log_err(ci, "Entry is not in \"attribute = " "value\" format"); goto error; } cp = cf_itemtopair(ci); map = radius_cp2map(ctx, cp, request_def, dst_list_def, REQUEST_CURRENT, src_list_def); if (!map) { goto error; } ctx = *tail = map; tail = &(map->next); } return 0; error: talloc_free(*head); return -1; }
/** Convert an 'update' config section into an attribute map. * * Uses 'name2' of section to set default request and lists. * * @param[in] parent to convert to map. * @param[out] head Where to store the head of the map. * @param[in] dst_list_def The default destination list, usually dictated by * the section the module is being called in. * @param[in] src_list_def The default source list, usually dictated by the * section the module is being called in. * @param[in] max number of mappings to process. * @return -1 on error, else 0. */ int radius_attrmap(CONF_SECTION *parent, value_pair_map_t **head, pair_lists_t dst_list_def, pair_lists_t src_list_def, unsigned int max) { const char *cs_list, *p; request_refs_t request_def = REQUEST_CURRENT; CONF_SECTION *cs; CONF_ITEM *ci = cf_sectiontoitem(cs); CONF_PAIR *cp; unsigned int total = 0; value_pair_map_t **tail, *map; *head = NULL; tail = head; if (!parent) return 0; cs = cf_section_sub_find(parent, "update"); if (!cs) return 0; cs_list = p = cf_section_name2(cs); if (cs_list) { request_def = radius_request_name(&p, REQUEST_UNKNOWN); if (request_def == REQUEST_UNKNOWN) { cf_log_err(ci, "Default request specified " "in mapping section is invalid"); return -1; } dst_list_def = fr_str2int(pair_lists, p, PAIR_LIST_UNKNOWN); if (dst_list_def == PAIR_LIST_UNKNOWN) { cf_log_err(ci, "Default list \"%s\" specified " "in mapping section is invalid", p); return -1; } } for (ci = cf_item_find_next(cs, NULL); ci != NULL; ci = cf_item_find_next(cs, ci)) { if (total++ == max) { cf_log_err(ci, "Map size exceeded"); goto error; } if (!cf_item_is_pair(ci)) { cf_log_err(ci, "Entry is not in \"attribute = " "value\" format"); goto error; } cp = cf_itemtopair(ci); map = radius_cp2map(cp, request_def, dst_list_def, REQUEST_CURRENT, src_list_def); if (!map) { goto error; } *tail = map; tail = &(map->next); } return 0; error: radius_mapfree(head); return -1; }
/** Parse qualifiers to convert attrname into a value_pair_tmpl_t. * * VPTs are used in various places where we need to pre-parse configuration * sections into attribute mappings. * * Note: name field is just a copy of the input pointer, if you know that * string might be freed before you're done with the vpt use radius_attr2tmpl * instead. * * The special return code of -2 is used only by radius_str2tmpl, which allow * bare words which might (or might not) be an attribute reference. * * @param[out] vpt to modify. * @param[in] name attribute name including qualifiers. * @param[in] request_def The default request to insert unqualified attributes into. * @param[in] list_def The default list to insert unqualified attributes into. * @return -2 on partial parse followed by error, -1 on other error, or 0 on success */ int radius_parse_attr(value_pair_tmpl_t *vpt, char const *name, request_refs_t request_def, pair_lists_t list_def) { int error = -1; char const *p; size_t len; unsigned long num; char *q; DICT_ATTR const *da; memset(vpt, 0, sizeof(*vpt)); vpt->name = name; p = name; if (*p == '&') { error = -2; p++; } vpt->vpt_request = radius_request_name(&p, request_def); len = p - name; if (vpt->vpt_request == REQUEST_UNKNOWN) { fr_strerror_printf("Invalid request qualifier \"%.*s\"", (int) len, name); return error; } name += len; vpt->vpt_list = radius_list_name(&p, list_def); if (vpt->vpt_list == PAIR_LIST_UNKNOWN) { len = p - name; fr_strerror_printf("Invalid list qualifier \"%.*s\"", (int) len, name); return error; } if (*p == '\0') { vpt->type = VPT_TYPE_LIST; return 0; } da = dict_attrbytagged_name(p); if (!da) { da = dict_attrunknownbyname(p, false); if (!da) { fr_strerror_printf("Unknown attribute \"%s\"", p); return error; } } vpt->vpt_da = da; vpt->type = VPT_TYPE_ATTR; vpt->vpt_tag = TAG_ANY; vpt->vpt_num = NUM_ANY; /* * After this point, we return -2 to indicate that parts * of the string were parsed as an attribute, but others * weren't. */ while (*p) { if (*p == ':') break; if (*p == '[') break; p++; } if (*p == ':') { if (!da->flags.has_tag) { fr_strerror_printf("Attribute '%s' cannot have a tag", da->name); return -2; } num = strtoul(p + 1, &q, 10); if (num > 0x1f) { fr_strerror_printf("Invalid tag value '%u' (should be between 0-31)", (unsigned int) num); return -2; } vpt->vpt_tag = num; p = q; } if (!*p) return 0; if (*p != '[') { fr_strerror_printf("Unexpected text after tag in '%s'", name); return -2; } num = strtoul(p + 1, &q, 10); if (num > 1000) { fr_strerror_printf("Invalid array reference '%u' (should be between 0-1000)", (unsigned int) num); return -2; } if ((*q != ']') || (q[1] != '\0')) { fr_strerror_printf("Unexpected text after array in '%s'", name); return -2; } vpt->vpt_num = num; return 0; }
/** Convert an 'update' config section into an attribute map. * * Uses 'name2' of section to set default request and lists. * Copied from map_afrom_cs, except that list assignments can have the RHS * be a bare word. * * @param[in] cs the update section * @param[out] out Where to store the head of the map. * @param[in] dst_list_def The default destination list, usually dictated by * the section the module is being called in. * @param[in] src_list_def The default source list, usually dictated by the * section the module is being called in. * @param[in] max number of mappings to process. * @return -1 on error, else 0. */ static int ldap_map_afrom_cs(value_pair_map_t **out, CONF_SECTION *cs, pair_lists_t dst_list_def, pair_lists_t src_list_def, unsigned int max) { char const *cs_list, *p; request_refs_t request_def = REQUEST_CURRENT; CONF_ITEM *ci; unsigned int total = 0; value_pair_map_t **tail, *map; TALLOC_CTX *ctx; *out = NULL; tail = out; if (!cs) return 0; /* * The first map has cs as the parent. * The rest have the previous map as the parent. */ ctx = cs; ci = cf_sectiontoitem(cs); cs_list = p = cf_section_name2(cs); if (cs_list) { request_def = radius_request_name(&p, REQUEST_CURRENT); if (request_def == REQUEST_UNKNOWN) { cf_log_err(ci, "Default request specified " "in mapping section is invalid"); return -1; } dst_list_def = fr_str2int(pair_lists, p, PAIR_LIST_UNKNOWN); if (dst_list_def == PAIR_LIST_UNKNOWN) { cf_log_err(ci, "Default list \"%s\" specified " "in mapping section is invalid", p); return -1; } } for (ci = cf_item_find_next(cs, NULL); ci != NULL; ci = cf_item_find_next(cs, ci)) { char const *attr; FR_TOKEN type; CONF_PAIR *cp; cp = cf_itemtopair(ci); type = cf_pair_value_type(cp); if (total++ == max) { cf_log_err(ci, "Map size exceeded"); goto error; } if (!cf_item_is_pair(ci)) { cf_log_err(ci, "Entry is not in \"attribute = value\" format"); goto error; } cp = cf_itemtopair(ci); /* * Look for "list: OP BARE_WORD". If it exists, * we can make the RHS a bare word. Otherwise, * just call map_afrom_cp() * * Otherwise, the map functions check the RHS of * list assignments, and complain that the RHS * isn't another list. */ attr = cf_pair_attr(cp); p = strrchr(attr, ':'); if (!p || (p[1] != '\0') || (type == T_DOUBLE_QUOTED_STRING)) { if (map_afrom_cp(ctx, &map, cp, request_def, dst_list_def, REQUEST_CURRENT, src_list_def) < 0) { goto error; } } else { ssize_t slen; char const *value; map = talloc_zero(ctx, value_pair_map_t); map->op = cf_pair_operator(cp); map->ci = cf_pairtoitem(cp); slen = tmpl_afrom_attr_str(ctx, &map->lhs, attr, request_def, dst_list_def); if (slen <= 0) { char *spaces, *text; fr_canonicalize_error(ctx, &spaces, &text, slen, attr); cf_log_err(ci, "Failed parsing list reference"); cf_log_err(ci, "%s", text); cf_log_err(ci, "%s^ %s", spaces, fr_strerror()); talloc_free(spaces); talloc_free(text); goto error; } if (map->lhs->type != TMPL_TYPE_LIST) { cf_log_err(map->ci, "Invalid list name"); goto error; } if (map->op != T_OP_ADD) { cf_log_err(map->ci, "Only '+=' operator is permitted for valuepair to list mapping"); goto error; } value = cf_pair_value(cp); if (!value) { cf_log_err(map->ci, "No value specified for list assignment"); goto error; } /* * the RHS type is a bare word or single * quoted string. We don't want it being * interpreted as a list or attribute * reference, so we force the RHS to be a * literal. */ slen = tmpl_afrom_str(ctx, &map->rhs, value, T_SINGLE_QUOTED_STRING, request_def, dst_list_def); if (slen <= 0) { char *spaces, *text; fr_canonicalize_error(ctx, &spaces, &text, slen, value); cf_log_err(ci, "Failed parsing string"); cf_log_err(ci, "%s", text); cf_log_err(ci, "%s^ %s", spaces, fr_strerror()); talloc_free(spaces); talloc_free(text); goto error; } /* * And unlike map_afrom_cp(), we do NOT * try to parse the RHS as a list * reference. It's a literal, and we * leave it as a literal. */ rad_assert(map->rhs->type == TMPL_TYPE_LITERAL); } ctx = *tail = map; tail = &(map->next); } return 0; error: TALLOC_FREE(*out); return -1; }