Updated through tag hostap_2_5 from git://w1.fi/hostap.git
[mech_eap.git] / libeap / src / eap_server / eap_server_tls.c
index c98fa18..bd18a4b 100644 (file)
@@ -2,14 +2,8 @@
  * hostapd / EAP-TLS (RFC 2716)
  * 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
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
  */
 
 #include "includes.h"
@@ -27,6 +21,7 @@ struct eap_tls_data {
        struct eap_ssl_data ssl;
        enum { START, CONTINUE, SUCCESS, FAILURE } state;
        int established;
+       u8 eap_type;
 };
 
 
@@ -53,6 +48,23 @@ static void eap_tls_state(struct eap_tls_data *data, int state)
                   eap_tls_state_txt(data->state),
                   eap_tls_state_txt(state));
        data->state = state;
+       if (state == FAILURE)
+               tls_connection_remove_session(data->ssl.conn);
+}
+
+
+static void eap_tls_valid_session(struct eap_sm *sm, struct eap_tls_data *data)
+{
+       struct wpabuf *buf;
+
+       if (!sm->tls_session_lifetime)
+               return;
+
+       buf = wpabuf_alloc(1);
+       if (!buf)
+               return;
+       wpabuf_put_u8(buf, data->eap_type);
+       tls_connection_set_success_data(data->ssl.conn, buf);
 }
 
 
@@ -65,14 +77,61 @@ static void * eap_tls_init(struct eap_sm *sm)
                return NULL;
        data->state = START;
 
-       if (eap_server_tls_ssl_init(sm, &data->ssl, 1)) {
+       if (eap_server_tls_ssl_init(sm, &data->ssl, 1, EAP_TYPE_TLS)) {
+               wpa_printf(MSG_INFO, "EAP-TLS: Failed to initialize SSL.");
+               eap_tls_reset(sm, data);
+               return NULL;
+       }
+
+       data->eap_type = EAP_TYPE_TLS;
+
+       return data;
+}
+
+
+#ifdef EAP_SERVER_UNAUTH_TLS
+static void * eap_unauth_tls_init(struct eap_sm *sm)
+{
+       struct eap_tls_data *data;
+
+       data = os_zalloc(sizeof(*data));
+       if (data == NULL)
+               return NULL;
+       data->state = START;
+
+       if (eap_server_tls_ssl_init(sm, &data->ssl, 0, EAP_UNAUTH_TLS_TYPE)) {
                wpa_printf(MSG_INFO, "EAP-TLS: Failed to initialize SSL.");
                eap_tls_reset(sm, data);
                return NULL;
        }
 
+       data->eap_type = EAP_UNAUTH_TLS_TYPE;
        return data;
 }
+#endif /* EAP_SERVER_UNAUTH_TLS */
+
+
+#ifdef CONFIG_HS20
+static void * eap_wfa_unauth_tls_init(struct eap_sm *sm)
+{
+       struct eap_tls_data *data;
+
+       data = os_zalloc(sizeof(*data));
+       if (data == NULL)
+               return NULL;
+       data->state = START;
+
+       if (eap_server_tls_ssl_init(sm, &data->ssl, 0,
+                                   EAP_WFA_UNAUTH_TLS_TYPE)) {
+               wpa_printf(MSG_INFO, "EAP-TLS: Failed to initialize SSL.");
+               eap_tls_reset(sm, data);
+               return NULL;
+       }
+
+       data->eap_type = EAP_WFA_UNAUTH_TLS_TYPE;
+       return data;
+}
+#endif /* CONFIG_HS20 */
 
 
 static void eap_tls_reset(struct eap_sm *sm, void *priv)
