/** Return a VP from a value_pair_tmpl_t * * @param out where to write the retrieved vp. * @param request current request. * @param vpt the value pair template * @return -1 if VP could not be found, -2 if list could not be found, -3 if context could not be found. */ int tmpl_find_vp(VALUE_PAIR **out, REQUEST *request, value_pair_tmpl_t const *vpt) { VALUE_PAIR **vps, *vp; rad_assert((vpt->type == TMPL_TYPE_ATTR) || (vpt->type == TMPL_TYPE_LIST)); if (out) *out = NULL; if (radius_request(&request, vpt->tmpl_request) < 0) { return -3; } vps = radius_list(request, vpt->tmpl_list); if (!vps) { return -2; } switch (vpt->type) { /* * May not may not be found, but it *is* a known name. */ case TMPL_TYPE_ATTR: { int num; vp_cursor_t cursor; if (vpt->tmpl_num == NUM_ANY) { vp = pairfind(*vps, vpt->tmpl_da->attr, vpt->tmpl_da->vendor, vpt->tmpl_tag); if (!vp) return -1; break; } (void) fr_cursor_init(&cursor, vps); num = vpt->tmpl_num; while ((vp = fr_cursor_next_by_da(&cursor, vpt->tmpl_da, vpt->tmpl_tag))) { VERIFY_VP(vp); if (num-- <= 0) goto finish; } return -1; } case TMPL_TYPE_LIST: vp = *vps; break; default: rad_assert(0); } finish: if (out) *out = vp; return 0; }
/** Find the pair with the matching DAs * */ VALUE_PAIR *pair_find_by_da(VALUE_PAIR *vp, DICT_ATTR const *da, int8_t tag) { vp_cursor_t cursor; if(!fr_assert(da)) { return NULL; } (void) fr_cursor_init(&cursor, &vp); return fr_cursor_next_by_da(&cursor, da, tag); }
/* * get the vps and put them in perl hash * If one VP have multiple values it is added as array_ref * Example for this is Cisco-AVPair that holds multiple values. * Which will be available as array_ref in $RAD_REQUEST{'Cisco-AVPair'} */ static void perl_store_vps(UNUSED TALLOC_CTX *ctx, REQUEST *request, VALUE_PAIR *vps, HV *rad_hv, const char *hashname, const char *list_name) { VALUE_PAIR *vp; hv_undef(rad_hv); vp_cursor_t cursor; RINDENT(); pairsort(&vps, attrtagcmp); for (vp = fr_cursor_init(&cursor, &vps); vp; vp = fr_cursor_next(&cursor)) { VALUE_PAIR *next; char const *name; char namebuf[256]; char buffer[1024]; size_t len; /* * Tagged attributes are added to the hash with name * <attribute>:<tag>, others just use the normal attribute * name as the key. */ if (vp->da->flags.has_tag && (vp->tag != TAG_ANY)) { snprintf(namebuf, sizeof(namebuf), "%s:%d", vp->da->name, vp->tag); name = namebuf; } else { name = vp->da->name; } /* * We've sorted by type, then tag, so attributes of the * same type/tag should follow on from each other. */ if ((next = fr_cursor_next_peek(&cursor)) && ATTRIBUTE_EQ(vp, next)) { int i; AV *av; av = newAV(); for (next = fr_cursor_next_by_da(&cursor, vp->da, vp->tag), i = 0; next; next = fr_cursor_next_by_da(&cursor, vp->da, vp->tag), i++) { switch (vp->da->type) { case PW_TYPE_STRING: RDEBUG("$%s{'%s'}[%i] = &%s:%s -> '%s'", hashname, next->da->name, i, list_name, next->da->name, next->vp_strvalue); av_push(av, newSVpvn(next->vp_strvalue, next->length)); break; case PW_TYPE_OCTETS: if (RDEBUG_ENABLED) { char *hex; hex = fr_abin2hex(request, next->vp_octets, next->length); RDEBUG("$%s{'%s'}[%i] = &%s:%s -> 0x%s", hashname, next->da->name, i, list_name, next->da->name, hex); talloc_free(hex); } av_push(av, newSVpvn((char const *)next->vp_octets, next->length)); break; default: len = vp_prints_value(buffer, sizeof(buffer), next, 0); RDEBUG("$%s{'%s'}[%i] = &%s:%s -> '%s'", hashname, next->da->name, i, list_name, next->da->name, buffer); av_push(av, newSVpvn(buffer, truncate_len(len, sizeof(buffer)))); break; } } (void)hv_store(rad_hv, name, strlen(name), newRV_noinc((SV *)av), 0); continue; } /* * It's a normal single valued attribute */ switch (vp->da->type) { case PW_TYPE_STRING: RDEBUG("$%s{'%s'} = &%s:%s -> '%s'", hashname, vp->da->name, list_name, vp->da->name, vp->vp_strvalue); (void)hv_store(rad_hv, name, strlen(name), newSVpvn(vp->vp_strvalue, vp->length), 0); break; case PW_TYPE_OCTETS: if (RDEBUG_ENABLED) { char *hex; hex = fr_abin2hex(request, vp->vp_octets, vp->length); RDEBUG("$%s{'%s'} = &%s:%s -> 0x%s", hashname, vp->da->name, list_name, vp->da->name, hex); talloc_free(hex); } (void)hv_store(rad_hv, name, strlen(name), newSVpvn((char const *)vp->vp_octets, vp->length), 0); break; default: len = vp_prints_value(buffer, sizeof(buffer), vp, 0); RDEBUG("$%s{'%s'} = &%s:%s -> '%s'", hashname, vp->da->name, list_name, vp->da->name, buffer); (void)hv_store(rad_hv, name, strlen(name), newSVpvn(buffer, truncate_len(len, sizeof(buffer))), 0); break; } } REXDENT(); }
/** Uses paircmp to verify all VALUE_PAIRs in list match the filter defined by check * * @note will sort both filter and list in place. * * @param failed pointer to an array to write the pointers of the filter/list attributes that didn't match. * May be NULL. * @param filter attributes to check list against. * @param list attributes, probably a request or reply */ bool pairvalidate_relaxed(VALUE_PAIR const *failed[2], VALUE_PAIR *filter, VALUE_PAIR *list) { vp_cursor_t filter_cursor; vp_cursor_t list_cursor; VALUE_PAIR *check, *last_check = NULL, *match = NULL; if (!filter && !list) { return true; } /* * This allows us to verify the sets of validate and reply are equal * i.e. we have a validate rule which matches every reply attribute. * * @todo this should be removed one we have sets and lists */ pairsort(&filter, attrtagcmp); pairsort(&list, attrtagcmp); fr_cursor_init(&list_cursor, &list); for (check = fr_cursor_init(&filter_cursor, &filter); check; check = fr_cursor_next(&filter_cursor)) { /* * Were processing check attributes of a new type. */ if (!ATTRIBUTE_EQ(last_check, check)) { /* * Record the start of the matching attributes in the pair list * For every other operator we require the match to be present */ match = fr_cursor_next_by_da(&list_cursor, check->da, check->tag); if (!match) { if (check->op == T_OP_CMP_FALSE) continue; goto mismatch; } fr_cursor_init(&list_cursor, &match); last_check = check; } /* * Now iterate over all attributes of the same type. */ for (match = fr_cursor_first(&list_cursor); ATTRIBUTE_EQ(match, check); match = fr_cursor_next(&list_cursor)) { /* * This attribute passed the filter */ if (!paircmp(check, match)) goto mismatch; } } return true; mismatch: if (failed) { failed[0] = check; failed[1] = match; } return false; }
/** Copy pairs matching a VPT in the current request * * @param ctx to allocate new VALUE_PAIRs under. * @param out where to write the copied vps. * @param request current request. * @param vpt the value pair template * @return -1 if VP could not be found, -2 if list could not be found, -3 if context could not be found. */ int tmpl_copy_vps(TALLOC_CTX *ctx, VALUE_PAIR **out, REQUEST *request, value_pair_tmpl_t const *vpt) { VALUE_PAIR **vps, *vp; REQUEST *current = request; vp_cursor_t from, to; rad_assert((vpt->type == TMPL_TYPE_ATTR) || (vpt->type == TMPL_TYPE_LIST)); if (out) *out = NULL; if (radius_request(¤t, vpt->tmpl_request) < 0) { return -3; } vps = radius_list(request, vpt->tmpl_list); if (!vps) { return -2; } switch (vpt->type) { /* * May not be found, but it *is* a known name. */ case TMPL_TYPE_ATTR: { int num; (void) fr_cursor_init(&to, out); (void) fr_cursor_init(&from, vps); vp = fr_cursor_next_by_da(&from, vpt->tmpl_da, vpt->tmpl_tag); if (!vp) return -1; switch (vpt->tmpl_num) { /* Copy all pairs of this type (and tag) */ case NUM_ALL: do { VERIFY_VP(vp); vp = paircopyvp(ctx, vp); if (!vp) { pairfree(out); return -4; } fr_cursor_insert(&to, vp); } while ((vp = fr_cursor_next_by_da(&from, vpt->tmpl_da, vpt->tmpl_tag))); break; /* Specific attribute number */ default: for (num = vpt->tmpl_num; num && vp; num--, vp = fr_cursor_next_by_da(&from, vpt->tmpl_da, vpt->tmpl_tag)) { VERIFY_VP(vp); } if (!vp) return -1; /* FALL-THROUGH */ /* Just copy the first pair */ case NUM_ANY: vp = paircopyvp(ctx, vp); if (!vp) { pairfree(out); return -4; } fr_cursor_insert(&to, vp); } } break; case TMPL_TYPE_LIST: vp = paircopy(ctx, *vps); if (!vp) return 0; fr_cursor_merge(&to, vp); break; default: rad_assert(0); } return 0; }