/** Evaluate a fr_cond_t; * * @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 failure, -2 on attribute not found, 0 for "no match", 1 for "match". */ int radius_evaluate_cond(REQUEST *request, int modreturn, int depth, fr_cond_t const *c) { int rcode = -1; while (c) { switch (c->type) { case COND_TYPE_EXISTS: rcode = radius_evaluate_tmpl(request, modreturn, depth, c->data.vpt); /* Existence checks are special, because we expect them to fail */ if (rcode < 0) rcode = false; break; case COND_TYPE_MAP: rcode = radius_evaluate_map(request, modreturn, depth, c); break; case COND_TYPE_CHILD: rcode = radius_evaluate_cond(request, modreturn, depth + 1, c->data.child); break; case COND_TYPE_TRUE: rcode = true; break; case COND_TYPE_FALSE: rcode = false; break; default: EVAL_DEBUG("FAIL %d", __LINE__); return -1; } if (rcode < 0) return rcode; if (c->negate) rcode = !rcode; if (!c->next) break; /* * FALSE && ... = FALSE */ if (!rcode && (c->next_op == COND_AND)) return false; /* * TRUE || ... = TRUE */ if (rcode && (c->next_op == COND_OR)) return true; c = c->next; } if (rcode < 0) { EVAL_DEBUG("FAIL %d", __LINE__); } return rcode; }
static int do_regex(REQUEST *request, value_pair_map_t const *map, bool iflag) { int compare, rcode; int cflags = REG_EXTENDED; regex_t reg, *preg; char *lhs, *rhs; regmatch_t rxmatch[REQUEST_MAX_REGEX + 1]; if (iflag) cflags |= REG_ICASE; /* * Expand and then compile it. */ if (map->src->type == VPT_TYPE_REGEX) { rcode = radius_expand_tmpl(&rhs, request, map->src); if (rcode < 0) { EVAL_DEBUG("FAIL %d", __LINE__); return -1; } rad_assert(rhs != NULL); compare = regcomp(®, rhs, cflags); if (compare != 0) { if (debug_flag) { char errbuf[128]; regerror(compare, ®, errbuf, sizeof(errbuf)); EDEBUG("Failed compiling regular expression: %s", errbuf); } EVAL_DEBUG("FAIL %d", __LINE__); return -1; } preg = ® } else { preg = map->src->vpt_preg; } rcode = radius_expand_tmpl(&lhs, request, map->dst); if (rcode < 0) { EVAL_DEBUG("FAIL %d", __LINE__); return -1; } rad_assert(lhs != NULL); memset(&rxmatch, 0, sizeof(rxmatch)); /* regexec does not seem to initialise unused elements */ compare = regexec(preg, lhs, REQUEST_MAX_REGEX + 1, rxmatch, 0); rad_regcapture(request, compare, lhs, rxmatch); return (compare == 0); }
static int do_regex(REQUEST *request, char const *lhs, char const *rhs, bool iflag) { int compare; int cflags = REG_EXTENDED; regex_t reg; regmatch_t rxmatch[REQUEST_MAX_REGEX + 1]; if (iflag) cflags |= REG_ICASE; /* * Include substring matches. */ compare = regcomp(®, rhs, cflags); if (compare != 0) { if (debug_flag) { char errbuf[128]; regerror(compare, ®, errbuf, sizeof(errbuf)); EDEBUG("Failed compiling regular expression: %s", errbuf); } EVAL_DEBUG("FAIL %d", __LINE__); return -1; } memset(&rxmatch, 0, sizeof(rxmatch)); /* regexec does not seem to initialise unused elements */ compare = regexec(®, lhs, REQUEST_MAX_REGEX + 1, rxmatch, 0); regfree(®); rad_regcapture(request, compare, lhs, rxmatch); return (compare == 0); }
static int do_regex(REQUEST *request, const char *lhs, const char *rhs, int iflag) { int i, compare; int cflags = REG_EXTENDED; regex_t reg; regmatch_t rxmatch[REQUEST_MAX_REGEX + 1]; if (iflag) cflags |= REG_ICASE; /* * Include substring matches. */ compare = regcomp(®, rhs, cflags); if (compare != 0) { if (debug_flag) { char errbuf[128]; regerror(compare, ®, errbuf, sizeof(errbuf)); EDEBUG("Failed compiling regular expression: %s", errbuf); } EVAL_DEBUG("FAIL %d", __LINE__); return -1; } compare = regexec(®, lhs, REQUEST_MAX_REGEX + 1, rxmatch, 0); regfree(®); /* * Add new %{0}, %{1}, etc. */ if (compare == 0) for (i = 0; i <= REQUEST_MAX_REGEX; i++) { char *r; free(request_data_get(request, request, REQUEST_DATA_REGEX | i)); /* * No %{i}, skip it. * We MAY have %{2} without %{1}. */ if (rxmatch[i].rm_so == -1) continue; /* * Copy substring into allocated buffer */ r = rad_malloc(rxmatch[i].rm_eo -rxmatch[i].rm_so + 1); memcpy(r, lhs + rxmatch[i].rm_so, rxmatch[i].rm_eo - rxmatch[i].rm_so); r[rxmatch[i].rm_eo - rxmatch[i].rm_so] = '\0'; request_data_add(request, request, REQUEST_DATA_REGEX | i, r, free); } return (compare == 0); }
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; }
/** 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; }
/* * Copy data from src to dst, where the attributes are of * different type. */ static bool do_cast_copy(VALUE_PAIR *dst, VALUE_PAIR const *src) { rad_assert(dst->da->type != src->da->type); if (dst->da->type == PW_TYPE_STRING) { dst->vp_strvalue = vp_aprint(dst, src); dst->length = strlen(dst->vp_strvalue); return true; } if (dst->da->type == PW_TYPE_OCTETS) { if (src->da->type == PW_TYPE_STRING) { pairmemcpy(dst, src->vp_octets, src->length); /* Copy embedded NULLs */ } else { pairmemcpy(dst, (uint8_t const *) &src->data, src->length); } return true; } if (src->da->type == PW_TYPE_STRING) { return pairparsevalue(dst, src->vp_strvalue); } if ((src->da->type == PW_TYPE_INTEGER64) && (dst->da->type == PW_TYPE_ETHERNET)) { uint8_t array[8]; uint64_t i; i = htonll(src->vp_integer64); memcpy(array, &i, 8); /* * For OUIs in the DB. */ if ((array[0] != 0) || (array[1] != 0)) return false; memcpy(&dst->vp_ether, &array[2], 6); dst->length = 6; return true; } /* * The attribute we've found has to have a size which is * compatible with the type of the destination cast. */ if ((src->length < dict_attr_sizes[dst->da->type][0]) || (src->length > dict_attr_sizes[dst->da->type][1])) { EVAL_DEBUG("Casted attribute is wrong size (%u)", (unsigned int) src->length); return false; } if (src->da->type == PW_TYPE_OCTETS) { switch (dst->da->type) { case PW_TYPE_INTEGER64: dst->vp_integer = ntohll(*(uint64_t const *) src->vp_octets); break; case PW_TYPE_INTEGER: case PW_TYPE_DATE: case PW_TYPE_SIGNED: dst->vp_integer = ntohl(*(uint32_t const *) src->vp_octets); break; case PW_TYPE_SHORT: dst->vp_integer = ntohs(*(uint16_t const *) src->vp_octets); break; case PW_TYPE_BYTE: dst->vp_integer = src->vp_octets[0]; break; default: memcpy(&dst->data, src->vp_octets, src->length); break; } dst->length = src->length; return true; } /* * Convert host order to network byte order. */ if ((dst->da->type == PW_TYPE_IPADDR) && ((src->da->type == PW_TYPE_INTEGER) || (src->da->type == PW_TYPE_DATE) || (src->da->type == PW_TYPE_SIGNED))) { dst->vp_ipaddr = htonl(src->vp_integer); } else if ((src->da->type == PW_TYPE_IPADDR) && ((dst->da->type == PW_TYPE_INTEGER) || (dst->da->type == PW_TYPE_DATE) || (dst->da->type == PW_TYPE_SIGNED))) { dst->vp_integer = htonl(src->vp_ipaddr); } else { /* they're of the same byte order */ memcpy(&dst->data, &src->data, src->length); } dst->length = src->length; return true; }
/** 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. */ int radius_expand_tmpl(char **out, REQUEST *request, value_pair_tmpl_t const *vpt) { VALUE_PAIR *vp; *out = NULL; rad_assert(vpt->type != TMPL_TYPE_LIST); switch (vpt->type) { case TMPL_TYPE_LITERAL: EVAL_DEBUG("TMPL LITERAL"); *out = talloc_typed_strdup(request, vpt->name); break; case TMPL_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 TMPL_TYPE_REGEX: EVAL_DEBUG("TMPL REGEX"); /* 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 TMPL_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 TMPL_TYPE_XLAT_STRUCT: EVAL_DEBUG("TMPL XLAT_STRUCT"); /* Error in expansion, this is distinct from zero length expansion */ if (radius_axlat_struct(out, request, vpt->tmpl_xlat, NULL, NULL) < 0) { rad_assert(!*out); return -1; } RDEBUG2("EXPAND %s", vpt->name); /* xlat_struct doesn't do this */ RDEBUG2(" --> %s", *out); break; case TMPL_TYPE_ATTR: { int ret; EVAL_DEBUG("TMPL ATTR"); ret = tmpl_find_vp(&vp, request, vpt); if (ret < 0) return -2; *out = vp_aprint_value(request, vp, false); if (!*out) return -1; } break; case TMPL_TYPE_DATA: case TMPL_TYPE_REGEX_STRUCT: 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 != TMPL_TYPE_UNKNOWN); rad_assert(map->src->type != TMPL_TYPE_UNKNOWN); rad_assert(map->dst->type != TMPL_TYPE_LIST); rad_assert(map->src->type != TMPL_TYPE_LIST); rad_assert(map->dst->type != TMPL_TYPE_REGEX); rad_assert(map->dst->type != TMPL_TYPE_REGEX_STRUCT); EVAL_DEBUG("MAP TYPES LHS: %s, RHS: %s", fr_int2str(template_names, map->dst->type, "???"), fr_int2str(template_names, map->src->type, "???")); /* * Verify regexes. */ if ((map->src->type == TMPL_TYPE_REGEX) || (map->src->type == TMPL_TYPE_REGEX_STRUCT)) { 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 == TMPL_TYPE_ATTR) && (map->dst->type == TMPL_TYPE_ATTR)) { VALUE_PAIR *lhs_vp, *rhs_vp, *cast_vp; EVAL_DEBUG("ATTR to ATTR"); if ((tmpl_find_vp(&lhs_vp, request, map->dst) < 0) || (tmpl_find_vp(&rhs_vp, request, map->src) < 0)) return -1; if (map->dst->tmpl_da->type == map->src->tmpl_da->type) { return paircmp_op(lhs_vp, map->op, rhs_vp); } /* * Compare a large integer (lhs) to a small integer (rhs). * We allow this without a cast. */ rad_assert((map->dst->tmpl_da->type == PW_TYPE_INTEGER64) || (map->dst->tmpl_da->type == PW_TYPE_INTEGER) || (map->dst->tmpl_da->type == PW_TYPE_SHORT)); rad_assert((map->src->tmpl_da->type == PW_TYPE_INTEGER) || (map->src->tmpl_da->type == PW_TYPE_SHORT) || (map->src->tmpl_da->type == PW_TYPE_BYTE)); cast_vp = pairalloc(request, lhs_vp->da); if (!cast_vp) return false; /* * Copy the RHS to the casted type. */ if (do_cast_copy(cast_vp, rhs_vp) < 0) { talloc_free(cast_vp); return false; } rcode = paircmp_op(lhs_vp, map->op, cast_vp); talloc_free(cast_vp); return rcode; } /* * 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; /* * Try to copy data from the VP which is being * casted, instead of printing it to a string and * then re-parsing it. */ if (map->dst->type == TMPL_TYPE_ATTR) { VALUE_PAIR *cast_vp; if (tmpl_find_vp(&cast_vp, request, map->dst) < 0) return false; lhs_vp = pairalloc(request, c->cast); if (!lhs_vp) return -1; /* * In a separate function for clarity */ if (do_cast_copy(lhs_vp, cast_vp) < 0) { talloc_free(lhs_vp); return -1; } } else { rcode = tmpl_cast_to_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 == TMPL_TYPE_ATTR) { if (tmpl_find_vp(&rhs_vp, request, map->src) < 0) { return -2; } } else { rcode = tmpl_cast_to_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 %s", fr_int2str(dict_attr_types, c->cast->type, "?Unknown?")); rcode = paircmp_op(lhs_vp, map->op, rhs_vp); pairfree(&lhs_vp); if (map->src->type != TMPL_TYPE_ATTR) { pairfree(&rhs_vp); } return rcode; } /* * Might be a virtual comparison */ if ((map->dst->type == TMPL_TYPE_ATTR) && (map->src->type != TMPL_TYPE_REGEX) && (map->src->type != TMPL_TYPE_REGEX_STRUCT) && (c->pass2_fixup == PASS2_PAIRCOMPARE)) { int ret; VALUE_PAIR *lhs_vp; EVAL_DEBUG("virtual ATTR to DATA"); rcode = tmpl_cast_to_vp(&lhs_vp, request, map->src, map->dst->tmpl_da); if (rcode < 0) return rcode; rad_assert(lhs_vp); /* * paircompare requires the operator be set for the * check attribute. */ lhs_vp->op = map->op; ret = paircompare(request, request->packet->vps, lhs_vp, NULL); talloc_free(lhs_vp); if (ret == 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 == TMPL_TYPE_ATTR) && (map->src->type == TMPL_TYPE_DATA)) { VALUE_PAIR *lhs_vp, *rhs_vp; EVAL_DEBUG("ATTR to DATA"); if (tmpl_find_vp(&lhs_vp, request, map->dst) < 0) return -2; rcode = tmpl_cast_to_vp(&rhs_vp, request, map->src, map->dst->tmpl_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 != TMPL_TYPE_DATA); rad_assert(map->dst->type != TMPL_TYPE_DATA); #ifdef HAVE_REGEX_H /* * Parse regular expressions. */ if ((map->src->type == TMPL_TYPE_REGEX) || (map->src->type == TMPL_TYPE_REGEX_STRUCT)) { return do_regex(request, map); } #endif /* * 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; } rad_assert(rhs != NULL); /* * 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 == TMPL_TYPE_ATTR) { VALUE_PAIR *lhs_vp, *rhs_vp; EVAL_DEBUG("ATTR to non-REGEX"); /* * No LHS means no match */ if (tmpl_find_vp(&lhs_vp, request, map->dst) < 0) { /* * Not a real attr: might be a dynamic comparison. */ if ((map->dst->type == TMPL_TYPE_ATTR) && (map->dst->tmpl_da->vendor == 0) && radius_find_compare(map->dst->tmpl_da)) { rhs_vp = pairalloc(request, map->dst->tmpl_da); rad_assert(rhs_vp != NULL); if (pairparsevalue(rhs_vp, rhs, 0) < 0) { 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->tmpl_da); rad_assert(rhs_vp != NULL); if (pairparsevalue(rhs_vp, rhs, 0) < 0) { 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; } rad_assert(lhs != NULL); EVAL_DEBUG("LHS is %s", lhs); /* * 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 { rad_assert(lhs != NULL); rad_assert(rhs != NULL); 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; }
/* * Copy data from src to dst, where the attributes are of * different type. */ static int do_cast_copy(VALUE_PAIR *dst, VALUE_PAIR const *src) { rad_assert(dst->da->type != src->da->type); if (dst->da->type == PW_TYPE_STRING) { dst->vp_strvalue = vp_aprint_value(dst, src, false); dst->length = strlen(dst->vp_strvalue); return 0; } if (dst->da->type == PW_TYPE_OCTETS) { if (src->da->type == PW_TYPE_STRING) { pairmemcpy(dst, src->vp_octets, src->length); /* Copy embedded NULLs */ } else { pairmemcpy(dst, (uint8_t const *) &src->data, src->length); } return 0; } if (src->da->type == PW_TYPE_STRING) { return pairparsevalue(dst, src->vp_strvalue, 0); } if ((src->da->type == PW_TYPE_INTEGER64) && (dst->da->type == PW_TYPE_ETHERNET)) { uint8_t array[8]; uint64_t i; i = htonll(src->vp_integer64); memcpy(array, &i, 8); /* * For OUIs in the DB. */ if ((array[0] != 0) || (array[1] != 0)) return -1; memcpy(&dst->vp_ether, &array[2], 6); dst->length = 6; return 0; } /* * For integers, we allow the casting of a SMALL type to * a larger type, but not vice-versa. */ if (dst->da->type == PW_TYPE_INTEGER64) { switch (src->da->type) { case PW_TYPE_BYTE: dst->vp_integer64 = src->vp_byte; break; case PW_TYPE_SHORT: dst->vp_integer64 = src->vp_short; break; case PW_TYPE_INTEGER: dst->vp_integer64 = src->vp_integer; break; case PW_TYPE_OCTETS: goto do_octets; default: EVAL_DEBUG("Invalid cast to integer64"); return -1; } return 0; } /* * We can compare LONG integers to SHORTER ones, so long * as the long one is on the LHS. */ if (dst->da->type == PW_TYPE_INTEGER) { switch (src->da->type) { case PW_TYPE_BYTE: dst->vp_integer = src->vp_byte; break; case PW_TYPE_SHORT: dst->vp_integer = src->vp_short; break; case PW_TYPE_OCTETS: goto do_octets; default: EVAL_DEBUG("Invalid cast to integer"); return -1; } return 0; } if (dst->da->type == PW_TYPE_SHORT) { switch (src->da->type) { case PW_TYPE_BYTE: dst->vp_short = src->vp_byte; break; case PW_TYPE_OCTETS: goto do_octets; default: EVAL_DEBUG("Invalid cast to short"); return -1; } return 0; } /* * The attribute we've found has to have a size which is * compatible with the type of the destination cast. */ if ((src->length < dict_attr_sizes[dst->da->type][0]) || (src->length > dict_attr_sizes[dst->da->type][1])) { EVAL_DEBUG("Casted attribute is wrong size (%u)", (unsigned int) src->length); return -1; } if (src->da->type == PW_TYPE_OCTETS) { do_octets: switch (dst->da->type) { case PW_TYPE_INTEGER64: dst->vp_integer = ntohll(*(uint64_t const *) src->vp_octets); break; case PW_TYPE_INTEGER: case PW_TYPE_DATE: case PW_TYPE_SIGNED: dst->vp_integer = ntohl(*(uint32_t const *) src->vp_octets); break; case PW_TYPE_SHORT: dst->vp_integer = ntohs(*(uint16_t const *) src->vp_octets); break; case PW_TYPE_BYTE: dst->vp_integer = src->vp_octets[0]; break; default: memcpy(&dst->data, src->vp_octets, src->length); break; } dst->length = src->length; return 0; } /* * Convert host order to network byte order. */ if ((dst->da->type == PW_TYPE_IPV4_ADDR) && ((src->da->type == PW_TYPE_INTEGER) || (src->da->type == PW_TYPE_DATE) || (src->da->type == PW_TYPE_SIGNED))) { dst->vp_ipaddr = htonl(src->vp_integer); } else if ((src->da->type == PW_TYPE_IPV4_ADDR) && ((dst->da->type == PW_TYPE_INTEGER) || (dst->da->type == PW_TYPE_DATE) || (dst->da->type == PW_TYPE_SIGNED))) { dst->vp_integer = htonl(src->vp_ipaddr); } else { /* they're of the same byte order */ memcpy(&dst->data, &src->data, src->length); } dst->length = src->length; return 0; }
static int do_regex(REQUEST *request, value_pair_map_t const *map) { int compare, rcode, ret; regex_t reg, *preg; char *lhs, *rhs; regmatch_t rxmatch[REQUEST_MAX_REGEX + 1]; /* * Expand and then compile it. */ switch (map->src->type) { case TMPL_TYPE_REGEX: rcode = radius_expand_tmpl(&rhs, request, map->src); if (rcode < 0) { EVAL_DEBUG("FAIL %d", __LINE__); return -1; } rad_assert(rhs != NULL); compare = regcomp(®, rhs, REG_EXTENDED | (map->src->tmpl_iflag ? REG_ICASE : 0)); if (compare != 0) { if (debug_flag) { char errbuf[128]; regerror(compare, ®, errbuf, sizeof(errbuf)); ERROR("Failed compiling regular expression: %s", errbuf); } EVAL_DEBUG("FAIL %d", __LINE__); return -1; } preg = ® break; case TMPL_TYPE_REGEX_STRUCT: preg = map->src->tmpl_preg; break; default: rad_assert(0); return -1; } rcode = radius_expand_tmpl(&lhs, request, map->dst); if (rcode < 0) { EVAL_DEBUG("FAIL %d", __LINE__); ret = -1; goto finish; } rad_assert(lhs != NULL); /* * regexec doesn't initialise unused elements */ memset(&rxmatch, 0, sizeof(rxmatch)); compare = regexec(preg, lhs, REQUEST_MAX_REGEX + 1, rxmatch, 0); rad_regcapture(request, compare, lhs, rxmatch); ret = (compare == 0); finish: /* * regcomp allocs extra memory for the expression, so if the * result wasn't cached we need to free it here. */ if (preg == ®) regfree(®); return ret; }