EAP-FAST: Cleaned up TLV processing and added support for EAP Sequences
authorJouni Malinen <j@w1.fi>
Thu, 28 Feb 2008 01:59:34 +0000 (17:59 -0800)
committerJouni Malinen <j@w1.fi>
Thu, 28 Feb 2008 01:59:34 +0000 (17:59 -0800)
Number of TLVs were processed in groups and these cases were now separated
into more flexible processing of one TLV at the time. wpabuf_concat()
function was added to make it easier to concatenate TLVs. EAP Sequences are
now supported in both server and peer code, but the server side is not
enabled by default.

hostapd/ChangeLog
src/eap_peer/eap_fast.c
src/eap_server/eap_fast.c
src/utils/wpabuf.c
src/utils/wpabuf.h
wpa_supplicant/ChangeLog

index 5e124ab..9601112 100644 (file)
@@ -3,6 +3,7 @@ ChangeLog for hostapd
 ????-??-?? - v0.6.4
        * added peer identity into EAP-FAST PAC-Opaque and skip Phase 2
          Identity Request if identity is already known
+       * added support for EAP Sequences in EAP-FAST Phase 2
 
 2008-02-22 - v0.6.3
        * fixed Reassociation Response callback processing when using internal
index a94ed6c..bf94e0f 100644 (file)
@@ -706,17 +706,16 @@ static u8 * eap_fast_write_pac_request(u8 *pos, u16 pac_type)
 static struct wpabuf * eap_fast_process_crypto_binding(
        struct eap_sm *sm, struct eap_fast_data *data,
        struct eap_method_ret *ret,
-       struct eap_tlv_crypto_binding_tlv *_bind, size_t bind_len, int final)
+       struct eap_tlv_crypto_binding_tlv *_bind, size_t bind_len)
 {
        struct wpabuf *resp;
        u8 *pos;
-       struct eap_tlv_intermediate_result_tlv *rresult;
        u8 cmk[EAP_FAST_CMK_LEN], cmac[SHA1_MAC_LEN];
-       int res, req_tunnel_pac = 0;
+       int res;
        size_t len;
 
        if (eap_fast_validate_crypto_binding(_bind) < 0)
-               return eap_fast_tlv_result(EAP_TLV_RESULT_FAILURE, 1);
+               return NULL;
 
        if (eap_fast_get_cmk(sm, data, cmk) < 0)
                return NULL;
@@ -735,9 +734,8 @@ static struct wpabuf * eap_fast_process_crypto_binding(
                    _bind->compound_mac, sizeof(cmac));
        if (res != 0) {
                wpa_printf(MSG_INFO, "EAP-FAST: Compound MAC did not match");
-               resp = eap_fast_tlv_result(EAP_TLV_RESULT_FAILURE, 1);
                os_memcpy(_bind->compound_mac, cmac, sizeof(cmac));
-               return resp;
+               return NULL;
        }
 
        /*
@@ -745,73 +743,25 @@ static struct wpabuf * eap_fast_process_crypto_binding(
         * crypto binding to allow server to complete authentication.
         */
 
-       if (data->current_pac == NULL && data->provisioning &&
-           !data->anon_provisioning) {
-               /*
-                * Need to request Tunnel PAC when using authenticated
-                * provisioning.
-                */
-               wpa_printf(MSG_DEBUG, "EAP-FAST: Request Tunnel PAC");
-               req_tunnel_pac = 1;
-       }
-
-       len = sizeof(*rresult) + sizeof(struct eap_tlv_crypto_binding_tlv);
-       if (req_tunnel_pac)
-               len += sizeof(struct eap_tlv_hdr) +
-                       sizeof(struct eap_tlv_request_action_tlv) +
-                       sizeof(struct eap_tlv_pac_type_tlv);
+       len = sizeof(struct eap_tlv_crypto_binding_tlv);
        resp = wpabuf_alloc(len);
        if (resp == NULL)
                return NULL;
 
-       /*
-        * Both intermediate and final Result TLVs are identical, so ok to use
-        * the same structure definition for them.
-        */
-       rresult = wpabuf_put(resp, sizeof(*rresult));
-       rresult->tlv_type = host_to_be16(EAP_TLV_TYPE_MANDATORY |
-                                        (final ? EAP_TLV_RESULT_TLV :
-                                         EAP_TLV_INTERMEDIATE_RESULT_TLV));
-       rresult->length = host_to_be16(2);
-       rresult->status = host_to_be16(EAP_TLV_RESULT_SUCCESS);
-
        if (!data->anon_provisioning && data->phase2_success &&
            eap_fast_derive_msk(data) < 0) {
                wpa_printf(MSG_INFO, "EAP-FAST: Failed to generate MSK");
                ret->methodState = METHOD_DONE;
                ret->decision = DECISION_FAIL;
-               rresult->status = host_to_be16(EAP_TLV_RESULT_FAILURE);
                data->phase2_success = 0;
+               wpabuf_free(resp);
+               return NULL;
        }
 
        pos = wpabuf_put(resp, sizeof(struct eap_tlv_crypto_binding_tlv));
        eap_fast_write_crypto_binding((struct eap_tlv_crypto_binding_tlv *)
                                      pos, _bind, cmk);
 
-       if (req_tunnel_pac) {
-               u8 *pos2;
-               pos = wpabuf_put(resp, 0);
-               pos2 = eap_fast_write_pac_request(pos, PAC_TYPE_TUNNEL_PAC);
-               wpabuf_put(resp, pos2 - pos);
-       }
-
-       if (final && data->phase2_success) {
-               if (data->anon_provisioning) {
-                       wpa_printf(MSG_DEBUG, "EAP-FAST: Unauthenticated "
-                                  "provisioning completed successfully.");
-                       ret->methodState = METHOD_DONE;
-                       ret->decision = DECISION_FAIL;
-               } else {
-                       wpa_printf(MSG_DEBUG, "EAP-FAST: Authentication "
-                                  "completed successfully.");
-                       if (data->provisioning)
-                               ret->methodState = METHOD_MAY_CONT;
-                       else
-                               ret->methodState = METHOD_DONE;
-                       ret->decision = DECISION_UNCOND_SUCC;
-               }
-       }
-
        return resp;
 }
 
