From d7ccad371a6a5f6299f74c9c3aa1f1fa64b86751 Mon Sep 17 00:00:00 2001 From: Hrudaynath Dhabe Date: Tue, 23 Jun 2020 14:04:23 +0530 Subject: [PATCH] wpa_supplicant: add support for PAP, MS-CHAP and CHAP as Phase 2 algorithms for TTLS alongside MS-CHAPv2 --- .../include/esp_supplicant/esp_wpa2.h | 18 + components/wpa_supplicant/src/eap_peer/chap.c | 5 +- components/wpa_supplicant/src/eap_peer/chap.h | 17 + components/wpa_supplicant/src/eap_peer/eap.c | 181 ++++++ components/wpa_supplicant/src/eap_peer/eap.h | 4 + .../wpa_supplicant/src/eap_peer/eap_config.h | 144 ++++ .../wpa_supplicant/src/eap_peer/eap_i.h | 191 +++++- .../wpa_supplicant/src/eap_peer/eap_ttls.c | 615 ++++++++++++++++-- .../src/esp_supplicant/esp_wpa2.c | 25 + .../wpa_supplicant/src/utils/ext_password.h | 2 +- .../wpa2_enterprise/main/Kconfig.projbuild | 21 + .../main/wpa2_enterprise_main.c | 8 + 12 files changed, 1154 insertions(+), 77 deletions(-) create mode 100644 components/wpa_supplicant/src/eap_peer/chap.h diff --git a/components/wpa_supplicant/include/esp_supplicant/esp_wpa2.h b/components/wpa_supplicant/include/esp_supplicant/esp_wpa2.h index 5315606598..c6c2930a0f 100644 --- a/components/wpa_supplicant/include/esp_supplicant/esp_wpa2.h +++ b/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 diff --git a/components/wpa_supplicant/src/eap_peer/chap.c b/components/wpa_supplicant/src/eap_peer/chap.c index f48db98193..5ff2a9f7cc 100644 --- a/components/wpa_supplicant/src/eap_peer/chap.c +++ b/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) diff --git a/components/wpa_supplicant/src/eap_peer/chap.h b/components/wpa_supplicant/src/eap_peer/chap.h new file mode 100644 index 0000000000..a791505f99 --- /dev/null +++ b/components/wpa_supplicant/src/eap_peer/chap.h @@ -0,0 +1,17 @@ +/* + * CHAP-MD5 (RFC 1994) + * Copyright (c) 2007-2009, Jouni Malinen + * + * 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 */ diff --git a/components/wpa_supplicant/src/eap_peer/eap.c b/components/wpa_supplicant/src/eap_peer/eap.c index 114985c9e5..b3277e3aa2 100644 --- a/components/wpa_supplicant/src/eap_peer/eap.c +++ b/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); diff --git a/components/wpa_supplicant/src/eap_peer/eap.h b/components/wpa_supplicant/src/eap_peer/eap.h index 4e84ea7fc7..5432f43c32 100644 --- a/components/wpa_supplicant/src/eap_peer/eap.h +++ b/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 */ diff --git a/components/wpa_supplicant/src/eap_peer/eap_config.h b/components/wpa_supplicant/src/eap_peer/eap_config.h index 3698e73252..c6cad6f936 100644 --- a/components/wpa_supplicant/src/eap_peer/eap_config.h +++ b/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; /** diff --git a/components/wpa_supplicant/src/eap_peer/eap_i.h b/components/wpa_supplicant/src/eap_peer/eap_i.h index a55a8ae388..d5ffab3e84 100644 --- a/components/wpa_supplicant/src/eap_peer/eap_i.h +++ b/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); diff --git a/components/wpa_supplicant/src/eap_peer/eap_ttls.c b/components/wpa_supplicant/src/eap_peer/eap_ttls.c index b014dbd395..d086087bac 100644 --- a/components/wpa_supplicant/src/eap_peer/eap_ttls.c +++ b/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) diff --git a/components/wpa_supplicant/src/esp_supplicant/esp_wpa2.c b/components/wpa_supplicant/src/esp_supplicant/esp_wpa2.c index 1ec6192d1b..ac8afdf10f 100644 --- a/components/wpa_supplicant/src/esp_supplicant/esp_wpa2.c +++ b/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; +} + diff --git a/components/wpa_supplicant/src/utils/ext_password.h b/components/wpa_supplicant/src/utils/ext_password.h index dfe8e6f0e7..748033c17c 100644 --- a/components/wpa_supplicant/src/utils/ext_password.h +++ b/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 */ diff --git a/examples/wifi/wpa2_enterprise/main/Kconfig.projbuild b/examples/wifi/wpa2_enterprise/main/Kconfig.projbuild index a2e4be005e..af9a5b0384 100644 --- a/examples/wifi/wpa2_enterprise/main/Kconfig.projbuild +++ b/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" diff --git a/examples/wifi/wpa2_enterprise/main/wpa2_enterprise_main.c b/examples/wifi/wpa2_enterprise/main/wpa2_enterprise_main.c index a3d453b6eb..63dd5f6a6a 100644 --- a/examples/wifi/wpa2_enterprise/main/wpa2_enterprise_main.c +++ b/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() ); }