mirror of
https://github.com/espressif/esp-idf.git
synced 2024-10-05 20:47:46 -04:00
esp_wifi: Add support for EAP-FAST authentication method
This commit is contained in:
parent
954d52ff3a
commit
e21832cabb
@ -32,6 +32,9 @@ set(srcs "port/os_xtensa.c"
|
||||
"src/eap_peer/eap_tls_common.c"
|
||||
"src/eap_peer/eap_ttls.c"
|
||||
"src/eap_peer/mschapv2.c"
|
||||
"src/eap_peer/eap_fast.c"
|
||||
"src/eap_peer/eap_fast_common.c"
|
||||
"src/eap_peer/eap_fast_pac.c"
|
||||
"src/rsn_supp/pmksa_cache.c"
|
||||
"src/rsn_supp/wpa.c"
|
||||
"src/rsn_supp/wpa_ie.c"
|
||||
@ -139,6 +142,7 @@ else()
|
||||
"src/crypto/sha1-internal.c"
|
||||
"src/crypto/sha1-pbkdf2.c"
|
||||
"src/crypto/sha1.c"
|
||||
"src/crypto/sha1-tprf.c"
|
||||
"src/crypto/sha256-internal.c"
|
||||
"src/crypto/sha384-internal.c"
|
||||
"src/crypto/sha512-internal.c"
|
||||
@ -179,6 +183,7 @@ target_compile_definitions(${COMPONENT_LIB} PRIVATE
|
||||
EAP_TTLS
|
||||
EAP_TLS
|
||||
EAP_PEAP
|
||||
EAP_FAST
|
||||
USE_WPA2_TASK
|
||||
CONFIG_WPS2
|
||||
CONFIG_WPS_PIN
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2019-2021 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2019-2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
@ -17,7 +17,13 @@ typedef enum {
|
||||
ESP_EAP_TTLS_PHASE2_MSCHAP,
|
||||
ESP_EAP_TTLS_PHASE2_PAP,
|
||||
ESP_EAP_TTLS_PHASE2_CHAP
|
||||
} esp_eap_ttls_phase2_types ;
|
||||
} esp_eap_ttls_phase2_types;
|
||||
|
||||
typedef struct {
|
||||
int fast_provisioning;
|
||||
int fast_max_pac_list_len;
|
||||
bool fast_pac_format_binary;
|
||||
} esp_eap_fast_config;
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
@ -211,6 +217,35 @@ esp_err_t esp_wifi_sta_wpa2_ent_set_ttls_phase2_method(esp_eap_ttls_phase2_types
|
||||
*/
|
||||
esp_err_t esp_wifi_sta_wpa2_set_suiteb_192bit_certification(bool enable);
|
||||
|
||||
/**
|
||||
* @brief Set client pac file
|
||||
*
|
||||
* @attention 1. For files read from the file system, length has to be decremented by 1 byte.
|
||||
* @attention 2. Disabling the WPA_MBEDTLS_CRYPTO config is required to use EAP-FAST.
|
||||
*
|
||||
* @param pac_file: pointer to the pac file
|
||||
* pac_file_len: length of the pac file
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK: succeed
|
||||
* - ESP_ERR_NO_MEM: fail(internal memory malloc fail)
|
||||
*/
|
||||
esp_err_t esp_wifi_sta_wpa2_ent_set_pac_file(const unsigned char *pac_file, int pac_file_len);
|
||||
|
||||
/**
|
||||
* @brief Set Phase 1 parameters for EAP-FAST
|
||||
*
|
||||
* @attention 1. Disabling the WPA_MBEDTLS_CRYPTO config is required to use EAP-FAST.
|
||||
*
|
||||
* @param config: eap fast phase 1 configuration
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK: succeed
|
||||
* - ESP_ERR_INVALID_ARG: fail(out of bound arguments)
|
||||
* - ESP_ERR_NO_MEM: fail(internal memory malloc fail)
|
||||
*/
|
||||
esp_err_t esp_wifi_sta_wpa2_ent_set_fast_phase1_params(esp_eap_fast_config config);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2019-2021 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2019-2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
@ -43,6 +43,15 @@
|
||||
#define DATA_MUTEX_TAKE() xSemaphoreTakeRecursive(s_wpa2_data_lock,portMAX_DELAY)
|
||||
#define DATA_MUTEX_GIVE() xSemaphoreGiveRecursive(s_wpa2_data_lock)
|
||||
|
||||
//length of the string "fast_provisioning={0/1/2} "
|
||||
#define FAST_PROVISIONING_CONFIG_STR_LEN 20
|
||||
//length of the string "fast_max_pac_list_len=(int < 100) "
|
||||
#define FAST_MAX_PAC_LIST_CONFIG_STR_LEN 25
|
||||
//length of the string "fast_pac_format=binary"
|
||||
#define FAST_PAC_FORMAT_STR_LEN 22
|
||||
//Total
|
||||
#define PHASE1_PARAM_STRING_LEN FAST_PROVISIONING_CONFIG_STR_LEN + FAST_MAX_PAC_LIST_CONFIG_STR_LEN + FAST_PAC_FORMAT_STR_LEN
|
||||
|
||||
static void *s_wpa2_data_lock = NULL;
|
||||
|
||||
static struct eap_sm *gEapSm = NULL;
|
||||
@ -974,6 +983,9 @@ void esp_wifi_sta_wpa2_ent_clear_cert_key(void)
|
||||
g_wpa_private_key_len = 0;
|
||||
g_wpa_private_key_passwd = NULL;
|
||||
g_wpa_private_key_passwd_len = 0;
|
||||
os_free(g_wpa_pac_file);
|
||||
g_wpa_pac_file = NULL;
|
||||
g_wpa_pac_file_len = 0;
|
||||
}
|
||||
|
||||
esp_err_t esp_wifi_sta_wpa2_ent_set_ca_cert(const unsigned char *ca_cert, int ca_cert_len)
|
||||
@ -1171,3 +1183,57 @@ esp_err_t esp_wifi_sta_wpa2_set_suiteb_192bit_certification(bool enable)
|
||||
return ESP_FAIL;
|
||||
#endif
|
||||
}
|
||||
|
||||
esp_err_t esp_wifi_sta_wpa2_ent_set_pac_file(const unsigned char *pac_file, int pac_file_len)
|
||||
{
|
||||
if (pac_file && pac_file_len > -1) {
|
||||
if (pac_file_len < 512) { // The file contains less than 1 pac and is to be rewritten later
|
||||
g_wpa_pac_file = (u8 *)os_zalloc(512);
|
||||
if (g_wpa_pac_file == NULL) {
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
g_wpa_pac_file_len = 0;
|
||||
} else { // The file contains pac data
|
||||
g_wpa_pac_file = (u8 *)os_zalloc(pac_file_len);
|
||||
if (g_wpa_pac_file == NULL) {
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
os_memcpy(g_wpa_pac_file, pac_file, pac_file_len);
|
||||
g_wpa_pac_file_len = pac_file_len;
|
||||
}
|
||||
} else {
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t esp_wifi_sta_wpa2_ent_set_fast_phase1_params(esp_eap_fast_config config)
|
||||
{
|
||||
char config_for_supplicant[PHASE1_PARAM_STRING_LEN] = "";
|
||||
if ((config.fast_provisioning > -1) && (config.fast_provisioning <= 2)) {
|
||||
os_sprintf((char *) &config_for_supplicant, "fast_provisioning=%d ", config.fast_provisioning);
|
||||
} else {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
if (config.fast_max_pac_list_len && config.fast_max_pac_list_len < 100) {
|
||||
os_sprintf((char *) &config_for_supplicant + strlen(config_for_supplicant), "fast_max_pac_list_len=%d ", config.fast_max_pac_list_len);
|
||||
} else if (config.fast_max_pac_list_len >= 100) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
if (config.fast_pac_format_binary) {
|
||||
os_strcat((char *) &config_for_supplicant, (const char *) "fast_pac_format=binary");
|
||||
}
|
||||
|
||||
// Free the old buffer if it already exists
|
||||
if (g_wpa_phase1_options != NULL) {
|
||||
os_free(g_wpa_phase1_options);
|
||||
}
|
||||
g_wpa_phase1_options = (char *)os_zalloc(sizeof(config_for_supplicant));
|
||||
if (g_wpa_phase1_options == NULL) {
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
os_memcpy(g_wpa_phase1_options, &config_for_supplicant, sizeof(config_for_supplicant));
|
||||
return ESP_OK;
|
||||
|
||||
}
|
||||
|
@ -38,6 +38,7 @@ struct wpabuf * wpabuf_alloc_ext_data(u8 *data, size_t len);
|
||||
struct wpabuf * wpabuf_alloc_copy(const void *data, size_t len);
|
||||
struct wpabuf * wpabuf_dup(const struct wpabuf *src);
|
||||
void wpabuf_free(struct wpabuf *buf);
|
||||
void wpabuf_clear_free(struct wpabuf *buf);
|
||||
void * wpabuf_put(struct wpabuf *buf, size_t len);
|
||||
struct wpabuf * wpabuf_concat(struct wpabuf *a, struct wpabuf *b);
|
||||
struct wpabuf * wpabuf_zeropad(struct wpabuf *buf, size_t len);
|
||||
|
@ -289,6 +289,9 @@ char * ets_strdup(const char *s);
|
||||
#ifndef os_strlcpy
|
||||
#define os_strlcpy(d, s, n) strlcpy((d), (s), (n))
|
||||
#endif
|
||||
#ifndef os_strcat
|
||||
#define os_strcat(d, s) strcat((d), (s))
|
||||
#endif
|
||||
|
||||
#ifndef os_snprintf
|
||||
#ifdef _MSC_VER
|
||||
@ -297,6 +300,9 @@ char * ets_strdup(const char *s);
|
||||
#define os_snprintf snprintf
|
||||
#endif
|
||||
#endif
|
||||
#ifndef os_sprintf
|
||||
#define os_sprintf sprintf
|
||||
#endif
|
||||
|
||||
static inline int os_snprintf_error(size_t size, int res)
|
||||
{
|
||||
|
@ -18,15 +18,6 @@
|
||||
#include "sae.h"
|
||||
#include "esp_wifi_crypto_types.h"
|
||||
|
||||
/*TBD Move the this api to proper files once they are taken out of lib*/
|
||||
void wpabuf_clear_free(struct wpabuf *buf)
|
||||
{
|
||||
if (buf) {
|
||||
os_memset(wpabuf_mhead(buf), 0, wpabuf_len(buf));
|
||||
wpabuf_free(buf);
|
||||
}
|
||||
}
|
||||
|
||||
int sae_set_group(struct sae_data *sae, int group)
|
||||
{
|
||||
struct sae_temporary_data *tmp;
|
||||
|
72
components/wpa_supplicant/src/crypto/sha1-tprf.c
Normal file
72
components/wpa_supplicant/src/crypto/sha1-tprf.c
Normal file
@ -0,0 +1,72 @@
|
||||
/*
|
||||
* SHA1 T-PRF for EAP-FAST
|
||||
* Copyright (c) 2003-2005, Jouni Malinen <j@w1.fi>
|
||||
*
|
||||
* This software may be distributed under the terms of the BSD license.
|
||||
* See README for more details.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
|
||||
#include "common.h"
|
||||
#include "sha1.h"
|
||||
#include "crypto.h"
|
||||
|
||||
/**
|
||||
* sha1_t_prf - EAP-FAST Pseudo-Random Function (T-PRF)
|
||||
* @key: Key for PRF
|
||||
* @key_len: Length of the key in bytes
|
||||
* @label: A unique label for each purpose of the PRF
|
||||
* @seed: Seed value to bind into the key
|
||||
* @seed_len: Length of the seed
|
||||
* @buf: Buffer for the generated pseudo-random key
|
||||
* @buf_len: Number of bytes of key to generate
|
||||
* Returns: 0 on success, -1 of failure
|
||||
*
|
||||
* This function is used to derive new, cryptographically separate keys from a
|
||||
* given key for EAP-FAST. T-PRF is defined in RFC 4851, Section 5.5.
|
||||
*/
|
||||
int sha1_t_prf(const u8 *key, size_t key_len, const char *label,
|
||||
const u8 *seed, size_t seed_len, u8 *buf, size_t buf_len)
|
||||
{
|
||||
unsigned char counter = 0;
|
||||
size_t pos, plen;
|
||||
u8 hash[SHA1_MAC_LEN];
|
||||
size_t label_len = os_strlen(label);
|
||||
u8 output_len[2];
|
||||
const unsigned char *addr[5];
|
||||
size_t len[5];
|
||||
|
||||
addr[0] = hash;
|
||||
len[0] = 0;
|
||||
addr[1] = (unsigned char *) label;
|
||||
len[1] = label_len + 1;
|
||||
addr[2] = seed;
|
||||
len[2] = seed_len;
|
||||
addr[3] = output_len;
|
||||
len[3] = 2;
|
||||
addr[4] = &counter;
|
||||
len[4] = 1;
|
||||
|
||||
output_len[0] = (buf_len >> 8) & 0xff;
|
||||
output_len[1] = buf_len & 0xff;
|
||||
pos = 0;
|
||||
while (pos < buf_len) {
|
||||
counter++;
|
||||
plen = buf_len - pos;
|
||||
if (hmac_sha1_vector(key, key_len, 5, addr, len, hash))
|
||||
return -1;
|
||||
if (plen >= SHA1_MAC_LEN) {
|
||||
os_memcpy(&buf[pos], hash, SHA1_MAC_LEN);
|
||||
pos += SHA1_MAC_LEN;
|
||||
} else {
|
||||
os_memcpy(&buf[pos], hash, plen);
|
||||
break;
|
||||
}
|
||||
len[0] = SHA1_MAC_LEN;
|
||||
}
|
||||
|
||||
forced_memzero(hash, SHA1_MAC_LEN);
|
||||
|
||||
return 0;
|
||||
}
|
@ -626,7 +626,8 @@ int tls_global_set_verify(void *tls_ctx, int check_crl)
|
||||
}
|
||||
|
||||
int tls_connection_set_verify(void *tls_ctx, struct tls_connection *conn,
|
||||
int verify_peer)
|
||||
int verify_peer, unsigned int flags,
|
||||
const u8 *session_ctx, size_t session_ctx_len)
|
||||
{
|
||||
wpa_printf(MSG_INFO, "TLS: tls_connection_set_verify not supported");
|
||||
return -1;
|
||||
@ -903,11 +904,27 @@ static int tls_connection_prf(void *tls_ctx, struct tls_connection *conn,
|
||||
}
|
||||
|
||||
int tls_connection_export_key(void *tls_ctx, struct tls_connection *conn,
|
||||
const char *label, u8 *out, size_t out_len)
|
||||
const char *label, const u8 *context,
|
||||
size_t context_len, u8 *out, size_t out_len)
|
||||
{
|
||||
return tls_connection_prf(tls_ctx, conn, label, 0, out, out_len);
|
||||
}
|
||||
|
||||
int tls_connection_get_eap_fast_key(void *tls_ctx, struct tls_connection *conn,
|
||||
u8 *out, size_t out_len)
|
||||
{
|
||||
wpa_printf(MSG_INFO, "TLS: tls_connection_get_eap_fast_key not supported, please unset mbedtls crypto and try again");
|
||||
return -1;
|
||||
}
|
||||
|
||||
int tls_connection_client_hello_ext(void *tls_ctx, struct tls_connection *conn,
|
||||
int ext_type, const u8 *data,
|
||||
size_t data_len)
|
||||
{
|
||||
wpa_printf(MSG_INFO, "TLS: tls_connection_client_hello_ext not supported, please unset mbedtls crypto and try again");
|
||||
return -1;
|
||||
}
|
||||
|
||||
int tls_connection_shutdown(void *tls_ctx, struct tls_connection *conn)
|
||||
{
|
||||
if (conn->tls_io_data.in_data) {
|
||||
|
@ -213,6 +213,13 @@ int eap_peer_register_methods(void)
|
||||
ret = eap_peer_mschapv2_register();
|
||||
#endif
|
||||
|
||||
#ifndef USE_MBEDTLS_CRYPTO
|
||||
#ifdef EAP_FAST
|
||||
if (ret == 0)
|
||||
ret = eap_peer_fast_register();
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef EAP_PEAP
|
||||
if (ret == 0)
|
||||
ret = eap_peer_peap_register();
|
||||
@ -435,11 +442,12 @@ int eap_peer_config_init(
|
||||
sm->config.client_cert = (u8 *)sm->blob[0].name;
|
||||
sm->config.private_key = (u8 *)sm->blob[1].name;
|
||||
sm->config.ca_cert = (u8 *)sm->blob[2].name;
|
||||
|
||||
sm->config.ca_path = NULL;
|
||||
|
||||
sm->config.fragment_size = 1400; /* fragment size */
|
||||
|
||||
sm->config.pac_file = (char *) "blob://";
|
||||
|
||||
/* anonymous identity */
|
||||
if (g_wpa_anonymous_identity && g_wpa_anonymous_identity_len > 0) {
|
||||
sm->config.anonymous_identity_len = g_wpa_anonymous_identity_len;
|
||||
@ -487,6 +495,10 @@ int eap_peer_config_init(
|
||||
sm->config.flags = TLS_CONN_SUITEB;
|
||||
}
|
||||
|
||||
/* To be used only for EAP-FAST */
|
||||
if (g_wpa_phase1_options) {
|
||||
sm->config.phase1 = g_wpa_phase1_options;
|
||||
}
|
||||
return 0;
|
||||
|
||||
}
|
||||
@ -543,6 +555,17 @@ int eap_peer_blob_init(struct eap_sm *sm)
|
||||
sm->blob[2].data = g_wpa_ca_cert;
|
||||
}
|
||||
|
||||
if (g_wpa_pac_file && g_wpa_pac_file_len) {
|
||||
sm->blob[3].name = (char *)os_zalloc(sizeof(char) * 8);
|
||||
if (sm->blob[3].name == NULL) {
|
||||
ret = -2;
|
||||
goto _out;
|
||||
}
|
||||
os_strncpy(sm->blob[3].name, "blob://", 8);
|
||||
sm->blob[3].len = g_wpa_pac_file_len;
|
||||
sm->blob[3].data = g_wpa_pac_file;
|
||||
}
|
||||
|
||||
return 0;
|
||||
_out:
|
||||
for (i = 0; i < BLOB_NUM; i++) {
|
||||
@ -615,6 +638,37 @@ void eap_sm_request_identity(struct eap_sm *sm)
|
||||
eap_sm_request(sm, WPA_CTRL_REQ_EAP_IDENTITY, NULL, 0);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* eap_sm_request_password - Request password from user (ctrl_iface)
|
||||
* @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
|
||||
*
|
||||
* EAP methods can call this function to request password information for the
|
||||
* current network. This is normally called when the password is not included
|
||||
* in the network configuration. The request will be sent to monitor programs
|
||||
* through the control interface.
|
||||
*/
|
||||
void eap_sm_request_password(struct eap_sm *sm)
|
||||
{
|
||||
eap_sm_request(sm, WPA_CTRL_REQ_EAP_PASSWORD, NULL, 0);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* eap_sm_request_new_password - Request new password from user (ctrl_iface)
|
||||
* @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
|
||||
*
|
||||
* EAP methods can call this function to request new password information for
|
||||
* the current network. This is normally called when the EAP method indicates
|
||||
* that the current password has expired and password change is required. The
|
||||
* request will be sent to monitor programs through the control interface.
|
||||
*/
|
||||
void eap_sm_request_new_password(struct eap_sm *sm)
|
||||
{
|
||||
eap_sm_request(sm, WPA_CTRL_REQ_EAP_NEW_PASSWORD, NULL, 0);
|
||||
}
|
||||
|
||||
|
||||
void eap_peer_blob_deinit(struct eap_sm *sm)
|
||||
{
|
||||
int i;
|
||||
@ -629,6 +683,7 @@ void eap_peer_blob_deinit(struct eap_sm *sm)
|
||||
sm->config.client_cert = NULL;
|
||||
sm->config.private_key = NULL;
|
||||
sm->config.ca_cert = NULL;
|
||||
sm->config.pac_file = NULL;
|
||||
}
|
||||
|
||||
void eap_sm_abort(struct eap_sm *sm)
|
||||
@ -721,6 +776,40 @@ const u8 * eap_get_config_new_password(struct eap_sm *sm, size_t *len)
|
||||
*len = config->new_password_len;
|
||||
return config->new_password;
|
||||
}
|
||||
|
||||
|
||||
static int eap_copy_buf(u8 **dst, size_t *dst_len,
|
||||
const u8 *src, size_t src_len)
|
||||
{
|
||||
if (src) {
|
||||
*dst = os_memdup(src, src_len);
|
||||
if (*dst == NULL)
|
||||
return -1;
|
||||
*dst_len = src_len;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* eap_set_config_blob - Set or add a named configuration blob
|
||||
* @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
|
||||
* @blob: New value for the blob
|
||||
*
|
||||
* Adds a new configuration blob or replaces the current value of an existing
|
||||
* blob.
|
||||
*/
|
||||
void eap_set_config_blob(struct eap_sm *sm, struct wpa_config_blob *blob)
|
||||
{
|
||||
if (!sm)
|
||||
return;
|
||||
|
||||
if (eap_copy_buf((u8 **)&sm->blob[3].data, (size_t *)&sm->blob[3].len, blob->data, blob->len) < 0) {
|
||||
wpa_printf(MSG_ERROR, "EAP: Set config blob: Unable to modify the configuration blob");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* eap_get_config_blob - Get a named configuration blob
|
||||
* @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
|
||||
|
@ -40,6 +40,10 @@ u8 *g_wpa_new_password;
|
||||
int g_wpa_new_password_len;
|
||||
|
||||
char *g_wpa_ttls_phase2_type;
|
||||
char *g_wpa_phase1_options;
|
||||
|
||||
u8 *g_wpa_pac_file;
|
||||
int g_wpa_pac_file_len;
|
||||
|
||||
bool g_wpa_suiteb_certification;
|
||||
|
||||
@ -56,5 +60,7 @@ 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);
|
||||
void eap_sm_request_password(struct eap_sm *sm);
|
||||
void eap_sm_request_new_password(struct eap_sm *sm);
|
||||
|
||||
#endif /* EAP_H */
|
||||
|
@ -294,6 +294,15 @@ struct eap_peer_config {
|
||||
*/
|
||||
int pending_req_passphrase;
|
||||
|
||||
/**
|
||||
* pending_req_sim - Pending SIM 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_sim;
|
||||
|
||||
/**
|
||||
* pending_req_otp - Whether there is a pending OTP request
|
||||
*
|
||||
@ -303,6 +312,18 @@ struct eap_peer_config {
|
||||
*/
|
||||
char *pending_req_otp;
|
||||
|
||||
/**
|
||||
* pac_file - File path or blob name for the PAC entries (EAP-FAST)
|
||||
*
|
||||
* wpa_supplicant will need to be able to create this file and write
|
||||
* updates to it when PAC is being provisioned or refreshed. 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.
|
||||
*/
|
||||
char *pac_file;
|
||||
|
||||
/**
|
||||
* mschapv2_retry - MSCHAPv2 retry in progress
|
||||
*
|
||||
@ -358,6 +379,26 @@ struct eap_peer_config {
|
||||
* 2 = require valid OCSP stapling response
|
||||
*/
|
||||
int ocsp;
|
||||
|
||||
/**
|
||||
* erp - Whether EAP Re-authentication Protocol (ERP) is enabled
|
||||
*/
|
||||
int erp;
|
||||
|
||||
/**
|
||||
* pending_ext_cert_check - External server certificate check status
|
||||
*
|
||||
* This field should not be set in configuration step. It is only used
|
||||
* internally when control interface is used to request external
|
||||
* validation of server certificate chain.
|
||||
*/
|
||||
enum {
|
||||
NO_CHECK = 0,
|
||||
PENDING_CHECK,
|
||||
EXT_CERT_CHECK_GOOD,
|
||||
EXT_CERT_CHECK_BAD,
|
||||
} pending_ext_cert_check;
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
1807
components/wpa_supplicant/src/eap_peer/eap_fast.c
Normal file
1807
components/wpa_supplicant/src/eap_peer/eap_fast.c
Normal file
File diff suppressed because it is too large
Load Diff
270
components/wpa_supplicant/src/eap_peer/eap_fast_common.c
Normal file
270
components/wpa_supplicant/src/eap_peer/eap_fast_common.c
Normal file
@ -0,0 +1,270 @@
|
||||
/*
|
||||
* EAP-FAST common helper functions (RFC 4851)
|
||||
* Copyright (c) 2008, Jouni Malinen <j@w1.fi>
|
||||
*
|
||||
* This software may be distributed under the terms of the BSD license.
|
||||
* See README for more details.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
|
||||
#include "common.h"
|
||||
#include "crypto/sha1.h"
|
||||
#include "tls/tls.h"
|
||||
#include "eap_peer/eap_defs.h"
|
||||
#include "eap_peer/eap_tlv_common.h"
|
||||
#include "eap_peer/eap_fast_common.h"
|
||||
|
||||
|
||||
void eap_fast_put_tlv_hdr(struct wpabuf *buf, u16 type, u16 len)
|
||||
{
|
||||
struct pac_tlv_hdr hdr;
|
||||
hdr.type = host_to_be16(type);
|
||||
hdr.len = host_to_be16(len);
|
||||
wpabuf_put_data(buf, &hdr, sizeof(hdr));
|
||||
}
|
||||
|
||||
|
||||
void eap_fast_put_tlv(struct wpabuf *buf, u16 type, const void *data,
|
||||
u16 len)
|
||||
{
|
||||
eap_fast_put_tlv_hdr(buf, type, len);
|
||||
wpabuf_put_data(buf, data, len);
|
||||
}
|
||||
|
||||
|
||||
void eap_fast_put_tlv_buf(struct wpabuf *buf, u16 type,
|
||||
const struct wpabuf *data)
|
||||
{
|
||||
eap_fast_put_tlv_hdr(buf, type, wpabuf_len(data));
|
||||
wpabuf_put_buf(buf, data);
|
||||
}
|
||||
|
||||
|
||||
struct wpabuf * eap_fast_tlv_eap_payload(struct wpabuf *buf)
|
||||
{
|
||||
struct wpabuf *e;
|
||||
|
||||
if (buf == NULL)
|
||||
return NULL;
|
||||
|
||||
/* Encapsulate EAP packet in EAP-Payload TLV */
|
||||
wpa_printf(MSG_DEBUG, "EAP-FAST: Add EAP-Payload TLV");
|
||||
e = wpabuf_alloc(sizeof(struct pac_tlv_hdr) + wpabuf_len(buf));
|
||||
if (e == NULL) {
|
||||
wpa_printf(MSG_DEBUG, "EAP-FAST: Failed to allocate memory "
|
||||
"for TLV encapsulation");
|
||||
wpabuf_free(buf);
|
||||
return NULL;
|
||||
}
|
||||
eap_fast_put_tlv_buf(e,
|
||||
EAP_TLV_TYPE_MANDATORY | EAP_TLV_EAP_PAYLOAD_TLV,
|
||||
buf);
|
||||
wpabuf_free(buf);
|
||||
return e;
|
||||
}
|
||||
|
||||
|
||||
void eap_fast_derive_master_secret(const u8 *pac_key, const u8 *server_random,
|
||||
const u8 *client_random, u8 *master_secret)
|
||||
{
|
||||
#define TLS_RANDOM_LEN 32
|
||||
#define TLS_MASTER_SECRET_LEN 48
|
||||
u8 seed[2 * TLS_RANDOM_LEN];
|
||||
|
||||
wpa_hexdump(MSG_DEBUG, "EAP-FAST: client_random",
|
||||
client_random, TLS_RANDOM_LEN);
|
||||
wpa_hexdump(MSG_DEBUG, "EAP-FAST: server_random",
|
||||
server_random, TLS_RANDOM_LEN);
|
||||
|
||||
/*
|
||||
* RFC 4851, Section 5.1:
|
||||
* master_secret = T-PRF(PAC-Key, "PAC to master secret label hash",
|
||||
* server_random + client_random, 48)
|
||||
*/
|
||||
os_memcpy(seed, server_random, TLS_RANDOM_LEN);
|
||||
os_memcpy(seed + TLS_RANDOM_LEN, client_random, TLS_RANDOM_LEN);
|
||||
sha1_t_prf(pac_key, EAP_FAST_PAC_KEY_LEN,
|
||||
"PAC to master secret label hash",
|
||||
seed, sizeof(seed), master_secret, TLS_MASTER_SECRET_LEN);
|
||||
|
||||
wpa_hexdump_key(MSG_DEBUG, "EAP-FAST: master_secret",
|
||||
master_secret, TLS_MASTER_SECRET_LEN);
|
||||
}
|
||||
|
||||
|
||||
u8 * eap_fast_derive_key(void *ssl_ctx, struct tls_connection *conn, size_t len)
|
||||
{
|
||||
u8 *out;
|
||||
|
||||
out = os_malloc(len);
|
||||
if (out == NULL)
|
||||
return NULL;
|
||||
|
||||
if (tls_connection_get_eap_fast_key(ssl_ctx, conn, out, len)) {
|
||||
os_free(out);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
|
||||
int eap_fast_derive_eap_msk(const u8 *simck, u8 *msk)
|
||||
{
|
||||
/*
|
||||
* RFC 4851, Section 5.4: EAP Master Session Key Generation
|
||||
* MSK = T-PRF(S-IMCK[j], "Session Key Generating Function", 64)
|
||||
*/
|
||||
|
||||
if (sha1_t_prf(simck, EAP_FAST_SIMCK_LEN,
|
||||
"Session Key Generating Function", (u8 *) "", 0,
|
||||
msk, EAP_FAST_KEY_LEN) < 0)
|
||||
return -1;
|
||||
wpa_hexdump_key(MSG_DEBUG, "EAP-FAST: Derived key (MSK)",
|
||||
msk, EAP_FAST_KEY_LEN);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int eap_fast_derive_eap_emsk(const u8 *simck, u8 *emsk)
|
||||
{
|
||||
/*
|
||||
* RFC 4851, Section 5.4: EAP Master Session Key Genreration
|
||||
* EMSK = T-PRF(S-IMCK[j],
|
||||
* "Extended Session Key Generating Function", 64)
|
||||
*/
|
||||
|
||||
if (sha1_t_prf(simck, EAP_FAST_SIMCK_LEN,
|
||||
"Extended Session Key Generating Function", (u8 *) "", 0,
|
||||
emsk, EAP_EMSK_LEN) < 0)
|
||||
return -1;
|
||||
wpa_hexdump_key(MSG_DEBUG, "EAP-FAST: Derived key (EMSK)",
|
||||
emsk, EAP_EMSK_LEN);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int eap_fast_parse_tlv(struct eap_fast_tlv_parse *tlv,
|
||||
int tlv_type, u8 *pos, size_t len)
|
||||
{
|
||||
switch (tlv_type) {
|
||||
case EAP_TLV_EAP_PAYLOAD_TLV:
|
||||
wpa_hexdump(MSG_MSGDUMP, "EAP-FAST: EAP-Payload TLV",
|
||||
pos, len);
|
||||
if (tlv->eap_payload_tlv) {
|
||||
wpa_printf(MSG_DEBUG, "EAP-FAST: More than one "
|
||||
"EAP-Payload TLV in the message");
|
||||
tlv->iresult = EAP_TLV_RESULT_FAILURE;
|
||||
return -2;
|
||||
}
|
||||
tlv->eap_payload_tlv = pos;
|
||||
tlv->eap_payload_tlv_len = len;
|
||||
break;
|
||||
case EAP_TLV_RESULT_TLV:
|
||||
wpa_hexdump(MSG_MSGDUMP, "EAP-FAST: Result TLV", pos, len);
|
||||
if (tlv->result) {
|
||||
wpa_printf(MSG_DEBUG, "EAP-FAST: More than one "
|
||||
"Result TLV in the message");
|
||||
tlv->result = EAP_TLV_RESULT_FAILURE;
|
||||
return -2;
|
||||
}
|
||||
if (len < 2) {
|
||||
wpa_printf(MSG_DEBUG, "EAP-FAST: Too short "
|
||||
"Result TLV");
|
||||
tlv->result = EAP_TLV_RESULT_FAILURE;
|
||||
break;
|
||||
}
|
||||
tlv->result = WPA_GET_BE16(pos);
|
||||
if (tlv->result != EAP_TLV_RESULT_SUCCESS &&
|
||||
tlv->result != EAP_TLV_RESULT_FAILURE) {
|
||||
wpa_printf(MSG_DEBUG, "EAP-FAST: Unknown Result %d",
|
||||
tlv->result);
|
||||
tlv->result = EAP_TLV_RESULT_FAILURE;
|
||||
}
|
||||
wpa_printf(MSG_DEBUG, "EAP-FAST: Result: %s",
|
||||
tlv->result == EAP_TLV_RESULT_SUCCESS ?
|
||||
"Success" : "Failure");
|
||||
break;
|
||||
case EAP_TLV_INTERMEDIATE_RESULT_TLV:
|
||||
wpa_hexdump(MSG_MSGDUMP, "EAP-FAST: Intermediate Result TLV",
|
||||
pos, len);
|
||||
if (len < 2) {
|
||||
wpa_printf(MSG_DEBUG, "EAP-FAST: Too short "
|
||||
"Intermediate-Result TLV");
|
||||
tlv->iresult = EAP_TLV_RESULT_FAILURE;
|
||||
break;
|
||||
}
|
||||
if (tlv->iresult) {
|
||||
wpa_printf(MSG_DEBUG, "EAP-FAST: More than one "
|
||||
"Intermediate-Result TLV in the message");
|
||||
tlv->iresult = EAP_TLV_RESULT_FAILURE;
|
||||
return -2;
|
||||
}
|
||||
tlv->iresult = WPA_GET_BE16(pos);
|
||||
if (tlv->iresult != EAP_TLV_RESULT_SUCCESS &&
|
||||
tlv->iresult != EAP_TLV_RESULT_FAILURE) {
|
||||
wpa_printf(MSG_DEBUG, "EAP-FAST: Unknown Intermediate "
|
||||
"Result %d", tlv->iresult);
|
||||
tlv->iresult = EAP_TLV_RESULT_FAILURE;
|
||||
}
|
||||
wpa_printf(MSG_DEBUG, "EAP-FAST: Intermediate Result: %s",
|
||||
tlv->iresult == EAP_TLV_RESULT_SUCCESS ?
|
||||
"Success" : "Failure");
|
||||
break;
|
||||
case EAP_TLV_CRYPTO_BINDING_TLV:
|
||||
wpa_hexdump(MSG_MSGDUMP, "EAP-FAST: Crypto-Binding TLV",
|
||||
pos, len);
|
||||
if (tlv->crypto_binding) {
|
||||
wpa_printf(MSG_DEBUG, "EAP-FAST: More than one "
|
||||
"Crypto-Binding TLV in the message");
|
||||
tlv->iresult = EAP_TLV_RESULT_FAILURE;
|
||||
return -2;
|
||||
}
|
||||
tlv->crypto_binding_len = sizeof(struct eap_tlv_hdr) + len;
|
||||
if (tlv->crypto_binding_len < sizeof(*tlv->crypto_binding)) {
|
||||
wpa_printf(MSG_DEBUG, "EAP-FAST: Too short "
|
||||
"Crypto-Binding TLV");
|
||||
tlv->iresult = EAP_TLV_RESULT_FAILURE;
|
||||
return -2;
|
||||
}
|
||||
tlv->crypto_binding = (struct eap_tlv_crypto_binding_tlv *)
|
||||
(pos - sizeof(struct eap_tlv_hdr));
|
||||
break;
|
||||
case EAP_TLV_REQUEST_ACTION_TLV:
|
||||
wpa_hexdump(MSG_MSGDUMP, "EAP-FAST: Request-Action TLV",
|
||||
pos, len);
|
||||
if (tlv->request_action) {
|
||||
wpa_printf(MSG_DEBUG, "EAP-FAST: More than one "
|
||||
"Request-Action TLV in the message");
|
||||
tlv->iresult = EAP_TLV_RESULT_FAILURE;
|
||||
return -2;
|
||||
}
|
||||
if (len < 2) {
|
||||
wpa_printf(MSG_DEBUG, "EAP-FAST: Too short "
|
||||
"Request-Action TLV");
|
||||
tlv->iresult = EAP_TLV_RESULT_FAILURE;
|
||||
break;
|
||||
}
|
||||
tlv->request_action = WPA_GET_BE16(pos);
|
||||
wpa_printf(MSG_DEBUG, "EAP-FAST: Request-Action: %d",
|
||||
tlv->request_action);
|
||||
break;
|
||||
case EAP_TLV_PAC_TLV:
|
||||
wpa_hexdump(MSG_MSGDUMP, "EAP-FAST: PAC TLV", pos, len);
|
||||
if (tlv->pac) {
|
||||
wpa_printf(MSG_DEBUG, "EAP-FAST: More than one "
|
||||
"PAC TLV in the message");
|
||||
tlv->iresult = EAP_TLV_RESULT_FAILURE;
|
||||
return -2;
|
||||
}
|
||||
tlv->pac = pos;
|
||||
tlv->pac_len = len;
|
||||
break;
|
||||
default:
|
||||
/* Unknown TLV */
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
107
components/wpa_supplicant/src/eap_peer/eap_fast_common.h
Normal file
107
components/wpa_supplicant/src/eap_peer/eap_fast_common.h
Normal file
@ -0,0 +1,107 @@
|
||||
/*
|
||||
* EAP-FAST definitions (RFC 4851)
|
||||
* Copyright (c) 2004-2008, Jouni Malinen <j@w1.fi>
|
||||
*
|
||||
* This software may be distributed under the terms of the BSD license.
|
||||
* See README for more details.
|
||||
*/
|
||||
|
||||
#ifndef EAP_FAST_H
|
||||
#define EAP_FAST_H
|
||||
|
||||
#define EAP_FAST_VERSION 1
|
||||
#define EAP_FAST_KEY_LEN 64
|
||||
#define EAP_FAST_SIMCK_LEN 40
|
||||
#define EAP_FAST_SKS_LEN 40
|
||||
#define EAP_FAST_CMK_LEN 20
|
||||
|
||||
#define TLS_EXT_PAC_OPAQUE 35
|
||||
|
||||
/*
|
||||
* RFC 5422: Section 4.2.1 - Formats for PAC TLV Attributes / Type Field
|
||||
* Note: bit 0x8000 (Mandatory) and bit 0x4000 (Reserved) are also defined
|
||||
* in the general PAC TLV format (Section 4.2).
|
||||
*/
|
||||
#define PAC_TYPE_PAC_KEY 1
|
||||
#define PAC_TYPE_PAC_OPAQUE 2
|
||||
#define PAC_TYPE_CRED_LIFETIME 3
|
||||
#define PAC_TYPE_A_ID 4
|
||||
#define PAC_TYPE_I_ID 5
|
||||
/*
|
||||
* 6 was previous assigned for SERVER_PROTECTED_DATA, but
|
||||
* draft-cam-winget-eap-fast-provisioning-02.txt changed this to Reserved.
|
||||
*/
|
||||
#define PAC_TYPE_A_ID_INFO 7
|
||||
#define PAC_TYPE_PAC_ACKNOWLEDGEMENT 8
|
||||
#define PAC_TYPE_PAC_INFO 9
|
||||
#define PAC_TYPE_PAC_TYPE 10
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma pack(push, 1)
|
||||
#endif /* _MSC_VER */
|
||||
|
||||
struct pac_tlv_hdr {
|
||||
be16 type;
|
||||
be16 len;
|
||||
} STRUCT_PACKED;
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma pack(pop)
|
||||
#endif /* _MSC_VER */
|
||||
|
||||
|
||||
#define EAP_FAST_PAC_KEY_LEN 32
|
||||
|
||||
/* RFC 5422: 4.2.6 PAC-Type TLV */
|
||||
#define PAC_TYPE_TUNNEL_PAC 1
|
||||
/* Application Specific Short Lived PACs (only in volatile storage) */
|
||||
/* User Authorization PAC */
|
||||
#define PAC_TYPE_USER_AUTHORIZATION 3
|
||||
/* Application Specific Long Lived PACs */
|
||||
/* Machine Authentication PAC */
|
||||
#define PAC_TYPE_MACHINE_AUTHENTICATION 2
|
||||
|
||||
|
||||
/*
|
||||
* RFC 5422:
|
||||
* Section 3.3 - Key Derivations Used in the EAP-FAST Provisioning Exchange
|
||||
*/
|
||||
struct eap_fast_key_block_provisioning {
|
||||
/* Extra key material after TLS key_block */
|
||||
u8 session_key_seed[EAP_FAST_SKS_LEN];
|
||||
u8 server_challenge[16]; /* MSCHAPv2 ServerChallenge */
|
||||
u8 client_challenge[16]; /* MSCHAPv2 ClientChallenge */
|
||||
};
|
||||
|
||||
|
||||
struct wpabuf;
|
||||
struct tls_connection;
|
||||
|
||||
struct eap_fast_tlv_parse {
|
||||
u8 *eap_payload_tlv;
|
||||
size_t eap_payload_tlv_len;
|
||||
struct eap_tlv_crypto_binding_tlv *crypto_binding;
|
||||
size_t crypto_binding_len;
|
||||
int iresult;
|
||||
int result;
|
||||
int request_action;
|
||||
u8 *pac;
|
||||
size_t pac_len;
|
||||
};
|
||||
|
||||
void eap_fast_put_tlv_hdr(struct wpabuf *buf, u16 type, u16 len);
|
||||
void eap_fast_put_tlv(struct wpabuf *buf, u16 type, const void *data,
|
||||
u16 len);
|
||||
void eap_fast_put_tlv_buf(struct wpabuf *buf, u16 type,
|
||||
const struct wpabuf *data);
|
||||
struct wpabuf * eap_fast_tlv_eap_payload(struct wpabuf *buf);
|
||||
void eap_fast_derive_master_secret(const u8 *pac_key, const u8 *server_random,
|
||||
const u8 *client_random, u8 *master_secret);
|
||||
u8 * eap_fast_derive_key(void *ssl_ctx, struct tls_connection *conn,
|
||||
size_t len);
|
||||
int eap_fast_derive_eap_msk(const u8 *simck, u8 *msk);
|
||||
int eap_fast_derive_eap_emsk(const u8 *simck, u8 *emsk);
|
||||
int eap_fast_parse_tlv(struct eap_fast_tlv_parse *tlv,
|
||||
int tlv_type, u8 *pos, size_t len);
|
||||
|
||||
#endif /* EAP_FAST_H */
|
932
components/wpa_supplicant/src/eap_peer/eap_fast_pac.c
Normal file
932
components/wpa_supplicant/src/eap_peer/eap_fast_pac.c
Normal file
@ -0,0 +1,932 @@
|
||||
/*
|
||||
* EAP peer method: EAP-FAST PAC file processing
|
||||
* Copyright (c) 2004-2006, Jouni Malinen <j@w1.fi>
|
||||
*
|
||||
* This software may be distributed under the terms of the BSD license.
|
||||
* See README for more details.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "os.h"
|
||||
|
||||
#include "utils/common.h"
|
||||
#include "eap_peer/eap_config.h"
|
||||
#include "eap_peer/eap_i.h"
|
||||
#include "eap_peer/eap_fast_pac.h"
|
||||
|
||||
/* TODO: encrypt PAC-Key in the PAC file */
|
||||
|
||||
|
||||
/* Text data format */
|
||||
static const char *pac_file_hdr =
|
||||
"wpa_supplicant EAP-FAST PAC file - version 1";
|
||||
|
||||
/*
|
||||
* Binary data format
|
||||
* 4-octet magic value: 6A E4 92 0C
|
||||
* 2-octet version (big endian)
|
||||
* <version specific data>
|
||||
*
|
||||
* version=0:
|
||||
* Sequence of PAC entries:
|
||||
* 2-octet PAC-Type (big endian)
|
||||
* 32-octet PAC-Key
|
||||
* 2-octet PAC-Opaque length (big endian)
|
||||
* <variable len> PAC-Opaque data (length bytes)
|
||||
* 2-octet PAC-Info length (big endian)
|
||||
* <variable len> PAC-Info data (length bytes)
|
||||
*/
|
||||
|
||||
#define EAP_FAST_PAC_BINARY_MAGIC 0x6ae4920c
|
||||
#define EAP_FAST_PAC_BINARY_FORMAT_VERSION 0
|
||||
|
||||
|
||||
/**
|
||||
* eap_fast_free_pac - Free PAC data
|
||||
* @pac: Pointer to the PAC entry
|
||||
*
|
||||
* Note that the PAC entry must not be in a list since this function does not
|
||||
* remove the list links.
|
||||
*/
|
||||
void eap_fast_free_pac(struct eap_fast_pac *pac)
|
||||
{
|
||||
os_free(pac->pac_opaque);
|
||||
os_free(pac->pac_info);
|
||||
os_free(pac->a_id);
|
||||
os_free(pac->i_id);
|
||||
os_free(pac->a_id_info);
|
||||
os_free(pac);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* eap_fast_get_pac - Get a PAC entry based on A-ID
|
||||
* @pac_root: Pointer to root of the PAC list
|
||||
* @a_id: A-ID to search for
|
||||
* @a_id_len: Length of A-ID
|
||||
* @pac_type: PAC-Type to search for
|
||||
* Returns: Pointer to the PAC entry, or %NULL if A-ID not found
|
||||
*/
|
||||
struct eap_fast_pac * eap_fast_get_pac(struct eap_fast_pac *pac_root,
|
||||
const u8 *a_id, size_t a_id_len,
|
||||
u16 pac_type)
|
||||
{
|
||||
struct eap_fast_pac *pac = pac_root;
|
||||
|
||||
while (pac) {
|
||||
if (pac->pac_type == pac_type && pac->a_id_len == a_id_len &&
|
||||
os_memcmp(pac->a_id, a_id, a_id_len) == 0) {
|
||||
return pac;
|
||||
}
|
||||
pac = pac->next;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
static void eap_fast_remove_pac(struct eap_fast_pac **pac_root,
|
||||
struct eap_fast_pac **pac_current,
|
||||
const u8 *a_id, size_t a_id_len, u16 pac_type)
|
||||
{
|
||||
struct eap_fast_pac *pac, *prev;
|
||||
|
||||
pac = *pac_root;
|
||||
prev = NULL;
|
||||
|
||||
while (pac) {
|
||||
if (pac->pac_type == pac_type && pac->a_id_len == a_id_len &&
|
||||
os_memcmp(pac->a_id, a_id, a_id_len) == 0) {
|
||||
if (prev == NULL)
|
||||
*pac_root = pac->next;
|
||||
else
|
||||
prev->next = pac->next;
|
||||
if (*pac_current == pac)
|
||||
*pac_current = NULL;
|
||||
eap_fast_free_pac(pac);
|
||||
break;
|
||||
}
|
||||
prev = pac;
|
||||
pac = pac->next;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static int eap_fast_copy_buf(u8 **dst, size_t *dst_len,
|
||||
const u8 *src, size_t src_len)
|
||||
{
|
||||
if (src) {
|
||||
*dst = os_memdup(src, src_len);
|
||||
if (*dst == NULL)
|
||||
return -1;
|
||||
*dst_len = src_len;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* eap_fast_add_pac - Add a copy of a PAC entry to a list
|
||||
* @pac_root: Pointer to PAC list root pointer
|
||||
* @pac_current: Pointer to the current PAC pointer
|
||||
* @entry: New entry to clone and add to the list
|
||||
* Returns: 0 on success, -1 on failure
|
||||
*
|
||||
* This function makes a clone of the given PAC entry and adds this copied
|
||||
* entry to the list (pac_root). If an old entry for the same A-ID is found,
|
||||
* it will be removed from the PAC list and in this case, pac_current entry
|
||||
* is set to %NULL if it was the removed entry.
|
||||
*/
|
||||
int eap_fast_add_pac(struct eap_fast_pac **pac_root,
|
||||
struct eap_fast_pac **pac_current,
|
||||
struct eap_fast_pac *entry)
|
||||
{
|
||||
struct eap_fast_pac *pac;
|
||||
|
||||
if (entry == NULL || entry->a_id == NULL)
|
||||
return -1;
|
||||
|
||||
/* Remove a possible old entry for the matching A-ID. */
|
||||
eap_fast_remove_pac(pac_root, pac_current,
|
||||
entry->a_id, entry->a_id_len, entry->pac_type);
|
||||
|
||||
/* Allocate a new entry and add it to the list of PACs. */
|
||||
pac = os_zalloc(sizeof(*pac));
|
||||
if (pac == NULL)
|
||||
return -1;
|
||||
|
||||
pac->pac_type = entry->pac_type;
|
||||
os_memcpy(pac->pac_key, entry->pac_key, EAP_FAST_PAC_KEY_LEN);
|
||||
if (eap_fast_copy_buf(&pac->pac_opaque, &pac->pac_opaque_len,
|
||||
entry->pac_opaque, entry->pac_opaque_len) < 0 ||
|
||||
eap_fast_copy_buf(&pac->pac_info, &pac->pac_info_len,
|
||||
entry->pac_info, entry->pac_info_len) < 0 ||
|
||||
eap_fast_copy_buf(&pac->a_id, &pac->a_id_len,
|
||||
entry->a_id, entry->a_id_len) < 0 ||
|
||||
eap_fast_copy_buf(&pac->i_id, &pac->i_id_len,
|
||||
entry->i_id, entry->i_id_len) < 0 ||
|
||||
eap_fast_copy_buf(&pac->a_id_info, &pac->a_id_info_len,
|
||||
entry->a_id_info, entry->a_id_info_len) < 0) {
|
||||
eap_fast_free_pac(pac);
|
||||
return -1;
|
||||
}
|
||||
|
||||
pac->next = *pac_root;
|
||||
*pac_root = pac;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
struct eap_fast_read_ctx {
|
||||
FILE *f;
|
||||
const char *pos;
|
||||
const char *end;
|
||||
int line;
|
||||
char *buf;
|
||||
size_t buf_len;
|
||||
};
|
||||
|
||||
static int eap_fast_read_line(struct eap_fast_read_ctx *rc, char **value)
|
||||
{
|
||||
char *pos;
|
||||
|
||||
rc->line++;
|
||||
if (rc->f) {
|
||||
if (fgets(rc->buf, rc->buf_len, rc->f) == NULL)
|
||||
return -1;
|
||||
} else {
|
||||
const char *l_end;
|
||||
size_t len;
|
||||
if (rc->pos >= rc->end)
|
||||
return -1;
|
||||
l_end = rc->pos;
|
||||
while (l_end < rc->end && *l_end != '\n')
|
||||
l_end++;
|
||||
len = l_end - rc->pos;
|
||||
if (len >= rc->buf_len)
|
||||
len = rc->buf_len - 1;
|
||||
os_memcpy(rc->buf, rc->pos, len);
|
||||
rc->buf[len] = '\0';
|
||||
rc->pos = l_end + 1;
|
||||
}
|
||||
|
||||
rc->buf[rc->buf_len - 1] = '\0';
|
||||
pos = rc->buf;
|
||||
while (*pos != '\0') {
|
||||
if (*pos == '\n' || *pos == '\r') {
|
||||
*pos = '\0';
|
||||
break;
|
||||
}
|
||||
pos++;
|
||||
}
|
||||
|
||||
pos = os_strchr(rc->buf, '=');
|
||||
if (pos)
|
||||
*pos++ = '\0';
|
||||
*value = pos;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static u8 * eap_fast_parse_hex(const char *value, size_t *len)
|
||||
{
|
||||
int hlen;
|
||||
u8 *buf;
|
||||
|
||||
if (value == NULL)
|
||||
return NULL;
|
||||
hlen = os_strlen(value);
|
||||
if (hlen & 1)
|
||||
return NULL;
|
||||
*len = hlen / 2;
|
||||
buf = os_malloc(*len);
|
||||
if (buf == NULL)
|
||||
return NULL;
|
||||
if (hexstr2bin(value, buf, *len)) {
|
||||
os_free(buf);
|
||||
return NULL;
|
||||
}
|
||||
return buf;
|
||||
}
|
||||
|
||||
|
||||
static int eap_fast_init_pac_data(struct eap_sm *sm, const char *pac_file,
|
||||
struct eap_fast_read_ctx *rc)
|
||||
{
|
||||
os_memset(rc, 0, sizeof(*rc));
|
||||
|
||||
rc->buf_len = 2048;
|
||||
rc->buf = os_malloc(rc->buf_len);
|
||||
if (rc->buf == NULL)
|
||||
return -1;
|
||||
|
||||
if (os_strncmp(pac_file, "blob://", 7) == 0) {
|
||||
const struct wpa_config_blob *blob;
|
||||
blob = eap_get_config_blob(sm, pac_file);
|
||||
if (blob == NULL) {
|
||||
wpa_printf(MSG_INFO, "EAP-FAST: No PAC blob '%s' - "
|
||||
"assume no PAC entries have been "
|
||||
"provisioned", pac_file);
|
||||
os_free(rc->buf);
|
||||
return -1;
|
||||
}
|
||||
rc->pos = (char *) blob->data;
|
||||
rc->end = (char *) blob->data + blob->len;
|
||||
} else {
|
||||
rc->f = fopen(pac_file, "rb");
|
||||
if (rc->f == NULL) {
|
||||
wpa_printf(MSG_INFO, "EAP-FAST: No PAC file '%s' - "
|
||||
"assume no PAC entries have been "
|
||||
"provisioned", pac_file);
|
||||
os_free(rc->buf);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static void eap_fast_deinit_pac_data(struct eap_fast_read_ctx *rc)
|
||||
{
|
||||
os_free(rc->buf);
|
||||
if (rc->f)
|
||||
fclose(rc->f);
|
||||
}
|
||||
|
||||
|
||||
static const char * eap_fast_parse_start(struct eap_fast_pac **pac)
|
||||
{
|
||||
if (*pac)
|
||||
return "START line without END";
|
||||
|
||||
*pac = os_zalloc(sizeof(struct eap_fast_pac));
|
||||
if (*pac == NULL)
|
||||
return "No memory for PAC entry";
|
||||
(*pac)->pac_type = PAC_TYPE_TUNNEL_PAC;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
static const char * eap_fast_parse_end(struct eap_fast_pac **pac_root,
|
||||
struct eap_fast_pac **pac)
|
||||
{
|
||||
if (*pac == NULL)
|
||||
return "END line without START";
|
||||
if (*pac_root) {
|
||||
struct eap_fast_pac *end = *pac_root;
|
||||
while (end->next)
|
||||
end = end->next;
|
||||
end->next = *pac;
|
||||
} else
|
||||
*pac_root = *pac;
|
||||
|
||||
*pac = NULL;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
static const char * eap_fast_parse_pac_type(struct eap_fast_pac *pac,
|
||||
char *pos)
|
||||
{
|
||||
if (!pos)
|
||||
return "Cannot parse pac type";
|
||||
pac->pac_type = atoi(pos);
|
||||
if (pac->pac_type != PAC_TYPE_TUNNEL_PAC &&
|
||||
pac->pac_type != PAC_TYPE_USER_AUTHORIZATION &&
|
||||
pac->pac_type != PAC_TYPE_MACHINE_AUTHENTICATION)
|
||||
return "Unrecognized PAC-Type";
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
static const char * eap_fast_parse_pac_key(struct eap_fast_pac *pac, char *pos)
|
||||
{
|
||||
u8 *key;
|
||||
size_t key_len;
|
||||
|
||||
key = eap_fast_parse_hex(pos, &key_len);
|
||||
if (key == NULL || key_len != EAP_FAST_PAC_KEY_LEN) {
|
||||
os_free(key);
|
||||
return "Invalid PAC-Key";
|
||||
}
|
||||
|
||||
os_memcpy(pac->pac_key, key, EAP_FAST_PAC_KEY_LEN);
|
||||
os_free(key);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
static const char * eap_fast_parse_pac_opaque(struct eap_fast_pac *pac,
|
||||
char *pos)
|
||||
{
|
||||
os_free(pac->pac_opaque);
|
||||
pac->pac_opaque = eap_fast_parse_hex(pos, &pac->pac_opaque_len);
|
||||
if (pac->pac_opaque == NULL)
|
||||
return "Invalid PAC-Opaque";
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
static const char * eap_fast_parse_a_id(struct eap_fast_pac *pac, char *pos)
|
||||
{
|
||||
os_free(pac->a_id);
|
||||
pac->a_id = eap_fast_parse_hex(pos, &pac->a_id_len);
|
||||
if (pac->a_id == NULL)
|
||||
return "Invalid A-ID";
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
static const char * eap_fast_parse_i_id(struct eap_fast_pac *pac, char *pos)
|
||||
{
|
||||
os_free(pac->i_id);
|
||||
pac->i_id = eap_fast_parse_hex(pos, &pac->i_id_len);
|
||||
if (pac->i_id == NULL)
|
||||
return "Invalid I-ID";
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
static const char * eap_fast_parse_a_id_info(struct eap_fast_pac *pac,
|
||||
char *pos)
|
||||
{
|
||||
os_free(pac->a_id_info);
|
||||
pac->a_id_info = eap_fast_parse_hex(pos, &pac->a_id_info_len);
|
||||
if (pac->a_id_info == NULL)
|
||||
return "Invalid A-ID-Info";
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* eap_fast_load_pac - Load PAC entries (text format)
|
||||
* @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
|
||||
* @pac_root: Pointer to root of the PAC list (to be filled)
|
||||
* @pac_file: Name of the PAC file/blob to load
|
||||
* Returns: 0 on success, -1 on failure
|
||||
*/
|
||||
int eap_fast_load_pac(struct eap_sm *sm, struct eap_fast_pac **pac_root,
|
||||
const char *pac_file)
|
||||
{
|
||||
struct eap_fast_read_ctx rc;
|
||||
struct eap_fast_pac *pac = NULL;
|
||||
int count = 0;
|
||||
char *pos;
|
||||
const char *err = NULL;
|
||||
|
||||
if (pac_file == NULL)
|
||||
return -1;
|
||||
|
||||
if (eap_fast_init_pac_data(sm, pac_file, &rc) < 0)
|
||||
return 0;
|
||||
|
||||
if (eap_fast_read_line(&rc, &pos) < 0) {
|
||||
/* empty file - assume it is fine to overwrite */
|
||||
printf("\n\nassuming it is fine to overwrite... \n\n");
|
||||
eap_fast_deinit_pac_data(&rc);
|
||||
return 0;
|
||||
}
|
||||
printf("\n\nPAC FILE =\n%s", rc.pos);
|
||||
if (os_strcmp(pac_file_hdr, rc.buf) != 0)
|
||||
err = "Unrecognized header line";
|
||||
|
||||
while (!err && eap_fast_read_line(&rc, &pos) == 0) {
|
||||
if (os_strcmp(rc.buf, "START") == 0)
|
||||
err = eap_fast_parse_start(&pac);
|
||||
else if (os_strcmp(rc.buf, "END") == 0) {
|
||||
err = eap_fast_parse_end(pac_root, &pac);
|
||||
count++;
|
||||
} else if (!pac)
|
||||
err = "Unexpected line outside START/END block";
|
||||
else if (os_strcmp(rc.buf, "PAC-Type") == 0)
|
||||
err = eap_fast_parse_pac_type(pac, pos);
|
||||
else if (os_strcmp(rc.buf, "PAC-Key") == 0)
|
||||
err = eap_fast_parse_pac_key(pac, pos);
|
||||
else if (os_strcmp(rc.buf, "PAC-Opaque") == 0)
|
||||
err = eap_fast_parse_pac_opaque(pac, pos);
|
||||
else if (os_strcmp(rc.buf, "A-ID") == 0)
|
||||
err = eap_fast_parse_a_id(pac, pos);
|
||||
else if (os_strcmp(rc.buf, "I-ID") == 0)
|
||||
err = eap_fast_parse_i_id(pac, pos);
|
||||
else if (os_strcmp(rc.buf, "A-ID-Info") == 0)
|
||||
err = eap_fast_parse_a_id_info(pac, pos);
|
||||
}
|
||||
|
||||
if (pac) {
|
||||
if (!err)
|
||||
err = "PAC block not terminated with END";
|
||||
eap_fast_free_pac(pac);
|
||||
}
|
||||
|
||||
eap_fast_deinit_pac_data(&rc);
|
||||
|
||||
if (err) {
|
||||
wpa_printf(MSG_INFO, "EAP-FAST: %s in '%s:%d'",
|
||||
err, pac_file, rc.line);
|
||||
return -1;
|
||||
}
|
||||
|
||||
wpa_printf(MSG_DEBUG, "EAP-FAST: Read %d PAC entries from '%s'",
|
||||
count, pac_file);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static void eap_fast_write(char **buf, char **pos, size_t *buf_len,
|
||||
const char *field, const u8 *data,
|
||||
size_t len, int txt)
|
||||
{
|
||||
size_t i, need;
|
||||
int ret;
|
||||
char *end;
|
||||
|
||||
if (data == NULL || buf == NULL || *buf == NULL ||
|
||||
pos == NULL || *pos == NULL || *pos < *buf)
|
||||
return;
|
||||
|
||||
need = os_strlen(field) + len * 2 + 30;
|
||||
if (txt)
|
||||
need += os_strlen(field) + len + 20;
|
||||
|
||||
if (*pos - *buf + need > *buf_len) {
|
||||
char *nbuf = os_realloc(*buf, *buf_len + need);
|
||||
if (nbuf == NULL) {
|
||||
os_free(*buf);
|
||||
*buf = NULL;
|
||||
return;
|
||||
}
|
||||
*pos = nbuf + (*pos - *buf);
|
||||
*buf = nbuf;
|
||||
*buf_len += need;
|
||||
}
|
||||
end = *buf + *buf_len;
|
||||
|
||||
ret = os_snprintf(*pos, end - *pos, "%s=", field);
|
||||
if (os_snprintf_error(end - *pos, ret))
|
||||
return;
|
||||
*pos += ret;
|
||||
*pos += wpa_snprintf_hex(*pos, end - *pos, data, len);
|
||||
ret = os_snprintf(*pos, end - *pos, "\n");
|
||||
if (os_snprintf_error(end - *pos, ret))
|
||||
return;
|
||||
*pos += ret;
|
||||
|
||||
if (txt) {
|
||||
ret = os_snprintf(*pos, end - *pos, "%s-txt=", field);
|
||||
if (os_snprintf_error(end - *pos, ret))
|
||||
return;
|
||||
*pos += ret;
|
||||
for (i = 0; i < len; i++) {
|
||||
ret = os_snprintf(*pos, end - *pos, "%c", data[i]);
|
||||
if (os_snprintf_error(end - *pos, ret))
|
||||
return;
|
||||
*pos += ret;
|
||||
}
|
||||
ret = os_snprintf(*pos, end - *pos, "\n");
|
||||
if (os_snprintf_error(end - *pos, ret))
|
||||
return;
|
||||
*pos += ret;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static int eap_fast_write_pac(struct eap_sm *sm, const char *pac_file,
|
||||
char *buf, size_t len)
|
||||
{
|
||||
if (os_strncmp(pac_file, "blob://", 7) == 0) {
|
||||
struct wpa_config_blob *blob;
|
||||
blob = os_zalloc(sizeof(*blob));
|
||||
if (blob == NULL)
|
||||
return -1;
|
||||
blob->data = (u8 *) buf;
|
||||
blob->len = len;
|
||||
buf = NULL;
|
||||
blob->name = os_strdup(pac_file + 7);
|
||||
if (blob->name == NULL) {
|
||||
os_free(blob);
|
||||
return -1;
|
||||
}
|
||||
eap_set_config_blob(sm, blob);
|
||||
} else {
|
||||
FILE *f;
|
||||
f = fopen(pac_file, "wb");
|
||||
if (f == NULL) {
|
||||
wpa_printf(MSG_INFO, "EAP-FAST: Failed to open PAC "
|
||||
"file '%s' for writing", pac_file);
|
||||
return -1;
|
||||
}
|
||||
if (fwrite(buf, 1, len, f) != len) {
|
||||
wpa_printf(MSG_INFO, "EAP-FAST: Failed to write all "
|
||||
"PACs into '%s'", pac_file);
|
||||
fclose(f);
|
||||
return -1;
|
||||
}
|
||||
os_free(buf);
|
||||
fclose(f);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int eap_fast_add_pac_data(struct eap_fast_pac *pac, char **buf,
|
||||
char **pos, size_t *buf_len)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = os_snprintf(*pos, *buf + *buf_len - *pos,
|
||||
"START\nPAC-Type=%d\n", pac->pac_type);
|
||||
if (os_snprintf_error(*buf + *buf_len - *pos, ret))
|
||||
return -1;
|
||||
|
||||
*pos += ret;
|
||||
eap_fast_write(buf, pos, buf_len, "PAC-Key",
|
||||
pac->pac_key, EAP_FAST_PAC_KEY_LEN, 0);
|
||||
eap_fast_write(buf, pos, buf_len, "PAC-Opaque",
|
||||
pac->pac_opaque, pac->pac_opaque_len, 0);
|
||||
eap_fast_write(buf, pos, buf_len, "PAC-Info",
|
||||
pac->pac_info, pac->pac_info_len, 0);
|
||||
eap_fast_write(buf, pos, buf_len, "A-ID",
|
||||
pac->a_id, pac->a_id_len, 0);
|
||||
eap_fast_write(buf, pos, buf_len, "I-ID",
|
||||
pac->i_id, pac->i_id_len, 1);
|
||||
eap_fast_write(buf, pos, buf_len, "A-ID-Info",
|
||||
pac->a_id_info, pac->a_id_info_len, 1);
|
||||
if (*buf == NULL) {
|
||||
wpa_printf(MSG_DEBUG, "EAP-FAST: No memory for PAC "
|
||||
"data");
|
||||
return -1;
|
||||
}
|
||||
ret = os_snprintf(*pos, *buf + *buf_len - *pos, "END\n");
|
||||
if (os_snprintf_error(*buf + *buf_len - *pos, ret))
|
||||
return -1;
|
||||
*pos += ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* eap_fast_save_pac - Save PAC entries (text format)
|
||||
* @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
|
||||
* @pac_root: Root of the PAC list
|
||||
* @pac_file: Name of the PAC file/blob
|
||||
* Returns: 0 on success, -1 on failure
|
||||
*/
|
||||
int eap_fast_save_pac(struct eap_sm *sm, struct eap_fast_pac *pac_root,
|
||||
const char *pac_file)
|
||||
{
|
||||
struct eap_fast_pac *pac;
|
||||
int ret, count = 0;
|
||||
char *buf, *pos;
|
||||
size_t buf_len;
|
||||
|
||||
if (pac_file == NULL)
|
||||
return -1;
|
||||
|
||||
buf_len = 1024;
|
||||
pos = buf = os_malloc(buf_len);
|
||||
if (buf == NULL)
|
||||
return -1;
|
||||
|
||||
ret = os_snprintf(pos, buf + buf_len - pos, "%s\n", pac_file_hdr);
|
||||
if (os_snprintf_error(buf + buf_len - pos, ret)) {
|
||||
os_free(buf);
|
||||
return -1;
|
||||
}
|
||||
pos += ret;
|
||||
|
||||
pac = pac_root;
|
||||
while (pac) {
|
||||
if (eap_fast_add_pac_data(pac, &buf, &pos, &buf_len)) {
|
||||
os_free(buf);
|
||||
return -1;
|
||||
}
|
||||
count++;
|
||||
pac = pac->next;
|
||||
}
|
||||
|
||||
if (eap_fast_write_pac(sm, pac_file, buf, pos - buf)) {
|
||||
os_free(buf);
|
||||
return -1;
|
||||
}
|
||||
|
||||
wpa_printf(MSG_DEBUG, "PAC file: %s", (sm->blob[3].data));
|
||||
wpa_printf(MSG_DEBUG, "EAP-FAST: Wrote %d PAC entries into '%s'",
|
||||
count, pac_file);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* eap_fast_pac_list_truncate - Truncate a PAC list to the given length
|
||||
* @pac_root: Root of the PAC list
|
||||
* @max_len: Maximum length of the list (>= 1)
|
||||
* Returns: Number of PAC entries removed
|
||||
*/
|
||||
size_t eap_fast_pac_list_truncate(struct eap_fast_pac *pac_root,
|
||||
size_t max_len)
|
||||
{
|
||||
struct eap_fast_pac *pac, *prev;
|
||||
size_t count;
|
||||
|
||||
pac = pac_root;
|
||||
prev = NULL;
|
||||
count = 0;
|
||||
|
||||
while (pac) {
|
||||
count++;
|
||||
if (count > max_len)
|
||||
break;
|
||||
prev = pac;
|
||||
pac = pac->next;
|
||||
}
|
||||
|
||||
if (count <= max_len || prev == NULL)
|
||||
return 0;
|
||||
|
||||
count = 0;
|
||||
prev->next = NULL;
|
||||
|
||||
while (pac) {
|
||||
prev = pac;
|
||||
pac = pac->next;
|
||||
eap_fast_free_pac(prev);
|
||||
count++;
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
|
||||
static void eap_fast_pac_get_a_id(struct eap_fast_pac *pac)
|
||||
{
|
||||
u8 *pos, *end;
|
||||
u16 type, len;
|
||||
|
||||
pos = pac->pac_info;
|
||||
end = pos + pac->pac_info_len;
|
||||
|
||||
while (end - pos > 4) {
|
||||
type = WPA_GET_BE16(pos);
|
||||
pos += 2;
|
||||
len = WPA_GET_BE16(pos);
|
||||
pos += 2;
|
||||
if (len > (unsigned int) (end - pos))
|
||||
break;
|
||||
|
||||
if (type == PAC_TYPE_A_ID) {
|
||||
os_free(pac->a_id);
|
||||
pac->a_id = os_memdup(pos, len);
|
||||
if (pac->a_id == NULL)
|
||||
break;
|
||||
pac->a_id_len = len;
|
||||
}
|
||||
|
||||
if (type == PAC_TYPE_A_ID_INFO) {
|
||||
os_free(pac->a_id_info);
|
||||
pac->a_id_info = os_memdup(pos, len);
|
||||
if (pac->a_id_info == NULL)
|
||||
break;
|
||||
pac->a_id_info_len = len;
|
||||
}
|
||||
|
||||
pos += len;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* eap_fast_load_pac_bin - Load PAC entries (binary format)
|
||||
* @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
|
||||
* @pac_root: Pointer to root of the PAC list (to be filled)
|
||||
* @pac_file: Name of the PAC file/blob to load
|
||||
* Returns: 0 on success, -1 on failure
|
||||
*/
|
||||
int eap_fast_load_pac_bin(struct eap_sm *sm, struct eap_fast_pac **pac_root,
|
||||
const char *pac_file)
|
||||
{
|
||||
const struct wpa_config_blob *blob = NULL;
|
||||
u8 *buf, *end, *pos;
|
||||
size_t len = 0;
|
||||
size_t count = 0;
|
||||
struct eap_fast_pac *pac, *prev;
|
||||
|
||||
*pac_root = NULL;
|
||||
|
||||
if (pac_file == NULL)
|
||||
return -1;
|
||||
|
||||
if (sm->config.pac_file != NULL) { //if (os_strncmp(pac_file, "blob://", 7) == 0) {
|
||||
blob = eap_get_config_blob(sm, PAC_FILE_NAME);
|
||||
if (blob == NULL) {
|
||||
wpa_printf(MSG_INFO, "EAP-FAST: No PAC blob '%s' - "
|
||||
"assume no PAC entries have been "
|
||||
"provisioned", pac_file);
|
||||
return 0;
|
||||
}
|
||||
buf = (u8 *) blob->data;
|
||||
len = blob->len;
|
||||
} else {
|
||||
buf = (u8 *) sm->blob[3].data; //(u8 *) os_readfile(pac_file, &len);
|
||||
if (buf == NULL) {
|
||||
wpa_printf(MSG_INFO, "EAP-FAST: No PAC file '%s' - "
|
||||
"assume no PAC entries have been "
|
||||
"provisioned", pac_file);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (len == 0) {
|
||||
if (blob == NULL)
|
||||
os_free(buf);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (len < 6 || WPA_GET_BE32(buf) != EAP_FAST_PAC_BINARY_MAGIC ||
|
||||
WPA_GET_BE16(buf + 4) != EAP_FAST_PAC_BINARY_FORMAT_VERSION) {
|
||||
wpa_printf(MSG_INFO, "EAP-FAST: Invalid PAC file '%s' (bin)",
|
||||
pac_file);
|
||||
if (blob == NULL)
|
||||
os_free(buf);
|
||||
return -1;
|
||||
}
|
||||
|
||||
pac = prev = NULL;
|
||||
pos = buf + 6;
|
||||
end = buf + len;
|
||||
while (pos < end) {
|
||||
u16 val;
|
||||
|
||||
if (end - pos < 2 + EAP_FAST_PAC_KEY_LEN + 2 + 2) {
|
||||
pac = NULL;
|
||||
goto parse_fail;
|
||||
}
|
||||
|
||||
pac = os_zalloc(sizeof(*pac));
|
||||
if (pac == NULL)
|
||||
goto parse_fail;
|
||||
|
||||
pac->pac_type = WPA_GET_BE16(pos);
|
||||
pos += 2;
|
||||
os_memcpy(pac->pac_key, pos, EAP_FAST_PAC_KEY_LEN);
|
||||
pos += EAP_FAST_PAC_KEY_LEN;
|
||||
val = WPA_GET_BE16(pos);
|
||||
pos += 2;
|
||||
if (val > end - pos)
|
||||
goto parse_fail;
|
||||
pac->pac_opaque_len = val;
|
||||
pac->pac_opaque = os_memdup(pos, pac->pac_opaque_len);
|
||||
if (pac->pac_opaque == NULL)
|
||||
goto parse_fail;
|
||||
pos += pac->pac_opaque_len;
|
||||
if (2 > end - pos)
|
||||
goto parse_fail;
|
||||
val = WPA_GET_BE16(pos);
|
||||
pos += 2;
|
||||
if (val > end - pos)
|
||||
goto parse_fail;
|
||||
pac->pac_info_len = val;
|
||||
pac->pac_info = os_memdup(pos, pac->pac_info_len);
|
||||
if (pac->pac_info == NULL)
|
||||
goto parse_fail;
|
||||
pos += pac->pac_info_len;
|
||||
eap_fast_pac_get_a_id(pac);
|
||||
|
||||
count++;
|
||||
if (prev)
|
||||
prev->next = pac;
|
||||
else
|
||||
*pac_root = pac;
|
||||
prev = pac;
|
||||
}
|
||||
|
||||
if (blob == NULL)
|
||||
os_free(buf);
|
||||
|
||||
wpa_printf(MSG_DEBUG, "EAP-FAST: Read %lu PAC entries from '%s' (bin)",
|
||||
(unsigned long) count, pac_file);
|
||||
|
||||
return 0;
|
||||
|
||||
parse_fail:
|
||||
wpa_printf(MSG_INFO, "EAP-FAST: Failed to parse PAC file '%s' (bin)",
|
||||
pac_file);
|
||||
if (blob == NULL)
|
||||
os_free(buf);
|
||||
if (pac)
|
||||
eap_fast_free_pac(pac);
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* eap_fast_save_pac_bin - Save PAC entries (binary format)
|
||||
* @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
|
||||
* @pac_root: Root of the PAC list
|
||||
* @pac_file: Name of the PAC file/blob
|
||||
* Returns: 0 on success, -1 on failure
|
||||
*/
|
||||
int eap_fast_save_pac_bin(struct eap_sm *sm, struct eap_fast_pac *pac_root,
|
||||
const char *pac_file)
|
||||
{
|
||||
size_t len, count = 0;
|
||||
struct eap_fast_pac *pac;
|
||||
u8 *buf, *pos;
|
||||
|
||||
len = 6;
|
||||
pac = pac_root;
|
||||
while (pac) {
|
||||
if (pac->pac_opaque_len > 65535 ||
|
||||
pac->pac_info_len > 65535)
|
||||
return -1;
|
||||
len += 2 + EAP_FAST_PAC_KEY_LEN + 2 + pac->pac_opaque_len +
|
||||
2 + pac->pac_info_len;
|
||||
pac = pac->next;
|
||||
}
|
||||
|
||||
buf = os_malloc(len);
|
||||
if (buf == NULL)
|
||||
return -1;
|
||||
|
||||
pos = buf;
|
||||
WPA_PUT_BE32(pos, EAP_FAST_PAC_BINARY_MAGIC);
|
||||
pos += 4;
|
||||
WPA_PUT_BE16(pos, EAP_FAST_PAC_BINARY_FORMAT_VERSION);
|
||||
pos += 2;
|
||||
|
||||
pac = pac_root;
|
||||
while (pac) {
|
||||
WPA_PUT_BE16(pos, pac->pac_type);
|
||||
pos += 2;
|
||||
os_memcpy(pos, pac->pac_key, EAP_FAST_PAC_KEY_LEN);
|
||||
pos += EAP_FAST_PAC_KEY_LEN;
|
||||
WPA_PUT_BE16(pos, pac->pac_opaque_len);
|
||||
pos += 2;
|
||||
os_memcpy(pos, pac->pac_opaque, pac->pac_opaque_len);
|
||||
pos += pac->pac_opaque_len;
|
||||
WPA_PUT_BE16(pos, pac->pac_info_len);
|
||||
pos += 2;
|
||||
os_memcpy(pos, pac->pac_info, pac->pac_info_len);
|
||||
pos += pac->pac_info_len;
|
||||
|
||||
pac = pac->next;
|
||||
count++;
|
||||
}
|
||||
|
||||
if (eap_fast_write_pac(sm, pac_file, (char *) buf, len)) {
|
||||
os_free(buf);
|
||||
return -1;
|
||||
}
|
||||
|
||||
wpa_printf(MSG_DEBUG, "EAP-FAST: Wrote %lu PAC entries into '%s' "
|
||||
"(bin)", (unsigned long) count, pac_file);
|
||||
|
||||
return 0;
|
||||
}
|
50
components/wpa_supplicant/src/eap_peer/eap_fast_pac.h
Normal file
50
components/wpa_supplicant/src/eap_peer/eap_fast_pac.h
Normal file
@ -0,0 +1,50 @@
|
||||
/*
|
||||
* EAP peer method: EAP-FAST PAC file processing
|
||||
* Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi>
|
||||
*
|
||||
* This software may be distributed under the terms of the BSD license.
|
||||
* See README for more details.
|
||||
*/
|
||||
|
||||
#ifndef EAP_FAST_PAC_H
|
||||
#define EAP_FAST_PAC_H
|
||||
|
||||
#include "eap_peer/eap_fast_common.h"
|
||||
|
||||
struct eap_fast_pac {
|
||||
struct eap_fast_pac *next;
|
||||
|
||||
u8 pac_key[EAP_FAST_PAC_KEY_LEN];
|
||||
u8 *pac_opaque;
|
||||
size_t pac_opaque_len;
|
||||
u8 *pac_info;
|
||||
size_t pac_info_len;
|
||||
u8 *a_id;
|
||||
size_t a_id_len;
|
||||
u8 *i_id;
|
||||
size_t i_id_len;
|
||||
u8 *a_id_info;
|
||||
size_t a_id_info_len;
|
||||
u16 pac_type;
|
||||
};
|
||||
|
||||
|
||||
void eap_fast_free_pac(struct eap_fast_pac *pac);
|
||||
struct eap_fast_pac * eap_fast_get_pac(struct eap_fast_pac *pac_root,
|
||||
const u8 *a_id, size_t a_id_len,
|
||||
u16 pac_type);
|
||||
int eap_fast_add_pac(struct eap_fast_pac **pac_root,
|
||||
struct eap_fast_pac **pac_current,
|
||||
struct eap_fast_pac *entry);
|
||||
int eap_fast_load_pac(struct eap_sm *sm, struct eap_fast_pac **pac_root,
|
||||
const char *pac_file);
|
||||
int eap_fast_save_pac(struct eap_sm *sm, struct eap_fast_pac *pac_root,
|
||||
const char *pac_file);
|
||||
size_t eap_fast_pac_list_truncate(struct eap_fast_pac *pac_root,
|
||||
size_t max_len);
|
||||
int eap_fast_load_pac_bin(struct eap_sm *sm, struct eap_fast_pac **pac_root,
|
||||
const char *pac_file);
|
||||
int eap_fast_save_pac_bin(struct eap_sm *sm, struct eap_fast_pac *pac_root,
|
||||
const char *pac_file);
|
||||
|
||||
#endif /* EAP_FAST_PAC_H */
|
@ -263,8 +263,9 @@ struct eap_method {
|
||||
#define CLIENT_CERT_NAME "CLC"
|
||||
#define CA_CERT_NAME "CAC"
|
||||
#define PRIVATE_KEY_NAME "PVK"
|
||||
#define PAC_FILE_NAME "PAC"
|
||||
#define BLOB_NAME_LEN 3
|
||||
#define BLOB_NUM 3
|
||||
#define BLOB_NUM 4
|
||||
|
||||
enum SIG_WPA2 {
|
||||
SIG_WPA2_START = 0,
|
||||
@ -282,6 +283,7 @@ struct eap_sm {
|
||||
void *eap_method_priv;
|
||||
int init_phase2;
|
||||
|
||||
void *msg_ctx;
|
||||
void *ssl_ctx;
|
||||
|
||||
unsigned int workaround;
|
||||
@ -296,6 +298,12 @@ struct eap_sm {
|
||||
#endif
|
||||
u8 finish_state;
|
||||
|
||||
/* Optional challenges generated in Phase 1 (EAP-FAST) */
|
||||
u8 *peer_challenge, *auth_challenge;
|
||||
|
||||
unsigned int expected_failure:1;
|
||||
unsigned int ext_cert_check:1;
|
||||
unsigned int waiting_ext_cert_check:1;
|
||||
bool peap_done;
|
||||
|
||||
u8 *eapKeyData;
|
||||
@ -319,6 +327,7 @@ 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);
|
||||
void eap_set_config_blob(struct eap_sm *sm, struct wpa_config_blob *blob);
|
||||
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);
|
||||
|
||||
|
@ -30,6 +30,7 @@ void eap_peer_unregister_methods(void);
|
||||
int eap_peer_tls_register(void);
|
||||
int eap_peer_peap_register(void);
|
||||
int eap_peer_ttls_register(void);
|
||||
int eap_peer_fast_register(void);
|
||||
int eap_peer_mschapv2_register(void);
|
||||
|
||||
void eap_peer_unregister_methods(void);
|
||||
|
@ -21,6 +21,7 @@
|
||||
#include "eap_peer/eap_config.h"
|
||||
#include "eap_peer/mschapv2.h"
|
||||
#include "eap_peer/eap_methods.h"
|
||||
#include "common/wpa_ctrl.h"
|
||||
|
||||
#define MSCHAPV2_OP_CHALLENGE 1
|
||||
#define MSCHAPV2_OP_RESPONSE 2
|
||||
@ -104,6 +105,24 @@ eap_mschapv2_init(struct eap_sm *sm)
|
||||
if (data == NULL)
|
||||
return NULL;
|
||||
|
||||
if (sm->peer_challenge) {
|
||||
data->peer_challenge = os_memdup(sm->peer_challenge,
|
||||
MSCHAPV2_CHAL_LEN);
|
||||
if (data->peer_challenge == NULL) {
|
||||
eap_mschapv2_deinit(sm, data);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if (sm->auth_challenge) {
|
||||
data->auth_challenge = os_memdup(sm->auth_challenge,
|
||||
MSCHAPV2_CHAL_LEN);
|
||||
if (data->auth_challenge == NULL) {
|
||||
eap_mschapv2_deinit(sm, data);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
data->phase2 = sm->init_phase2;
|
||||
|
||||
return data;
|
||||
@ -139,8 +158,15 @@ eap_mschapv2_challenge_reply(
|
||||
ms = wpabuf_put(resp, sizeof(*ms));
|
||||
ms->op_code = MSCHAPV2_OP_RESPONSE;
|
||||
ms->mschapv2_id = mschapv2_id;
|
||||
if (data->prev_error)
|
||||
if (data->prev_error) {
|
||||
/*
|
||||
* TODO: this does not seem to be enough when processing two
|
||||
* or more failure messages. IAS did not increment mschapv2_id
|
||||
* in its own packets, but it seemed to expect the peer to
|
||||
* increment this for all packets(?).
|
||||
*/
|
||||
ms->mschapv2_id++;
|
||||
}
|
||||
WPA_PUT_BE16(ms->ms_length, ms_len);
|
||||
wpabuf_put_u8(resp, sizeof(*r));
|
||||
|
||||
@ -148,33 +174,53 @@ eap_mschapv2_challenge_reply(
|
||||
r = wpabuf_put(resp, sizeof(*r));
|
||||
peer_challenge = r->peer_challenge;
|
||||
if (data->peer_challenge) {
|
||||
wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: peer_challenge generated "
|
||||
"in Phase 1");
|
||||
peer_challenge = data->peer_challenge;
|
||||
os_memset(r->peer_challenge, 0, MSCHAPV2_CHAL_LEN);
|
||||
os_memset(r->peer_challenge, 0, MSCHAPV2_CHAL_LEN);
|
||||
} else if (random_get_bytes(peer_challenge, MSCHAPV2_CHAL_LEN)) {
|
||||
wpabuf_free(resp);
|
||||
return NULL;
|
||||
}
|
||||
os_memset(r->reserved, 0, 8);
|
||||
if (data->auth_challenge)
|
||||
if (data->auth_challenge) {
|
||||
wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: auth_challenge generated "
|
||||
"in Phase 1");
|
||||
auth_challenge = data->auth_challenge;
|
||||
}
|
||||
if (mschapv2_derive_response(identity, identity_len, password,
|
||||
password_len, pwhash, auth_challenge,
|
||||
peer_challenge, r->nt_response,
|
||||
data->auth_response, data->master_key)) {
|
||||
wpa_printf(MSG_ERROR, "EAP-MSCHAPV2: Failed to derive "
|
||||
"response");
|
||||
wpabuf_free(resp);
|
||||
return NULL;
|
||||
}
|
||||
data->auth_response_valid = 1;
|
||||
data->master_key_valid = 1;
|
||||
|
||||
r->flags = 0;
|
||||
r->flags = 0; /* reserved, must be zero */
|
||||
|
||||
wpabuf_put_data(resp, identity, identity_len);
|
||||
wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: TX identifier %d mschapv2_id %d "
|
||||
"(response)", id, ms->mschapv2_id);
|
||||
return resp;
|
||||
}
|
||||
|
||||
static struct wpabuf *
|
||||
eap_mschapv2_challenge(
|
||||
|
||||
/**
|
||||
* eap_mschapv2_process - Process an EAP-MSCHAPv2 challenge message
|
||||
* @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
|
||||
* @data: Pointer to private EAP method data from eap_mschapv2_init()
|
||||
* @ret: Return values from EAP request validation and processing
|
||||
* @req: Pointer to EAP-MSCHAPv2 header from the request
|
||||
* @req_len: Length of the EAP-MSCHAPv2 data
|
||||
* @id: EAP identifier used in the request
|
||||
* Returns: Pointer to allocated EAP response packet (eapRespData) or %NULL if
|
||||
* no reply available
|
||||
*/
|
||||
static struct wpabuf * eap_mschapv2_challenge(
|
||||
struct eap_sm *sm, struct eap_mschapv2_data *data,
|
||||
struct eap_method_ret *ret, const struct eap_mschapv2_hdr *req,
|
||||
size_t req_len, u8 id)
|
||||
@ -186,7 +232,10 @@ eap_mschapv2_challenge(
|
||||
eap_get_config_password(sm, &len) == NULL)
|
||||
return NULL;
|
||||
|
||||
wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Received challenge");
|
||||
if (req_len < sizeof(*req) + 1) {
|
||||
wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Too short challenge data "
|
||||
"(len %lu)", (unsigned long) req_len);
|
||||
ret->ignore = true;
|
||||
return NULL;
|
||||
}
|
||||
@ -194,21 +243,30 @@ eap_mschapv2_challenge(
|
||||
challenge_len = *pos++;
|
||||
len = req_len - sizeof(*req) - 1;
|
||||
if (challenge_len != MSCHAPV2_CHAL_LEN) {
|
||||
wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Invalid challenge length "
|
||||
"%lu", (unsigned long) challenge_len);
|
||||
ret->ignore = true;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (len < challenge_len) {
|
||||
wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Too short challenge"
|
||||
" packet: len=%lu challenge_len=%lu",
|
||||
(unsigned long) len, (unsigned long) challenge_len);
|
||||
ret->ignore = true;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (data->passwd_change_challenge_valid)
|
||||
if (data->passwd_change_challenge_valid) {
|
||||
wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Using challenge from the "
|
||||
"failure message");
|
||||
challenge = data->passwd_change_challenge;
|
||||
else
|
||||
} else
|
||||
challenge = pos;
|
||||
pos += challenge_len;
|
||||
len -= challenge_len;
|
||||
wpa_hexdump_ascii(MSG_DEBUG, "EAP-MSCHAPV2: Authentication Servername",
|
||||
pos, len);
|
||||
|
||||
ret->ignore = false;
|
||||
ret->methodState = METHOD_MAY_CONT;
|
||||
@ -225,9 +283,13 @@ eap_mschapv2_password_changed(struct eap_sm *sm,
|
||||
{
|
||||
struct eap_peer_config *config = eap_get_config(sm);
|
||||
if (config && config->new_password) {
|
||||
wpa_msg(sm->msg_ctx, MSG_INFO,
|
||||
WPA_EVENT_PASSWORD_CHANGED
|
||||
"EAP-MSCHAPV2: Password changed successfully");
|
||||
data->prev_error = 0;
|
||||
os_free(config->password);
|
||||
if (config->flags & EAP_CONFIG_FLAGS_EXT_PASSWORD) {
|
||||
/* TODO: update external storage */
|
||||
} else if (config->flags & EAP_CONFIG_FLAGS_PASSWORD_NTHASH) {
|
||||
config->password = os_malloc(16);
|
||||
config->password_len = 16;
|
||||
@ -257,11 +319,14 @@ eap_mschapv2_success(struct eap_sm *sm,
|
||||
const u8 *pos;
|
||||
size_t len;
|
||||
|
||||
wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Received success");
|
||||
len = req_len - sizeof(*req);
|
||||
pos = (const u8 *)(req + 1);
|
||||
pos = (const u8 *) (req + 1);
|
||||
if (!data->auth_response_valid ||
|
||||
mschapv2_verify_auth_response(data->auth_response, pos, len)) {
|
||||
ret->methodState = METHOD_NONE;
|
||||
wpa_printf(MSG_WARNING, "EAP-MSCHAPV2: Invalid authenticator "
|
||||
"response in success request");
|
||||
ret->methodState = METHOD_DONE;
|
||||
ret->decision = DECISION_FAIL;
|
||||
return NULL;
|
||||
}
|
||||
@ -271,15 +336,23 @@ eap_mschapv2_success(struct eap_sm *sm,
|
||||
pos++;
|
||||
len--;
|
||||
}
|
||||
wpa_hexdump_ascii(MSG_DEBUG, "EAP-MSCHAPV2: Success message",
|
||||
pos, len);
|
||||
wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Authentication succeeded");
|
||||
|
||||
/* Note: Only op_code of the EAP-MSCHAPV2 header is included in success
|
||||
* message. */
|
||||
resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, 1,
|
||||
EAP_CODE_RESPONSE, id);
|
||||
if (resp == NULL) {
|
||||
wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Failed to allocate "
|
||||
"buffer for success response");
|
||||
ret->ignore = true;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
wpabuf_put_u8(resp, MSCHAPV2_OP_SUCCESS);
|
||||
wpabuf_put_u8(resp, MSCHAPV2_OP_SUCCESS); /* op_code */
|
||||
|
||||
ret->methodState = METHOD_DONE;
|
||||
ret->decision = DECISION_UNCOND_SUCC;
|
||||
ret->allowNotifications = false;
|
||||
@ -291,19 +364,25 @@ eap_mschapv2_success(struct eap_sm *sm,
|
||||
return resp;
|
||||
}
|
||||
|
||||
static int
|
||||
eap_mschapv2_failure_txt(struct eap_sm *sm,
|
||||
struct eap_mschapv2_data *data, char *txt)
|
||||
|
||||
static int eap_mschapv2_failure_txt(struct eap_sm *sm,
|
||||
struct eap_mschapv2_data *data, char *txt)
|
||||
{
|
||||
char *pos;
|
||||
char *pos = "";
|
||||
int retry = 1;
|
||||
struct eap_peer_config *config = eap_get_config(sm);
|
||||
|
||||
/* For example:
|
||||
* E=691 R=1 C=<32 octets hex challenge> V=3 M=Authentication Failure
|
||||
*/
|
||||
|
||||
pos = txt;
|
||||
|
||||
if (pos && os_strncmp(pos, "E=", 2) == 0) {
|
||||
pos += 2;
|
||||
data->prev_error = atoi(pos);
|
||||
wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: error %d",
|
||||
data->prev_error);
|
||||
pos = (char *)os_strchr(pos, ' ');
|
||||
if (pos)
|
||||
pos++;
|
||||
@ -312,6 +391,8 @@ eap_mschapv2_failure_txt(struct eap_sm *sm,
|
||||
if (pos && os_strncmp(pos, "R=", 2) == 0) {
|
||||
pos += 2;
|
||||
retry = atoi(pos);
|
||||
wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: retry is %sallowed",
|
||||
retry == 1 ? "" : "not ");
|
||||
pos = (char *)os_strchr(pos, ' ');
|
||||
if (pos)
|
||||
pos++;
|
||||
@ -324,19 +405,32 @@ eap_mschapv2_failure_txt(struct eap_sm *sm,
|
||||
if (hex_len == PASSWD_CHANGE_CHAL_LEN * 2) {
|
||||
if (hexstr2bin(pos, data->passwd_change_challenge,
|
||||
PASSWD_CHANGE_CHAL_LEN)) {
|
||||
wpa_printf(MSG_ERROR, "EAP-MSCHAPV2: invalid failure challenge\n");
|
||||
wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: invalid "
|
||||
"failure challenge");
|
||||
} else {
|
||||
wpa_hexdump(MSG_DEBUG, "EAP-MSCHAPV2: failure "
|
||||
"challenge",
|
||||
data->passwd_change_challenge,
|
||||
PASSWD_CHANGE_CHAL_LEN);
|
||||
data->passwd_change_challenge_valid = 1;
|
||||
}
|
||||
} else {
|
||||
wpa_printf(MSG_ERROR, "EAP-MSCHAPV2: required challenge field "
|
||||
"was not present in failure message\n");
|
||||
wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: invalid failure "
|
||||
"challenge len %d", hex_len);
|
||||
}
|
||||
pos = os_strchr(pos, ' ');
|
||||
if (pos)
|
||||
pos++;
|
||||
} else {
|
||||
wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: required challenge field "
|
||||
"was not present in failure message");
|
||||
}
|
||||
|
||||
if (pos && os_strncmp(pos, "V=", 2) == 0) {
|
||||
pos += 2;
|
||||
data->passwd_change_version = atoi(pos);
|
||||
wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: password changing "
|
||||
"protocol version %d", data->passwd_change_version);
|
||||
pos = (char *)os_strchr(pos, ' ');
|
||||
if (pos)
|
||||
pos++;
|
||||
@ -345,24 +439,38 @@ eap_mschapv2_failure_txt(struct eap_sm *sm,
|
||||
if (pos && os_strncmp(pos, "M=", 2) == 0) {
|
||||
pos += 2;
|
||||
}
|
||||
if (data->prev_error == ERROR_AUTHENTICATION_FAILURE && retry &&
|
||||
config && config->phase2 &&
|
||||
os_strstr(config->phase2, "mschapv2_retry=0")) {
|
||||
wpa_printf(MSG_DEBUG,
|
||||
"EAP-MSCHAPV2: mark password retry disabled based on local configuration");
|
||||
retry = 0;
|
||||
}
|
||||
if (data->prev_error == ERROR_PASSWD_EXPIRED &&
|
||||
data->passwd_change_version == 3 && config) {
|
||||
if (config->new_password == NULL) {
|
||||
wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Password expired - "
|
||||
"password change reqired\n");
|
||||
wpa_msg(sm->msg_ctx, MSG_INFO,
|
||||
"EAP-MSCHAPV2: Password expired - password "
|
||||
"change required");
|
||||
eap_sm_request_new_password(sm);
|
||||
}
|
||||
} else if (retry == 1 && config) {
|
||||
/* TODO: could prevent the current password from being used
|
||||
* again at least for some period of time */
|
||||
if (!config->mschapv2_retry)
|
||||
eap_sm_request_identity(sm);
|
||||
eap_sm_request_password(sm);
|
||||
config->mschapv2_retry = 1;
|
||||
} else if (config) {
|
||||
/* TODO: prevent retries using same username/password */
|
||||
config->mschapv2_retry = 0;
|
||||
}
|
||||
|
||||
return retry == 1;
|
||||
}
|
||||
|
||||
static struct wpabuf *
|
||||
eap_mschapv2_change_password(
|
||||
|
||||
static struct wpabuf * eap_mschapv2_change_password(
|
||||
struct eap_sm *sm, struct eap_mschapv2_data *data,
|
||||
struct eap_method_ret *ret, const struct eap_mschapv2_hdr *req, u8 id)
|
||||
{
|
||||
@ -393,46 +501,67 @@ eap_mschapv2_change_password(
|
||||
EAP_CODE_RESPONSE, id);
|
||||
if (resp == NULL)
|
||||
return NULL;
|
||||
|
||||
ms = wpabuf_put(resp, sizeof(*ms));
|
||||
ms->op_code = MSCHAPV2_OP_CHANGE_PASSWORD;
|
||||
ms->mschapv2_id = req->mschapv2_id + 1;
|
||||
WPA_PUT_BE16(ms->ms_length, ms_len);
|
||||
cp = wpabuf_put(resp, sizeof(*cp));
|
||||
|
||||
/* Encrypted-Password */
|
||||
if (pwhash) {
|
||||
if (encrypt_pw_block_with_password_hash(
|
||||
new_password, new_password_len,
|
||||
password, cp->encr_password))
|
||||
new_password, new_password_len,
|
||||
password, cp->encr_password))
|
||||
goto fail;
|
||||
} else {
|
||||
if (new_password_encrypted_with_old_nt_password_hash(
|
||||
new_password, new_password_len,
|
||||
password, password_len, cp->encr_password))
|
||||
new_password, new_password_len,
|
||||
password, password_len, cp->encr_password))
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* Encrypted-Hash */
|
||||
if (pwhash) {
|
||||
u8 new_password_hash[16];
|
||||
nt_password_hash(new_password, new_password_len,
|
||||
new_password_hash);
|
||||
nt_password_hash_encrypted_with_block(password,
|
||||
new_password_hash,
|
||||
cp->encr_hash);
|
||||
if (nt_password_hash(new_password, new_password_len,
|
||||
new_password_hash) ||
|
||||
nt_password_hash_encrypted_with_block(password,
|
||||
new_password_hash,
|
||||
cp->encr_hash))
|
||||
goto fail;
|
||||
} else {
|
||||
old_nt_password_hash_encrypted_with_new_nt_password_hash(
|
||||
new_password, new_password_len,
|
||||
password, password_len, cp->encr_hash);
|
||||
if (old_nt_password_hash_encrypted_with_new_nt_password_hash(
|
||||
new_password, new_password_len,
|
||||
password, password_len, cp->encr_hash))
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* Peer-Challenge */
|
||||
if (random_get_bytes(cp->peer_challenge, MSCHAPV2_CHAL_LEN))
|
||||
goto fail;
|
||||
|
||||
/* Reserved, must be zero */
|
||||
os_memset(cp->reserved, 0, 8);
|
||||
|
||||
/* NT-Response */
|
||||
wpa_hexdump(MSG_DEBUG, "EAP-MSCHAPV2: auth_challenge",
|
||||
data->passwd_change_challenge, PASSWD_CHANGE_CHAL_LEN);
|
||||
wpa_hexdump(MSG_DEBUG, "EAP-MSCHAPV2: peer_challenge",
|
||||
cp->peer_challenge, MSCHAPV2_CHAL_LEN);
|
||||
wpa_hexdump_ascii(MSG_DEBUG, "EAP-MSCHAPV2: username",
|
||||
username, username_len);
|
||||
wpa_hexdump_ascii_key(MSG_DEBUG, "EAP-MSCHAPV2: new password",
|
||||
new_password, new_password_len);
|
||||
generate_nt_response(data->passwd_change_challenge, cp->peer_challenge,
|
||||
username, username_len, new_password,
|
||||
new_password_len, cp->nt_response);
|
||||
username, username_len,
|
||||
new_password, new_password_len,
|
||||
cp->nt_response);
|
||||
wpa_hexdump(MSG_DEBUG, "EAP-MSCHAPV2: NT-Response",
|
||||
cp->nt_response, MSCHAPV2_NT_RESPONSE_LEN);
|
||||
|
||||
/* Authenticator response is not really needed yet, but calculate it
|
||||
* here so that challenges need not be saved. */
|
||||
generate_authenticator_response(new_password, new_password_len,
|
||||
cp->peer_challenge,
|
||||
data->passwd_change_challenge,
|
||||
@ -440,13 +569,23 @@ eap_mschapv2_change_password(
|
||||
cp->nt_response, data->auth_response);
|
||||
data->auth_response_valid = 1;
|
||||
|
||||
nt_password_hash(new_password, new_password_len, password_hash);
|
||||
hash_nt_password_hash(password_hash, password_hash_hash);
|
||||
get_master_key(password_hash_hash, cp->nt_response, data->master_key);
|
||||
/* Likewise, generate master_key here since we have the needed data
|
||||
* available. */
|
||||
if (nt_password_hash(new_password, new_password_len, password_hash) ||
|
||||
hash_nt_password_hash(password_hash, password_hash_hash) ||
|
||||
get_master_key(password_hash_hash, cp->nt_response,
|
||||
data->master_key)) {
|
||||
data->auth_response_valid = 0;
|
||||
goto fail;
|
||||
}
|
||||
data->master_key_valid = 1;
|
||||
|
||||
/* Flags */
|
||||
os_memset(cp->flags, 0, 2);
|
||||
|
||||
wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: TX identifier %d mschapv2_id %d "
|
||||
"(change pw)", id, ms->mschapv2_id);
|
||||
|
||||
return resp;
|
||||
|
||||
fail:
|
||||
@ -454,19 +593,38 @@ fail:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static struct wpabuf *
|
||||
eap_mschapv2_failure(struct eap_sm *sm,
|
||||
struct eap_mschapv2_data *data,
|
||||
struct eap_method_ret *ret,
|
||||
const struct eap_mschapv2_hdr *req,
|
||||
size_t req_len, u8 id)
|
||||
|
||||
/**
|
||||
* eap_mschapv2_process - Process an EAP-MSCHAPv2 failure message
|
||||
* @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
|
||||
* @data: Pointer to private EAP method data from eap_mschapv2_init()
|
||||
* @ret: Return values from EAP request validation and processing
|
||||
* @req: Pointer to EAP-MSCHAPv2 header from the request
|
||||
* @req_len: Length of the EAP-MSCHAPv2 data
|
||||
* @id: EAP identifier used in th erequest
|
||||
* Returns: Pointer to allocated EAP response packet (eapRespData) or %NULL if
|
||||
* no reply available
|
||||
*/
|
||||
static struct wpabuf * eap_mschapv2_failure(struct eap_sm *sm,
|
||||
struct eap_mschapv2_data *data,
|
||||
struct eap_method_ret *ret,
|
||||
const struct eap_mschapv2_hdr *req,
|
||||
size_t req_len, u8 id)
|
||||
{
|
||||
struct wpabuf *resp;
|
||||
const u8 *msdata = (const u8 *)(req + 1);
|
||||
const u8 *msdata = (const u8 *) (req + 1);
|
||||
char *buf;
|
||||
size_t len = req_len - sizeof(*req);
|
||||
int retry = 0;
|
||||
|
||||
wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Received failure");
|
||||
wpa_hexdump_ascii(MSG_DEBUG, "EAP-MSCHAPV2: Failure data",
|
||||
msdata, len);
|
||||
/*
|
||||
* eap_mschapv2_failure_txt() expects a nul terminated string, so we
|
||||
* must allocate a large enough temporary buffer to create that since
|
||||
* the received message does not include nul termination.
|
||||
*/
|
||||
buf = (char *)dup_binstr(msdata, len);
|
||||
if (buf) {
|
||||
retry = eap_mschapv2_failure_txt(sm, data, buf);
|
||||
@ -482,23 +640,31 @@ eap_mschapv2_failure(struct eap_sm *sm,
|
||||
data->passwd_change_version == 3) {
|
||||
struct eap_peer_config *config = eap_get_config(sm);
|
||||
if (config && config->new_password)
|
||||
return eap_mschapv2_change_password(sm, data, ret,
|
||||
req, id);
|
||||
return eap_mschapv2_change_password(sm, data, ret, req,
|
||||
id);
|
||||
if (config && config->pending_req_new_password)
|
||||
return NULL;
|
||||
} else if (retry && data->prev_error == ERROR_AUTHENTICATION_FAILURE) {
|
||||
/* TODO: could try to retry authentication, e.g, after having
|
||||
* changed the username/password. In this case, EAP MS-CHAP-v2
|
||||
* Failure Response would not be sent here. */
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Note: Only op_code of the EAP-MSCHAPV2 header is included in failure
|
||||
* message. */
|
||||
resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, 1,
|
||||
EAP_CODE_RESPONSE, id);
|
||||
if (resp == NULL)
|
||||
return NULL;
|
||||
|
||||
wpabuf_put_u8(resp, MSCHAPV2_OP_FAILURE);
|
||||
wpabuf_put_u8(resp, MSCHAPV2_OP_FAILURE); /* op_code */
|
||||
|
||||
return resp;
|
||||
}
|
||||
|
||||
static int
|
||||
eap_mschapv2_check_config(struct eap_sm *sm)
|
||||
|
||||
static int eap_mschapv2_check_config(struct eap_sm *sm)
|
||||
{
|
||||
struct eap_peer_config *config = eap_get_config(sm);
|
||||
|
||||
@ -520,19 +686,24 @@ eap_mschapv2_check_config(struct eap_sm *sm)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
eap_mschapv2_check_mslen(struct eap_sm *sm, size_t len,
|
||||
const struct eap_mschapv2_hdr *ms)
|
||||
|
||||
static int eap_mschapv2_check_mslen(struct eap_sm *sm, size_t len,
|
||||
const struct eap_mschapv2_hdr *ms)
|
||||
{
|
||||
size_t ms_len = WPA_GET_BE16(ms->ms_length);
|
||||
|
||||
if (ms_len == len)
|
||||
return 0;
|
||||
|
||||
wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Invalid header: len=%lu "
|
||||
"ms_len=%lu", (unsigned long) len, (unsigned long) ms_len);
|
||||
if (sm->workaround) {
|
||||
wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Workaround, ignore Invalid"
|
||||
" header len=%lu ms_len=%lu\n",
|
||||
(unsigned long)len, (unsigned long)ms_len);
|
||||
/* Some authentication servers use invalid ms_len,
|
||||
* ignore it for interoperability. */
|
||||
wpa_printf(MSG_INFO, "EAP-MSCHAPV2: workaround, ignore"
|
||||
" invalid ms_len %lu (len %lu)",
|
||||
(unsigned long) ms_len,
|
||||
(unsigned long) len);
|
||||
return 0;
|
||||
}
|
||||
wpa_printf(MSG_ERROR, "EAP-MSCHAPV2: Invalid header len=%lu ms_len=%lu\n",
|
||||
@ -541,18 +712,31 @@ eap_mschapv2_check_mslen(struct eap_sm *sm, size_t len,
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void
|
||||
eap_mschapv2_copy_challenge(struct eap_mschapv2_data *data,
|
||||
const struct wpabuf *reqData)
|
||||
|
||||
static void eap_mschapv2_copy_challenge(struct eap_mschapv2_data *data,
|
||||
const struct wpabuf *reqData)
|
||||
{
|
||||
/*
|
||||
* Store a copy of the challenge message, so that it can be processed
|
||||
* again in case retry is allowed after a possible failure.
|
||||
*/
|
||||
wpabuf_free(data->prev_challenge);
|
||||
data->prev_challenge = wpabuf_dup(reqData);
|
||||
}
|
||||
|
||||
static struct wpabuf *
|
||||
eap_mschapv2_process(struct eap_sm *sm, void *priv,
|
||||
struct eap_method_ret *ret,
|
||||
const struct wpabuf *reqData)
|
||||
|
||||
/**
|
||||
* eap_mschapv2_process - Process an EAP-MSCHAPv2 request
|
||||
* @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
|
||||
* @priv: Pointer to private EAP method data from eap_mschapv2_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) or %NULL if
|
||||
* no reply available
|
||||
*/
|
||||
static struct wpabuf * eap_mschapv2_process(struct eap_sm *sm, void *priv,
|
||||
struct eap_method_ret *ret,
|
||||
const struct wpabuf *reqData)
|
||||
{
|
||||
u8 id;
|
||||
size_t len;
|
||||
@ -569,27 +753,31 @@ eap_mschapv2_process(struct eap_sm *sm, void *priv,
|
||||
|
||||
if (config->mschapv2_retry && data->prev_challenge &&
|
||||
data->prev_error == ERROR_AUTHENTICATION_FAILURE) {
|
||||
wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Replacing pending packet "
|
||||
"with the previous challenge");
|
||||
|
||||
reqData = data->prev_challenge;
|
||||
using_prev_challenge = 1;
|
||||
config->mschapv2_retry = 0;
|
||||
}
|
||||
|
||||
pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2,
|
||||
reqData, &len);
|
||||
pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, reqData,
|
||||
&len);
|
||||
if (pos == NULL || len < sizeof(*ms) + 1) {
|
||||
ret->ignore = true;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ms = (const struct eap_mschapv2_hdr *)pos;
|
||||
ms = (const struct eap_mschapv2_hdr *) pos;
|
||||
if (eap_mschapv2_check_mslen(sm, len, ms)) {
|
||||
ret->ignore = true;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
id = eap_get_id(reqData);
|
||||
wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: RX identifier %d mschapv2_id %d\n",
|
||||
id, ms->mschapv2_id);
|
||||
wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: RX identifier %d mschapv2_id %d",
|
||||
id, ms->mschapv2_id);
|
||||
|
||||
switch (ms->op_code) {
|
||||
case MSCHAPV2_OP_CHALLENGE:
|
||||
if (!using_prev_challenge)
|
||||
@ -602,19 +790,20 @@ eap_mschapv2_process(struct eap_sm *sm, void *priv,
|
||||
default:
|
||||
wpa_printf(MSG_ERROR, "EAP-MSCHAPV2: Unknown op code %d - ignored\n",
|
||||
ms->op_code);
|
||||
ret->ignore = TRUE;
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static bool
|
||||
eap_mschapv2_isKeyAvailable(struct eap_sm *sm, void *priv)
|
||||
|
||||
static bool eap_mschapv2_isKeyAvailable(struct eap_sm *sm, void *priv)
|
||||
{
|
||||
struct eap_mschapv2_data *data = priv;
|
||||
return data->success && data->master_key_valid;
|
||||
}
|
||||
|
||||
static u8 *
|
||||
eap_mschapv2_getKey(struct eap_sm *sm, void *priv, size_t *len)
|
||||
|
||||
static u8 * eap_mschapv2_getKey(struct eap_sm *sm, void *priv, size_t *len)
|
||||
{
|
||||
struct eap_mschapv2_data *data = priv;
|
||||
u8 *key;
|
||||
@ -626,20 +815,31 @@ eap_mschapv2_getKey(struct eap_sm *sm, void *priv, size_t *len)
|
||||
key_len = 2 * MSCHAPV2_KEY_LEN;
|
||||
|
||||
key = os_malloc(key_len);
|
||||
if (key == NULL)
|
||||
return NULL;
|
||||
|
||||
/* MSK = server MS-MPPE-Recv-Key | MS-MPPE-Send-Key,
|
||||
* peer MS-MPPE-Send-Key | MS-MPPE-Recv-Key */
|
||||
get_asymetric_start_key(data->master_key, key,
|
||||
MSCHAPV2_KEY_LEN, 1, 0);
|
||||
/* MSK = server MS-MPPE-Recv-Key | MS-MPPE-Send-Key, i.e.,
|
||||
* peer MS-MPPE-Send-Key | MS-MPPE-Recv-Key */
|
||||
get_asymetric_start_key(data->master_key, key, MSCHAPV2_KEY_LEN, 1, 0);
|
||||
get_asymetric_start_key(data->master_key, key + MSCHAPV2_KEY_LEN,
|
||||
MSCHAPV2_KEY_LEN, 0, 0);
|
||||
|
||||
wpa_hexdump_key(MSG_DEBUG, "EAP-MSCHAPV2: Derived key",
|
||||
key, key_len);
|
||||
|
||||
*len = key_len;
|
||||
return key;
|
||||
}
|
||||
|
||||
int
|
||||
eap_peer_mschapv2_register(void)
|
||||
|
||||
/**
|
||||
* eap_peer_mschapv2_register - Register EAP-MSCHAPv2 peer method
|
||||
* Returns: 0 on success, -1 on failure
|
||||
*
|
||||
* This function is used to register EAP-MSCHAPv2 peer method into the EAP
|
||||
* method list.
|
||||
*/
|
||||
int eap_peer_mschapv2_register(void)
|
||||
{
|
||||
struct eap_method *eap;
|
||||
int ret;
|
||||
|
@ -16,6 +16,10 @@
|
||||
#include "eap_peer/eap_config.h"
|
||||
#include "eap_peer/eap_methods.h"
|
||||
|
||||
|
||||
static void eap_tls_deinit(struct eap_sm *sm, void *priv);
|
||||
|
||||
|
||||
struct eap_tls_data {
|
||||
struct eap_ssl_data ssl;
|
||||
u8 *key_data;
|
||||
@ -26,19 +30,6 @@ struct eap_tls_data {
|
||||
};
|
||||
|
||||
|
||||
|
||||
static void eap_tls_deinit(struct eap_sm *sm, void *priv)
|
||||
{
|
||||
struct eap_tls_data *data = priv;
|
||||
if (data == NULL)
|
||||
return;
|
||||
eap_peer_tls_ssl_deinit(sm, &data->ssl);
|
||||
os_free(data->key_data);
|
||||
os_free(data->session_id);
|
||||
os_free(data);
|
||||
}
|
||||
|
||||
|
||||
static void * eap_tls_init(struct eap_sm *sm)
|
||||
{
|
||||
struct eap_tls_data *data;
|
||||
@ -66,6 +57,19 @@ static void * eap_tls_init(struct eap_sm *sm)
|
||||
return data;
|
||||
}
|
||||
|
||||
|
||||
static void eap_tls_deinit(struct eap_sm *sm, void *priv)
|
||||
{
|
||||
struct eap_tls_data *data = priv;
|
||||
if (data == NULL)
|
||||
return;
|
||||
eap_peer_tls_ssl_deinit(sm, &data->ssl);
|
||||
os_free(data->key_data);
|
||||
os_free(data->session_id);
|
||||
os_free(data);
|
||||
}
|
||||
|
||||
|
||||
static struct wpabuf * eap_tls_failure(struct eap_sm *sm,
|
||||
struct eap_tls_data *data,
|
||||
struct eap_method_ret *ret, int res,
|
||||
|
@ -107,6 +107,10 @@ static int eap_tls_params_from_conf(struct eap_sm *sm,
|
||||
|
||||
wpa_printf(MSG_DEBUG, "TLS: using phase1 config options");
|
||||
eap_tls_params_from_conf1(params, config);
|
||||
if (data->eap_type == EAP_TYPE_FAST) {
|
||||
wpa_printf(MSG_DEBUG, "EAP-TYPE == EAP-FAST #####################################");
|
||||
params->flags |= TLS_CONN_EAP_FAST;
|
||||
}
|
||||
|
||||
/*
|
||||
* Use blob data, if available. Otherwise, leave reference to external
|
||||
@ -259,7 +263,7 @@ u8 * eap_peer_tls_derive_key(struct eap_sm *sm, struct eap_ssl_data *data,
|
||||
if (out == NULL)
|
||||
return NULL;
|
||||
|
||||
if (tls_connection_export_key(data->ssl_ctx, data->conn, label, out,
|
||||
if (tls_connection_export_key(data->ssl_ctx, data->conn, label, 0, 0, out,
|
||||
len)) {
|
||||
os_free(out);
|
||||
return NULL;
|
||||
|
@ -14,10 +14,11 @@ const u8 * mschapv2_remove_domain(const u8 *username, size_t *len)
|
||||
size_t i;
|
||||
|
||||
/*
|
||||
* MSCHAPV2 does not include optional domain name in the
|
||||
* MSCHAPv2 does not include optional domain name in the
|
||||
* challenge-response calculation, so remove domain prefix
|
||||
* (if present)
|
||||
* (if present).
|
||||
*/
|
||||
|
||||
for (i = 0; i < *len; i++) {
|
||||
if (username[i] == '\\') {
|
||||
*len -= i + 1;
|
||||
@ -28,31 +29,48 @@ const u8 * mschapv2_remove_domain(const u8 *username, size_t *len)
|
||||
return username;
|
||||
}
|
||||
|
||||
|
||||
int mschapv2_derive_response(const u8 *identity, size_t identity_len,
|
||||
const u8 *password, size_t password_len,
|
||||
int pwhash,
|
||||
const u8 *auth_challenge,
|
||||
const u8 *peer_challenge,
|
||||
u8 *nt_response, u8 *auth_response,
|
||||
u8 *master_key)
|
||||
const u8 *password, size_t password_len,
|
||||
int pwhash,
|
||||
const u8 *auth_challenge,
|
||||
const u8 *peer_challenge,
|
||||
u8 *nt_response, u8 *auth_response,
|
||||
u8 *master_key)
|
||||
{
|
||||
const u8 *username;
|
||||
size_t username_len;
|
||||
u8 password_hash[16], password_hash_hash[16];
|
||||
|
||||
wpa_hexdump_ascii(MSG_DEBUG, "MSCHAPV2: Identity",
|
||||
identity, identity_len);
|
||||
username_len = identity_len;
|
||||
username = mschapv2_remove_domain(identity, &username_len);
|
||||
wpa_hexdump_ascii(MSG_DEBUG, "MSCHAPV2: Username",
|
||||
username, username_len);
|
||||
|
||||
wpa_hexdump(MSG_DEBUG, "MSCHAPV2: auth_challenge",
|
||||
auth_challenge, MSCHAPV2_CHAL_LEN);
|
||||
wpa_hexdump(MSG_DEBUG, "MSCHAPV2: peer_challenge",
|
||||
peer_challenge, MSCHAPV2_CHAL_LEN);
|
||||
wpa_hexdump_ascii(MSG_DEBUG, "MSCHAPV2: username",
|
||||
username, username_len);
|
||||
/* Authenticator response is not really needed yet, but calculate it
|
||||
* here so that challenges need not be saved. */
|
||||
if (pwhash) {
|
||||
wpa_hexdump_key(MSG_DEBUG, "MSCHAPV2: password hash",
|
||||
password, password_len);
|
||||
if (generate_nt_response_pwhash(auth_challenge, peer_challenge,
|
||||
username, username_len,
|
||||
password, nt_response) ||
|
||||
generate_authenticator_response_pwhash(
|
||||
password, peer_challenge, auth_challenge,
|
||||
username, username_len, nt_response,
|
||||
auth_response))
|
||||
password, peer_challenge, auth_challenge,
|
||||
username, username_len, nt_response,
|
||||
auth_response))
|
||||
return -1;
|
||||
} else {
|
||||
wpa_hexdump_ascii_key(MSG_DEBUG, "MSCHAPV2: password",
|
||||
password, password_len);
|
||||
if (generate_nt_response(auth_challenge, peer_challenge,
|
||||
username, username_len,
|
||||
password, password_len,
|
||||
@ -65,7 +83,12 @@ int mschapv2_derive_response(const u8 *identity, size_t identity_len,
|
||||
auth_response))
|
||||
return -1;
|
||||
}
|
||||
wpa_hexdump(MSG_DEBUG, "MSCHAPV2: NT Response",
|
||||
nt_response, MSCHAPV2_NT_RESPONSE_LEN);
|
||||
wpa_hexdump(MSG_DEBUG, "MSCHAPV2: Auth Response",
|
||||
auth_response, MSCHAPV2_AUTH_RESPONSE_LEN);
|
||||
|
||||
/* Generate master_key here since we have the needed data available. */
|
||||
if (pwhash) {
|
||||
if (hash_nt_password_hash(password, password_hash_hash))
|
||||
return -1;
|
||||
@ -76,17 +99,20 @@ int mschapv2_derive_response(const u8 *identity, size_t identity_len,
|
||||
}
|
||||
if (get_master_key(password_hash_hash, nt_response, master_key))
|
||||
return -1;
|
||||
wpa_hexdump_key(MSG_DEBUG, "MSCHAPV2: Master Key",
|
||||
master_key, MSCHAPV2_MASTER_KEY_LEN);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int mschapv2_verify_auth_response(const u8 *auth_response,
|
||||
const u8 *buf, size_t buf_len)
|
||||
const u8 *buf, size_t buf_len)
|
||||
{
|
||||
u8 recv_response[MSCHAPV2_AUTH_RESPONSE_LEN];
|
||||
if (buf_len < 2 + 2 * MSCHAPV2_AUTH_RESPONSE_LEN ||
|
||||
buf[0] != 'S' || buf[1] != '=' ||
|
||||
hexstr2bin((char *)(buf + 2), recv_response,
|
||||
hexstr2bin((char *) (buf + 2), recv_response,
|
||||
MSCHAPV2_AUTH_RESPONSE_LEN) ||
|
||||
os_memcmp(auth_response, recv_response,
|
||||
MSCHAPV2_AUTH_RESPONSE_LEN) != 0)
|
||||
|
@ -1,4 +1,3 @@
|
||||
|
||||
/*
|
||||
* WPA Supplicant - WPA state machine and EAPOL-Key processing
|
||||
* Copyright (c) 2003-2010, Jouni Malinen <j@w1.fi>
|
||||
@ -12,6 +11,7 @@
|
||||
*
|
||||
* See README and COPYING for more details.
|
||||
*/
|
||||
|
||||
#include "utils/includes.h"
|
||||
|
||||
#include "utils/common.h"
|
||||
|
@ -83,6 +83,7 @@ struct tls_config {
|
||||
#define TLS_CONN_REQUEST_OCSP BIT(3)
|
||||
#define TLS_CONN_REQUIRE_OCSP BIT(4)
|
||||
#define TLS_CONN_SUITEB BIT(11)
|
||||
#define TLS_CONN_EAP_FAST BIT(7)
|
||||
|
||||
/**
|
||||
* struct tls_connection_params - Parameters for TLS connection
|
||||
@ -279,17 +280,23 @@ int __must_check tls_global_set_verify(void *tls_ctx, int check_crl);
|
||||
* @tls_ctx: TLS context data from tls_init()
|
||||
* @conn: Connection context data from tls_connection_init()
|
||||
* @verify_peer: 1 = verify peer certificate
|
||||
* @flags: Connection flags (TLS_CONN_*)
|
||||
* @session_ctx: Session caching context or %NULL to use default
|
||||
* @session_ctx_len: Length of @session_ctx in bytes.
|
||||
* Returns: 0 on success, -1 on failure
|
||||
*/
|
||||
int __must_check tls_connection_set_verify(void *tls_ctx,
|
||||
int tls_connection_set_verify(void *tls_ctx,
|
||||
struct tls_connection *conn,
|
||||
int verify_peer);
|
||||
int verify_peer,
|
||||
unsigned int flags,
|
||||
const u8 *session_ctx,
|
||||
size_t session_ctx_len);
|
||||
|
||||
/**
|
||||
* tls_connection_get_random - Get random data from TLS connection
|
||||
* @tls_ctx: TLS context data from tls_init()
|
||||
* @conn: Connection context data from tls_connection_init()
|
||||
* @keys: Structure of key/random data (filled on success)
|
||||
* @data: Structure of client/server random data (filled on success)
|
||||
* Returns: 0 on success, -1 on failure
|
||||
*/
|
||||
int __must_check tls_connection_get_random(void *tls_ctx,
|
||||
@ -301,17 +308,39 @@ int __must_check tls_connection_get_random(void *tls_ctx,
|
||||
* @tls_ctx: TLS context data from tls_init()
|
||||
* @conn: Connection context data from tls_connection_init()
|
||||
* @label: Label (e.g., description of the key) for PRF
|
||||
* @context: Optional extra upper-layer context (max len 2^16)
|
||||
* @context_len: The length of the context value
|
||||
* @out: Buffer for output data from TLS-PRF
|
||||
* @out_len: Length of the output buffer
|
||||
* Returns: 0 on success, -1 on failure
|
||||
*
|
||||
* Exports keying material using the mechanism described in RFC 5705.
|
||||
* Exports keying material using the mechanism described in RFC 5705. If
|
||||
* context is %NULL, context is not provided; otherwise, context is provided
|
||||
* (including the case of empty context with context_len == 0).
|
||||
*/
|
||||
int __must_check tls_connection_export_key(void *tls_ctx,
|
||||
struct tls_connection *conn,
|
||||
const char *label,
|
||||
const u8 *context,
|
||||
size_t context_len,
|
||||
u8 *out, size_t out_len);
|
||||
|
||||
/**
|
||||
* tls_connection_get_eap_fast_key - Derive key material for EAP-FAST
|
||||
* @tls_ctx: TLS context data from tls_init()
|
||||
* @conn: Connection context data from tls_connection_init()
|
||||
* @out: Buffer for output data from TLS-PRF
|
||||
* @out_len: Length of the output buffer
|
||||
* Returns: 0 on success, -1 on failure
|
||||
*
|
||||
* Exports key material after the normal TLS key block for use with
|
||||
* EAP-FAST. Most callers will want tls_connection_export_key(), but EAP-FAST
|
||||
* uses a different legacy mechanism.
|
||||
*/
|
||||
int __must_check tls_connection_get_eap_fast_key(void *tls_ctx,
|
||||
struct tls_connection *conn,
|
||||
u8 *out, size_t out_len);
|
||||
|
||||
/**
|
||||
* tls_connection_handshake - Process TLS handshake (client side)
|
||||
* @tls_ctx: TLS context data from tls_init()
|
||||
@ -413,7 +442,9 @@ enum {
|
||||
TLS_CIPHER_RC4_SHA /* 0x0005 */,
|
||||
TLS_CIPHER_AES128_SHA /* 0x002f */,
|
||||
TLS_CIPHER_RSA_DHE_AES128_SHA /* 0x0031 */,
|
||||
TLS_CIPHER_ANON_DH_AES128_SHA /* 0x0034 */
|
||||
TLS_CIPHER_ANON_DH_AES128_SHA /* 0x0034 */,
|
||||
TLS_CIPHER_RSA_DHE_AES256_SHA /* 0x0039 */,
|
||||
TLS_CIPHER_AES256_SHA /* 0x0035 */,
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -267,7 +267,8 @@ int tls_global_set_verify(void *tls_ctx, int check_crl)
|
||||
|
||||
|
||||
int tls_connection_set_verify(void *tls_ctx, struct tls_connection *conn,
|
||||
int verify_peer)
|
||||
int verify_peer, unsigned int flags,
|
||||
const u8 *session_ctx, size_t session_ctx_len)
|
||||
{
|
||||
#ifdef CONFIG_TLS_INTERNAL_SERVER
|
||||
if (conn->server)
|
||||
@ -276,6 +277,7 @@ int tls_connection_set_verify(void *tls_ctx, struct tls_connection *conn,
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
int tls_connection_get_random(void *tls_ctx, struct tls_connection *conn,
|
||||
struct tls_random *data)
|
||||
{
|
||||
@ -290,6 +292,7 @@ int tls_connection_get_random(void *tls_ctx, struct tls_connection *conn,
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
static int tls_get_keyblock_size(struct tls_connection *conn)
|
||||
{
|
||||
#ifdef CONFIG_TLS_INTERNAL_CLIENT
|
||||
@ -303,8 +306,10 @@ static int tls_get_keyblock_size(struct tls_connection *conn)
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
static int tls_connection_prf(void *tls_ctx, struct tls_connection *conn,
|
||||
const char *label, int server_random_first,
|
||||
const char *label, const u8 *context,
|
||||
size_t context_len, int server_random_first,
|
||||
int skip_keyblock, u8 *out, size_t out_len)
|
||||
{
|
||||
int ret = -1, skip = 0;
|
||||
@ -320,33 +325,46 @@ static int tls_connection_prf(void *tls_ctx, struct tls_connection *conn,
|
||||
return -1;
|
||||
_out = tmp_out;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_TLS_INTERNAL_CLIENT
|
||||
if (conn->client) {
|
||||
ret = tlsv1_client_prf(conn->client, label,
|
||||
server_random_first,
|
||||
out, out_len);
|
||||
server_random_first,
|
||||
_out, skip + out_len);
|
||||
}
|
||||
#endif /* CONFIG_TLS_INTERNAL_CLIENT */
|
||||
#ifdef CONFIG_TLS_INTERNAL_SERVER
|
||||
if (conn->server) {
|
||||
ret = tlsv1_server_prf(conn->server, label,
|
||||
server_random_first,
|
||||
out, out_len);
|
||||
server_random_first,
|
||||
_out, skip + out_len);
|
||||
}
|
||||
#endif /* CONFIG_TLS_INTERNAL_SERVER */
|
||||
if (ret == 0 && skip_keyblock)
|
||||
os_memcpy(out, _out + skip, out_len);
|
||||
wpa_bin_clear_free(tmp_out, skip);
|
||||
bin_clear_free(tmp_out, skip);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
int tls_connection_export_key(void *tls_ctx, struct tls_connection *conn,
|
||||
const char *label, u8 *out, size_t out_len)
|
||||
const char *label, const u8 *context,
|
||||
size_t context_len, u8 *out, size_t out_len)
|
||||
{
|
||||
return tls_connection_prf(tls_ctx, conn, label, 0, 0, out, out_len);
|
||||
return tls_connection_prf(tls_ctx, conn, label, context, context_len,
|
||||
0, 0, out, out_len);
|
||||
}
|
||||
|
||||
|
||||
int tls_connection_get_eap_fast_key(void *tls_ctx, struct tls_connection *conn,
|
||||
u8 *out, size_t out_len)
|
||||
{
|
||||
return tls_connection_prf(tls_ctx, conn, "key expansion", NULL, 0,
|
||||
1, 1, out, out_len);
|
||||
}
|
||||
|
||||
|
||||
struct wpabuf * tls_connection_handshake(void *tls_ctx,
|
||||
struct tls_connection *conn,
|
||||
const struct wpabuf *in_data,
|
||||
|
@ -207,6 +207,15 @@ void wpabuf_free(struct wpabuf *buf)
|
||||
}
|
||||
|
||||
|
||||
void wpabuf_clear_free(struct wpabuf *buf)
|
||||
{
|
||||
if (buf) {
|
||||
os_memset(wpabuf_mhead(buf), 0, wpabuf_len(buf));
|
||||
wpabuf_free(buf);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void * wpabuf_put(struct wpabuf *buf, size_t len)
|
||||
{
|
||||
void *tmp = wpabuf_mhead_u8(buf) + wpabuf_len(buf);
|
||||
|
Loading…
Reference in New Issue
Block a user