/* * Create a new valuepair. */ VALUE_PAIR *paircreate(int attr, int type) { VALUE_PAIR *vp; DICT_ATTR *da; da = dict_attrbyvalue(attr); if ((vp = pairalloc(da)) == NULL) { fr_strerror_printf("out of memory"); return NULL; } vp->operator = T_OP_EQ; /* * It isn't in the dictionary: update the name. */ if (!da) { char *p = (char *) (vp + 1); vp->vendor = VENDOR(attr); vp->attribute = attr; vp->name = p; vp->type = type; /* be forgiving */ if (!vp_print_name(p, FR_VP_NAME_LEN, vp->attribute)) { free(vp); return NULL; } } return vp; }
/* * Set the SQL user name. * * We don't call the escape function here. The resulting string * will be escaped later in the queries xlat so we don't need to * escape it twice. (it will make things wrong if we have an * escape candidate character in the username) */ int sql_set_user(rlm_sql_t *inst, REQUEST *request, const char *username) { char buffer[254]; VALUE_PAIR *vp = NULL; const char *sqluser; size_t len; if (username != NULL) { sqluser = username; } else if (*inst->config->query_user) { sqluser = inst->config->query_user; } else { return 0; } len = radius_xlat(buffer, sizeof(buffer), sqluser, request, NULL, NULL); if (!len) { return -1; } vp = pairalloc(NULL, inst->sql_user); vp->op = T_OP_SET; strlcpy(vp->vp_strvalue, buffer, sizeof(vp->vp_strvalue)); vp->length = strlen(vp->vp_strvalue); pairadd(&request->packet->vps, vp); RDEBUG2("SQL-User-Name updated"); return 0; }
/* * Set the SQL user name. * * We don't call the escape function here. The resulting string * will be escaped later in the queries xlat so we don't need to * escape it twice. (it will make things wrong if we have an * escape candidate character in the username) */ int sql_set_user(rlm_sql_t *inst, REQUEST *request, char const *username) { char *expanded = NULL; VALUE_PAIR *vp = NULL; char const *sqluser; ssize_t len; if (username != NULL) { sqluser = username; } else if (inst->config->query_user[0] != '\0') { sqluser = inst->config->query_user; } else { return 0; } len = radius_axlat(&expanded, request, sqluser, NULL, NULL); if (len < 0) { return -1; } vp = pairalloc(request->packet, inst->sql_user); if (!vp) { talloc_free(expanded); return -1; } pairstrsteal(vp, expanded); RDEBUG2("SQL-User-Name set to '%s'", vp->vp_strvalue); vp->op = T_OP_SET; pairmove(request, &request->packet->vps, &vp); /* needs to be pair move else op is not respected */ return 0; }
/* * 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; }
/** 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; }
/** Copy data from one VP to another * * Allocate a new pair using da, and copy over the value from the specified * vp. * * @todo Should be able to do type conversions. * * @param[in] ctx for talloc * @param[in] da of new attribute to alloc. * @param[in] vp to copy data from. * @return the new valuepair. */ VALUE_PAIR *paircopyvpdata(TALLOC_CTX *ctx, DICT_ATTR const *da, VALUE_PAIR const *vp) { VALUE_PAIR *n; if (!vp) return NULL; VERIFY(vp); if (da->type != vp->da->type) return NULL; n = pairalloc(ctx, da); if (!n) { return NULL; } memcpy(n, vp, sizeof(*n)); n->da = da; if (n->type == VT_XLAT) { n->value.xlat = talloc_strdup(n, n->value.xlat); } if ((n->da->type == PW_TYPE_TLV) || (n->da->type == PW_TYPE_OCTETS) || (n->da->type == PW_TYPE_STRING)) { if (n->vp_octets != NULL) { n->vp_octets = talloc_memdup(n, vp->vp_octets, n->length); } } n->next = NULL; return n; }
/** Copy data from one VP to another * * Allocate a new pair using da, and copy over the value from the specified * vp. * * @todo Should be able to do type conversions. * * @param[in] da of new attribute to alloc. * @param[in] vp to copy data from. * @return the new valuepair. */ VALUE_PAIR *paircopyvpdata(const DICT_ATTR *da, const VALUE_PAIR *vp) { VALUE_PAIR *n; if (!vp) return NULL; if (da->type != vp->type) return NULL; n = pairalloc(da); if (!n) { return NULL; } memcpy(&(n->data), &(vp->data), sizeof(n->data)); n->length = vp->length; if ((n->type == PW_TYPE_TLV) && (n->vp_tlv != NULL)) { n->vp_tlv = malloc(n->length); memcpy(n->vp_tlv, vp->vp_tlv, n->length); } return n; }
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; }
/** 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; }
/* * Merge a cached entry into a REQUEST. */ static void cache_merge(rlm_cache_t *inst, REQUEST *request, rlm_cache_entry_t *c) { VALUE_PAIR *vp; rad_assert(request != NULL); rad_assert(c != NULL); vp = pairfind(request->config_items, inst->cache_merge->attr, inst->cache_merge->vendor, TAG_ANY); if (vp && (vp->vp_integer == 0)) { RDEBUG2("Told not to merge entry into request"); return; } if (c->control) { RDEBUG2("Merging cached control list:"); rdebug_pair_list(2, request, c->control); vp = paircopy(c->control); pairmove(&request->config_items, &vp); pairfree(&vp); } if (c->request && request->packet) { RDEBUG2("Merging cached request list:"); rdebug_pair_list(2, request, c->request); vp = paircopy(c->request); pairmove(&request->packet->vps, &vp); pairfree(&vp); } if (c->reply && request->reply) { RDEBUG2("Merging cached reply list:"); rdebug_pair_list(2, request, c->reply); vp = paircopy(c->reply); pairmove(&request->reply->vps, &vp); pairfree(&vp); } if (inst->stats) { vp = pairalloc(inst->cache_entry_hits); rad_assert(vp != NULL); vp->vp_integer = c->hits; pairadd(&request->packet->vps, vp); } }
/* * 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; }
/** Create a new valuepair * * If attr and vendor match a dictionary entry then a VP with that DICT_ATTR * will be returned. * * If attr or vendor are uknown will call dict_attruknown to create a dynamic * DICT_ATTR of PW_TYPE_OCTETS. * * Which type of DICT_ATTR the VALUE_PAIR was created with can be determined by * checking @verbatim vp->da->flags.is_unknown @endverbatim. * * @param[in] ctx for allocated memory, usually a pointer to a RADIUS_PACKET * @param[in] attr number. * @param[in] vendor number. * @return the new valuepair or NULL on error. */ VALUE_PAIR *paircreate(TALLOC_CTX *ctx, unsigned int attr, unsigned int vendor) { DICT_ATTR const *da; da = dict_attrbyvalue(attr, vendor); if (!da) { da = dict_unknown_afrom_fields(ctx, attr, vendor); if (!da) { return NULL; } } return pairalloc(ctx, da); }
/** Copy a single valuepair * * Allocate a new valuepair and copy the da from the old vp. * * @param[in] ctx for talloc * @param[in] vp to copy. * @return a copy of the input VP or NULL on error. */ VALUE_PAIR *paircopyvp(TALLOC_CTX *ctx, VALUE_PAIR const *vp) { VALUE_PAIR *n; if (!vp) return NULL; VERIFY_VP(vp); n = pairalloc(ctx, vp->da); if (!n) return NULL; memcpy(n, vp, sizeof(*n)); /* * If the DA is unknown, steal "n" to "ctx". This does * nothing for "n", but will also copy the unknown "da". */ if (n->da->flags.is_unknown) { pairsteal(ctx, n); } n->next = NULL; /* * If it's an xlat, copy the raw string and return early, * so we don't pre-expand or otherwise mangle the VALUE_PAIR. */ if (vp->type == VT_XLAT) { n->xlat = talloc_typed_strdup(n, n->xlat); return n; } switch (vp->da->type) { case PW_TYPE_TLV: case PW_TYPE_OCTETS: n->vp_octets = NULL; /* else pairmemcpy will free vp's value */ pairmemcpy(n, vp->vp_octets, n->vp_length); break; case PW_TYPE_STRING: n->vp_strvalue = NULL; /* else pairstrnpy will free vp's value */ pairstrncpy(n, vp->vp_strvalue, n->vp_length); break; default: break; } return n; }
/** Create a new valuepair * * If attr and vendor match a dictionary entry then a VP with that DICT_ATTR * will be returned. * * If attr or vendor are uknown will call dict_attruknown to create a dynamic * DICT_ATTR of PW_TYPE_OCTETS. * * Which type of DICT_ATTR the VALUE_PAIR was created with can be determined by * checking @verbatim vp->da->flags.is_unknown @endverbatim. * * @param[in] ctx for allocated memory, usually a pointer to a RADIUS_PACKET * @param[in] attr number. * @param[in] vendor number. * @return the new valuepair or NULL on error. */ VALUE_PAIR *paircreate(TALLOC_CTX *ctx, unsigned int attr, unsigned int vendor) { const DICT_ATTR *da; da = dict_attrbyvalue(attr, vendor); if (!da) { da = dict_attrunknown(attr, vendor, true); if (!da) { return NULL; } } return pairalloc(ctx, da); }
/** Copy a single valuepair * * Allocate a new valuepair and copy the da from the old vp. * * @param[in] ctx for talloc * @param[in] vp to copy. * @return a copy of the input VP or NULL on error. */ VALUE_PAIR *paircopyvp(TALLOC_CTX *ctx, VALUE_PAIR const *vp) { VALUE_PAIR *n; if (!vp) return NULL; VERIFY(vp); n = pairalloc(ctx, vp->da); if (!n) { fr_strerror_printf("out of memory"); return NULL; } memcpy(n, vp, sizeof(*n)); /* * Now copy the value */ if (vp->type == VT_XLAT) { n->value.xlat = talloc_strdup(n, n->value.xlat); } n->da = dict_attr_copy(vp->da, true); if (!n->da) { pairbasicfree(n); return NULL; } n->next = NULL; if ((n->da->type == PW_TYPE_TLV) || (n->da->type == PW_TYPE_OCTETS)) { if (n->vp_octets != NULL) { n->vp_octets = talloc_memdup(n, vp->vp_octets, n->length); } } else if (n->da->type == PW_TYPE_STRING) { if (n->vp_strvalue != NULL) { /* * Equivalent to, and faster than strdup. */ n->vp_strvalue = talloc_memdup(n, vp->vp_octets, n->length + 1); } } return n; }
/* * Create a new valuepair. */ VALUE_PAIR *paircreate(int attr, int vendor, int type) { VALUE_PAIR *vp; DICT_ATTR *da; da = dict_attrbyvalue(attr, vendor); if ((vp = pairalloc(da)) == NULL) { return NULL; } /* * It isn't in the dictionary: update the name. */ if (!da) return paircreate_raw(attr, vendor, type, vp); return vp; }
/* * Create a new valuepair. */ VALUE_PAIR *paircreate(int attr, int type) { VALUE_PAIR *vp; DICT_ATTR *da; da = dict_attrbyvalue(attr); if ((vp = pairalloc(da)) == NULL) { fr_strerror_printf("out of memory"); return NULL; } vp->operator = T_OP_EQ; /* * It isn't in the dictionary: update the name. */ if (!da) return paircreate_raw(attr, type, vp); return vp; }
/* * 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; }
/** 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; }
/* * 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; }
/** 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; }
/** Unpack data * * Example: %{unpack:&Class 0 integer} * * Expands Class, treating octet at offset 0 (bytes 0-3) as an "integer". */ static ssize_t unpack_xlat(UNUSED void *instance, REQUEST *request, char const *fmt, char *out, size_t outlen) { char *data_name, *data_size, *data_type; char *p; size_t len, input_len; int offset; PW_TYPE type; DICT_ATTR const *da; VALUE_PAIR *vp, *cast; uint8_t const *input; char buffer[256]; uint8_t blob[256]; /* * FIXME: copy only the fields here, as we parse them. */ strlcpy(buffer, fmt, sizeof(buffer)); p = buffer; while (isspace((int) *p)) p++; /* skip leading spaces */ data_name = p; while (*p && !isspace((int) *p)) p++; if (!*p) { error: REDEBUG("Format string should be '<data> <offset> <type>' e.g. '&Class 1 integer'"); nothing: *out = '\0'; return -1; } while (isspace((int) *p)) *(p++) = '\0'; if (!*p) GOTO_ERROR; data_size = p; while (*p && !isspace((int) *p)) p++; if (!*p) GOTO_ERROR; while (isspace((int) *p)) *(p++) = '\0'; if (!*p) GOTO_ERROR; data_type = p; while (*p && !isspace((int) *p)) p++; if (*p) GOTO_ERROR; /* anything after the type is an error */ /* * Attribute reference */ if (*data_name == '&') { if (radius_get_vp(&vp, request, data_name) < 0) goto nothing; if ((vp->da->type != PW_TYPE_OCTETS) && (vp->da->type != PW_TYPE_STRING)) { REDEBUG("unpack requires the input attribute to be 'string' or 'octets'"); goto nothing; } input = vp->vp_octets; input_len = vp->vp_length; } else if ((data_name[0] == '0') && (data_name[1] == 'x')) { /* * Hex data. */ len = strlen(data_name + 2); if ((len & 0x01) != 0) { RDEBUG("Invalid hex string in '%s'", data_name); goto nothing; } input = blob; input_len = fr_hex2bin(blob, sizeof(blob), data_name + 2, len); } else { GOTO_ERROR; } offset = (int) strtoul(data_size, &p, 10); if (*p) { REDEBUG("unpack requires a decimal number, not '%s'", data_size); goto nothing; } type = fr_str2int(dict_attr_types, data_type, PW_TYPE_INVALID); if (type == PW_TYPE_INVALID) { REDEBUG("Invalid data type '%s'", data_type); goto nothing; } /* * Output must be a non-zero limited size. */ if ((dict_attr_sizes[type][0] == 0) || (dict_attr_sizes[type][0] != dict_attr_sizes[type][1])) { REDEBUG("unpack requires fixed-size output type, not '%s'", data_type); goto nothing; } if (input_len < (offset + dict_attr_sizes[type][0])) { REDEBUG("Insufficient data to unpack '%s' from '%s'", data_type, data_name); goto nothing; } da = dict_attrbyvalue(PW_CAST_BASE + type, 0); if (!da) { REDEBUG("Cannot decode type '%s'", data_type); goto nothing; } cast = pairalloc(request, da); if (!cast) goto nothing; memcpy(&(cast->data), input + offset, dict_attr_sizes[type][0]); cast->vp_length = dict_attr_sizes[type][0]; /* * Hacks */ switch (type) { case PW_TYPE_SIGNED: case PW_TYPE_INTEGER: case PW_TYPE_DATE: cast->vp_integer = ntohl(cast->vp_integer); break; case PW_TYPE_SHORT: cast->vp_short = ((input[offset] << 8) | input[offset + 1]); break; case PW_TYPE_INTEGER64: cast->vp_integer64 = ntohll(cast->vp_integer64); break; default: break; } len = vp_prints_value(out, outlen, cast, 0); talloc_free(cast); if (is_truncated(len, outlen)) { REDEBUG("Insufficient buffer space to unpack data"); goto nothing; } return len; }
/** Convert group membership information into attributes * * @param[in] inst rlm_ldap configuration. * @param[in] request Current request. * @param[in,out] pconn to use. May change as this function calls functions which auto re-connect. * @param[in] entry retrieved by rlm_ldap_find_user or rlm_ldap_search. * @param[in] attr membership attribute to look for in the entry. * @return One of the RLM_MODULE_* values. */ rlm_rcode_t rlm_ldap_cacheable_userobj(ldap_instance_t const *inst, REQUEST *request, ldap_handle_t **pconn, LDAPMessage *entry, char const *attr) { rlm_rcode_t rcode = RLM_MODULE_OK; struct berval **values; size_t value_len = 0; TALLOC_CTX *value_pool; char *group_name[LDAP_MAX_CACHEABLE + 1]; char **name_p = group_name; char *group_dn[LDAP_MAX_CACHEABLE + 1]; char **dn_p; char *name; VALUE_PAIR *vp, **vps; TALLOC_CTX *ctx; vp_cursor_t cursor; int is_dn, i, count; rad_assert(entry); rad_assert(attr); /* * Parse the membership information we got in the initial user query. */ values = ldap_get_values_len((*pconn)->handle, entry, attr); if (!values) { RDEBUG2("No cacheable group memberships found in user object"); return RLM_MODULE_OK; } count = ldap_count_values_len(values); vps = radius_list(request, PAIR_LIST_CONTROL); ctx = radius_list_ctx(request, PAIR_LIST_CONTROL); fr_cursor_init(&cursor, vps); /* * Avoid allocing buffers for each value. * * The old code used ldap_get_values, which was likely doing * a very similar thing internally to produce \0 terminated * buffers from bervalues. */ for (i = 0; (i < LDAP_MAX_CACHEABLE) && (i < count); i++) value_len += values[i]->bv_len + 1; value_pool = talloc_pool(request, value_len); for (i = 0; (i < LDAP_MAX_CACHEABLE) && (i < count); i++) { is_dn = rlm_ldap_is_dn(values[i]->bv_val, values[i]->bv_len); if (inst->cacheable_group_dn) { /* * The easy case, we're caching DNs and we got a DN. */ if (is_dn) { MEM(vp = pairalloc(ctx, inst->cache_da)); pairstrncpy(vp, values[i]->bv_val, values[i]->bv_len); fr_cursor_insert(&cursor, vp); RDEBUG("Added %s with value \"%s\" to control list", inst->cache_da->name, vp->vp_strvalue); /* * We were told to cache DNs but we got a name, we now need to resolve * this to a DN. Store all the group names in an array so we can do one query. */ } else { *name_p++ = rlm_ldap_berval_to_string(value_pool, values[i]); } } if (inst->cacheable_group_name) { /* * The easy case, we're caching names and we got a name. */ if (!is_dn) { MEM(vp = pairalloc(ctx, inst->cache_da)); pairstrncpy(vp, values[i]->bv_val, values[i]->bv_len); fr_cursor_insert(&cursor, vp); RDEBUG("Added control:%s with value \"%s\"", inst->cache_da->name, vp->vp_strvalue); /* * We were told to cache names but we got a DN, we now need to resolve * this to a name. * Only Active Directory supports filtering on DN, so we have to search * for each individual group. */ } else { char *dn; dn = rlm_ldap_berval_to_string(value_pool, values[i]); rcode = rlm_ldap_group_dn2name(inst, request, pconn, dn, &name); talloc_free(dn); if (rcode != RLM_MODULE_OK) { ldap_value_free_len(values); talloc_free(value_pool); return rcode; } MEM(vp = pairalloc(ctx, inst->cache_da)); pairstrncpy(vp, name, talloc_array_length(name) - 1); fr_cursor_insert(&cursor, vp); RDEBUG("Added control:%s with value \"%s\"", inst->cache_da->name, name); talloc_free(name); } } } *name_p = NULL; rcode = rlm_ldap_group_name2dn(inst, request, pconn, group_name, group_dn, sizeof(group_dn)); ldap_value_free_len(values); talloc_free(value_pool); if (rcode != RLM_MODULE_OK) return rcode; dn_p = group_dn; while (*dn_p) { MEM(vp = pairalloc(ctx, inst->cache_da)); pairstrcpy(vp, *dn_p); fr_cursor_insert(&cursor, vp); RDEBUG("Added control:%s with value \"%s\"", inst->cache_da->name, *dn_p); ldap_memfree(*dn_p); dn_p++; } return rcode; }
/** Convert a map to a VALUE_PAIR. * * @param[out] out Where to write the VALUE_PAIR(s). * @param[in] request structure (used only for talloc) * @param[in] map the map. The LHS (dst) has to be VPT_TYPE_ATTR or VPT_TYPE_LIST. * @param[in] ctx unused * @return 0 on success, -1 on failure, -2 on attribute not found/equivalent */ int radius_map2vp(VALUE_PAIR **out, REQUEST *request, value_pair_map_t const *map, UNUSED void *ctx) { int rcode = 0; VALUE_PAIR *vp = NULL, *found, **from = NULL; DICT_ATTR const *da; REQUEST *context = request; vp_cursor_t cursor; rad_assert(request != NULL); rad_assert(map != NULL); *out = NULL; /* * Special case for !*, we don't need to parse RHS as this is a unary operator. */ if (map->op == T_OP_CMP_FALSE) { /* * Were deleting all the attributes in a list. This isn't like the other * mappings because lists aren't represented as attributes (yet), * so we can't return a <list> attribute with the !* operator for * radius_pairmove() to consume, and need to do the work here instead. */ if (map->dst->type == VPT_TYPE_LIST) { if (radius_request(&context, map->dst->request) == 0) { from = radius_list(context, map->dst->list); } if (!from) return -2; pairfree(from); /* @fixme hacky! */ if (map->dst->list == PAIR_LIST_REQUEST) { context->username = NULL; context->password = NULL; } return 0; } /* Not a list, but an attribute, radius_pairmove() will perform that actual delete */ vp = pairalloc(request, map->dst->da); if (!vp) return -1; vp->op = map->op; *out = vp; return 0; } /* * List to list found, this is a special case because we don't need * to allocate any attributes, just finding the current list, and change * the op. */ if ((map->dst->type == VPT_TYPE_LIST) && (map->src->type == VPT_TYPE_LIST)) { if (radius_request(&context, map->src->request) == 0) { from = radius_list(context, map->src->list); } if (!from) return -2; found = paircopy(request, *from); /* * List to list copy is invalid if the src list has no attributes. */ if (!found) return -2; for (vp = fr_cursor_init(&cursor, &found); vp; vp = fr_cursor_next(&cursor)) { vp->op = T_OP_ADD; } *out = found; return 0; } /* * Deal with all non-list operations. */ da = map->dst->da ? map->dst->da : map->src->da; switch (map->src->type) { case VPT_TYPE_XLAT: case VPT_TYPE_XLAT_STRUCT: case VPT_TYPE_LITERAL: case VPT_TYPE_DATA: vp = pairalloc(request, da); if (!vp) return -1; vp->op = map->op; break; default: break; } /* * And parse the RHS */ switch (map->src->type) { ssize_t slen; char *str; case VPT_TYPE_XLAT_STRUCT: rad_assert(map->dst->da); /* Need to know where were going to write the new attribute */ rad_assert(map->src->xlat != NULL); str = NULL; slen = radius_axlat_struct(&str, request, map->src->xlat, NULL, NULL); if (slen < 0) { rcode = slen; goto error; } /* * We do the debug printing because radius_axlat_struct * doesn't have access to the original string. It's been * mangled during the parsing to xlat_exp_t */ RDEBUG2("EXPAND %s", map->src->name); RDEBUG2(" --> %s", str); rcode = pairparsevalue(vp, str); talloc_free(str); if (!rcode) { pairfree(&vp); rcode = -1; goto error; } break; case VPT_TYPE_XLAT: rad_assert(map->dst->da); /* Need to know where were going to write the new attribute */ str = NULL; slen = radius_axlat(&str, request, map->src->name, NULL, NULL); if (slen < 0) { rcode = slen; goto error; } rcode = pairparsevalue(vp, str); talloc_free(str); if (!rcode) { pairfree(&vp); rcode = -1; goto error; } break; case VPT_TYPE_LITERAL: if (!pairparsevalue(vp, map->src->name)) { rcode = -2; goto error; } break; case VPT_TYPE_ATTR: rad_assert(!map->dst->da || (map->src->da->type == map->dst->da->type) || (map->src->da->type == PW_TYPE_OCTETS) || (map->dst->da->type == PW_TYPE_OCTETS)); /* * Special case, destination is a list, found all instance of an attribute. */ if (map->dst->type == VPT_TYPE_LIST) { context = request; if (radius_request(&context, map->src->request) == 0) { from = radius_list(context, map->src->list); } /* * Can't add the attribute if the list isn't * valid. */ if (!from) { rcode = -2; goto error; } found = paircopy2(request, *from, map->src->da->attr, map->src->da->vendor, TAG_ANY); if (!found) { REDEBUG("Attribute \"%s\" not found in request", map->src->name); rcode = -2; goto error; } for (vp = fr_cursor_init(&cursor, &found); vp; vp = fr_cursor_next(&cursor)) { vp->op = T_OP_ADD; } *out = found; return 0; } if (radius_vpt_get_vp(&found, request, map->src) < 0) { REDEBUG("Attribute \"%s\" not found in request", map->src->name); rcode = -2; goto error; } /* * Copy the data over verbatim, assuming it's * actually data. */ vp = paircopyvpdata(request, da, found); if (!vp) { return -1; } vp->op = map->op; break; case VPT_TYPE_DATA: rad_assert(map->src && map->src->da); rad_assert(map->dst && map->dst->da); rad_assert(map->src->da->type == map->dst->da->type); memcpy(&vp->data, map->src->vpd, sizeof(vp->data)); vp->length = map->src->length; break; /* * This essentially does the same as rlm_exec xlat, except it's non-configurable. * It's only really here as a convenience for people who expect the contents of * backticks to be executed in a shell. * * exec string is xlat expanded and arguments are shell escaped. */ case VPT_TYPE_EXEC: return radius_mapexec(out, request, map); default: rad_assert(0); /* Should of been caught at parse time */ error: pairfree(&vp); return rcode; } *out = vp; return 0; }
/** 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; }
/** 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; }
/** Print a template to a string * * @param[out] buffer for the output string * @param[in] bufsize of the buffer * @param[in] vpt to print * @return the size of the string printed */ size_t radius_tmpl2str(char *buffer, size_t bufsize, value_pair_tmpl_t const *vpt) { char c; char const *p; char *q = buffer; char *end; switch (vpt->type) { default: return 0; case VPT_TYPE_REGEX: c = '/'; break; case VPT_TYPE_XLAT: c = '"'; break; case VPT_TYPE_LITERAL: /* single-quoted or bare word */ /* * Hack */ for (p = vpt->name; *p != '\0'; p++) { if (*p == ' ') break; if (*p == '\'') break; if (!dict_attr_allowed_chars[(int) *p]) break; } if (!*p) { strlcpy(buffer, vpt->name, bufsize); return strlen(buffer); } c = '\''; break; case VPT_TYPE_EXEC: c = '`'; break; case VPT_TYPE_ATTR: buffer[0] = '&'; if (vpt->request == REQUEST_CURRENT) { if (vpt->list == PAIR_LIST_REQUEST) { strlcpy(buffer + 1, vpt->da->name, bufsize - 1); } else { snprintf(buffer + 1, bufsize - 1, "%s:%s", fr_int2str(pair_lists, vpt->list, ""), vpt->da->name); } } else { snprintf(buffer + 1, bufsize - 1, "%s.%s:%s", fr_int2str(request_refs, vpt->request, ""), fr_int2str(pair_lists, vpt->list, ""), vpt->da->name); } return strlen(buffer); case VPT_TYPE_DATA: { VALUE_PAIR *vp; TALLOC_CTX *ctx; memcpy(&ctx, &vpt, sizeof(ctx)); /* hack */ vp = pairalloc(ctx, vpt->da); memcpy(&vp->data, vpt->vpd, sizeof(vp->data)); vp->length = vpt->length; q = vp_aprint(vp, vp); if ((vpt->da->type != PW_TYPE_STRING) && (vpt->da->type != PW_TYPE_DATE)) { strlcpy(buffer, q, bufsize); } else { /* * FIXME: properly escape the string... */ snprintf(buffer, bufsize, "\"%s\"", q); } talloc_free(q); pairfree(&vp); return strlen(buffer); } } if (bufsize <= 3) { no_room: *buffer = '\0'; return 0; } p = vpt->name; *(q++) = c; end = buffer + bufsize - 3; /* quotes + EOS */ while (*p && (q < end)) { if (*p == c) { if ((q - end) < 4) goto no_room; /* escape, char, quote, EOS */ *(q++) = '\\'; *(q++) = *(p++); continue; } switch (*p) { case '\\': if ((q - end) < 4) goto no_room; *(q++) = '\\'; *(q++) = *(p++); break; case '\r': if ((q - end) < 4) goto no_room; *(q++) = '\\'; *(q++) = 'r'; p++; break; case '\n': if ((q - end) < 4) goto no_room; *(q++) = '\\'; *(q++) = 'r'; p++; break; case '\t': if ((q - end) < 4) goto no_room; *(q++) = '\\'; *(q++) = 't'; p++; break; default: *(q++) = *(p++); break; } } *(q++) = c; *q = '\0'; return q - buffer; }
/** Converts a serialized cache entry back into a structure * * @param c Cache entry to populate (should already be allocated) * @param in String representation of cache entry. * @param inlen Length of string. May be < 0 in which case strlen will be * used to calculate the length of the string. * @return 0 on success, -1 on error. */ int cache_deserialize(rlm_cache_entry_t *c, char *in, ssize_t inlen) { vp_cursor_t packet, control, reply; TALLOC_CTX *store = NULL; char *p, *q; store = talloc_pool(c, 1024); if (!store) return -1; if (inlen < 0) inlen = strlen(in); fr_cursor_init(&packet, &c->packet); fr_cursor_init(&control, &c->control); fr_cursor_init(&reply, &c->reply); p = in; while (((size_t)(p - in)) < (size_t)inlen) { value_pair_map_t *map = NULL; VALUE_PAIR *vp = NULL; ssize_t len; q = strchr(p, '\n'); if (!q) break; /* List should also be terminated with a \n */ *q = '\0'; if (map_afrom_attr_str(store, &map, p, REQUEST_CURRENT, PAIR_LIST_REQUEST, REQUEST_CURRENT, PAIR_LIST_REQUEST) < 0) { fr_strerror_printf("Failed parsing pair: %s", p); goto error; } if (map->lhs->type != TMPL_TYPE_ATTR) { fr_strerror_printf("Pair left hand side \"%s\" parsed as %s, needed attribute. " "Check local dictionaries", map->lhs->name, fr_int2str(tmpl_names, map->lhs->type, "<INVALID>")); goto error; } if (map->rhs->type != TMPL_TYPE_LITERAL) { fr_strerror_printf("Pair right hand side \"%s\" parsed as %s, needed literal. " "Check serialized data quoting", map->rhs->name, fr_int2str(tmpl_names, map->rhs->type, "<INVALID>")); goto error; } /* * Convert literal to a type appropriate for * the VP. */ if (tmpl_cast_in_place(map->rhs, map->lhs->tmpl_da->type, map->lhs->tmpl_da) < 0) goto error; vp = pairalloc(c, map->lhs->tmpl_da); len = value_data_copy(vp, &vp->data, map->rhs->tmpl_data_type, &map->rhs->tmpl_data_value, map->rhs->tmpl_data_length); if (len < 0) goto error; /* * Pull out the special attributes, and set the * relevant cache entry fields. */ if (vp->da->vendor == 0) switch (vp->da->attr) { case PW_CACHE_CREATED: c->created = vp->vp_date; talloc_free(vp); goto next; case PW_CACHE_EXPIRES: c->expires = vp->vp_date; talloc_free(vp); goto next; default: break; } switch (map->lhs->tmpl_list) { case PAIR_LIST_REQUEST: fr_cursor_insert(&packet, vp); break; case PAIR_LIST_CONTROL: fr_cursor_insert(&control, vp); break; case PAIR_LIST_REPLY: fr_cursor_insert(&reply, vp); break; default: fr_strerror_printf("Invalid cache list for pair: %s", p); error: talloc_free(vp); talloc_free(map); return -1; } next: p = q + 1; talloc_free(map); } 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; }
/** 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; }