Przeglądaj źródła

Merge branch 'feature/support_phase2TTLS_algos' into 'master'

wpa_supplicant: add support for PAP, MS-CHAP and CHAP as Phase 2 algorithms for TTLS alongside MS-CHAPv2

Closes FCS-361

See merge request espressif/esp-idf!8816
Jiang Jiang Jian 5 lat temu
rodzic
commit
c27bd40d54

+ 18 - 0
components/wpa_supplicant/include/esp_supplicant/esp_wpa2.h

@@ -19,6 +19,14 @@
 
 #include "esp_err.h"
 
+typedef enum {
+    ESP_EAP_TTLS_PHASE2_EAP,
+    ESP_EAP_TTLS_PHASE2_MSCHAPV2,
+    ESP_EAP_TTLS_PHASE2_MSCHAP,
+    ESP_EAP_TTLS_PHASE2_PAP,
+    ESP_EAP_TTLS_PHASE2_CHAP
+} esp_eap_ttls_phase2_types ;
+
 #ifdef __cplusplus
 extern "C" {
 #endif
@@ -191,6 +199,16 @@ esp_err_t esp_wifi_sta_wpa2_ent_set_disable_time_check(bool disable);
   */
 esp_err_t esp_wifi_sta_wpa2_ent_get_disable_time_check(bool *disable);
 
+/**
+  * @brief  Set wpa2 enterprise ttls phase2 method
+  *
+  * @param  type: the type of phase 2 method to be used
+  *
+  * @return
+  *    - ESP_OK: succeed
+  */
+esp_err_t esp_wifi_sta_wpa2_ent_set_ttls_phase2_method(esp_eap_ttls_phase2_types type);
+
 #ifdef __cplusplus
 }
 #endif

+ 3 - 2
components/wpa_supplicant/src/eap_peer/chap.c

@@ -2,12 +2,13 @@
  * CHAP-MD5
  *
  */
-#ifdef CHAP_MD5
+#ifndef CHAP_MD5
+#define CHAP_MD5
 
 #include "utils/includes.h"
 #include "utils/common.h"
 #include "crypto/crypto.h"
-#include "wpa2/eap_peer/chap.h"
+#include "eap_peer/chap.h"
 
 int chap_md5(u8 id, const u8 *secret, size_t secret_len, const u8 *challenge,
 	     size_t challenge_len, u8 *response)

+ 17 - 0
components/wpa_supplicant/src/eap_peer/chap.h

@@ -0,0 +1,17 @@
+/*
+ * CHAP-MD5 (RFC 1994)
+ * Copyright (c) 2007-2009, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef CHAP_H
+#define CHAP_H
+
+#define CHAP_MD5_LEN 16
+
+int chap_md5(u8 id, const u8 *secret, size_t secret_len, const u8 *challenge,
+	     size_t challenge_len, u8 *response);
+
+#endif /* CHAP_H */

+ 181 - 0
components/wpa_supplicant/src/eap_peer/eap.c

@@ -27,6 +27,7 @@
 #include "rsn_supp/wpa.h"
 
 #include "crypto/crypto.h"
+#include "crypto/sha256.h"
 
 #include "utils/ext_password.h"
 #include "tls/tls.h"
@@ -191,6 +192,8 @@ void eap_peer_unregister_methods(void)
 	}
 }
 
+
+
 int eap_peer_register_methods(void)
 {
 	int ret = 0;
@@ -232,6 +235,90 @@ void eap_deinit_prev_method(struct eap_sm *sm, const char *txt)
 	sm->m = NULL;
 }
 
+static int eap_sm_set_scard_pin(struct eap_sm *sm,
+				struct eap_peer_config *conf)
+{
+	return -1;
+}
+
+static int eap_sm_get_scard_identity(struct eap_sm *sm,
+				     struct eap_peer_config *conf)
+{
+	return -1;
+}
+
+
+/**
+ * eap_sm_buildIdentity - Build EAP-Identity/Response for the current network
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @id: EAP identifier for the packet
+ * @encrypted: Whether the packet is for encrypted tunnel (EAP phase 2)
+ * Returns: Pointer to the allocated EAP-Identity/Response packet or %NULL on
+ * failure
+ *
+ * This function allocates and builds an EAP-Identity/Response packet for the
+ * current network. The caller is responsible for freeing the returned data.
+ */
+struct wpabuf * eap_sm_buildIdentity(struct eap_sm *sm, int id, int encrypted)
+{
+	struct eap_peer_config *config = eap_get_config(sm);
+	struct wpabuf *resp;
+	const u8 *identity;
+	size_t identity_len;
+
+	if (config == NULL) {
+		wpa_printf(MSG_ERROR, "EAP: buildIdentity: configuration "
+			   "was not available");
+		return NULL;
+	}
+
+	if (sm->m && sm->m->get_identity &&
+	    (identity = sm->m->get_identity(sm, sm->eap_method_priv,
+					    &identity_len)) != NULL) {
+		wpa_hexdump_ascii(MSG_DEBUG, "EAP: using method re-auth "
+				  "identity", identity, identity_len);
+	} else if (!encrypted && config->anonymous_identity) {
+		identity = config->anonymous_identity;
+		identity_len = config->anonymous_identity_len;
+		wpa_hexdump_ascii(MSG_DEBUG, "EAP: using anonymous identity",
+				  identity, identity_len);
+	} else {
+		identity = config->identity;
+		identity_len = config->identity_len;
+		wpa_hexdump_ascii(MSG_DEBUG, "EAP: using real identity",
+				  identity, identity_len);
+	}
+
+	if (identity == NULL) {
+		wpa_printf(MSG_ERROR, "EAP: buildIdentity: identity "
+			   "configuration was not available");
+		if (config->pcsc) {
+			if (eap_sm_get_scard_identity(sm, config) < 0)
+				return NULL;
+			identity = config->identity;
+			identity_len = config->identity_len;
+			wpa_hexdump_ascii(MSG_DEBUG, "permanent identity from "
+					  "IMSI", identity, identity_len);
+		} else {
+			eap_sm_request_identity(sm);
+			return NULL;
+		}
+	} else if (config->pcsc) {
+		if (eap_sm_set_scard_pin(sm, config) < 0)
+			return NULL;
+	}
+
+	resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_IDENTITY, identity_len,
+			     EAP_CODE_RESPONSE, id);
+	if (resp == NULL)
+		return NULL;
+
+	wpabuf_put_data(resp, identity, identity_len);
+
+	return resp;
+}
+
+
 struct wpabuf * eap_sm_build_identity_resp(struct eap_sm *sm, u8 id, int encrypted)
 {
 	const u8 *identity;
@@ -389,6 +476,10 @@ int eap_peer_config_init(
 			  sm->config.new_password_len);
 	}
 
+    if (g_wpa_ttls_phase2_type) {
+        sm->config.phase2 = g_wpa_ttls_phase2_type;
+	}
+
 	return 0;
 	
 }
@@ -458,6 +549,65 @@ _out:
 	return ret;
 }
 
+#if defined(CONFIG_CTRL_IFACE) || !defined(CONFIG_NO_STDOUT_DEBUG)
+static void eap_sm_request(struct eap_sm *sm, enum wpa_ctrl_req_type field,
+			   const char *msg, size_t msglen)
+{
+	struct eap_peer_config *config;
+
+	if (sm == NULL)
+		return;
+	config = eap_get_config(sm);
+	if (config == NULL)
+		return;
+
+	switch (field) {
+	case WPA_CTRL_REQ_EAP_IDENTITY:
+		config->pending_req_identity++;
+		break;
+	case WPA_CTRL_REQ_EAP_PASSWORD:
+		config->pending_req_password++;
+		break;
+	case WPA_CTRL_REQ_EAP_NEW_PASSWORD:
+		config->pending_req_new_password++;
+		break;
+	case WPA_CTRL_REQ_EAP_PIN:
+		config->pending_req_pin++;
+		break;
+	case WPA_CTRL_REQ_EAP_PASSPHRASE:
+		config->pending_req_passphrase++;
+		break;
+	default:
+		return;
+	}
+
+}
+#else /* CONFIG_CTRL_IFACE || !CONFIG_NO_STDOUT_DEBUG */
+#define eap_sm_request(sm, type, msg, msglen) do { } while (0)
+#endif /* CONFIG_CTRL_IFACE || !CONFIG_NO_STDOUT_DEBUG */
+
+const char * eap_sm_get_method_name(struct eap_sm *sm)
+{
+	if (sm->m == NULL)
+		return "UNKNOWN";
+	return sm->m->name;
+}
+
+
+/**
+ * eap_sm_request_identity - Request identity from user (ctrl_iface)
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ *
+ * EAP methods can call this function to request identity information for the
+ * current network. This is normally called when the identity is not included
+ * in the network configuration. The request will be sent to monitor programs
+ * through the control interface.
+ */
+void eap_sm_request_identity(struct eap_sm *sm)
+{
+	eap_sm_request(sm, WPA_CTRL_REQ_EAP_IDENTITY, NULL, 0);
+}
+
 void eap_peer_blob_deinit(struct eap_sm *sm)
 {
 	int i;
@@ -495,6 +645,13 @@ struct eap_peer_config * eap_get_config(struct eap_sm *sm)
 	return &sm->config;
 }
 
