esp_wifi: Add support for EAP-FAST authentication method

This commit is contained in:
Hrudaynath Dhabe 2021-08-30 15:42:32 +05:30
parent 954d52ff3a
commit e21832cabb
26 changed files with 3934 additions and 137 deletions

View File

@ -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

View File

@ -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

View File

@ -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;
}

View File

@ -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);

View File

@ -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)
{

View File

@ -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;

View 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;
}

View File

@ -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) {

View File

@ -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()

View File

@ -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 */

View File

@ -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;
};

File diff suppressed because it is too large Load Diff

View 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;
}

View 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 */

View 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;
}

View 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 */

View File

@ -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);

View File

@ -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);

View File

@ -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;

View File

@ -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,

View File

@ -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;

View File

@ -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)

View File

@ -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"

View File

@ -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 */,
};
/**

View File

@ -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,

View File

@ -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);