From 34f564dbd5168626da55a7119b04832e98793160 Mon Sep 17 00:00:00 2001 From: Jouni Malinen Date: Wed, 28 May 2008 09:57:17 +0300 Subject: [PATCH] Redesigned EAP-TLS/PEAP/TTLS/FAST fragmentation/reassembly Fragmentation is now done as a separate step to clean up the design and to allow the same code to be used in both Phase 1 and Phase 2. This adds support for fragmenting EAP-PEAP/TTLS/FAST Phase 2 (tunneled) data. --- hostapd/ChangeLog | 2 + src/eap_server/eap_fast.c | 175 ++++---------- src/eap_server/eap_peap.c | 180 +++++---------- src/eap_server/eap_tls.c | 76 +++--- src/eap_server/eap_tls_common.c | 398 ++++++++++++++++++-------------- src/eap_server/eap_tls_common.h | 34 +-- src/eap_server/eap_ttls.c | 211 +++++++---------- 7 files changed, 469 insertions(+), 607 deletions(-) diff --git a/hostapd/ChangeLog b/hostapd/ChangeLog index 56673de7f..5146d1a09 100644 --- a/hostapd/ChangeLog +++ b/hostapd/ChangeLog @@ -10,6 +10,8 @@ ChangeLog for hostapd and IF-TNCCS interfaces from TNCS) * added support for optional cryptobinding with PEAPv0 * added fragmentation support for EAP-TNC + * added support for fragmenting EAP-TTLS/PEAP/FAST Phase 2 (tunneled) + data 2008-02-22 - v0.6.3 * fixed Reassociation Response callback processing when using internal diff --git a/src/eap_server/eap_fast.c b/src/eap_server/eap_fast.c index c50ffd2c0..6bcfabc66 100644 --- a/src/eap_server/eap_fast.c +++ b/src/eap_server/eap_fast.c @@ -533,63 +533,6 @@ static int eap_fast_phase1_done(struct eap_sm *sm, struct eap_fast_data *data) } -static struct wpabuf * eap_fast_build_req(struct eap_sm *sm, - struct eap_fast_data *data, u8 id) -{ - int res; - struct wpabuf *req; - - res = eap_server_tls_buildReq_helper(sm, &data->ssl, EAP_TYPE_FAST, - data->fast_version, id, &req); - - if (tls_connection_established(sm->ssl_ctx, data->ssl.conn)) { - if (eap_fast_phase1_done(sm, data) < 0) { - os_free(req); - return NULL; - } - } - - if (res == 1) - return eap_server_tls_build_ack(id, EAP_TYPE_FAST, - data->fast_version); - return req; -} - - -static struct wpabuf * eap_fast_encrypt(struct eap_sm *sm, - struct eap_fast_data *data, - u8 id, u8 *plain, size_t plain_len) -{ - int res; - struct wpabuf *buf; - - /* TODO: add support for fragmentation, if needed. This will need to - * add TLS Message Length field, if the frame is fragmented. */ - buf = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_FAST, - 1 + data->ssl.tls_out_limit, - EAP_CODE_REQUEST, id); - if (buf == NULL) - return NULL; - - wpabuf_put_u8(buf, data->fast_version); - - res = tls_connection_encrypt(sm->ssl_ctx, data->ssl.conn, - plain, plain_len, wpabuf_put(buf, 0), - data->ssl.tls_out_limit); - if (res < 0) { - wpa_printf(MSG_INFO, "EAP-FAST: Failed to encrypt Phase 2 " - "data"); - wpabuf_free(buf); - return NULL; - } - - wpabuf_put(buf, res); - eap_update_len(buf); - - return buf; -} - - static struct wpabuf * eap_fast_build_phase2_req(struct eap_sm *sm, struct eap_fast_data *data, u8 id) @@ -827,16 +770,28 @@ static struct wpabuf * eap_fast_build_pac(struct eap_sm *sm, static struct wpabuf * eap_fast_buildReq(struct eap_sm *sm, void *priv, u8 id) { struct eap_fast_data *data = priv; - struct wpabuf *req; + struct wpabuf *req = NULL; struct wpabuf *encr; - if (data->state == START) - return eap_fast_build_start(sm, data, id); + if (data->ssl.state == FRAG_ACK) { + return eap_server_tls_build_ack(id, EAP_TYPE_FAST, + data->fast_version); + } - if (data->state == PHASE1) - return eap_fast_build_req(sm, data, id); + 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; + } + break; case PHASE2_ID: case PHASE2_METHOD: req = eap_fast_build_phase2_req(sm, data, id); @@ -864,16 +819,21 @@ static struct wpabuf * eap_fast_buildReq(struct eap_sm *sm, void *priv, u8 id) return NULL; } - if (req == NULL) - return NULL; + if (req) { + wpa_hexdump_buf_key(MSG_DEBUG, "EAP-FAST: Encrypting Phase 2 " + "TLVs", req); + encr = eap_server_tls_encrypt(sm, &data->ssl, + wpabuf_mhead(req), + wpabuf_len(req)); + wpabuf_free(req); - wpa_hexdump_buf_key(MSG_DEBUG, "EAP-FAST: Encrypting Phase 2 TLVs", - req); - encr = eap_fast_encrypt(sm, data, id, wpabuf_mhead(req), - wpabuf_len(req)); - wpabuf_free(req); + wpabuf_free(data->ssl.out_buf); + data->ssl.out_used = 0; + data->ssl.out_buf = encr; + } - return encr; + return eap_server_tls_build_msg(&data->ssl, EAP_TYPE_FAST, + data->fast_version, id); } @@ -1305,11 +1265,16 @@ static void eap_fast_process_phase2_tlvs(struct eap_sm *sm, static void eap_fast_process_phase2(struct eap_sm *sm, struct eap_fast_data *data, - const u8 *in_data, size_t in_len) + struct wpabuf *in_buf) { u8 *in_decrypted; - int len_decrypted, res; + 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); @@ -1325,15 +1290,7 @@ static void eap_fast_process_phase2(struct eap_sm *sm, return; } - /* FIX: get rid of const -> non-const typecast */ - res = eap_server_tls_data_reassemble(sm, &data->ssl, (u8 **) &in_data, - &in_len); - if (res < 0 || res == 1) - return; - buf_len = in_len; - if (data->ssl.tls_in_total > buf_len) - buf_len = data->ssl.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 @@ -1344,9 +1301,6 @@ static void eap_fast_process_phase2(struct eap_sm *sm, buf_len *= 3; in_decrypted = os_malloc(buf_len); if (in_decrypted == NULL) { - os_free(data->ssl.tls_in); - data->ssl.tls_in = NULL; - data->ssl.tls_in_len = 0; wpa_printf(MSG_WARNING, "EAP-FAST: Failed to allocate memory " "for decryption"); return; @@ -1355,9 +1309,6 @@ static void eap_fast_process_phase2(struct eap_sm *sm, len_decrypted = tls_connection_decrypt(sm->ssl_ctx, data->ssl.conn, in_data, in_len, in_decrypted, buf_len); - os_free(data->ssl.tls_in); - data->ssl.tls_in = NULL; - data->ssl.tls_in_len = 0; if (len_decrypted < 0) { wpa_printf(MSG_INFO, "EAP-FAST: Failed to decrypt Phase 2 " "data"); @@ -1407,49 +1358,17 @@ static int eap_fast_process_version(struct eap_fast_data *data, } -static int eap_fast_process_length(struct eap_fast_data *data, - const u8 **pos, size_t *left) -{ - u32 tls_msg_len; - - if (*left < 4) { - wpa_printf(MSG_INFO, "EAP-FAST: Short frame with TLS " - "length"); - eap_fast_state(data, FAILURE); - return -1; - } - - tls_msg_len = WPA_GET_BE32(*pos); - wpa_printf(MSG_DEBUG, "EAP-FAST: TLS Message Length: %d", - tls_msg_len); - - if (data->ssl.tls_in_left == 0) { - data->ssl.tls_in_total = tls_msg_len; - data->ssl.tls_in_left = tls_msg_len; - os_free(data->ssl.tls_in); - data->ssl.tls_in = NULL; - data->ssl.tls_in_len = 0; - } - - *pos += 4; - *left -= 4; - - return 0; -} - - static int eap_fast_process_phase1(struct eap_sm *sm, - struct eap_fast_data *data, - const u8 *pos, size_t left) + struct eap_fast_data *data) { - if (eap_server_tls_process_helper(sm, &data->ssl, pos, left) < 0) { + 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) || - data->ssl.tls_out_len > 0) + wpabuf_len(data->ssl.out_buf) > 0) return 1; /* @@ -1504,6 +1423,7 @@ static void eap_fast_process(struct eap_sm *sm, void *priv, const u8 *pos; u8 flags; size_t left; + int ret; pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_FAST, respData, &left); @@ -1518,13 +1438,16 @@ static void eap_fast_process(struct eap_sm *sm, void *priv, if (eap_fast_process_version(data, flags & EAP_PEAP_VERSION_MASK)) return; - if ((flags & EAP_TLS_FLAGS_LENGTH_INCLUDED) && - eap_fast_process_length(data, &pos, &left)) + ret = eap_server_tls_reassemble(&data->ssl, flags, &pos, &left); + if (ret < 0) { + eap_fast_state(data, FAILURE); + return; + } else if (ret == 1) return; switch (data->state) { case PHASE1: - if (eap_fast_process_phase1(sm, data, pos, left)) + if (eap_fast_process_phase1(sm, data)) break; /* fall through to PHASE2_START */ @@ -1535,7 +1458,7 @@ static void eap_fast_process(struct eap_sm *sm, void *priv, case PHASE2_METHOD: case CRYPTO_BINDING: case REQUEST_PAC: - eap_fast_process_phase2(sm, data, pos, left); + eap_fast_process_phase2(sm, data, data->ssl.in_buf); break; default: wpa_printf(MSG_DEBUG, "EAP-FAST: Unexpected state %d in %s", @@ -1548,6 +1471,8 @@ static void eap_fast_process(struct eap_sm *sm, void *priv, "in TLS processing"); eap_fast_state(data, FAILURE); } + + eap_server_tls_free_in_buf(&data->ssl); } diff --git a/src/eap_server/eap_peap.c b/src/eap_server/eap_peap.c index 77c254ade..4f0248c23 100644 --- a/src/eap_server/eap_peap.c +++ b/src/eap_server/eap_peap.c @@ -230,64 +230,6 @@ static struct wpabuf * eap_peap_build_start(struct eap_sm *sm, } -static struct wpabuf * eap_peap_build_req(struct eap_sm *sm, - struct eap_peap_data *data, u8 id) -{ - int res; - struct wpabuf *req; - - res = eap_server_tls_buildReq_helper(sm, &data->ssl, EAP_TYPE_PEAP, - data->peap_version, id, &req); - - if (data->peap_version < 2 && - tls_connection_established(sm->ssl_ctx, data->ssl.conn)) { - wpa_printf(MSG_DEBUG, "EAP-PEAP: Phase1 done, starting " - "Phase2"); - eap_peap_state(data, PHASE2_START); - } - - if (res == 1) - return eap_server_tls_build_ack(id, EAP_TYPE_PEAP, - data->peap_version); - return req; -} - - -static struct wpabuf * eap_peap_encrypt(struct eap_sm *sm, - struct eap_peap_data *data, - u8 id, const u8 *plain, - size_t plain_len) -{ - int res; - struct wpabuf *buf; - - /* TODO: add support for fragmentation, if needed. This will need to - * add TLS Message Length field, if the frame is fragmented. */ - buf = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_PEAP, - 1 + data->ssl.tls_out_limit, - EAP_CODE_REQUEST, id); - if (buf == NULL) - return NULL; - - wpabuf_put_u8(buf, data->peap_version); - - res = tls_connection_encrypt(sm->ssl_ctx, data->ssl.conn, - plain, plain_len, wpabuf_put(buf, 0), - data->ssl.tls_out_limit); - if (res < 0) { - wpa_printf(MSG_INFO, "EAP-PEAP: Failed to encrypt Phase 2 " - "data"); - wpabuf_free(buf); - return NULL; - } - - wpabuf_put(buf, res); - eap_update_len(buf); - - return buf; -} - - static struct wpabuf * eap_peap_build_phase2_req(struct eap_sm *sm, struct eap_peap_data *data, u8 id) @@ -317,7 +259,7 @@ static struct wpabuf * eap_peap_build_phase2_req(struct eap_sm *sm, req_len -= sizeof(struct eap_hdr); } - encr_req = eap_peap_encrypt(sm, data, id, req, req_len); + encr_req = eap_server_tls_encrypt(sm, &data->ssl, req, req_len); wpabuf_free(buf); return encr_req; @@ -355,7 +297,7 @@ static struct wpabuf * eap_peap_build_phase2_soh(struct eap_sm *sm, req += sizeof(struct eap_hdr); req_len -= sizeof(struct eap_hdr); - encr_req = eap_peap_encrypt(sm, data, id, req, req_len); + encr_req = eap_server_tls_encrypt(sm, &data->ssl, req, req_len); wpabuf_free(buf); return encr_req; @@ -577,8 +519,8 @@ static struct wpabuf * eap_peap_build_phase2_tlv(struct eap_sm *sm, wpa_hexdump_buf_key(MSG_DEBUG, "EAP-PEAP: Encrypting Phase 2 TLV data", buf); - encr_req = eap_peap_encrypt(sm, data, id, wpabuf_head(buf), - wpabuf_len(buf)); + encr_req = eap_server_tls_encrypt(sm, &data->ssl, wpabuf_head(buf), + wpabuf_len(buf)); wpabuf_free(buf); return encr_req; @@ -605,7 +547,7 @@ static struct wpabuf * eap_peap_build_phase2_term(struct eap_sm *sm, wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: Encrypting Phase 2 data", (u8 *) hdr, req_len); - encr_req = eap_peap_encrypt(sm, data, id, (u8 *) hdr, req_len); + encr_req = eap_server_tls_encrypt(sm, &data->ssl, (u8 *) hdr, req_len); os_free(hdr); return encr_req; @@ -616,30 +558,66 @@ static struct wpabuf * eap_peap_buildReq(struct eap_sm *sm, void *priv, u8 id) { struct eap_peap_data *data = priv; + if (data->ssl.state == FRAG_ACK) { + return eap_server_tls_build_ack(id, EAP_TYPE_PEAP, + data->peap_version); + } + + if (data->ssl.state == WAIT_FRAG_ACK) { + return eap_server_tls_build_msg(&data->ssl, EAP_TYPE_PEAP, + data->peap_version, id); + } + switch (data->state) { case START: return eap_peap_build_start(sm, data, id); case PHASE1: case PHASE1_ID2: - return eap_peap_build_req(sm, data, id); + if (data->peap_version < 2 && + tls_connection_established(sm->ssl_ctx, data->ssl.conn)) { + wpa_printf(MSG_DEBUG, "EAP-PEAP: Phase1 done, " + "starting Phase2"); + eap_peap_state(data, PHASE2_START); + } + break; case PHASE2_ID: case PHASE2_METHOD: - return eap_peap_build_phase2_req(sm, data, id); + wpabuf_free(data->ssl.out_buf); + data->ssl.out_used = 0; + data->ssl.out_buf = eap_peap_build_phase2_req(sm, data, id); + break; #ifdef EAP_TNC case PHASE2_SOH: - return eap_peap_build_phase2_soh(sm, data, id); + wpabuf_free(data->ssl.out_buf); + data->ssl.out_used = 0; + data->ssl.out_buf = eap_peap_build_phase2_soh(sm, data, id); + break; #endif /* EAP_TNC */ case PHASE2_TLV: - return eap_peap_build_phase2_tlv(sm, data, id); + wpabuf_free(data->ssl.out_buf); + data->ssl.out_used = 0; + data->ssl.out_buf = eap_peap_build_phase2_tlv(sm, data, id); + break; case SUCCESS_REQ: - return eap_peap_build_phase2_term(sm, data, id, 1); + wpabuf_free(data->ssl.out_buf); + data->ssl.out_used = 0; + data->ssl.out_buf = eap_peap_build_phase2_term(sm, data, id, + 1); + break; case FAILURE_REQ: - return eap_peap_build_phase2_term(sm, data, id, 0); + wpabuf_free(data->ssl.out_buf); + data->ssl.out_used = 0; + data->ssl.out_buf = eap_peap_build_phase2_term(sm, data, id, + 0); + break; default: wpa_printf(MSG_DEBUG, "EAP-PEAP: %s - unexpected state %d", __func__, data->state); return NULL; } + + return eap_server_tls_build_msg(&data->ssl, EAP_TYPE_PEAP, + data->peap_version, id); } @@ -1135,7 +1113,7 @@ static void eap_peap_process_phase2(struct eap_sm *sm, const u8 *in_data, size_t in_len) { struct wpabuf *in_decrypted; - int len_decrypted, res; + int len_decrypted; const struct eap_hdr *hdr; size_t buf_len, len; @@ -1152,15 +1130,7 @@ static void eap_peap_process_phase2(struct eap_sm *sm, return; } - /* FIX: get rid of const -> non-const typecast */ - res = eap_server_tls_data_reassemble(sm, &data->ssl, (u8 **) &in_data, - &in_len); - if (res < 0 || res == 1) - return; - buf_len = in_len; - if (data->ssl.tls_in_total > buf_len) - buf_len = data->ssl.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 @@ -1171,9 +1141,6 @@ static void eap_peap_process_phase2(struct eap_sm *sm, buf_len *= 3; in_decrypted = wpabuf_alloc(buf_len); if (in_decrypted == NULL) { - os_free(data->ssl.tls_in); - data->ssl.tls_in = NULL; - data->ssl.tls_in_len = 0; wpa_printf(MSG_WARNING, "EAP-PEAP: failed to allocate memory " "for decryption"); return; @@ -1183,9 +1150,6 @@ static void eap_peap_process_phase2(struct eap_sm *sm, in_data, in_len, wpabuf_mhead(in_decrypted), buf_len); - os_free(data->ssl.tls_in); - data->ssl.tls_in = NULL; - data->ssl.tls_in_len = 0; if (len_decrypted < 0) { wpa_printf(MSG_INFO, "EAP-PEAP: Failed to decrypt Phase 2 " "data"); @@ -1315,7 +1279,6 @@ static int eap_peapv2_start_phase2(struct eap_sm *sm, { struct wpabuf *buf, *buf2; int res; - u8 *tls_out; wpa_printf(MSG_DEBUG, "EAP-PEAPv2: Phase1 done, include first Phase2 " "payload in the same message"); @@ -1358,18 +1321,11 @@ static int eap_peapv2_start_phase2(struct eap_sm *sm, buf); /* Append TLS data into the pending buffer after the Server Finished */ - tls_out = os_realloc(data->ssl.tls_out, - data->ssl.tls_out_len + wpabuf_len(buf)); - if (tls_out == NULL) { + if (wpabuf_resize(&data->ssl.out_buf, wpabuf_len(buf)) < 0) { wpabuf_free(buf); return -1; } - - os_memcpy(tls_out + data->ssl.tls_out_len, wpabuf_head(buf), - wpabuf_len(buf)); - data->ssl.tls_out = tls_out; - data->ssl.tls_out_len += wpabuf_len(buf); - + wpabuf_put_buf(data->ssl.out_buf, buf); wpabuf_free(buf); return 0; @@ -1383,8 +1339,8 @@ static void eap_peap_process(struct eap_sm *sm, void *priv, const u8 *pos; u8 flags; size_t left; - unsigned int tls_msg_len; int peer_version; + int ret; pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_PEAP, respData, &left); @@ -1409,33 +1365,17 @@ static void eap_peap_process(struct eap_sm *sm, void *priv, peer_version, data->peap_version, peer_version); data->peap_version = peer_version; } - if (flags & EAP_TLS_FLAGS_LENGTH_INCLUDED) { - if (left < 4) { - wpa_printf(MSG_INFO, "EAP-PEAP: Short frame with TLS " - "length"); - eap_peap_state(data, FAILURE); - return; - } - tls_msg_len = WPA_GET_BE32(pos); - wpa_printf(MSG_DEBUG, "EAP-PEAP: TLS Message Length: %d", - tls_msg_len); - if (data->ssl.tls_in_left == 0) { - data->ssl.tls_in_total = tls_msg_len; - data->ssl.tls_in_left = tls_msg_len; - os_free(data->ssl.tls_in); - data->ssl.tls_in = NULL; - data->ssl.tls_in_len = 0; - } - pos += 4; - left -= 4; - } + + ret = eap_server_tls_reassemble(&data->ssl, flags, &pos, &left); + if (ret < 0) { + eap_peap_state(data, FAILURE); + return; + } else if (ret == 1) + return; switch (data->state) { case PHASE1: - if (eap_server_tls_process_helper(sm, &data->ssl, pos, left) < - 0) { - wpa_printf(MSG_INFO, "EAP-PEAP: TLS processing " - "failed"); + if (eap_server_tls_phase1(sm, &data->ssl) < 0) { eap_peap_state(data, FAILURE); break; } @@ -1476,6 +1416,8 @@ static void eap_peap_process(struct eap_sm *sm, void *priv, "in TLS processing"); eap_peap_state(data, FAILURE); } + + eap_server_tls_free_in_buf(&data->ssl); } diff --git a/src/eap_server/eap_tls.c b/src/eap_server/eap_tls.c index 2bc96bdf8..c52ec5aa5 100644 --- a/src/eap_server/eap_tls.c +++ b/src/eap_server/eap_tls.c @@ -1,6 +1,6 @@ /* * hostapd / EAP-TLS (RFC 2716) - * Copyright (c) 2004-2007, Jouni Malinen + * Copyright (c) 2004-2008, Jouni Malinen * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -80,40 +80,36 @@ static struct wpabuf * eap_tls_build_start(struct eap_sm *sm, } -static struct wpabuf * eap_tls_build_req(struct eap_sm *sm, - struct eap_tls_data *data, u8 id) -{ - int res; - struct wpabuf *req; - - res = eap_server_tls_buildReq_helper(sm, &data->ssl, EAP_TYPE_TLS, 0, - id, &req); - - if (tls_connection_established(sm->ssl_ctx, data->ssl.conn)) { - wpa_printf(MSG_DEBUG, "EAP-TLS: Done"); - data->state = SUCCESS; - } - - if (res == 1) - return eap_server_tls_build_ack(id, EAP_TYPE_TLS, 0); - return req; -} - - static struct wpabuf * eap_tls_buildReq(struct eap_sm *sm, void *priv, u8 id) { struct eap_tls_data *data = priv; + + if (data->ssl.state == FRAG_ACK) { + return eap_server_tls_build_ack(id, EAP_TYPE_TLS, 0); + } + + if (data->ssl.state == WAIT_FRAG_ACK) { + return eap_server_tls_build_msg(&data->ssl, EAP_TYPE_TLS, 0, + id); + } + switch (data->state) { case START: return eap_tls_build_start(sm, data, id); case CONTINUE: - return eap_tls_build_req(sm, data, id); + if (tls_connection_established(sm->ssl_ctx, data->ssl.conn)) { + wpa_printf(MSG_DEBUG, "EAP-TLS: Done"); + data->state = SUCCESS; + } + break; default: wpa_printf(MSG_DEBUG, "EAP-TLS: %s - unexpected state %d", __func__, data->state); return NULL; } + + return eap_server_tls_build_msg(&data->ssl, EAP_TYPE_TLS, 0, id); } @@ -140,50 +136,34 @@ static void eap_tls_process(struct eap_sm *sm, void *priv, const u8 *pos; u8 flags; size_t left; - unsigned int tls_msg_len; + int ret; pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_TLS, respData, &left); if (pos == NULL || left < 1) return; /* Should not happen - frame already validated */ - flags = *pos++; left--; wpa_printf(MSG_DEBUG, "EAP-TLS: Received packet(len=%lu) - " "Flags 0x%02x", (unsigned long) wpabuf_len(respData), flags); - if (flags & EAP_TLS_FLAGS_LENGTH_INCLUDED) { - if (left < 4) { - wpa_printf(MSG_INFO, "EAP-TLS: Short frame with TLS " - "length"); - data->state = FAILURE; - return; - } - tls_msg_len = WPA_GET_BE32(pos); - wpa_printf(MSG_DEBUG, "EAP-TLS: TLS Message Length: %d", - tls_msg_len); - if (data->ssl.tls_in_left == 0) { - data->ssl.tls_in_total = tls_msg_len; - data->ssl.tls_in_left = tls_msg_len; - os_free(data->ssl.tls_in); - data->ssl.tls_in = NULL; - data->ssl.tls_in_len = 0; - } - pos += 4; - left -= 4; - } - if (eap_server_tls_process_helper(sm, &data->ssl, pos, left) < 0) { - wpa_printf(MSG_INFO, "EAP-TLS: TLS processing failed"); + ret = eap_server_tls_reassemble(&data->ssl, flags, &pos, &left); + if (ret < 0) { data->state = FAILURE; return; - } + } else if (ret == 1) + return; + + if (eap_server_tls_phase1(sm, &data->ssl) < 0) + data->state = FAILURE; if (tls_connection_get_write_alerts(sm->ssl_ctx, data->ssl.conn) > 1) { wpa_printf(MSG_INFO, "EAP-TLS: Locally detected fatal error " "in TLS processing"); data->state = FAILURE; - return; } + + eap_server_tls_free_in_buf(&data->ssl); } diff --git a/src/eap_server/eap_tls_common.c b/src/eap_server/eap_tls_common.c index f74113378..7cd1d03a1 100644 --- a/src/eap_server/eap_tls_common.c +++ b/src/eap_server/eap_tls_common.c @@ -1,6 +1,6 @@ /* * hostapd / EAP-TLS/PEAP/TTLS/FAST common functions - * Copyright (c) 2004-2007, Jouni Malinen + * Copyright (c) 2004-2008, Jouni Malinen * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -58,8 +58,8 @@ int eap_server_tls_ssl_init(struct eap_sm *sm, struct eap_ssl_data *data, void eap_server_tls_ssl_deinit(struct eap_sm *sm, struct eap_ssl_data *data) { tls_connection_deinit(sm->ssl_ctx, data->conn); - os_free(data->tls_in); - os_free(data->tls_out); + os_free(data->in_buf); + os_free(data->out_buf); } @@ -106,176 +106,60 @@ fail: } -int eap_server_tls_data_reassemble(struct eap_sm *sm, - struct eap_ssl_data *data, - u8 **in_data, size_t *in_len) +struct wpabuf * eap_server_tls_build_msg(struct eap_ssl_data *data, + int eap_type, int peap_version, u8 id) { - u8 *buf; - - if (data->tls_in_left > *in_len || data->tls_in) { - if (*in_len == 0) { - wpa_printf(MSG_INFO, "SSL: Empty fragment when trying " - "to reassemble"); - return -1; - } - if (data->tls_in_len + *in_len > 65536) { - /* Limit length to avoid rogue peers from causing large - * memory allocations. */ - os_free(data->tls_in); - data->tls_in = NULL; - data->tls_in_len = 0; - wpa_printf(MSG_INFO, "SSL: Too long TLS fragment (size" - " over 64 kB)"); - return -1; - } - buf = os_realloc(data->tls_in, data->tls_in_len + *in_len); - if (buf == NULL) { - os_free(data->tls_in); - data->tls_in = NULL; - data->tls_in_len = 0; - wpa_printf(MSG_INFO, "SSL: Could not allocate memory " - "for TLS data"); - return -1; - } - os_memcpy(buf + data->tls_in_len, *in_data, *in_len); - data->tls_in = buf; - data->tls_in_len += *in_len; - if (*in_len > data->tls_in_left) { - wpa_printf(MSG_INFO, "SSL: more data than TLS message " - "length indicated"); - data->tls_in_left = 0; - return -1; - } - 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; - } - - *in_data = data->tls_in; - *in_len = data->tls_in_len; - } else - data->tls_in_left = 0; - - return 0; -} - - -int eap_server_tls_process_helper(struct eap_sm *sm, struct eap_ssl_data *data, - const u8 *in_data, size_t in_len) -{ - WPA_ASSERT(data->tls_out_len == 0 || in_len == 0); - - if (data->tls_out_len == 0) { - u8 *_in_data = (u8 *) in_data; /* FIX: get rid of the typecast - */ - /* No more data to send out - expect to receive more data from - * the peer. */ - int res = eap_server_tls_data_reassemble(sm, data, &_in_data, - &in_len); - if (res < 0 || res == 1) { - wpa_printf(MSG_DEBUG, "SSL: data reassembly failed"); - return res; - } - /* Full TLS message reassembled - continue handshake processing - */ - if (data->tls_out) { - /* This should not happen.. */ - wpa_printf(MSG_INFO, "SSL: eap_tls_process_helper - " - "pending tls_out data even though " - "tls_out_len = 0"); - os_free(data->tls_out); - WPA_ASSERT(data->tls_out == NULL); - } - data->tls_out = tls_connection_server_handshake( - sm->ssl_ctx, data->conn, _in_data, in_len, - &data->tls_out_len); - - /* Clear reassembled input data (if the buffer was needed). */ - data->tls_in_left = data->tls_in_total = data->tls_in_len = 0; - os_free(data->tls_in); - data->tls_in = NULL; - } - - if (data->tls_out == NULL) { - wpa_printf(MSG_DEBUG, "SSL: failed to generate output data"); - data->tls_out_len = 0; - return -1; - } - if (data->tls_out_len == 0) { - /* TLS negotiation should now be complete since all other cases - * needing more that should have been catched above based on - * the TLS Message Length field. */ - wpa_printf(MSG_DEBUG, "SSL: No data to be sent out"); - os_free(data->tls_out); - data->tls_out = NULL; - - if (tls_connection_get_read_alerts(sm->ssl_ctx, data->conn)) { - wpa_printf(MSG_DEBUG, "SSL: Remote end sent a fatal " - "alert - abort handshake"); - return -1; - } - - return 1; - } - - wpa_printf(MSG_DEBUG, "SSL: %lu bytes left to be sent out (of total " - "%lu bytes)", - (unsigned long) data->tls_out_len - data->tls_out_pos, - (unsigned long) data->tls_out_len); - - return 0; -} - - -int eap_server_tls_buildReq_helper(struct eap_sm *sm, - struct eap_ssl_data *data, - int eap_type, int peap_version, u8 id, - struct wpabuf **out_data) -{ - size_t len; - u8 *flags; struct wpabuf *req; - int incl_len; + u8 flags; + size_t send_len, plen; - incl_len = data->tls_out_pos == 0 && - data->tls_out_len > data->tls_out_limit; - req = eap_msg_alloc(EAP_VENDOR_IETF, eap_type, - 1 + (incl_len ? 4 : 0) + data->tls_out_limit, + wpa_printf(MSG_DEBUG, "SSL: Generating Request"); + + flags = peap_version; + send_len = wpabuf_len(data->out_buf) - data->out_used; + if (1 + send_len > data->tls_out_limit) { + send_len = data->tls_out_limit - 1; + flags |= EAP_TLS_FLAGS_MORE_FRAGMENTS; + if (data->out_used == 0) { + flags |= EAP_TLS_FLAGS_LENGTH_INCLUDED; + send_len -= 4; + } + } + + plen = 1 + send_len; + if (flags & EAP_TLS_FLAGS_LENGTH_INCLUDED) + plen += 4; + + req = eap_msg_alloc(EAP_VENDOR_IETF, eap_type, plen, EAP_CODE_REQUEST, id); - if (req == NULL) { - *out_data = NULL; - return -1; - } - flags = wpabuf_put(req, 1); - *flags = peap_version; - if (incl_len) { - *flags |= EAP_TLS_FLAGS_LENGTH_INCLUDED; - wpabuf_put_be32(req, data->tls_out_len); + if (req == NULL) + return NULL; + + wpabuf_put_u8(req, flags); /* Flags */ + if (flags & EAP_TLS_FLAGS_LENGTH_INCLUDED) + wpabuf_put_be32(req, wpabuf_len(data->out_buf)); + + wpabuf_put_data(req, wpabuf_head_u8(data->out_buf) + data->out_used, + send_len); + data->out_used += send_len; + + if (data->out_used == wpabuf_len(data->out_buf)) { + wpa_printf(MSG_DEBUG, "SSL: Sending out %lu bytes " + "(message sent completely)", + (unsigned long) send_len); + wpabuf_free(data->out_buf); + data->out_buf = NULL; + data->out_used = 0; + data->state = MSG; + } else { + wpa_printf(MSG_DEBUG, "SSL: Sending out %lu bytes " + "(%lu more to send)", (unsigned long) send_len, + (unsigned long) wpabuf_len(data->out_buf) - + data->out_used); + data->state = WAIT_FRAG_ACK; } - len = data->tls_out_len - data->tls_out_pos; - if (len > data->tls_out_limit) { - *flags |= EAP_TLS_FLAGS_MORE_FRAGMENTS; - len = data->tls_out_limit; - wpa_printf(MSG_DEBUG, "SSL: sending %lu bytes, more fragments " - "will follow", (unsigned long) len); - } - wpabuf_put_data(req, &data->tls_out[data->tls_out_pos], len); - data->tls_out_pos += len; - - eap_update_len(req); - *out_data = req; - - if (!(*flags & EAP_TLS_FLAGS_MORE_FRAGMENTS)) { - data->tls_out_len = 0; - data->tls_out_pos = 0; - os_free(data->tls_out); - data->tls_out = NULL; - } - - return 0; + return req; } @@ -291,3 +175,183 @@ struct wpabuf * eap_server_tls_build_ack(u8 id, int eap_type, int peap_version) wpabuf_put_u8(req, peap_version); /* Flags */ return req; } + + +static int eap_server_tls_process_cont(struct eap_ssl_data *data, + const u8 *buf, size_t len) +{ + /* Process continuation of a pending message */ + if (len > wpabuf_tailroom(data->in_buf)) { + wpa_printf(MSG_DEBUG, "SSL: Fragment overflow"); + return -1; + } + + wpabuf_put_data(data->in_buf, buf, len); + wpa_printf(MSG_DEBUG, "SSL: Received %lu bytes, waiting for %lu " + "bytes more", (unsigned long) len, + (unsigned long) wpabuf_tailroom(data->in_buf)); + + return 0; +} + + +static int eap_server_tls_process_fragment(struct eap_ssl_data *data, + u8 flags, u32 message_length, + const u8 *buf, size_t len) +{ + /* Process a fragment that is not the last one of the message */ + if (data->in_buf == NULL && !(flags & EAP_TLS_FLAGS_LENGTH_INCLUDED)) { + wpa_printf(MSG_DEBUG, "SSL: No Message Length field in a " + "fragmented packet"); + return -1; + } + + if (data->in_buf == NULL) { + /* First fragment of the message */ + + /* Limit length to avoid rogue peers from causing large + * memory allocations. */ + if (message_length > 65536) { + wpa_printf(MSG_INFO, "SSL: Too long TLS fragment (size" + " over 64 kB)"); + return -1; + } + + data->in_buf = wpabuf_alloc(message_length); + if (data->in_buf == NULL) { + wpa_printf(MSG_DEBUG, "SSL: No memory for message"); + return -1; + } + wpabuf_put_data(data->in_buf, buf, len); + wpa_printf(MSG_DEBUG, "SSL: Received %lu bytes in first " + "fragment, waiting for %lu bytes more", + (unsigned long) len, + (unsigned long) wpabuf_tailroom(data->in_buf)); + } + + return 0; +} + + +int eap_server_tls_phase1(struct eap_sm *sm, struct eap_ssl_data *data) +{ + u8 *next; + size_t next_len; + + next = tls_connection_server_handshake( + sm->ssl_ctx, data->conn, + wpabuf_mhead(data->in_buf), + wpabuf_len(data->in_buf), + &next_len); + if (next == NULL) { + wpa_printf(MSG_INFO, "SSL: TLS processing failed"); + return -1; + } + if (data->out_buf) { + /* This should not happen.. */ + wpa_printf(MSG_INFO, "SSL: pending tls_out data when " + "processing new message"); + os_free(data->out_buf); + WPA_ASSERT(data->out_buf == NULL); + } + data->out_buf = wpabuf_alloc_ext_data(next, next_len); + if (data->out_buf == NULL) { + os_free(next); + return -1; + } + return 0; +} + + +int eap_server_tls_reassemble(struct eap_ssl_data *data, u8 flags, + const u8 **pos, size_t *left) +{ + unsigned int tls_msg_len = 0; + const u8 *end = *pos + *left; + + if (flags & EAP_TLS_FLAGS_LENGTH_INCLUDED) { + if (*left < 4) { + wpa_printf(MSG_INFO, "SSL: Short frame with TLS " + "length"); + return -1; + } + tls_msg_len = WPA_GET_BE32(*pos); + wpa_printf(MSG_DEBUG, "SSL: TLS Message Length: %d", + tls_msg_len); + *pos += 4; + *left -= 4; + } + + wpa_printf(MSG_DEBUG, "SSL: Received packet: Flags 0x%x " + "Message Length %u", flags, tls_msg_len); + + if (data->state == WAIT_FRAG_ACK) { + if (*left != 0) { + wpa_printf(MSG_DEBUG, "SSL: Unexpected payload in " + "WAIT_FRAG_ACK state"); + return -1; + } + wpa_printf(MSG_DEBUG, "SSL: Fragment acknowledged"); + return 1; + } + + if (data->in_buf && + eap_server_tls_process_cont(data, *pos, end - *pos) < 0) + return -1; + + if (flags & EAP_TLS_FLAGS_MORE_FRAGMENTS) { + if (eap_server_tls_process_fragment(data, flags, tls_msg_len, + *pos, end - *pos) < 0) + return -1; + + data->state = FRAG_ACK; + return 1; + } + + if (data->state == FRAG_ACK) { + wpa_printf(MSG_DEBUG, "SSL: All fragments received"); + data->state = MSG; + } + + if (data->in_buf == NULL) { + /* Wrap unfragmented messages as wpabuf without extra copy */ + wpabuf_set(&data->tmpbuf, *pos, end - *pos); + data->in_buf = &data->tmpbuf; + } + + return 0; +} + + +void eap_server_tls_free_in_buf(struct eap_ssl_data *data) +{ + if (data->in_buf != &data->tmpbuf) + wpabuf_free(data->in_buf); + data->in_buf = NULL; +} + + +struct wpabuf * eap_server_tls_encrypt(struct eap_sm *sm, + struct eap_ssl_data *data, + const u8 *plain, size_t plain_len) +{ + int res; + struct wpabuf *buf; + size_t buf_len; + + /* reserve some extra room for encryption overhead */ + buf_len = plain_len + 200; + buf = wpabuf_alloc(buf_len); + res = tls_connection_encrypt(sm->ssl_ctx, data->conn, + plain, plain_len, wpabuf_put(buf, 0), + buf_len); + if (res < 0) { + wpa_printf(MSG_INFO, "SSL: Failed to encrypt Phase 2 data"); + wpabuf_free(buf); + return NULL; + } + + wpabuf_put(buf, res); + + return buf; +} diff --git a/src/eap_server/eap_tls_common.h b/src/eap_server/eap_tls_common.h index 2470faa1b..4d89f77c3 100644 --- a/src/eap_server/eap_tls_common.h +++ b/src/eap_server/eap_tls_common.h @@ -1,6 +1,6 @@ /* * hostapd / EAP-TLS/PEAP/TTLS/FAST common functions - * Copyright (c) 2004-2007, Jouni Malinen + * Copyright (c) 2004-2008, Jouni Malinen * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -18,18 +18,17 @@ struct eap_ssl_data { struct tls_connection *conn; - u8 *tls_out; - size_t tls_out_len; - size_t tls_out_pos; size_t tls_out_limit; - u8 *tls_in; - size_t tls_in_len; - size_t tls_in_left; - size_t tls_in_total; int phase2; struct eap_sm *eap; + + enum { MSG, FRAG_ACK, WAIT_FRAG_ACK } state; + struct wpabuf *in_buf; + struct wpabuf *out_buf; + size_t out_used; + struct wpabuf tmpbuf; }; @@ -48,16 +47,17 @@ int eap_server_tls_ssl_init(struct eap_sm *sm, struct eap_ssl_data *data, void eap_server_tls_ssl_deinit(struct eap_sm *sm, struct eap_ssl_data *data); u8 * eap_server_tls_derive_key(struct eap_sm *sm, struct eap_ssl_data *data, char *label, size_t len); -int eap_server_tls_data_reassemble(struct eap_sm *sm, - struct eap_ssl_data *data, - u8 **in_data, size_t *in_len); -int eap_server_tls_process_helper(struct eap_sm *sm, struct eap_ssl_data *data, - const u8 *in_data, size_t in_len); -int eap_server_tls_buildReq_helper(struct eap_sm *sm, - struct eap_ssl_data *data, - int eap_type, int peap_version, u8 id, - struct wpabuf **out_data); +struct wpabuf * eap_server_tls_build_msg(struct eap_ssl_data *data, + int eap_type, int peap_version, + u8 id); struct wpabuf * eap_server_tls_build_ack(u8 id, int eap_type, int peap_version); +int eap_server_tls_phase1(struct eap_sm *sm, struct eap_ssl_data *data); +int eap_server_tls_reassemble(struct eap_ssl_data *data, u8 flags, + const u8 **pos, size_t *left); +void eap_server_tls_free_in_buf(struct eap_ssl_data *data); +struct wpabuf * eap_server_tls_encrypt(struct eap_sm *sm, + struct eap_ssl_data *data, + const u8 *plain, size_t plain_len); #endif /* EAP_TLS_COMMON_H */ diff --git a/src/eap_server/eap_ttls.c b/src/eap_server/eap_ttls.c index 4c71b5fa2..dab1b26fd 100644 --- a/src/eap_server/eap_ttls.c +++ b/src/eap_server/eap_ttls.c @@ -445,62 +445,6 @@ static struct wpabuf * eap_ttls_build_start(struct eap_sm *sm, } -static struct wpabuf * eap_ttls_build_req(struct eap_sm *sm, - struct eap_ttls_data *data, u8 id) -{ - int res; - struct wpabuf *req; - - res = eap_server_tls_buildReq_helper(sm, &data->ssl, EAP_TYPE_TTLS, - data->ttls_version, id, &req); - - if (tls_connection_established(sm->ssl_ctx, data->ssl.conn)) { - wpa_printf(MSG_DEBUG, "EAP-TTLS: Phase1 done, starting " - "Phase2"); - eap_ttls_state(data, PHASE2_START); - } - - if (res == 1) - return eap_server_tls_build_ack(id, EAP_TYPE_TTLS, - data->ttls_version); - return req; -} - - -static struct wpabuf * eap_ttls_encrypt(struct eap_sm *sm, - struct eap_ttls_data *data, - u8 id, u8 *plain, size_t plain_len) -{ - int res; - struct wpabuf *buf; - - /* TODO: add support for fragmentation, if needed. This will need to - * add TLS Message Length field, if the frame is fragmented. */ - buf = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_TTLS, - 1 + data->ssl.tls_out_limit, - EAP_CODE_REQUEST, id); - if (buf == NULL) - return NULL; - - wpabuf_put_u8(buf, data->ttls_version); - - res = tls_connection_encrypt(sm->ssl_ctx, data->ssl.conn, - plain, plain_len, wpabuf_put(buf, 0), - data->ssl.tls_out_limit); - if (res < 0) { - wpa_printf(MSG_INFO, "EAP-TTLS: Failed to encrypt Phase 2 " - "data"); - wpabuf_free(buf); - return NULL; - } - - wpabuf_put(buf, res); - eap_update_len(buf); - - return buf; -} - - static struct wpabuf * eap_ttls_build_phase2_eap_req( struct eap_sm *sm, struct eap_ttls_data *data, u8 id) { @@ -528,7 +472,7 @@ static struct wpabuf * eap_ttls_build_phase2_eap_req( wpa_hexdump_key(MSG_DEBUG, "EAP-TTLS/EAP: Encrypt encapsulated Phase " "2 data", req, req_len); - encr_req = eap_ttls_encrypt(sm, data, id, req, req_len); + encr_req = eap_server_tls_encrypt(sm, &data->ssl, req, req_len); wpabuf_free(buf); return encr_req; @@ -536,7 +480,7 @@ static struct wpabuf * eap_ttls_build_phase2_eap_req( static struct wpabuf * eap_ttls_build_phase2_mschapv2( - struct eap_sm *sm, struct eap_ttls_data *data, u8 id) + struct eap_sm *sm, struct eap_ttls_data *data) { struct wpabuf *encr_req; u8 *req, *pos, *end; @@ -570,7 +514,7 @@ static struct wpabuf * eap_ttls_build_phase2_mschapv2( wpa_hexdump_key(MSG_DEBUG, "EAP-TTLS/MSCHAPV2: Encrypting Phase 2 " "data", req, req_len); - encr_req = eap_ttls_encrypt(sm, data, id, req, req_len); + encr_req = eap_server_tls_encrypt(sm, &data->ssl, req, req_len); os_free(req); return encr_req; @@ -578,20 +522,16 @@ static struct wpabuf * eap_ttls_build_phase2_mschapv2( static struct wpabuf * eap_ttls_build_phase_finished( - struct eap_sm *sm, struct eap_ttls_data *data, u8 id, int final) + struct eap_sm *sm, struct eap_ttls_data *data, int final) { int len; struct wpabuf *req; const int max_len = 300; - len = 1 + max_len; - req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_TTLS, len, - EAP_CODE_REQUEST, id); + req = wpabuf_alloc(max_len); if (req == NULL) return NULL; - wpabuf_put_u8(req, data->ttls_version); - len = tls_connection_ia_send_phase_finished(sm->ssl_ctx, data->ssl.conn, final, wpabuf_mhead(req), @@ -601,7 +541,6 @@ static struct wpabuf * eap_ttls_build_phase_finished( return NULL; } wpabuf_put(req, len); - eap_update_len(req); return req; } @@ -611,22 +550,50 @@ static struct wpabuf * eap_ttls_buildReq(struct eap_sm *sm, void *priv, u8 id) { struct eap_ttls_data *data = priv; + if (data->ssl.state == FRAG_ACK) { + return eap_server_tls_build_ack(id, EAP_TYPE_TTLS, + data->ttls_version); + } + + if (data->ssl.state == WAIT_FRAG_ACK) { + return eap_server_tls_build_msg(&data->ssl, EAP_TYPE_TTLS, + data->ttls_version, id); + } + switch (data->state) { case START: return eap_ttls_build_start(sm, data, id); case PHASE1: - return eap_ttls_build_req(sm, data, id); + if (tls_connection_established(sm->ssl_ctx, data->ssl.conn)) { + wpa_printf(MSG_DEBUG, "EAP-TTLS: Phase1 done, " + "starting Phase2"); + eap_ttls_state(data, PHASE2_START); + } + break; case PHASE2_METHOD: - return eap_ttls_build_phase2_eap_req(sm, data, id); + wpabuf_free(data->ssl.out_buf); + data->ssl.out_used = 0; + data->ssl.out_buf = eap_ttls_build_phase2_eap_req(sm, data, + id); + break; case PHASE2_MSCHAPV2_RESP: - return eap_ttls_build_phase2_mschapv2(sm, data, id); + wpabuf_free(data->ssl.out_buf); + data->ssl.out_used = 0; + data->ssl.out_buf = eap_ttls_build_phase2_mschapv2(sm, data); + break; case PHASE_FINISHED: - return eap_ttls_build_phase_finished(sm, data, id, 1); + wpabuf_free(data->ssl.out_buf); + data->ssl.out_used = 0; + data->ssl.out_buf = eap_ttls_build_phase_finished(sm, data, 1); + break; default: wpa_printf(MSG_DEBUG, "EAP-TTLS: %s - unexpected state %d", __func__, data->state); return NULL; } + + return eap_server_tls_build_msg(&data->ssl, EAP_TYPE_TTLS, + data->ttls_version, id); } @@ -1148,12 +1115,17 @@ static void eap_ttls_process_phase2_eap(struct eap_sm *sm, static void eap_ttls_process_phase2(struct eap_sm *sm, struct eap_ttls_data *data, - u8 *in_data, size_t in_len) + struct wpabuf *in_buf) { u8 *in_decrypted; - int len_decrypted, res; + int len_decrypted; struct eap_ttls_avp parse; 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-TTLS: received %lu bytes encrypted data for" " Phase 2", (unsigned long) in_len); @@ -1169,14 +1141,7 @@ static void eap_ttls_process_phase2(struct eap_sm *sm, return; } - res = eap_server_tls_data_reassemble(sm, &data->ssl, &in_data, - &in_len); - if (res < 0 || res == 1) - return; - buf_len = in_len; - if (data->ssl.tls_in_total > buf_len) - buf_len = data->ssl.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 @@ -1187,9 +1152,6 @@ static void eap_ttls_process_phase2(struct eap_sm *sm, buf_len *= 3; in_decrypted = os_malloc(buf_len); if (in_decrypted == NULL) { - os_free(data->ssl.tls_in); - data->ssl.tls_in = NULL; - data->ssl.tls_in_len = 0; wpa_printf(MSG_WARNING, "EAP-TTLS: failed to allocate memory " "for decryption"); return; @@ -1198,9 +1160,6 @@ static void eap_ttls_process_phase2(struct eap_sm *sm, len_decrypted = tls_connection_decrypt(sm->ssl_ctx, data->ssl.conn, in_data, in_len, in_decrypted, buf_len); - os_free(data->ssl.tls_in); - data->ssl.tls_in = NULL; - data->ssl.tls_in_len = 0; if (len_decrypted < 0) { wpa_printf(MSG_INFO, "EAP-TTLS: Failed to decrypt Phase 2 " "data"); @@ -1313,26 +1272,10 @@ static void eap_ttls_start_tnc(struct eap_sm *sm, struct eap_ttls_data *data) } -static void eap_ttls_process(struct eap_sm *sm, void *priv, - struct wpabuf *respData) +static void eap_ttls_process_version(struct eap_sm *sm, + struct eap_ttls_data *data, + int peer_version) { - struct eap_ttls_data *data = priv; - const u8 *pos; - u8 flags; - size_t left; - unsigned int tls_msg_len; - int peer_version; - - pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_TTLS, respData, - &left); - if (pos == NULL || left < 1) - return; - flags = *pos++; - left--; - wpa_printf(MSG_DEBUG, "EAP-TTLS: Received packet(len=%lu) - " - "Flags 0x%02x", (unsigned long) wpabuf_len(respData), - flags); - peer_version = flags & EAP_PEAP_VERSION_MASK; if (peer_version < data->ttls_version) { wpa_printf(MSG_DEBUG, "EAP-TTLS: peer ver=%d, own ver=%d; " "use version %d", @@ -1349,42 +1292,46 @@ static void eap_ttls_process(struct eap_sm *sm, void *priv, } data->tls_ia_configured = 1; } +} - if (flags & EAP_TLS_FLAGS_LENGTH_INCLUDED) { - if (left < 4) { - wpa_printf(MSG_INFO, "EAP-TTLS: Short frame with TLS " - "length"); - eap_ttls_state(data, FAILURE); - return; - } - tls_msg_len = WPA_GET_BE32(pos); - wpa_printf(MSG_DEBUG, "EAP-TTLS: TLS Message Length: %d", - tls_msg_len); - if (data->ssl.tls_in_left == 0) { - data->ssl.tls_in_total = tls_msg_len; - data->ssl.tls_in_left = tls_msg_len; - os_free(data->ssl.tls_in); - data->ssl.tls_in = NULL; - data->ssl.tls_in_len = 0; - } - pos += 4; - left -= 4; - } + +static void eap_ttls_process(struct eap_sm *sm, void *priv, + struct wpabuf *respData) +{ + struct eap_ttls_data *data = priv; + const u8 *pos; + u8 flags; + size_t left; + int ret; + + pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_TTLS, respData, + &left); + if (pos == NULL || left < 1) + return; + flags = *pos++; + left--; + wpa_printf(MSG_DEBUG, "EAP-TTLS: Received packet(len=%lu) - " + "Flags 0x%02x", (unsigned long) wpabuf_len(respData), + flags); + + eap_ttls_process_version(sm, data, flags & EAP_PEAP_VERSION_MASK); + + ret = eap_server_tls_reassemble(&data->ssl, flags, &pos, &left); + if (ret < 0) { + eap_ttls_state(data, FAILURE); + return; + } else if (ret == 1) + return; switch (data->state) { case PHASE1: - if (eap_server_tls_process_helper(sm, &data->ssl, pos, left) < - 0) { - wpa_printf(MSG_INFO, "EAP-TTLS: TLS processing " - "failed"); + if (eap_server_tls_phase1(sm, &data->ssl) < 0) eap_ttls_state(data, FAILURE); - } break; case PHASE2_START: case PHASE2_METHOD: case PHASE_FINISHED: - /* FIX: get rid of const->non-const typecast */ - eap_ttls_process_phase2(sm, data, (u8 *) pos, left); + eap_ttls_process_phase2(sm, data, data->ssl.in_buf); eap_ttls_start_tnc(sm, data); break; case PHASE2_MSCHAPV2_RESP: @@ -1417,6 +1364,8 @@ static void eap_ttls_process(struct eap_sm *sm, void *priv, "in TLS processing"); eap_ttls_state(data, FAILURE); } + + eap_server_tls_free_in_buf(&data->ssl); }