@@ -90,8 +149,7 @@ static struct wpabuf * eap_tls_build_start(struct eap_sm *sm,
 {
        struct wpabuf *req;
 
-       req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_TLS, 1, EAP_CODE_REQUEST,
-                           id);
+       req = eap_tls_msg_alloc(data->eap_type, 1, EAP_CODE_REQUEST, id);
        if (req == NULL) {
                wpa_printf(MSG_ERROR, "EAP-TLS: Failed to allocate memory for "
                           "request");
@@ -113,11 +171,11 @@ static struct wpabuf * eap_tls_buildReq(struct eap_sm *sm, void *priv, u8 id)
        struct wpabuf *res;
 
        if (data->ssl.state == FRAG_ACK) {
-               return eap_server_tls_build_ack(id, EAP_TYPE_TLS, 0);
+               return eap_server_tls_build_ack(id, data->eap_type, 0);
        }
 
        if (data->ssl.state == WAIT_FRAG_ACK) {
-               res = eap_server_tls_build_msg(&data->ssl, EAP_TYPE_TLS, 0,
+               res = eap_server_tls_build_msg(&data->ssl, data->eap_type, 0,
                                               id);
                goto check_established;
        }
@@ -135,7 +193,7 @@ static struct wpabuf * eap_tls_buildReq(struct eap_sm *sm, void *priv, u8 id)
                return NULL;
        }
 
-       res = eap_server_tls_build_msg(&data->ssl, EAP_TYPE_TLS, 0, id);
+       res = eap_server_tls_build_msg(&data->ssl, data->eap_type, 0, id);
 
 check_established:
        if (data->established && data->ssl.state != WAIT_FRAG_ACK) {
@@ -143,6 +201,7 @@ check_established:
                 * fragments waiting to be sent out. */
                wpa_printf(MSG_DEBUG, "EAP-TLS: Done");
                eap_tls_state(data, SUCCESS);
+               eap_tls_valid_session(sm, data);
        }
 
        return res;
@@ -152,10 +211,21 @@ check_established:
 static Boolean eap_tls_check(struct eap_sm *sm, void *priv,
                             struct wpabuf *respData)
 {
+       struct eap_tls_data *data = priv;
        const u8 *pos;
        size_t len;
 
-       pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_TLS, respData, &len);
+       if (data->eap_type == EAP_UNAUTH_TLS_TYPE)
+               pos = eap_hdr_validate(EAP_VENDOR_UNAUTH_TLS,
+                                      EAP_VENDOR_TYPE_UNAUTH_TLS, respData,
+                                      &len);
+       else if (data->eap_type == EAP_WFA_UNAUTH_TLS_TYPE)
+               pos = eap_hdr_validate(EAP_VENDOR_WFA_NEW,
+                                      EAP_VENDOR_WFA_UNAUTH_TLS, respData,
+                                      &len);
+       else
+               pos = eap_hdr_validate(EAP_VENDOR_IETF, data->eap_type,
+                                      respData, &len);
        if (pos == NULL || len < 1) {
                wpa_printf(MSG_INFO, "EAP-TLS: Invalid frame");
                return TRUE;
@@ -183,10 +253,41 @@ static void eap_tls_process(struct eap_sm *sm, void *priv,
                            struct wpabuf *respData)
 {
        struct eap_tls_data *data = priv;
+       const struct wpabuf *buf;
+       const u8 *pos;
+
        if (eap_server_tls_process(sm, &data->ssl, respData, data,
-                                  EAP_TYPE_TLS, NULL, eap_tls_process_msg) <
-           0)
+                                  data->eap_type, NULL, eap_tls_process_msg) <
+           0) {
+               eap_tls_state(data, FAILURE);
+               return;
+       }
+
+       if (!tls_connection_established(sm->ssl_ctx, data->ssl.conn) ||
+           !tls_connection_resumed(sm->ssl_ctx, data->ssl.conn))
+               return;
+
+       buf = tls_connection_get_success_data(data->ssl.conn);
+       if (!buf || wpabuf_len(buf) < 1) {
+               wpa_printf(MSG_DEBUG,
+                          "EAP-TLS: No success data in resumed session - reject attempt");
                eap_tls_state(data, FAILURE);
+               return;
+       }
+
+       pos = wpabuf_head(buf);
+       if (*pos != data->eap_type) {
+               wpa_printf(MSG_DEBUG,
+                          "EAP-TLS: Resumed session for another EAP type (%u) - reject attempt",
+                          *pos);
+               eap_tls_state(data, FAILURE);
+               return;
+       }
+
+       wpa_printf(MSG_DEBUG,
+                  "EAP-TLS: Resuming previous session");
+       eap_tls_state(data, SUCCESS);
+       tls_connection_set_success_data_resumed(data->ssl.conn);
 }
 
 
@@ -236,7 +337,7 @@ static u8 * eap_tls_get_emsk(struct eap_sm *sm, void *priv, size_t *len)
                if (emsk)
                        os_memcpy(emsk, eapKeyData + EAP_TLS_KEY_LEN,
                                  EAP_EMSK_LEN);
-               os_free(eapKeyData);
+               bin_clear_free(eapKeyData, EAP_TLS_KEY_LEN + EAP_EMSK_LEN);
        } else
                emsk = NULL;
 