+
+/**
+ * eap_get_config_identity - Get identity from the network configuration
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @len: Buffer for the length of the identity
+ * Returns: Pointer to the identity or %NULL if not found
+ */
 const u8 * eap_get_config_identity(struct eap_sm *sm, size_t *len)
 {
 	struct eap_peer_config *config = eap_get_config(sm);
@@ -504,6 +661,13 @@ const u8 * eap_get_config_identity(struct eap_sm *sm, size_t *len)
 	return config->identity;
 }
 
+
+/**
+ * eap_get_config_password - Get password from the network configuration
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @len: Buffer for the length of the password
+ * Returns: Pointer to the password or %NULL if not found
+ */
 const u8 * eap_get_config_password(struct eap_sm *sm, size_t *len)
 {
 	struct eap_peer_config *config = eap_get_config(sm);
@@ -513,6 +677,16 @@ const u8 * eap_get_config_password(struct eap_sm *sm, size_t *len)
 	return config->password;
 }
 
+
+/**
+ * eap_get_config_password2 - Get password from the network configuration
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @len: Buffer for the length of the password
+ * @hash: Buffer for returning whether the password is stored as a
+ * NtPasswordHash instead of plaintext password; can be %NULL if this
+ * information is not needed
+ * Returns: Pointer to the password or %NULL if not found
+ */
 const u8 * eap_get_config_password2(struct eap_sm *sm, size_t *len, int *hash)
 {
 	struct eap_peer_config *config = eap_get_config(sm);
@@ -525,6 +699,13 @@ const u8 * eap_get_config_password2(struct eap_sm *sm, size_t *len, int *hash)
 	return config->password;
 }
 
