]> git.ipfire.org Git - thirdparty/hostap.git/blobdiff - src/eap_peer/eap_tls.c
EAP-TEAP peer: Add support for machine credentials using certificates
[thirdparty/hostap.git] / src / eap_peer / eap_tls.c
index bb9f3f261149318219c24f3dc248c0485209f4fe..d9771f601acebbac144021459c523a113b9911b4 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * EAP peer method: EAP-TLS (RFC 2716)
- * Copyright (c) 2004-2008, 2012, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2008, 2012-2019, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -25,6 +25,7 @@ struct eap_tls_data {
        size_t id_len;
        void *ssl_ctx;
        u8 eap_type;
+       struct wpabuf *pending_resp;
 };
 
 
@@ -32,10 +33,17 @@ static void * eap_tls_init(struct eap_sm *sm)
 {
        struct eap_tls_data *data;
        struct eap_peer_config *config = eap_get_config(sm);
-       if (config == NULL ||
-           ((sm->init_phase2 ? config->private_key2 : config->private_key)
-            == NULL &&
-            (sm->init_phase2 ? config->engine2 : config->engine) == 0)) {
+       struct eap_peer_cert_config *cert;
+
+       if (!config)
+               return NULL;
+       if (!sm->init_phase2)
+               cert = &config->cert;
+       else if (sm->use_machine_cred)
+               cert = &config->machine_cert;
+       else
+               cert = &config->phase2_cert;
+       if (!cert->private_key && cert->engine == 0) {
                wpa_printf(MSG_INFO, "EAP-TLS: Private key not configured");
                return NULL;
        }
@@ -50,13 +58,12 @@ static void * eap_tls_init(struct eap_sm *sm)
        if (eap_peer_tls_ssl_init(sm, &data->ssl, config, EAP_TYPE_TLS)) {
                wpa_printf(MSG_INFO, "EAP-TLS: Failed to initialize SSL.");
                eap_tls_deinit(sm, data);
-               if (config->engine) {
+               if (cert->engine) {
                        wpa_printf(MSG_DEBUG, "EAP-TLS: Requesting Smartcard "
                                   "PIN");
                        eap_sm_request_pin(sm);
                        sm->ignore = TRUE;
-               } else if (config->private_key && !config->private_key_passwd)
-               {
+               } else if (cert->private_key && !cert->private_key_passwd) {
                        wpa_printf(MSG_DEBUG, "EAP-TLS: Requesting private "
                                   "key passphrase");
                        eap_sm_request_passphrase(sm);
@@ -125,14 +132,24 @@ static void * eap_wfa_unauth_tls_init(struct eap_sm *sm)
 #endif /* CONFIG_HS20 */
 
 
+static void eap_tls_free_key(struct eap_tls_data *data)
+{
+       if (data->key_data) {
+               bin_clear_free(data->key_data, EAP_TLS_KEY_LEN + EAP_EMSK_LEN);
+               data->key_data = NULL;
+       }
+}
+
+
 static void eap_tls_deinit(struct eap_sm *sm, void *priv)
 {
        struct eap_tls_data *data = priv;
        if (data == NULL)
                return;
        eap_peer_tls_ssl_deinit(sm, &data->ssl);
-       os_free(data->key_data);
+       eap_tls_free_key(data);
        os_free(data->session_id);
+       wpabuf_free(data->pending_resp);
        os_free(data);
 }
 
@@ -147,20 +164,6 @@ static struct wpabuf * eap_tls_failure(struct eap_sm *sm,
        ret->methodState = METHOD_DONE;
        ret->decision = DECISION_FAIL;
 
-       if (res == -1) {
-               struct eap_peer_config *config = eap_get_config(sm);
-               if (config) {
-                       /*
-                        * The TLS handshake failed. So better forget the old
-                        * PIN. It may be wrong, we cannot be sure but trying
-                        * the wrong one again might block it on the card--so
-                        * better ask the user again.
-                        */
-                       os_free(config->pin);
-                       config->pin = NULL;
-               }
-       }
-
        if (resp) {
                /*
                 * This is likely an alert message, so send it instead of just
@@ -176,14 +179,37 @@ static struct wpabuf * eap_tls_failure(struct eap_sm *sm,
 static void eap_tls_success(struct eap_sm *sm, struct eap_tls_data *data,
                            struct eap_method_ret *ret)
 {
+       const char *label;
+       const u8 eap_tls13_context[] = { EAP_TYPE_TLS };
+       const u8 *context = NULL;
+       size_t context_len = 0;
+
        wpa_printf(MSG_DEBUG, "EAP-TLS: Done");
 
-       ret->methodState = METHOD_DONE;
-       ret->decision = DECISION_UNCOND_SUCC;
+       if (data->ssl.tls_out) {
+               wpa_printf(MSG_DEBUG, "EAP-TLS: Fragment(s) remaining");
+               return;
+       }
+
+       if (data->ssl.tls_v13) {
+               label = "EXPORTER_EAP_TLS_Key_Material";
+               context = eap_tls13_context;
+               context_len = 1;
+
+               /* A possible NewSessionTicket may be received before
+                * EAP-Success, so need to allow it to be received. */
+               ret->methodState = METHOD_MAY_CONT;
+               ret->decision = DECISION_COND_SUCC;
+       } else {
+               label = "client EAP encryption";
 
-       os_free(data->key_data);
-       data->key_data = eap_peer_tls_derive_key(sm, &data->ssl,
-                                                "client EAP encryption",
+               ret->methodState = METHOD_DONE;
+               ret->decision = DECISION_UNCOND_SUCC;
+       }
+
+       eap_tls_free_key(data);
+       data->key_data = eap_peer_tls_derive_key(sm, &data->ssl, label,
+                                                context, context_len,
                                                 EAP_TLS_KEY_LEN +
                                                 EAP_EMSK_LEN);
        if (data->key_data) {
@@ -219,6 +245,33 @@ static struct wpabuf * eap_tls_process(struct eap_sm *sm, void *priv,
        u8 flags, id;
        const u8 *pos;
        struct eap_tls_data *data = priv;
+       struct wpabuf msg;
+
+       if (sm->waiting_ext_cert_check && data->pending_resp) {
+               struct eap_peer_config *config = eap_get_config(sm);
+
+               if (config->pending_ext_cert_check == EXT_CERT_CHECK_GOOD) {
+                       wpa_printf(MSG_DEBUG,
+                                  "EAP-TLS: External certificate check succeeded - continue handshake");
+                       resp = data->pending_resp;
+                       data->pending_resp = NULL;
+                       sm->waiting_ext_cert_check = 0;
+                       return resp;
+               }
+
+               if (config->pending_ext_cert_check == EXT_CERT_CHECK_BAD) {
+                       wpa_printf(MSG_DEBUG,
+                                  "EAP-TLS: External certificate check failed - force authentication failure");
+                       ret->methodState = METHOD_DONE;
+                       ret->decision = DECISION_FAIL;
+                       sm->waiting_ext_cert_check = 0;
+                       return NULL;
+               }
+
+               wpa_printf(MSG_DEBUG,
+                          "EAP-TLS: Continuing to wait external server certificate validation");
+               return NULL;
+       }
 
        pos = eap_peer_tls_process_init(sm, &data->ssl, data->eap_type, ret,
                                        reqData, &left, &flags);
@@ -233,13 +286,34 @@ static struct wpabuf * eap_tls_process(struct eap_sm *sm, void *priv,
        }
 
        resp = NULL;
+       wpabuf_set(&msg, pos, left);
        res = eap_peer_tls_process_helper(sm, &data->ssl, data->eap_type, 0,
-                                         id, pos, left, &resp);
+                                         id, &msg, &resp);
 
        if (res < 0) {
                return eap_tls_failure(sm, data, ret, res, resp, id);
        }
 
+       if (sm->waiting_ext_cert_check) {
+               wpa_printf(MSG_DEBUG,
+                          "EAP-TLS: Waiting external server certificate validation");
+               wpabuf_free(data->pending_resp);
+               data->pending_resp = resp;
+               return NULL;
+       }
+
+       if (res == 2) {
+               /* Application data included in the handshake message (used by
+                * EAP-TLS 1.3 to indicate conclusion of the exchange). */
+               wpa_hexdump_buf(MSG_DEBUG, "EAP-TLS: Received Application Data",
+                               resp);
+               wpa_hexdump_buf(MSG_DEBUG, "EAP-TLS: Remaining tls_out data",
+                               data->ssl.tls_out);
+               eap_peer_tls_reset_output(&data->ssl);
+               /* Send an ACK to allow the server to complete exchange */
+               res = 1;
+       }
+
        if (tls_connection_established(data->ssl_ctx, data->ssl.conn))
                eap_tls_success(sm, data, ret);
 
@@ -261,14 +335,17 @@ static Boolean eap_tls_has_reauth_data(struct eap_sm *sm, void *priv)
 
 static void eap_tls_deinit_for_reauth(struct eap_sm *sm, void *priv)
 {
+       struct eap_tls_data *data = priv;
+
+       wpabuf_free(data->pending_resp);
+       data->pending_resp = NULL;
 }
 
 
 static void * eap_tls_init_for_reauth(struct eap_sm *sm, void *priv)
 {
        struct eap_tls_data *data = priv;
-       os_free(data->key_data);
-       data->key_data = NULL;
+       eap_tls_free_key(data);
        os_free(data->session_id);
        data->session_id = NULL;
        if (eap_peer_tls_reauth_init(sm, &data->ssl)) {
@@ -302,12 +379,11 @@ static u8 * eap_tls_getKey(struct eap_sm *sm, void *priv, size_t *len)
        if (data->key_data == NULL)
                return NULL;
 
-       key = os_malloc(EAP_TLS_KEY_LEN);
+       key = os_memdup(data->key_data, EAP_TLS_KEY_LEN);
        if (key == NULL)
                return NULL;
 
        *len = EAP_TLS_KEY_LEN;
-       os_memcpy(key, data->key_data, EAP_TLS_KEY_LEN);
 
        return key;
 }
@@ -321,12 +397,11 @@ static u8 * eap_tls_get_emsk(struct eap_sm *sm, void *priv, size_t *len)
        if (data->key_data == NULL)
                return NULL;
 
-       key = os_malloc(EAP_EMSK_LEN);
+       key = os_memdup(data->key_data + EAP_TLS_KEY_LEN, EAP_EMSK_LEN);
        if (key == NULL)
                return NULL;
 
        *len = EAP_EMSK_LEN;
-       os_memcpy(key, data->key_data + EAP_TLS_KEY_LEN, EAP_EMSK_LEN);
 
        return key;
 }
@@ -340,12 +415,11 @@ static u8 * eap_tls_get_session_id(struct eap_sm *sm, void *priv, size_t *len)
        if (data->session_id == NULL)
                return NULL;
 
-       id = os_malloc(data->id_len);
+       id = os_memdup(data->session_id, data->id_len);
        if (id == NULL)
                return NULL;
 
        *len = data->id_len;
-       os_memcpy(id, data->session_id, data->id_len);
 
        return id;
 }
@@ -354,7 +428,6 @@ static u8 * eap_tls_get_session_id(struct eap_sm *sm, void *priv, size_t *len)
 int eap_peer_tls_register(void)
 {
        struct eap_method *eap;
-       int ret;
 
        eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION,
                                    EAP_VENDOR_IETF, EAP_TYPE_TLS, "TLS");
@@ -373,10 +446,7 @@ int eap_peer_tls_register(void)
        eap->init_for_reauth = eap_tls_init_for_reauth;
        eap->get_emsk = eap_tls_get_emsk;
 
-       ret = eap_peer_method_register(eap);
-       if (ret)
-               eap_peer_method_free(eap);
-       return ret;
+       return eap_peer_method_register(eap);
 }
 
 
@@ -384,7 +454,6 @@ int eap_peer_tls_register(void)
 int eap_peer_unauth_tls_register(void)
 {
        struct eap_method *eap;
-       int ret;
 
        eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION,
                                    EAP_VENDOR_UNAUTH_TLS,
@@ -403,10 +472,7 @@ int eap_peer_unauth_tls_register(void)
        eap->init_for_reauth = eap_tls_init_for_reauth;
        eap->get_emsk = eap_tls_get_emsk;
 
-       ret = eap_peer_method_register(eap);
-       if (ret)
-               eap_peer_method_free(eap);
-       return ret;
+       return eap_peer_method_register(eap);
 }
 #endif /* EAP_UNAUTH_TLS */
 
@@ -415,7 +481,6 @@ int eap_peer_unauth_tls_register(void)
 int eap_peer_wfa_unauth_tls_register(void)
 {
        struct eap_method *eap;
-       int ret;
 
        eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION,
                                    EAP_VENDOR_WFA_NEW,
@@ -435,9 +500,6 @@ int eap_peer_wfa_unauth_tls_register(void)
        eap->init_for_reauth = eap_tls_init_for_reauth;
        eap->get_emsk = eap_tls_get_emsk;
 
-       ret = eap_peer_method_register(eap);
-       if (ret)
-               eap_peer_method_free(eap);
-       return ret;
+       return eap_peer_method_register(eap);
 }
 #endif /* CONFIG_HS20 */