@@ -259,6 +360,18 @@ static Boolean eap_tls_isSuccess(struct eap_sm *sm, void *priv)
 }
 
 
+static u8 * eap_tls_get_session_id(struct eap_sm *sm, void *priv, size_t *len)
+{
+       struct eap_tls_data *data = priv;
+
+       if (data->state != SUCCESS)
+               return NULL;
+
+       return eap_server_tls_derive_session_id(sm, &data->ssl, EAP_TYPE_TLS,
+                                               len);
+}
+
+
 int eap_server_tls_register(void)
 {
        struct eap_method *eap;
@@ -278,9 +391,72 @@ int eap_server_tls_register(void)
        eap->getKey = eap_tls_getKey;
        eap->isSuccess = eap_tls_isSuccess;
        eap->get_emsk = eap_tls_get_emsk;
+       eap->getSessionId = eap_tls_get_session_id;
+
+       ret = eap_server_method_register(eap);
+       if (ret)
+               eap_server_method_free(eap);
+       return ret;
+}
+
+
+#ifdef EAP_SERVER_UNAUTH_TLS
+int eap_server_unauth_tls_register(void)
+{
+       struct eap_method *eap;
+       int ret;
+
+       eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION,
+                                     EAP_VENDOR_UNAUTH_TLS,
+                                     EAP_VENDOR_TYPE_UNAUTH_TLS,
+                                     "UNAUTH-TLS");
+       if (eap == NULL)
+               return -1;
+
+       eap->init = eap_unauth_tls_init;
+       eap->reset = eap_tls_reset;
+       eap->buildReq = eap_tls_buildReq;
+       eap->check = eap_tls_check;
+       eap->process = eap_tls_process;
+       eap->isDone = eap_tls_isDone;
+       eap->getKey = eap_tls_getKey;
+       eap->isSuccess = eap_tls_isSuccess;
+       eap->get_emsk = eap_tls_get_emsk;
+
+       ret = eap_server_method_register(eap);
+       if (ret)
+               eap_server_method_free(eap);
+       return ret;
+}
+#endif /* EAP_SERVER_UNAUTH_TLS */
+
+
+#ifdef CONFIG_HS20
+int eap_server_wfa_unauth_tls_register(void)
+{
+       struct eap_method *eap;
+       int ret;
+
+       eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION,
+                                     EAP_VENDOR_WFA_NEW,
+                                     EAP_VENDOR_WFA_UNAUTH_TLS,
+                                     "WFA-UNAUTH-TLS");
+       if (eap == NULL)
+               return -1;
+
+       eap->init = eap_wfa_unauth_tls_init;
+       eap->reset = eap_tls_reset;
+       eap->buildReq = eap_tls_buildReq;
+       eap->check = eap_tls_check;
+       eap->process = eap_tls_process;
+       eap->isDone = eap_tls_isDone;
+       eap->getKey = eap_tls_getKey;
+       eap->isSuccess = eap_tls_isSuccess;
+       eap->get_emsk = eap_tls_get_emsk;
 
        ret = eap_server_method_register(eap);
        if (ret)
                eap_server_method_free(eap);
        return ret;
 }
+#endif /* CONFIG_HS20 */