/** Cast a literal vpt to a value_data_t * * @param[in,out] vpt the template to modify * @param[in] da the dictionary attribute to case it to * @return true for success, false for failure. */ bool radius_cast_tmpl(value_pair_tmpl_t *vpt, DICT_ATTR const *da) { VALUE_PAIR *vp; value_data_t *data; rad_assert(vpt != NULL); rad_assert(da != NULL); rad_assert(vpt->type == VPT_TYPE_LITERAL); vp = pairalloc(vpt, da); if (!vp) return false; if (!pairparsevalue(vp, vpt->name)) { pairfree(&vp); return false; } vpt->vpt_length = vp->length; vpt->vpt_value = data = talloc(vpt, value_data_t); if (!vpt->vpt_value) return false; vpt->type = VPT_TYPE_DATA; vpt->vpt_da = da; if (vp->da->flags.is_pointer) { data->ptr = talloc_steal(vpt, vp->data.ptr); vp->data.ptr = NULL; } else { memcpy(data, &vp->data, sizeof(*data)); } pairfree(&vp); return true; }
/* * Expand a template to a string, parse it as type of "cast", and * create a VP from the data. */ static VALUE_PAIR *get_cast_vp(REQUEST *request, value_pair_tmpl_t const *vpt, DICT_ATTR const *cast) { VALUE_PAIR *vp; char *str; vp = pairalloc(request, cast); if (!vp) return NULL; if (vpt->type == VPT_TYPE_DATA) { rad_assert(vp->da->type == vpt->da->type); memcpy(&vp->data, vpt->vpd, sizeof(vp->data)); vp->length = vpt->length; return vp; } str = radius_expand_tmpl(request, vpt); if (!str) { pairfree(&vp); return NULL; } if (!pairparsevalue(vp, str)) { talloc_free(str); pairfree(&vp); return NULL; } return vp; }
/** Expand a template to a string, parse it as type of "cast", and create a VP from the data. */ int tmpl_cast_to_vp(VALUE_PAIR **out, REQUEST *request, value_pair_tmpl_t const *vpt, DICT_ATTR const *cast) { int rcode; VALUE_PAIR *vp; char *str; *out = NULL; vp = pairalloc(request, cast); if (!vp) return -1; if (vpt->type == TMPL_TYPE_DATA) { rad_assert(vp->da->type == vpt->tmpl_da->type); pairdatacpy(vp, vpt->tmpl_da, vpt->tmpl_value, vpt->tmpl_length); *out = vp; return 0; } rcode = radius_expand_tmpl(&str, request, vpt); if (rcode < 0) { pairfree(&vp); return rcode; } if (pairparsevalue(vp, str, 0) < 0) { talloc_free(str); pairfree(&vp); return rcode; } *out = vp; return 0; }
/** Expands an attribute marked with pairmark_xlat * * Writes the new value to the vp. * * @param request Current request. * @param vp to expand. * @return 0 if successful else -1 (on xlat failure) or -2 (on parse failure). * On failure pair will still no longer be marked for xlat expansion. */ int radius_xlat_do(REQUEST *request, VALUE_PAIR *vp) { ssize_t len; char buffer[1024]; if (vp->type != VT_XLAT) return 0; vp->type = VT_DATA; len = radius_xlat(buffer, sizeof(buffer), request, vp->value.xlat, NULL, NULL); rad_const_free(vp->value.xlat); vp->value.xlat = NULL; if (len < 0) { return -1; } /* * Parse the string into a new value. */ if (!pairparsevalue(vp, buffer)){ return -2; } return 0; }
static VALUE_PAIR *rlm_ldap_map_getvalue(REQUEST *request, value_pair_map_t const *map, void *ctx) { rlm_ldap_result_t *self = ctx; VALUE_PAIR *head = NULL, *vp; vp_cursor_t out; int i; paircursor(&out, &head); /* * Iterate over all the retrieved values, * don't try and be clever about changing operators * just use whatever was set in the attribute map. */ for (i = 0; i < self->count; i++) { vp = pairalloc(request, map->dst->da); rad_assert(vp); if (!pairparsevalue(vp, self->values[i])) { RDEBUG("Failed parsing value for \"%s\"", map->dst->da->name); pairbasicfree(vp); continue; } vp->op = map->op; pairinsert(&out, vp); } return head; }
/* * Convert field X to a VP. */ static int csv_map_getvalue(TALLOC_CTX *ctx, VALUE_PAIR **out, REQUEST *request, vp_map_t const *map, void *uctx) { char const *str = uctx; VALUE_PAIR *head = NULL, *vp; vp_cursor_t cursor; DICT_ATTR const *da; rad_assert(ctx != NULL); fr_cursor_init(&cursor, &head); /* * FIXME: allow multiple entries. */ if (map->lhs->type == TMPL_TYPE_ATTR) { da = map->lhs->tmpl_da; } else { char *attr; if (tmpl_aexpand(ctx, &attr, request, map->lhs, NULL, NULL) <= 0) { RWDEBUG("Failed expanding string"); return -1; } da = dict_attrbyname(attr); if (!da) { RWDEBUG("No such attribute '%s'", attr); return -1; } talloc_free(attr); } vp = pairalloc(ctx, da); rad_assert(vp); if (pairparsevalue(vp, str, talloc_array_length(str) - 1) < 0) { char *escaped; escaped = fr_aprints(vp, str, talloc_array_length(str) - 1, '\''); RWDEBUG("Failed parsing value \"%s\" for attribute %s: %s", escaped, map->lhs->tmpl_da->name, fr_strerror()); talloc_free(vp); /* also frees escaped */ return -1; } vp->op = map->op; fr_cursor_merge(&cursor, vp); *out = head; return 0; }
/* * * Verify that a Perl SV is a string and save it in FreeRadius * Value Pair Format * */ static int pairadd_sv(TALLOC_CTX *ctx, REQUEST *request, VALUE_PAIR **vps, char *key, SV *sv, FR_TOKEN op, const char *hashname, const char *list_name) { char *val; VALUE_PAIR *vp; if (SvOK(sv)) { STRLEN len; val = SvPV(sv, len); vp = pairmake(ctx, vps, key, NULL, op); if (!vp) { fail: REDEBUG("Failed to create pair %s:%s %s %s", list_name, key, fr_int2str(fr_tokens, op, "<INVALID>"), val); return -1; } switch (vp->da->type) { case PW_TYPE_STRING: pairstrncpy(vp, val, len); break; case PW_TYPE_OCTETS: pairmemcpy(vp, (uint8_t const *)val, len); break; default: if (pairparsevalue(vp, val, len) < 0) goto fail; } RDEBUG("&%s:%s %s $%s{'%s'} -> '%s'", list_name, key, fr_int2str(fr_tokens, op, "<INVALID>"), hashname, key, val); return 0; } return -1; }
/* * Expand a template to a string, parse it as type of "cast", and * create a VP from the data. */ static int get_cast_vp(VALUE_PAIR **out, REQUEST *request, value_pair_tmpl_t const *vpt, DICT_ATTR const *cast) { int rcode; VALUE_PAIR *vp; char *str; *out = NULL; vp = pairalloc(request, cast); if (!vp) { return -1; } if (vpt->type == VPT_TYPE_DATA) { rad_assert(vp->da->type == vpt->da->type); memcpy(&vp->data, vpt->vpd, sizeof(vp->data)); vp->length = vpt->length; goto finish; } rcode = radius_expand_tmpl(&str, request, vpt); if (rcode < 0) { pairfree(&vp); return rcode; } if (!pairparsevalue(vp, str)) { talloc_free(str); pairfree(&vp); return -1; } finish: *out = vp; return 0; }
/* * Compare two pair lists except for the password information. * For every element in "check" at least one matching copy must * be present in "reply". * * Return 0 on match. */ int paircmp(REQUEST *req, VALUE_PAIR *request, VALUE_PAIR *check, VALUE_PAIR **reply) { VALUE_PAIR *check_item; VALUE_PAIR *auth_item; int result = 0; int compare; int other; #ifdef HAVE_REGEX_H regex_t reg; #endif for (check_item = check; check_item != NULL; check_item = check_item->next) { /* * If the user is setting a configuration value, * then don't bother comparing it to any attributes * sent to us by the user. It ALWAYS matches. */ if ((check_item->operator == T_OP_SET) || (check_item->operator == T_OP_ADD)) { continue; } switch (check_item->attribute) { /* * Attributes we skip during comparison. * These are "server" check items. */ case PW_CRYPT_PASSWORD: case PW_AUTH_TYPE: case PW_AUTZ_TYPE: case PW_ACCT_TYPE: case PW_SESSION_TYPE: case PW_STRIP_USER_NAME: continue; break; /* * IF the password attribute exists, THEN * we can do comparisons against it. If not, * then the request did NOT contain a * User-Password attribute, so we CANNOT do * comparisons against it. * * This hack makes CHAP-Password work.. */ case PW_PASSWORD: if (pairfind(request, PW_PASSWORD) == NULL) { continue; } break; } /* * See if this item is present in the request. */ other = otherattr(check_item->attribute); auth_item = request; try_again: for (; auth_item != NULL; auth_item = auth_item->next) { if (auth_item->attribute == other || other == 0) break; } /* * Not found, it's not a match. */ if (auth_item == NULL) { /* * Didn't find it. If we were *trying* * to not find it, then we succeeded. */ if (check_item->operator == T_OP_CMP_FALSE) return 0; else return -1; } /* * Else we found it, but we were trying to not * find it, so we failed. */ if (check_item->operator == T_OP_CMP_FALSE) return -1; /* * We've got to xlat the string before doing * the comparison. */ if (check_item->flags.do_xlat) { int rcode; char buffer[sizeof(check_item->strvalue)]; check_item->flags.do_xlat = 0; rcode = radius_xlat(buffer, sizeof(buffer), check_item->strvalue, req, NULL); /* * Parse the string into a new value. */ pairparsevalue(check_item, buffer); } /* * OK it is present now compare them. */ compare = paircompare(req, auth_item, check_item, check, reply); switch (check_item->operator) { case T_OP_EQ: default: radlog(L_ERR, "Invalid operator for item %s: " "reverting to '=='", check_item->name); /*FALLTHRU*/ case T_OP_CMP_TRUE: /* compare always == 0 */ case T_OP_CMP_FALSE: /* compare always == 1 */ case T_OP_CMP_EQ: if (compare != 0) result = -1; break; case T_OP_NE: if (compare == 0) result = -1; break; case T_OP_LT: if (compare >= 0) result = -1; break; case T_OP_GT: if (compare <= 0) result = -1; break; case T_OP_LE: if (compare > 0) result = -1; break; case T_OP_GE: if (compare < 0) result = -1; break; #ifdef HAVE_REGEX_H case T_OP_REG_EQ: { int i; regmatch_t rxmatch[9]; /* * Include substring matches. */ regcomp(®, (char *)check_item->strvalue, REG_EXTENDED); compare = regexec(®, (char *)auth_item->strvalue, REQUEST_MAX_REGEX + 1, rxmatch, 0); regfree(®); /* * Add %{0}, %{1}, etc. */ for (i = 0; i <= REQUEST_MAX_REGEX; i++) { char *p; char buffer[sizeof(check_item->strvalue)]; /* * Didn't match: delete old * match, if it existed. */ if ((compare != 0) || (rxmatch[i].rm_so == -1)) { p = request_data_get(req, req, REQUEST_DATA_REGEX | i); if (p) { free(p); continue; } /* * No previous match * to delete, stop. */ break; } /* * Copy substring into buffer. */ memcpy(buffer, auth_item->strvalue + rxmatch[i].rm_so, rxmatch[i].rm_eo - rxmatch[i].rm_so); buffer[rxmatch[i].rm_eo - rxmatch[i].rm_so] = '\0'; /* * Copy substring, and add it to * the request. * * Note that we don't check * for out of memory, which is * the only error we can get... */ p = strdup(buffer); request_data_add(req, req, REQUEST_DATA_REGEX | i, p, free); } } if (compare != 0) result = -1; break; case T_OP_REG_NE: regcomp(®, (char *)check_item->strvalue, REG_EXTENDED|REG_NOSUB); compare = regexec(®, (char *)auth_item->strvalue, 0, NULL, 0); regfree(®); if (compare == 0) result = -1; break; #endif } /* switch over the operator of the check item */ /* * This attribute didn't match, but maybe there's * another of the same attribute, which DOES match. */ if (result != 0) { auth_item = auth_item->next; result = 0; goto try_again; } } /* for every entry in the check item list */ return 0; /* it matched */ }
/* * 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; }
/** * @brief Compare two pair lists except for the password information. * * For every element in "check" at least one matching copy must * be present in "reply". * * @param req Current request * @param request request valuepairs * @param check check/control valuepairs * @param[in,out] reply reply value pairs * * @return 0 on match. */ int paircompare(REQUEST *req, VALUE_PAIR *request, VALUE_PAIR *check, VALUE_PAIR **reply) { VALUE_PAIR *check_item; VALUE_PAIR *auth_item; int result = 0; int compare; int other; for (check_item = check; check_item != NULL; check_item = check_item->next) { /* * If the user is setting a configuration value, * then don't bother comparing it to any attributes * sent to us by the user. It ALWAYS matches. */ if ((check_item->operator == T_OP_SET) || (check_item->operator == T_OP_ADD)) { continue; } switch (check_item->attribute) { /* * Attributes we skip during comparison. * These are "server" check items. */ case PW_CRYPT_PASSWORD: case PW_AUTH_TYPE: case PW_AUTZ_TYPE: case PW_ACCT_TYPE: case PW_SESSION_TYPE: case PW_STRIP_USER_NAME: continue; break; /* * IF the password attribute exists, THEN * we can do comparisons against it. If not, * then the request did NOT contain a * User-Password attribute, so we CANNOT do * comparisons against it. * * This hack makes CHAP-Password work.. */ case PW_USER_PASSWORD: if (check_item->operator == T_OP_CMP_EQ) { DEBUG("WARNING: Found User-Password == \"...\"."); DEBUG("WARNING: Are you sure you don't mean Cleartext-Password?"); DEBUG("WARNING: See \"man rlm_pap\" for more information."); } if (pairfind(request, PW_USER_PASSWORD, 0) == NULL) { continue; } break; } /* * See if this item is present in the request. */ other = otherattr(check_item->attribute); auth_item = request; try_again: if (other >= 0) { for (; auth_item != NULL; auth_item = auth_item->next) { if (auth_item->attribute == (unsigned int) other || other == 0) break; } } /* * Not found, it's not a match. */ if (auth_item == NULL) { /* * Didn't find it. If we were *trying* * to not find it, then we succeeded. */ if (check_item->operator == T_OP_CMP_FALSE) continue; else return -1; } /* * Else we found it, but we were trying to not * find it, so we failed. */ if (check_item->operator == T_OP_CMP_FALSE) return -1; /* * We've got to xlat the string before doing * the comparison. */ if (check_item->flags.do_xlat) { int rcode; char buffer[sizeof(check_item->vp_strvalue)]; check_item->flags.do_xlat = 0; rcode = radius_xlat(buffer, sizeof(buffer), check_item->vp_strvalue, req, NULL); /* * Parse the string into a new value. */ pairparsevalue(check_item, buffer); } /* * OK it is present now compare them. */ compare = radius_callback_compare(req, auth_item, check_item, check, reply); switch (check_item->operator) { case T_OP_EQ: default: radlog(L_INFO, "Invalid operator for item %s: " "reverting to '=='", check_item->name); /*FALLTHRU*/ case T_OP_CMP_TRUE: /* compare always == 0 */ case T_OP_CMP_FALSE: /* compare always == 1 */ case T_OP_CMP_EQ: if (compare != 0) result = -1; break; case T_OP_NE: if (compare == 0) result = -1; break; case T_OP_LT: if (compare >= 0) result = -1; break; case T_OP_GT: if (compare <= 0) result = -1; break; case T_OP_LE: if (compare > 0) result = -1; break; case T_OP_GE: if (compare < 0) result = -1; break; #ifdef HAVE_REGEX_H case T_OP_REG_EQ: case T_OP_REG_NE: if (compare != 0) result = -1; break; #endif } /* switch over the operator of the check item */ /* * This attribute didn't match, but maybe there's * another of the same attribute, which DOES match. */ if ((result != 0) && (other >= 0)) { auth_item = auth_item->next; result = 0; goto try_again; } } /* for every entry in the check item list */ return result; }
/* * *presult is "did comparison match or not" */ static int radius_do_cmp(REQUEST *request, int *presult, FR_TOKEN lt, const char *pleft, FR_TOKEN token, FR_TOKEN rt, const char *pright, int cflags, int modreturn) { int result; uint32_t lint, rint; VALUE_PAIR *vp = NULL; #ifdef HAVE_REGEX_H char buffer[8192]; #else cflags = cflags; /* -Wunused */ #endif rt = rt; /* -Wunused */ if (lt == T_BARE_WORD) { /* * Maybe check the last return code. */ if (token == T_OP_CMP_TRUE) { int isreturn; /* * Looks like a return code, treat is as such. */ isreturn = fr_str2int(modreturn_table, pleft, -1); if (isreturn != -1) { *presult = (modreturn == isreturn); return TRUE; } } /* * Bare words on the left can be attribute names. */ if (radius_get_vp(request, pleft, &vp)) { VALUE_PAIR myvp; /* * VP exists, and that's all we're looking for. */ if (token == T_OP_CMP_TRUE) { *presult = (vp != NULL); return TRUE; } if (!vp) { DICT_ATTR *da; /* * The attribute on the LHS may * have been a dynamically * registered callback. i.e. it * doesn't exist as a VALUE_PAIR. * If so, try looking for it. */ da = dict_attrbyname(pleft); if (da && (da->vendor == 0) && radius_find_compare(da->attr)) { VALUE_PAIR *check = pairmake(pleft, pright, token); *presult = (radius_callback_compare(request, NULL, check, NULL, NULL) == 0); RDEBUG3(" Callback returns %d", *presult); pairfree(&check); return TRUE; } RDEBUG2(" (Attribute %s was not found)", pleft); *presult = 0; return TRUE; } #ifdef HAVE_REGEX_H /* * Regex comparisons treat everything as * strings. */ if ((token == T_OP_REG_EQ) || (token == T_OP_REG_NE)) { vp_prints_value(buffer, sizeof(buffer), vp, 0); pleft = buffer; goto do_checks; } #endif memcpy(&myvp, vp, sizeof(myvp)); if (!pairparsevalue(&myvp, pright)) { RDEBUG2("Failed parsing \"%s\": %s", pright, fr_strerror()); return FALSE; } myvp.operator = token; *presult = paircmp(&myvp, vp); RDEBUG3(" paircmp -> %d", *presult); return TRUE; } /* else it's not a VP in a list */ } #ifdef HAVE_REGEX_H do_checks: #endif switch (token) { case T_OP_GE: case T_OP_GT: case T_OP_LE: case T_OP_LT: if (!all_digits(pright)) { RDEBUG2(" (Right field is not a number at: %s)", pright); return FALSE; } rint = strtoul(pright, NULL, 0); if (!all_digits(pleft)) { RDEBUG2(" (Left field is not a number at: %s)", pleft); return FALSE; } lint = strtoul(pleft, NULL, 0); break; default: lint = rint = 0; /* quiet the compiler */ break; } switch (token) { case T_OP_CMP_TRUE: /* * Check for truth or falsehood. */ if (all_digits(pleft)) { lint = strtoul(pleft, NULL, 0); result = (lint != 0); } else { result = (*pleft != '\0'); } break; case T_OP_CMP_EQ: result = (strcmp(pleft, pright) == 0); break; case T_OP_NE: result = (strcmp(pleft, pright) != 0); break; case T_OP_GE: result = (lint >= rint); break; case T_OP_GT: result = (lint > rint); break; case T_OP_LE: result = (lint <= rint); break; case T_OP_LT: result = (lint < rint); break; #ifdef HAVE_REGEX_H case T_OP_REG_EQ: { int i, compare; regex_t reg; regmatch_t rxmatch[REQUEST_MAX_REGEX + 1]; /* * Include substring matches. */ compare = regcomp(®, pright, cflags); if (compare != 0) { if (debug_flag) { char errbuf[128]; regerror(compare, ®, errbuf, sizeof(errbuf)); DEBUG("ERROR: Failed compiling regular expression: %s", errbuf); } return FALSE; } compare = regexec(®, pleft, 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, pleft + 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); } result = (compare == 0); } break; case T_OP_REG_NE: { int compare; regex_t reg; regmatch_t rxmatch[REQUEST_MAX_REGEX + 1]; /* * Include substring matches. */ compare = regcomp(®, pright, cflags); if (compare != 0) { if (debug_flag) { char errbuf[128]; regerror(compare, ®, errbuf, sizeof(errbuf)); DEBUG("ERROR: Failed compiling regular expression: %s", errbuf); } return FALSE; } compare = regexec(®, pleft, REQUEST_MAX_REGEX + 1, rxmatch, 0); regfree(®); result = (compare != 0); } break; #endif default: DEBUG("ERROR: Comparison operator %s is not supported", fr_token_name(token)); result = FALSE; break; } *presult = result; return TRUE; }
/* * Create a VALUE_PAIR from an ASCII attribute and value. */ VALUE_PAIR *pairmake(const char *attribute, const char *value, int operator) { DICT_ATTR *da; VALUE_PAIR *vp; char *tc, *ts; signed char tag; int found_tag; char buffer[64]; const char *attrname = attribute; /* * Check for tags in 'Attribute:Tag' format. */ found_tag = 0; tag = 0; ts = strrchr(attribute, ':'); if (ts && !ts[1]) { fr_strerror_printf("Invalid tag for attribute %s", attribute); return NULL; } if (ts && ts[1]) { strlcpy(buffer, attribute, sizeof(buffer)); attrname = buffer; ts = strrchr(attrname, ':'); /* Colon found with something behind it */ if (ts[1] == '*' && ts[2] == 0) { /* Wildcard tag for check items */ tag = TAG_ANY; *ts = 0; } else if ((ts[1] >= '0') && (ts[1] <= '9')) { /* It's not a wild card tag */ tag = strtol(ts + 1, &tc, 0); if (tc && !*tc && TAG_VALID_ZERO(tag)) *ts = 0; else tag = 0; } else { fr_strerror_printf("Invalid tag for attribute %s", attribute); return NULL; } found_tag = 1; } /* * It's not found in the dictionary, so we use * another method to create the attribute. */ if ((da = dict_attrbyname(attrname)) == NULL) { return pairmake_any(attrname, value, operator); } if ((vp = pairalloc(da)) == NULL) { fr_strerror_printf("out of memory"); return NULL; } vp->operator = (operator == 0) ? T_OP_EQ : operator; /* Check for a tag in the 'Merit' format of: * :Tag:Value. Print an error if we already found * a tag in the Attribute. */ if (value && (*value == ':' && da->flags.has_tag)) { /* If we already found a tag, this is invalid */ if(found_tag) { fr_strerror_printf("Duplicate tag %s for attribute %s", value, vp->name); DEBUG("Duplicate tag %s for attribute %s\n", value, vp->name); pairbasicfree(vp); return NULL; } /* Colon found and attribute allows a tag */ if (value[1] == '*' && value[2] == ':') { /* Wildcard tag for check items */ tag = TAG_ANY; value += 3; } else { /* Real tag */ tag = strtol(value + 1, &tc, 0); if (tc && *tc==':' && TAG_VALID_ZERO(tag)) value = tc + 1; else tag = 0; } found_tag = 1; } if (found_tag) { vp->flags.tag = tag; } switch (vp->operator) { default: break; /* * For =* and !* operators, the value is irrelevant * so we return now. */ case T_OP_CMP_TRUE: case T_OP_CMP_FALSE: vp->vp_strvalue[0] = '\0'; vp->length = 0; return vp; break; /* * Regular expression comparison of integer attributes * does a STRING comparison of the names of their * integer attributes. */ case T_OP_REG_EQ: /* =~ */ case T_OP_REG_NE: /* !~ */ if (!value) { fr_strerror_printf("No regular expression found in %s", vp->name); pairbasicfree(vp); return NULL; } strlcpy(vp->vp_strvalue, value, sizeof(vp->vp_strvalue)); vp->length = strlen(vp->vp_strvalue); /* * If anything goes wrong, this is a run-time error, * not a compile-time error. */ return vp; } /* * FIXME: if (strcasecmp(attribute, vp->name) != 0) * then the user MAY have typed in the attribute name * as Vendor-%d-Attr-%d, and the value MAY be octets. * * We probably want to fix pairparsevalue to accept * octets as values for any attribute. */ if (value && (pairparsevalue(vp, value) == NULL)) { pairbasicfree(vp); return NULL; } return vp; }
/** 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; vp_cursor_t cursor; rad_assert(request != NULL); rad_assert(map != NULL); *out = NULL; /* * Special case for !*, we don't need to parse the value, just allocate an attribute with * the right operator. */ if (map->op == T_OP_CMP_FALSE) { 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 found the current list, and change * the op. */ if ((map->dst->type == VPT_TYPE_LIST) && (map->src->type == VPT_TYPE_LIST)) { from = radius_list(request, 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 = paircursor(&cursor, &found); vp; vp = pairnext(&cursor)) { vp->op = T_OP_ADD; } *out = found; return 0; } /* * Deal with all non-list founding operations. */ da = map->dst->da ? map->dst->da : map->src->da; switch (map->src->type) { case VPT_TYPE_XLAT: 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) { case VPT_TYPE_XLAT: rad_assert(map->dst->da); /* Need to know where were going to write the new attribute */ /* * Don't call unnecessary expansions */ if (strchr(map->src->name, '%') != NULL) { ssize_t slen; char *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; } /* FALL-THROUGH */ 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)); 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; } /* * Special case, destination is a list, found all instance of an attribute. */ if (map->dst->type == VPT_TYPE_LIST) { 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 = paircursor(&cursor, &found); vp; vp = pairnext(&cursor)) { vp->op = T_OP_ADD; } *out = found; return 0; } /* * FIXME: allow tag references? */ found = pairfind(*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; } /* * Copy the data over verbatim, assuming it's * actually data. */ // rad_assert(found->type == VT_DATA); vp = paircopyvpdata(request, da, found); if (!vp) { return -1; } vp->op = map->op; break; case VPT_TYPE_DATA: 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; }
/** Process map which has exec as a src * * Evaluate maps which specify exec as a src. This may be used by various sorts of update sections, and so * has been broken out into it's own function. * * @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) must be VPT_TYPE_ATTR or VPT_TYPE_LIST. The RHS (src) must be VPT_TYPE_EXEC. * @return -1 on failure, 0 on success. */ int radius_mapexec(VALUE_PAIR **out, REQUEST *request, value_pair_map_t const *map) { int result; char *expanded = NULL; char answer[1024]; VALUE_PAIR **input_pairs = NULL; VALUE_PAIR **output_pairs = NULL; *out = NULL; rad_assert(map->src->type == VPT_TYPE_EXEC); rad_assert((map->dst->type == VPT_TYPE_ATTR) || (map->dst->type == VPT_TYPE_LIST)); /* * We always put the request pairs into the environment */ input_pairs = radius_list(request, PAIR_LIST_REQUEST); /* * Automagically switch output type depending on our destination * If dst is a list, then we create attributes from the output of the program * if dst is an attribute, then we create an attribute of that type and then * call pairparsevalue on the output of the script. */ out[0] = '\0'; result = radius_exec_program(request, map->src->name, true, true, answer, sizeof(answer), input_pairs ? *input_pairs : NULL, (map->dst->type == VPT_TYPE_LIST) ? output_pairs : NULL); talloc_free(expanded); if (result != 0) { REDEBUG("%s", answer); talloc_free(output_pairs); return -1; } switch (map->dst->type) { case VPT_TYPE_LIST: if (!output_pairs) { return -2; } *out = *output_pairs; return 0; case VPT_TYPE_ATTR: { VALUE_PAIR *vp; vp = pairalloc(request, map->dst->da); if (!vp) return -1; vp->op = map->op; if (!pairparsevalue(vp, answer)) { pairfree(&vp); return -2; } *out = vp; return 0; } default: rad_assert(0); } return -1; }
static int do_attr_rewrite(void *instance, REQUEST *request) { rlm_attr_rewrite_t *data = (rlm_attr_rewrite_t *) instance; int ret = RLM_MODULE_NOOP; VALUE_PAIR *attr_vp = NULL; VALUE_PAIR *tmp = NULL; regex_t preg; regmatch_t pmatch[9]; int cflags = 0; int err = 0; char done_xlat = 0; unsigned int len = 0; char err_msg[MAX_STRING_LEN]; unsigned int i = 0; unsigned int j = 0; unsigned int counter = 0; char new_str[MAX_STRING_LEN]; char *ptr, *ptr2; char search_STR[MAX_STRING_LEN]; char replace_STR[MAX_STRING_LEN]; if ((attr_vp = pairfind(request->config_items, PW_REWRITE_RULE)) != NULL){ if (data->name == NULL || strcmp(data->name,attr_vp->vp_strvalue)) return RLM_MODULE_NOOP; } if (data->new_attr){ /* new_attribute = yes */ if (!radius_xlat(replace_STR, sizeof(replace_STR), data->replace, request, NULL)) { DEBUG2("%s: xlat on replace string failed.", data->name); return ret; } attr_vp = pairmake(data->attribute,replace_STR,0); if (attr_vp == NULL){ DEBUG2("%s: Could not add new attribute %s with value '%s'", data->name, data->attribute,replace_STR); return ret; } switch(data->searchin){ case RLM_REGEX_INPACKET: pairadd(&request->packet->vps,attr_vp); break; case RLM_REGEX_INCONFIG: pairadd(&request->config_items,attr_vp); break; case RLM_REGEX_INREPLY: pairadd(&request->reply->vps,attr_vp); break; case RLM_REGEX_INPROXY: if (!request->proxy) { pairbasicfree(attr_vp); return RLM_MODULE_NOOP; } pairadd(&request->proxy->vps, attr_vp); break; case RLM_REGEX_INPROXYREPLY: if (!request->proxy_reply) { pairbasicfree(attr_vp); return RLM_MODULE_NOOP; } pairadd(&request->proxy_reply->vps, attr_vp); break; default: radlog(L_ERR, "%s: Illegal value for searchin. Changing to packet.", data->name); data->searchin = RLM_REGEX_INPACKET; pairadd(&request->packet->vps,attr_vp); break; } DEBUG2("%s: Added attribute %s with value '%s'", data->name,data->attribute,replace_STR); ret = RLM_MODULE_OK; } else { int replace_len = 0; /* new_attribute = no */ switch (data->searchin) { case RLM_REGEX_INPACKET: if (data->attr_num == PW_USER_NAME) attr_vp = request->username; else if (data->attr_num == PW_USER_PASSWORD) attr_vp = request->password; else tmp = request->packet->vps; break; case RLM_REGEX_INCONFIG: tmp = request->config_items; break; case RLM_REGEX_INREPLY: tmp = request->reply->vps; break; case RLM_REGEX_INPROXYREPLY: if (!request->proxy_reply) return RLM_MODULE_NOOP; tmp = request->proxy_reply->vps; break; case RLM_REGEX_INPROXY: if (!request->proxy) return RLM_MODULE_NOOP; tmp = request->proxy->vps; break; default: radlog(L_ERR, "%s: Illegal value for searchin. Changing to packet.", data->name); data->searchin = RLM_REGEX_INPACKET; attr_vp = pairfind(request->packet->vps, data->attr_num); break; } do_again: if (tmp != NULL) attr_vp = pairfind(tmp, data->attr_num); if (attr_vp == NULL) { DEBUG2("%s: Could not find value pair for attribute %s", data->name,data->attribute); return ret; } if (attr_vp->vp_strvalue == NULL || attr_vp->length == 0){ DEBUG2("%s: Attribute %s string value NULL or of zero length", data->name,data->attribute); return ret; } cflags |= REG_EXTENDED; if (data->nocase) cflags |= REG_ICASE; if (!radius_xlat(search_STR, sizeof(search_STR), data->search, request, NULL) && data->search_len != 0) { DEBUG2("%s: xlat on search string failed.", data->name); return ret; } if ((err = regcomp(&preg,search_STR,cflags))) { regerror(err, &preg, err_msg, MAX_STRING_LEN); DEBUG2("%s: regcomp() returned error: %s", data->name,err_msg); return ret; } if ((attr_vp->type == PW_TYPE_IPADDR) && (attr_vp->vp_strvalue[0] == '\0')) { inet_ntop(AF_INET, &(attr_vp->vp_ipaddr), attr_vp->vp_strvalue, sizeof(attr_vp->vp_strvalue)); } ptr = new_str; ptr2 = attr_vp->vp_strvalue; counter = 0; for ( i = 0 ;i < (unsigned)data->num_matches; i++) { err = regexec(&preg, ptr2, REQUEST_MAX_REGEX, pmatch, 0); if (err == REG_NOMATCH) { if (i == 0) { DEBUG2("%s: Does not match: %s = %s", data->name, data->attribute, attr_vp->vp_strvalue); regfree(&preg); goto to_do_again; } else break; } if (err != 0) { regfree(&preg); radlog(L_ERR, "%s: match failure for attribute %s with value '%s'", data->name, data->attribute, attr_vp->vp_strvalue); return ret; } if (pmatch[0].rm_so == -1) break; len = pmatch[0].rm_so; if (data->append) { len = len + (pmatch[0].rm_eo - pmatch[0].rm_so); } counter += len; if (counter >= MAX_STRING_LEN) { regfree(&preg); DEBUG2("%s: Replacement out of limits for attribute %s with value '%s'", data->name, data->attribute, attr_vp->vp_strvalue); return ret; } memcpy(ptr, ptr2,len); ptr += len; *ptr = '\0'; ptr2 += pmatch[0].rm_eo; if (i == 0){ /* * We only run on the first match, sorry */ for(j = 0; j <= REQUEST_MAX_REGEX; j++){ char *p; char buffer[sizeof(attr_vp->vp_strvalue)]; /* * Stolen from src/main/valuepair.c, paircompare() */ /* * Delete old matches if the corresponding match does not * exist in the current regex */ if (pmatch[j].rm_so == -1){ p = request_data_get(request,request,REQUEST_DATA_REGEX | j); if (p){ free(p); continue; } break; } memcpy(buffer, attr_vp->vp_strvalue + pmatch[j].rm_so, pmatch[j].rm_eo - pmatch[j].rm_so); buffer[pmatch[j].rm_eo - pmatch[j].rm_so] = '\0'; p = strdup(buffer); request_data_add(request,request,REQUEST_DATA_REGEX | j,p,free); } } if (!done_xlat){ if (data->replace_len != 0 && radius_xlat(replace_STR, sizeof(replace_STR), data->replace, request, NULL) == 0) { DEBUG2("%s: xlat on replace string failed.", data->name); return ret; } replace_len = (data->replace_len != 0) ? strlen(replace_STR) : 0; done_xlat = 1; } counter += replace_len; if (counter >= MAX_STRING_LEN) { regfree(&preg); DEBUG2("%s: Replacement out of limits for attribute %s with value '%s'", data->name, data->attribute, attr_vp->vp_strvalue); return ret; } if (replace_len){ memcpy(ptr, replace_STR, replace_len); ptr += replace_len; *ptr = '\0'; } } regfree(&preg); len = strlen(ptr2) + 1; /* We add the ending NULL */ counter += len; if (counter >= MAX_STRING_LEN){ DEBUG2("%s: Replacement out of limits for attribute %s with value '%s'", data->name, data->attribute, attr_vp->vp_strvalue); return ret; } memcpy(ptr, ptr2, len); ptr[len] = '\0'; DEBUG2("%s: Changed value for attribute %s from '%s' to '%s'", data->name, data->attribute, attr_vp->vp_strvalue, new_str); if (pairparsevalue(attr_vp, new_str) == NULL) { DEBUG2("%s: Could not write value '%s' into attribute %s: %s", data->name, new_str, data->attribute, fr_strerror()); return ret; } to_do_again: ret = RLM_MODULE_OK; if (tmp != NULL){ tmp = attr_vp->next; if (tmp != NULL) goto do_again; } } return ret; }
/** 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; }
/** 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; }
/************************************************************************* * * Function: sql_userparse * * Purpose: Read entries from the database and fill VALUE_PAIR structures * *************************************************************************/ int sql_userparse(TALLOC_CTX *ctx, VALUE_PAIR **head, rlm_sql_row_t row) { VALUE_PAIR *vp; char const *ptr, *value; char buf[MAX_STRING_LEN]; char do_xlat = 0; FR_TOKEN token, operator = T_EOL; /* * Verify the 'Attribute' field */ if (!row[2] || row[2][0] == '\0') { ERROR("rlm_sql: The 'Attribute' field is empty or NULL, skipping the entire row"); return -1; } /* * Verify the 'op' field */ if (row[4] != NULL && row[4][0] != '\0') { ptr = row[4]; operator = gettoken(&ptr, buf, sizeof(buf), false); if ((operator < T_OP_ADD) || (operator > T_OP_CMP_EQ)) { ERROR("rlm_sql: Invalid operator \"%s\" for attribute %s", row[4], row[2]); return -1; } } else { /* * Complain about empty or invalid 'op' field */ operator = T_OP_CMP_EQ; ERROR("rlm_sql: The 'op' field for attribute '%s = %s' is NULL, or non-existent.", row[2], row[3]); ERROR("rlm_sql: You MUST FIX THIS if you want the configuration to behave as you expect"); } /* * The 'Value' field may be empty or NULL */ value = row[3]; /* * If we have a new-style quoted string, where the * *entire* string is quoted, do xlat's. */ if (row[3] != NULL && ((row[3][0] == '\'') || (row[3][0] == '`') || (row[3][0] == '"')) && (row[3][0] == row[3][strlen(row[3])-1])) { token = gettoken(&value, buf, sizeof(buf), false); switch (token) { /* * Take the unquoted string. */ case T_SINGLE_QUOTED_STRING: case T_DOUBLE_QUOTED_STRING: value = buf; break; /* * Mark the pair to be allocated later. */ case T_BACK_QUOTED_STRING: value = NULL; do_xlat = 1; break; /* * Keep the original string. */ default: value = row[3]; break; } } /* * Create the pair */ vp = pairmake(ctx, NULL, row[2], NULL, operator); if (!vp) { ERROR("rlm_sql: Failed to create the pair: %s", fr_strerror()); return -1; } if (do_xlat) { if (pairmark_xlat(vp, value) < 0) { ERROR("rlm_sql: Error marking pair for xlat"); talloc_free(vp); return -1; } } else { if (pairparsevalue(vp, value, -1) < 0) { ERROR("rlm_sql: Error parsing value: %s", fr_strerror()); talloc_free(vp); return -1; } } /* * Add the pair into the packet */ pairadd(head, vp); return 0; }
/* * Read a valuepair from a buffer, and advance pointer. * Sets *eol to T_EOL if end of line was encountered. */ VALUE_PAIR *pairread(const char **ptr, FR_TOKEN *eol) { char buf[64]; char attr[64]; char value[1024], *q; const char *p; FR_TOKEN token, t, xlat; VALUE_PAIR *vp; size_t len; *eol = T_OP_INVALID; p = *ptr; while ((*p == ' ') || (*p == '\t')) p++; if (!*p) { *eol = T_OP_INVALID; fr_strerror_printf("No token read where we expected an attribute name"); return NULL; } if (*p == '#') { *eol = T_HASH; fr_strerror_printf("Read a comment instead of a token"); return NULL; } q = attr; for (len = 0; len < sizeof(attr); len++) { if (valid_attr_name[(int)*p]) { *q++ = *p++; continue; } break; } if (len == sizeof(attr)) { *eol = T_OP_INVALID; fr_strerror_printf("Attribute name is too long"); return NULL; } /* * We may have Foo-Bar:= stuff, so back up. */ if ((len > 0) && (attr[len - 1] == ':')) { p--; len--; } attr[len] = '\0'; *ptr = p; /* Now we should have an operator here. */ token = gettoken(ptr, buf, sizeof(buf)); if (token < T_EQSTART || token > T_EQEND) { fr_strerror_printf("expecting operator"); return NULL; } /* Read value. Note that empty string values are allowed */ xlat = gettoken(ptr, value, sizeof(value)); if (xlat == T_EOL) { fr_strerror_printf("failed to get value"); return NULL; } /* * Peek at the next token. Must be T_EOL, T_COMMA, or T_HASH */ p = *ptr; t = gettoken(&p, buf, sizeof(buf)); if (t != T_EOL && t != T_COMMA && t != T_HASH) { fr_strerror_printf("Expected end of line or comma"); return NULL; } *eol = t; if (t == T_COMMA) { *ptr = p; } vp = NULL; switch (xlat) { /* * Make the full pair now. */ default: vp = pairmake(attr, value, token); break; /* * Perhaps do xlat's */ case T_DOUBLE_QUOTED_STRING: p = strchr(value, '%'); if (p && (p[1] == '{')) { if (strlen(value) >= sizeof(vp->vp_strvalue)) { fr_strerror_printf("Value too long"); return NULL; } vp = pairmake(attr, NULL, token); if (!vp) { *eol = T_OP_INVALID; return NULL; } strlcpy(vp->vp_strvalue, value, sizeof(vp->vp_strvalue)); vp->flags.do_xlat = 1; vp->length = 0; } else { /* * Parse && escape it, as defined by the * data type. */ vp = pairmake(attr, value, token); if (!vp) { *eol = T_OP_INVALID; return NULL; } } break; case T_SINGLE_QUOTED_STRING: vp = pairmake(attr, NULL, token); if (!vp) { *eol = T_OP_INVALID; return NULL; } /* * String and octet types get copied verbatim. */ if ((vp->type == PW_TYPE_STRING) || (vp->type == PW_TYPE_OCTETS)) { strlcpy(vp->vp_strvalue, value, sizeof(vp->vp_strvalue)); vp->length = strlen(vp->vp_strvalue); /* * Everything else gets parsed: it's * DATA, not a string! */ } else if (!pairparsevalue(vp, value)) { pairfree(&vp); *eol = T_OP_INVALID; return NULL; } break; /* * Mark the pair to be allocated later. */ case T_BACK_QUOTED_STRING: if (strlen(value) >= sizeof(vp->vp_strvalue)) { fr_strerror_printf("Value too long"); return NULL; } vp = pairmake(attr, NULL, token); if (!vp) { *eol = T_OP_INVALID; return NULL; } vp->flags.do_xlat = 1; strlcpy(vp->vp_strvalue, value, sizeof(vp->vp_strvalue)); vp->length = 0; break; } /* * If we didn't make a pair, return an error. */ if (!vp) { *eol = T_OP_INVALID; return NULL; } return vp; }
/* * 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 rlm_ldap_map_getvalue(VALUE_PAIR **out, REQUEST *request, value_pair_map_t const *map, void *ctx) { rlm_ldap_result_t *self = ctx; VALUE_PAIR *head = NULL, *vp; vp_cursor_t cursor; int i; fr_cursor_init(&cursor, &head); switch (map->dst->type) { /* * This is a mapping in the form of: * <list>: += <ldap attr> * * Where <ldap attr> is: * <list>:<attr> <op> <value> * * It is to allow for legacy installations which stored * RADIUS control and reply attributes in separate LDAP * attributes. */ case VPT_TYPE_LIST: for (i = 0; i < self->count; i++) { value_pair_map_t *attr = NULL; RDEBUG3("Parsing valuepair string \"%s\"", self->values[i]); if (radius_strpair2map(&attr, request, self->values[i], map->dst->vpt_request, map->dst->vpt_list, REQUEST_CURRENT, PAIR_LIST_REQUEST) < 0) { RWDEBUG("Failed parsing \"%s\" as valuepair, skipping...", self->values[i]); continue; } if (attr->dst->vpt_request != map->dst->vpt_request) { RWDEBUG("valuepair \"%s\" has conflicting request qualifier (%s vs %s), skipping...", self->values[i], fr_int2str(request_refs, attr->dst->vpt_request, "<INVALID>"), fr_int2str(request_refs, map->dst->vpt_request, "<INVALID>")); next_pair: talloc_free(attr); continue; } if ((attr->dst->vpt_list != map->dst->vpt_list)) { RWDEBUG("valuepair \"%s\" has conflicting list qualifier (%s vs %s), skipping...", self->values[i], fr_int2str(pair_lists, attr->dst->vpt_list, "<INVALID>"), fr_int2str(pair_lists, map->dst->vpt_list, "<INVALID>")); goto next_pair; } if (radius_map2vp(&vp, request, attr, NULL) < 0) { RWDEBUG("Failed creating attribute for \"%s\", skipping...", self->values[i]); goto next_pair; } fr_cursor_insert(&cursor, vp); talloc_free(attr); } break; /* * Iterate over all the retrieved values, * don't try and be clever about changing operators * just use whatever was set in the attribute map. */ case VPT_TYPE_ATTR: for (i = 0; i < self->count; i++) { vp = pairalloc(request, map->dst->vpt_da); rad_assert(vp); if (!pairparsevalue(vp, self->values[i])) { RDEBUG("Failed parsing value for \"%s\"", map->dst->vpt_da->name); talloc_free(vp); continue; } vp->op = map->op; fr_cursor_insert(&cursor, vp); } break; default: rad_assert(0); } *out = head; 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; }
/** * @brief Move pairs, replacing/over-writing them, and doing xlat. * * Move attributes from one list to the other * if not already present. */ void pairxlatmove(REQUEST *req, VALUE_PAIR **to, VALUE_PAIR **from) { VALUE_PAIR **tailto, *i, *j, *next; VALUE_PAIR *tailfrom = NULL; VALUE_PAIR *found; /* * Point "tailto" to the end of the "to" list. */ tailto = to; for(i = *to; i; i = i->next) { tailto = &i->next; } /* * Loop over the "from" list. */ for(i = *from; i; i = next) { next = i->next; /* * Don't move 'fallthrough' over. */ if (i->attribute == PW_FALL_THROUGH) { tailfrom = i; continue; } /* * We've got to xlat the string before moving * it over. */ if (i->flags.do_xlat) { int rcode; char buffer[sizeof(i->vp_strvalue)]; i->flags.do_xlat = 0; rcode = radius_xlat(buffer, sizeof(buffer), i->vp_strvalue, req, NULL); /* * Parse the string into a new value. */ pairparsevalue(i, buffer); } found = pairfind(*to, i->attribute, i->vendor); switch (i->operator) { /* * If a similar attribute is found, * delete it. */ case T_OP_SUB: /* -= */ if (found) { if (!i->vp_strvalue[0] || (strcmp((char *)found->vp_strvalue, (char *)i->vp_strvalue) == 0)){ pairdelete(to, found->attribute, found->vendor); /* * 'tailto' may have been * deleted... */ tailto = to; for(j = *to; j; j = j->next) { tailto = &j->next; } } } tailfrom = i; continue; break; /* * Add it, if it's not already there. */ case T_OP_EQ: /* = */ if (found) { tailfrom = i; continue; /* with the loop */ } break; /* * If a similar attribute is found, * replace it with the new one. Otherwise, * add the new one to the list. */ case T_OP_SET: /* := */ if (found) { VALUE_PAIR *vp; vp = found->next; memcpy(found, i, sizeof(*found)); found->next = vp; tailfrom = i; continue; } break; /* * FIXME: Add support for <=, >=, <, > * * which will mean (for integers) * 'make the attribute the smaller, etc' */ /* * Add the new element to the list, even * if similar ones already exist. */ default: case T_OP_ADD: /* += */ break; } if (tailfrom) tailfrom->next = next; else *from = next; /* * If ALL of the 'to' attributes have been deleted, * then ensure that the 'tail' is updated to point * to the head. */ if (!*to) { tailto = to; } *tailto = i; if (i) { i->next = NULL; tailto = &i->next; } } /* loop over the 'from' list */ }
/** Callback for map_to_request * * Performs exactly the same job as map_to_vp, but pulls attribute values from LDAP entries * * @see map_to_vp */ int rlm_ldap_map_getvalue(TALLOC_CTX *ctx, VALUE_PAIR **out, REQUEST *request, vp_map_t const *map, void *uctx) { rlm_ldap_result_t *self = uctx; VALUE_PAIR *head = NULL, *vp; vp_cursor_t cursor; int i; fr_cursor_init(&cursor, &head); switch (map->lhs->type) { /* * This is a mapping in the form of: * <list>: += <ldap attr> * * Where <ldap attr> is: * <list>:<attr> <op> <value> * * It is to allow for legacy installations which stored * RADIUS control and reply attributes in separate LDAP * attributes. */ case TMPL_TYPE_LIST: for (i = 0; i < self->count; i++) { vp_map_t *attr = NULL; RDEBUG3("Parsing valuepair string \"%s\"", self->values[i]->bv_val); if (map_afrom_attr_str(ctx, &attr, self->values[i]->bv_val, map->lhs->tmpl_request, map->lhs->tmpl_list, REQUEST_CURRENT, PAIR_LIST_REQUEST) < 0) { RWDEBUG("Failed parsing \"%s\" as valuepair (%s), skipping...", fr_strerror(), self->values[i]->bv_val); continue; } if (attr->lhs->tmpl_request != map->lhs->tmpl_request) { RWDEBUG("valuepair \"%s\" has conflicting request qualifier (%s vs %s), skipping...", self->values[i]->bv_val, fr_int2str(request_refs, attr->lhs->tmpl_request, "<INVALID>"), fr_int2str(request_refs, map->lhs->tmpl_request, "<INVALID>")); next_pair: talloc_free(attr); continue; } if ((attr->lhs->tmpl_list != map->lhs->tmpl_list)) { RWDEBUG("valuepair \"%s\" has conflicting list qualifier (%s vs %s), skipping...", self->values[i]->bv_val, fr_int2str(pair_lists, attr->lhs->tmpl_list, "<INVALID>"), fr_int2str(pair_lists, map->lhs->tmpl_list, "<INVALID>")); goto next_pair; } if (map_to_vp(request, &vp, request, attr, NULL) < 0) { RWDEBUG("Failed creating attribute for valuepair \"%s\", skipping...", self->values[i]->bv_val); goto next_pair; } fr_cursor_merge(&cursor, vp); talloc_free(attr); /* * Only process the first value, unless the operator is += */ if (map->op != T_OP_ADD) break; } break; /* * Iterate over all the retrieved values, * don't try and be clever about changing operators * just use whatever was set in the attribute map. */ case TMPL_TYPE_ATTR: for (i = 0; i < self->count; i++) { if (!self->values[i]->bv_len) continue; vp = pairalloc(ctx, map->lhs->tmpl_da); rad_assert(vp); if (pairparsevalue(vp, self->values[i]->bv_val, self->values[i]->bv_len) < 0) { char *escaped; escaped = fr_aprints(vp, self->values[i]->bv_val, self->values[i]->bv_len, '"'); RWDEBUG("Failed parsing value \"%s\" for attribute %s: %s", escaped, map->lhs->tmpl_da->name, fr_strerror()); talloc_free(vp); /* also frees escaped */ continue; } vp->op = map->op; fr_cursor_insert(&cursor, vp); /* * Only process the first value, unless the operator is += */ if (map->op != T_OP_ADD) break; } break; default: rad_assert(0); } *out = head; return 0; }