+
+/**
+ * eap_get_config_new_password - Get new password from network configuration
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @len: Buffer for the length of the new password
+ * Returns: Pointer to the new password or %NULL if not found
+ */
 const u8 * eap_get_config_new_password(struct eap_sm *sm, size_t *len)
 {
 	struct eap_peer_config *config = eap_get_config(sm);

+ 4 - 0
components/wpa_supplicant/src/eap_peer/eap.h

@@ -39,9 +39,12 @@ int g_wpa_password_len;
 u8 *g_wpa_new_password;
 int g_wpa_new_password_len;
 
+char *g_wpa_ttls_phase2_type;
+
 const u8 * eap_get_eapKeyData(struct eap_sm *sm, size_t *len);
 void eap_deinit_prev_method(struct eap_sm *sm, const char *txt);
 struct wpabuf * eap_sm_build_nak(struct eap_sm *sm, EapType type, u8 id);
+struct wpabuf * eap_sm_buildIdentity(struct eap_sm *sm, int id, int encrypted);
 int eap_peer_blob_init(struct eap_sm *sm);
 void eap_peer_blob_deinit(struct eap_sm *sm);
 int eap_peer_config_init(
@@ -50,5 +53,6 @@ int eap_peer_config_init(
 void eap_peer_config_deinit(struct eap_sm *sm);
 void eap_sm_abort(struct eap_sm *sm);
 int eap_peer_register_methods(void);
+void eap_sm_request_identity(struct eap_sm *sm);
 
 #endif /* EAP_H */

+ 144 - 0
components/wpa_supplicant/src/eap_peer/eap_config.h

@@ -26,8 +26,24 @@ struct eap_peer_config {
 	 */
 	size_t identity_len;
 
+	/**
+	 * anonymous_identity -  Anonymous EAP Identity
+	 *
+	 * This field is used for unencrypted use with EAP types that support
+	 * different tunnelled identity, e.g., EAP-TTLS, in order to reveal the
+	 * real identity (identity field) only to the authentication server.
+	 *
+	 * If not set, the identity field will be used for both unencrypted and
+	 * protected fields.
+	 *
+	 * This field can also be used with EAP-SIM/AKA/AKA' to store the
+	 * pseudonym identity.
+	 */
 	u8 *anonymous_identity;
 
+	/**
+	 * anonymous_identity_len - Length of anonymous_identity
+	 */
 	size_t anonymous_identity_len;
 
 	/**
@@ -148,24 +164,80 @@ struct eap_peer_config {
 	 */
 	u8 *ca_cert2;
 
+	/**
+	 * ca_path2 - Directory path for CA certificate files (PEM) (Phase 2)
+	 *
+	 * This path may contain multiple CA certificates in OpenSSL format.
+	 * Common use for this is to point to system trusted CA list which is
+	 * often installed into directory like /etc/ssl/certs. If configured,
+	 * these certificates are added to the list of trusted CAs. ca_cert
+	 * may also be included in that case, but it is not required.
+	 *
+	 * This field is like ca_path, but used for phase 2 (inside
+	 * EAP-TTLS/PEAP/FAST tunnel) authentication.
+	 */
 	u8 *ca_path2;
 
+	/**
+	 * client_cert2 - File path to client certificate file
+	 *
+	 * This field is like client_cert, but used for phase 2 (inside
+	 * EAP-TTLS/PEAP/FAST tunnel) authentication. Full path to the
+	 * file should be used since working directory may change when
+	 * wpa_supplicant is run in the background.
+	 *
+	 * Alternatively, a named configuration blob can be used by setting
+	 * this to blob://blob_name.
+	 */
 	u8 *client_cert2;
 
+	/**
+	 * private_key2 - File path to client private key file
+	 *
+	 * This field is like private_key, but used for phase 2 (inside
+	 * EAP-TTLS/PEAP/FAST tunnel) authentication. Full path to the
+	 * file should be used since working directory may change when
+	 * wpa_supplicant is run in the background.
+	 *
+	 * Alternatively, a named configuration blob can be used by setting
+	 * this to blob://blob_name.
+	 */
 	u8 *private_key2;
 
 	u8 *private_key2_password;
 
 	/**
 	 * eap_methods - Allowed EAP methods
+	 *
+	 * (vendor=EAP_VENDOR_IETF,method=EAP_TYPE_NONE) terminated list of
+	 * allowed EAP methods or %NULL if all methods are accepted.
 	 */
 	struct eap_method_type *eap_methods;
 
 
 	char *phase1;
 
+	/**
+	 * phase2 - Phase2 (inner authentication with TLS tunnel) parameters
+	 *
+	 * String with field-value pairs, e.g., "auth=MSCHAPV2" for EAP-PEAP or
+	 * "autheap=MSCHAPV2 autheap=MD5" for EAP-TTLS. "mschapv2_retry=0" can
+	 * be used to disable MSCHAPv2 password retry in authentication failure
+	 * cases.
+	 */
 	char *phase2;
 
+	/**
+	 * pcsc - Parameters for PC/SC smartcard interface for USIM and GSM SIM
+	 *
+	 * This field is used to configure PC/SC smartcard interface.
+	 * Currently, the only configuration is whether this field is %NULL (do
+	 * not use PC/SC) or non-NULL (e.g., "") to enable PC/SC.
+	 *
+	 * This field is used for EAP-SIM and EAP-AKA.
+	 */
+	char *pcsc;
+
 	/**
 	 * pin - PIN for USIM, GSM SIM, and smartcards
 	 *
@@ -177,8 +249,80 @@ struct eap_peer_config {
 	 */
 	char *pin;
 
+	/**
+	 * pending_req_identity - Whether there is a pending identity request
+	 *
+	 * This field should not be set in configuration step. It is only used
+	 * internally when control interface is used to request needed
+	 * information.
+	 */
+	int pending_req_identity;
+
+	/**
+	 * pending_req_password - Whether there is a pending password request
+	 *
+	 * This field should not be set in configuration step. It is only used
+	 * internally when control interface is used to request needed
+	 * information.
+	 */
+	int pending_req_password;
+
+	/**
+	 * pending_req_pin - Whether there is a pending PIN request
+	 *
+	 * This field should not be set in configuration step. It is only used
+	 * internally when control interface is used to request needed
+	 * information.
+	 */
+	int pending_req_pin;
+
+	/**
+	 * pending_req_new_password - Pending password update request
+	 *
+	 * This field should not be set in configuration step. It is only used
+	 * internally when control interface is used to request needed
+	 * information.
+	 */
+	int pending_req_new_password;
+
+	/**
+	 * pending_req_passphrase - Pending passphrase request
+	 *
+	 * This field should not be set in configuration step. It is only used
+	 * internally when control interface is used to request needed
+	 * information.
+	 */
+	int pending_req_passphrase;
+
+	/**
+	 * pending_req_otp - Whether there is a pending OTP request
+	 *
+	 * This field should not be set in configuration step. It is only used
+	 * internally when control interface is used to request needed
+	 * information.
+	 */
+	char *pending_req_otp;
+
+	/**
+	 * mschapv2_retry - MSCHAPv2 retry in progress
+	 *
+	 * This field is used internally by EAP-MSCHAPv2 and should not be set
+	 * as part of configuration.
+	 */
 	int mschapv2_retry;
+
+	/**
+	 * new_password - New password for password update
+	 *
+	 * This field is used during MSCHAPv2 password update. This is normally
+	 * requested from the user through the control interface and not set
+	 * from configuration.
+	 */
 	u8 *new_password;
+
+	/**
+	 * new_password_len - Length of new_password field
+	 */
 	size_t new_password_len;
 
 	/**

+ 183 - 8
components/wpa_supplicant/src/eap_peer/eap_i.h

@@ -57,38 +57,206 @@ struct eap_method_ret {
 
 struct eap_sm;
 
+/**
+ * struct eap_method - EAP method interface
+ * This structure defines the EAP method interface. Each method will need to
+ * register its own EAP type, EAP name, and set of function pointers for method
+ * specific operations. This interface is based on section 4.4 of RFC 4137.
+ */
 struct eap_method {
 	/**
-	 * vendor -EAP Vendor-ID
+	 * vendor - EAP Vendor-ID (EAP_VENDOR_*) (0 = IETF)
 	 */
 	int vendor;
 
 	/**
-	 * method - EAP type number
+	 * method - EAP type number (EAP_TYPE_*)
 	 */
-	EapType method;	
+	EapType method;
 
 	/**
 	 * name - Name of the method (e.g., "TLS")
 	 */
 	const char *name;
 
-	struct eap_method *next;
-
+	/**
+	 * init - Initialize an EAP method
+	 * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+	 * Returns: Pointer to allocated private data, or %NULL on failure
+	 *
+	 * This function is used to initialize the EAP method explicitly
+	 * instead of using METHOD_INIT state as specific in RFC 4137. The
+	 * method is expected to initialize it method-specific state and return
+	 * a pointer that will be used as the priv argument to other calls.
+	 */
 	void * (*init)(struct eap_sm *sm);
+
+	/**
+	 * deinit - Deinitialize an EAP method
+	 * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+	 * @priv: Pointer to private EAP method data from eap_method::init()
+	 *
+	 * Deinitialize the EAP method and free any allocated private data.
+	 */
 	void (*deinit)(struct eap_sm *sm, void *priv);
+
+	/**
+	 * process - Process an EAP request
+	 * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+	 * @priv: Pointer to private EAP method data from eap_method::init()
+	 * @ret: Return values from EAP request validation and processing
+	 * @reqData: EAP request to be processed (eapReqData)
+	 * Returns: Pointer to allocated EAP response packet (eapRespData)
+	 *
+	 * This function is a combination of m.check(), m.process(), and
+	 * m.buildResp() procedures defined in section 4.4 of RFC 4137 In other
+	 * words, this function validates the incoming request, processes it,
+	 * and build a response packet. m.check() and m.process() return values
+	 * are returned through struct eap_method_ret *ret variable. Caller is
+	 * responsible for freeing the returned EAP response packet.
+	 */
 	struct wpabuf * (*process)(struct eap_sm *sm, void *priv,
 				   struct eap_method_ret *ret,
 				   const struct wpabuf *reqData);
 	bool (*isKeyAvailable)(struct eap_sm *sm, void *priv);
 	u8 * (*getKey)(struct eap_sm *sm, void *priv, size_t *len);
+
+	/**
+	 * get_status - Get EAP method status
+	 * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+	 * @priv: Pointer to private EAP method data from eap_method::init()
+	 * @buf: Buffer for status information
+	 * @buflen: Maximum buffer length
+	 * @verbose: Whether to include verbose status information
+	 * Returns: Number of bytes written to buf
+	 *
+	 * Query EAP method for status information. This function fills in a
+	 * text area with current status information from the EAP method. If
+	 * the buffer (buf) is not large enough, status information will be
+	 * truncated to fit the buffer.
+	 */
 	int (*get_status)(struct eap_sm *sm, void *priv, char *buf,
 			  size_t buflen, int verbose);
-	const u8 * (*get_identity)(struct eap_sm *sm, void *priv, size_t *len);
-	void (*free)(struct eap_method *method);
+
+	/**
+	 * has_reauth_data - Whether method is ready for fast reauthentication
+	 * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+	 * @priv: Pointer to private EAP method data from eap_method::init()
+	 * Returns: %TRUE or %FALSE based on whether fast reauthentication is
+	 * possible
+	 *
+	 * This function is an optional handler that only EAP methods
+	 * supporting fast re-authentication need to implement.
+	 */
 	bool (*has_reauth_data)(struct eap_sm *sm, void *priv);
+
+	/**
+	 * deinit_for_reauth - Release data that is not needed for fast re-auth
+	 * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+	 * @priv: Pointer to private EAP method data from eap_method::init()
+	 *
+	 * This function is an optional handler that only EAP methods
+	 * supporting fast re-authentication need to implement. This is called
+	 * when authentication has been completed and EAP state machine is
+	 * requesting that enough state information is maintained for fast
+	 * re-authentication
+	 */
 	void (*deinit_for_reauth)(struct eap_sm *sm, void *priv);
+
+	/**
+	 * init_for_reauth - Prepare for start of fast re-authentication
+	 * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+	 * @priv: Pointer to private EAP method data from eap_method::init()
+	 *
+	 * This function is an optional handler that only EAP methods
+	 * supporting fast re-authentication need to implement. This is called
+	 * when EAP authentication is started and EAP state machine is
+	 * requesting fast re-authentication to be used.
+	 */
 	void * (*init_for_reauth)(struct eap_sm *sm, void *priv);
+
+	/**
+	 * get_identity - Get method specific identity for re-authentication
+	 * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+	 * @priv: Pointer to private EAP method data from eap_method::init()
+	 * @len: Length of the returned identity
+	 * Returns: Pointer to the method specific identity or %NULL if default
+	 * identity is to be used
+	 *
+	 * This function is an optional handler that only EAP methods
+	 * that use method specific identity need to implement.
+	 */
+	const u8 * (*get_identity)(struct eap_sm *sm, void *priv, size_t *len);
+
+	/**
+	 * free - Free EAP method data
+	 * @method: Pointer to the method data registered with
+	 * eap_peer_method_register().
+	 *
+	 * This function will be called when the EAP method is being
+	 * unregistered. If the EAP method allocated resources during
+	 * registration (e.g., allocated struct eap_method), they should be
+	 * freed in this function. No other method functions will be called
+	 * after this call. If this function is not defined (i.e., function
+	 * pointer is %NULL), a default handler is used to release the method
+	 * data with free(method). This is suitable for most cases.
+	 */
+	void (*free)(struct eap_method *method);
+
+#define EAP_PEER_METHOD_INTERFACE_VERSION 1
+	/**
+	 * version - Version of the EAP peer method interface
+	 *
+	 * The EAP peer method implementation should set this variable to
+	 * EAP_PEER_METHOD_INTERFACE_VERSION. This is used to verify that the
+	 * EAP method is using supported API version when using dynamically
+	 * loadable EAP methods.
+	 */
+	int version;
+
+	/**
+	 * next - Pointer to the next EAP method
+	 *
+	 * This variable is used internally in the EAP method registration code
+	 * to create a linked list of registered EAP methods.
+	 */
+	struct eap_method *next;
+
+#ifdef CONFIG_DYNAMIC_EAP_METHODS
+	/**
+	 * dl_handle - Handle for the dynamic library
+	 *
+	 * This variable is used internally in the EAP method registration code
+	 * to store a handle for the dynamic library. If the method is linked
+	 * in statically, this is %NULL.
+	 */
+	void *dl_handle;
+#endif /* CONFIG_DYNAMIC_EAP_METHODS */
+
+	/**
+	 * get_emsk - Get EAP method specific keying extended material (EMSK)
+	 * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+	 * @priv: Pointer to private EAP method data from eap_method::init()
+	 * @len: Pointer to a variable to store EMSK length
+	 * Returns: EMSK or %NULL if not available
+	 *
+	 * This function can be used to get the extended keying material from
+	 * the EAP method. The key may already be stored in the method-specific
+	 * private data or this function may derive the key.
+	 */
+	u8 * (*get_emsk)(struct eap_sm *sm, void *priv, size_t *len);
+
+	/**
+	 * getSessionId - Get EAP method specific Session-Id
+	 * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+	 * @priv: Pointer to private EAP method data from eap_method::init()
+	 * @len: Pointer to a variable to store Session-Id length
+	 * Returns: Session-Id or %NULL if not available
+	 *
+	 * This function can be used to get the Session-Id from the EAP method.
+	 * The Session-Id may already be stored in the method-specific private
+	 * data or this function may derive the Session-Id.
+	 */
 	u8 * (*getSessionId)(struct eap_sm *sm, void *priv, size_t *len);
 };
 
@@ -109,7 +277,10 @@ enum SIG_WPA2 {
  * struct eap_sm - EAP state machine data
  */
 struct eap_sm {
+	/* not defined in RFC 4137 */
+	Boolean changed;
 	void *eap_method_priv;
+	int init_phase2;
 
 	void *ssl_ctx;
 
@@ -125,7 +296,6 @@ struct eap_sm {
 #endif
 	u8 finish_state;
 
-	int init_phase2;
 	bool peap_done;
 
 	u8 *eapKeyData;
@@ -143,6 +313,11 @@ const u8 * eap_get_config_identity(struct eap_sm *sm, size_t *len);
 const u8 * eap_get_config_password(struct eap_sm *sm, size_t *len);
 const u8 * eap_get_config_password2(struct eap_sm *sm, size_t *len, int *hash);
 const u8 * eap_get_config_new_password(struct eap_sm *sm, size_t *len);
+const u8 * eap_get_config_otp(struct eap_sm *sm, size_t *len);
+void eap_clear_config_otp(struct eap_sm *sm);
+const char * eap_get_config_phase1(struct eap_sm *sm);
+const char * eap_get_config_phase2(struct eap_sm *sm);
+int eap_get_config_fragment_size(struct eap_sm *sm);
 struct eap_peer_config * eap_get_config(struct eap_sm *sm);
 const struct wpa_config_blob * eap_get_config_blob(struct eap_sm *sm, const char *name);
 bool wifi_sta_get_enterprise_disable_time_check(void);

+ 549 - 66
components/wpa_supplicant/src/eap_peer/eap_ttls.c

@@ -10,8 +10,10 @@
 
 #ifdef EAP_TTLS
 #include "utils/common.h"
+#include "crypto/ms_funcs.h"
 #include "crypto/sha1.h"
 #include "tls/tls.h"
+#include "eap_peer/chap.h"
 #include "eap_peer/eap.h"
 #include "eap_peer/eap_ttls.h"
 #include "eap_peer/mschapv2.h"
@@ -74,29 +76,21 @@ static void * eap_ttls_init(struct eap_sm *sm)
 	if (data == NULL)
 		return NULL;
 	data->ttls_version = EAP_TTLS_VERSION;
-	data->phase2_type = EAP_TTLS_PHASE2_MSCHAPV2;
+	data->phase2_type = EAP_TTLS_PHASE2_EAP;
 	
-/*	
-    selected = "MSCHAPV2";
 	if (config && config->phase2) {
 		if (os_strstr(config->phase2, "autheap=")) {
-			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) {
 		if (eap_peer_select_phase2_methods(config, "autheap=",
@@ -110,7 +104,6 @@ static void * eap_ttls_init(struct eap_sm *sm)
 		data->phase2_eap_type.vendor = EAP_VENDOR_IETF;
 		data->phase2_eap_type.method = EAP_TYPE_NONE;
 	}
-*/
 
 	if (eap_peer_tls_ssl_init(sm, &data->ssl, config, EAP_TYPE_TTLS)) {
 		wpa_printf(MSG_ERROR, "EAP-TTLS: Failed to initialize SSL.\n");
@@ -121,6 +114,7 @@ static void * eap_ttls_init(struct eap_sm *sm)
 	return data;
 }
 
+
 static void eap_ttls_phase2_eap_deinit(struct eap_sm *sm,
 			   struct eap_ttls_data *data)
 {
@@ -131,6 +125,7 @@ static void eap_ttls_phase2_eap_deinit(struct eap_sm *sm,
 	}
 }
 
+
 static void eap_ttls_deinit(struct eap_sm *sm, void *priv)
 {
 	struct eap_ttls_data *data = priv;
@@ -145,6 +140,7 @@ static void eap_ttls_deinit(struct eap_sm *sm, void *priv)
 	os_free(data);
 }
 
+
 static u8 * eap_ttls_avp_hdr(u8 *avphdr, u32 avp_code, u32 vendor_id,
 		 int mandatory, size_t len)
 {
@@ -181,6 +177,32 @@ static u8 * eap_ttls_avp_add(u8 *start, u8 *avphdr, u32 avp_code,
 	return pos;
 }
 
+
+static int eap_ttls_avp_encapsulate(struct wpabuf **resp, u32 avp_code,
+				    int mandatory)
+{
+	struct wpabuf *msg;
+	u8 *avp, *pos;
+
+	msg = wpabuf_alloc(sizeof(struct ttls_avp) + wpabuf_len(*resp) + 4);
+	if (msg == NULL) {
+		wpabuf_free(*resp);
+		*resp = NULL;
+		return -1;
+	}
+
+	avp = wpabuf_mhead(msg);
+	pos = eap_ttls_avp_hdr(avp, avp_code, 0, mandatory, wpabuf_len(*resp));
+	os_memcpy(pos, wpabuf_head(*resp), wpabuf_len(*resp));
+	pos += wpabuf_len(*resp);
+	AVP_PAD(avp, pos);
+	wpabuf_free(*resp);
+	wpabuf_put(msg, pos - avp);
+	*resp = msg;
+	return 0;
+}
+
+
 static int eap_ttls_v0_derive_key(struct eap_sm *sm,
 		       struct eap_ttls_data *data)
 {
@@ -193,11 +215,19 @@ static int eap_ttls_v0_derive_key(struct eap_sm *sm,
 		return -1;
 	}
 
+	wpa_hexdump_key(MSG_DEBUG, "EAP-TTLS: Derived key",
+			data->key_data, EAP_TLS_KEY_LEN);
+	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\n");
 	}
@@ -206,17 +236,187 @@ static int eap_ttls_v0_derive_key(struct eap_sm *sm,
 }
 
 
+#ifndef CONFIG_FIPS
 static u8 * eap_ttls_implicit_challenge(struct eap_sm *sm,
 			    struct eap_ttls_data *data, size_t len)
 {
 	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,
+					      u8 method)
+{
+	size_t i;
+	for (i = 0; i < data->num_phase2_eap_types; i++) {
+		if (data->phase2_eap_types[i].vendor != EAP_VENDOR_IETF ||
+		    data->phase2_eap_types[i].method != method)
+			continue;
+
+		data->phase2_eap_type.vendor =
+			data->phase2_eap_types[i].vendor;
+		data->phase2_eap_type.method =
+			data->phase2_eap_types[i].method;
+		wpa_printf(MSG_DEBUG, "EAP-TTLS: Selected "
+			   "Phase 2 EAP vendor %d method %d",
+			   data->phase2_eap_type.vendor,
+			   data->phase2_eap_type.method);
+		break;
+	}
+}
+
+
+static int eap_ttls_phase2_eap_process(struct eap_sm *sm,
+				       struct eap_ttls_data *data,
+				       struct eap_method_ret *ret,
+				       struct eap_hdr *hdr, size_t len,
+				       struct wpabuf **resp)
+{
+	struct wpabuf msg;
+	struct eap_method_ret iret;
+
+	os_memset(&iret, 0, sizeof(iret));
+	wpabuf_set(&msg, hdr, len);
+	*resp = data->phase2_method->process(sm, data->phase2_priv, &iret,
+					     &msg);
+	if ((iret.methodState == METHOD_DONE ||
+	     iret.methodState == METHOD_MAY_CONT) &&
+	    (iret.decision == DECISION_UNCOND_SUCC ||
+	     iret.decision == DECISION_COND_SUCC ||
+	     iret.decision == DECISION_FAIL)) {
+		ret->methodState = iret.methodState;
+		ret->decision = iret.decision;
+	}
+
+	return 0;
+}
+
+
+static int eap_ttls_phase2_request_eap_method(struct eap_sm *sm,
+					      struct eap_ttls_data *data,
+					      struct eap_method_ret *ret,
+					      struct eap_hdr *hdr, size_t len,
+					      u8 method, struct wpabuf **resp)
+{
+#ifdef EAP_TNC
+	if (data->tnc_started && data->phase2_method &&
+	    data->phase2_priv && method == EAP_TYPE_TNC &&
+	    data->phase2_eap_type.method == EAP_TYPE_TNC)
+		return eap_ttls_phase2_eap_process(sm, data, ret, hdr, len,
+						   resp);
+
+	if (data->ready_for_tnc && !data->tnc_started &&
+	    method == EAP_TYPE_TNC) {
+		wpa_printf(MSG_DEBUG, "EAP-TTLS: Start TNC after completed "
+			   "EAP method");
+		data->tnc_started = 1;
+	}
+
+	if (data->tnc_started) {
+		if (data->phase2_eap_type.vendor != EAP_VENDOR_IETF ||
+		    data->phase2_eap_type.method == EAP_TYPE_TNC) {
+			wpa_printf(MSG_DEBUG, "EAP-TTLS: Unexpected EAP "
+				   "type %d for TNC", method);
+			return -1;
+		}
+
+		data->phase2_eap_type.vendor = EAP_VENDOR_IETF;
+		data->phase2_eap_type.method = method;
+		wpa_printf(MSG_DEBUG, "EAP-TTLS: Selected "
+			   "Phase 2 EAP vendor %d method %d (TNC)",
+			   data->phase2_eap_type.vendor,
+			   data->phase2_eap_type.method);
+
+		if (data->phase2_type == EAP_TTLS_PHASE2_EAP)
+			eap_ttls_phase2_eap_deinit(sm, data);
+	}
+#endif /* EAP_TNC */
+
+	if (data->phase2_eap_type.vendor == EAP_VENDOR_IETF &&
+	    data->phase2_eap_type.method == EAP_TYPE_NONE)
+		eap_ttls_phase2_select_eap_method(data, method);
+
+	if (method != data->phase2_eap_type.method || method == EAP_TYPE_NONE)
+	{
+		if (eap_peer_tls_phase2_nak(data->phase2_eap_types,
+					    data->num_phase2_eap_types,
+					    hdr, resp))
+			return -1;
+		return 0;
+	}
+
+	if (data->phase2_priv == NULL) {
+		data->phase2_method = eap_peer_get_eap_method(
+			EAP_VENDOR_IETF, method);
+		if (data->phase2_method) {
+			sm->init_phase2 = 1;
+			data->phase2_priv = data->phase2_method->init(sm);
+			sm->init_phase2 = 0;
+		}
+	}
+	if (data->phase2_priv == NULL || data->phase2_method == NULL) {
+		wpa_printf(MSG_INFO, "EAP-TTLS: failed to initialize "
+			   "Phase 2 EAP method %d", method);
+		return -1;
+	}
+
+	return eap_ttls_phase2_eap_process(sm, data, ret, hdr, len, resp);
+}
+
+
+static int eap_ttls_phase2_request_eap(struct eap_sm *sm,
+				       struct eap_ttls_data *data,
+				       struct eap_method_ret *ret,
+				       struct eap_hdr *hdr,
+				       struct wpabuf **resp)
+{
+	size_t len = be_to_host16(hdr->length);
+	u8 *pos;
+	struct eap_peer_config *config = eap_get_config(sm);
+
+	if (len <= sizeof(struct eap_hdr)) {
+		wpa_printf(MSG_INFO, "EAP-TTLS: too short "
+			   "Phase 2 request (len=%lu)", (unsigned long) len);
+		return -1;
+	}
+	pos = (u8 *) (hdr + 1);
+	wpa_printf(MSG_DEBUG, "EAP-TTLS: Phase 2 EAP Request: type=%d", *pos);
+	switch (*pos) {
+	case EAP_TYPE_IDENTITY:
+		*resp = eap_sm_buildIdentity(sm, hdr->identifier, 1);
+		break;
+	default:
+		if (eap_ttls_phase2_request_eap_method(sm, data, ret, hdr, len,
+						       *pos, resp) < 0)
+			return -1;
+		break;
+	}
+
+	if (*resp == NULL &&
+	    (config->pending_req_identity || config->pending_req_password ||
+	     config->pending_req_otp)) {
+		return 0;
+	}
+
+	if (*resp == NULL)
+		return -1;
+
+	wpa_hexdump_buf(MSG_DEBUG, "EAP-TTLS: AVP encapsulate EAP Response",
+			*resp);
+	return eap_ttls_avp_encapsulate(resp, RADIUS_ATTR_EAP_MESSAGE, 1);
+}
+
 
 static int eap_ttls_phase2_request_mschapv2(struct eap_sm *sm,
-				 struct eap_ttls_data *data,
-				 struct eap_method_ret *ret,
-				 struct wpabuf **resp)
+					    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;
@@ -309,8 +509,232 @@ static int eap_ttls_phase2_request_mschapv2(struct eap_sm *sm,
 	wpa_printf(MSG_ERROR, "EAP-TTLS: MSCHAPv2 not included in the build\n");
 	return -1;
 #endif /* EAP_MSCHAPv2 */
+#endif /* CONFIG_FIPS */
+}
+
+
+static int eap_ttls_phase2_request_mschap(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: MSCHAP not supported in FIPS build");
+	return -1;
+#else /* CONFIG_FIPS */
+	struct wpabuf *msg;
+	u8 *buf, *pos, *challenge;
+	const u8 *identity, *password;
+	size_t identity_len, password_len;
+	int pwhash;
+
+	wpa_printf(MSG_DEBUG, "EAP-TTLS: Phase 2 MSCHAP Request");
+
+	identity = eap_get_config_identity(sm, &identity_len);
+	password = eap_get_config_password2(sm, &password_len, &pwhash);
+	if (identity == NULL || password == NULL)
+		return -1;
+
+	msg = wpabuf_alloc(identity_len + 1000);
+	if (msg == NULL) {
+		wpa_printf(MSG_ERROR,
+			   "EAP-TTLS/MSCHAP: Failed to allocate memory");
+		return -1;
+	}
+	pos = buf = wpabuf_mhead(msg);
+
+	/* User-Name */
+	pos = eap_ttls_avp_add(buf, pos, RADIUS_ATTR_USER_NAME, 0, 1,
+			       identity, identity_len);
+
+	/* MS-CHAP-Challenge */
+	challenge = eap_ttls_implicit_challenge(
+		sm, data, EAP_TTLS_MSCHAP_CHALLENGE_LEN + 1);
+	if (challenge == NULL) {
+		wpabuf_free(msg);
+		wpa_printf(MSG_ERROR, "EAP-TTLS/MSCHAP: Failed to derive "
+			   "implicit challenge");
+		return -1;
+	}
+
+	pos = eap_ttls_avp_add(buf, pos, RADIUS_ATTR_MS_CHAP_CHALLENGE,
+			       RADIUS_VENDOR_ID_MICROSOFT, 1,
+			       challenge, EAP_TTLS_MSCHAP_CHALLENGE_LEN);
+
+	/* MS-CHAP-Response */
+	pos = eap_ttls_avp_hdr(pos, RADIUS_ATTR_MS_CHAP_RESPONSE,
+			       RADIUS_VENDOR_ID_MICROSOFT, 1,
+			       EAP_TTLS_MSCHAP_RESPONSE_LEN);
+	data->ident = challenge[EAP_TTLS_MSCHAP_CHALLENGE_LEN];
+	*pos++ = data->ident;
+	*pos++ = 1; /* Flags: Use NT style passwords */
+	os_memset(pos, 0, 24); /* LM-Response */
+	pos += 24;
+	if (pwhash) {
+		challenge_response(challenge, password, pos); /* NT-Response */
+		wpa_hexdump_key(MSG_DEBUG, "EAP-TTLS: MSCHAP password hash",
+				password, 16);
+	} else {
+		nt_challenge_response(challenge, password, password_len,
+				      pos); /* NT-Response */
+		wpa_hexdump_ascii_key(MSG_DEBUG, "EAP-TTLS: MSCHAP password",
+				      password, password_len);
+	}
+	wpa_hexdump(MSG_DEBUG, "EAP-TTLS: MSCHAP implicit challenge",
+		    challenge, EAP_TTLS_MSCHAP_CHALLENGE_LEN);
+	wpa_hexdump(MSG_DEBUG, "EAP-TTLS: MSCHAP response", pos, 24);
+	pos += 24;
+	os_free(challenge);
+	AVP_PAD(buf, pos);
+
+	wpabuf_put(msg, pos - buf);
+	*resp = msg;
+
+	/* 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 */
 }
 
+
+static int eap_ttls_phase2_request_pap(struct eap_sm *sm,
+				       struct eap_ttls_data *data,
+				       struct eap_method_ret *ret,
+				       struct wpabuf **resp)
+{
+	struct wpabuf *msg;
+	u8 *buf, *pos;
+	size_t pad;
+	const u8 *identity, *password;
+	size_t identity_len, password_len;
+
+	wpa_printf(MSG_DEBUG, "EAP-TTLS: Phase 2 PAP Request");
+
+	identity = eap_get_config_identity(sm, &identity_len);
+	password = eap_get_config_password(sm, &password_len);
+	if (identity == NULL || password == NULL)
+		return -1;
+
+	msg = wpabuf_alloc(identity_len + password_len + 100);
+	if (msg == NULL) {
+		wpa_printf(MSG_ERROR,
+			   "EAP-TTLS/PAP: Failed to allocate memory");
+		return -1;
+	}
+	pos = buf = wpabuf_mhead(msg);
+
+	/* User-Name */
+	pos = eap_ttls_avp_add(buf, pos, RADIUS_ATTR_USER_NAME, 0, 1,
+			       identity, identity_len);
+
+	/* User-Password; in RADIUS, this is encrypted, but EAP-TTLS encrypts
+	 * the data, so no separate encryption is used in the AVP itself.
+	 * However, the password is padded to obfuscate its length. */
+	pad = password_len == 0 ? 16 : (16 - (password_len & 15)) & 15;
+	pos = eap_ttls_avp_hdr(pos, RADIUS_ATTR_USER_PASSWORD, 0, 1,
+			       password_len + pad);
+	os_memcpy(pos, password, password_len);
+	pos += password_len;
+	os_memset(pos, 0, pad);
+	pos += pad;
+	AVP_PAD(buf, pos);
+
+	wpabuf_put(msg, pos - buf);
+	*resp = msg;
+
+	/* 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;
+}
+
+
+static int eap_ttls_phase2_request_chap(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: CHAP not supported in FIPS build");
+	return -1;
+#else /* CONFIG_FIPS */
+	struct wpabuf *msg;
+	u8 *buf, *pos, *challenge;
+	const u8 *identity, *password;
+	size_t identity_len, password_len;
+
+	wpa_printf(MSG_DEBUG, "EAP-TTLS: Phase 2 CHAP Request");
+
+	identity = eap_get_config_identity(sm, &identity_len);
+	password = eap_get_config_password(sm, &password_len);
+	if (identity == NULL || password == NULL)
+		return -1;
+
+	msg = wpabuf_alloc(identity_len + 1000);
+	if (msg == NULL) {
+		wpa_printf(MSG_ERROR,
+			   "EAP-TTLS/CHAP: Failed to allocate memory");
+		return -1;
+	}
+	pos = buf = wpabuf_mhead(msg);
+
+	/* User-Name */
+	pos = eap_ttls_avp_add(buf, pos, RADIUS_ATTR_USER_NAME, 0, 1,
+			       identity, identity_len);
+
+	/* CHAP-Challenge */
+	challenge = eap_ttls_implicit_challenge(
+		sm, data, EAP_TTLS_CHAP_CHALLENGE_LEN + 1);
+	if (challenge == NULL) {
+		wpabuf_free(msg);
+		wpa_printf(MSG_ERROR, "EAP-TTLS/CHAP: Failed to derive "
+			   "implicit challenge");
+		return -1;
+	}
+
+	pos = eap_ttls_avp_add(buf, pos, RADIUS_ATTR_CHAP_CHALLENGE, 0, 1,
+			       challenge, EAP_TTLS_CHAP_CHALLENGE_LEN);
+
+	/* CHAP-Password */
+	pos = eap_ttls_avp_hdr(pos, RADIUS_ATTR_CHAP_PASSWORD, 0, 1,
+			       1 + EAP_TTLS_CHAP_PASSWORD_LEN);
+	data->ident = challenge[EAP_TTLS_CHAP_CHALLENGE_LEN];
+	*pos++ = data->ident;
+
+	/* MD5(Ident + Password + Challenge) */
+	chap_md5(data->ident, password, password_len, challenge,
+		 EAP_TTLS_CHAP_CHALLENGE_LEN, pos);
+
+	wpa_hexdump_ascii(MSG_DEBUG, "EAP-TTLS: CHAP username",
+			  identity, identity_len);
+	wpa_hexdump_ascii_key(MSG_DEBUG, "EAP-TTLS: CHAP password",
+			      password, password_len);
+	wpa_hexdump(MSG_DEBUG, "EAP-TTLS: CHAP implicit challenge",
+		    challenge, EAP_TTLS_CHAP_CHALLENGE_LEN);
+	wpa_hexdump(MSG_DEBUG, "EAP-TTLS: CHAP password",
+		    pos, EAP_TTLS_CHAP_PASSWORD_LEN);
+	pos += EAP_TTLS_CHAP_PASSWORD_LEN;
+	os_free(challenge);
+	AVP_PAD(buf, pos);
+
+	wpabuf_put(msg, pos - buf);
+	*resp = msg;
+
+	/* 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 */
+}
+
+
 static int eap_ttls_phase2_request(struct eap_sm *sm,
 			struct eap_ttls_data *data,
 			struct eap_method_ret *ret,
@@ -329,32 +753,14 @@ static int eap_ttls_phase2_request(struct eap_sm *sm,
 	}
 #endif /* EAP_TNC */
 
-	if (phase2_type == EAP_TTLS_PHASE2_MSCHAPV2) {
-		if (eap_get_config_identity(sm, &len) == NULL) {
-			wpa_printf(MSG_ERROR, "EAP-TTLS: Identity not configured\n");
-			printf("[Debug] Return because no identity  EAP_TTLS_PHASE2_MSCHAPV2\n");
-			return 0;
-		}
-		if (eap_get_config_password(sm, &len) == NULL) {
-			wpa_printf(MSG_ERROR, "EAP-TTLS: Password not configured\n");
-			printf("[Debug] Return because no password  EAP_TTLS_PHASE2_MSCHAPV2\n");
-			return 0;
-		}
-
-		res = eap_ttls_phase2_request_mschapv2(sm, data, ret, resp);
-	} else {
-		wpa_printf(MSG_ERROR, "EAP-TTLS: Phase 2 - Unknown type %d\n", phase2_type);
-		res = -1;
-	}
-
-/*	if (phase2_type == EAP_TTLS_PHASE2_MSCHAPV2 ||
+	if (phase2_type == EAP_TTLS_PHASE2_MSCHAPV2 ||
 	    phase2_type == EAP_TTLS_PHASE2_MSCHAP ||
 	    phase2_type == EAP_TTLS_PHASE2_PAP ||
 	    phase2_type == EAP_TTLS_PHASE2_CHAP) {
 		if (eap_get_config_identity(sm, &len) == NULL) {
 			wpa_printf(MSG_ERROR, "EAP-TTLS: Identity not configured\n");
-			if (eap_get_config_password(sm, &len) == NULL);
-            printf("[Debug] Return because no identity  EAP_TTLS_PHASE2_MSCHAPV2 EAP_TTLS_PHASE2_MSCHAP\n");
+            if (eap_get_config_password(sm, &len) == NULL)
+                printf("[Debug] Return because no identity  EAP_TTLS_PHASE2_MSCHAPV2 EAP_TTLS_PHASE2_MSCHAP\n");
 			return 0;
 		}
 
@@ -367,31 +773,25 @@ static int eap_ttls_phase2_request(struct eap_sm *sm,
 
 	switch (phase2_type) {
 	case EAP_TTLS_PHASE2_EAP:
-	    printf("[Debug] EAP_TTLS_PHASE2_EAP typed \n");
 		res = eap_ttls_phase2_request_eap(sm, data, ret, hdr, resp);
 		break;
 	case EAP_TTLS_PHASE2_MSCHAPV2:
-	    printf("[Debug] EAP_TTLS_PHASE2_MSCHAPV2 typed \n");
 		res = eap_ttls_phase2_request_mschapv2(sm, data, ret, resp);
 		break;
 	case EAP_TTLS_PHASE2_MSCHAP:
-	    printf("[Debug] EAP_TTLS_PHASE2_MSCHAP typed \n");
 		res = eap_ttls_phase2_request_mschap(sm, data, ret, resp);
 		break;
 	case EAP_TTLS_PHASE2_PAP:
-	    printf("[Debug] EAP_TTLS_PHASE2_PAP typed \n");
 		res = eap_ttls_phase2_request_pap(sm, data, ret, resp);
 		break;
 	case EAP_TTLS_PHASE2_CHAP:
-	    printf("[Debug] EAP_TTLS_PHASE2_CHAP typed \n");
 		res = eap_ttls_phase2_request_chap(sm, data, ret, resp);
 		break;
 	default:
-	    printf("[Debug] Default typed \n");
 		wpa_printf(MSG_ERROR, "EAP-TTLS: Phase 2 - Unknown\n");
 		res = -1;
 		break;
-	}*/
+	}
 
 	if (res < 0) {
 		ret->methodState = METHOD_DONE;
@@ -483,14 +883,20 @@ static int eap_ttls_parse_avp(u8 *pos, size_t left,
 		dlen -= 4;
 	}
 
+	wpa_hexdump(MSG_DEBUG, "EAP-TTLS: AVP data", dpos, dlen);
+
 	if (vendor_id == 0 && avp_code == RADIUS_ATTR_EAP_MESSAGE) {
 		if (eap_ttls_parse_attr_eap(dpos, dlen, parse) < 0)
 			return -1;
 	} else if (vendor_id == 0 && avp_code == RADIUS_ATTR_REPLY_MESSAGE) {
 		/* This is an optional message that can be displayed to
 		 * the user. */
+		wpa_hexdump_ascii(MSG_DEBUG, "EAP-TTLS: AVP - Reply-Message",
+				  dpos, dlen);
 	} else if (vendor_id == RADIUS_VENDOR_ID_MICROSOFT &&
 		   avp_code == RADIUS_ATTR_MS_CHAP2_SUCCESS) {
+		wpa_hexdump_ascii(MSG_DEBUG, "EAP-TTLS: MS-CHAP2-Success",
+				  dpos, dlen);
 		if (dlen != 43) {
 			wpa_printf(MSG_ERROR, "EAP-TTLS: Unexpected "
 				  "MS-CHAP2-Success length "
@@ -501,6 +907,8 @@ static int eap_ttls_parse_avp(u8 *pos, size_t left,
 		parse->mschapv2 = dpos;
 	} else if (vendor_id == RADIUS_VENDOR_ID_MICROSOFT &&
 		   avp_code == RADIUS_ATTR_MS_CHAP_ERROR) {
+		wpa_hexdump_ascii(MSG_DEBUG, "EAP-TTLS: MS-CHAP-Error",
+				  dpos, dlen);
 		parse->mschapv2_error = 1;
 	} else if (avp_flags & AVP_FLAGS_MANDATORY) {
 		wpa_printf(MSG_ERROR, "EAP-TTLS: Unsupported mandatory AVP "
@@ -526,6 +934,7 @@ static int eap_ttls_parse_avps(struct wpabuf *in_decrypted,
 
 	pos = wpabuf_mhead(in_decrypted);
 	left = wpabuf_len(in_decrypted);
+	wpa_hexdump(MSG_DEBUG, "EAP-TTLS: Decrypted Phase 2 AVPs", pos, left);
 	if (left < sizeof(struct ttls_avp)) {
 		wpa_printf(MSG_ERROR, "EAP-TTLS: Too short Phase 2 AVP frame"
 			  " len=%lu expected %lu or more - dropped\n",
@@ -597,6 +1006,63 @@ static int eap_ttls_encrypt_response(struct eap_sm *sm,
 	return 0;
 }
 
+
+static int eap_ttls_process_phase2_eap(struct eap_sm *sm,
+				       struct eap_ttls_data *data,
+				       struct eap_method_ret *ret,
+				       struct ttls_parse_avp *parse,
+				       struct wpabuf **resp)
+{
+	struct eap_hdr *hdr;
+	size_t len;
+
+	if (parse->eapdata == NULL) {
+		wpa_printf(MSG_WARNING, "EAP-TTLS: No EAP Message in the "
+			   "packet - dropped");
+		return -1;
+	}
+
+	wpa_hexdump(MSG_DEBUG, "EAP-TTLS: Phase 2 EAP",
+		    parse->eapdata, parse->eap_len);
+	hdr = (struct eap_hdr *) parse->eapdata;
+
+	if (parse->eap_len < sizeof(*hdr)) {
+		wpa_printf(MSG_WARNING, "EAP-TTLS: Too short Phase 2 EAP "
+			   "frame (len=%lu, expected %lu or more) - dropped",
+			   (unsigned long) parse->eap_len,
+			   (unsigned long) sizeof(*hdr));
+		return -1;
+	}
+	len = be_to_host16(hdr->length);
+	if (len > parse->eap_len) {
+		wpa_printf(MSG_INFO, "EAP-TTLS: Length mismatch in Phase 2 "
+			   "EAP frame (EAP hdr len=%lu, EAP data len in "
+			   "AVP=%lu)",
+			   (unsigned long) len,
+			   (unsigned long) parse->eap_len);
+		return -1;
+	}
+	wpa_printf(MSG_DEBUG, "EAP-TTLS: received Phase 2: code=%d "
+		   "identifier=%d length=%lu",
+		   hdr->code, hdr->identifier, (unsigned long) len);
+	switch (hdr->code) {
+	case EAP_CODE_REQUEST:
+		if (eap_ttls_phase2_request(sm, data, ret, hdr, resp)) {
+			wpa_printf(MSG_INFO, "EAP-TTLS: Phase2 Request "
+				   "processing failed");
+			return -1;
+		}
+		break;
+	default:
+		wpa_printf(MSG_INFO, "EAP-TTLS: Unexpected code=%d in "
+			   "Phase 2 EAP header", hdr->code);
+		return -1;
+	}
+
+	return 0;
+}
+
+
 static int eap_ttls_process_phase2_mschapv2(struct eap_sm *sm,
 				 struct eap_ttls_data *data,
 				 struct eap_method_ret *ret,
@@ -701,6 +1167,7 @@ static int eap_ttls_process_decrypted(struct eap_sm *sm,
 	struct wpabuf *resp = NULL;
 	int res;
 	enum phase2_types phase2_type = data->phase2_type;
+	struct eap_peer_config *config = eap_get_config(sm);
 
 #ifdef EAP_TNC
 	if (data->tnc_started)
@@ -708,6 +1175,11 @@ static int eap_ttls_process_decrypted(struct eap_sm *sm,
 #endif /* EAP_TNC */
 
 	switch (phase2_type) {
+	case EAP_TTLS_PHASE2_EAP:
+		if (eap_ttls_process_phase2_eap(sm, data, ret, parse, &resp) <
+		    0)
+			return -1;
+		break;
 	case EAP_TTLS_PHASE2_MSCHAPV2:
 		res = eap_ttls_process_phase2_mschapv2(sm, data, ret, parse);
 #ifdef EAP_TNC
@@ -724,11 +1196,6 @@ static int eap_ttls_process_decrypted(struct eap_sm *sm,
 		}
 #endif /* EAP_TNC */
 		return res;
-/*	case EAP_TTLS_PHASE2_EAP:
-		if (eap_ttls_process_phase2_eap(sm, data, ret, parse, &resp) <
-		    0)
-			return -1;
-		break;
 	case EAP_TTLS_PHASE2_MSCHAP:
 	case EAP_TTLS_PHASE2_PAP:
 	case EAP_TTLS_PHASE2_CHAP:
@@ -737,32 +1204,26 @@ static int eap_ttls_process_decrypted(struct eap_sm *sm,
 		    0)
 			return -1;
 		break;
-#else // EAP_TNC
-		// EAP-TTLS/{MSCHAP,PAP,CHAP} should not send any TLS tunneled
-		// requests to the supplicant
-		wpa_printf(MSG_ERROR, "EAP-TTLS: Phase 2 received unexpected "
-			  "tunneled data\n");
-		return -1;
-#endif // EAP_TNC
-*/
-	default:
+#else /* EAP_TNC */
+		/* EAP-TTLS/{MSCHAP,PAP,CHAP} should not send any TLS tunneled
+		 * requests to the supplicant */
+		wpa_printf(MSG_INFO, "EAP-TTLS: Phase 2 received unexpected "
+			   "tunneled data");
 		return -1;
+#endif /* EAP_TNC */
 	}
 
 	if (resp) {
 		if (eap_ttls_encrypt_response(sm, data, resp, identifier,
 					      out_data) < 0)
 			return -1;
-	} else {
-		wpabuf_free(data->pending_phase2_req);
-		data->pending_phase2_req = wpabuf_dup(in_decrypted);
-	}/* else if (config->pending_req_identity ||
+	} else if (config->pending_req_identity ||
 		   config->pending_req_password ||
 		   config->pending_req_otp ||
 		   config->pending_req_new_password) {
 		wpabuf_free(data->pending_phase2_req);
 		data->pending_phase2_req = wpabuf_dup(in_decrypted);
-	}*/
+	}
 
 	return 0;
 }
@@ -791,11 +1252,12 @@ static int eap_ttls_implicit_identity_request(struct eap_sm *sm,
 			  "processing failed\n");
 		retval = -1;
 	} else {
-		if (resp == NULL) {/* &&
+		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)) {*/
+		     config->pending_req_new_password)) {
 			/*
 			 * Use empty buffer to force implicit request
 			 * processing when EAP request is re-processed after
@@ -1118,15 +1580,15 @@ static int eap_ttls_get_status(struct eap_sm *sm, void *priv, char *buf,
 		return len;
 	len += ret;
 	switch (data->phase2_type) {
-	/*case EAP_TTLS_PHASE2_EAP:
+	case EAP_TTLS_PHASE2_EAP:
 		ret = os_snprintf(buf + len, buflen - len, "EAP-%s\n",
 				  data->phase2_method ?
 				  data->phase2_method->name : "?");
-		break;*/
+		break;
 	case EAP_TTLS_PHASE2_MSCHAPV2:
 		ret = snprintf(buf + len, buflen - len, "MSCHAPV2\n");
 		break;
-	/*case EAP_TTLS_PHASE2_MSCHAP:
+	case EAP_TTLS_PHASE2_MSCHAP:
 		ret = os_snprintf(buf + len, buflen - len, "MSCHAP\n");
 		break;
 	case EAP_TTLS_PHASE2_PAP:
@@ -1134,7 +1596,7 @@ static int eap_ttls_get_status(struct eap_sm *sm, void *priv, char *buf,
 		break;
 	case EAP_TTLS_PHASE2_CHAP:
 		ret = os_snprintf(buf + len, buflen - len, "CHAP\n");
-		break;*/
+		break;
 	default:
 		ret = 0;
 		break;
@@ -1191,6 +1653,26 @@ static u8 * eap_ttls_get_session_id(struct eap_sm *sm, void *priv, size_t *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;
@@ -1211,6 +1693,7 @@ int eap_peer_ttls_register(void)
 	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)

+ 25 - 0
components/wpa_supplicant/src/esp_supplicant/esp_wpa2.c

@@ -1148,3 +1148,28 @@ esp_err_t esp_wifi_sta_wpa2_ent_get_disable_time_check(bool *disable)
     return ESP_OK;
 }
 
+esp_err_t esp_wifi_sta_wpa2_ent_set_ttls_phase2_method(esp_eap_ttls_phase2_types type)
+{
+    switch (type) {
+        case ESP_EAP_TTLS_PHASE2_EAP:
+            g_wpa_ttls_phase2_type = "auth=EAP";
+            break;
+        case ESP_EAP_TTLS_PHASE2_MSCHAPV2:
+            g_wpa_ttls_phase2_type = "auth=MSCHAPV2";
+            break;
+        case ESP_EAP_TTLS_PHASE2_MSCHAP:
+            g_wpa_ttls_phase2_type = "auth=MSCHAP";
+            break;
+        case ESP_EAP_TTLS_PHASE2_PAP:
+            g_wpa_ttls_phase2_type = "auth=PAP";
+            break;
+        case ESP_EAP_TTLS_PHASE2_CHAP:
+            g_wpa_ttls_phase2_type = "auth=CHAP";
+            break;
+        default:
+            g_wpa_ttls_phase2_type = "auth=MSCHAPV2";
+            break;
+    }
+    return ESP_OK;
+}
+

+ 1 - 1
components/wpa_supplicant/src/utils/ext_password.h

@@ -25,7 +25,7 @@ void ext_password_free(struct wpabuf *pw);
 
 #define ext_password_init(b, p)
 #define ext_password_deinit(d)
-#define ext_password_get(d, n)
+#define ext_password_get(d, n) NULL
 #define ext_password_free(p)
 
 #endif /* CONFIG_EXT_PASSWORD */

+ 21 - 0
examples/wifi/wpa2_enterprise/main/Kconfig.projbuild

@@ -29,6 +29,27 @@ menu "Example Configuration"
         default 1 if EXAMPLE_EAP_METHOD_PEAP
         default 2 if EXAMPLE_EAP_METHOD_TTLS
 
+    choice
+        prompt "Phase2 method for TTLS"
+        depends on EXAMPLE_EAP_METHOD_TTLS
+        default EXAMPLE_EAP_METHOD_TTLS_PHASE2_MSCHAPV2
+        config EXAMPLE_EAP_METHOD_TTLS_PHASE2_MSCHAPV2
+            bool "MSCHAPV2"
+        config EXAMPLE_EAP_METHOD_TTLS_PHASE2_MSCHAP
+            bool "MSCHAP"
+        config EXAMPLE_EAP_METHOD_TTLS_PHASE2_PAP
+            bool "PAP"
+        config EXAMPLE_EAP_METHOD_TTLS_PHASE2_CHAP
+            bool "CHAP"
+    endchoice
+
+    config EXAMPLE_EAP_METHOD_TTLS_PHASE_2
+        int
+        default 1 if EXAMPLE_EAP_METHOD_TTLS_PHASE2_MSCHAPV2
+        default 2 if EXAMPLE_EAP_METHOD_TTLS_PHASE2_MSCHAP
+        default 3 if EXAMPLE_EAP_METHOD_TTLS_PHASE2_PAP
+        default 4 if EXAMPLE_EAP_METHOD_TTLS_PHASE2_CHAP
+
     config EXAMPLE_EAP_ID
         string "EAP ID"
         default "example@espressif.com"

+ 8 - 0
examples/wifi/wpa2_enterprise/main/wpa2_enterprise_main.c

@@ -80,6 +80,10 @@ extern uint8_t client_key_start[] asm("_binary_wpa2_client_key_start");
 extern uint8_t client_key_end[]   asm("_binary_wpa2_client_key_end");
 #endif /* CONFIG_EXAMPLE_EAP_METHOD_TLS */
 
+#if defined CONFIG_EXAMPLE_EAP_METHOD_TTLS
+esp_eap_ttls_phase2_types TTLS_PHASE2_METHOD = CONFIG_EXAMPLE_EAP_METHOD_TTLS_PHASE_2;
+#endif /* CONFIG_EXAMPLE_EAP_METHOD_TTLS */
+
 static void event_handler(void* arg, esp_event_base_t event_base, 
                                 int32_t event_id, void* event_data)
 {
@@ -139,6 +143,10 @@ static void initialise_wifi(void)
     ESP_ERROR_CHECK( esp_wifi_sta_wpa2_ent_set_password((uint8_t *)EXAMPLE_EAP_PASSWORD, strlen(EXAMPLE_EAP_PASSWORD)) );
 #endif /* CONFIG_EXAMPLE_EAP_METHOD_PEAP || CONFIG_EXAMPLE_EAP_METHOD_TTLS */
    
+#if defined CONFIG_EXAMPLE_EAP_METHOD_TTLS
+    ESP_ERROR_CHECK( esp_wifi_sta_wpa2_ent_set_ttls_phase2_method(TTLS_PHASE2_METHOD) );
+#endif /* CONFIG_EXAMPLE_EAP_METHOD_TTLS */
+
     ESP_ERROR_CHECK( esp_wifi_sta_wpa2_ent_enable() );
     ESP_ERROR_CHECK( esp_wifi_start() );
 }