static int eap_fast_process_phase2_start(struct eap_sm *sm, struct eap_fast_data *data) { u8 next_type; if (data->identity) { os_free(sm->identity); sm->identity = data->identity; data->identity = NULL; sm->identity_len = data->identity_len; data->identity_len = 0; sm->require_identity_match = 1; 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); } else { wpa_printf(MSG_DEBUG, "EAP-FAST: Identity already " "known - skip Phase 2 Identity Request"); next_type = sm->user->methods[0].method; sm->user_eap_method_index = 1; } eap_fast_state(data, PHASE2_METHOD); } else { eap_fast_state(data, PHASE2_ID); next_type = EAP_TYPE_IDENTITY; } return eap_fast_phase2_init(sm, data, next_type); }
static int eap_fast_phase1_done(struct eap_sm *sm, struct eap_fast_data *data) { char cipher[64]; wpa_printf(MSG_DEBUG, "EAP-FAST: Phase1 done, starting Phase2"); if (tls_get_cipher(sm->ssl_ctx, data->ssl.conn, cipher, sizeof(cipher)) < 0) { wpa_printf(MSG_DEBUG, "EAP-FAST: Failed to get cipher " "information"); eap_fast_state(data, FAILURE); return -1; } data->anon_provisioning = os_strstr(cipher, "ADH") != NULL; if (data->anon_provisioning) { wpa_printf(MSG_DEBUG, "EAP-FAST: Anonymous provisioning"); eap_fast_derive_key_provisioning(sm, data); } else eap_fast_derive_key_auth(sm, data); eap_fast_state(data, PHASE2_START); return 0; }
static EapType eap_fast_req_failure(struct eap_sm *sm, struct eap_fast_data *data) { /* TODO: send Result TLV(FAILURE) */ eap_fast_state(data, FAILURE); return EAP_TYPE_NONE; }
static void eap_fast_process(struct eap_sm *sm, void *priv, struct wpabuf *respData) { struct eap_fast_data *data = priv; if (eap_server_tls_process(sm, &data->ssl, respData, data, EAP_TYPE_FAST, eap_fast_process_version, eap_fast_process_msg) < 0) eap_fast_state(data, FAILURE); }
static struct wpabuf * eap_fast_build_start(struct eap_sm *sm, struct eap_fast_data *data, u8 id) { struct wpabuf *req; req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_FAST, 1 + sizeof(struct pac_tlv_hdr) + data->srv_id_len, EAP_CODE_REQUEST, id); if (req == NULL) { wpa_printf(MSG_ERROR, "EAP-FAST: Failed to allocate memory for" " request"); eap_fast_state(data, FAILURE); return NULL; } wpabuf_put_u8(req, EAP_TLS_FLAGS_START | data->fast_version); /* RFC 4851, 4.1.1. Authority ID Data */ eap_fast_put_tlv(req, PAC_TYPE_A_ID, data->srv_id, data->srv_id_len); eap_fast_state(data, PHASE1); return req; }
static void eap_fast_process_phase2(struct eap_sm *sm, struct eap_fast_data *data, struct wpabuf *in_buf) { struct wpabuf *in_decrypted; wpa_printf(MSG_DEBUG, "EAP-FAST: Received %lu bytes encrypted data for" " Phase 2", (unsigned long) wpabuf_len(in_buf)); if (data->pending_phase2_resp) { wpa_printf(MSG_DEBUG, "EAP-PEAP: Pending Phase 2 response - " "skip decryption and use old data"); eap_fast_process_phase2_tlvs(sm, data, data->pending_phase2_resp); wpabuf_free(data->pending_phase2_resp); data->pending_phase2_resp = NULL; return; } in_decrypted = tls_connection_decrypt(sm->ssl_ctx, data->ssl.conn, in_buf); if (in_decrypted == NULL) { wpa_printf(MSG_INFO, "EAP-FAST: Failed to decrypt Phase 2 " "data"); eap_fast_state(data, FAILURE); return; } wpa_hexdump_buf_key(MSG_DEBUG, "EAP-FAST: Decrypted Phase 2 TLVs", in_decrypted); eap_fast_process_phase2_tlvs(sm, data, in_decrypted); if (sm->method_pending == METHOD_PENDING_WAIT) { wpa_printf(MSG_DEBUG, "EAP-FAST: Phase2 method is in " "pending wait state - save decrypted response"); wpabuf_free(data->pending_phase2_resp); data->pending_phase2_resp = in_decrypted; return; } wpabuf_free(in_decrypted); }
static int eap_fast_process_phase1(struct eap_sm *sm, struct eap_fast_data *data) { if (eap_server_tls_phase1(sm, &data->ssl) < 0) { wpa_printf(MSG_INFO, "EAP-FAST: TLS processing failed"); eap_fast_state(data, FAILURE); return -1; } if (!tls_connection_established(sm->ssl_ctx, data->ssl.conn) || wpabuf_len(data->ssl.tls_out) > 0) return 1; /* * Phase 1 was completed with the received message (e.g., when using * abbreviated handshake), so Phase 2 can be started immediately * without having to send through an empty message to the peer. */ return eap_fast_phase1_done(sm, data); }
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); }
static struct wpabuf * eap_fast_buildReq(struct eap_sm *sm, void *priv, u8 id) { struct eap_fast_data *data = priv; struct wpabuf *req = NULL; int piggyback = 0; if (data->ssl.state == FRAG_ACK) { return eap_server_tls_build_ack(id, EAP_TYPE_FAST, data->fast_version); } if (data->ssl.state == WAIT_FRAG_ACK) { return eap_server_tls_build_msg(&data->ssl, EAP_TYPE_FAST, data->fast_version, id); } switch (data->state) { case START: return eap_fast_build_start(sm, data, id); case PHASE1: if (tls_connection_established(sm->ssl_ctx, data->ssl.conn)) { if (eap_fast_phase1_done(sm, data) < 0) return NULL; if (data->state == PHASE2_START) { /* * Try to generate Phase 2 data to piggyback * with the end of Phase 1 to avoid extra * roundtrip. */ wpa_printf(MSG_DEBUG, "EAP-FAST: Try to start " "Phase 2"); if (eap_fast_process_phase2_start(sm, data)) break; req = eap_fast_build_phase2_req(sm, data, id); piggyback = 1; } } break; case PHASE2_ID: case PHASE2_METHOD: req = eap_fast_build_phase2_req(sm, data, id); break; case CRYPTO_BINDING: req = eap_fast_build_crypto_binding(sm, data); if (data->phase2_method) { /* * Include the start of the next EAP method in the * sequence in the same message with Crypto-Binding to * save a round-trip. */ struct wpabuf *eap; eap = eap_fast_build_phase2_req(sm, data, id); req = wpabuf_concat(req, eap); eap_fast_state(data, PHASE2_METHOD); } break; case REQUEST_PAC: req = eap_fast_build_pac(sm, data); break; default: wpa_printf(MSG_DEBUG, "EAP-FAST: %s - unexpected state %d", __func__, data->state); return NULL; } if (req && eap_fast_encrypt_phase2(sm, data, req, piggyback) < 0) return NULL; return eap_server_tls_build_msg(&data->ssl, EAP_TYPE_FAST, data->fast_version, id); }
static void eap_fast_process_phase2_tlvs(struct eap_sm *sm, struct eap_fast_data *data, struct wpabuf *in_data) { struct eap_fast_tlv_parse tlv; int check_crypto_binding = data->state == CRYPTO_BINDING; if (eap_fast_parse_tlvs(in_data, &tlv) < 0) { wpa_printf(MSG_DEBUG, "EAP-FAST: Failed to parse received " "Phase 2 TLVs"); return; } if (tlv.result == EAP_TLV_RESULT_FAILURE) { wpa_printf(MSG_DEBUG, "EAP-FAST: Result TLV indicated " "failure"); eap_fast_state(data, FAILURE); return; } if (data->state == REQUEST_PAC) { u16 type, len, res; if (tlv.pac == NULL || tlv.pac_len < 6) { wpa_printf(MSG_DEBUG, "EAP-FAST: No PAC " "Acknowledgement received"); eap_fast_state(data, FAILURE); return; } type = WPA_GET_BE16(tlv.pac); len = WPA_GET_BE16(tlv.pac + 2); res = WPA_GET_BE16(tlv.pac + 4); if (type != PAC_TYPE_PAC_ACKNOWLEDGEMENT || len != 2 || res != EAP_TLV_RESULT_SUCCESS) { wpa_printf(MSG_DEBUG, "EAP-FAST: PAC TLV did not " "contain acknowledgement"); eap_fast_state(data, FAILURE); return; } wpa_printf(MSG_DEBUG, "EAP-FAST: PAC-Acknowledgement received " "- PAC provisioning succeeded"); eap_fast_state(data, (data->anon_provisioning || data->send_new_pac == 2) ? FAILURE : SUCCESS); return; } if (check_crypto_binding) { if (tlv.crypto_binding == NULL) { wpa_printf(MSG_DEBUG, "EAP-FAST: No Crypto-Binding " "TLV received"); eap_fast_state(data, FAILURE); return; } if (data->final_result && tlv.result != EAP_TLV_RESULT_SUCCESS) { wpa_printf(MSG_DEBUG, "EAP-FAST: Crypto-Binding TLV " "without Success Result"); eap_fast_state(data, FAILURE); return; } if (!data->final_result && tlv.iresult != EAP_TLV_RESULT_SUCCESS) { wpa_printf(MSG_DEBUG, "EAP-FAST: Crypto-Binding TLV " "without intermediate Success Result"); eap_fast_state(data, FAILURE); return; } if (eap_fast_validate_crypto_binding(data, tlv.crypto_binding, tlv.crypto_binding_len)) { eap_fast_state(data, FAILURE); return; } wpa_printf(MSG_DEBUG, "EAP-FAST: Valid Crypto-Binding TLV " "received"); if (data->final_result) { wpa_printf(MSG_DEBUG, "EAP-FAST: Authentication " "completed successfully"); } if (data->anon_provisioning && sm->eap_fast_prov != ANON_PROV && sm->eap_fast_prov != BOTH_PROV) { wpa_printf(MSG_DEBUG, "EAP-FAST: Client is trying to " "use unauthenticated provisioning which is " "disabled"); eap_fast_state(data, FAILURE); return; } if (sm->eap_fast_prov != AUTH_PROV && sm->eap_fast_prov != BOTH_PROV && tlv.request_action == EAP_TLV_ACTION_PROCESS_TLV && eap_fast_pac_type(tlv.pac, tlv.pac_len, PAC_TYPE_TUNNEL_PAC)) { wpa_printf(MSG_DEBUG, "EAP-FAST: Client is trying to " "use authenticated provisioning which is " "disabled"); eap_fast_state(data, FAILURE); return; } if (data->anon_provisioning || (tlv.request_action == EAP_TLV_ACTION_PROCESS_TLV && eap_fast_pac_type(tlv.pac, tlv.pac_len, PAC_TYPE_TUNNEL_PAC))) { wpa_printf(MSG_DEBUG, "EAP-FAST: Requested a new " "Tunnel PAC"); eap_fast_state(data, REQUEST_PAC); } else if (data->send_new_pac) { wpa_printf(MSG_DEBUG, "EAP-FAST: Server triggered " "re-keying of Tunnel PAC"); eap_fast_state(data, REQUEST_PAC); } else if (data->final_result) eap_fast_state(data, SUCCESS); } if (tlv.eap_payload_tlv) { eap_fast_process_phase2_eap(sm, data, tlv.eap_payload_tlv, tlv.eap_payload_tlv_len); } }
static void eap_fast_process_phase2(struct eap_sm *sm, struct eap_fast_data *data, struct wpabuf *in_buf) { u8 *in_decrypted; int len_decrypted; size_t buf_len; u8 *in_data; size_t in_len; in_data = wpabuf_mhead(in_buf); in_len = wpabuf_len(in_buf); wpa_printf(MSG_DEBUG, "EAP-FAST: Received %lu bytes encrypted data for" " Phase 2", (unsigned long) in_len); if (data->pending_phase2_resp) { wpa_printf(MSG_DEBUG, "EAP-PEAP: Pending Phase 2 response - " "skip decryption and use old data"); eap_fast_process_phase2_tlvs( sm, data, wpabuf_mhead(data->pending_phase2_resp), wpabuf_len(data->pending_phase2_resp)); wpabuf_free(data->pending_phase2_resp); data->pending_phase2_resp = NULL; return; } buf_len = in_len; /* * Even though we try to disable TLS compression, it is possible that * this cannot be done with all TLS libraries. Add extra buffer space * to handle the possibility of the decrypted data being longer than * input data. */ buf_len += 500; buf_len *= 3; in_decrypted = os_malloc(buf_len); if (in_decrypted == NULL) { wpa_printf(MSG_WARNING, "EAP-FAST: Failed to allocate memory " "for decryption"); return; } len_decrypted = tls_connection_decrypt(sm->ssl_ctx, data->ssl.conn, in_data, in_len, in_decrypted, buf_len); if (len_decrypted < 0) { wpa_printf(MSG_INFO, "EAP-FAST: Failed to decrypt Phase 2 " "data"); os_free(in_decrypted); eap_fast_state(data, FAILURE); return; } wpa_hexdump_key(MSG_DEBUG, "EAP-FAST: Decrypted Phase 2 TLVs", in_decrypted, len_decrypted); eap_fast_process_phase2_tlvs(sm, data, in_decrypted, len_decrypted); if (sm->method_pending == METHOD_PENDING_WAIT) { wpa_printf(MSG_DEBUG, "EAP-FAST: Phase2 method is in " "pending wait state - save decrypted response"); wpabuf_free(data->pending_phase2_resp); data->pending_phase2_resp = wpabuf_alloc_copy(in_decrypted, len_decrypted); } os_free(in_decrypted); }