buf = talloc_size(request, ksize + sizeof(*t->keyblock));
scratch = talloc_size(request, ksize + sizeof(*t->keyblock));
- t->keyblock = talloc(request, eap_fast_keyblock_t);
+ t->keyblock = talloc(t, eap_fast_keyblock_t);
eap_fast_tls_gen_challenge(tls_session->ssl, buf, scratch, ksize + sizeof(*t->keyblock), "key expansion");
memcpy(t->keyblock, &buf[ksize], sizeof(*t->keyblock));
memset(buf, 0, ksize + sizeof(*t->keyblock));
- t->simck = talloc_size(request, EAP_FAST_SIMCK_LEN);
+ t->simck = talloc_size(t, EAP_FAST_SIMCK_LEN);
memcpy(t->simck, t->keyblock, EAP_FAST_SKS_LEN); /* S-IMCK[0] = session_key_seed */
- t->cmk = talloc_size(request, EAP_FAST_CMK_LEN); /* note that CMK[0] is not defined */
+ t->cmk = talloc_size(t, EAP_FAST_CMK_LEN); /* note that CMK[0] is not defined */
t->imckc = 0;
talloc_free(buf);
*
* RFC 4851 section 5.4 - EAP Master Session Key Generation
*/
- t->msk = talloc_size(request, EAP_FAST_KEY_LEN);
+ t->msk = talloc_size(t, EAP_FAST_KEY_LEN);
T_PRF(t->simck, EAP_FAST_SIMCK_LEN, "Session Key Generating Function", NULL, 0, t->msk, EAP_FAST_KEY_LEN);
- t->emsk = talloc_size(request, EAP_EMSK_LEN);
+ t->emsk = talloc_size(t, EAP_EMSK_LEN);
T_PRF(t->simck, EAP_FAST_SIMCK_LEN, "Extended Session Key Generating Function", NULL, 0, t->emsk, EAP_EMSK_LEN);
}
return 1;
}
+static ssize_t eap_fast_decode_vp(TALLOC_CTX *request, DICT_ATTR const *parent,
+ uint8_t const *data, size_t const attr_len, VALUE_PAIR **out)
+{
+ int8_t tag = TAG_NONE;
+ VALUE_PAIR *vp;
+ uint8_t const *p = data;
+
+ /*
+ * FIXME: Attrlen can be larger than 253 for extended attrs!
+ */
+ if (!parent || !out ) {
+ RERROR("eap_fast_decode_vp: Invalid arguments");
+ return -1;
+ }
+
+ /*
+ * Silently ignore zero-length attributes.
+ */
+ if (attr_len == 0) return 0;
+
+ /*
+ * And now that we've verified the basic type
+ * information, decode the actual p.
+ */
+ vp = fr_pair_afrom_da(request, parent);
+ if (!vp) return -1;
+
+ vp->vp_length = attr_len;
+ vp->tag = tag;
+
+ switch (parent->type) {
+ case PW_TYPE_STRING:
+ fr_pair_value_bstrncpy(vp, p, attr_len);
+ break;
+
+ case PW_TYPE_OCTETS:
+ fr_pair_value_memcpy(vp, p, attr_len);
+ break;
+
+ case PW_TYPE_ABINARY:
+ if (vp->vp_length > sizeof(vp->vp_filter)) {
+ vp->vp_length = sizeof(vp->vp_filter);
+ }
+ memcpy(vp->vp_filter, p, vp->vp_length);
+ break;
+
+ case PW_TYPE_BYTE:
+ vp->vp_byte = p[0];
+ break;
+
+ case PW_TYPE_SHORT:
+ vp->vp_short = (p[0] << 8) | p[1];
+ break;
+
+ case PW_TYPE_INTEGER:
+ memcpy(&vp->vp_integer, p, 4);
+ vp->vp_integer = ntohl(vp->vp_integer);
+ break;
+
+ case PW_TYPE_INTEGER64:
+ memcpy(&vp->vp_integer64, p, 8);
+ vp->vp_integer64 = ntohll(vp->vp_integer64);
+ break;
+
+ case PW_TYPE_DATE:
+ memcpy(&vp->vp_date, p, 4);
+ vp->vp_date = ntohl(vp->vp_date);
+ break;
+
+ case PW_TYPE_ETHERNET:
+ memcpy(vp->vp_ether, p, 6);
+ break;
+
+ case PW_TYPE_IPV4_ADDR:
+ memcpy(&vp->vp_ipaddr, p, 4);
+ break;
+
+ case PW_TYPE_IFID:
+ memcpy(vp->vp_ifid, p, 8);
+ break;
+
+ case PW_TYPE_IPV6_ADDR:
+ memcpy(&vp->vp_ipv6addr, p, 16);
+ break;
+
+ case PW_TYPE_IPV6_PREFIX:
+ /*
+ * FIXME: double-check that
+ * (vp->vp_octets[1] >> 3) matches vp->vp_length + 2
+ */
+ memcpy(vp->vp_ipv6prefix, p, vp->vp_length);
+ if (vp->vp_length < 18) {
+ memset(((uint8_t *)vp->vp_ipv6prefix) + vp->vp_length, 0,
+ 18 - vp->vp_length);
+ }
+ break;
+
+ case PW_TYPE_IPV4_PREFIX:
+ /* FIXME: do the same double-check as for IPv6Prefix */
+ memcpy(vp->vp_ipv4prefix, p, vp->vp_length);
+
+ /*
+ * /32 means "keep all bits". Otherwise, mask
+ * them out.
+ */
+ if ((p[1] & 0x3f) > 32) {
+ uint32_t addr, mask;
+
+ memcpy(&addr, vp->vp_octets + 2, sizeof(addr));
+ mask = 1;
+ mask <<= (32 - (p[1] & 0x3f));
+ mask--;
+ mask = ~mask;
+ mask = htonl(mask);
+ addr &= mask;
+ memcpy(vp->vp_ipv4prefix + 2, &addr, sizeof(addr));
+ }
+ break;
+
+ case PW_TYPE_SIGNED: /* overloaded with vp_integer */
+ memcpy(&vp->vp_integer, p, 4);
+ vp->vp_integer = ntohl(vp->vp_integer);
+ break;
+
+ default:
+ RERROR("eap_fast_decode_vp: type %d Internal sanity check %d ", parent->type, __LINE__);
+ fr_pair_list_free(&vp);
+ return -1;
+ }
+ vp->type = VT_DATA;
+ *out = vp;
+ return attr_len;
+}
+
VALUE_PAIR *eap_fast_fast2vp(REQUEST *request, SSL *ssl, uint8_t const *data, size_t data_len,
DICT_ATTR const *fast_da, vp_cursor_t *out)
DICT_ATTR const *da;
if (!fast_da)
- fast_da = dict_attrbyvalue(0, PW_EAP_FAST_TLV);
+ fast_da = dict_attrbyvalue(PW_FREERADIUS_EAP_FAST_TLV, VENDORPEC_FREERADIUS);
rad_assert(fast_da != NULL);
if (!out) {
*
* For now, if it doesn't exist, ignore it.
*/
- da = dict_attrbyparent(fast_da, attr, 0);
- if (!da) goto next_attr;
-
+ da = dict_attrbyparent(fast_da, attr, fast_da->vendor);
+ if (!da) {
+ RDEBUG("eap_fast_fast2vp: no sub attribute found %s attr: %u vendor: %u",
+ fast_da->name, attr, fast_da->vendor);
+ goto next_attr;
+ }
if (da->type == PW_TYPE_TLV) {
eap_fast_fast2vp(request, ssl, data, length, da, out);
goto next_attr;
}
-/*
-ssize_t fr_radius_decode_pair_value(TALLOC_CTX *ctx, vp_cursor_t *cursor, fr_dict_attr_t const *parent,
- uint8_t const *data, size_t const attr_len, size_t const packet_len,
- void *decoder_ctx)
-
- vp = NULL;
- decoded = rad_attr2vp(request->packet, NULL, NULL, NULL,
- data, size + 2, &vp);
+ decoded = eap_fast_decode_vp(request, da, data, length, &vp);
if (decoded < 0) {
RERROR("Failed decoding %s: %s", da->name, fr_strerror());
goto next_attr;
}
-*/
+
+ fr_cursor_merge(out, vp);
next_attr:
while (fr_cursor_next(out)) {
}
-static void eap_vp2fast(tls_session_t *tls_session, VALUE_PAIR *first)
-{
- VALUE_PAIR *vp;
- vp_cursor_t cursor;
+static void eapfast_copy_request_to_tunnel(REQUEST *request, REQUEST *fake) {
+ VALUE_PAIR *copy, *vp;
+ vp_cursor_t cursor;
- for (vp = fr_cursor_init(&cursor, &first); vp; vp = fr_cursor_next(&cursor))
- {
- if (vp->da->vendor != 0 && vp->da->attr != PW_EAP_MESSAGE) continue;
+ for (vp = fr_cursor_init(&cursor, &request->packet->vps);
+ vp;
+ vp = fr_cursor_next(&cursor)) {
+ /*
+ * The attribute is a server-side thingy,
+ * don't copy it.
+ */
+ if ((vp->da->attr > 255) && (((vp->da->attr >> 16) & 0xffff) == 0)) {
+ continue;
+ }
- eap_fast_tlv_append(tls_session, EAP_FAST_TLV_EAP_PAYLOAD, true, vp->vp_length, vp->vp_octets);
- }
-}
+ /*
+ * The outside attribute is already in the
+ * tunnel, don't copy it.
+ *
+ * This works for BOTH attributes which
+ * are originally in the tunneled request,
+ * AND attributes which are copied there
+ * from below.
+ */
+ if (fr_pair_find_by_da(fake->packet->vps, vp->da, TAG_ANY)) continue;
+ /*
+ * Some attributes are handled specially.
+ */
+ if (!vp->da->vendor) switch (vp->da->attr) {
+ /*
+ * NEVER copy Message-Authenticator,
+ * EAP-Message, or State. They're
+ * only for outside of the tunnel.
+ */
+ case PW_USER_NAME:
+ case PW_USER_PASSWORD:
+ case PW_CHAP_PASSWORD:
+ case PW_CHAP_CHALLENGE:
+ case PW_PROXY_STATE:
+ case PW_MESSAGE_AUTHENTICATOR:
+ case PW_EAP_MESSAGE:
+ case PW_STATE:
+ continue;
+
+ /*
+ * By default, copy it over.
+ */
+ default:
+ break;
+ }
+
+ /*
+ * Don't copy from the head, we've already
+ * checked it.
+ */
+ copy = fr_pair_list_copy_by_num(fake->packet, vp, vp->da->attr, vp->da->vendor, TAG_ANY);
+ fr_pair_add(&fake->packet->vps, copy);
+ }
+}
/*
* Use a reply packet to determine what to do.
REQUEST *request, RADIUS_PACKET *reply)
{
rlm_rcode_t rcode = RLM_MODULE_REJECT;
- VALUE_PAIR *vp, *tunnel_vps = NULL;
+ VALUE_PAIR *vp;
vp_cursor_t cursor;
- vp_cursor_t to_tunnel;
eap_fast_tunnel_t *t = tls_session->opaque;
* If the response packet was Access-Accept, then
* we're OK. If not, die horribly.
*
- * FIXME: Take MS-CHAP2-Success attribute, and
- * tunnel it back to the client, to authenticate
- * ourselves to the client.
- *
- * FIXME: If we have an Access-Challenge, then
- * the Reply-Message is tunneled back to the client.
- *
- * FIXME: If we have an EAP-Message, then that message
- * must be tunneled back to the client.
- *
- * FIXME: If we have an Access-Challenge with a State
- * attribute, then do we tunnel that to the client, or
- * keep track of it ourselves?
- *
* FIXME: EAP-Messages can only start with 'identity',
* NOT 'eap start', so we should check for that....
*/
switch (reply->code) {
case PW_CODE_ACCESS_ACCEPT:
RDEBUG("Got tunneled Access-Accept");
- fr_cursor_init(&to_tunnel, &tunnel_vps);
rcode = RLM_MODULE_OK;
for (vp = fr_cursor_init(&cursor, &reply->vps); vp; vp = fr_cursor_next(&cursor)) {
- switch (vp->da->vendor) {
- case VENDORPEC_MICROSOFT:
- /* FIXME must be a better way to capture/re-derive this later for ISK */
- switch(vp->da->attr) {
- case PW_MSCHAP_MPPE_SEND_KEY:
- memcpy(t->isk.mppe_send, vp->vp_octets, CHAP_VALUE_LENGTH);
- break;
-
- case PW_MSCHAP_MPPE_RECV_KEY:
- memcpy(t->isk.mppe_recv, vp->vp_octets, CHAP_VALUE_LENGTH);
- break;
-
- case PW_MSCHAP2_SUCCESS:
- RDEBUG("Got %s, tunneling it to the client in a challenge", vp->da->name);
- rcode = RLM_MODULE_HANDLED;
- if (t->use_tunneled_reply) {
- t->authenticated = true;
- /*
- * Clean up the tunneled reply.
- */
- fr_pair_delete_by_num(&reply->vps, PW_PROXY_STATE, 0, TAG_ANY);
- fr_pair_delete_by_num(&reply->vps, PW_EAP_MESSAGE, 0, TAG_ANY);
- fr_pair_delete_by_num(&reply->vps, PW_MESSAGE_AUTHENTICATOR, 0, TAG_ANY);
-
- /*
- * Delete MPPE keys & encryption policy. We don't
- * want these here.
- */
- fr_pair_delete_by_num(&reply->vps, 7, VENDORPEC_MICROSOFT, TAG_ANY);
- fr_pair_delete_by_num(&reply->vps, 8, VENDORPEC_MICROSOFT, TAG_ANY);
- fr_pair_delete_by_num(&reply->vps, 16, VENDORPEC_MICROSOFT, TAG_ANY);
- fr_pair_delete_by_num(&reply->vps, 17, VENDORPEC_MICROSOFT, TAG_ANY);
-
- fr_pair_list_free(&t->accept_vps); /* for proxying MS-CHAP2 */
- fr_pair_list_mcopy_by_num(t, &t->accept_vps, &reply->vps, 0, 0, TAG_ANY);
- rad_assert(!reply->vps);
- }
- break;
-
- default:
- break;
- }
+ if (vp->da->vendor != VENDORPEC_MICROSOFT) continue;
+
+ /* FIXME must be a better way to capture/re-derive this later for ISK */
+ switch (vp->da->attr) {
+ case PW_MSCHAP_MPPE_SEND_KEY:
+ memcpy(t->isk.mppe_send, vp->vp_octets, CHAP_VALUE_LENGTH);
+ break;
+
+ case PW_MSCHAP_MPPE_RECV_KEY:
+ memcpy(t->isk.mppe_recv, vp->vp_octets, CHAP_VALUE_LENGTH);
break;
+ case PW_MSCHAP2_SUCCESS:
+ RDEBUG("Got %s, tunneling it to the client in a challenge", vp->da->name);
+ rcode = RLM_MODULE_HANDLED;
+ if (t->use_tunneled_reply) {
+ t->authenticated = true;
+ /*
+ * Clean up the tunneled reply.
+ */
+ fr_pair_delete_by_num(&reply->vps, PW_PROXY_STATE, 0, TAG_ANY);
+ fr_pair_delete_by_num(&reply->vps, PW_EAP_MESSAGE, 0, TAG_ANY);
+ fr_pair_delete_by_num(&reply->vps, PW_MESSAGE_AUTHENTICATOR, 0, TAG_ANY);
+
+ /*
+ * Delete MPPE keys & encryption policy. We don't
+ * want these here.
+ */
+ fr_pair_delete_by_num(&reply->vps, 7, VENDORPEC_MICROSOFT, TAG_ANY);
+ fr_pair_delete_by_num(&reply->vps, 8, VENDORPEC_MICROSOFT, TAG_ANY);
+ fr_pair_delete_by_num(&reply->vps, 16, VENDORPEC_MICROSOFT, TAG_ANY);
+ fr_pair_delete_by_num(&reply->vps, 17, VENDORPEC_MICROSOFT, TAG_ANY);
+
+ fr_pair_list_free(&t->accept_vps); /* for proxying MS-CHAP2 */
+ fr_pair_list_mcopy_by_num(t, &t->accept_vps, &reply->vps, 0, 0, TAG_ANY);
+ rad_assert(!reply->vps);
+ }
+ break;
+
default:
break;
}
fr_pair_list_mcopy_by_num(t, &t->state, &reply->vps, PW_STATE, 0, TAG_ANY);
/*
- * We should really be a bit smarter about this,
- * and move over only those attributes which
- * are relevant to the authentication request,
- * but that's a lot more work, and this "dumb"
- * method works in 99.9% of the situations.
+ * Copy the EAP-Message back to the tunnel.
*/
- vp = NULL;
- fr_pair_list_mcopy_by_num(t, &vp, &reply->vps, PW_EAP_MESSAGE, 0, TAG_ANY);
+ (void) fr_cursor_init(&cursor, &reply->vps);
- /*
- * There MUST be a Reply-Message in the challenge,
- * which we tunnel back to the client.
- *
- * If there isn't one in the reply VP's, then
- * we MUST create one, with an empty string as
- * it's value.
- */
- fr_pair_list_mcopy_by_num(t, &vp, &reply->vps, PW_REPLY_MESSAGE, 0, TAG_ANY);
+ while ((vp = fr_cursor_next_by_num(&cursor, PW_EAP_MESSAGE, 0, TAG_ANY)) != NULL) {
+ eap_fast_tlv_append(tls_session, EAP_FAST_TLV_EAP_PAYLOAD, true, vp->vp_length, vp->vp_octets);
+ }
rcode = RLM_MODULE_HANDLED;
break;
}
- /*
- * Pack any tunnelled VPs and send them back
- * to the supplicant.
- */
- if (tunnel_vps) {
- RDEBUG("Sending tunneled reply attributes");
- rdebug_pair_list(L_DBG_LVL_2, request, tunnel_vps, NULL);
-
- eap_vp2fast(tls_session, tunnel_vps);
- fr_pair_list_free(&tunnel_vps);
- }
-
return rcode;
}
* Add the tunneled attributes to the fake request.
*/
- fake->packet->vps = fr_pair_afrom_num(fake->packet, 0, PW_EAP_MESSAGE);
+ fake->packet->vps = fr_pair_afrom_num(fake->packet, PW_EAP_MESSAGE, 0);
fr_pair_value_memcpy(fake->packet->vps, tlv_eap_payload->vp_octets, tlv_eap_payload->vp_length);
RDEBUG("Got tunneled request");
/*
* Update other items in the REQUEST data structure.
*/
- fake->username = fr_pair_find_by_num(fake->packet->vps, 0, PW_USER_NAME, TAG_ANY);
- fake->password = fr_pair_find_by_num(fake->packet->vps, 0, PW_USER_PASSWORD, TAG_ANY);
+ fake->username = fr_pair_find_by_num(fake->packet->vps, PW_USER_NAME, 0, TAG_ANY);
+ fake->password = fr_pair_find_by_num(fake->packet->vps, PW_USER_PASSWORD, 0, TAG_ANY);
/*
* No User-Name, try to create one from stored data.
* an EAP-Identity, and pull it out of there.
*/
if (!t->username) {
- vp = fr_pair_find_by_num(fake->packet->vps, 0, PW_EAP_MESSAGE, TAG_ANY);
+ vp = fr_pair_find_by_num(fake->packet->vps, PW_EAP_MESSAGE, 0, TAG_ANY);
if (vp &&
(vp->vp_length >= EAP_HEADER_LEN + 2) &&
(vp->vp_strvalue[0] == PW_EAP_RESPONSE) &&
if (t->username) {
vp = fr_pair_list_copy(fake->packet, t->username);
fr_pair_add(&fake->packet->vps, vp);
- fake->username = fr_pair_find_by_num(fake->packet->vps, 0, PW_USER_NAME, TAG_ANY);
+ fake->username = vp;
}
} /* else the request ALREADY had a User-Name */
+ /*
+ * Add the State attribute, too, if it exists.
+ */
+ if (t->state) {
+ vp = fr_pair_list_copy(fake->packet, t->state);
+ if (vp) fr_pair_add(&fake->packet->vps, vp);
+ }
+
+
if (t->stage == AUTHENTICATION) { /* FIXME do this only for MSCHAPv2 */
VALUE_PAIR *tvp;
- tvp = fr_pair_afrom_num(fake->packet, 0, PW_EAP_TYPE);
- tvp->vp_integer = t->default_provisioning_method;
- fr_pair_add(&fake->config, tvp);
+ RDEBUG2("AUTHENTICATION");
+ vp = fr_pair_make(fake, &fake->config, "EAP-Type", "0", T_OP_EQ);
+ vp->vp_integer = t->default_method;
/*
* RFC 5422 section 3.2.3 - Authenticating Using EAP-FAST-MSCHAPv2
*/
if (t->mode == EAP_FAST_PROVISIONING_ANON) {
- tvp = fr_pair_afrom_num(fake->packet, VENDORPEC_MICROSOFT, PW_MSCHAP_CHALLENGE);
+ tvp = fr_pair_afrom_num(fake, PW_MSCHAP_CHALLENGE, VENDORPEC_MICROSOFT);
fr_pair_value_memcpy(tvp, t->keyblock->server_challenge, CHAP_VALUE_LENGTH);
fr_pair_add(&fake->config, tvp);
- tvp = fr_pair_afrom_num(fake->packet, 0, PW_MS_CHAP_PEER_CHALLENGE);
+ tvp = fr_pair_afrom_num(fake, PW_MS_CHAP_PEER_CHALLENGE, 0);
fr_pair_value_memcpy(tvp, t->keyblock->client_challenge, CHAP_VALUE_LENGTH);
fr_pair_add(&fake->config, tvp);
}
}
+ if (t->copy_request_to_tunnel) {
+ eapfast_copy_request_to_tunnel(request, fake);
+ }
+
+ if ((vp = fr_pair_find_by_num(request->config, PW_VIRTUAL_SERVER, 0, TAG_ANY)) != NULL) {
+ fake->server = vp->vp_strvalue;
+
+ } else if (t->virtual_server) {
+ fake->server = t->virtual_server;
+
+ } /* else fake->server == request->server */
+
/*
* Call authentication recursively, which will
* do PAP, CHAP, MS-CHAP, etc.
* Decide what to do with the reply.
*/
switch (fake->reply->code) {
- case 0: /* No reply code, must be proxied... */
-#ifdef WITH_PROXY
- vp = fr_pair_find_by_num(fake->config, 0, PW_PROXY_TO_REALM, TAG_ANY);
- if (vp) {
- int ret;
- eap_tunnel_data_t *tunnel;
-
- RDEBUG("Tunneled authentication will be proxied to %s", vp->vp_strvalue);
-
- /*
- * Tell the original request that it's going
- * to be proxied.
- */
- fr_pair_list_mcopy_by_num(request, &request->config, &fake->config, 0,
- PW_PROXY_TO_REALM, TAG_ANY);
-
- /*
- * Seed the proxy packet with the
- * tunneled request.
- */
- rad_assert(!request->proxy);
-
- request->proxy = talloc_steal(request, fake->packet);
-
- memset(&request->proxy->src_ipaddr, 0,
- sizeof(request->proxy->src_ipaddr));
- memset(&request->proxy->src_ipaddr, 0,
- sizeof(request->proxy->src_ipaddr));
- request->proxy->src_port = 0;
- request->proxy->dst_port = 0;
- fake->packet = NULL;
- rad_free(&fake->reply);
- fake->reply = NULL;
-
- /*
- * Set up the callbacks for the tunnel
- */
- tunnel = talloc_zero(request, eap_tunnel_data_t);
- tunnel->tls_session = tls_session;
-
- /*
- * Associate the callback with the request.
- */
- ret = request_data_add(request, request->proxy, REQUEST_DATA_EAP_TUNNEL_CALLBACK,
- tunnel, false);
- rad_assert(ret == 0);
-
- /*
- * rlm_eap.c has taken care of associating
- * the eap_session with the fake request.
- *
- * So we associate the fake request with
- * this request.
- */
- ret = request_data_add(request, request->proxy, REQUEST_DATA_EAP_MSCHAP_TUNNEL_CALLBACK,
- fake, true);
- rad_assert(ret == 0);
-
- fake = NULL;
-
- /*
- * Didn't authenticate the packet, but
- * we're proxying it.
- */
- code = PW_CODE_STATUS_CLIENT;
-
- } else
-#endif /* WITH_PROXY */
- {
- RDEBUG("No tunneled reply was found, and the request was not proxied: rejecting the user.");
- code = PW_CODE_ACCESS_REJECT;
- }
+ case 0:
+ RDEBUG("No tunneled reply was found, rejecting the user.");
+ code = PW_CODE_ACCESS_REJECT;
break;
default:
return PW_CODE_ACCESS_ACCEPT;
}
+
+#define PW_EAP_FAST_TLV_PAC (PW_FREERADIUS_EAP_FAST_TLV | (EAP_FAST_TLV_PAC << 8))
+
+
+
static PW_CODE eap_fast_process_tlvs(REQUEST *request, eap_handler_t *eap_session,
tls_session_t *tls_session, VALUE_PAIR *fast_vps)
{
for (vp = fr_cursor_init(&cursor, &fast_vps); vp; vp = fr_cursor_next(&cursor)) {
PW_CODE code = PW_CODE_ACCESS_REJECT;
char *value;
- DICT_ATTR * parent = dict_parent(vp->da->attr, vp->da->vendor);
+ DICT_ATTR const *parent_da = NULL;
+ parent_da = dict_parent(vp->da->attr, vp->da->vendor);
+ if (parent_da == NULL || vp->da->vendor != VENDORPEC_FREERADIUS ||
+ ((vp->da->attr & 0xff) != PW_FREERADIUS_EAP_FAST_TLV)) {
+ value = vp_aprints(request->packet, vp, '"');
+ RDEBUG2("ignoring non-EAP-FAST TLV %s", value);
+ talloc_free(value);
+ continue;
+ }
- switch (parent->attr) {
- case PW_EAP_FAST_TLV:
- switch (vp->da->attr) {
+ switch (parent_da->attr) {
+ case PW_FREERADIUS_EAP_FAST_TLV:
+ switch (vp->da->attr >> 8) {
case EAP_FAST_TLV_EAP_PAYLOAD:
code = eap_fast_eap_payload(request, eap_session, tls_session, vp);
if (code == PW_CODE_ACCESS_ACCEPT)
code = PW_CODE_ACCESS_ACCEPT;
t->stage = PROVISIONING;
break;
+ case EAP_FAST_TLV_CRYPTO_BINDING:
+ if (!binding) {
+ binding = talloc_zero(request->packet, eap_tlv_crypto_binding_tlv_t);
+ memcpy(binding, vp->vp_octets, sizeof(*binding));
+ binding->tlv_type = htons(EAP_FAST_TLV_MANDATORY | EAP_FAST_TLV_CRYPTO_BINDING);
+ binding->length = htons(sizeof(*binding) - 2 * sizeof(uint16_t));
+ }
+ continue;
default:
value = vp_aprints_value(request->packet, vp, '"');
RDEBUG2("ignoring unknown %s", value);
continue;
}
break;
- case EAP_FAST_TLV_CRYPTO_BINDING:
- if (!binding) {
- binding = talloc_zero(request->packet, eap_tlv_crypto_binding_tlv_t);
- binding->tlv_type = htons(EAP_FAST_TLV_MANDATORY | EAP_FAST_TLV_CRYPTO_BINDING);
- binding->length = htons(sizeof(*binding) - 2 * sizeof(uint16_t));
- }
- /*
- * fr_radius_encode_pair() does not work for structures
- */
- switch (vp->da->attr) {
- case 1: /* PW_EAP_FAST_CRYPTO_BINDING_RESERVED */
- binding->reserved = vp->vp_integer;
- break;
- case 2: /* PW_EAP_FAST_CRYPTO_BINDING_VERSION */
- binding->version = vp->vp_integer;
- break;
- case 3: /* PW_EAP_FAST_CRYPTO_BINDING_RECV_VERSION */
- binding->received_version = vp->vp_integer;
- break;
- case 4: /* PW_EAP_FAST_CRYPTO_BINDING_SUB_TYPE */
- binding->subtype = vp->vp_integer;
- break;
- case 5: /* PW_EAP_FAST_CRYPTO_BINDING_NONCE */
- memcpy(binding->nonce, vp->vp_octets, vp->vp_length);
- break;
- case 6: /* PW_EAP_FAST_CRYPTO_BINDING_COMPOUND_MAC */
- memcpy(binding->compound_mac, vp->vp_octets, vp->vp_length);
- break;
- }
- continue;
- case EAP_FAST_TLV_PAC:
- switch (vp->da->attr) {
+ case PW_EAP_FAST_TLV_PAC:
+ switch ( ( vp->da->attr >> 16 )) {
case PAC_INFO_PAC_ACK:
if (vp->vp_integer == EAP_FAST_TLV_RESULT_SUCCESS) {
code = PW_CODE_ACCESS_ACCEPT;
t->pac.send = true;
continue;
default:
- value = vp_aprints_value(request->packet, vp, '"');
+ value = vp_aprints(request->packet, vp, '"');
RDEBUG2("ignoring unknown EAP-FAST-PAC-TLV %s", value);
talloc_free(value);
continue;
}
break;
default:
- value = vp_aprints_value(request->packet, vp, '"');
- RDEBUG2("ignoring non-EAP-FAST TLV %s", value);
+ value = vp_aprints(request->packet, vp, '"');
+ RDEBUG2("ignoring EAP-FAST TLV %s", value);
talloc_free(value);
continue;
}
/*
* See if the tunneled data is well formed.
*/
- if (!eap_fast_verify(request, tls_session, data, data_len)) return RLM_MODULE_REJECT;
+ if (!eap_fast_verify(request, tls_session, data, data_len)) return PW_CODE_ACCESS_REJECT;
if (t->stage == TLS_SESSION_HANDSHAKE) {
rad_assert(t->mode == EAP_FAST_UNKNOWN);
fr_pair_list_free(&fast_vps);
- if (code == RLM_MODULE_REJECT) return RLM_MODULE_REJECT;
+ if (code == PW_CODE_ACCESS_REJECT) return PW_CODE_ACCESS_REJECT;
switch (t->stage) {
case AUTHENTICATION:
eap_fast_append_result(tls_session, code);
- if (code == RLM_MODULE_REJECT)
- break;
-
if (t->pac.send) {
RDEBUG("Peer requires new PAC");
eap_fast_send_pac_tunnel(request, tls_session);
/*
* RFC 5422 section 3.5 - Network Access after EAP-FAST Provisioning
*/
- if ((t->pac.type && t->pac.expired) || t->mode == EAP_FAST_PROVISIONING_ANON) {
- RDEBUG("Rejecting expired PAC or unauthenticated provisioning");
- code = RLM_MODULE_REJECT;
+ if (t->pac.type && t->pac.expired) {
+ REDEBUG("Rejecting expired PAC.");
+ code = PW_CODE_ACCESS_REJECT;
+ break;
+ }
+
+ if (t->mode == EAP_FAST_PROVISIONING_ANON) {
+ REDEBUG("Rejecting unauthenticated provisioning");
+ code = PW_CODE_ACCESS_REJECT;
break;
}
eap_add_reply(request, "EAP-EMSK", t->emsk, EAP_EMSK_LEN);
break;
+
default:
- RERROR("no idea! %d", t->stage);
- code = RLM_MODULE_REJECT;
+ RERROR("Internal sanity check failed in EAP-FAST at %d", t->stage);
+ code = PW_CODE_ACCESS_REJECT;
}
return code;