static char *radius_expand_tmpl(REQUEST *request, value_pair_tmpl_t const *vpt) { char *buffer = NULL; VALUE_PAIR *vp; rad_assert(vpt->type != VPT_TYPE_LIST); switch (vpt->type) { case VPT_TYPE_LITERAL: EVAL_DEBUG("TMPL LITERAL"); buffer = talloc_strdup(request, vpt->name); break; case VPT_TYPE_EXEC: EVAL_DEBUG("TMPL EXEC"); buffer = talloc_array(request, char, 1024); if (radius_exec_program(vpt->name, request, 1, buffer, 1024, NULL, NULL, 0) != 0) { talloc_free(buffer); return NULL; } break; case VPT_TYPE_REGEX: EVAL_DEBUG("TMPL REGEX"); if (strchr(vpt->name, '%') == NULL) { buffer = talloc_strdup(request, vpt->name); break; } /* FALL-THROUGH */ case VPT_TYPE_XLAT: EVAL_DEBUG("TMPL XLAT"); buffer = NULL; if (radius_axlat(&buffer, request, vpt->name, NULL, NULL) == 0) { return NULL; } break; case VPT_TYPE_ATTR: EVAL_DEBUG("TMPL ATTR"); vp = radius_vpt_get_vp(request, vpt); if (!vp) return NULL; buffer = vp_aprint(request, vp); break; case VPT_TYPE_DATA: rad_assert(0 == 1); /* FALL-THROUGH */ default: buffer = NULL; break; } EVAL_DEBUG("Expand tmpl --> %s", buffer); return buffer; }
/** Return a VP from the specified request. * * @param out where to write the pointer to the resolved VP. * Will be NULL if the attribute couldn't be resolved. * @param request current request. * @param name attribute name including qualifiers. * @return -4 if either the attribute or qualifier were invalid, and the same error codes as radius_vpt_get_vp for other * error conditions. */ int radius_get_vp(VALUE_PAIR **out, REQUEST *request, char const *name) { value_pair_tmpl_t vpt; *out = NULL; if (radius_parse_attr(name, &vpt, REQUEST_CURRENT, PAIR_LIST_REQUEST) < 0) { return -4; } return radius_vpt_get_vp(out, request, &vpt); }
/** Expand the RHS of a template * * @note Length of expanded string can be found with talloc_array_length(*out) - 1 * * @param out where to write a pointer to the newly allocated buffer. * @param request Current request. * @param vpt to evaluate. * @return -1 on error, else 0. */ static int radius_expand_tmpl(char **out, REQUEST *request, value_pair_tmpl_t const *vpt) { VALUE_PAIR *vp; *out = NULL; rad_assert(vpt->type != VPT_TYPE_LIST); switch (vpt->type) { case VPT_TYPE_LITERAL: EVAL_DEBUG("TMPL LITERAL"); *out = talloc_strdup(request, vpt->name); break; case VPT_TYPE_EXEC: EVAL_DEBUG("TMPL EXEC"); *out = talloc_array(request, char, 1024); if (radius_exec_program(request, vpt->name, true, false, *out, 1024, EXEC_TIMEOUT, NULL, NULL) != 0) { TALLOC_FREE(*out); return -1; } break; case VPT_TYPE_REGEX: EVAL_DEBUG("TMPL REGEX"); if (strchr(vpt->name, '%') == NULL) { *out = talloc_strdup(request, vpt->name); break; } /* FALL-THROUGH */ case VPT_TYPE_XLAT: EVAL_DEBUG("TMPL XLAT"); /* Error in expansion, this is distinct from zero length expansion */ if (radius_axlat(out, request, vpt->name, NULL, NULL) < 0) { rad_assert(!*out); return -1; } break; case VPT_TYPE_ATTR: EVAL_DEBUG("TMPL ATTR"); if (radius_vpt_get_vp(&vp, request, vpt) < 0) { return -2; } *out = vp_aprint(request, vp); if (!*out) { return -1; } break; case VPT_TYPE_DATA: rad_assert(0 == 1); /* FALL-THROUGH */ default: break; } EVAL_DEBUG("Expand tmpl --> %s", *out); return 0; }
/** Evaluate a map * * @param[in] request the REQUEST * @param[in] modreturn the previous module return code * @param[in] depth of the recursion (only used for debugging) * @param[in] c the condition to evaluate * @return -1 on error, 0 for "no match", 1 for "match". */ int radius_evaluate_map(REQUEST *request, UNUSED int modreturn, UNUSED int depth, fr_cond_t const *c) { int rcode; char *lhs, *rhs; value_pair_map_t *map; rad_assert(c->type == COND_TYPE_MAP); map = c->data.map; rad_assert(map->dst->type != VPT_TYPE_UNKNOWN); rad_assert(map->src->type != VPT_TYPE_UNKNOWN); rad_assert(map->dst->type != VPT_TYPE_LIST); rad_assert(map->src->type != VPT_TYPE_LIST); rad_assert(map->dst->type != VPT_TYPE_REGEX); /* * Verify regexes. */ if (map->src->type == VPT_TYPE_REGEX) { rad_assert(map->op == T_OP_REG_EQ); } else { rad_assert(!((map->op == T_OP_REG_EQ) || (map->op == T_OP_REG_NE))); } /* * They're both attributes. Do attribute-specific work. * * LHS is DST. RHS is SRC <sigh> */ if (!c->cast && (map->src->type == VPT_TYPE_ATTR) && (map->dst->type == VPT_TYPE_ATTR)) { VALUE_PAIR *lhs_vp, *rhs_vp; EVAL_DEBUG("ATTR to ATTR"); if ((radius_vpt_get_vp(&lhs_vp, request, map->dst) < 0) || (radius_vpt_get_vp(&rhs_vp, request, map->src) < 0)) { return -2; } return paircmp_op(lhs_vp, map->op, rhs_vp); } /* * LHS is a cast. Do type-specific comparisons, as if * the LHS was a real attribute. */ if (c->cast) { VALUE_PAIR *lhs_vp, *rhs_vp; rcode = get_cast_vp(&lhs_vp, request, map->dst, c->cast); if (rcode < 0) { return rcode; } rad_assert(lhs_vp); /* * Get either a real VP, or parse the RHS into a * VP, and return that. */ if (map->src->type == VPT_TYPE_ATTR) { if (radius_vpt_get_vp(&rhs_vp, request, map->src) < 0) { return -2; } } else { rcode = get_cast_vp(&rhs_vp, request, map->src, c->cast); if (rcode < 0) { return rcode; } rad_assert(rhs_vp); } if (!rhs_vp) return -2; EVAL_DEBUG("CAST to ..."); rcode = paircmp_op(lhs_vp, map->op, rhs_vp); pairfree(&lhs_vp); if (map->src->type != VPT_TYPE_ATTR) { pairfree(&rhs_vp); } return rcode; } /* * Might be a virtual comparison */ if ((map->dst->type == VPT_TYPE_ATTR) && (map->src->type != VPT_TYPE_REGEX) && (c->pass2_fixup == PASS2_PAIRCOMPARE)) { VALUE_PAIR *rhs_vp; EVAL_DEBUG("virtual ATTR to DATA"); rcode = get_cast_vp(&rhs_vp, request, map->src, map->dst->da); if (rcode < 0) { return rcode; } rad_assert(rhs_vp); if (paircompare(request, request->packet->vps, rhs_vp, NULL) == 0) { return true; } return false; } rad_assert(c->pass2_fixup != PASS2_PAIRCOMPARE); /* * RHS has been pre-parsed into binary data. Go check * that. */ if ((map->dst->type == VPT_TYPE_ATTR) && (map->src->type == VPT_TYPE_DATA)) { VALUE_PAIR *lhs_vp, *rhs_vp; EVAL_DEBUG("ATTR to DATA"); if (radius_vpt_get_vp(&lhs_vp, request, map->dst) < 0) { return -2; } rcode = get_cast_vp(&rhs_vp, request, map->src, map->dst->da); if (rcode < 0) { return rcode; } rad_assert(rhs_vp); #ifdef WITH_EVAL_DEBUG debug_pair(lhs_vp); debug_pair(rhs_vp); #endif rcode = paircmp_op(lhs_vp, map->op, rhs_vp); pairfree(&rhs_vp); return rcode; } rad_assert(map->src->type != VPT_TYPE_DATA); rad_assert(map->dst->type != VPT_TYPE_DATA); /* * The RHS now needs to be expanded into a string. */ rcode = radius_expand_tmpl(&rhs, request, map->src); if (rcode < 0) { EVAL_DEBUG("FAIL %d", __LINE__); return rcode; } /* * User-Name == FOO * * Parse the RHS to be the same DA as the LHS. do * comparisons. So long as it's not a regex, which does * string comparisons. * * The LHS may be a virtual attribute, too. */ if ((map->dst->type == VPT_TYPE_ATTR) && (map->src->type != VPT_TYPE_REGEX)) { VALUE_PAIR *lhs_vp, *rhs_vp; EVAL_DEBUG("ATTR to non-REGEX"); /* * No LHS means no match */ if (radius_vpt_get_vp(&lhs_vp, request, map->dst) < 0) { /* * Not a real attr: might be a dynamic comparison. */ if ((map->dst->type == VPT_TYPE_ATTR) && (map->dst->da->vendor == 0) && radius_find_compare(map->dst->da)) { rhs_vp = pairalloc(request, map->dst->da); if (!pairparsevalue(rhs_vp, rhs)) { talloc_free(rhs); EVAL_DEBUG("FAIL %d", __LINE__); return -1; } talloc_free(rhs); rcode = (radius_callback_compare(request, NULL, rhs_vp, NULL, NULL) == 0); pairfree(&rhs_vp); return rcode; } return -2; } /* * Get VP for RHS */ rhs_vp = pairalloc(request, map->dst->da); rad_assert(rhs_vp != NULL); if (!pairparsevalue(rhs_vp, rhs)) { talloc_free(rhs); pairfree(&rhs_vp); EVAL_DEBUG("FAIL %d", __LINE__); return -1; } rcode = paircmp_op(lhs_vp, map->op, rhs_vp); talloc_free(rhs); pairfree(&rhs_vp); return rcode; } /* * The LHS is a string. Expand it. */ rcode = radius_expand_tmpl(&lhs, request, map->dst); if (rcode < 0) { EVAL_DEBUG("FAIL %d", __LINE__); return rcode; } EVAL_DEBUG("LHS is %s", lhs); /* * Compile the RHS to a regex, and do regex stuff */ if (map->src->type == VPT_TYPE_REGEX) { return do_regex(request, lhs, rhs, c->regex_i); } /* * Loop over the string, doing comparisons */ if (all_digits(lhs) && all_digits(rhs)) { int lint, rint; lint = strtoul(lhs, NULL, 0); rint = strtoul(rhs, NULL, 0); talloc_free(lhs); talloc_free(rhs); switch (map->op) { case T_OP_CMP_EQ: return (lint == rint); case T_OP_NE: return (lint != rint); case T_OP_LT: return (lint < rint); case T_OP_GT: return (lint > rint); case T_OP_LE: return (lint <= rint); case T_OP_GE: return (lint >= rint); default: break; } } else { rcode = strcmp(lhs, rhs); talloc_free(lhs); talloc_free(rhs); switch (map->op) { case T_OP_CMP_EQ: return (rcode == 0); case T_OP_NE: return (rcode != 0); case T_OP_LT: return (rcode < 0); case T_OP_GT: return (rcode > 0); case T_OP_LE: return (rcode <= 0); case T_OP_GE: return (rcode >= 0); default: break; } } EVAL_DEBUG("FAIL %d", __LINE__); return -1; }
/** Evaluate a template * * @param[in] request the REQUEST * @param[in] modreturn the previous module return code * @param[in] depth of the recursion (only used for debugging) * @param[in] vpt the template to evaluate * @return -1 on error, 0 for "no match", 1 for "match". */ int radius_evaluate_tmpl(REQUEST *request, int modreturn, UNUSED int depth, value_pair_tmpl_t const *vpt) { int rcode; int modcode; char *buffer; switch (vpt->type) { case VPT_TYPE_LITERAL: modcode = fr_str2int(modreturn_table, vpt->name, RLM_MODULE_UNKNOWN); if (modcode != RLM_MODULE_UNKNOWN) { rcode = (modcode == modreturn); break; } /* * Else it's a literal string. Empty string is * false, non-empty string is true. * * @todo: Maybe also check for digits? * * The VPT *doesn't* have a "bare word" type, * which arguably it should. */ rcode = (vpt->name != '\0'); break; case VPT_TYPE_ATTR: case VPT_TYPE_LIST: if (radius_vpt_get_vp(NULL, request, vpt) == 0) { rcode = true; } else { rcode = false; } break; /* * FIXME: expand the strings * if not empty, return! */ case VPT_TYPE_XLAT: case VPT_TYPE_EXEC: if (!*vpt->name) return false; rcode = radius_expand_tmpl(&buffer, request, vpt); if (rcode < 0) { EVAL_DEBUG("FAIL %d", __LINE__); return -1; } rcode = (buffer && (*buffer != '\0')); talloc_free(buffer); break; /* * Can't have a bare ... (/foo/) ... */ case VPT_TYPE_REGEX: EVAL_DEBUG("FAIL %d", __LINE__); rad_assert(0 == 1); /* FALL-THROUGH */ default: rcode = -1; break; } return rcode; }
/** Convert a map to a VALUE_PAIR. * * @param[out] out Where to write the VALUE_PAIR(s). * @param[in] request structure (used only for talloc) * @param[in] map the map. The LHS (dst) has to be VPT_TYPE_ATTR or VPT_TYPE_LIST. * @param[in] ctx unused * @return 0 on success, -1 on failure, -2 on attribute not found/equivalent */ int radius_map2vp(VALUE_PAIR **out, REQUEST *request, value_pair_map_t const *map, UNUSED void *ctx) { int rcode = 0; VALUE_PAIR *vp = NULL, *found, **from = NULL; DICT_ATTR const *da; REQUEST *context = request; vp_cursor_t cursor; rad_assert(request != NULL); rad_assert(map != NULL); *out = NULL; /* * Special case for !*, we don't need to parse RHS as this is a unary operator. */ if (map->op == T_OP_CMP_FALSE) { /* * Were deleting all the attributes in a list. This isn't like the other * mappings because lists aren't represented as attributes (yet), * so we can't return a <list> attribute with the !* operator for * radius_pairmove() to consume, and need to do the work here instead. */ if (map->dst->type == VPT_TYPE_LIST) { if (radius_request(&context, map->dst->request) == 0) { from = radius_list(context, map->dst->list); } if (!from) return -2; pairfree(from); /* @fixme hacky! */ if (map->dst->list == PAIR_LIST_REQUEST) { context->username = NULL; context->password = NULL; } return 0; } /* Not a list, but an attribute, radius_pairmove() will perform that actual delete */ vp = pairalloc(request, map->dst->da); if (!vp) return -1; vp->op = map->op; *out = vp; return 0; } /* * List to list found, this is a special case because we don't need * to allocate any attributes, just finding the current list, and change * the op. */ if ((map->dst->type == VPT_TYPE_LIST) && (map->src->type == VPT_TYPE_LIST)) { if (radius_request(&context, map->src->request) == 0) { from = radius_list(context, map->src->list); } if (!from) return -2; found = paircopy(request, *from); /* * List to list copy is invalid if the src list has no attributes. */ if (!found) return -2; for (vp = fr_cursor_init(&cursor, &found); vp; vp = fr_cursor_next(&cursor)) { vp->op = T_OP_ADD; } *out = found; return 0; } /* * Deal with all non-list operations. */ da = map->dst->da ? map->dst->da : map->src->da; switch (map->src->type) { case VPT_TYPE_XLAT: case VPT_TYPE_XLAT_STRUCT: case VPT_TYPE_LITERAL: case VPT_TYPE_DATA: vp = pairalloc(request, da); if (!vp) return -1; vp->op = map->op; break; default: break; } /* * And parse the RHS */ switch (map->src->type) { ssize_t slen; char *str; case VPT_TYPE_XLAT_STRUCT: rad_assert(map->dst->da); /* Need to know where were going to write the new attribute */ rad_assert(map->src->xlat != NULL); str = NULL; slen = radius_axlat_struct(&str, request, map->src->xlat, NULL, NULL); if (slen < 0) { rcode = slen; goto error; } /* * We do the debug printing because radius_axlat_struct * doesn't have access to the original string. It's been * mangled during the parsing to xlat_exp_t */ RDEBUG2("EXPAND %s", map->src->name); RDEBUG2(" --> %s", str); rcode = pairparsevalue(vp, str); talloc_free(str); if (!rcode) { pairfree(&vp); rcode = -1; goto error; } break; case VPT_TYPE_XLAT: rad_assert(map->dst->da); /* Need to know where were going to write the new attribute */ str = NULL; slen = radius_axlat(&str, request, map->src->name, NULL, NULL); if (slen < 0) { rcode = slen; goto error; } rcode = pairparsevalue(vp, str); talloc_free(str); if (!rcode) { pairfree(&vp); rcode = -1; goto error; } break; case VPT_TYPE_LITERAL: if (!pairparsevalue(vp, map->src->name)) { rcode = -2; goto error; } break; case VPT_TYPE_ATTR: rad_assert(!map->dst->da || (map->src->da->type == map->dst->da->type) || (map->src->da->type == PW_TYPE_OCTETS) || (map->dst->da->type == PW_TYPE_OCTETS)); /* * Special case, destination is a list, found all instance of an attribute. */ if (map->dst->type == VPT_TYPE_LIST) { context = request; if (radius_request(&context, map->src->request) == 0) { from = radius_list(context, map->src->list); } /* * Can't add the attribute if the list isn't * valid. */ if (!from) { rcode = -2; goto error; } found = paircopy2(request, *from, map->src->da->attr, map->src->da->vendor, TAG_ANY); if (!found) { REDEBUG("Attribute \"%s\" not found in request", map->src->name); rcode = -2; goto error; } for (vp = fr_cursor_init(&cursor, &found); vp; vp = fr_cursor_next(&cursor)) { vp->op = T_OP_ADD; } *out = found; return 0; } if (radius_vpt_get_vp(&found, request, map->src) < 0) { REDEBUG("Attribute \"%s\" not found in request", map->src->name); rcode = -2; goto error; } /* * Copy the data over verbatim, assuming it's * actually data. */ vp = paircopyvpdata(request, da, found); if (!vp) { return -1; } vp->op = map->op; break; case VPT_TYPE_DATA: rad_assert(map->src && map->src->da); rad_assert(map->dst && map->dst->da); rad_assert(map->src->da->type == map->dst->da->type); memcpy(&vp->data, map->src->vpd, sizeof(vp->data)); vp->length = map->src->length; break; /* * This essentially does the same as rlm_exec xlat, except it's non-configurable. * It's only really here as a convenience for people who expect the contents of * backticks to be executed in a shell. * * exec string is xlat expanded and arguments are shell escaped. */ case VPT_TYPE_EXEC: return radius_mapexec(out, request, map); default: rad_assert(0); /* Should of been caught at parse time */ error: pairfree(&vp); return rcode; } *out = vp; return 0; }