Merged the hostap_2.6 updates, and the Leap of Faith work, from the hostap_update...
[mech_eap.git] / libeap / src / eap_peer / eap_ttls.c
index ae15645..1e94345 100644 (file)
@@ -1,15 +1,9 @@
 /*
  * EAP peer method: EAP-TTLS (RFC 5281)
- * Copyright (c) 2004-2008, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2015, 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"
 #include "eap_config.h"
 
 
-/* Maximum supported TTLS version
- * 0 = RFC 5281
- * 1 = draft-funk-eap-ttls-v1-00.txt
- */
-#ifndef EAP_TTLS_VERSION
-#define EAP_TTLS_VERSION 0 /* TTLSv1 implementation is not yet complete */
-#endif /* EAP_TTLS_VERSION */
-
-
-#define MSCHAPV2_KEY_LEN 16
-#define MSCHAPV2_NT_RESPONSE_LEN 24
+#define EAP_TTLS_VERSION 0
 
 
 static void eap_ttls_deinit(struct eap_sm *sm, void *priv);
@@ -45,14 +29,14 @@ static void eap_ttls_deinit(struct eap_sm *sm, void *priv);
 
 struct eap_ttls_data {
        struct eap_ssl_data ssl;
-       int ssl_initialized;
 
-       int ttls_version, force_ttls_version;
+       int ttls_version;
 
        const struct eap_method *phase2_method;
        void *phase2_priv;
        int phase2_success;
        int phase2_start;
+       EapDecision decision_succ;
 
        enum phase2_types {
                EAP_TTLS_PHASE2_EAP,
@@ -72,11 +56,14 @@ struct eap_ttls_data {
        int resuming; /* starting a resumed session */
        int reauth; /* reauthentication */
        u8 *key_data;
+       u8 *session_id;
+       size_t id_len;
 
        struct wpabuf *pending_phase2_req;
+       struct wpabuf *pending_resp;
        int chbind_req_sent; /* channel binding request was sent */
        int done_butfor_cb; /*we turned METHOD_DONE into METHOD_MAY_CONT to receive cb*/
-  EapDecision cbDecision;
+       EapDecision cbDecision;
 #ifdef EAP_TNC
        int ready_for_tnc;
        int tnc_started;
@@ -105,46 +92,75 @@ static void * eap_ttls_init(struct eap_sm *sm)
 {
        struct eap_ttls_data *data;
        struct eap_peer_config *config = eap_get_config(sm);
+       int selected_non_eap;
        char *selected;
 
        data = os_zalloc(sizeof(*data));
        if (data == NULL)
                return NULL;
        data->ttls_version = EAP_TTLS_VERSION;
-       data->force_ttls_version = -1;
        selected = "EAP";
+       selected_non_eap = 0;
        data->phase2_type = EAP_TTLS_PHASE2_EAP;
 
-#if EAP_TTLS_VERSION > 0
-       if (config && config->phase1) {
-               const char *pos = os_strstr(config->phase1, "ttlsver=");
-               if (pos) {
-                       data->force_ttls_version = atoi(pos + 8);
-                       data->ttls_version = data->force_ttls_version;
-                       wpa_printf(MSG_DEBUG, "EAP-TTLS: Forced TTLS version "
-                                  "%d", data->force_ttls_version);
+       /*
+        * Either one auth= type or one or more autheap= methods can be
+        * specified.
+        */
+       if (config && config->phase2) {
+               const char *token, *last = NULL;
+
+               while ((token = cstr_token(config->phase2, " \t", &last))) {
+                       if (os_strncmp(token, "auth=", 5) != 0)
+                               continue;
+                       token += 5;
+
+                       if (last - token == 8 &&
+                           os_strncmp(token, "MSCHAPV2", 8) == 0) {
+                               selected = "MSCHAPV2";
+                               data->phase2_type = EAP_TTLS_PHASE2_MSCHAPV2;
+                       } else if (last - token == 6 &&
+                                  os_strncmp(token, "MSCHAP", 6) == 0) {
+                               selected = "MSCHAP";
+                               data->phase2_type = EAP_TTLS_PHASE2_MSCHAP;
+                       } else if (last - token == 3 &&
+                                  os_strncmp(token, "PAP", 3) == 0) {
+                               selected = "PAP";
+                               data->phase2_type = EAP_TTLS_PHASE2_PAP;
+                       } else if (last - token == 4 &&
+                                  os_strncmp(token, "CHAP", 4) == 0) {
+                               selected = "CHAP";
+                               data->phase2_type = EAP_TTLS_PHASE2_CHAP;
+                       } else {
+                               wpa_printf(MSG_ERROR,
+                                          "EAP-TTLS: Unsupported Phase2 type '%s'",
+                                          token);
+                               eap_ttls_deinit(sm, data);
+                               return NULL;
+                       }
+
+                       if (selected_non_eap) {
+                               wpa_printf(MSG_ERROR,
+                                          "EAP-TTLS: Only one Phase2 type can be specified");
+                               eap_ttls_deinit(sm, data);
+                               return NULL;
+                       }
+
+                       selected_non_eap = 1;
                }
-       }
-#endif /* EAP_TTLS_VERSION */
 
-       if (config && config->phase2) {
                if (os_strstr(config->phase2, "autheap=")) {
+                       if (selected_non_eap) {
+                               wpa_printf(MSG_ERROR,
+                                          "EAP-TTLS: Both auth= and autheap= params cannot be specified");
+                               eap_ttls_deinit(sm, data);
+                               return NULL;
+                       }
                        selected = "EAP";
                        data->phase2_type = EAP_TTLS_PHASE2_EAP;
-               } else if (os_strstr(config->phase2, "auth=MSCHAPV2")) {
-                       selected = "MSCHAPV2";
-                       data->phase2_type = EAP_TTLS_PHASE2_MSCHAPV2;
-               } else if (os_strstr(config->phase2, "auth=MSCHAP")) {
-                       selected = "MSCHAP";
-                       data->phase2_type = EAP_TTLS_PHASE2_MSCHAP;
-               } else if (os_strstr(config->phase2, "auth=PAP")) {
-                       selected = "PAP";
-                       data->phase2_type = EAP_TTLS_PHASE2_PAP;
-               } else if (os_strstr(config->phase2, "auth=CHAP")) {
-                       selected = "CHAP";
-                       data->phase2_type = EAP_TTLS_PHASE2_CHAP;
                }
        }
+
        wpa_printf(MSG_DEBUG, "EAP-TTLS: Phase2 type: %s", selected);
 
        if (data->phase2_type == EAP_TTLS_PHASE2_EAP) {
@@ -160,19 +176,11 @@ static void * eap_ttls_init(struct eap_sm *sm)
                data->phase2_eap_type.method = EAP_TYPE_NONE;
        }
 
-#if EAP_TTLS_VERSION > 0
-       if (!(tls_capabilities(sm->ssl_ctx) & TLS_CAPABILITY_IA) &&
-           data->ttls_version > 0) {
-               if (data->force_ttls_version > 0) {
-                       wpa_printf(MSG_INFO, "EAP-TTLS: Forced TTLSv%d and "
-                                  "TLS library does not support TLS/IA.",
-                                  data->force_ttls_version);
-                       eap_ttls_deinit(sm, data);
-                       return NULL;
-               }
-               data->ttls_version = 0;
+       if (eap_peer_tls_ssl_init(sm, &data->ssl, config, EAP_TYPE_TTLS)) {
+               wpa_printf(MSG_INFO, "EAP-TTLS: Failed to initialize SSL.");
+               eap_ttls_deinit(sm, data);
+               return NULL;
        }
-#endif /* EAP_TTLS_VERSION */
 
        return data;
 }
@@ -189,6 +197,15 @@ static void eap_ttls_phase2_eap_deinit(struct eap_sm *sm,
 }
 
 
+static void eap_ttls_free_key(struct eap_ttls_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_ttls_deinit(struct eap_sm *sm, void *priv)
 {
        struct eap_ttls_data *data = priv;
@@ -196,10 +213,11 @@ static void eap_ttls_deinit(struct eap_sm *sm, void *priv)
                return;
        eap_ttls_phase2_eap_deinit(sm, data);
        os_free(data->phase2_eap_types);
-       if (data->ssl_initialized)
-               eap_peer_tls_ssl_deinit(sm, &data->ssl);
-       os_free(data->key_data);
+       eap_peer_tls_ssl_deinit(sm, &data->ssl);
+       eap_ttls_free_key(data);
+       os_free(data->session_id);
        wpabuf_free(data->pending_phase2_req);
+       wpabuf_free(data->pending_resp);
        os_free(data);
 }
 
@@ -222,7 +240,8 @@ static u8 * eap_ttls_avp_hdr(u8 *avphdr, u32 avp_code, u32 vendor_id,
        }
 
        avp->avp_code = host_to_be32(avp_code);
-       avp->avp_length = host_to_be32((flags << 24) | (hdrlen + len));
+       avp->avp_length = host_to_be32(((u32) flags << 24) |
+                                      (u32) (hdrlen + len));
 
        return avphdr + hdrlen;
 }
@@ -308,46 +327,14 @@ static int eap_ttls_avp_radius_vsa_encapsulate(struct wpabuf **resp, u32 vendor,
        return 0;
 }
 
-#if EAP_TTLS_VERSION > 0
-static int eap_ttls_ia_permute_inner_secret(struct eap_sm *sm,
-                                           struct eap_ttls_data *data,
-                                           const u8 *key, size_t key_len)
-{
-       u8 *buf;
-       size_t buf_len;
-       int ret;
-
-       if (key) {
-               buf_len = 2 + key_len;
-               buf = os_malloc(buf_len);
-               if (buf == NULL)
-                       return -1;
-               WPA_PUT_BE16(buf, key_len);
-               os_memcpy(buf + 2, key, key_len);
-       } else {
-               buf = NULL;
-               buf_len = 0;
-       }
-
-       wpa_hexdump_key(MSG_DEBUG, "EAP-TTLS: Session keys for TLS/IA inner "
-                       "secret permutation", buf, buf_len);
-       ret = tls_connection_ia_permute_inner_secret(sm->ssl_ctx,
-                                                    data->ssl.conn,
-                                                    buf, buf_len);
-       os_free(buf);
-
-       return ret;
-}
-#endif /* EAP_TTLS_VERSION */
-
-
 static int eap_ttls_v0_derive_key(struct eap_sm *sm,
                                  struct eap_ttls_data *data)
 {
-       os_free(data->key_data);
+       eap_ttls_free_key(data);
        data->key_data = eap_peer_tls_derive_key(sm, &data->ssl,
                                                 "ttls keying material",
-                                                EAP_TLS_KEY_LEN);
+                                                EAP_TLS_KEY_LEN +
+                                                EAP_EMSK_LEN);
        if (!data->key_data) {
                wpa_printf(MSG_INFO, "EAP-TTLS: Failed to derive key");
                return -1;
@@ -355,162 +342,32 @@ static int eap_ttls_v0_derive_key(struct eap_sm *sm,
 
        wpa_hexdump_key(MSG_DEBUG, "EAP-TTLS: Derived key",
                        data->key_data, EAP_TLS_KEY_LEN);
-
-       return 0;
-}
-
-
-#if EAP_TTLS_VERSION > 0
-static int eap_ttls_v1_derive_key(struct eap_sm *sm,
-                                 struct eap_ttls_data *data)
-{
-       struct tls_keys keys;
-       u8 *rnd;
-
-       os_free(data->key_data);
-       data->key_data = NULL;
-
-       os_memset(&keys, 0, sizeof(keys));
-       if (tls_connection_get_keys(sm->ssl_ctx, data->ssl.conn, &keys) ||
-           keys.client_random == NULL || keys.server_random == NULL ||
-           keys.inner_secret == NULL) {
-               wpa_printf(MSG_INFO, "EAP-TTLS: Could not get inner secret, "
-                          "client random, or server random to derive keying "
-                          "material");
-               return -1;
-       }
-
-       rnd = os_malloc(keys.client_random_len + keys.server_random_len);
-       data->key_data = os_malloc(EAP_TLS_KEY_LEN);
-       if (rnd == NULL || data->key_data == NULL) {
-               wpa_printf(MSG_INFO, "EAP-TTLS: No memory for key derivation");
-               os_free(rnd);
-               os_free(data->key_data);
-               data->key_data = NULL;
-               return -1;
-       }
-       os_memcpy(rnd, keys.client_random, keys.client_random_len);
-       os_memcpy(rnd + keys.client_random_len, keys.server_random,
-                 keys.server_random_len);
-
-       if (tls_prf(keys.inner_secret, keys.inner_secret_len,
-                   "ttls v1 keying material", rnd, keys.client_random_len +
-                   keys.server_random_len, data->key_data, EAP_TLS_KEY_LEN)) {
-               wpa_printf(MSG_DEBUG, "EAP-TTLS: Failed to derive key");
-               os_free(rnd);
-               os_free(data->key_data);
-               data->key_data = NULL;
-               return -1;
+       wpa_hexdump_key(MSG_DEBUG, "EAP-TTLS: Derived EMSK",
+                       data->key_data + EAP_TLS_KEY_LEN,
+                       EAP_EMSK_LEN);
+
+       os_free(data->session_id);
+       data->session_id = eap_peer_tls_derive_session_id(sm, &data->ssl,
+                                                         EAP_TYPE_TTLS,
+                                                         &data->id_len);
+       if (data->session_id) {
+               wpa_hexdump(MSG_DEBUG, "EAP-TTLS: Derived Session-Id",
+                           data->session_id, data->id_len);
+       } else {
+               wpa_printf(MSG_ERROR, "EAP-TTLS: Failed to derive Session-Id");
        }
 
-       wpa_hexdump(MSG_DEBUG, "EAP-TTLS: client/server random",
-                   rnd, keys.client_random_len + keys.server_random_len);
-       wpa_hexdump_key(MSG_DEBUG, "EAP-TTLS: TLS/IA inner secret",
-                       keys.inner_secret, keys.inner_secret_len);
-
-       os_free(rnd);
-
-       wpa_hexdump_key(MSG_DEBUG, "EAP-TTLS: Derived key",
-                       data->key_data, EAP_TLS_KEY_LEN);
-
        return 0;
 }
-#endif /* EAP_TTLS_VERSION */
 
 
+#ifndef CONFIG_FIPS
 static u8 * eap_ttls_implicit_challenge(struct eap_sm *sm,
                                        struct eap_ttls_data *data, size_t len)
 {
-#if EAP_TTLS_VERSION > 0
-       struct tls_keys keys;
-       u8 *challenge, *rnd;
-#endif /* EAP_TTLS_VERSION */
-
-       if (data->ttls_version == 0) {
-               return eap_peer_tls_derive_key(sm, &data->ssl,
-                                              "ttls challenge", len);
-       }
-
-#if EAP_TTLS_VERSION > 0
-
-       os_memset(&keys, 0, sizeof(keys));
-       if (tls_connection_get_keys(sm->ssl_ctx, data->ssl.conn, &keys) ||
-           keys.client_random == NULL || keys.server_random == NULL ||
-           keys.inner_secret == NULL) {
-               wpa_printf(MSG_INFO, "EAP-TTLS: Could not get inner secret, "
-                          "client random, or server random to derive "
-                          "implicit challenge");
-               return NULL;
-       }
-
-       rnd = os_malloc(keys.client_random_len + keys.server_random_len);
-       challenge = os_malloc(len);
-       if (rnd == NULL || challenge == NULL) {
-               wpa_printf(MSG_INFO, "EAP-TTLS: No memory for implicit "
-                          "challenge derivation");
-               os_free(rnd);
-               os_free(challenge);
-               return NULL;
-       }
-       os_memcpy(rnd, keys.server_random, keys.server_random_len);
-       os_memcpy(rnd + keys.server_random_len, keys.client_random,
-                 keys.client_random_len);
-
-       if (tls_prf(keys.inner_secret, keys.inner_secret_len,
-                   "inner application challenge", rnd,
-                   keys.client_random_len + keys.server_random_len,
-                   challenge, len)) {
-               wpa_printf(MSG_DEBUG, "EAP-TTLS: Failed to derive implicit "
-                          "challenge");
-               os_free(rnd);
-               os_free(challenge);
-               return NULL;
-       }
-
-       os_free(rnd);
-
-       wpa_hexdump_key(MSG_DEBUG, "EAP-TTLS: Derived implicit challenge",
-                       challenge, len);
-
-       return challenge;
-
-#else /* EAP_TTLS_VERSION */
-
-       return NULL;
-
-#endif /* EAP_TTLS_VERSION */
-}
-
-
-static void eap_ttlsv1_phase2_eap_finish(struct eap_sm *sm,
-                                        struct eap_ttls_data *data,
-                                        struct eap_method_ret *ret)
-{
-#if EAP_TTLS_VERSION > 0
-       if (data->ttls_version > 0) {
-               const struct eap_method *m = data->phase2_method;
-               void *priv = data->phase2_priv;
-
-               /* TTLSv1 requires TLS/IA FinalPhaseFinished */
-               if (ret->decision == DECISION_UNCOND_SUCC)
-                       ret->decision = DECISION_COND_SUCC;
-               ret->methodState = METHOD_CONT;
-
-               if (ret->decision == DECISION_COND_SUCC &&
-                   m->isKeyAvailable && m->getKey &&
-                   m->isKeyAvailable(sm, priv)) {
-                       u8 *key;
-                       size_t key_len;
-                       key = m->getKey(sm, priv, &key_len);
-                       if (key) {
-                               eap_ttls_ia_permute_inner_secret(
-                                       sm, data, key, key_len);
-                               os_free(key);
-                       }
-               }
-       }
-#endif /* EAP_TTLS_VERSION */
+       return eap_peer_tls_derive_key(sm, &data->ssl, "ttls challenge", len);
 }
+#endif /* CONFIG_FIPS */
 
 
 static void eap_ttls_phase2_select_eap_method(struct eap_ttls_data *data,
@@ -556,7 +413,6 @@ static int eap_ttls_phase2_eap_process(struct eap_sm *sm,
                ret->methodState = iret.methodState;
                ret->decision = iret.decision;
        }
-       eap_ttlsv1_phase2_eap_finish(sm, data, ret);
 
        return 0;
 }
@@ -677,31 +533,16 @@ static int eap_ttls_phase2_request_eap(struct eap_sm *sm,
 }
 
 
-static void eap_ttlsv1_permute_inner(struct eap_sm *sm,
-                                    struct eap_ttls_data *data)
-{
-#if EAP_TTLS_VERSION > 0
-       u8 session_key[2 * MSCHAPV2_KEY_LEN];
-
-       if (data->ttls_version == 0)
-               return;
-
-       get_asymetric_start_key(data->master_key, session_key,
-                               MSCHAPV2_KEY_LEN, 0, 0);
-       get_asymetric_start_key(data->master_key,
-                               session_key + MSCHAPV2_KEY_LEN,
-                               MSCHAPV2_KEY_LEN, 1, 0);
-       eap_ttls_ia_permute_inner_secret(sm, data, session_key,
-                                        sizeof(session_key));
-#endif /* EAP_TTLS_VERSION */
-}
-
-
 static int eap_ttls_phase2_request_mschapv2(struct eap_sm *sm,
                                            struct eap_ttls_data *data,
                                            struct eap_method_ret *ret,
                                            struct wpabuf **resp)
 {
+#ifdef CONFIG_FIPS
+       wpa_printf(MSG_ERROR, "EAP-TTLS: MSCHAPV2 not supported in FIPS build");
+       return -1;
+#else /* CONFIG_FIPS */
+#ifdef EAP_MSCHAPv2
        struct wpabuf *msg;
        u8 *buf, *pos, *challenge, *peer_challenge;
        const u8 *identity, *password;
@@ -736,7 +577,6 @@ static int eap_ttls_phase2_request_mschapv2(struct eap_sm *sm,
                           "implicit challenge");
                return -1;
        }
-       peer_challenge = challenge + 1 + EAP_TTLS_MSCHAPV2_CHALLENGE_LEN;
 
        pos = eap_ttls_avp_add(buf, pos, RADIUS_ATTR_MS_CHAP_CHALLENGE,
                               RADIUS_VENDOR_ID_MICROSOFT, 1,
@@ -749,7 +589,14 @@ static int eap_ttls_phase2_request_mschapv2(struct eap_sm *sm,
        data->ident = challenge[EAP_TTLS_MSCHAPV2_CHALLENGE_LEN];
        *pos++ = data->ident;
        *pos++ = 0; /* Flags */
-       os_memcpy(pos, peer_challenge, EAP_TTLS_MSCHAPV2_CHALLENGE_LEN);
+       if (os_get_random(pos, EAP_TTLS_MSCHAPV2_CHALLENGE_LEN) < 0) {
+               os_free(challenge);
+               wpabuf_free(msg);
+               wpa_printf(MSG_ERROR, "EAP-TTLS/MSCHAPV2: Failed to get "
+                          "random data for peer challenge");
+               return -1;
+       }
+       peer_challenge = pos;
        pos += EAP_TTLS_MSCHAPV2_CHALLENGE_LEN;
        os_memset(pos, 0, 8); /* Reserved, must be zero */
        pos += 8;
@@ -757,6 +604,7 @@ static int eap_ttls_phase2_request_mschapv2(struct eap_sm *sm,
                                     password_len, pwhash, challenge,
                                     peer_challenge, pos, data->auth_response,
                                     data->master_key)) {
+               os_free(challenge);
                wpabuf_free(msg);
                wpa_printf(MSG_ERROR, "EAP-TTLS/MSCHAPV2: Failed to derive "
                           "response");
@@ -764,8 +612,6 @@ static int eap_ttls_phase2_request_mschapv2(struct eap_sm *sm,
        }
        data->auth_response_valid = 1;
 
-       eap_ttlsv1_permute_inner(sm, data);
-
        pos += 24;
        os_free(challenge);
        AVP_PAD(buf, pos);
@@ -773,17 +619,12 @@ static int eap_ttls_phase2_request_mschapv2(struct eap_sm *sm,
        wpabuf_put(msg, pos - buf);
        *resp = msg;
 
-       if (sm->workaround && data->ttls_version == 0) {
-               /* At least FreeRADIUS seems to be terminating
-                * EAP-TTLS/MSHCAPV2 without the expected MS-CHAP-v2 Success
-                * packet. */
-               wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAPV2: EAP workaround - "
-                          "allow success without tunneled response");
-               ret->methodState = METHOD_MAY_CONT;
-               ret->decision = DECISION_COND_SUCC;
-       }
-
        return 0;
+#else /* EAP_MSCHAPv2 */
+       wpa_printf(MSG_ERROR, "EAP-TTLS: MSCHAPv2 not included in the build");
+       return -1;
+#endif /* EAP_MSCHAPv2 */
+#endif /* CONFIG_FIPS */
 }
 
 
@@ -792,6 +633,10 @@ static int eap_ttls_phase2_request_mschap(struct eap_sm *sm,
                                          struct eap_method_ret *ret,
                                          struct wpabuf **resp)
 {
+#ifdef CONFIG_FIPS
+       wpa_printf(MSG_ERROR, "EAP-TTLS: MSCHAP not supported in FIPS build");
+       return -1;
+#else /* CONFIG_FIPS */
        struct wpabuf *msg;
        u8 *buf, *pos, *challenge;
        const u8 *identity, *password;
@@ -860,19 +705,13 @@ static int eap_ttls_phase2_request_mschap(struct eap_sm *sm,
        wpabuf_put(msg, pos - buf);
        *resp = msg;
 
-       if (data->ttls_version > 0) {
-               /* EAP-TTLSv1 uses TLS/IA FinalPhaseFinished to report success,
-                * so do not allow connection to be terminated yet. */
-               ret->methodState = METHOD_CONT;
-               ret->decision = DECISION_COND_SUCC;
-       } else {
-               /* EAP-TTLS/MSCHAP does not provide tunneled success
-                * notification, so assume that Phase2 succeeds. */
-               ret->methodState = METHOD_DONE;
-               ret->decision = DECISION_COND_SUCC;
-       }
+       /* EAP-TTLS/MSCHAP does not provide tunneled success
+        * notification, so assume that Phase2 succeeds. */
+       ret->methodState = METHOD_DONE;
+       ret->decision = DECISION_COND_SUCC;
 
        return 0;
+#endif /* CONFIG_FIPS */
 }
 
 
@@ -921,17 +760,10 @@ static int eap_ttls_phase2_request_pap(struct eap_sm *sm,
        wpabuf_put(msg, pos - buf);
        *resp = msg;
 
-       if (data->ttls_version > 0) {
-               /* EAP-TTLSv1 uses TLS/IA FinalPhaseFinished to report success,
-                * so do not allow connection to be terminated yet. */
-               ret->methodState = METHOD_CONT;
-               ret->decision = DECISION_COND_SUCC;
-       } else {
-               /* EAP-TTLS/PAP does not provide tunneled success notification,
-                * so assume that Phase2 succeeds. */
-               ret->methodState = METHOD_DONE;
-               ret->decision = DECISION_COND_SUCC;
-       }
+       /* EAP-TTLS/PAP does not provide tunneled success notification,
+        * so assume that Phase2 succeeds. */
+       ret->methodState = METHOD_DONE;
+       ret->decision = DECISION_COND_SUCC;
 
        return 0;
 }
@@ -942,6 +774,10 @@ static int eap_ttls_phase2_request_chap(struct eap_sm *sm,
                                        struct eap_method_ret *ret,
                                        struct wpabuf **resp)
 {
+#ifdef CONFIG_FIPS
+       wpa_printf(MSG_ERROR, "EAP-TTLS: CHAP not supported in FIPS build");
+       return -1;
+#else /* CONFIG_FIPS */
        struct wpabuf *msg;
        u8 *buf, *pos, *challenge;
        const u8 *identity, *password;
@@ -1004,19 +840,13 @@ static int eap_ttls_phase2_request_chap(struct eap_sm *sm,
        wpabuf_put(msg, pos - buf);
        *resp = msg;
 
-       if (data->ttls_version > 0) {
-               /* EAP-TTLSv1 uses TLS/IA FinalPhaseFinished to report success,
-                * so do not allow connection to be terminated yet. */
-               ret->methodState = METHOD_CONT;
-               ret->decision = DECISION_COND_SUCC;
-       } else {
-               /* EAP-TTLS/CHAP does not provide tunneled success
-                * notification, so assume that Phase2 succeeds. */
-               ret->methodState = METHOD_DONE;
-               ret->decision = DECISION_COND_SUCC;
-       }
+       /* EAP-TTLS/CHAP does not provide tunneled success
+        * notification, so assume that Phase2 succeeds. */
+       ret->methodState = METHOD_DONE;
+       ret->decision = DECISION_COND_SUCC;
 
        return 0;
+#endif /* CONFIG_FIPS */
 }
 
 
@@ -1089,36 +919,6 @@ static int eap_ttls_phase2_request(struct eap_sm *sm,
 }
 
 
-#if EAP_TTLS_VERSION > 0
-static struct wpabuf * eap_ttls_build_phase_finished(
-       struct eap_sm *sm, struct eap_ttls_data *data, int id, int final)
-{
-       struct wpabuf *req, *buf;
-
-       buf = tls_connection_ia_send_phase_finished(sm->ssl_ctx,
-                                                   data->ssl.conn,
-                                                   final);
-       if (buf == NULL)
-               return NULL;
-
-       req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_TTLS,
-                           1 + wpabuf_len(buf),
-                           EAP_CODE_RESPONSE, id);
-       if (req == NULL) {
-               wpabuf_free(buf);
-               return NULL;
-       }
-
-       wpabuf_put_u8(req, data->ttls_version);
-       wpabuf_put_buf(req, buf);
-       wpabuf_free(buf);
-       eap_update_len(req);
-
-       return req;
-}
-#endif /* EAP_TTLS_VERSION */
-
-
 struct ttls_parse_avp {
        u8 *mschapv2;
        u8 *eapdata;
@@ -1359,6 +1159,7 @@ static int eap_ttls_encrypt_response(struct eap_sm *sm,
                                 resp, out_data)) {
                wpa_printf(MSG_INFO, "EAP-TTLS: Failed to encrypt a Phase 2 "
                           "frame");
+               wpabuf_free(resp);
                return -1;
        }
        wpabuf_free(resp);
@@ -1533,6 +1334,7 @@ static int eap_ttls_process_phase2_mschapv2(struct eap_sm *sm,
                                            struct eap_method_ret *ret,
                                            struct ttls_parse_avp *parse)
 {
+#ifdef EAP_MSCHAPv2
        if (parse->mschapv2_error) {
                wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAPV2: Received "
                           "MS-CHAP-Error - failed");
@@ -1572,25 +1374,19 @@ static int eap_ttls_process_phase2_mschapv2(struct eap_sm *sm,
 
        wpa_printf(MSG_INFO, "EAP-TTLS: Phase 2 MSCHAPV2 "
                   "authentication succeeded");
-       if (data->ttls_version > 0) {
-               /*
-                * EAP-TTLSv1 uses TLS/IA FinalPhaseFinished to report
-                * success, so do not allow connection to be terminated
-                * yet.
-                */
-               ret->methodState = METHOD_CONT;
-               ret->decision = DECISION_COND_SUCC;
-       } else {
-               ret->methodState = METHOD_DONE;
-               ret->decision = DECISION_UNCOND_SUCC;
-               data->phase2_success = 1;
-       }
+       ret->methodState = METHOD_DONE;
+       ret->decision = DECISION_UNCOND_SUCC;
+       data->phase2_success = 1;
 
        /*
         * Reply with empty data; authentication server will reply
         * with EAP-Success after this.
         */
        return 1;
+#else /* EAP_MSCHAPv2 */
+       wpa_printf(MSG_ERROR, "EAP-TTLS: MSCHAPv2 not included in the build");
+       return -1;
+#endif /* EAP_MSCHAPv2 */
 }
 
 
@@ -1728,24 +1524,6 @@ static int eap_ttls_process_decrypted(struct eap_sm *sm,
 }
 
 
-#if EAP_TTLS_VERSION > 0
-static void eap_ttls_final_phase_finished(struct eap_sm *sm,
-                                         struct eap_ttls_data *data,
-                                         struct eap_method_ret *ret,
-                                         u8 identifier,
-                                         struct wpabuf **out_data)
-{
-       wpa_printf(MSG_DEBUG, "EAP-TTLS: FinalPhaseFinished received");
-       wpa_printf(MSG_INFO, "EAP-TTLS: TLS/IA authentication succeeded");
-       ret->methodState = METHOD_DONE;
-       ret->decision = DECISION_UNCOND_SUCC;
-       data->phase2_success = 1;
-       *out_data = eap_ttls_build_phase_finished(sm, data, identifier, 1);
-       eap_ttls_v1_derive_key(sm, data);
-}
-#endif /* EAP_TTLS_VERSION */
-
-
 static int eap_ttls_implicit_identity_request(struct eap_sm *sm,
                                              struct eap_ttls_data *data,
                                              struct eap_method_ret *ret,
@@ -1769,6 +1547,21 @@ static int eap_ttls_implicit_identity_request(struct eap_sm *sm,
                           "processing failed");
                retval = -1;
        } else {
+               struct eap_peer_config *config = eap_get_config(sm);
+               if (resp == NULL &&
+                   (config->pending_req_identity ||
+                    config->pending_req_password ||
+                    config->pending_req_otp ||
+                    config->pending_req_new_password)) {
+                       /*
+                        * Use empty buffer to force implicit request
+                        * processing when EAP request is re-processed after
+                        * user input.
+                        */
+                       wpabuf_free(data->pending_phase2_req);
+                       data->pending_phase2_req = wpabuf_alloc(0);
+               }
+
                retval = eap_ttls_encrypt_response(sm, data, resp, identifier,
                                                   out_data);
        }
@@ -1862,17 +1655,6 @@ static int eap_ttls_decrypt(struct eap_sm *sm, struct eap_ttls_data *data,
        if (retval)
                goto done;
 
-#if EAP_TTLS_VERSION > 0
-       if (data->ttls_version > 0 &&
-           (in_decrypted == NULL || wpabuf_len(in_decrypted) == 0) &&
-           tls_connection_ia_final_phase_finished(sm->ssl_ctx,
-                                                  data->ssl.conn)) {
-               eap_ttls_final_phase_finished(sm, data, ret, identifier,
-                                             out_data);
-               goto done;
-       }
-#endif /* EAP_TTLS_VERSION */
-
 continue_req:
        data->phase2_start = 0;
 
@@ -1897,58 +1679,59 @@ done:
 }
 
 
-static int eap_ttls_process_start(struct eap_sm *sm,
-                                 struct eap_ttls_data *data, u8 flags,
-                                 struct eap_method_ret *ret)
-{
-       struct eap_peer_config *config = eap_get_config(sm);
-
-       wpa_printf(MSG_DEBUG, "EAP-TTLS: Start (server ver=%d, own ver=%d)",
-                  flags & EAP_TLS_VERSION_MASK, data->ttls_version);
-#if EAP_TTLS_VERSION > 0
-       if ((flags & EAP_TLS_VERSION_MASK) < data->ttls_version)
-               data->ttls_version = flags & EAP_TLS_VERSION_MASK;
-       if (data->force_ttls_version >= 0 &&
-           data->force_ttls_version != data->ttls_version) {
-               wpa_printf(MSG_WARNING, "EAP-TTLS: Failed to select "
-                          "forced TTLS version %d",
-                          data->force_ttls_version);
-               ret->methodState = METHOD_DONE;
-               ret->decision = DECISION_FAIL;
-               ret->allowNotifications = FALSE;
-               return -1;
-       }
-       wpa_printf(MSG_DEBUG, "EAP-TTLS: Using TTLS version %d",
-                  data->ttls_version);
-
-       if (data->ttls_version > 0)
-               data->ssl.tls_ia = 1;
-#endif /* EAP_TTLS_VERSION */
-       if (!data->ssl_initialized &&
-           eap_peer_tls_ssl_init(sm, &data->ssl, config)) {
-               wpa_printf(MSG_INFO, "EAP-TTLS: Failed to initialize SSL.");
-               return -1;
-       }
-       data->ssl_initialized = 1;
-
-       wpa_printf(MSG_DEBUG, "EAP-TTLS: Start");
-
-       return 0;
-}
-
-
 static int eap_ttls_process_handshake(struct eap_sm *sm,
                                      struct eap_ttls_data *data,
                                      struct eap_method_ret *ret,
                                      u8 identifier,
-                                     const u8 *in_data, size_t in_len,
+                                     const struct wpabuf *in_data,
                                      struct wpabuf **out_data)
 {
        int res;
 
+       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-TTLS: External certificate check succeeded - continue handshake");
+                       *out_data = data->pending_resp;
+                       data->pending_resp = NULL;
+                       sm->waiting_ext_cert_check = 0;
+                       return 0;
+               }
+
+               if (config->pending_ext_cert_check == EXT_CERT_CHECK_BAD) {
+                       wpa_printf(MSG_DEBUG,
+                                  "EAP-TTLS: External certificate check failed - force authentication failure");
+                       ret->methodState = METHOD_DONE;
+                       ret->decision = DECISION_FAIL;
+                       sm->waiting_ext_cert_check = 0;
+                       return 0;
+               }
+
+               wpa_printf(MSG_DEBUG,
+                          "EAP-TTLS: Continuing to wait external server certificate validation");
+               return 0;
+       }
+
        res = eap_peer_tls_process_helper(sm, &data->ssl, EAP_TYPE_TTLS,
                                          data->ttls_version, identifier,
-                                         in_data, in_len, out_data);
+                                         in_data, out_data);
+       if (res < 0) {
+               wpa_printf(MSG_DEBUG, "EAP-TTLS: TLS processing failed");
+               ret->methodState = METHOD_DONE;
+               ret->decision = DECISION_FAIL;
+               return -1;
+       }
+
+       if (sm->waiting_ext_cert_check) {
+               wpa_printf(MSG_DEBUG,
+                          "EAP-TTLS: Waiting external server certificate validation");
+               wpabuf_free(data->pending_resp);
+               data->pending_resp = *out_data;
+               *out_data = NULL;
+               return 0;
+       }
 
        if (tls_connection_established(sm->ssl_ctx, data->ssl.conn)) {
                wpa_printf(MSG_DEBUG, "EAP-TTLS: TLS done, proceed to "
@@ -1960,8 +1743,7 @@ static int eap_ttls_process_handshake(struct eap_sm *sm,
                        ret->methodState = METHOD_MAY_CONT;
                }
                data->phase2_start = 1;
-               if (data->ttls_version == 0)
-                       eap_ttls_v0_derive_key(sm, data);
+               eap_ttls_v0_derive_key(sm, data);
 
                if (*out_data == NULL || wpabuf_len(*out_data) == 0) {
                        if (eap_ttls_decrypt(sm, data, ret, identifier,
@@ -1976,15 +1758,13 @@ static int eap_ttls_process_handshake(struct eap_sm *sm,
        }
 
        if (res == 2) {
-               struct wpabuf msg;
                /*
                 * Application data included in the handshake message.
                 */
                wpabuf_free(data->pending_phase2_req);
                data->pending_phase2_req = *out_data;
                *out_data = NULL;
-               wpabuf_set(&msg, in_data, in_len);
-               res = eap_ttls_decrypt(sm, data, ret, identifier, &msg,
+               res = eap_ttls_decrypt(sm, data, ret, identifier, in_data,
                                       out_data);
        }
 
@@ -1996,13 +1776,14 @@ static void eap_ttls_check_auth_status(struct eap_sm *sm,
                                       struct eap_ttls_data *data,
                                       struct eap_method_ret *ret)
 {
-       if (data->ttls_version == 0 && ret->methodState == METHOD_DONE) {
+       if (ret->methodState == METHOD_DONE) {
                ret->allowNotifications = FALSE;
                if (ret->decision == DECISION_UNCOND_SUCC ||
                    ret->decision == DECISION_COND_SUCC) {
                        wpa_printf(MSG_DEBUG, "EAP-TTLS: Authentication "
                                   "completed successfully");
                        data->phase2_success = 1;
+                       data->decision_succ = ret->decision;
 #ifdef EAP_TNC
                        if (!data->ready_for_tnc && !data->tnc_started) {
                                /*
@@ -2014,13 +1795,24 @@ static void eap_ttls_check_auth_status(struct eap_sm *sm,
                        }
 #endif /* EAP_TNC */
                }
-       } else if (data->ttls_version == 0 &&
-                  ret->methodState == METHOD_MAY_CONT &&
+       } else if (ret->methodState == METHOD_MAY_CONT &&
                   (ret->decision == DECISION_UNCOND_SUCC ||
                    ret->decision == DECISION_COND_SUCC)) {
                        wpa_printf(MSG_DEBUG, "EAP-TTLS: Authentication "
                                   "completed successfully (MAY_CONT)");
                        data->phase2_success = 1;
+                       data->decision_succ = ret->decision;
+       } else if (data->decision_succ != DECISION_FAIL &&
+                  data->phase2_success &&
+                  !data->ssl.tls_out) {
+               /*
+                * This is needed to cover the case where the final Phase 2
+                * message gets fragmented since fragmentation clears
+                * decision back to FAIL.
+                */
+               wpa_printf(MSG_DEBUG,
+                          "EAP-TTLS: Restore success decision after fragmented frame sent completely");
+               ret->decision = data->decision_succ;
        }
 }
 
@@ -2035,6 +1827,7 @@ static struct wpabuf * eap_ttls_process(struct eap_sm *sm, void *priv,
        struct wpabuf *resp;
        const u8 *pos;
        struct eap_ttls_data *data = priv;
+       struct wpabuf msg;
 
        pos = eap_peer_tls_process_init(sm, &data->ssl, EAP_TYPE_TTLS, ret,
                                        reqData, &left, &flags);
@@ -2043,8 +1836,9 @@ static struct wpabuf * eap_ttls_process(struct eap_sm *sm, void *priv,
        id = eap_get_id(reqData);
 
        if (flags & EAP_TLS_FLAGS_START) {
-               if (eap_ttls_process_start(sm, data, flags, ret) < 0)
-                       return NULL;
+               wpa_printf(MSG_DEBUG, "EAP-TTLS: Start (server ver=%d, own "
+                          "ver=%d)", flags & EAP_TLS_VERSION_MASK,
+                          data->ttls_version);
 
                /* RFC 5281, Ch. 9.2:
                 * "This packet MAY contain additional information in the form
@@ -2052,24 +1846,17 @@ static struct wpabuf * eap_ttls_process(struct eap_sm *sm, void *priv,
                 * For now, ignore any potential extra data.
                 */
                left = 0;
-       } else if (!data->ssl_initialized) {
-               wpa_printf(MSG_DEBUG, "EAP-TTLS: First message did not "
-                          "include Start flag");
-               ret->methodState = METHOD_DONE;
-               ret->decision = DECISION_FAIL;
-               ret->allowNotifications = FALSE;
-               return NULL;
        }
 
+       wpabuf_set(&msg, pos, left);
+
        resp = NULL;
        if (tls_connection_established(sm->ssl_ctx, data->ssl.conn) &&
            !data->resuming) {
-               struct wpabuf msg;
-               wpabuf_set(&msg, pos, left);
                res = eap_ttls_decrypt(sm, data, ret, id, &msg, &resp);
        } else {
                res = eap_ttls_process_handshake(sm, data, ret, id,
-                                                pos, left, &resp);
+                                                &msg, &resp);
        }
 
        eap_ttls_check_auth_status(sm, data, ret);
@@ -2098,6 +1885,9 @@ static void eap_ttls_deinit_for_reauth(struct eap_sm *sm, void *priv)
        struct eap_ttls_data *data = priv;
        wpabuf_free(data->pending_phase2_req);
        data->pending_phase2_req = NULL;
+       wpabuf_free(data->pending_resp);
+       data->pending_resp = NULL;
+       data->decision_succ = DECISION_FAIL;
 #ifdef EAP_TNC
        data->ready_for_tnc = 0;
        data->tnc_started = 0;
@@ -2108,8 +1898,9 @@ static void eap_ttls_deinit_for_reauth(struct eap_sm *sm, void *priv)
 static void * eap_ttls_init_for_reauth(struct eap_sm *sm, void *priv)
 {
        struct eap_ttls_data *data = priv;
-       os_free(data->key_data);
-       data->key_data = NULL;
+       eap_ttls_free_key(data);
+       os_free(data->session_id);
+       data->session_id = NULL;
        if (eap_peer_tls_reauth_init(sm, &data->ssl)) {
                os_free(data);
                return NULL;
@@ -2135,7 +1926,7 @@ static int eap_ttls_get_status(struct eap_sm *sm, void *priv, char *buf,
        ret = os_snprintf(buf + len, buflen - len,
                          "EAP-TTLSv%d Phase2 method=",
                          data->ttls_version);
-       if (ret < 0 || (size_t) ret >= buflen - len)
+       if (os_snprintf_error(buflen - len, ret))
                return len;
        len += ret;
        switch (data->phase2_type) {
@@ -2160,7 +1951,7 @@ static int eap_ttls_get_status(struct eap_sm *sm, void *priv, char *buf,
                ret = 0;
                break;
        }
-       if (ret < 0 || (size_t) ret >= buflen - len)
+       if (os_snprintf_error(buflen - len, ret))
                return len;
        len += ret;
 
@@ -2194,10 +1985,47 @@ static u8 * eap_ttls_getKey(struct eap_sm *sm, void *priv, size_t *len)
 }
 
 
+static u8 * eap_ttls_get_session_id(struct eap_sm *sm, void *priv, size_t *len)
+{
+       struct eap_ttls_data *data = priv;
+       u8 *id;
+
+       if (data->session_id == NULL || !data->phase2_success)
+               return NULL;
+
+       id = os_malloc(data->id_len);
+       if (id == NULL)
+               return NULL;
+
+       *len = data->id_len;
+       os_memcpy(id, data->session_id, data->id_len);
+
+       return id;
+}
+
+
+static u8 * eap_ttls_get_emsk(struct eap_sm *sm, void *priv, size_t *len)
+{
+       struct eap_ttls_data *data = priv;
+       u8 *key;
+
+       if (data->key_data == NULL)
+               return NULL;
+
+       key = os_malloc(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;
+}
+
+
 int eap_peer_ttls_register(void)
 {
        struct eap_method *eap;
-       int ret;
 
        eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION,
                                    EAP_VENDOR_IETF, EAP_TYPE_TTLS, "TTLS");
@@ -2209,13 +2037,12 @@ int eap_peer_ttls_register(void)
        eap->process = eap_ttls_process;
        eap->isKeyAvailable = eap_ttls_isKeyAvailable;
        eap->getKey = eap_ttls_getKey;
+       eap->getSessionId = eap_ttls_get_session_id;
        eap->get_status = eap_ttls_get_status;
        eap->has_reauth_data = eap_ttls_has_reauth_data;
        eap->deinit_for_reauth = eap_ttls_deinit_for_reauth;
        eap->init_for_reauth = eap_ttls_init_for_reauth;
+       eap->get_emsk = eap_ttls_get_emsk;
 
-       ret = eap_peer_method_register(eap);
-       if (ret)
-               eap_peer_method_free(eap);
-       return ret;
+       return eap_peer_method_register(eap);
 }