static void eap_fast_process_phase2_response(struct eap_sm *sm, struct eap_fast_data *data, u8 *in_data, size_t in_len) { u8 next_type = EAP_TYPE_NONE; struct eap_hdr *hdr; u8 *pos; size_t left; struct wpabuf buf; const struct eap_method *m = data->phase2_method; void *priv = data->phase2_priv; if (priv == NULL) { wpa_printf(MSG_DEBUG, "EAP-FAST: %s - Phase2 not " "initialized?!", __func__); return; } hdr = (struct eap_hdr *) in_data; pos = (u8 *) (hdr + 1); if (in_len > sizeof(*hdr) && *pos == EAP_TYPE_NAK) { left = in_len - sizeof(*hdr); wpa_hexdump(MSG_DEBUG, "EAP-FAST: Phase2 type Nak'ed; " "allowed types", pos + 1, left - 1); #ifdef EAP_SERVER_TNC if (m && m->vendor == EAP_VENDOR_IETF && m->method == EAP_TYPE_TNC) { wpa_printf(MSG_DEBUG, "EAP-FAST: Peer Nak'ed required " "TNC negotiation"); next_type = eap_fast_req_failure(sm, data); eap_fast_phase2_init(sm, data, next_type); return; } #endif /* EAP_SERVER_TNC */ eap_sm_process_nak(sm, pos + 1, left - 1); if (sm->user && sm->user_eap_method_index < EAP_MAX_METHODS && sm->user->methods[sm->user_eap_method_index].method != EAP_TYPE_NONE) { next_type = sm->user->methods[ sm->user_eap_method_index++].method; wpa_printf(MSG_DEBUG, "EAP-FAST: try EAP type %d", next_type); } else { next_type = eap_fast_req_failure(sm, data); } eap_fast_phase2_init(sm, data, next_type); return; } wpabuf_set(&buf, in_data, in_len); if (m->check(sm, priv, &buf)) { wpa_printf(MSG_DEBUG, "EAP-FAST: Phase2 check() asked to " "ignore the packet"); eap_fast_req_failure(sm, data); return; } m->process(sm, priv, &buf); if (!m->isDone(sm, priv)) return; if (!m->isSuccess(sm, priv)) { wpa_printf(MSG_DEBUG, "EAP-FAST: Phase2 method failed"); next_type = eap_fast_req_failure(sm, data); eap_fast_phase2_init(sm, data, next_type); return; } switch (data->state) { case PHASE2_ID: if (eap_user_get(sm, sm->identity, sm->identity_len, 1) != 0) { wpa_hexdump_ascii(MSG_DEBUG, "EAP-FAST: Phase2 " "Identity not found in the user " "database", sm->identity, sm->identity_len); next_type = eap_fast_req_failure(sm, data); break; } eap_fast_state(data, PHASE2_METHOD); if (data->anon_provisioning) { /* * Only EAP-MSCHAPv2 is allowed for anonymous * provisioning. */ next_type = EAP_TYPE_MSCHAPV2; sm->user_eap_method_index = 0; } else { next_type = sm->user->methods[0].method; sm->user_eap_method_index = 1; } wpa_printf(MSG_DEBUG, "EAP-FAST: try EAP type %d", next_type); break; case PHASE2_METHOD: case CRYPTO_BINDING: eap_fast_update_icmk(sm, data); eap_fast_state(data, CRYPTO_BINDING); data->eap_seq++; next_type = EAP_TYPE_NONE; #ifdef EAP_SERVER_TNC if (sm->tnc && !data->tnc_started) { wpa_printf(MSG_DEBUG, "EAP-FAST: Initialize TNC"); next_type = EAP_TYPE_TNC; data->tnc_started = 1; } #endif /* EAP_SERVER_TNC */ break; case FAILURE: break; default: wpa_printf(MSG_DEBUG, "EAP-FAST: %s - unexpected state %d", __func__, data->state); break; } eap_fast_phase2_init(sm, data, next_type); }
/* * Process the inner tunnel data */ FR_CODE eap_fast_process(eap_session_t *eap_session, tls_session_t *tls_session) { FR_CODE code; VALUE_PAIR *fast_vps = NULL; fr_cursor_t cursor; uint8_t const *data; size_t data_len; eap_fast_tunnel_t *t; REQUEST *request = eap_session->request; /* * Just look at the buffer directly, without doing * record_to_buff. */ data_len = tls_session->clean_out.used; tls_session->clean_out.used = 0; data = tls_session->clean_out.data; t = talloc_get_type_abort(tls_session->opaque, eap_fast_tunnel_t); /* * See if the tunneled data is well formed. */ if (!eap_fast_verify(request, tls_session, data, data_len)) return FR_CODE_ACCESS_REJECT; if (t->stage == EAP_FAST_TLS_SESSION_HANDSHAKE) { rad_assert(t->mode == EAP_FAST_UNKNOWN); char buf[256]; if (strstr(SSL_CIPHER_description(SSL_get_current_cipher(tls_session->ssl), buf, sizeof(buf)), "Au=None")) { /* FIXME enforce MSCHAPv2 - RFC 5422 section 3.2.2 */ RDEBUG2("Using anonymous provisioning"); t->mode = EAP_FAST_PROVISIONING_ANON; t->pac.send = true; } else { if (SSL_session_reused(tls_session->ssl)) { RDEBUG2("Session Resumed from PAC"); t->mode = EAP_FAST_NORMAL_AUTH; } else { RDEBUG2("Using authenticated provisioning"); t->mode = EAP_FAST_PROVISIONING_AUTH; } if (!t->pac.expires || t->pac.expired || t->pac.expires - time(NULL) < t->pac_lifetime * 0.6) { t->pac.send = true; } } eap_fast_init_keys(request, tls_session); eap_fast_send_identity_request(request, tls_session, eap_session); t->stage = EAP_FAST_AUTHENTICATION; return FR_CODE_ACCESS_CHALLENGE; } fr_cursor_init(&cursor, &fast_vps); if (eap_fast_decode_pair(request, &cursor, attr_eap_fast_tlv, data, data_len, NULL) < 0) return FR_CODE_ACCESS_REJECT; RDEBUG2("Got Tunneled FAST TLVs"); log_request_pair_list(L_DBG_LVL_1, request, fast_vps, NULL); code = eap_fast_process_tlvs(request, eap_session, tls_session, fast_vps); fr_pair_list_free(&fast_vps); if (code == FR_CODE_ACCESS_REJECT) return FR_CODE_ACCESS_REJECT; switch (t->stage) { case EAP_FAST_AUTHENTICATION: code = FR_CODE_ACCESS_CHALLENGE; break; case EAP_FAST_CRYPTOBIND_CHECK: { if (t->mode != EAP_FAST_PROVISIONING_ANON && !t->pac.send) t->result_final = true; eap_fast_append_result(tls_session, code); eap_fast_update_icmk(request, tls_session, (uint8_t *)&t->isk); eap_fast_append_crypto_binding(request, tls_session); code = FR_CODE_ACCESS_CHALLENGE; break; } case EAP_FAST_PROVISIONING: t->result_final = true; eap_fast_append_result(tls_session, code); if (t->pac.send) { RDEBUG2("Peer requires new PAC"); eap_fast_send_pac_tunnel(request, tls_session); code = FR_CODE_ACCESS_CHALLENGE; break; } t->stage = EAP_FAST_COMPLETE; /* fallthrough */ case EAP_FAST_COMPLETE: /* * RFC 5422 section 3.5 - Network Access after EAP-FAST Provisioning */ if (t->pac.type && t->pac.expired) { REDEBUG("Rejecting expired PAC."); code = FR_CODE_ACCESS_REJECT; break; } if (t->mode == EAP_FAST_PROVISIONING_ANON) { REDEBUG("Rejecting unauthenticated provisioning"); code = FR_CODE_ACCESS_REJECT; break; } /* * eap_crypto_mppe_keys() is unsuitable for EAP-FAST as Cisco decided * it would be a great idea to flip the recv/send keys around */ #define EAPTLS_MPPE_KEY_LEN 32 eap_add_reply(request, attr_ms_mppe_recv_key, t->msk, EAPTLS_MPPE_KEY_LEN); eap_add_reply(request, attr_ms_mppe_send_key, &t->msk[EAPTLS_MPPE_KEY_LEN], EAPTLS_MPPE_KEY_LEN); eap_add_reply(request, attr_eap_msk, t->msk, EAP_FAST_KEY_LEN); eap_add_reply(request, attr_eap_emsk, t->emsk, EAP_EMSK_LEN); break; default: RERROR("Internal sanity check failed in EAP-FAST at %d", t->stage); code = FR_CODE_ACCESS_REJECT; } return code; }