/** * eap_peer_tls_reassemble_fragment - Reassemble a received fragment * @data: Data for TLS processing * @in_data: Next incoming TLS segment * Returns: 0 on success, 1 if more data is needed for the full message, or * -1 on error */ static int eap_peer_tls_reassemble_fragment(struct eap_ssl_data *data, const struct wpabuf *in_data) { size_t tls_in_len, in_len; tls_in_len = data->tls_in ? wpabuf_len(data->tls_in) : 0; in_len = in_data ? wpabuf_len(in_data) : 0; if (tls_in_len + in_len == 0) { /* No message data received?! */ wpa_printf(MSG_WARNING, "SSL: Invalid reassembly state: " "tls_in_left=%lu tls_in_len=%lu in_len=%lu", (unsigned long) data->tls_in_left, (unsigned long) tls_in_len, (unsigned long) in_len); eap_peer_tls_reset_input(data); return -1; } if (tls_in_len + in_len > 65536) { /* * Limit length to avoid rogue servers from causing large * memory allocations. */ wpa_printf(MSG_INFO, "SSL: Too long TLS fragment (size over " "64 kB)"); eap_peer_tls_reset_input(data); return -1; } if (in_len > data->tls_in_left) { /* Sender is doing something odd - reject message */ wpa_printf(MSG_INFO, "SSL: more data than TLS message length " "indicated"); eap_peer_tls_reset_input(data); return -1; } if (wpabuf_resize(&data->tls_in, in_len) < 0) { wpa_printf(MSG_INFO, "SSL: Could not allocate memory for TLS " "data"); eap_peer_tls_reset_input(data); return -1; } if (in_data) wpabuf_put_buf(data->tls_in, in_data); data->tls_in_left -= in_len; if (data->tls_in_left > 0) { wpa_printf(MSG_DEBUG, "SSL: Need %lu bytes more input " "data", (unsigned long) data->tls_in_left); return 1; } return 0; }
/** * eap_peer_tls_decrypt - Decrypt received phase 2 TLS message * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() * @data: Data for TLS processing * @in_data: Message received from the server * @in_decrypted: Buffer for returning a pointer to the decrypted message * Returns: 0 on success, 1 if more input data is needed, or -1 on failure */ int eap_peer_tls_decrypt(struct eap_sm *sm, struct eap_ssl_data *data, const struct wpabuf *in_data, struct wpabuf **in_decrypted) { int res; const u8 *msg; size_t msg_len, buf_len; int need_more_input; msg = eap_peer_tls_data_reassemble(data, wpabuf_head(in_data), wpabuf_len(in_data), &msg_len, &need_more_input); if (msg == NULL) return need_more_input ? 1 : -1; buf_len = wpabuf_len(in_data); if (data->tls_in_total > buf_len) buf_len = data->tls_in_total; /* * 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 = wpabuf_alloc(buf_len ? buf_len : 1); if (*in_decrypted == NULL) { eap_peer_tls_reset_input(data); wpa_printf(MSG_WARNING, "SSL: Failed to allocate memory for " "decryption"); return -1; } res = tls_connection_decrypt(sm->ssl_ctx, data->conn, msg, msg_len, wpabuf_mhead(*in_decrypted), buf_len); eap_peer_tls_reset_input(data); if (res < 0) { wpa_printf(MSG_INFO, "SSL: Failed to decrypt Phase 2 data"); return -1; } wpabuf_put(*in_decrypted, res); return 0; }
static int eap_fast_decrypt(struct eap_sm *sm, struct eap_fast_data *data, struct eap_method_ret *ret, const struct eap_hdr *req, const struct wpabuf *in_data, struct wpabuf **out_data) { struct wpabuf *in_decrypted; int res; wpa_printf(MSG_DEBUG, "EAP-FAST: Received %lu bytes encrypted data for" " Phase 2", (unsigned long) wpabuf_len(in_data)); if (data->pending_phase2_req) { wpa_printf(MSG_DEBUG, "EAP-FAST: Pending Phase 2 request - " "skip decryption and use old data"); /* Clear TLS reassembly state. */ eap_peer_tls_reset_input(&data->ssl); in_decrypted = data->pending_phase2_req; data->pending_phase2_req = NULL; goto continue_req; } if (wpabuf_len(in_data) == 0) { /* Received TLS ACK - requesting more fragments */ return eap_peer_tls_encrypt(sm, &data->ssl, EAP_TYPE_FAST, data->fast_version, req->identifier, NULL, out_data); } res = eap_peer_tls_decrypt(sm, &data->ssl, in_data, &in_decrypted); if (res) return res; continue_req: wpa_hexdump_buf(MSG_MSGDUMP, "EAP-FAST: Decrypted Phase 2 TLV(s)", in_decrypted); if (wpabuf_len(in_decrypted) < 4) { wpa_printf(MSG_INFO, "EAP-FAST: Too short Phase 2 " "TLV frame (len=%lu)", (unsigned long) wpabuf_len(in_decrypted)); wpabuf_free(in_decrypted); return -1; } res = eap_fast_process_decrypted(sm, data, ret, req, in_decrypted, out_data); wpabuf_free(in_decrypted); return res; }
/** * eap_tls_process_input - Process incoming TLS message * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() * @data: Data for TLS processing * @in_data: Message received from the server * @in_len: Length of in_data * @out_data: Buffer for returning a pointer to application data (if available) * Returns: 0 on success, 1 if more input data is needed, 2 if application data * is available, -1 on failure */ static int eap_tls_process_input(struct eap_sm *sm, struct eap_ssl_data *data, const u8 *in_data, size_t in_len, struct wpabuf **out_data) { const u8 *msg; size_t msg_len; int need_more_input; u8 *appl_data; size_t appl_data_len; msg = eap_peer_tls_data_reassemble(data, in_data, in_len, &msg_len, &need_more_input); if (msg == NULL) return need_more_input ? 1 : -1; /* Full TLS message reassembled - continue handshake processing */ if (data->tls_out) { /* This should not happen.. */ wpa_printf(MSG_INFO, "SSL: eap_tls_process_input - pending " "tls_out data even though tls_out_len = 0"); os_free(data->tls_out); WPA_ASSERT(data->tls_out == NULL); } appl_data = NULL; data->tls_out = tls_connection_handshake(sm->ssl_ctx, data->conn, msg, msg_len, &data->tls_out_len, &appl_data, &appl_data_len); eap_peer_tls_reset_input(data); if (appl_data && tls_connection_established(sm->ssl_ctx, data->conn) && !tls_connection_get_failed(sm->ssl_ctx, data->conn)) { wpa_hexdump_key(MSG_MSGDUMP, "SSL: Application data", appl_data, appl_data_len); *out_data = wpabuf_alloc_ext_data(appl_data, appl_data_len); if (*out_data == NULL) { os_free(appl_data); return -1; } return 2; } os_free(appl_data); return 0; }
/** * eap_peer_tls_decrypt - Decrypt received phase 2 TLS message * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() * @data: Data for TLS processing * @in_data: Message received from the server * @in_decrypted: Buffer for returning a pointer to the decrypted message * Returns: 0 on success, 1 if more input data is needed, or -1 on failure */ int eap_peer_tls_decrypt(struct eap_sm *sm, struct eap_ssl_data *data, const struct wpabuf *in_data, struct wpabuf **in_decrypted) { const struct wpabuf *msg; int need_more_input; msg = eap_peer_tls_data_reassemble(data, in_data, &need_more_input); if (msg == NULL) return need_more_input ? 1 : -1; *in_decrypted = tls_connection_decrypt(data->ssl_ctx, data->conn, msg); eap_peer_tls_reset_input(data); if (*in_decrypted == NULL) { wpa_printf(MSG_INFO, "SSL: Failed to decrypt Phase 2 data"); return -1; } return 0; }
static int eap_peap_decrypt(struct eap_sm *sm, struct eap_peap_data *data, struct eap_method_ret *ret, const struct eap_hdr *req, const struct wpabuf *in_data, struct wpabuf **out_data) { struct wpabuf *in_decrypted = NULL; int res, skip_change = 0; struct eap_hdr *hdr, *rhdr; struct wpabuf *resp = NULL; size_t len; wpa_printf(MSG_DEBUG, "EAP-PEAP: received %lu bytes encrypted data for" " Phase 2", (unsigned long) wpabuf_len(in_data)); if (data->pending_phase2_req) { wpa_printf(MSG_DEBUG, "EAP-PEAP: Pending Phase 2 request - " "skip decryption and use old data"); /* Clear TLS reassembly state. */ eap_peer_tls_reset_input(&data->ssl); in_decrypted = data->pending_phase2_req; data->pending_phase2_req = NULL; skip_change = 1; goto continue_req; } if (wpabuf_len(in_data) == 0 && sm->workaround && data->phase2_success) { /* * Cisco ACS seems to be using TLS ACK to terminate * EAP-PEAPv0/GTC. Try to reply with TLS ACK. */ wpa_printf(MSG_DEBUG, "EAP-PEAP: Received TLS ACK, but " "expected data - acknowledge with TLS ACK since " "Phase 2 has been completed"); ret->decision = DECISION_COND_SUCC; ret->methodState = METHOD_DONE; return 1; } else if (wpabuf_len(in_data) == 0) { /* Received TLS ACK - requesting more fragments */ return eap_peer_tls_encrypt(sm, &data->ssl, EAP_TYPE_PEAP, data->peap_version, req->identifier, NULL, out_data); } res = eap_peer_tls_decrypt(sm, &data->ssl, in_data, &in_decrypted); if (res) return res; continue_req: wpa_hexdump_buf(MSG_DEBUG, "EAP-PEAP: Decrypted Phase 2 EAP", in_decrypted); hdr = wpabuf_mhead(in_decrypted); if (wpabuf_len(in_decrypted) == 5 && hdr->code == EAP_CODE_REQUEST && be_to_host16(hdr->length) == 5 && eap_get_type(in_decrypted) == EAP_TYPE_IDENTITY) { /* At least FreeRADIUS seems to send full EAP header with * EAP Request Identity */ skip_change = 1; } if (wpabuf_len(in_decrypted) >= 5 && hdr->code == EAP_CODE_REQUEST && eap_get_type(in_decrypted) == EAP_TYPE_TLV) { skip_change = 1; } if (data->peap_version == 0 && !skip_change) { struct eap_hdr *nhdr; struct wpabuf *nmsg = wpabuf_alloc(sizeof(struct eap_hdr) + wpabuf_len(in_decrypted)); if (nmsg == NULL) { wpabuf_free(in_decrypted); return 0; } nhdr = wpabuf_put(nmsg, sizeof(*nhdr)); wpabuf_put_buf(nmsg, in_decrypted); nhdr->code = req->code; nhdr->identifier = req->identifier; nhdr->length = host_to_be16(sizeof(struct eap_hdr) + wpabuf_len(in_decrypted)); wpabuf_free(in_decrypted); in_decrypted = nmsg; } if (data->peap_version >= 2) { struct eap_tlv_hdr *tlv; struct wpabuf *nmsg; if (wpabuf_len(in_decrypted) < sizeof(*tlv) + sizeof(*hdr)) { wpa_printf(MSG_INFO, "EAP-PEAPv2: Too short Phase 2 " "EAP TLV"); wpabuf_free(in_decrypted); return 0; } tlv = wpabuf_mhead(in_decrypted); if ((be_to_host16(tlv->tlv_type) & 0x3fff) != EAP_TLV_EAP_PAYLOAD_TLV) { wpa_printf(MSG_INFO, "EAP-PEAPv2: Not an EAP TLV"); wpabuf_free(in_decrypted); return 0; } if (sizeof(*tlv) + be_to_host16(tlv->length) > wpabuf_len(in_decrypted)) { wpa_printf(MSG_INFO, "EAP-PEAPv2: Invalid EAP TLV " "length"); wpabuf_free(in_decrypted); return 0; } hdr = (struct eap_hdr *) (tlv + 1); if (be_to_host16(hdr->length) > be_to_host16(tlv->length)) { wpa_printf(MSG_INFO, "EAP-PEAPv2: No room for full " "EAP packet in EAP TLV"); wpabuf_free(in_decrypted); return 0; } nmsg = wpabuf_alloc(be_to_host16(hdr->length)); if (nmsg == NULL) { wpabuf_free(in_decrypted); return 0; } wpabuf_put_data(nmsg, hdr, be_to_host16(hdr->length)); wpabuf_free(in_decrypted); in_decrypted = nmsg; } hdr = wpabuf_mhead(in_decrypted); if (wpabuf_len(in_decrypted) < sizeof(*hdr)) { wpa_printf(MSG_INFO, "EAP-PEAP: Too short Phase 2 " "EAP frame (len=%lu)", (unsigned long) wpabuf_len(in_decrypted)); wpabuf_free(in_decrypted); return 0; } len = be_to_host16(hdr->length); if (len > wpabuf_len(in_decrypted)) { wpa_printf(MSG_INFO, "EAP-PEAP: Length mismatch in " "Phase 2 EAP frame (len=%lu hdr->length=%lu)", (unsigned long) wpabuf_len(in_decrypted), (unsigned long) len); wpabuf_free(in_decrypted); return 0; } if (len < wpabuf_len(in_decrypted)) { wpa_printf(MSG_INFO, "EAP-PEAP: Odd.. Phase 2 EAP header has " "shorter length than full decrypted data " "(%lu < %lu)", (unsigned long) len, (unsigned long) wpabuf_len(in_decrypted)); } wpa_printf(MSG_DEBUG, "EAP-PEAP: received Phase 2: code=%d " "identifier=%d length=%lu", hdr->code, hdr->identifier, (unsigned long) len); switch (hdr->code) { case EAP_CODE_REQUEST: if (eap_peap_phase2_request(sm, data, ret, in_decrypted, &resp)) { wpabuf_free(in_decrypted); wpa_printf(MSG_INFO, "EAP-PEAP: Phase2 Request " "processing failed"); return 0; } break; case EAP_CODE_SUCCESS: wpa_printf(MSG_DEBUG, "EAP-PEAP: Phase 2 Success"); if (data->peap_version == 1) { /* EAP-Success within TLS tunnel is used to indicate * shutdown of the TLS channel. The authentication has * been completed. */ if (data->phase2_eap_started && !data->phase2_eap_success) { wpa_printf(MSG_DEBUG, "EAP-PEAP: Phase 2 " "Success used to indicate success, " "but Phase 2 EAP was not yet " "completed successfully"); ret->methodState = METHOD_DONE; ret->decision = DECISION_FAIL; wpabuf_free(in_decrypted); return 0; } wpa_printf(MSG_DEBUG, "EAP-PEAP: Version 1 - " "EAP-Success within TLS tunnel - " "authentication completed"); ret->decision = DECISION_UNCOND_SUCC; ret->methodState = METHOD_DONE; data->phase2_success = 1; if (data->peap_outer_success == 2) { wpabuf_free(in_decrypted); wpa_printf(MSG_DEBUG, "EAP-PEAP: Use TLS ACK " "to finish authentication"); return 1; } else if (data->peap_outer_success == 1) { /* Reply with EAP-Success within the TLS * channel to complete the authentication. */ resp = wpabuf_alloc(sizeof(struct eap_hdr)); if (resp) { rhdr = wpabuf_put(resp, sizeof(*rhdr)); rhdr->code = EAP_CODE_SUCCESS; rhdr->identifier = hdr->identifier; rhdr->length = host_to_be16(sizeof(*rhdr)); } } else { /* No EAP-Success expected for Phase 1 (outer, * unencrypted auth), so force EAP state * machine to SUCCESS state. */ sm->peap_done = TRUE; } } else { /* FIX: ? */ } break; case EAP_CODE_FAILURE: wpa_printf(MSG_DEBUG, "EAP-PEAP: Phase 2 Failure"); ret->decision = DECISION_FAIL; ret->methodState = METHOD_MAY_CONT; ret->allowNotifications = FALSE; /* Reply with EAP-Failure within the TLS channel to complete * failure reporting. */ resp = wpabuf_alloc(sizeof(struct eap_hdr)); if (resp) { rhdr = wpabuf_put(resp, sizeof(*rhdr)); rhdr->code = EAP_CODE_FAILURE; rhdr->identifier = hdr->identifier; rhdr->length = host_to_be16(sizeof(*rhdr)); } break; default: wpa_printf(MSG_INFO, "EAP-PEAP: Unexpected code=%d in " "Phase 2 EAP header", hdr->code); break; } wpabuf_free(in_decrypted); if (resp) { int skip_change2 = 0; struct wpabuf *rmsg, buf; wpa_hexdump_buf_key(MSG_DEBUG, "EAP-PEAP: Encrypting Phase 2 data", resp); /* PEAP version changes */ if (data->peap_version >= 2) { resp = eap_peapv2_tlv_eap_payload(resp); if (resp == NULL) return -1; } if (wpabuf_len(resp) >= 5 && wpabuf_head_u8(resp)[0] == EAP_CODE_RESPONSE && eap_get_type(resp) == EAP_TYPE_TLV) skip_change2 = 1; rmsg = resp; if (data->peap_version == 0 && !skip_change2) { wpabuf_set(&buf, wpabuf_head_u8(resp) + sizeof(struct eap_hdr), wpabuf_len(resp) - sizeof(struct eap_hdr)); rmsg = &buf; } if (eap_peer_tls_encrypt(sm, &data->ssl, EAP_TYPE_PEAP, data->peap_version, req->identifier, rmsg, out_data)) { wpa_printf(MSG_INFO, "EAP-PEAP: Failed to encrypt " "a Phase 2 frame"); } wpabuf_free(resp); } return 0; }
/** * eap_peer_tls_reauth_init - Re-initialize shared TLS for session resumption * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() * @data: Data for TLS processing * Returns: 0 on success, -1 on failure */ int eap_peer_tls_reauth_init(struct eap_sm *sm, struct eap_ssl_data *data) { eap_peer_tls_reset_input(data); eap_peer_tls_reset_output(data); return tls_connection_shutdown(data->ssl_ctx, data->conn); }
/** * eap_peer_tls_ssl_deinit - Deinitialize shared TLS functionality * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() * @data: Data for TLS processing * * This function deinitializes shared TLS functionality that was initialized * with eap_peer_tls_ssl_init(). */ void eap_peer_tls_ssl_deinit(struct eap_sm *sm, struct eap_ssl_data *data) { tls_connection_deinit(data->ssl_ctx, data->conn); eap_peer_tls_reset_input(data); eap_peer_tls_reset_output(data); }