/** 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(VALUE_PAIR const *failed[2], VALUE_PAIR *filter, VALUE_PAIR *list) { vp_cursor_t filter_cursor; vp_cursor_t list_cursor; VALUE_PAIR *check, *match; 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); check = fr_cursor_init(&filter_cursor, &filter); match = fr_cursor_init(&list_cursor, &list); while (match || check) { /* * Lists are of different lengths */ if (!match || !check) goto mismatch; /* * The lists are sorted, so if the first * attributes aren't of the same type, then we're * done. */ if (!ATTRIBUTE_EQ(check, match)) goto mismatch; /* * They're of the same type, but don't have the * same values. This is a problem. * * Note that the RFCs say that for attributes of * the same type, order is important. */ if (paircmp(check, match) != 1) goto mismatch; check = fr_cursor_next(&filter_cursor); match = fr_cursor_next(&list_cursor); } return true; mismatch: if (failed) { failed[0] = check; failed[1] = match; } return false; }
static int vector_gsm_from_triplets(eap_session_t *eap_session, VALUE_PAIR *vps, int idx, fr_sim_keys_t *keys) { REQUEST *request = eap_session->request; VALUE_PAIR *rand = NULL, *sres = NULL, *kc = NULL; fr_cursor_t cursor; int i; for (i = 0, (kc = fr_cursor_iter_by_da_init(&cursor, &vps, attr_eap_sim_kc)); (i < idx) && (kc = fr_cursor_next(&cursor)); i++); if (!kc) { RDEBUG3("No &control:%s[%i] attribute found, not using GSM triplets", attr_eap_sim_kc->name, idx); return 1; } if (kc->vp_length != SIM_VECTOR_GSM_KC_SIZE) { REDEBUG("&control:%s[%i] is not " STRINGIFY(SIM_VECTOR_GSM_KC_SIZE) " bytes, got %zu bytes", attr_eap_sim_kc->name, idx, kc->vp_length); return -1; } for (i = 0, (rand = fr_cursor_iter_by_da_init(&cursor, &vps, attr_eap_sim_rand)); (i < idx) && (rand = fr_cursor_next(&cursor)); i++); if (!rand) { RDEBUG3("No &control:%s[%i] attribute found, not using GSM triplets", attr_eap_sim_rand->name, idx); return 1; } if (rand->vp_length != SIM_VECTOR_GSM_RAND_SIZE) { REDEBUG("&control:EAP-SIM-Rand[%i] is not " STRINGIFY(SIM_RAND_SIZE) " bytes, got %zu bytes", idx, rand->vp_length); return -1; } for (i = 0, (sres = fr_cursor_iter_by_da_init(&cursor, &vps, attr_eap_sim_sres)); (i < idx) && (sres = fr_cursor_next(&cursor)); i++); if (!sres) { RDEBUG3("No &control:%s[%i] attribute found, not using GSM triplets", attr_eap_sim_sres->name, idx); return 1; } if (sres->vp_length != SIM_VECTOR_GSM_SRES_SIZE) { REDEBUG("&control:%s[%i] is not " STRINGIFY(SIM_VECTOR_GSM_SRES_SIZE) " bytes, got %zu bytes", attr_eap_sim_sres->name, idx, sres->vp_length); return -1; } memcpy(keys->gsm.vector[idx].kc, kc->vp_strvalue, SIM_VECTOR_GSM_KC_SIZE); memcpy(keys->gsm.vector[idx].rand, rand->vp_octets, SIM_VECTOR_GSM_RAND_SIZE); memcpy(keys->gsm.vector[idx].sres, sres->vp_octets, SIM_VECTOR_GSM_SRES_SIZE); return 0; }
/** Remove item from parent and fixup trees * * @param[in] parent to remove child from. * @param[in] child to remove. * @return * - The item removed. * - NULL if the item wasn't set. */ CONF_ITEM *cf_remove(CONF_ITEM *parent, CONF_ITEM *child) { CONF_ITEM *found; bool in_ident1, in_ident2; if (!parent || !parent->child) return NULL; if (parent != child->parent) return NULL; for (found = fr_cursor_head(&parent->cursor); found && (child != found); found = fr_cursor_next(&parent->cursor)); if (!found) return NULL; /* * Fixup the linked list */ found = fr_cursor_remove(&parent->cursor); if (!fr_cond_assert(found == child)) return NULL; in_ident1 = (rbtree_finddata(parent->ident1, child) == child); if (in_ident1 && (!rbtree_deletebydata(parent->ident1, child))) { rad_assert(0); return NULL; } in_ident2 = (rbtree_finddata(parent->ident2, child) == child); if (in_ident2 && (!rbtree_deletebydata(parent->ident2, child))) { rad_assert(0); return NULL; } /* * Look for twins */ for (found = fr_cursor_head(&parent->cursor); found && (in_ident1 || in_ident2); found = fr_cursor_next(&parent->cursor)) { if (in_ident1 && (_cf_ident1_cmp(found, child) == 0)) { rbtree_insert(parent->ident1, child); in_ident1 = false; } if (in_ident2 && (_cf_ident2_cmp(found, child) == 0)) { rbtree_insert(parent->ident2, child); in_ident2 = false; } } return child; }
/** Do any RADIUS-layer fixups for proxying. * */ static void radius_fixups(rlm_radius_t *inst, REQUEST *request) { VALUE_PAIR *vp; /* * Check for proxy loops. */ if (RDEBUG_ENABLED) { fr_cursor_t cursor; for (vp = fr_cursor_iter_by_da_init(&cursor, &request->packet->vps, attr_proxy_state); vp; vp = fr_cursor_next(&cursor)) { if (vp->vp_length != 4) continue; if (memcmp(&inst->proxy_state, vp->vp_octets, 4) == 0) { RWARN("Possible proxy loop - please check server configuration."); break; } } } if (request->packet->code != FR_CODE_ACCESS_REQUEST) return; if (fr_pair_find_by_da(request->packet->vps, attr_chap_password, TAG_ANY) && !fr_pair_find_by_da(request->packet->vps, attr_chap_challenge, TAG_ANY)) { MEM(pair_add_request(&vp, attr_chap_challenge) >= 0); fr_pair_value_memcpy(vp, request->packet->vector, sizeof(request->packet->vector)); } }
/** Print a list of valuepairs to the request list. * * @param[in] level Debug level (1-4). * @param[in] request to read logging params from. * @param[in] vp to print. */ void rdebug_pair_list(int level, REQUEST *request, VALUE_PAIR *vp) { vp_cursor_t cursor; char buffer[256]; if (!vp || !request || !request->log.func) return; if (!radlog_debug_enabled(L_DBG, level, request)) return; for (vp = fr_cursor_init(&cursor, &vp); vp; vp = fr_cursor_next(&cursor)) { /* * Take this opportunity to verify all the VALUE_PAIRs are still valid. */ if (!talloc_get_type(vp, VALUE_PAIR)) { REDEBUG("Expected VALUE_PAIR pointer got \"%s\"", talloc_get_name(vp)); fr_log_talloc_report(vp); rad_assert(0); } vp_prints(buffer, sizeof(buffer), vp); RDEBUGX(level, "\t%s", buffer); } }
static void print_packet(FILE *fp, RADIUS_PACKET *packet) { VALUE_PAIR *vp; vp_cursor_t cursor; if (!packet) { fprintf(fp, "\n"); return; } fprintf(fp, "%s\n", fr_packet_codes[packet->code]); for (vp = fr_cursor_init(&cursor, &packet->vps); vp; vp = fr_cursor_next(&cursor)) { /* * Take this opportunity to verify all the VALUE_PAIRs are still valid. */ if (!talloc_get_type(vp, VALUE_PAIR)) { ERROR("Expected VALUE_PAIR pointer got \"%s\"", talloc_get_name(vp)); fr_log_talloc_report(vp); rad_assert(0); } vp_print(fp, vp); } fflush(fp); }
/** Remove the current pair * * @todo this is really inefficient and should be fixed... * * @addtogroup module_safe * * @param cursor to remove the current pair from. * @return * - #VALUE_PAIR we just replaced. * - NULL on error. */ VALUE_PAIR *fr_cursor_remove(vp_cursor_t *cursor) { VALUE_PAIR *vp, **last; if (!fr_assert(cursor->first)) return NULL; /* cursor must have been initialised */ vp = cursor->current; if (!vp) return NULL; last = cursor->first; while (*last != vp) last = &(*last)->next; fr_cursor_next(cursor); /* Advance the cursor past the one were about to delete */ *last = vp->next; vp->next = NULL; /* * Fixup cursor->found if we removed the VP it was referring to */ if (vp == cursor->found) cursor->found = *last; /* * Fixup cursor->last if we removed the VP it was referring to */ if (vp == cursor->last) cursor->last = *last; return vp; }
/** Add a child * * @param[in] parent to add child to. * @param[in] child to add. */ void _cf_item_add(CONF_ITEM *parent, CONF_ITEM *child) { fr_cursor_t to_merge; CONF_ITEM *ci; rad_assert(parent != child); if (!parent || !child) return; /* * New child, add child trees. */ if (!parent->ident1) parent->ident1 = rbtree_create(parent, _cf_ident1_cmp, NULL, RBTREE_FLAG_NONE); if (!parent->ident2) parent->ident2 = rbtree_create(parent, _cf_ident2_cmp, NULL, RBTREE_FLAG_NONE); fr_cursor_init(&to_merge, &child); for (ci = fr_cursor_head(&to_merge); ci; ci = fr_cursor_next(&to_merge)) { rbtree_insert(parent->ident1, ci); rbtree_insert(parent->ident2, ci); /* NULL ident2 is still a value */ fr_cursor_append(&parent->cursor, ci); /* Append to the list of children */ } }
/** Copy matching pairs * * Copy pairs of a matching attribute number, vendor number and tag from the * the input list to a new list, and returns the head of this list. * * @param[in] ctx for talloc * @param[in] from whence to copy VALUE_PAIRs. * @param[in] attr to match, if 0 input list will not be filtered by attr. * @param[in] vendor to match. * @param[in] tag to match, TAG_ANY matches any tag, TAG_NONE matches tagless VPs. * @return the head of the new VALUE_PAIR list or NULL on error. */ VALUE_PAIR *paircopy_by_num(TALLOC_CTX *ctx, VALUE_PAIR *from, unsigned int attr, unsigned int vendor, int8_t tag) { vp_cursor_t src, dst; VALUE_PAIR *out = NULL, *vp; fr_cursor_init(&dst, &out); for (vp = fr_cursor_init(&src, &from); vp; vp = fr_cursor_next(&src)) { VERIFY_VP(vp); if ((vp->da->attr != attr) || (vp->da->vendor != vendor)) { continue; } if (vp->da->flags.has_tag && !TAG_EQ(tag, vp->tag)) { continue; } vp = paircopyvp(ctx, vp); if (!vp) { pairfree(&out); return NULL; } fr_cursor_insert(&dst, vp); } return out; }
/* * Don't even ask what this is doing... */ static void alvarion_vsa_hack(VALUE_PAIR *vp) { int number = 1; vp_cursor_t cursor; for (vp = fr_cursor_init(&cursor, &vp); vp; vp = fr_cursor_next(&cursor)) { DICT_ATTR const *da; if (vp->da->vendor != 12394) { continue; } if (vp->da->type != PW_TYPE_STRING) { continue; } da = dict_attrbyvalue(number, 12394); if (!da) { continue; } vp->da = da; number++; } }
/* * Allow single attribute values to be retrieved from the dhcp. */ static ssize_t dhcp_options_xlat(UNUSED void *instance, REQUEST *request, char const *fmt, char **out, size_t freespace) { vp_cursor_t cursor, src_cursor; vp_tmpl_t src; VALUE_PAIR *vp, *head = NULL; int decoded = 0; ssize_t slen; while (isspace((int) *fmt)) fmt++; slen = tmpl_from_attr_str(&src, fmt, REQUEST_CURRENT, PAIR_LIST_REQUEST, false, false); if (slen <= 0) { REMARKER(fmt, slen, fr_strerror()); error: return -1; } if (src.type != TMPL_TYPE_ATTR) { REDEBUG("dhcp_options cannot operate on a %s", fr_int2str(tmpl_names, src.type, "<INVALID>")); goto error; } if (src.tmpl_da->type != PW_TYPE_OCTETS) { REDEBUG("dhcp_options got a %s attribute needed octets", fr_int2str(dict_attr_types, src.tmpl_da->type, "<INVALID>")); goto error; } for (vp = tmpl_cursor_init(NULL, &src_cursor, request, &src); vp; vp = tmpl_cursor_next(&src_cursor, &src)) { /* * @fixme: we should pass in a cursor, then decoding multiple * source attributes can be made atomic. */ if ((fr_dhcp_decode_options(request->packet, &head, vp->vp_octets, vp->vp_length) < 0) || (!head)) { RWDEBUG("DHCP option decoding failed: %s", fr_strerror()); goto error; } for (vp = fr_cursor_init(&cursor, &head); vp; vp = fr_cursor_next(&cursor)) { rdebug_pair(L_DBG_LVL_2, request, vp, "dhcp_options: "); decoded++; } fr_pair_list_move(request->packet, &(request->packet->vps), &head); /* Free any unmoved pairs */ fr_pair_list_free(&head); } snprintf(*out, freespace, "%i", decoded); return strlen(*out); }
/* * This hack strips out Cisco's VSA duplicities in lines * (Cisco not implemented VSA's in standard way. * * Cisco sends it's VSA attributes with the attribute name *again* * in the string, like: H323-Attribute = "h323-attribute=value". * This sort of behaviour is nonsense. */ static void cisco_vsa_hack(REQUEST *request) { int vendorcode; char *ptr; char newattr[MAX_STRING_LEN]; VALUE_PAIR *vp; vp_cursor_t cursor; for (vp = fr_cursor_init(&cursor, &request->packet->vps); vp; vp = fr_cursor_next(&cursor)) { vendorcode = vp->da->vendor; if (!((vendorcode == 9) || (vendorcode == 6618))) { continue; /* not a Cisco or Quintum VSA, continue */ } if (vp->da->type != PW_TYPE_STRING) { continue; } /* * No weird packing. Ignore it. */ ptr = strchr(vp->vp_strvalue, '='); /* find an '=' */ if (!ptr) { continue; } /* * Cisco-AVPair's get packed as: * * Cisco-AVPair = "h323-foo-bar = baz" * Cisco-AVPair = "h323-foo-bar=baz" * * which makes sense only if you're a lunatic. * This code looks for the attribute named inside * of the string, and if it exists, adds it as a new * attribute. */ if (vp->da->attr == 1) { char const *p; p = vp->vp_strvalue; gettoken(&p, newattr, sizeof(newattr), false); if (dict_attrbyname(newattr) != NULL) { pairmake_packet(newattr, ptr + 1, T_OP_EQ); } } else { /* h322-foo-bar = "h323-foo-bar = baz" */ /* * We strip out the duplicity from the * value field, we use only the value on * the right side of the '=' character. */ pairstrcpy(vp, ptr + 1); } } }
static void dhcp_packet_debug(RADIUS_PACKET *packet, bool received) { fr_cursor_t cursor; char buffer[256]; char src_ipaddr[INET6_ADDRSTRLEN]; char dst_ipaddr[INET6_ADDRSTRLEN]; #if defined(WITH_UDPFROMTO) && defined(WITH_IFINDEX_NAME_RESOLUTION) char if_name[IFNAMSIZ]; #endif VALUE_PAIR *vp; if (!packet) return; /* * Client-specific debugging re-prints the input * packet into the client log. * * This really belongs in a utility library */ printf("%s %s Id %08x from %s%s%s:%i to %s%s%s:%i " #if defined(WITH_UDPFROMTO) && defined(WITH_IFINDEX_NAME_RESOLUTION) "%s%s%s" #endif "length %zu\n", received ? "Received" : "Sending", dhcp_message_types[packet->code], packet->id, packet->src_ipaddr.af == AF_INET6 ? "[" : "", inet_ntop(packet->src_ipaddr.af, &packet->src_ipaddr.addr, src_ipaddr, sizeof(src_ipaddr)), packet->src_ipaddr.af == AF_INET6 ? "]" : "", packet->src_port, packet->dst_ipaddr.af == AF_INET6 ? "[" : "", inet_ntop(packet->dst_ipaddr.af, &packet->dst_ipaddr.addr, dst_ipaddr, sizeof(dst_ipaddr)), packet->dst_ipaddr.af == AF_INET6 ? "]" : "", packet->dst_port, #if defined(WITH_UDPFROMTO) && defined(WITH_IFINDEX_NAME_RESOLUTION) packet->if_index ? "via " : "", packet->if_index ? fr_ifname_from_ifindex(if_name, packet->if_index) : "", packet->if_index ? " " : "", #endif packet->data_len); for (vp = fr_cursor_init(&cursor, &packet->vps); vp; vp = fr_cursor_next(&cursor)) { VP_VERIFY(vp); fr_pair_snprint(buffer, sizeof(buffer), vp); printf("\t%s\n", buffer); } }
/** Merges two sets of VPs * * The list represented by cursor will hold the union of cursor and * add lists. * * @param cursor to insert VALUE_PAIRs with * @param add one or more VALUE_PAIRs. */ void fr_cursor_merge(vp_cursor_t *cursor, VALUE_PAIR *add) { vp_cursor_t from; VALUE_PAIR *vp; for (vp = fr_cursor_init(&from, &add); vp; vp = fr_cursor_next(&from)) { fr_cursor_insert(cursor, vp); } }
/* * Dump a whole list of attributes to DEBUG2 */ void vp_listdebug(VALUE_PAIR *vp) { vp_cursor_t cursor; char tmpPair[70]; for (vp = fr_cursor_init(&cursor, &vp); vp; vp = fr_cursor_next(&cursor)) { vp_prints(tmpPair, sizeof(tmpPair), vp); DEBUG2(" %s", tmpPair); } }
/** Print a list of attributes and enumv * * @param fp to output to. * @param const_vp to print. */ void vp_printlist(FILE *fp, VALUE_PAIR const *const_vp) { VALUE_PAIR *vp; vp_cursor_t cursor; memcpy(&vp, &const_vp, sizeof(vp)); /* const work-arounds */ for (vp = fr_cursor_init(&cursor, &vp); vp; vp = fr_cursor_next(&cursor)) { vp_print(fp, vp); } }
/** Print a list of valuepairs to stderr or error log. * * @param[in] vp to print. */ void debug_pair_list(VALUE_PAIR *vp) { vp_cursor_t cursor; if (!vp || !debug_flag || !fr_log_fp) return; for (vp = fr_cursor_init(&cursor, &vp); vp; vp = fr_cursor_next(&cursor)) { vp_print(fr_log_fp, vp); } fflush(fr_log_fp); }
/** Wind cursor to the last pair in the list * * @addtogroup module_safe * * @param cursor to operate on. * @return #VALUE_PAIR at the end of the list. */ VALUE_PAIR *fr_cursor_last(vp_cursor_t *cursor) { if (!cursor->first || !*cursor->first) return NULL; /* Need to start at the start */ if (!cursor->current) fr_cursor_first(cursor); /* Wind to the end */ while (cursor->next) fr_cursor_next(cursor); return cursor->current; }
/** Merges multiple VALUE_PAIR into the cursor * * Add multiple VALUE_PAIR from add to cursor. * * @addtogroup module_safe * * @param cursor to insert VALUE_PAIRs with * @param add one or more VALUE_PAIRs (may be NULL, which results in noop). */ void fr_cursor_merge(vp_cursor_t *cursor, VALUE_PAIR *add) { vp_cursor_t from; VALUE_PAIR *vp; if (!add) return; if (!fr_assert(cursor->first)) return; /* cursor must have been initialised */ for (vp = fr_cursor_init(&from, &add); vp; vp = fr_cursor_next(&from)) { fr_cursor_insert(cursor, vp); } }
/** Print a list of VALUE_PAIRs. * * @param[in] lvl Debug lvl (1-4). * @param[in] request to read logging params from. * @param[in] vp to print. * @param[in] prefix (optional). */ void log_request_pair_list(fr_log_lvl_t lvl, REQUEST *request, VALUE_PAIR *vp, char const *prefix) { fr_cursor_t cursor; if (!vp || !request || !request->log.dst) return; if (!log_debug_enabled(L_DBG, lvl, request)) return; RINDENT(); for (vp = fr_cursor_init(&cursor, &vp); vp; vp = fr_cursor_next(&cursor)) { VP_VERIFY(vp); RDEBUGX(lvl, "%s%pP", prefix ? prefix : "&", vp); } REXDENT(); }
/** Free memory used by a valuepair list. * * @todo TLV: needs to free all dependents of each VP freed. */ void pairfree(VALUE_PAIR **vps) { VALUE_PAIR *vp; vp_cursor_t cursor; if (!vps || !*vps) { return; } for (vp = fr_cursor_init(&cursor, vps); vp; vp = fr_cursor_next(&cursor)) { VERIFY_VP(vp); talloc_free(vp); } *vps = NULL; }
static int attr_filter_getfile(TALLOC_CTX *ctx, char const *filename, PAIR_LIST **pair_list) { vp_cursor_t cursor; int rcode; PAIR_LIST *attrs = NULL; PAIR_LIST *entry; VALUE_PAIR *vp; rcode = pairlist_read(ctx, filename, &attrs, 1); if (rcode < 0) { return -1; } /* * Walk through the 'attrs' file list. */ entry = attrs; while (entry) { entry->check = entry->reply; entry->reply = NULL; for (vp = fr_cursor_init(&cursor, &entry->check); vp; vp = fr_cursor_next(&cursor)) { /* * If it's NOT a vendor attribute, * and it's NOT a wire protocol * and we ignore Fall-Through, * then bitch about it, giving a good warning message. */ if ((vp->da->vendor == 0) && (vp->da->attr > 1000)) { WARN("[%s]:%d Check item \"%s\"\n\tfound in filter list for realm \"%s\".\n", filename, entry->lineno, vp->da->name, entry->name); } } entry = entry->next; } *pair_list = attrs; return 0; }
/* * Compare the request with the "reply" part in the * huntgroup, which normally only contains username or group. * At least one of the "reply" items has to match. */ static int hunt_paircmp(REQUEST *req, VALUE_PAIR *request, VALUE_PAIR *check) { vp_cursor_t cursor; VALUE_PAIR *check_item; VALUE_PAIR *tmp; int result = -1; if (!check) return 0; for (check_item = fr_cursor_init(&cursor, &check); check_item && (result != 0); check_item = fr_cursor_next(&cursor)) { /* FIXME: paircopy should be removed once VALUE_PAIRs are no longer in linked lists */ tmp = paircopyvp(request, check_item); result = paircompare(req, request, check_item, NULL); pairfree(&tmp); } return result; }
/** Add our logging destination to the linked list of logging destinations (if it doesn't already exist) * * @param[in] instance of rlm_logtee. * @param[in] thread Thread specific data. * @param[in] request request to add our log destination to. * @return * - #RLM_MODULE_NOOP if log destination already exists. * - #RLM_MODULE_OK if we added a new destination. */ static rlm_rcode_t mod_insert_logtee(UNUSED void *instance, void *thread, REQUEST *request) { fr_cursor_t cursor; log_dst_t *dst; bool exists = false; for (dst = fr_cursor_init(&cursor, &request->log.dst); dst; dst = fr_cursor_next(&cursor)) { if (dst->uctx == thread) exists = true; } if (exists) return RLM_MODULE_NOOP; dst = talloc_zero(request, log_dst_t); dst->func = logtee_it; dst->uctx = thread; fr_cursor_append(&cursor, dst); return RLM_MODULE_OK; }
/** Copy a pairlist. * * Copy all pairs from 'from' regardless of tag, attribute or vendor. * * @param[in] ctx for new VALUE_PAIRs to be allocated in. * @param[in] from whence to copy VALUE_PAIRs. * @return the head of the new VALUE_PAIR list or NULL on error. */ VALUE_PAIR *paircopy(TALLOC_CTX *ctx, VALUE_PAIR *from) { vp_cursor_t src, dst; VALUE_PAIR *out = NULL, *vp; fr_cursor_init(&dst, &out); for (vp = fr_cursor_init(&src, &from); vp; vp = fr_cursor_next(&src)) { VERIFY_VP(vp); vp = paircopyvp(ctx, vp); if (!vp) { pairfree(&out); return NULL; } fr_cursor_insert(&dst, vp); /* paircopy sets next pointer to NULL */ } return out; }
/** Remove the current pair * * @todo this is really inefficient and should be fixed... * * @param cursor to remove the current pair from. * @return NULL on error, else the VALUE_PAIR we just removed. */ VALUE_PAIR *fr_cursor_remove(vp_cursor_t *cursor) { VALUE_PAIR *vp, **last; vp = fr_cursor_current(cursor); if (!vp) { return NULL; } last = cursor->first; while (*last != vp) { last = &(*last)->next; } fr_cursor_next(cursor); /* Advance the cursor past the one were about to delete */ *last = vp->next; vp->next = NULL; return vp; }
/* * Allow single attribute values to be retrieved from the dhcp. */ static ssize_t dhcp_options_xlat(UNUSED void *instance, REQUEST *request, char const *fmt, char *out, size_t freespace) { vp_cursor_t cursor; VALUE_PAIR *vp, *head = NULL; int decoded = 0; while (isspace((int) *fmt)) fmt++; if ((radius_get_vp(&vp, request, fmt) < 0) || !vp) { *out = '\0'; return 0; } if ((fr_dhcp_decode_options(request->packet, vp->vp_octets, vp->length, &head) < 0) || (!head)) { RWDEBUG("DHCP option decoding failed"); *out = '\0'; return -1; } for (vp = fr_cursor_init(&cursor, &head); vp; vp = fr_cursor_next(&cursor)) { decoded++; } pairmove(request->packet, &(request->packet->vps), &head); /* Free any unmoved pairs */ pairfree(&head); snprintf(out, freespace, "%i", decoded); return strlen(out); }
/** Remove the current pair * * @todo this is really inefficient and should be fixed... * * @param cursor to remove the current pair from. * @return NULL on error, else the VALUE_PAIR we just removed. */ VALUE_PAIR *fr_cursor_remove(vp_cursor_t *cursor) { VALUE_PAIR *vp, **last; vp = fr_cursor_current(cursor); if (!vp) { return NULL; } last = cursor->first; while (*last != vp) { last = &(*last)->next; } fr_cursor_next(cursor); /* Advance the cursor past the one were about to delete */ *last = vp->next; vp->next = NULL; /* Fixup cursor->found if we removed the VP it was referring to */ if (vp == cursor->found) cursor->found = *last; return vp; }
/* * 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 *hash_name, const char *list_name) { VALUE_PAIR *vp; hv_undef(rad_hv); fr_cursor_t cursor; RINDENT(); fr_pair_list_sort(vps, fr_pair_cmp_by_da_tag); for (vp = fr_cursor_init(&cursor, vps); vp; vp = fr_cursor_next(&cursor)) { VALUE_PAIR *next; char const *name; char namebuf[256]; /* * 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 = 0; AV *av; av = newAV(); perl_vp_to_svpvn_element(request, av, vp, &i, hash_name, list_name); do { perl_vp_to_svpvn_element(request, av, next, &i, hash_name, list_name); fr_cursor_next(&cursor); } while ((next = fr_cursor_next_peek(&cursor)) && ATTRIBUTE_EQ(vp, next)); (void)hv_store(rad_hv, name, strlen(name), newRV_noinc((SV *)av), 0); continue; } /* * It's a normal single valued attribute */ switch (vp->vp_type) { case FR_TYPE_STRING: RDEBUG2("$%s{'%s'} = &%s:%s -> '%pV'", hash_name, vp->da->name, list_name, vp->da->name, &vp->data); (void)hv_store(rad_hv, name, strlen(name), newSVpvn(vp->vp_strvalue, vp->vp_length), 0); break; case FR_TYPE_OCTETS: RDEBUG2("$%s{'%s'} = &%s:%s -> %pV", hash_name, vp->da->name, list_name, vp->da->name, &vp->data); (void)hv_store(rad_hv, name, strlen(name), newSVpvn((char const *)vp->vp_octets, vp->vp_length), 0); break; default: { char buffer[1024]; size_t len; len = fr_pair_value_snprint(buffer, sizeof(buffer), vp, '\0'); RDEBUG2("$%s{'%s'} = &%s:%s -> '%s'", hash_name, 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(); }
/* * Do caching checks. Since we can update ANY VP list, we do * exactly the same thing for all sections (autz / auth / etc.) * * If you want to cache something different in different sections, * configure another cache module. */ static rlm_rcode_t CC_HINT(nonnull) mod_cache_it(void *instance, REQUEST *request) { rlm_cache_entry_t *c; rlm_cache_t *inst = instance; rlm_cache_handle_t *handle; vp_cursor_t cursor; VALUE_PAIR *vp; char buffer[1024]; rlm_rcode_t rcode; int ttl = inst->ttl; if (radius_xlat(buffer, sizeof(buffer), request, inst->key, NULL, NULL) < 0) return RLM_MODULE_FAIL; if (buffer[0] == '\0') { REDEBUG("Zero length key string is invalid"); return RLM_MODULE_INVALID; } if (cache_acquire(&handle, inst, request) < 0) return RLM_MODULE_FAIL; rcode = cache_find(&c, inst, request, &handle, buffer); if (rcode == RLM_MODULE_FAIL) goto finish; rad_assert(handle); /* * If Cache-Status-Only == yes, only return whether we found a * valid cache entry */ vp = pairfind(request->config_items, PW_CACHE_STATUS_ONLY, 0, TAG_ANY); if (vp && vp->vp_integer) { rcode = c ? RLM_MODULE_OK: RLM_MODULE_NOTFOUND; goto finish; } /* * Update the expiry time based on the TTL. * A TTL of 0 means "delete from the cache". * A TTL < 0 means "delete from the cache and recreate the entry". */ vp = pairfind(request->config_items, PW_CACHE_TTL, 0, TAG_ANY); if (vp) ttl = vp->vp_signed; /* * If there's no existing cache entry, go and create a new one. */ if (!c) { if (ttl <= 0) ttl = inst->ttl; goto insert; } /* * Expire the entry if requested to do so */ if (vp) { if (ttl == 0) { cache_expire(inst, request, &handle, &c); RDEBUG("Forcing expiry of entry"); rcode = RLM_MODULE_OK; goto finish; } if (ttl < 0) { RDEBUG("Forcing expiry of existing entry"); cache_expire(inst, request, &handle, &c); ttl *= -1; goto insert; } c->expires = request->timestamp + ttl; RDEBUG("Setting TTL to %d", ttl); } /* * Cache entry was still valid, so we merge it into the request * and return. No need to add a new entry. */ cache_merge(inst, request, c); rcode = RLM_MODULE_UPDATED; goto finish; insert: /* * If Cache-Read-Only == yes, then we only allow already cached entries * to be merged into the request */ vp = pairfind(request->config_items, PW_CACHE_READ_ONLY, 0, TAG_ANY); if (vp && vp->vp_integer) { rcode = RLM_MODULE_NOTFOUND; goto finish; } /* * Create a new entry. */ rcode = cache_insert(inst, request, &handle, buffer, ttl); rad_assert(handle); finish: cache_free(inst, &c); cache_release(inst, request, &handle); /* * Clear control attributes */ for (vp = fr_cursor_init(&cursor, &request->config_items); vp; vp = fr_cursor_next(&cursor)) { if (vp->da->vendor == 0) switch (vp->da->attr) { case PW_CACHE_TTL: case PW_CACHE_STATUS_ONLY: case PW_CACHE_READ_ONLY: case PW_CACHE_MERGE: vp = fr_cursor_remove(&cursor); talloc_free(vp); break; } } return rcode; }