Redesigned EAP-TLS/PEAP/TTLS/FAST fragmentation/reassembly
authorJouni Malinen <j@w1.fi>
Wed, 28 May 2008 06:57:17 +0000 (09:57 +0300)
committerJouni Malinen <j@w1.fi>
Wed, 28 May 2008 06:57:17 +0000 (09:57 +0300)
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
src/eap_server/eap_fast.c
src/eap_server/eap_peap.c
src/eap_server/eap_tls.c
src/eap_server/eap_tls_common.c
src/eap_server/eap_tls_common.h
src/eap_server/eap_ttls.c

index 56673de..5146d1a 100644 (file)
@@ -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
index c50ffd2..6bcfabc 100644 (file)
@@ -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);
 }
 
 
index 77c254a..4f0248c 100644 (file)
@@ -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);
 }
 
 
index 2bc96bd..c52ec5a 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * hostapd / EAP-TLS (RFC 2716)
- * Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2008, Jouni Malinen <j@w1.fi>
  *
  * 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)
+static struct wpabuf * eap_tls_buildReq(struct eap_sm *sm, void *priv, u8 id)
 {
-       int res;
-       struct wpabuf *req;
+       struct eap_tls_data *data = priv;
 
-       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)
+       if (data->ssl.state == FRAG_ACK) {
                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 == 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);
 }
 
 
index f741133..7cd1d03 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * hostapd / EAP-TLS/PEAP/TTLS/FAST common functions
- * Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2008, Jouni Malinen <j@w1.fi>
  *
  * 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,188 +106,252 @@ 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;
+       struct wpabuf *req;
+       u8 flags;
+       size_t send_len, plen;
+
+       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;
                }
+       }
 
-               *in_data = data->tls_in;
-               *in_len = data->tls_in_len;
-       } else
-               data->tls_in_left = 0;
+       plen = 1 + send_len;
+       if (flags & EAP_TLS_FLAGS_LENGTH_INCLUDED)
+               plen += 4;
 
-       return 0;
+       req = eap_msg_alloc(EAP_VENDOR_IETF, eap_type, plen,
+                           EAP_CODE_REQUEST, id);
+       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;
+       }
+
+       return req;
 }
 
 
-int eap_server_tls_process_helper(struct eap_sm *sm, struct eap_ssl_data *data,
-                                 const u8 *in_data, size_t in_len)
+struct wpabuf * eap_server_tls_build_ack(u8 id, int eap_type, int peap_version)
 {
-       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;
+       struct wpabuf *req;
+
+       req = eap_msg_alloc(EAP_VENDOR_IETF, eap_type, 1, EAP_CODE_REQUEST,
+                           id);
+       if (req == NULL)
+               return NULL;
+       wpa_printf(MSG_DEBUG, "SSL: Building ACK");
+       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;
        }
 
-       if (data->tls_out == NULL) {
-               wpa_printf(MSG_DEBUG, "SSL: failed to generate output data");
-               data->tls_out_len = 0;
+       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->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");
+
+       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;
                }
 
-               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));
        }
 
-       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_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_buildReq_helper(struct eap_sm *sm,
-                                  struct eap_ssl_data *data,
-                                  int eap_type, int peap_version, u8 id,
-                                  struct wpabuf **out_data)
+int eap_server_tls_reassemble(struct eap_ssl_data *data, u8 flags,
+                             const u8 **pos, size_t *left)
 {
-       size_t len;
-       u8 *flags;
-       struct wpabuf *req;
-       int incl_len;
+       unsigned int tls_msg_len = 0;
+       const u8 *end = *pos + *left;
 
-       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,
-                           EAP_CODE_REQUEST, id);
-       if (req == NULL) {
-               *out_data = NULL;
-               return -1;
+       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;
        }
-       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);
+
+       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;
        }
 
-       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);
+       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;
        }
-       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 (data->state == FRAG_ACK) {
+               wpa_printf(MSG_DEBUG, "SSL: All fragments received");
+               data->state = MSG;
+       }
 
-       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;
+       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;
 }
 
 
-struct wpabuf * eap_server_tls_build_ack(u8 id, int eap_type, int peap_version)
+void eap_server_tls_free_in_buf(struct eap_ssl_data *data)
 {
-       struct wpabuf *req;
+       if (data->in_buf != &data->tmpbuf)
+               wpabuf_free(data->in_buf);
+       data->in_buf = NULL;
+}
 
-       req = eap_msg_alloc(EAP_VENDOR_IETF, eap_type, 1, EAP_CODE_REQUEST,
-                           id);
-       if (req == 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;
-       wpa_printf(MSG_DEBUG, "SSL: Building ACK");
-       wpabuf_put_u8(req, peap_version); /* Flags */
-       return req;
+       }
+
+       wpabuf_put(buf, res);
+
+       return buf;
 }
index 2470faa..4d89f77 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * hostapd / EAP-TLS/PEAP/TTLS/FAST common functions
- * Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2008, Jouni Malinen <j@w1.fi>
  *
  * 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
 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 */
index 4c71b5f..dab1b26 100644 (file)
@@ -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,6 +1272,29 @@ static void eap_ttls_start_tnc(struct eap_sm *sm, struct eap_ttls_data *data)
 }
 
 
+static void eap_ttls_process_version(struct eap_sm *sm,
+                                    struct eap_ttls_data *data,
+                                    int peer_version)
+{
+       if (peer_version < data->ttls_version) {
+               wpa_printf(MSG_DEBUG, "EAP-TTLS: peer ver=%d, own ver=%d; "
+                          "use version %d",
+                          peer_version, data->ttls_version, peer_version);
+               data->ttls_version = peer_version;
+       }
+
+       if (data->ttls_version > 0 && !data->tls_ia_configured) {
+               if (tls_connection_set_ia(sm->ssl_ctx, data->ssl.conn, 1)) {
+                       wpa_printf(MSG_INFO, "EAP-TTLS: Failed to enable "
+                                  "TLS/IA");
+                       eap_ttls_state(data, FAILURE);
+                       return;
+               }
+               data->tls_ia_configured = 1;
+       }
+}
+
+
 static void eap_ttls_process(struct eap_sm *sm, void *priv,
                             struct wpabuf *respData)
 {
@@ -1320,8 +1302,7 @@ static void eap_ttls_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_TTLS, respData,
                               &left);
@@ -1332,59 +1313,25 @@ static void eap_ttls_process(struct eap_sm *sm, void *priv,
        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",
-                          peer_version, data->ttls_version, peer_version);
-               data->ttls_version = peer_version;
-       }
 
-       if (data->ttls_version > 0 && !data->tls_ia_configured) {
-               if (tls_connection_set_ia(sm->ssl_ctx, data->ssl.conn, 1)) {
-                       wpa_printf(MSG_INFO, "EAP-TTLS: Failed to enable "
-                                  "TLS/IA");
-                       eap_ttls_state(data, FAILURE);
-                       return;
-               }
-               data->tls_ia_configured = 1;
-       }
+       eap_ttls_process_version(sm, data, flags & EAP_PEAP_VERSION_MASK);
 
-       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;
-       }
+       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);
 }