Redesigned EAP-TLS/PEAP/TTLS/FAST fragmentation/reassembly
[mech_eap.git] / src / eap_server / eap_ttls.c
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);
 }