/** Iterate over a collection of VALUE_PAIRs of a given type in the pairlist * * Find the next attribute of a given type. If no fr_pair_cursor_next_by_* function * has been called on a cursor before, or the previous call returned * NULL, the search will start with the current attribute. Subsequent calls to * fr_pair_cursor_next_by_* functions will start the search from the previously * matched attribute. * * @param cursor to operate on. * @param attr number to match. * @param vendor number to match (0 for none vendor attribute). * @param tag to match. Either a tag number or TAG_ANY to match any tagged or * untagged attribute, TAG_NONE to match attributes without tags. * @return * - The next matching #VALUE_PAIR. * - NULL if no #VALUE_PAIR (s) match. */ VALUE_PAIR *fr_pair_cursor_next_by_num(vp_cursor_t *cursor, unsigned int vendor, unsigned int attr, int8_t tag) { VALUE_PAIR *i; if (!cursor->first) return NULL; if (!vendor) { /* * Find top-level attributes. */ for (i = cursor->found ? cursor->found->next : cursor->current; i != NULL; i = i->next) { VP_VERIFY(i); if (fr_dict_attr_is_top_level(i->da) && (i->da->attr == attr) && ATTR_TAG_MATCH(i, tag)) { break; } } } else { for (i = cursor->found ? cursor->found->next : cursor->current; i != NULL; i = i->next) { VP_VERIFY(i); if ((i->da->parent->type == FR_TYPE_VENDOR) && (i->da->attr == attr) && (fr_dict_vendor_num_by_da(i->da) == vendor) && ATTR_TAG_MATCH(i, tag)) { break; } } } return fr_pair_cursor_update(cursor, i); }
/** Return the vendor number of an attribute reference * */ static ssize_t xlat_vendor_num(TALLOC_CTX *ctx, char **out, UNUSED size_t outlen, UNUSED void const *mod_inst, UNUSED void const *xlat_inst, REQUEST *request, char const *fmt) { VALUE_PAIR *vp; fr_skip_spaces(fmt); if ((xlat_fmt_get_vp(&vp, request, fmt) < 0) || !vp) return 0; *out = talloc_typed_asprintf(ctx, "%i", fr_dict_vendor_num_by_da(vp->da)); return talloc_array_length(*out) - 1; }
/** Decode DHCP option * * @param[in] ctx context to alloc new attributes in. * @param[in,out] cursor Where to write the decoded options. * @param[in] dict to lookup attributes in. * @param[in] data to parse. * @param[in] data_len of data to parse. * @param[in] decoder_ctx Unused. */ ssize_t fr_dhcpv4_decode_option(TALLOC_CTX *ctx, fr_cursor_t *cursor, fr_dict_t const *dict, uint8_t const *data, size_t data_len, UNUSED void *decoder_ctx) { ssize_t ret; uint8_t const *p = data; fr_dict_attr_t const *child; fr_dict_attr_t const *parent; FR_PROTO_TRACE("%s called to parse %zu byte(s)", __FUNCTION__, data_len); if (data_len == 0) return 0; FR_PROTO_HEX_DUMP(data, data_len, NULL); parent = fr_dict_root(dict); /* * Padding / End of options */ if (p[0] == 0) return 1; /* 0x00 - Padding option */ if (p[0] == 255) { /* 0xff - End of options signifier */ size_t i; for (i = 1; i < data_len; i++) { if (p[i] != 0) { FR_PROTO_HEX_DUMP(p + i, data_len - i, "ignoring trailing junk at end of packet"); break; } } return data_len; } /* * Everything else should be real options */ if ((data_len < 2) || (data[1] > data_len)) { fr_strerror_printf("%s: Insufficient data", __FUNCTION__); return -1; } child = fr_dict_attr_child_by_num(parent, p[0]); if (!child) { /* * Unknown attribute, create an octets type * attribute with the contents of the sub-option. */ child = fr_dict_unknown_afrom_fields(ctx, parent, fr_dict_vendor_num_by_da(parent), p[0]); if (!child) return -1; } FR_PROTO_TRACE("decode context changed %s:%s -> %s:%s", fr_int2str(fr_value_box_type_table, parent->type, "<invalid>"), parent->name, fr_int2str(fr_value_box_type_table, child->type, "<invalid>"), child->name); ret = decode_value(ctx, cursor, child, data + 2, data[1]); if (ret < 0) { fr_dict_unknown_free(&child); return ret; } ret += 2; /* For header */ FR_PROTO_TRACE("decoding option complete, returning %zu byte(s)", ret); return ret; }
/** Decode DHCP suboptions * * @param[in] ctx context to alloc new attributes in. * @param[in,out] cursor Where to write the decoded options. * @param[in] parent of sub TLVs. * @param[in] data to parse. * @param[in] data_len of data parsed. */ static ssize_t decode_tlv(TALLOC_CTX *ctx, fr_cursor_t *cursor, fr_dict_attr_t const *parent, uint8_t const *data, size_t data_len) { uint8_t const *p = data; uint8_t const *end = data + data_len; fr_dict_attr_t const *child; if (data_len < 3) return -1; /* type, length, value */ FR_PROTO_TRACE("%s called to parse %zu byte(s)", __FUNCTION__, data_len); FR_PROTO_HEX_DUMP(data, data_len, NULL); /* * Each TLV may contain multiple children */ while (p < end) { ssize_t tlv_len; if (p[0] == 0) { p++; continue; } /* * RFC 3046 is very specific about not allowing termination * with a 255 sub-option. But it's required for decoding * option 43, and vendors will probably screw it up * anyway. */ if (p[0] == 255) { p++; return p - data; } /* * Everything else should be real options */ if ((end - p) < 2) { fr_strerror_printf("%s: Insufficient data: Needed at least 2 bytes, got %zu", __FUNCTION__, (end - p)); return -1; } if (p[1] > (end - p)) { fr_strerror_printf("%s: Suboption would overflow option. Remaining option data %zu byte(s) " "(from %zu), Suboption length %u", __FUNCTION__, (end - p), data_len, p[1]); return -1; } child = fr_dict_attr_child_by_num(parent, p[0]); if (!child) { fr_dict_attr_t const *unknown_child; FR_PROTO_TRACE("failed to find child %u of TLV %s", p[0], parent->name); /* * Build an unknown attr */ unknown_child = fr_dict_unknown_afrom_fields(ctx, parent, fr_dict_vendor_num_by_da(parent), p[0]); if (!unknown_child) return -1; child = unknown_child; } FR_PROTO_TRACE("decode context changed %s:%s -> %s:%s", fr_int2str(fr_value_box_type_table, parent->type, "<invalid>"), parent->name, fr_int2str(fr_value_box_type_table, child->type, "<invalid>"), child->name); tlv_len = decode_value(ctx, cursor, child, p + 2, p[1]); if (tlv_len < 0) { fr_dict_unknown_free(&child); return tlv_len; } p += tlv_len + 2; FR_PROTO_TRACE("decode_value returned %zu, adding 2 (for header)", tlv_len); FR_PROTO_TRACE("remaining TLV data %zu byte(s)" , end - p); } FR_PROTO_TRACE("tlv parsing complete, returning %zu byte(s)", p - data); return p - data; }
/** Create any kind of VP from the attribute contents * * @param[in] ctx to allocate new attributes in. * @param[in] cursor to addd new attributes to. * @param[in] parent the current attribute we're processing. * @param[in] data to parse. Points to the data field of the attribute. * @param[in] attr_len length of the attribute being parsed. * @param[in] data_len length of the remaining data in the packet. * @param[in] decoder_ctx IVs, keys etc... * @return * - Length on success. * - -1 on failure. */ static ssize_t sim_decode_pair_value(TALLOC_CTX *ctx, fr_cursor_t *cursor, fr_dict_attr_t const *parent, uint8_t const *data, size_t const attr_len, size_t const data_len, void *decoder_ctx) { VALUE_PAIR *vp; uint8_t const *p = data; size_t prefix = 0; fr_sim_decode_ctx_t *packet_ctx = decoder_ctx; if (!fr_cond_assert(attr_len <= data_len)) return -1; if (!fr_cond_assert(parent)) return -1; FR_PROTO_TRACE("Parent %s len %zu", parent->name, attr_len); FR_PROTO_HEX_DUMP(data, attr_len, __FUNCTION__ ); FR_PROTO_TRACE("Type \"%s\" (%u)", fr_int2str(fr_value_box_type_table, parent->type, "?Unknown?"), parent->type); /* * Special cases, attributes that either have odd formats, or need * have information we need to decode the packet. */ switch (parent->attr) { /* * We need to record packet_ctx so we can decrypt AT_ENCR attributes. * * If we don't find it before, then that's fine, we'll try and * find it in the rest of the packet after the encrypted * attribute. * * 0 1 2 3 * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | AT_IV | Length = 5 | Reserved | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | | * | Initialization Vector | * | | * | | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ */ case FR_SIM_IV: if (sim_iv_extract(&packet_ctx->iv[0], data, attr_len) < 0) return -1; packet_ctx->have_iv = true; break; /* Now create the attribute */ /* * AT_RES - Special case (RES length is in bits) * * 0 1 2 3 * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | AT_RES | Length | RES Length | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-| * | | * | RES | * | | * | | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ */ case FR_EAP_AKA_RES: { uint16_t res_len; if (attr_len < 2) goto raw; /* Need at least two bytes for the length field */ res_len = (p[0] << 8) | p[1]; if (res_len % 8) { fr_strerror_printf("%s: RES Length (%hu) is not a multiple of 8", __FUNCTION__, res_len); return -1; } res_len /= 8; if (res_len > (attr_len - 2)) { fr_strerror_printf("%s: RES Length field value (%u bits) > attribute value length (%zu bits)", __FUNCTION__, res_len * 8, (attr_len - 2) * 8); return -1; } if ((res_len < 4) || (res_len > 16)) { fr_strerror_printf("%s: RES Length field value must be between 32-128 bits, got %u bits", __FUNCTION__, (res_len * 8)); return -1; } vp = fr_pair_afrom_da(ctx, parent); if (!vp) return -1; fr_pair_value_memcpy(vp, p + 2, res_len, true); } goto done; /* * AT_CHECKCODE - Special case (Variable length with no length field) * * 0 1 2 3 * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | AT_CHECKCODE | Length | Reserved | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | | * | Checkcode (0 or 20 bytes) | * | | * | | * | | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ */ case FR_EAP_AKA_CHECKCODE: if (attr_len < 2) goto raw; /* Need at least two bytes for reserved field */ vp = fr_pair_afrom_da(ctx, parent); if (!vp) return -1; fr_pair_value_memcpy(vp, p + 2, attr_len - 2, true); goto done; default: break; } switch (parent->type) { case FR_TYPE_STRING: if (attr_len < 2) goto raw; /* Need at least two bytes for the length field */ if (parent->flags.length && (attr_len != parent->flags.length)) { wrong_len: fr_strerror_printf("%s: Attribute \"%s\" needs a value of exactly %zu bytes, " "but value was %zu bytes", __FUNCTION__, parent->name, (size_t)parent->flags.length, attr_len); goto raw; } break; case FR_TYPE_OCTETS: /* * Get the number of bytes we expect before the value */ prefix = fr_sim_octets_prefix_len(parent); if (attr_len < prefix) goto raw; if (parent->flags.length && (attr_len != (parent->flags.length + prefix))) goto wrong_len; break; case FR_TYPE_BOOL: case FR_TYPE_UINT8: case FR_TYPE_UINT16: case FR_TYPE_UINT32: case FR_TYPE_UINT64: if (attr_len != fr_sim_attr_sizes[parent->type][0]) goto raw; break; case FR_TYPE_TLV: if (attr_len < 2) goto raw; /* * We presume that the TLVs all fit into one * attribute, OR they've already been grouped * into a contiguous memory buffer. */ return sim_decode_tlv(ctx, cursor, parent, p, attr_len, data_len, decoder_ctx); default: raw: /* * We can't create unknowns for non-skippable attributes * as we're prohibited from continuing by the SIM RFCs. */ if (parent->attr <= SIM_SKIPPABLE_MAX) { fr_strerror_printf_push("%s: Failed parsing non-skippable attribute '%s'", __FUNCTION__, parent->name); return -1; } #ifdef __clang_analyzer__ if (!parent->parent) return -1; /* stupid static analyzers */ #endif rad_assert(parent->parent); /* * Re-write the attribute to be "raw". It is * therefore of type "octets", and will be * handled below. */ parent = fr_dict_unknown_afrom_fields(ctx, parent->parent, fr_dict_vendor_num_by_da(parent), parent->attr); if (!parent) { fr_strerror_printf_push("%s[%d]: Internal sanity check failed", __FUNCTION__, __LINE__); return -1; } } vp = fr_pair_afrom_da(ctx, parent); if (!vp) return -1; /* * For unknown attributes copy the entire value, not skipping * any reserved bytes. */ if (parent->flags.is_unknown || parent->flags.is_raw) { fr_pair_value_memcpy(vp, p, attr_len, true); vp->vp_length = attr_len; goto done; } switch (parent->type) { /* * 0 1 2 3 * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | AT_<STRING> | Length | Actual <STRING> Length | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | | * . String . * . . * | | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ */ case FR_TYPE_STRING: { uint16_t actual_len = (p[0] << 8) | p[1]; if (actual_len > (attr_len - 2)) { fr_strerror_printf("%s: Actual length field value (%hu) > attribute value length (%zu)", __FUNCTION__, actual_len, attr_len - 2); return -1; } fr_pair_value_bstrncpy(vp, p + 2, actual_len); } break; case FR_TYPE_OCTETS: /* * Variable length octets buffer */ if (!parent->flags.length) { uint16_t actual_len = (p[0] << 8) | p[1]; if (actual_len > (attr_len - prefix)) { fr_strerror_printf("%s: Actual length field value (%hu) > attribute value length (%zu)", __FUNCTION__, actual_len, attr_len - 2); return -1; } fr_pair_value_memcpy(vp, p + prefix, actual_len, true); /* * Fixed length octets buffer */ } else { fr_pair_value_memcpy(vp, p + prefix, attr_len - prefix, true); } break; /* * Not proper bool. We Use packet_ctx to represent * flag attributes like AT_FULLAUTH_ID_REQ * * 0 1 2 3 * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | AT_<BOOL> | Length = 1 | Reserved | * +---------------+---------------+-------------------------------+ */ case FR_TYPE_BOOL: vp->vp_bool = true; break; /* * Numbers are network byte order. * * In the base RFCs only short (16bit) unsigned integers are used. * We add support for more, just for completeness. * * 0 1 2 3 * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | AT_<SHORT> | Length = 1 | Short 1 | Short 2 | * +---------------+---------------+-------------------------------+ */ case FR_TYPE_UINT8: vp->vp_uint8 = p[0]; break; case FR_TYPE_UINT16: memcpy(&vp->vp_uint16, p, sizeof(vp->vp_uint16)); vp->vp_uint16 = ntohs(vp->vp_uint32); break; case FR_TYPE_UINT32: memcpy(&vp->vp_uint32, p, sizeof(vp->vp_uint32)); vp->vp_uint32 = ntohl(vp->vp_uint32); break; case FR_TYPE_UINT64: memcpy(&vp->vp_uint64, p, sizeof(vp->vp_uint64)); vp->vp_uint64 = ntohll(vp->vp_uint64); break; default: fr_pair_list_free(&vp); fr_strerror_printf_push("%s[%d]: Internal sanity check failed", __FUNCTION__, __LINE__); return -1; } done: vp->type = VT_DATA; fr_cursor_append(cursor, vp); return attr_len; }
/** Break apart a TLV attribute into individual attributes * * @param[in] ctx to allocate new attributes in. * @param[in] cursor to addd new attributes to. * @param[in] parent the current attribute TLV attribute we're processing. * @param[in] data to parse. Points to the data field of the attribute. * @param[in] attr_len length of the TLV attribute. * @param[in] data_len remaining data in the packet. * @param[in] decoder_ctx IVs, keys etc... * @return * - Length on success. * - < 0 on malformed attribute. */ static ssize_t sim_decode_tlv(TALLOC_CTX *ctx, fr_cursor_t *cursor, fr_dict_attr_t const *parent, uint8_t const *data, size_t const attr_len, size_t data_len, void *decoder_ctx) { uint8_t const *p = data, *end = p + attr_len; uint8_t *decr = NULL; ssize_t decr_len; fr_dict_attr_t const *child; VALUE_PAIR *head = NULL; fr_cursor_t tlv_cursor; ssize_t rcode; if (data_len < 2) { fr_strerror_printf("%s: Insufficient data", __FUNCTION__); return -1; /* minimum attr size */ } /* * We have an AES-128-CBC encrypted attribute * * IV is from AT_IV, key is from k_encr. * * unfortunately the ordering of these two attributes * aren't specified, so we may have to hunt for the IV. */ if (parent->flags.encrypt) { FR_PROTO_TRACE("found encrypted attribute '%s'", parent->name); decr_len = sim_value_decrypt(ctx, &decr, p + 2, attr_len - 2, data_len - 2, decoder_ctx); /* Skip reserved */ if (decr_len < 0) return -1; p = decr; end = p + decr_len; } else { p += 2; /* Skip the reserved bytes */ } FR_PROTO_HEX_DUMP(p, end - p, "tlvs"); /* * Record where we were in the list when packet_ctx function was called */ fr_cursor_init(&tlv_cursor, &head); while ((size_t)(end - p) >= sizeof(uint32_t)) { uint8_t sim_at = p[0]; size_t sim_at_len = ((size_t)p[1]) << 2; if ((p + sim_at_len) > end) { fr_strerror_printf("%s: Malformed nested attribute %d: Length field (%zu bytes) value " "longer than remaining data in parent (%zu bytes)", __FUNCTION__, sim_at, sim_at_len, end - p); error: talloc_free(decr); fr_pair_list_free(&head); return -1; } if (sim_at_len == 0) { fr_strerror_printf("%s: Malformed nested attribute %d: Length field 0", __FUNCTION__, sim_at); goto error; } /* * Padding attributes are cleartext inside of * encrypted TLVs to pad out the value to the * correct length for the block cipher * (16 in the case of AES-128-CBC). */ if (sim_at == FR_SIM_PADDING) { uint8_t zero = 0; uint8_t i; if (!parent->flags.encrypt) { fr_strerror_printf("%s: Found padding attribute outside of an encrypted TLV", __FUNCTION__); goto error; } if (!fr_cond_assert(data_len % 4)) goto error; if (sim_at_len > 12) { fr_strerror_printf("%s: Expected padding attribute length <= 12 bytes, got %zu bytes", __FUNCTION__, sim_at_len); goto error; } /* * RFC says we MUST verify that FR_SIM_PADDING * data is zeroed out. */ for (i = 2; i < sim_at_len; i++) zero |= p[i]; if (zero) { fr_strerror_printf("%s: Padding attribute value not zeroed 0x%pH", __FUNCTION__, fr_box_octets(p + 2, sim_at_len - 2)); goto error; } p += sim_at_len; continue; } child = fr_dict_attr_child_by_num(parent, p[0]); if (!child) { fr_dict_attr_t const *unknown_child; FR_PROTO_TRACE("Failed to find child %u of TLV %s", p[0], parent->name); /* * Encountered none skippable attribute * * RFC says we need to die on these if we don't * understand them. non-skippables are < 128. */ if (sim_at <= SIM_SKIPPABLE_MAX) { fr_strerror_printf("%s: Unknown (non-skippable) attribute %i", __FUNCTION__, sim_at); goto error; } /* * Build an unknown attr */ unknown_child = fr_dict_unknown_afrom_fields(ctx, parent, fr_dict_vendor_num_by_da(parent), p[0]); if (!unknown_child) goto error; child = unknown_child; } FR_PROTO_TRACE("decode context changed %s -> %s", parent->name, child->name); rcode = sim_decode_pair_value(ctx, &tlv_cursor, child, p + 2, sim_at_len - 2, (end - p) - 2, decoder_ctx); if (rcode < 0) goto error; p += sim_at_len; } fr_cursor_head(&tlv_cursor); fr_cursor_tail(cursor); fr_cursor_merge(cursor, &tlv_cursor); /* Wind to the end of the new pairs */ talloc_free(decr); return attr_len; }
/* * Use a reply packet to determine what to do. */ static rlm_rcode_t CC_HINT(nonnull) process_reply(NDEBUG_UNUSED eap_session_t *eap_session, tls_session_t *tls_session, REQUEST *request, RADIUS_PACKET *reply) { rlm_rcode_t rcode = RLM_MODULE_REJECT; VALUE_PAIR *vp; fr_cursor_t cursor; eap_fast_tunnel_t *t = talloc_get_type_abort(tls_session->opaque, eap_fast_tunnel_t); rad_assert(eap_session->request == request); /* * If the response packet was Access-Accept, then * we're OK. If not, die horribly. * * FIXME: EAP-Messages can only start with 'identity', * NOT 'eap start', so we should check for that.... */ switch (reply->code) { case FR_CODE_ACCESS_ACCEPT: RDEBUG2("Got tunneled Access-Accept"); rcode = RLM_MODULE_OK; /* * Copy what we need into the TTLS tunnel and leave * the rest to be cleaned up. */ for (vp = fr_cursor_init(&cursor, &reply->vps); vp; vp = fr_cursor_next(&cursor)) { if (fr_dict_vendor_num_by_da(vp->da) != VENDORPEC_MICROSOFT) continue; /* FIXME must be a better way to capture/re-derive this later for ISK */ switch (vp->da->attr) { case FR_MSCHAP_MPPE_SEND_KEY: if (vp->vp_length != RADIUS_CHAP_CHALLENGE_LENGTH) { wrong_length: REDEBUG("Found %s with incorrect length. Expected %u, got %zu", vp->da->name, RADIUS_CHAP_CHALLENGE_LENGTH, vp->vp_length); rcode = RLM_MODULE_INVALID; break; } memcpy(t->isk.mppe_send, vp->vp_octets, RADIUS_CHAP_CHALLENGE_LENGTH); break; case FR_MSCHAP_MPPE_RECV_KEY: if (vp->vp_length != RADIUS_CHAP_CHALLENGE_LENGTH) goto wrong_length; memcpy(t->isk.mppe_recv, vp->vp_octets, RADIUS_CHAP_CHALLENGE_LENGTH); break; case FR_MSCHAP2_SUCCESS: RDEBUG2("Got %s, tunneling it to the client in a challenge", vp->da->name); rcode = RLM_MODULE_HANDLED; t->authenticated = true; break; default: break; } } RHEXDUMP(L_DBG_LVL_MAX, (uint8_t *)&t->isk, 2 * RADIUS_CHAP_CHALLENGE_LENGTH, "ISK[j]"); /* FIXME (part of above) */ break; case FR_CODE_ACCESS_REJECT: REDEBUG("Got tunneled Access-Reject"); rcode = RLM_MODULE_REJECT; break; case FR_CODE_ACCESS_CHALLENGE: RDEBUG2("Got tunneled Access-Challenge"); /* * Copy the EAP-Message back to the tunnel. */ (void) fr_cursor_init(&cursor, &reply->vps); for (vp = fr_cursor_iter_by_da_init(&cursor, &reply->vps, attr_eap_message); vp; vp = fr_cursor_next(&cursor)) { eap_fast_tlv_append(tls_session, attr_eap_fast_eap_payload, true, vp->vp_length, vp->vp_octets); } rcode = RLM_MODULE_HANDLED; break; default: REDEBUG("Unknown RADIUS packet type %d: rejecting tunneled user", reply->code); rcode = RLM_MODULE_INVALID; break; } return rcode; }
static int getusersfile(TALLOC_CTX *ctx, char const *filename, rbtree_t **ptree) { int rcode; VALUE_PAIR *vp; PAIR_LIST *users = NULL; PAIR_LIST *entry, *next; PAIR_LIST *user_list, *default_list, **default_tail; rbtree_t *tree; if (!filename) { *ptree = NULL; return 0; } rcode = pairlist_read(ctx, dict_radius, filename, &users, 1); if (rcode < 0) { return -1; } /* * Walk through the 'users' file list */ entry = users; while (entry) { fr_cursor_t cursor; /* * Look for improper use of '=' in the * check items. They should be using * '==' for on-the-wire RADIUS attributes, * and probably ':=' for server * configuration items. */ for (vp = fr_cursor_init(&cursor, &entry->check); vp; vp = fr_cursor_next(&cursor)) { /* * Ignore attributes which are set * properly. */ if (vp->op != T_OP_EQ) { continue; } /* * If it's a vendor attribute, * or it's a wire protocol, * ensure it has '=='. */ if ((fr_dict_vendor_num_by_da(vp->da) != 0) || (vp->da->attr < 0x100)) { WARN("[%s]:%d Changing '%s =' to '%s =='\n\tfor comparing RADIUS attribute in check item list for user %s", filename, entry->lineno, vp->da->name, vp->da->name, entry->name); vp->op = T_OP_CMP_EQ; continue; } } /* end of loop over check items */ /* * Look for server configuration items * in the reply list. * * It's a common enough mistake, that it's * worth doing. */ for (vp = fr_cursor_init(&cursor, &entry->reply); 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 (fr_dict_attr_is_top_level(vp->da) && (vp->da->attr > 1000)) { WARN("[%s]:%d Check item \"%s\"\n" "\tfound in reply item list for user \"%s\".\n" "\tThis attribute MUST go on the first line" " with the other check items", filename, entry->lineno, vp->da->name, entry->name); } } entry = entry->next; } tree = rbtree_create(ctx, pairlist_cmp, NULL, RBTREE_FLAG_NONE); if (!tree) { pairlist_free(&users); return -1; } default_list = NULL; default_tail = &default_list; /* * We've read the entries in linearly, but putting them * into an indexed data structure would be much faster. * Let's go fix that now. */ for (entry = users; entry != NULL; entry = next) { /* * Remove this entry from the input list. */ next = entry->next; entry->next = NULL; /* * DEFAULT entries get their own list. */ if (strcmp(entry->name, "DEFAULT") == 0) { if (!default_list) { default_list = entry; /* * Insert the first DEFAULT into the tree. */ if (!rbtree_insert(tree, entry)) { error: pairlist_free(&entry); pairlist_free(&next); talloc_free(tree); return -1; } } else { /* * Tack this entry onto the tail * of the DEFAULT list. */ *default_tail = entry; } default_tail = &entry->next; continue; } /* * Not DEFAULT, must be a normal user. */ user_list = rbtree_finddata(tree, entry); if (!user_list) { /* * Insert the first one. */ if (!rbtree_insert(tree, entry)) goto error; } else { /* * Find the tail of this list, and add it * there. */ while (user_list->next) user_list = user_list->next; user_list->next = entry; } } *ptree = tree; return 0; }