@@ -1028,7 +978,7 @@ static struct wpabuf * eap_fast_process_pac(struct eap_sm *sm,
        os_memset(&entry, 0, sizeof(entry));
        if (eap_fast_process_pac_tlv(&entry, pac, pac_len) ||
            eap_fast_process_pac_info(&entry))
-               return eap_fast_tlv_result(EAP_TLV_RESULT_FAILURE, 0);
+               return NULL;
 
        eap_fast_add_pac(&data->pac, &data->current_pac, &entry);
        eap_fast_pac_list_truncate(data->pac, data->max_pac_list_len);
@@ -1140,6 +1090,24 @@ static int eap_fast_encrypt_response(struct eap_sm *sm,
 }
 
 
+static struct wpabuf * eap_fast_pac_request(void)
+{
+       struct wpabuf *tmp;
+       u8 *pos, *pos2;
+
+       tmp = wpabuf_alloc(sizeof(struct eap_tlv_hdr) +
+                          sizeof(struct eap_tlv_request_action_tlv) +
+                          sizeof(struct eap_tlv_pac_type_tlv));
+       if (tmp == NULL)
+               return NULL;
+
+       pos = wpabuf_put(tmp, 0);
+       pos2 = eap_fast_write_pac_request(pos, PAC_TYPE_TUNNEL_PAC);
+       wpabuf_put(tmp, pos2 - pos);
+       return tmp;
+}
+
+
 static int eap_fast_process_decrypted(struct eap_sm *sm,
                                      struct eap_fast_data *data,
                                      struct eap_method_ret *ret,
@@ -1147,8 +1115,9 @@ static int eap_fast_process_decrypted(struct eap_sm *sm,
                                      struct wpabuf *decrypted,
                                      struct wpabuf **out_data)
 {
-       struct wpabuf *resp = NULL;
+       struct wpabuf *resp = NULL, *tmp;
        struct eap_fast_tlv_parse tlv;
+       int failed = 0;
 
        if (eap_fast_parse_decrypted(decrypted, &tlv, &resp) < 0)
                return 0;
@@ -1168,43 +1137,84 @@ static int eap_fast_process_decrypted(struct eap_sm *sm,
                                                 req->identifier, out_data);
        }
 
+       if (tlv.crypto_binding) {
+               tmp = eap_fast_process_crypto_binding(sm, data, ret,
+                                                     tlv.crypto_binding,
+                                                     tlv.crypto_binding_len);
+               if (tmp == NULL)
+                       failed = 1;
+               else
+                       resp = wpabuf_concat(resp, tmp);
+       }
+
+       if (tlv.iresult == EAP_TLV_RESULT_SUCCESS) {
+               tmp = eap_fast_tlv_result(failed ? EAP_TLV_RESULT_FAILURE :
+                                         EAP_TLV_RESULT_SUCCESS, 1);
+               resp = wpabuf_concat(resp, tmp);
+       }
+
        if (tlv.eap_payload_tlv) {
-               resp = eap_fast_process_eap_payload_tlv(
+               tmp = eap_fast_process_eap_payload_tlv(
                        sm, data, ret, req, tlv.eap_payload_tlv,
                        tlv.eap_payload_tlv_len);
-               return eap_fast_encrypt_response(sm, data, resp,
-                                                req->identifier, out_data);
-       }
-
-       if (tlv.crypto_binding) {
-               int final = tlv.result == EAP_TLV_RESULT_SUCCESS;
-               resp = eap_fast_process_crypto_binding(sm, data, ret,
-                                                      tlv.crypto_binding,
-                                                      tlv.crypto_binding_len,
-                                                      final);
-               return eap_fast_encrypt_response(sm, data, resp,
-                                                req->identifier, out_data);
+               resp = wpabuf_concat(resp, tmp);
        }
 
        if (tlv.pac && tlv.result != EAP_TLV_RESULT_SUCCESS) {
                wpa_printf(MSG_DEBUG, "EAP-FAST: PAC TLV without Result TLV "
                           "acknowledging success");
-               resp = eap_fast_tlv_result(EAP_TLV_RESULT_FAILURE, 0);
-               return eap_fast_encrypt_response(sm, data, resp,
-                                                req->identifier, out_data);
+               failed = 1;
+       } else if (tlv.pac && tlv.result == EAP_TLV_RESULT_SUCCESS) {
+               tmp = eap_fast_process_pac(sm, data, ret, tlv.pac,
+                                          tlv.pac_len);
+               resp = wpabuf_concat(resp, tmp);
        }
 
-       if (tlv.pac && tlv.result == EAP_TLV_RESULT_SUCCESS) {
-               resp = eap_fast_process_pac(sm, data, ret, tlv.pac,
-                                           tlv.pac_len);
-               return eap_fast_encrypt_response(sm, data, resp,
-                                                req->identifier, out_data);
+       if (data->current_pac == NULL && data->provisioning &&
+           !data->anon_provisioning) {
+               /*
+                * Need to request Tunnel PAC when using authenticated
+                * provisioning.
+                */
+               wpa_printf(MSG_DEBUG, "EAP-FAST: Request Tunnel PAC");
+               tmp = eap_fast_pac_request();
+               resp = wpabuf_concat(resp, tmp);
+       }
+
+       if (tlv.result == EAP_TLV_RESULT_SUCCESS && !failed) {
+               tmp = eap_fast_tlv_result(EAP_TLV_RESULT_SUCCESS, 0);
+               resp = wpabuf_concat(resp, tmp);
+       } else if (failed) {
+               tmp = eap_fast_tlv_result(EAP_TLV_RESULT_FAILURE, 0);
+               resp = wpabuf_concat(resp, tmp);
+       }
+
+       if (resp && tlv.result == EAP_TLV_RESULT_SUCCESS && !failed &&
+           tlv.crypto_binding && data->phase2_success) {
+               if (data->anon_provisioning) {
+                       wpa_printf(MSG_DEBUG, "EAP-FAST: Unauthenticated "
+                                  "provisioning completed successfully.");
+                       ret->methodState = METHOD_DONE;
+                       ret->decision = DECISION_FAIL;
+               } else {
+                       wpa_printf(MSG_DEBUG, "EAP-FAST: Authentication "
+                                  "completed successfully.");
+                       if (data->provisioning)
+                               ret->methodState = METHOD_MAY_CONT;
+                       else
+                               ret->methodState = METHOD_DONE;
+                       ret->decision = DECISION_UNCOND_SUCC;
+               }
+       }
+
+       if (resp == NULL) {
+               wpa_printf(MSG_DEBUG, "EAP-FAST: No recognized TLVs - send "
+                          "empty response packet");
+               resp = wpabuf_alloc(1);
        }
 
-       wpa_printf(MSG_DEBUG, "EAP-FAST: No recognized TLVs - send "
-                  "empty response packet");
-       return eap_fast_encrypt_response(sm, data, wpabuf_alloc(1),
-                                        req->identifier, out_data);
+       return eap_fast_encrypt_response(sm, data, resp, req->identifier,
+                                        out_data);
 }
 
 
index a7ade96..170f3fa 100644 (file)
@@ -74,6 +74,7 @@ struct eap_fast_data {
        struct wpabuf *pending_phase2_resp;
        u8 *identity; /* from PAC-Opaque */
        size_t identity_len;
+       int eap_seq;
 };
 
 
@@ -614,26 +615,39 @@ static struct wpabuf * eap_fast_build_crypto_binding(
        struct wpabuf *buf;
        struct eap_tlv_result_tlv *result;
        struct eap_tlv_crypto_binding_tlv *binding;
-       int type;
 
-       buf = wpabuf_alloc(sizeof(*result) + sizeof(*binding));
+       buf = wpabuf_alloc(2 * sizeof(*result) + sizeof(*binding));
        if (buf == NULL)
                return NULL;
 
-       if (data->send_new_pac || data->anon_provisioning) {
-               type = EAP_TLV_INTERMEDIATE_RESULT_TLV;
+       if (data->send_new_pac || data->anon_provisioning ||
+           data->phase2_method)
                data->final_result = 0;
-       } else {
-               type = EAP_TLV_RESULT_TLV;
+       else
                data->final_result = 1;
-       }
 
-       /* Result TLV */
-       wpa_printf(MSG_DEBUG, "EAP-FAST: Add Result TLV (status=SUCCESS)");
-       result = wpabuf_put(buf, sizeof(*result));
-       result->tlv_type = host_to_be16(EAP_TLV_TYPE_MANDATORY | type);
-       result->length = host_to_be16(2);
-       result->status = host_to_be16(EAP_TLV_RESULT_SUCCESS);
+       if (!data->final_result || data->eap_seq > 1) {
+               /* Intermediate-Result */
+               wpa_printf(MSG_DEBUG, "EAP-FAST: Add Intermediate-Result TLV "
+                          "(status=SUCCESS)");
+               result = wpabuf_put(buf, sizeof(*result));
+               result->tlv_type = host_to_be16(
+                       EAP_TLV_TYPE_MANDATORY |
+                       EAP_TLV_INTERMEDIATE_RESULT_TLV);
+               result->length = host_to_be16(2);
+               result->status = host_to_be16(EAP_TLV_RESULT_SUCCESS);
+       }
+
+       if (data->final_result) {
+               /* Result TLV */
+               wpa_printf(MSG_DEBUG, "EAP-FAST: Add Result TLV "
+                          "(status=SUCCESS)");
+               result = wpabuf_put(buf, sizeof(*result));
+               result->tlv_type = host_to_be16(EAP_TLV_TYPE_MANDATORY |
+                                               EAP_TLV_RESULT_TLV);
+               result->length = host_to_be16(2);
+               result->status = host_to_be16(EAP_TLV_RESULT_SUCCESS);
+       }
 
        /* Crypto-Binding TLV */
        binding = wpabuf_put(buf, sizeof(*binding));
@@ -828,6 +842,16 @@ static struct wpabuf * eap_fast_buildReq(struct eap_sm *sm, void *priv, u8 id)
                break;
        case CRYPTO_BINDING:
                req = eap_fast_build_crypto_binding(sm, data);
+               if (data->phase2_method) {
+                       /*
+                        * Include the start of the next EAP method in the
+                        * sequence in the same message with Crypto-Binding to
+                        * save a round-trip.
+                        */
+                       struct wpabuf *eap;
+                       eap = eap_fast_build_phase2_req(sm, data, id);
+                       req = wpabuf_concat(req, eap);
+               }
                break;
        case REQUEST_PAC:
                req = eap_fast_build_pac(sm, data);
@@ -981,9 +1005,13 @@ static void eap_fast_process_phase2_response(struct eap_sm *sm,
                wpa_printf(MSG_DEBUG, "EAP-FAST: try EAP type %d", next_type);
                break;
        case PHASE2_METHOD:
+       case CRYPTO_BINDING:
                eap_fast_update_icmk(sm, data);
                eap_fast_state(data, CRYPTO_BINDING);
+               data->eap_seq++;
                next_type = EAP_TYPE_NONE;
+               /* TODO: could start another EAP method in sequence by setting
+                * next_type to the selected method */
                break;
        case FAILURE:
                break;
@@ -1199,11 +1227,6 @@ static void eap_fast_process_phase2_tlvs(struct eap_sm *sm,
                return;
        }
 
-       if (tlv.eap_payload_tlv) {
-               eap_fast_process_phase2_eap(sm, data, tlv.eap_payload_tlv,
-                                           tlv.eap_payload_tlv_len);
-       }
-
        if (check_crypto_binding) {
                if (tlv.crypto_binding == NULL) {
                        wpa_printf(MSG_DEBUG, "EAP-FAST: No Crypto-Binding "
@@ -1235,7 +1258,11 @@ static void eap_fast_process_phase2_tlvs(struct eap_sm *sm,
                }
 
                wpa_printf(MSG_DEBUG, "EAP-FAST: Valid Crypto-Binding TLV "
-                          "received - authentication completed successfully");
+                          "received");
+               if (data->final_result) {
+                       wpa_printf(MSG_DEBUG, "EAP-FAST: Authentication "
+                                  "completed successfully");
+               }
 
                if (data->anon_provisioning ||
                    (tlv.request_action == EAP_TLV_ACTION_PROCESS_TLV &&
@@ -1248,9 +1275,14 @@ static void eap_fast_process_phase2_tlvs(struct eap_sm *sm,
                        wpa_printf(MSG_DEBUG, "EAP-FAST: Server triggered "
                                   "re-keying of Tunnel PAC");
                        eap_fast_state(data, REQUEST_PAC);
-               } else
+               } else if (data->final_result)
                        eap_fast_state(data, SUCCESS);
        }
+
+       if (tlv.eap_payload_tlv) {
+               eap_fast_process_phase2_eap(sm, data, tlv.eap_payload_tlv,
+                                           tlv.eap_payload_tlv_len);
+       }
 }
 
 
index 5b50b30..3719aae 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * Dynamic data buffer
- * Copyright (c) 2007, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2007-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
@@ -123,3 +123,40 @@ void * wpabuf_put(struct wpabuf *buf, size_t len)
        }
        return tmp;
 }
+
+
+/**
+ * wpabuf_concat - Concatenate two buffers into a newly allocated one
+ * @a: First buffer
+ * @b: Second buffer
+ * Returns: wpabuf with concatenated a + b data or %NULL on failure
+ *
+ * Both buffers a and b will be freed regardless of the return value. Input
+ * buffers can be %NULL which is interpreted as an empty buffer.
+ */
+struct wpabuf * wpabuf_concat(struct wpabuf *a, struct wpabuf *b)
+{
+       struct wpabuf *n = NULL;
+       size_t len = 0;
+
+       if (b == NULL)
+               return a;
+
+       if (a)
+               len += wpabuf_len(a);
+       if (b)
+               len += wpabuf_len(b);
+
+       n = wpabuf_alloc(len);
+       if (n) {
+               if (a)
+                       wpabuf_put_buf(n, a);
+               if (b)
+                       wpabuf_put_buf(n, b);
+       }
+
+       wpabuf_free(a);
+       wpabuf_free(b);
+
+       return n;
+}
index 22f2794..724412e 100644 (file)
@@ -36,6 +36,7 @@ struct wpabuf * wpabuf_alloc_copy(const void *data, size_t len);
 struct wpabuf * wpabuf_dup(const struct wpabuf *src);
 void wpabuf_free(struct wpabuf *buf);
 void * wpabuf_put(struct wpabuf *buf, size_t len);
+struct wpabuf * wpabuf_concat(struct wpabuf *a, struct wpabuf *b);
 
 
 /**
index 969b858..8e80802 100644 (file)
@@ -1,5 +1,8 @@
 ChangeLog for wpa_supplicant
 
+????-??-?? - v0.6.4
+       * added support for EAP Sequences in EAP-FAST Phase 2
+
 2008-02-22 - v0.6.3
        * removed 'nai' and 'eappsk' network configuration variables that were
          previously used for configuring user identity and key for EAP-PSK,