rtl8188eu: Add hostapd and configurationb file

Signed-off-by: Larry Finger <Larry.Finger@lwfinger.net>
This commit is contained in:
Larry Finger 2013-11-27 14:23:24 -06:00
parent 7638be5ee7
commit e275d5ba1f
477 changed files with 220879 additions and 0 deletions

View file

@ -0,0 +1,8 @@
all:
@echo Nothing to be made.
clean:
rm -f *~ *.o *.d
install:
@echo Nothing to be made.

View file

@ -0,0 +1,128 @@
/*
* hostapd / EAP Full Authenticator state machine (RFC 4137)
* Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* Alternatively, this software may be distributed under the terms of BSD
* license.
*
* See README and COPYING for more details.
*/
#ifndef EAP_H
#define EAP_H
#include "common/defs.h"
#include "eap_common/eap_defs.h"
#include "eap_server/eap_methods.h"
#include "wpabuf.h"
struct eap_sm;
#define EAP_MAX_METHODS 8
#define EAP_TTLS_AUTH_PAP 1
#define EAP_TTLS_AUTH_CHAP 2
#define EAP_TTLS_AUTH_MSCHAP 4
#define EAP_TTLS_AUTH_MSCHAPV2 8
struct eap_user {
struct {
int vendor;
u32 method;
} methods[EAP_MAX_METHODS];
u8 *password;
size_t password_len;
int password_hash; /* whether password is hashed with
* nt_password_hash() */
int phase2;
int force_version;
int ttls_auth; /* bitfield of
* EAP_TTLS_AUTH_{PAP,CHAP,MSCHAP,MSCHAPV2} */
};
struct eap_eapol_interface {
/* Lower layer to full authenticator variables */
Boolean eapResp; /* shared with EAPOL Backend Authentication */
struct wpabuf *eapRespData;
Boolean portEnabled;
int retransWhile;
Boolean eapRestart; /* shared with EAPOL Authenticator PAE */
int eapSRTT;
int eapRTTVAR;
/* Full authenticator to lower layer variables */
Boolean eapReq; /* shared with EAPOL Backend Authentication */
Boolean eapNoReq; /* shared with EAPOL Backend Authentication */
Boolean eapSuccess;
Boolean eapFail;
Boolean eapTimeout;
struct wpabuf *eapReqData;
u8 *eapKeyData;
size_t eapKeyDataLen;
Boolean eapKeyAvailable; /* called keyAvailable in IEEE 802.1X-2004 */
/* AAA interface to full authenticator variables */
Boolean aaaEapReq;
Boolean aaaEapNoReq;
Boolean aaaSuccess;
Boolean aaaFail;
struct wpabuf *aaaEapReqData;
u8 *aaaEapKeyData;
size_t aaaEapKeyDataLen;
Boolean aaaEapKeyAvailable;
int aaaMethodTimeout;
/* Full authenticator to AAA interface variables */
Boolean aaaEapResp;
struct wpabuf *aaaEapRespData;
/* aaaIdentity -> eap_get_identity() */
Boolean aaaTimeout;
};
struct eapol_callbacks {
int (*get_eap_user)(void *ctx, const u8 *identity, size_t identity_len,
int phase2, struct eap_user *user);
const char * (*get_eap_req_id_text)(void *ctx, size_t *len);
};
struct eap_config {
void *ssl_ctx;
void *msg_ctx;
void *eap_sim_db_priv;
Boolean backend_auth;
int eap_server;
u16 pwd_group;
u8 *pac_opaque_encr_key;
u8 *eap_fast_a_id;
size_t eap_fast_a_id_len;
char *eap_fast_a_id_info;
int eap_fast_prov;
int pac_key_lifetime;
int pac_key_refresh_time;
int eap_sim_aka_result_ind;
int tnc;
struct wps_context *wps;
const struct wpabuf *assoc_wps_ie;
const struct wpabuf *assoc_p2p_ie;
const u8 *peer_addr;
int fragment_size;
};
struct eap_sm * eap_server_sm_init(void *eapol_ctx,
struct eapol_callbacks *eapol_cb,
struct eap_config *eap_conf);
void eap_server_sm_deinit(struct eap_sm *sm);
int eap_server_sm_step(struct eap_sm *sm);
void eap_sm_notify_cached(struct eap_sm *sm);
void eap_sm_pending_cb(struct eap_sm *sm);
int eap_sm_method_pending(struct eap_sm *sm);
const u8 * eap_get_identity(struct eap_sm *sm, size_t *len);
struct eap_eapol_interface * eap_get_interface(struct eap_sm *sm);
void eap_server_clear_identity(struct eap_sm *sm);
#endif /* EAP_H */

View file

@ -0,0 +1,201 @@
/*
* hostapd / EAP Authenticator state machine internal structures (RFC 4137)
* Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* Alternatively, this software may be distributed under the terms of BSD
* license.
*
* See README and COPYING for more details.
*/
#ifndef EAP_I_H
#define EAP_I_H
#include "wpabuf.h"
#include "eap_server/eap.h"
#include "eap_common/eap_common.h"
/* RFC 4137 - EAP Standalone Authenticator */
/**
* struct eap_method - EAP method interface
* This structure defines the EAP method interface. Each method will need to
* register its own EAP type, EAP name, and set of function pointers for method
* specific operations. This interface is based on section 5.4 of RFC 4137.
*/
struct eap_method {
int vendor;
EapType method;
const char *name;
void * (*init)(struct eap_sm *sm);
void * (*initPickUp)(struct eap_sm *sm);
void (*reset)(struct eap_sm *sm, void *priv);
struct wpabuf * (*buildReq)(struct eap_sm *sm, void *priv, u8 id);
int (*getTimeout)(struct eap_sm *sm, void *priv);
Boolean (*check)(struct eap_sm *sm, void *priv,
struct wpabuf *respData);
void (*process)(struct eap_sm *sm, void *priv,
struct wpabuf *respData);
Boolean (*isDone)(struct eap_sm *sm, void *priv);
u8 * (*getKey)(struct eap_sm *sm, void *priv, size_t *len);
/* isSuccess is not specified in draft-ietf-eap-statemachine-05.txt,
* but it is useful in implementing Policy.getDecision() */
Boolean (*isSuccess)(struct eap_sm *sm, void *priv);
/**
* free - Free EAP method data
* @method: Pointer to the method data registered with
* eap_server_method_register().
*
* This function will be called when the EAP method is being
* unregistered. If the EAP method allocated resources during
* registration (e.g., allocated struct eap_method), they should be
* freed in this function. No other method functions will be called
* after this call. If this function is not defined (i.e., function
* pointer is %NULL), a default handler is used to release the method
* data with free(method). This is suitable for most cases.
*/
void (*free)(struct eap_method *method);
#define EAP_SERVER_METHOD_INTERFACE_VERSION 1
/**
* version - Version of the EAP server method interface
*
* The EAP server method implementation should set this variable to
* EAP_SERVER_METHOD_INTERFACE_VERSION. This is used to verify that the
* EAP method is using supported API version when using dynamically
* loadable EAP methods.
*/
int version;
/**
* next - Pointer to the next EAP method
*
* This variable is used internally in the EAP method registration code
* to create a linked list of registered EAP methods.
*/
struct eap_method *next;
/**
* get_emsk - Get EAP method specific keying extended material (EMSK)
* @sm: Pointer to EAP state machine allocated with eap_sm_init()
* @priv: Pointer to private EAP method data from eap_method::init()
* @len: Pointer to a variable to store EMSK length
* Returns: EMSK or %NULL if not available
*
* This function can be used to get the extended keying material from
* the EAP method. The key may already be stored in the method-specific
* private data or this function may derive the key.
*/
u8 * (*get_emsk)(struct eap_sm *sm, void *priv, size_t *len);
};
/**
* struct eap_sm - EAP server state machine data
*/
struct eap_sm {
enum {
EAP_DISABLED, EAP_INITIALIZE, EAP_IDLE, EAP_RECEIVED,
EAP_INTEGRITY_CHECK, EAP_METHOD_RESPONSE, EAP_METHOD_REQUEST,
EAP_PROPOSE_METHOD, EAP_SELECT_ACTION, EAP_SEND_REQUEST,
EAP_DISCARD, EAP_NAK, EAP_RETRANSMIT, EAP_SUCCESS, EAP_FAILURE,
EAP_TIMEOUT_FAILURE, EAP_PICK_UP_METHOD,
EAP_INITIALIZE_PASSTHROUGH, EAP_IDLE2, EAP_RETRANSMIT2,
EAP_RECEIVED2, EAP_DISCARD2, EAP_SEND_REQUEST2,
EAP_AAA_REQUEST, EAP_AAA_RESPONSE, EAP_AAA_IDLE,
EAP_TIMEOUT_FAILURE2, EAP_FAILURE2, EAP_SUCCESS2
} EAP_state;
/* Constants */
int MaxRetrans;
struct eap_eapol_interface eap_if;
/* Full authenticator state machine local variables */
/* Long-term (maintained betwen packets) */
EapType currentMethod;
int currentId;
enum {
METHOD_PROPOSED, METHOD_CONTINUE, METHOD_END
} methodState;
int retransCount;
struct wpabuf *lastReqData;
int methodTimeout;
/* Short-term (not maintained between packets) */
Boolean rxResp;
int respId;
EapType respMethod;
int respVendor;
u32 respVendorMethod;
Boolean ignore;
enum {
DECISION_SUCCESS, DECISION_FAILURE, DECISION_CONTINUE,
DECISION_PASSTHROUGH
} decision;
/* Miscellaneous variables */
const struct eap_method *m; /* selected EAP method */
/* not defined in RFC 4137 */
Boolean changed;
void *eapol_ctx, *msg_ctx;
struct eapol_callbacks *eapol_cb;
void *eap_method_priv;
u8 *identity;
size_t identity_len;
/* Whether Phase 2 method should validate identity match */
int require_identity_match;
int lastId; /* Identifier used in the last EAP-Packet */
struct eap_user *user;
int user_eap_method_index;
int init_phase2;
void *ssl_ctx;
void *eap_sim_db_priv;
Boolean backend_auth;
Boolean update_user;
int eap_server;
int num_rounds;
enum {
METHOD_PENDING_NONE, METHOD_PENDING_WAIT, METHOD_PENDING_CONT
} method_pending;
u8 *auth_challenge;
u8 *peer_challenge;
u8 *pac_opaque_encr_key;
u8 *eap_fast_a_id;
size_t eap_fast_a_id_len;
char *eap_fast_a_id_info;
enum {
NO_PROV, ANON_PROV, AUTH_PROV, BOTH_PROV
} eap_fast_prov;
int pac_key_lifetime;
int pac_key_refresh_time;
int eap_sim_aka_result_ind;
int tnc;
u16 pwd_group;
struct wps_context *wps;
struct wpabuf *assoc_wps_ie;
struct wpabuf *assoc_p2p_ie;
Boolean start_reauth;
u8 peer_addr[ETH_ALEN];
/* Fragmentation size for EAP method init() handler */
int fragment_size;
};
int eap_user_get(struct eap_sm *sm, const u8 *identity, size_t identity_len,
int phase2);
void eap_sm_process_nak(struct eap_sm *sm, const u8 *nak_list, size_t len);
#endif /* EAP_I_H */

View file

@ -0,0 +1,54 @@
/*
* EAP server method registration
* Copyright (c) 2004-2009, Jouni Malinen <j@w1.fi>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* Alternatively, this software may be distributed under the terms of BSD
* license.
*
* See README and COPYING for more details.
*/
#ifndef EAP_SERVER_METHODS_H
#define EAP_SERVER_METHODS_H
#include "eap_common/eap_defs.h"
const struct eap_method * eap_server_get_eap_method(int vendor,
EapType method);
struct eap_method * eap_server_method_alloc(int version, int vendor,
EapType method, const char *name);
void eap_server_method_free(struct eap_method *method);
int eap_server_method_register(struct eap_method *method);
EapType eap_server_get_type(const char *name, int *vendor);
void eap_server_unregister_methods(void);
const char * eap_server_get_name(int vendor, EapType type);
/* EAP server method registration calls for statically linked in methods */
int eap_server_identity_register(void);
int eap_server_md5_register(void);
int eap_server_tls_register(void);
int eap_server_mschapv2_register(void);
int eap_server_peap_register(void);
int eap_server_tlv_register(void);
int eap_server_gtc_register(void);
int eap_server_ttls_register(void);
int eap_server_sim_register(void);
int eap_server_aka_register(void);
int eap_server_aka_prime_register(void);
int eap_server_pax_register(void);
int eap_server_psk_register(void);
int eap_server_sake_register(void);
int eap_server_gpsk_register(void);
int eap_server_vendor_test_register(void);
int eap_server_fast_register(void);
int eap_server_wsc_register(void);
int eap_server_ikev2_register(void);
int eap_server_tnc_register(void);
int eap_server_pwd_register(void);
#endif /* EAP_SERVER_METHODS_H */

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,634 @@
/*
* hostapd / EAP-GPSK (RFC 5433) server
* Copyright (c) 2006-2007, Jouni Malinen <j@w1.fi>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* Alternatively, this software may be distributed under the terms of BSD
* license.
*
* See README and COPYING for more details.
*/
#include "includes.h"
#include "common.h"
#include "crypto/random.h"
#include "eap_server/eap_i.h"
#include "eap_common/eap_gpsk_common.h"
struct eap_gpsk_data {
enum { GPSK_1, GPSK_3, SUCCESS, FAILURE } state;
u8 rand_server[EAP_GPSK_RAND_LEN];
u8 rand_peer[EAP_GPSK_RAND_LEN];
u8 msk[EAP_MSK_LEN];
u8 emsk[EAP_EMSK_LEN];
u8 sk[EAP_GPSK_MAX_SK_LEN];
size_t sk_len;
u8 pk[EAP_GPSK_MAX_PK_LEN];
size_t pk_len;
u8 *id_peer;
size_t id_peer_len;
u8 *id_server;
size_t id_server_len;
#define MAX_NUM_CSUITES 2
struct eap_gpsk_csuite csuite_list[MAX_NUM_CSUITES];
size_t csuite_count;
int vendor; /* CSuite/Vendor */
int specifier; /* CSuite/Specifier */
};
static const char * eap_gpsk_state_txt(int state)
{
switch (state) {
case GPSK_1:
return "GPSK-1";
case GPSK_3:
return "GPSK-3";
case SUCCESS:
return "SUCCESS";
case FAILURE:
return "FAILURE";
default:
return "?";
}
}
static void eap_gpsk_state(struct eap_gpsk_data *data, int state)
{
wpa_printf(MSG_DEBUG, "EAP-GPSK: %s -> %s",
eap_gpsk_state_txt(data->state),
eap_gpsk_state_txt(state));
data->state = state;
}
static void * eap_gpsk_init(struct eap_sm *sm)
{
struct eap_gpsk_data *data;
data = os_zalloc(sizeof(*data));
if (data == NULL)
return NULL;
data->state = GPSK_1;
/* TODO: add support for configuring ID_Server */
data->id_server = (u8 *) os_strdup("hostapd");
if (data->id_server)
data->id_server_len = os_strlen((char *) data->id_server);
data->csuite_count = 0;
if (eap_gpsk_supported_ciphersuite(EAP_GPSK_VENDOR_IETF,
EAP_GPSK_CIPHER_AES)) {
WPA_PUT_BE32(data->csuite_list[data->csuite_count].vendor,
EAP_GPSK_VENDOR_IETF);
WPA_PUT_BE16(data->csuite_list[data->csuite_count].specifier,
EAP_GPSK_CIPHER_AES);
data->csuite_count++;
}
if (eap_gpsk_supported_ciphersuite(EAP_GPSK_VENDOR_IETF,
EAP_GPSK_CIPHER_SHA256)) {
WPA_PUT_BE32(data->csuite_list[data->csuite_count].vendor,
EAP_GPSK_VENDOR_IETF);
WPA_PUT_BE16(data->csuite_list[data->csuite_count].specifier,
EAP_GPSK_CIPHER_SHA256);
data->csuite_count++;
}
return data;
}
static void eap_gpsk_reset(struct eap_sm *sm, void *priv)
{
struct eap_gpsk_data *data = priv;
os_free(data->id_server);
os_free(data->id_peer);
os_free(data);
}
static struct wpabuf * eap_gpsk_build_gpsk_1(struct eap_sm *sm,
struct eap_gpsk_data *data, u8 id)
{
size_t len;
struct wpabuf *req;
wpa_printf(MSG_DEBUG, "EAP-GPSK: Request/GPSK-1");
if (random_get_bytes(data->rand_server, EAP_GPSK_RAND_LEN)) {
wpa_printf(MSG_ERROR, "EAP-GPSK: Failed to get random data");
eap_gpsk_state(data, FAILURE);
return NULL;
}
wpa_hexdump(MSG_MSGDUMP, "EAP-GPSK: RAND_Server",
data->rand_server, EAP_GPSK_RAND_LEN);
len = 1 + 2 + data->id_server_len + EAP_GPSK_RAND_LEN + 2 +
data->csuite_count * sizeof(struct eap_gpsk_csuite);
req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_GPSK, len,
EAP_CODE_REQUEST, id);
if (req == NULL) {
wpa_printf(MSG_ERROR, "EAP-GPSK: Failed to allocate memory "
"for request/GPSK-1");
eap_gpsk_state(data, FAILURE);
return NULL;
}
wpabuf_put_u8(req, EAP_GPSK_OPCODE_GPSK_1);
wpabuf_put_be16(req, data->id_server_len);
wpabuf_put_data(req, data->id_server, data->id_server_len);
wpabuf_put_data(req, data->rand_server, EAP_GPSK_RAND_LEN);
wpabuf_put_be16(req,
data->csuite_count * sizeof(struct eap_gpsk_csuite));
wpabuf_put_data(req, data->csuite_list,
data->csuite_count * sizeof(struct eap_gpsk_csuite));
return req;
}
static struct wpabuf * eap_gpsk_build_gpsk_3(struct eap_sm *sm,
struct eap_gpsk_data *data, u8 id)
{
u8 *pos, *start;
size_t len, miclen;
struct eap_gpsk_csuite *csuite;
struct wpabuf *req;
wpa_printf(MSG_DEBUG, "EAP-GPSK: Request/GPSK-3");
miclen = eap_gpsk_mic_len(data->vendor, data->specifier);
len = 1 + 2 * EAP_GPSK_RAND_LEN + 2 + data->id_server_len +
sizeof(struct eap_gpsk_csuite) + 2 + miclen;
req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_GPSK, len,
EAP_CODE_REQUEST, id);
if (req == NULL) {
wpa_printf(MSG_ERROR, "EAP-GPSK: Failed to allocate memory "
"for request/GPSK-3");
eap_gpsk_state(data, FAILURE);
return NULL;
}
wpabuf_put_u8(req, EAP_GPSK_OPCODE_GPSK_3);
start = wpabuf_put(req, 0);
wpabuf_put_data(req, data->rand_peer, EAP_GPSK_RAND_LEN);
wpabuf_put_data(req, data->rand_server, EAP_GPSK_RAND_LEN);
wpabuf_put_be16(req, data->id_server_len);
wpabuf_put_data(req, data->id_server, data->id_server_len);
csuite = wpabuf_put(req, sizeof(*csuite));
WPA_PUT_BE32(csuite->vendor, data->vendor);
WPA_PUT_BE16(csuite->specifier, data->specifier);
/* no PD_Payload_2 */
wpabuf_put_be16(req, 0);
pos = wpabuf_put(req, miclen);
if (eap_gpsk_compute_mic(data->sk, data->sk_len, data->vendor,
data->specifier, start, pos - start, pos) < 0)
{
os_free(req);
eap_gpsk_state(data, FAILURE);
return NULL;
}
return req;
}
static struct wpabuf * eap_gpsk_buildReq(struct eap_sm *sm, void *priv, u8 id)
{
struct eap_gpsk_data *data = priv;
switch (data->state) {
case GPSK_1:
return eap_gpsk_build_gpsk_1(sm, data, id);
case GPSK_3:
return eap_gpsk_build_gpsk_3(sm, data, id);
default:
wpa_printf(MSG_DEBUG, "EAP-GPSK: Unknown state %d in buildReq",
data->state);
break;
}
return NULL;
}
static Boolean eap_gpsk_check(struct eap_sm *sm, void *priv,
struct wpabuf *respData)
{
struct eap_gpsk_data *data = priv;
const u8 *pos;
size_t len;
pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_GPSK, respData, &len);
if (pos == NULL || len < 1) {
wpa_printf(MSG_INFO, "EAP-GPSK: Invalid frame");
return TRUE;
}
wpa_printf(MSG_DEBUG, "EAP-GPSK: Received frame: opcode=%d", *pos);
if (data->state == GPSK_1 && *pos == EAP_GPSK_OPCODE_GPSK_2)
return FALSE;
if (data->state == GPSK_3 && *pos == EAP_GPSK_OPCODE_GPSK_4)
return FALSE;
wpa_printf(MSG_INFO, "EAP-GPSK: Unexpected opcode=%d in state=%d",
*pos, data->state);
return TRUE;
}
static void eap_gpsk_process_gpsk_2(struct eap_sm *sm,
struct eap_gpsk_data *data,
const u8 *payload, size_t payloadlen)
{
const u8 *pos, *end;
u16 alen;
const struct eap_gpsk_csuite *csuite;
size_t i, miclen;
u8 mic[EAP_GPSK_MAX_MIC_LEN];
if (data->state != GPSK_1)
return;
wpa_printf(MSG_DEBUG, "EAP-GPSK: Received Response/GPSK-2");
pos = payload;
end = payload + payloadlen;
if (end - pos < 2) {
wpa_printf(MSG_DEBUG, "EAP-GPSK: Too short message for "
"ID_Peer length");
eap_gpsk_state(data, FAILURE);
return;
}
alen = WPA_GET_BE16(pos);
pos += 2;
if (end - pos < alen) {
wpa_printf(MSG_DEBUG, "EAP-GPSK: Too short message for "
"ID_Peer");
eap_gpsk_state(data, FAILURE);
return;
}
os_free(data->id_peer);
data->id_peer = os_malloc(alen);
if (data->id_peer == NULL) {
wpa_printf(MSG_DEBUG, "EAP-GPSK: Not enough memory to store "
"%d-octet ID_Peer", alen);
return;
}
os_memcpy(data->id_peer, pos, alen);
data->id_peer_len = alen;
wpa_hexdump_ascii(MSG_DEBUG, "EAP-GPSK: ID_Peer",
data->id_peer, data->id_peer_len);
pos += alen;
if (end - pos < 2) {
wpa_printf(MSG_DEBUG, "EAP-GPSK: Too short message for "
"ID_Server length");
eap_gpsk_state(data, FAILURE);
return;
}
alen = WPA_GET_BE16(pos);
pos += 2;
if (end - pos < alen) {
wpa_printf(MSG_DEBUG, "EAP-GPSK: Too short message for "
"ID_Server");
eap_gpsk_state(data, FAILURE);
return;
}
if (alen != data->id_server_len ||
os_memcmp(pos, data->id_server, alen) != 0) {
wpa_printf(MSG_DEBUG, "EAP-GPSK: ID_Server in GPSK-1 and "
"GPSK-2 did not match");
eap_gpsk_state(data, FAILURE);
return;
}
pos += alen;
if (end - pos < EAP_GPSK_RAND_LEN) {
wpa_printf(MSG_DEBUG, "EAP-GPSK: Too short message for "
"RAND_Peer");
eap_gpsk_state(data, FAILURE);
return;
}
os_memcpy(data->rand_peer, pos, EAP_GPSK_RAND_LEN);
wpa_hexdump(MSG_DEBUG, "EAP-GPSK: RAND_Peer",
data->rand_peer, EAP_GPSK_RAND_LEN);
pos += EAP_GPSK_RAND_LEN;
if (end - pos < EAP_GPSK_RAND_LEN) {
wpa_printf(MSG_DEBUG, "EAP-GPSK: Too short message for "
"RAND_Server");
eap_gpsk_state(data, FAILURE);
return;
}
if (os_memcmp(data->rand_server, pos, EAP_GPSK_RAND_LEN) != 0) {
wpa_printf(MSG_DEBUG, "EAP-GPSK: RAND_Server in GPSK-1 and "
"GPSK-2 did not match");
wpa_hexdump(MSG_DEBUG, "EAP-GPSK: RAND_Server in GPSK-1",
data->rand_server, EAP_GPSK_RAND_LEN);
wpa_hexdump(MSG_DEBUG, "EAP-GPSK: RAND_Server in GPSK-2",
pos, EAP_GPSK_RAND_LEN);
eap_gpsk_state(data, FAILURE);
return;
}
pos += EAP_GPSK_RAND_LEN;
if (end - pos < 2) {
wpa_printf(MSG_DEBUG, "EAP-GPSK: Too short message for "
"CSuite_List length");
eap_gpsk_state(data, FAILURE);
return;
}
alen = WPA_GET_BE16(pos);
pos += 2;
if (end - pos < alen) {
wpa_printf(MSG_DEBUG, "EAP-GPSK: Too short message for "
"CSuite_List");
eap_gpsk_state(data, FAILURE);
return;
}
if (alen != data->csuite_count * sizeof(struct eap_gpsk_csuite) ||
os_memcmp(pos, data->csuite_list, alen) != 0) {
wpa_printf(MSG_DEBUG, "EAP-GPSK: CSuite_List in GPSK-1 and "
"GPSK-2 did not match");
eap_gpsk_state(data, FAILURE);
return;
}
pos += alen;
if (end - pos < (int) sizeof(*csuite)) {
wpa_printf(MSG_DEBUG, "EAP-GPSK: Too short message for "
"CSuite_Sel");
eap_gpsk_state(data, FAILURE);
return;
}
csuite = (const struct eap_gpsk_csuite *) pos;
for (i = 0; i < data->csuite_count; i++) {
if (os_memcmp(csuite, &data->csuite_list[i], sizeof(*csuite))
== 0)
break;
}
if (i == data->csuite_count) {
wpa_printf(MSG_DEBUG, "EAP-GPSK: Peer selected unsupported "
"ciphersuite %d:%d",
WPA_GET_BE32(csuite->vendor),
WPA_GET_BE16(csuite->specifier));
eap_gpsk_state(data, FAILURE);
return;
}
data->vendor = WPA_GET_BE32(csuite->vendor);
data->specifier = WPA_GET_BE16(csuite->specifier);
wpa_printf(MSG_DEBUG, "EAP-GPSK: CSuite_Sel %d:%d",
data->vendor, data->specifier);
pos += sizeof(*csuite);
if (end - pos < 2) {
wpa_printf(MSG_DEBUG, "EAP-GPSK: Too short message for "
"PD_Payload_1 length");
eap_gpsk_state(data, FAILURE);
return;
}
alen = WPA_GET_BE16(pos);
pos += 2;
if (end - pos < alen) {
wpa_printf(MSG_DEBUG, "EAP-GPSK: Too short message for "
"PD_Payload_1");
eap_gpsk_state(data, FAILURE);
return;
}
wpa_hexdump(MSG_DEBUG, "EAP-GPSK: PD_Payload_1", pos, alen);
pos += alen;
if (sm->user == NULL || sm->user->password == NULL) {
wpa_printf(MSG_INFO, "EAP-GPSK: No PSK/password configured "
"for the user");
eap_gpsk_state(data, FAILURE);
return;
}
if (eap_gpsk_derive_keys(sm->user->password, sm->user->password_len,
data->vendor, data->specifier,
data->rand_peer, data->rand_server,
data->id_peer, data->id_peer_len,
data->id_server, data->id_server_len,
data->msk, data->emsk,
data->sk, &data->sk_len,
data->pk, &data->pk_len) < 0) {
wpa_printf(MSG_DEBUG, "EAP-GPSK: Failed to derive keys");
eap_gpsk_state(data, FAILURE);
return;
}
miclen = eap_gpsk_mic_len(data->vendor, data->specifier);
if (end - pos < (int) miclen) {
wpa_printf(MSG_DEBUG, "EAP-GPSK: Message too short for MIC "
"(left=%lu miclen=%lu)",
(unsigned long) (end - pos),
(unsigned long) miclen);
eap_gpsk_state(data, FAILURE);
return;
}
if (eap_gpsk_compute_mic(data->sk, data->sk_len, data->vendor,
data->specifier, payload, pos - payload, mic)
< 0) {
wpa_printf(MSG_DEBUG, "EAP-GPSK: Failed to compute MIC");
eap_gpsk_state(data, FAILURE);
return;
}
if (os_memcmp(mic, pos, miclen) != 0) {
wpa_printf(MSG_INFO, "EAP-GPSK: Incorrect MIC in GPSK-2");
wpa_hexdump(MSG_DEBUG, "EAP-GPSK: Received MIC", pos, miclen);
wpa_hexdump(MSG_DEBUG, "EAP-GPSK: Computed MIC", mic, miclen);
eap_gpsk_state(data, FAILURE);
return;
}
pos += miclen;
if (pos != end) {
wpa_printf(MSG_DEBUG, "EAP-GPSK: Ignored %lu bytes of extra "
"data in the end of GPSK-2",
(unsigned long) (end - pos));
}
eap_gpsk_state(data, GPSK_3);
}
static void eap_gpsk_process_gpsk_4(struct eap_sm *sm,
struct eap_gpsk_data *data,
const u8 *payload, size_t payloadlen)
{
const u8 *pos, *end;
u16 alen;
size_t miclen;
u8 mic[EAP_GPSK_MAX_MIC_LEN];
if (data->state != GPSK_3)
return;
wpa_printf(MSG_DEBUG, "EAP-GPSK: Received Response/GPSK-4");
pos = payload;
end = payload + payloadlen;
if (end - pos < 2) {
wpa_printf(MSG_DEBUG, "EAP-GPSK: Too short message for "
"PD_Payload_1 length");
eap_gpsk_state(data, FAILURE);
return;
}
alen = WPA_GET_BE16(pos);
pos += 2;
if (end - pos < alen) {
wpa_printf(MSG_DEBUG, "EAP-GPSK: Too short message for "
"PD_Payload_1");
eap_gpsk_state(data, FAILURE);
return;
}
wpa_hexdump(MSG_DEBUG, "EAP-GPSK: PD_Payload_1", pos, alen);
pos += alen;
miclen = eap_gpsk_mic_len(data->vendor, data->specifier);
if (end - pos < (int) miclen) {
wpa_printf(MSG_DEBUG, "EAP-GPSK: Message too short for MIC "
"(left=%lu miclen=%lu)",
(unsigned long) (end - pos),
(unsigned long) miclen);
eap_gpsk_state(data, FAILURE);
return;
}
if (eap_gpsk_compute_mic(data->sk, data->sk_len, data->vendor,
data->specifier, payload, pos - payload, mic)
< 0) {
wpa_printf(MSG_DEBUG, "EAP-GPSK: Failed to compute MIC");
eap_gpsk_state(data, FAILURE);
return;
}
if (os_memcmp(mic, pos, miclen) != 0) {
wpa_printf(MSG_INFO, "EAP-GPSK: Incorrect MIC in GPSK-4");
wpa_hexdump(MSG_DEBUG, "EAP-GPSK: Received MIC", pos, miclen);
wpa_hexdump(MSG_DEBUG, "EAP-GPSK: Computed MIC", mic, miclen);
eap_gpsk_state(data, FAILURE);
return;
}
pos += miclen;
if (pos != end) {
wpa_printf(MSG_DEBUG, "EAP-GPSK: Ignored %lu bytes of extra "
"data in the end of GPSK-4",
(unsigned long) (end - pos));
}
eap_gpsk_state(data, SUCCESS);
}
static void eap_gpsk_process(struct eap_sm *sm, void *priv,
struct wpabuf *respData)
{
struct eap_gpsk_data *data = priv;
const u8 *pos;
size_t len;
pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_GPSK, respData, &len);
if (pos == NULL || len < 1)
return;
switch (*pos) {
case EAP_GPSK_OPCODE_GPSK_2:
eap_gpsk_process_gpsk_2(sm, data, pos + 1, len - 1);
break;
case EAP_GPSK_OPCODE_GPSK_4:
eap_gpsk_process_gpsk_4(sm, data, pos + 1, len - 1);
break;
}
}
static Boolean eap_gpsk_isDone(struct eap_sm *sm, void *priv)
{
struct eap_gpsk_data *data = priv;
return data->state == SUCCESS || data->state == FAILURE;
}
static u8 * eap_gpsk_getKey(struct eap_sm *sm, void *priv, size_t *len)
{
struct eap_gpsk_data *data = priv;
u8 *key;
if (data->state != SUCCESS)
return NULL;
key = os_malloc(EAP_MSK_LEN);
if (key == NULL)
return NULL;
os_memcpy(key, data->msk, EAP_MSK_LEN);
*len = EAP_MSK_LEN;
return key;
}
static u8 * eap_gpsk_get_emsk(struct eap_sm *sm, void *priv, size_t *len)
{
struct eap_gpsk_data *data = priv;
u8 *key;
if (data->state != SUCCESS)
return NULL;
key = os_malloc(EAP_EMSK_LEN);
if (key == NULL)
return NULL;
os_memcpy(key, data->emsk, EAP_EMSK_LEN);
*len = EAP_EMSK_LEN;
return key;
}
static Boolean eap_gpsk_isSuccess(struct eap_sm *sm, void *priv)
{
struct eap_gpsk_data *data = priv;
return data->state == SUCCESS;
}
int eap_server_gpsk_register(void)
{
struct eap_method *eap;
int ret;
eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION,
EAP_VENDOR_IETF, EAP_TYPE_GPSK, "GPSK");
if (eap == NULL)
return -1;
eap->init = eap_gpsk_init;
eap->reset = eap_gpsk_reset;
eap->buildReq = eap_gpsk_buildReq;
eap->check = eap_gpsk_check;
eap->process = eap_gpsk_process;
eap->isDone = eap_gpsk_isDone;
eap->getKey = eap_gpsk_getKey;
eap->isSuccess = eap_gpsk_isSuccess;
eap->get_emsk = eap_gpsk_get_emsk;
ret = eap_server_method_register(eap);
if (ret)
eap_server_method_free(eap);
return ret;
}

View file

@ -0,0 +1,230 @@
/*
* hostapd / EAP-GTC (RFC 3748)
* Copyright (c) 2004-2006, Jouni Malinen <j@w1.fi>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* Alternatively, this software may be distributed under the terms of BSD
* license.
*
* See README and COPYING for more details.
*/
#include "includes.h"
#include "common.h"
#include "eap_i.h"
struct eap_gtc_data {
enum { CONTINUE, SUCCESS, FAILURE } state;
int prefix;
};
static void * eap_gtc_init(struct eap_sm *sm)
{
struct eap_gtc_data *data;
data = os_zalloc(sizeof(*data));
if (data == NULL)
return NULL;
data->state = CONTINUE;
#ifdef EAP_SERVER_FAST
if (sm->m && sm->m->vendor == EAP_VENDOR_IETF &&
sm->m->method == EAP_TYPE_FAST) {
wpa_printf(MSG_DEBUG, "EAP-GTC: EAP-FAST tunnel - use prefix "
"with challenge/response");
data->prefix = 1;
}
#endif /* EAP_SERVER_FAST */
return data;
}
static void eap_gtc_reset(struct eap_sm *sm, void *priv)
{
struct eap_gtc_data *data = priv;
os_free(data);
}
static struct wpabuf * eap_gtc_buildReq(struct eap_sm *sm, void *priv, u8 id)
{
struct eap_gtc_data *data = priv;
struct wpabuf *req;
char *msg;
size_t msg_len;
msg = data->prefix ? "CHALLENGE=Password" : "Password";
msg_len = os_strlen(msg);
req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_GTC, msg_len,
EAP_CODE_REQUEST, id);
if (req == NULL) {
wpa_printf(MSG_ERROR, "EAP-GTC: Failed to allocate memory for "
"request");
data->state = FAILURE;
return NULL;
}
wpabuf_put_data(req, msg, msg_len);
data->state = CONTINUE;
return req;
}
static Boolean eap_gtc_check(struct eap_sm *sm, void *priv,
struct wpabuf *respData)
{
const u8 *pos;
size_t len;
pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_GTC, respData, &len);
if (pos == NULL || len < 1) {
wpa_printf(MSG_INFO, "EAP-GTC: Invalid frame");
return TRUE;
}
return FALSE;
}
static void eap_gtc_process(struct eap_sm *sm, void *priv,
struct wpabuf *respData)
{
struct eap_gtc_data *data = priv;
const u8 *pos;
size_t rlen;
pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_GTC, respData, &rlen);
if (pos == NULL || rlen < 1)
return; /* Should not happen - frame already validated */
wpa_hexdump_ascii_key(MSG_MSGDUMP, "EAP-GTC: Response", pos, rlen);
#ifdef EAP_SERVER_FAST
if (data->prefix) {
const u8 *pos2, *end;
/* "RESPONSE=<user>\0<password>" */
if (rlen < 10) {
wpa_printf(MSG_DEBUG, "EAP-GTC: Too short response "
"for EAP-FAST prefix");
data->state = FAILURE;
return;
}
end = pos + rlen;
pos += 9;
pos2 = pos;
while (pos2 < end && *pos2)
pos2++;
if (pos2 == end) {
wpa_printf(MSG_DEBUG, "EAP-GTC: No password in "
"response to EAP-FAST prefix");
data->state = FAILURE;
return;
}
wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-GTC: Response user",
pos, pos2 - pos);
if (sm->identity && sm->require_identity_match &&
(pos2 - pos != (int) sm->identity_len ||
os_memcmp(pos, sm->identity, sm->identity_len))) {
wpa_printf(MSG_DEBUG, "EAP-GTC: Phase 2 Identity did "
"not match with required Identity");
wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-GTC: Expected "
"identity",
sm->identity, sm->identity_len);
data->state = FAILURE;
return;
} else {
os_free(sm->identity);
sm->identity_len = pos2 - pos;
sm->identity = os_malloc(sm->identity_len);
if (sm->identity == NULL) {
data->state = FAILURE;
return;
}
os_memcpy(sm->identity, pos, sm->identity_len);
}
if (eap_user_get(sm, sm->identity, sm->identity_len, 1) != 0) {
wpa_hexdump_ascii(MSG_DEBUG, "EAP-GTC: Phase2 "
"Identity not found in the user "
"database",
sm->identity, sm->identity_len);
data->state = FAILURE;
return;
}
pos = pos2 + 1;
rlen = end - pos;
wpa_hexdump_ascii_key(MSG_MSGDUMP,
"EAP-GTC: Response password",
pos, rlen);
}
#endif /* EAP_SERVER_FAST */
if (sm->user == NULL || sm->user->password == NULL ||
sm->user->password_hash) {
wpa_printf(MSG_INFO, "EAP-GTC: Plaintext password not "
"configured");
data->state = FAILURE;
return;
}
if (rlen != sm->user->password_len ||
os_memcmp(pos, sm->user->password, rlen) != 0) {
wpa_printf(MSG_DEBUG, "EAP-GTC: Done - Failure");
data->state = FAILURE;
} else {
wpa_printf(MSG_DEBUG, "EAP-GTC: Done - Success");
data->state = SUCCESS;
}
}
static Boolean eap_gtc_isDone(struct eap_sm *sm, void *priv)
{
struct eap_gtc_data *data = priv;
return data->state != CONTINUE;
}
static Boolean eap_gtc_isSuccess(struct eap_sm *sm, void *priv)
{
struct eap_gtc_data *data = priv;
return data->state == SUCCESS;
}
int eap_server_gtc_register(void)
{
struct eap_method *eap;
int ret;
eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION,
EAP_VENDOR_IETF, EAP_TYPE_GTC, "GTC");
if (eap == NULL)
return -1;
eap->init = eap_gtc_init;
eap->reset = eap_gtc_reset;
eap->buildReq = eap_gtc_buildReq;
eap->check = eap_gtc_check;
eap->process = eap_gtc_process;
eap->isDone = eap_gtc_isDone;
eap->isSuccess = eap_gtc_isSuccess;
ret = eap_server_method_register(eap);
if (ret)
eap_server_method_free(eap);
return ret;
}

View file

@ -0,0 +1,180 @@
/*
* hostapd / EAP-Identity
* Copyright (c) 2004-2006, Jouni Malinen <j@w1.fi>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* Alternatively, this software may be distributed under the terms of BSD
* license.
*
* See README and COPYING for more details.
*/
#include "includes.h"
#include "common.h"
#include "eap_i.h"
struct eap_identity_data {
enum { CONTINUE, SUCCESS, FAILURE } state;
int pick_up;
};
static void * eap_identity_init(struct eap_sm *sm)
{
struct eap_identity_data *data;
data = os_zalloc(sizeof(*data));
if (data == NULL)
return NULL;
data->state = CONTINUE;
return data;
}
static void * eap_identity_initPickUp(struct eap_sm *sm)
{
struct eap_identity_data *data;
data = eap_identity_init(sm);
if (data) {
data->pick_up = 1;
}
return data;
}
static void eap_identity_reset(struct eap_sm *sm, void *priv)
{
struct eap_identity_data *data = priv;
os_free(data);
}
static struct wpabuf * eap_identity_buildReq(struct eap_sm *sm, void *priv,
u8 id)
{
struct eap_identity_data *data = priv;
struct wpabuf *req;
const char *req_data;
size_t req_data_len;
if (sm->eapol_cb->get_eap_req_id_text) {
req_data = sm->eapol_cb->get_eap_req_id_text(sm->eapol_ctx,
&req_data_len);
} else {
req_data = NULL;
req_data_len = 0;
}
req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_IDENTITY, req_data_len,
EAP_CODE_REQUEST, id);
if (req == NULL) {
wpa_printf(MSG_ERROR, "EAP-Identity: Failed to allocate "
"memory for request");
data->state = FAILURE;
return NULL;
}
wpabuf_put_data(req, req_data, req_data_len);
return req;
}
static Boolean eap_identity_check(struct eap_sm *sm, void *priv,
struct wpabuf *respData)
{
const u8 *pos;
size_t len;
pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_IDENTITY,
respData, &len);
if (pos == NULL) {
wpa_printf(MSG_INFO, "EAP-Identity: Invalid frame");
return TRUE;
}
return FALSE;
}
static void eap_identity_process(struct eap_sm *sm, void *priv,
struct wpabuf *respData)
{
struct eap_identity_data *data = priv;
const u8 *pos;
size_t len;
if (data->pick_up) {
if (eap_identity_check(sm, data, respData)) {
wpa_printf(MSG_DEBUG, "EAP-Identity: failed to pick "
"up already started negotiation");
data->state = FAILURE;
return;
}
data->pick_up = 0;
}
pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_IDENTITY,
respData, &len);
if (pos == NULL)
return; /* Should not happen - frame already validated */
wpa_hexdump_ascii(MSG_DEBUG, "EAP-Identity: Peer identity", pos, len);
if (sm->identity)
sm->update_user = TRUE;
os_free(sm->identity);
sm->identity = os_malloc(len ? len : 1);
if (sm->identity == NULL) {
data->state = FAILURE;
} else {
os_memcpy(sm->identity, pos, len);
sm->identity_len = len;
data->state = SUCCESS;
}
}
static Boolean eap_identity_isDone(struct eap_sm *sm, void *priv)
{
struct eap_identity_data *data = priv;
return data->state != CONTINUE;
}
static Boolean eap_identity_isSuccess(struct eap_sm *sm, void *priv)
{
struct eap_identity_data *data = priv;
return data->state == SUCCESS;
}
int eap_server_identity_register(void)
{
struct eap_method *eap;
int ret;
eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION,
EAP_VENDOR_IETF, EAP_TYPE_IDENTITY,
"Identity");
if (eap == NULL)
return -1;
eap->init = eap_identity_init;
eap->initPickUp = eap_identity_initPickUp;
eap->reset = eap_identity_reset;
eap->buildReq = eap_identity_buildReq;
eap->check = eap_identity_check;
eap->process = eap_identity_process;
eap->isDone = eap_identity_isDone;
eap->isSuccess = eap_identity_isSuccess;
ret = eap_server_method_register(eap);
if (ret)
eap_server_method_free(eap);
return ret;
}

View file

@ -0,0 +1,539 @@
/*
* EAP-IKEv2 server (RFC 5106)
* Copyright (c) 2007, Jouni Malinen <j@w1.fi>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* Alternatively, this software may be distributed under the terms of BSD
* license.
*
* See README and COPYING for more details.
*/
#include "includes.h"
#include "common.h"
#include "eap_i.h"
#include "eap_common/eap_ikev2_common.h"
#include "ikev2.h"
struct eap_ikev2_data {
struct ikev2_initiator_data ikev2;
enum { MSG, FRAG_ACK, WAIT_FRAG_ACK, DONE, FAIL } state;
struct wpabuf *in_buf;
struct wpabuf *out_buf;
size_t out_used;
size_t fragment_size;
int keys_ready;
u8 keymat[EAP_MSK_LEN + EAP_EMSK_LEN];
int keymat_ok;
};
static const u8 * eap_ikev2_get_shared_secret(void *ctx, const u8 *IDr,
size_t IDr_len,
size_t *secret_len)
{
struct eap_sm *sm = ctx;
if (IDr == NULL) {
wpa_printf(MSG_DEBUG, "EAP-IKEV2: No IDr received - default "
"to user identity from EAP-Identity");
IDr = sm->identity;
IDr_len = sm->identity_len;
}
if (eap_user_get(sm, IDr, IDr_len, 0) < 0 || sm->user == NULL ||
sm->user->password == NULL) {
wpa_printf(MSG_DEBUG, "EAP-IKEV2: No user entry found");
return NULL;
}
*secret_len = sm->user->password_len;
return sm->user->password;
}
static const char * eap_ikev2_state_txt(int state)
{
switch (state) {
case MSG:
return "MSG";
case FRAG_ACK:
return "FRAG_ACK";
case WAIT_FRAG_ACK:
return "WAIT_FRAG_ACK";
case DONE:
return "DONE";
case FAIL:
return "FAIL";
default:
return "?";
}
}
static void eap_ikev2_state(struct eap_ikev2_data *data, int state)
{
wpa_printf(MSG_DEBUG, "EAP-IKEV2: %s -> %s",
eap_ikev2_state_txt(data->state),
eap_ikev2_state_txt(state));
data->state = state;
}
static void * eap_ikev2_init(struct eap_sm *sm)
{
struct eap_ikev2_data *data;
data = os_zalloc(sizeof(*data));
if (data == NULL)
return NULL;
data->state = MSG;
data->fragment_size = sm->fragment_size > 0 ? sm->fragment_size :
IKEV2_FRAGMENT_SIZE;
data->ikev2.state = SA_INIT;
data->ikev2.peer_auth = PEER_AUTH_SECRET;
data->ikev2.key_pad = (u8 *) os_strdup("Key Pad for EAP-IKEv2");
if (data->ikev2.key_pad == NULL)
goto failed;
data->ikev2.key_pad_len = 21;
/* TODO: make proposals configurable */
data->ikev2.proposal.proposal_num = 1;
data->ikev2.proposal.integ = AUTH_HMAC_SHA1_96;
data->ikev2.proposal.prf = PRF_HMAC_SHA1;
data->ikev2.proposal.encr = ENCR_AES_CBC;
data->ikev2.proposal.dh = DH_GROUP2_1024BIT_MODP;
data->ikev2.IDi = (u8 *) os_strdup("hostapd");
data->ikev2.IDi_len = 7;
data->ikev2.get_shared_secret = eap_ikev2_get_shared_secret;
data->ikev2.cb_ctx = sm;
return data;
failed:
ikev2_initiator_deinit(&data->ikev2);
os_free(data);
return NULL;
}
static void eap_ikev2_reset(struct eap_sm *sm, void *priv)
{
struct eap_ikev2_data *data = priv;
wpabuf_free(data->in_buf);
wpabuf_free(data->out_buf);
ikev2_initiator_deinit(&data->ikev2);
os_free(data);
}
static struct wpabuf * eap_ikev2_build_msg(struct eap_ikev2_data *data, u8 id)
{
struct wpabuf *req;
u8 flags;
size_t send_len, plen, icv_len = 0;
wpa_printf(MSG_DEBUG, "EAP-IKEV2: Generating Request");
flags = 0;
send_len = wpabuf_len(data->out_buf) - data->out_used;
if (1 + send_len > data->fragment_size) {
send_len = data->fragment_size - 1;
flags |= IKEV2_FLAGS_MORE_FRAGMENTS;
if (data->out_used == 0) {
flags |= IKEV2_FLAGS_LENGTH_INCLUDED;
send_len -= 4;
}
}
plen = 1 + send_len;
if (flags & IKEV2_FLAGS_LENGTH_INCLUDED)
plen += 4;
if (data->keys_ready) {
const struct ikev2_integ_alg *integ;
wpa_printf(MSG_DEBUG, "EAP-IKEV2: Add Integrity Checksum "
"Data");
flags |= IKEV2_FLAGS_ICV_INCLUDED;
integ = ikev2_get_integ(data->ikev2.proposal.integ);
if (integ == NULL) {
wpa_printf(MSG_DEBUG, "EAP-IKEV2: Unknown INTEG "
"transform / cannot generate ICV");
return NULL;
}
icv_len = integ->hash_len;
plen += icv_len;
}
req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_IKEV2, plen,
EAP_CODE_REQUEST, id);
if (req == NULL)
return NULL;
wpabuf_put_u8(req, flags); /* Flags */
if (flags & IKEV2_FLAGS_LENGTH_INCLUDED)
wpabuf_put_be32(req, wpabuf_len(data->out_buf));
wpabuf_put_data(req, wpabuf_head_u8(data->out_buf) + data->out_used,
send_len);
data->out_used += send_len;
if (flags & IKEV2_FLAGS_ICV_INCLUDED) {
const u8 *msg = wpabuf_head(req);
size_t len = wpabuf_len(req);
ikev2_integ_hash(data->ikev2.proposal.integ,
data->ikev2.keys.SK_ai,
data->ikev2.keys.SK_integ_len,
msg, len, wpabuf_put(req, icv_len));
}
if (data->out_used == wpabuf_len(data->out_buf)) {
wpa_printf(MSG_DEBUG, "EAP-IKEV2: Sending out %lu bytes "
"(message sent completely)",
(unsigned long) send_len);
wpabuf_free(data->out_buf);
data->out_buf = NULL;
data->out_used = 0;
} else {
wpa_printf(MSG_DEBUG, "EAP-IKEV2: Sending out %lu bytes "
"(%lu more to send)", (unsigned long) send_len,
(unsigned long) wpabuf_len(data->out_buf) -
data->out_used);
eap_ikev2_state(data, WAIT_FRAG_ACK);
}
return req;
}
static struct wpabuf * eap_ikev2_buildReq(struct eap_sm *sm, void *priv, u8 id)
{
struct eap_ikev2_data *data = priv;
switch (data->state) {
case MSG:
if (data->out_buf == NULL) {
data->out_buf = ikev2_initiator_build(&data->ikev2);
if (data->out_buf == NULL) {
wpa_printf(MSG_DEBUG, "EAP-IKEV2: Failed to "
"generate IKEv2 message");
return NULL;
}
data->out_used = 0;
}
/* pass through */
case WAIT_FRAG_ACK:
return eap_ikev2_build_msg(data, id);
case FRAG_ACK:
return eap_ikev2_build_frag_ack(id, EAP_CODE_REQUEST);
default:
wpa_printf(MSG_DEBUG, "EAP-IKEV2: Unexpected state %d in "
"buildReq", data->state);
return NULL;
}
}
static Boolean eap_ikev2_check(struct eap_sm *sm, void *priv,
struct wpabuf *respData)
{
const u8 *pos;
size_t len;
pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_IKEV2, respData,
&len);
if (pos == NULL) {
wpa_printf(MSG_INFO, "EAP-IKEV2: Invalid frame");
return TRUE;
}
return FALSE;
}
static int eap_ikev2_process_icv(struct eap_ikev2_data *data,
const struct wpabuf *respData,
u8 flags, const u8 *pos, const u8 **end)
{
if (flags & IKEV2_FLAGS_ICV_INCLUDED) {
int icv_len = eap_ikev2_validate_icv(
data->ikev2.proposal.integ, &data->ikev2.keys, 0,
respData, pos, *end);
if (icv_len < 0)
return -1;
/* Hide Integrity Checksum Data from further processing */
*end -= icv_len;
} else if (data->keys_ready) {
wpa_printf(MSG_INFO, "EAP-IKEV2: The message should have "
"included integrity checksum");
return -1;
}
return 0;
}
static int eap_ikev2_process_cont(struct eap_ikev2_data *data,
const u8 *buf, size_t len)
{
/* Process continuation of a pending message */
if (len > wpabuf_tailroom(data->in_buf)) {
wpa_printf(MSG_DEBUG, "EAP-IKEV2: Fragment overflow");
eap_ikev2_state(data, FAIL);
return -1;
}
wpabuf_put_data(data->in_buf, buf, len);
wpa_printf(MSG_DEBUG, "EAP-IKEV2: Received %lu bytes, waiting for %lu "
"bytes more", (unsigned long) len,
(unsigned long) wpabuf_tailroom(data->in_buf));
return 0;
}
static int eap_ikev2_process_fragment(struct eap_ikev2_data *data,
u8 flags, u32 message_length,
const u8 *buf, size_t len)
{
/* Process a fragment that is not the last one of the message */
if (data->in_buf == NULL && !(flags & IKEV2_FLAGS_LENGTH_INCLUDED)) {
wpa_printf(MSG_DEBUG, "EAP-IKEV2: No Message Length field in "
"a fragmented packet");
return -1;
}
if (data->in_buf == NULL) {
/* First fragment of the message */
data->in_buf = wpabuf_alloc(message_length);
if (data->in_buf == NULL) {
wpa_printf(MSG_DEBUG, "EAP-IKEV2: No memory for "
"message");
return -1;
}
wpabuf_put_data(data->in_buf, buf, len);
wpa_printf(MSG_DEBUG, "EAP-IKEV2: Received %lu bytes in first "
"fragment, waiting for %lu bytes more",
(unsigned long) len,
(unsigned long) wpabuf_tailroom(data->in_buf));
}
return 0;
}
static int eap_ikev2_server_keymat(struct eap_ikev2_data *data)
{
if (eap_ikev2_derive_keymat(
data->ikev2.proposal.prf, &data->ikev2.keys,
data->ikev2.i_nonce, data->ikev2.i_nonce_len,
data->ikev2.r_nonce, data->ikev2.r_nonce_len,
data->keymat) < 0) {
wpa_printf(MSG_DEBUG, "EAP-IKEV2: Failed to derive "
"key material");
return -1;
}
data->keymat_ok = 1;
return 0;
}
static void eap_ikev2_process(struct eap_sm *sm, void *priv,
struct wpabuf *respData)
{
struct eap_ikev2_data *data = priv;
const u8 *start, *pos, *end;
size_t len;
u8 flags;
u32 message_length = 0;
struct wpabuf tmpbuf;
pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_IKEV2, respData,
&len);
if (pos == NULL)
return; /* Should not happen; message already verified */
start = pos;
end = start + len;
if (len == 0) {
/* fragment ack */
flags = 0;
} else
flags = *pos++;
if (eap_ikev2_process_icv(data, respData, flags, pos, &end) < 0) {
eap_ikev2_state(data, FAIL);
return;
}
if (flags & IKEV2_FLAGS_LENGTH_INCLUDED) {
if (end - pos < 4) {
wpa_printf(MSG_DEBUG, "EAP-IKEV2: Message underflow");
eap_ikev2_state(data, FAIL);
return;
}
message_length = WPA_GET_BE32(pos);
pos += 4;
if (message_length < (u32) (end - pos)) {
wpa_printf(MSG_DEBUG, "EAP-IKEV2: Invalid Message "
"Length (%d; %ld remaining in this msg)",
message_length, (long) (end - pos));
eap_ikev2_state(data, FAIL);
return;
}
}
wpa_printf(MSG_DEBUG, "EAP-IKEV2: Received packet: Flags 0x%x "
"Message Length %u", flags, message_length);
if (data->state == WAIT_FRAG_ACK) {
if (len != 0) {
wpa_printf(MSG_DEBUG, "EAP-IKEV2: Unexpected payload "
"in WAIT_FRAG_ACK state");
eap_ikev2_state(data, FAIL);
return;
}
wpa_printf(MSG_DEBUG, "EAP-IKEV2: Fragment acknowledged");
eap_ikev2_state(data, MSG);
return;
}
if (data->in_buf && eap_ikev2_process_cont(data, pos, end - pos) < 0) {
eap_ikev2_state(data, FAIL);
return;
}
if (flags & IKEV2_FLAGS_MORE_FRAGMENTS) {
if (eap_ikev2_process_fragment(data, flags, message_length,
pos, end - pos) < 0)
eap_ikev2_state(data, FAIL);
else
eap_ikev2_state(data, FRAG_ACK);
return;
} else if (data->state == FRAG_ACK) {
wpa_printf(MSG_DEBUG, "EAP-TNC: All fragments received");
data->state = MSG;
}
if (data->in_buf == NULL) {
/* Wrap unfragmented messages as wpabuf without extra copy */
wpabuf_set(&tmpbuf, pos, end - pos);
data->in_buf = &tmpbuf;
}
if (ikev2_initiator_process(&data->ikev2, data->in_buf) < 0) {
if (data->in_buf == &tmpbuf)
data->in_buf = NULL;
eap_ikev2_state(data, FAIL);
return;
}
switch (data->ikev2.state) {
case SA_AUTH:
/* SA_INIT was sent out, so message have to be
* integrity protected from now on. */
data->keys_ready = 1;
break;
case IKEV2_DONE:
if (data->state == FAIL)
break;
wpa_printf(MSG_DEBUG, "EAP-IKEV2: Authentication completed "
"successfully");
if (eap_ikev2_server_keymat(data))
break;
eap_ikev2_state(data, DONE);
break;
default:
break;
}
if (data->in_buf != &tmpbuf)
wpabuf_free(data->in_buf);
data->in_buf = NULL;
}
static Boolean eap_ikev2_isDone(struct eap_sm *sm, void *priv)
{
struct eap_ikev2_data *data = priv;
return data->state == DONE || data->state == FAIL;
}
static Boolean eap_ikev2_isSuccess(struct eap_sm *sm, void *priv)
{
struct eap_ikev2_data *data = priv;
return data->state == DONE && data->ikev2.state == IKEV2_DONE &&
data->keymat_ok;
}
static u8 * eap_ikev2_getKey(struct eap_sm *sm, void *priv, size_t *len)
{
struct eap_ikev2_data *data = priv;
u8 *key;
if (data->state != DONE || !data->keymat_ok)
return NULL;
key = os_malloc(EAP_MSK_LEN);
if (key) {
os_memcpy(key, data->keymat, EAP_MSK_LEN);
*len = EAP_MSK_LEN;
}
return key;
}
static u8 * eap_ikev2_get_emsk(struct eap_sm *sm, void *priv, size_t *len)
{
struct eap_ikev2_data *data = priv;
u8 *key;
if (data->state != DONE || !data->keymat_ok)
return NULL;
key = os_malloc(EAP_EMSK_LEN);
if (key) {
os_memcpy(key, data->keymat + EAP_MSK_LEN, EAP_EMSK_LEN);
*len = EAP_EMSK_LEN;
}
return key;
}
int eap_server_ikev2_register(void)
{
struct eap_method *eap;
int ret;
eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION,
EAP_VENDOR_IETF, EAP_TYPE_IKEV2,
"IKEV2");
if (eap == NULL)
return -1;
eap->init = eap_ikev2_init;
eap->reset = eap_ikev2_reset;
eap->buildReq = eap_ikev2_buildReq;
eap->check = eap_ikev2_check;
eap->process = eap_ikev2_process;
eap->isDone = eap_ikev2_isDone;
eap->getKey = eap_ikev2_getKey;
eap->isSuccess = eap_ikev2_isSuccess;
eap->get_emsk = eap_ikev2_get_emsk;
ret = eap_server_method_register(eap);
if (ret)
eap_server_method_free(eap);
return ret;
}

View file

@ -0,0 +1,177 @@
/*
* hostapd / EAP-MD5 server
* Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* Alternatively, this software may be distributed under the terms of BSD
* license.
*
* See README and COPYING for more details.
*/
#include "includes.h"
#include "common.h"
#include "crypto/random.h"
#include "eap_i.h"
#include "eap_common/chap.h"
#define CHALLENGE_LEN 16
struct eap_md5_data {
u8 challenge[CHALLENGE_LEN];
enum { CONTINUE, SUCCESS, FAILURE } state;
};
static void * eap_md5_init(struct eap_sm *sm)
{
struct eap_md5_data *data;
data = os_zalloc(sizeof(*data));
if (data == NULL)
return NULL;
data->state = CONTINUE;
return data;
}
static void eap_md5_reset(struct eap_sm *sm, void *priv)
{
struct eap_md5_data *data = priv;
os_free(data);
}
static struct wpabuf * eap_md5_buildReq(struct eap_sm *sm, void *priv, u8 id)
{
struct eap_md5_data *data = priv;
struct wpabuf *req;
if (random_get_bytes(data->challenge, CHALLENGE_LEN)) {
wpa_printf(MSG_ERROR, "EAP-MD5: Failed to get random data");
data->state = FAILURE;
return NULL;
}
req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_MD5, 1 + CHALLENGE_LEN,
EAP_CODE_REQUEST, id);
if (req == NULL) {
wpa_printf(MSG_ERROR, "EAP-MD5: Failed to allocate memory for "
"request");
data->state = FAILURE;
return NULL;
}
wpabuf_put_u8(req, CHALLENGE_LEN);
wpabuf_put_data(req, data->challenge, CHALLENGE_LEN);
wpa_hexdump(MSG_MSGDUMP, "EAP-MD5: Challenge", data->challenge,
CHALLENGE_LEN);
data->state = CONTINUE;
return req;
}
static Boolean eap_md5_check(struct eap_sm *sm, void *priv,
struct wpabuf *respData)
{
const u8 *pos;
size_t len;
pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_MD5, respData, &len);
if (pos == NULL || len < 1) {
wpa_printf(MSG_INFO, "EAP-MD5: Invalid frame");
return TRUE;
}
if (*pos != CHAP_MD5_LEN || 1 + CHAP_MD5_LEN > len) {
wpa_printf(MSG_INFO, "EAP-MD5: Invalid response "
"(response_len=%d payload_len=%lu",
*pos, (unsigned long) len);
return TRUE;
}
return FALSE;
}
static void eap_md5_process(struct eap_sm *sm, void *priv,
struct wpabuf *respData)
{
struct eap_md5_data *data = priv;
const u8 *pos;
size_t plen;
u8 hash[CHAP_MD5_LEN], id;
if (sm->user == NULL || sm->user->password == NULL ||
sm->user->password_hash) {
wpa_printf(MSG_INFO, "EAP-MD5: Plaintext password not "
"configured");
data->state = FAILURE;
return;
}
pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_MD5, respData, &plen);
if (pos == NULL || *pos != CHAP_MD5_LEN || plen < 1 + CHAP_MD5_LEN)
return; /* Should not happen - frame already validated */
pos++; /* Skip response len */
wpa_hexdump(MSG_MSGDUMP, "EAP-MD5: Response", pos, CHAP_MD5_LEN);
id = eap_get_id(respData);
chap_md5(id, sm->user->password, sm->user->password_len,
data->challenge, CHALLENGE_LEN, hash);
if (os_memcmp(hash, pos, CHAP_MD5_LEN) == 0) {
wpa_printf(MSG_DEBUG, "EAP-MD5: Done - Success");
data->state = SUCCESS;
} else {
wpa_printf(MSG_DEBUG, "EAP-MD5: Done - Failure");
data->state = FAILURE;
}
}
static Boolean eap_md5_isDone(struct eap_sm *sm, void *priv)
{
struct eap_md5_data *data = priv;
return data->state != CONTINUE;
}
static Boolean eap_md5_isSuccess(struct eap_sm *sm, void *priv)
{
struct eap_md5_data *data = priv;
return data->state == SUCCESS;
}
int eap_server_md5_register(void)
{
struct eap_method *eap;
int ret;
eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION,
EAP_VENDOR_IETF, EAP_TYPE_MD5, "MD5");
if (eap == NULL)
return -1;
eap->init = eap_md5_init;
eap->reset = eap_md5_reset;
eap->buildReq = eap_md5_buildReq;
eap->check = eap_md5_check;
eap->process = eap_md5_process;
eap->isDone = eap_md5_isDone;
eap->isSuccess = eap_md5_isSuccess;
ret = eap_server_method_register(eap);
if (ret)
eap_server_method_free(eap);
return ret;
}

View file

@ -0,0 +1,175 @@
/*
* EAP server method registration
* Copyright (c) 2004-2009, Jouni Malinen <j@w1.fi>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* Alternatively, this software may be distributed under the terms of BSD
* license.
*
* See README and COPYING for more details.
*/
#include "includes.h"
#include "common.h"
#include "eap_i.h"
#include "eap_methods.h"
static struct eap_method *eap_methods;
/**
* eap_server_get_eap_method - Get EAP method based on type number
* @vendor: EAP Vendor-Id (0 = IETF)
* @method: EAP type number
* Returns: Pointer to EAP method or %NULL if not found
*/
const struct eap_method * eap_server_get_eap_method(int vendor, EapType method)
{
struct eap_method *m;
for (m = eap_methods; m; m = m->next) {
if (m->vendor == vendor && m->method == method)
return m;
}
return NULL;
}
/**
* eap_server_get_type - Get EAP type for the given EAP method name
* @name: EAP method name, e.g., TLS
* @vendor: Buffer for returning EAP Vendor-Id
* Returns: EAP method type or %EAP_TYPE_NONE if not found
*
* This function maps EAP type names into EAP type numbers based on the list of
* EAP methods included in the build.
*/
EapType eap_server_get_type(const char *name, int *vendor)
{
struct eap_method *m;
for (m = eap_methods; m; m = m->next) {
if (os_strcmp(m->name, name) == 0) {
*vendor = m->vendor;
return m->method;
}
}
*vendor = EAP_VENDOR_IETF;
return EAP_TYPE_NONE;
}
/**
* eap_server_method_alloc - Allocate EAP server method structure
* @version: Version of the EAP server method interface (set to
* EAP_SERVER_METHOD_INTERFACE_VERSION)
* @vendor: EAP Vendor-ID (EAP_VENDOR_*) (0 = IETF)
* @method: EAP type number (EAP_TYPE_*)
* @name: Name of the method (e.g., "TLS")
* Returns: Allocated EAP method structure or %NULL on failure
*
* The returned structure should be freed with eap_server_method_free() when it
* is not needed anymore.
*/
struct eap_method * eap_server_method_alloc(int version, int vendor,
EapType method, const char *name)
{
struct eap_method *eap;
eap = os_zalloc(sizeof(*eap));
if (eap == NULL)
return NULL;
eap->version = version;
eap->vendor = vendor;
eap->method = method;
eap->name = name;
return eap;
}
/**
* eap_server_method_free - Free EAP server method structure
* @method: Method structure allocated with eap_server_method_alloc()
*/
void eap_server_method_free(struct eap_method *method)
{
os_free(method);
}
/**
* eap_server_method_register - Register an EAP server method
* @method: EAP method to register
* Returns: 0 on success, -1 on invalid method, or -2 if a matching EAP method
* has already been registered
*
* Each EAP server method needs to call this function to register itself as a
* supported EAP method.
*/
int eap_server_method_register(struct eap_method *method)
{
struct eap_method *m, *last = NULL;
if (method == NULL || method->name == NULL ||
method->version != EAP_SERVER_METHOD_INTERFACE_VERSION)
return -1;
for (m = eap_methods; m; m = m->next) {
if ((m->vendor == method->vendor &&
m->method == method->method) ||
os_strcmp(m->name, method->name) == 0)
return -2;
last = m;
}
if (last)
last->next = method;
else
eap_methods = method;
return 0;
}
/**
* eap_server_unregister_methods - Unregister EAP server methods
*
* This function is called at program termination to unregister all EAP server
* methods.
*/
void eap_server_unregister_methods(void)
{
struct eap_method *m;
while (eap_methods) {
m = eap_methods;
eap_methods = eap_methods->next;
if (m->free)
m->free(m);
else
eap_server_method_free(m);
}
}
/**
* eap_server_get_name - Get EAP method name for the given EAP type
* @vendor: EAP Vendor-Id (0 = IETF)
* @type: EAP method type
* Returns: EAP method name, e.g., TLS, or %NULL if not found
*
* This function maps EAP type numbers into EAP type names based on the list of
* EAP methods included in the build.
*/
const char * eap_server_get_name(int vendor, EapType type)
{
struct eap_method *m;
for (m = eap_methods; m; m = m->next) {
if (m->vendor == vendor && m->method == type)
return m->name;
}
return NULL;
}

View file

@ -0,0 +1,575 @@
/*
* hostapd / EAP-MSCHAPv2 (draft-kamath-pppext-eap-mschapv2-00.txt) server
* Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* Alternatively, this software may be distributed under the terms of BSD
* license.
*
* See README and COPYING for more details.
*/
#include "includes.h"
#include "common.h"
#include "crypto/ms_funcs.h"
#include "crypto/random.h"
#include "eap_i.h"
struct eap_mschapv2_hdr {
u8 op_code; /* MSCHAPV2_OP_* */
u8 mschapv2_id; /* must be changed for challenges, but not for
* success/failure */
u8 ms_length[2]; /* Note: misaligned; length - 5 */
/* followed by data */
} STRUCT_PACKED;
#define MSCHAPV2_OP_CHALLENGE 1
#define MSCHAPV2_OP_RESPONSE 2
#define MSCHAPV2_OP_SUCCESS 3
#define MSCHAPV2_OP_FAILURE 4
#define MSCHAPV2_OP_CHANGE_PASSWORD 7
#define MSCHAPV2_RESP_LEN 49
#define ERROR_RESTRICTED_LOGON_HOURS 646
#define ERROR_ACCT_DISABLED 647
#define ERROR_PASSWD_EXPIRED 648
#define ERROR_NO_DIALIN_PERMISSION 649
#define ERROR_AUTHENTICATION_FAILURE 691
#define ERROR_CHANGING_PASSWORD 709
#define PASSWD_CHANGE_CHAL_LEN 16
#define MSCHAPV2_KEY_LEN 16
#define CHALLENGE_LEN 16
struct eap_mschapv2_data {
u8 auth_challenge[CHALLENGE_LEN];
int auth_challenge_from_tls;
u8 *peer_challenge;
u8 auth_response[20];
enum { CHALLENGE, SUCCESS_REQ, FAILURE_REQ, SUCCESS, FAILURE } state;
u8 resp_mschapv2_id;
u8 master_key[16];
int master_key_valid;
};
static void * eap_mschapv2_init(struct eap_sm *sm)
{
struct eap_mschapv2_data *data;
data = os_zalloc(sizeof(*data));
if (data == NULL)
return NULL;
data->state = CHALLENGE;
if (sm->auth_challenge) {
os_memcpy(data->auth_challenge, sm->auth_challenge,
CHALLENGE_LEN);
data->auth_challenge_from_tls = 1;
}
if (sm->peer_challenge) {
data->peer_challenge = os_malloc(CHALLENGE_LEN);
if (data->peer_challenge == NULL) {
os_free(data);
return NULL;
}
os_memcpy(data->peer_challenge, sm->peer_challenge,
CHALLENGE_LEN);
}
return data;
}
static void eap_mschapv2_reset(struct eap_sm *sm, void *priv)
{
struct eap_mschapv2_data *data = priv;
if (data == NULL)
return;
os_free(data->peer_challenge);
os_free(data);
}
static struct wpabuf * eap_mschapv2_build_challenge(
struct eap_sm *sm, struct eap_mschapv2_data *data, u8 id)
{
struct wpabuf *req;
struct eap_mschapv2_hdr *ms;
char *name = "hostapd"; /* TODO: make this configurable */
size_t ms_len;
if (!data->auth_challenge_from_tls &&
random_get_bytes(data->auth_challenge, CHALLENGE_LEN)) {
wpa_printf(MSG_ERROR, "EAP-MSCHAPV2: Failed to get random "
"data");
data->state = FAILURE;
return NULL;
}
ms_len = sizeof(*ms) + 1 + CHALLENGE_LEN + os_strlen(name);
req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, ms_len,
EAP_CODE_REQUEST, id);
if (req == NULL) {
wpa_printf(MSG_ERROR, "EAP-MSCHAPV2: Failed to allocate memory"
" for request");
data->state = FAILURE;
return NULL;
}
ms = wpabuf_put(req, sizeof(*ms));
ms->op_code = MSCHAPV2_OP_CHALLENGE;
ms->mschapv2_id = id;
WPA_PUT_BE16(ms->ms_length, ms_len);
wpabuf_put_u8(req, CHALLENGE_LEN);
if (!data->auth_challenge_from_tls)
wpabuf_put_data(req, data->auth_challenge, CHALLENGE_LEN);
else
wpabuf_put(req, CHALLENGE_LEN);
wpa_hexdump(MSG_MSGDUMP, "EAP-MSCHAPV2: Challenge",
data->auth_challenge, CHALLENGE_LEN);
wpabuf_put_data(req, name, os_strlen(name));
return req;
}
static struct wpabuf * eap_mschapv2_build_success_req(
struct eap_sm *sm, struct eap_mschapv2_data *data, u8 id)
{
struct wpabuf *req;
struct eap_mschapv2_hdr *ms;
u8 *msg;
char *message = "OK";
size_t ms_len;
ms_len = sizeof(*ms) + 2 + 2 * sizeof(data->auth_response) + 1 + 2 +
os_strlen(message);
req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, ms_len,
EAP_CODE_REQUEST, id);
if (req == NULL) {
wpa_printf(MSG_ERROR, "EAP-MSCHAPV2: Failed to allocate memory"
" for request");
data->state = FAILURE;
return NULL;
}
ms = wpabuf_put(req, sizeof(*ms));
ms->op_code = MSCHAPV2_OP_SUCCESS;
ms->mschapv2_id = data->resp_mschapv2_id;
WPA_PUT_BE16(ms->ms_length, ms_len);
msg = (u8 *) (ms + 1);
wpabuf_put_u8(req, 'S');
wpabuf_put_u8(req, '=');
wpa_snprintf_hex_uppercase(
wpabuf_put(req, sizeof(data->auth_response) * 2),
sizeof(data->auth_response) * 2 + 1,
data->auth_response, sizeof(data->auth_response));
wpabuf_put_u8(req, ' ');
wpabuf_put_u8(req, 'M');
wpabuf_put_u8(req, '=');
wpabuf_put_data(req, message, os_strlen(message));
wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-MSCHAPV2: Success Request Message",
msg, ms_len - sizeof(*ms));
return req;
}
static struct wpabuf * eap_mschapv2_build_failure_req(
struct eap_sm *sm, struct eap_mschapv2_data *data, u8 id)
{
struct wpabuf *req;
struct eap_mschapv2_hdr *ms;
char *message = "E=691 R=0 C=00000000000000000000000000000000 V=3 "
"M=FAILED";
size_t ms_len;
ms_len = sizeof(*ms) + os_strlen(message);
req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, ms_len,
EAP_CODE_REQUEST, id);
if (req == NULL) {
wpa_printf(MSG_ERROR, "EAP-MSCHAPV2: Failed to allocate memory"
" for request");
data->state = FAILURE;
return NULL;
}
ms = wpabuf_put(req, sizeof(*ms));
ms->op_code = MSCHAPV2_OP_FAILURE;
ms->mschapv2_id = data->resp_mschapv2_id;
WPA_PUT_BE16(ms->ms_length, ms_len);
wpabuf_put_data(req, message, os_strlen(message));
wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-MSCHAPV2: Failure Request Message",
(u8 *) message, os_strlen(message));
return req;
}
static struct wpabuf * eap_mschapv2_buildReq(struct eap_sm *sm, void *priv,
u8 id)
{
struct eap_mschapv2_data *data = priv;
switch (data->state) {
case CHALLENGE:
return eap_mschapv2_build_challenge(sm, data, id);
case SUCCESS_REQ:
return eap_mschapv2_build_success_req(sm, data, id);
case FAILURE_REQ:
return eap_mschapv2_build_failure_req(sm, data, id);
default:
wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Unknown state %d in "
"buildReq", data->state);
break;
}
return NULL;
}
static Boolean eap_mschapv2_check(struct eap_sm *sm, void *priv,
struct wpabuf *respData)
{
struct eap_mschapv2_data *data = priv;
struct eap_mschapv2_hdr *resp;
const u8 *pos;
size_t len;
pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, respData,
&len);
if (pos == NULL || len < 1) {
wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Invalid frame");
return TRUE;
}
resp = (struct eap_mschapv2_hdr *) pos;
if (data->state == CHALLENGE &&
resp->op_code != MSCHAPV2_OP_RESPONSE) {
wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Expected Response - "
"ignore op %d", resp->op_code);
return TRUE;
}
if (data->state == SUCCESS_REQ &&
resp->op_code != MSCHAPV2_OP_SUCCESS &&
resp->op_code != MSCHAPV2_OP_FAILURE) {
wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Expected Success or "
"Failure - ignore op %d", resp->op_code);
return TRUE;
}
if (data->state == FAILURE_REQ &&
resp->op_code != MSCHAPV2_OP_FAILURE) {
wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Expected Failure "
"- ignore op %d", resp->op_code);
return TRUE;
}
return FALSE;
}
static void eap_mschapv2_process_response(struct eap_sm *sm,
struct eap_mschapv2_data *data,
struct wpabuf *respData)
{
struct eap_mschapv2_hdr *resp;
const u8 *pos, *end, *peer_challenge, *nt_response, *name;
u8 flags;
size_t len, name_len, i;
u8 expected[24];
const u8 *username, *user;
size_t username_len, user_len;
int res;
pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, respData,
&len);
if (pos == NULL || len < 1)
return; /* Should not happen - frame already validated */
end = pos + len;
resp = (struct eap_mschapv2_hdr *) pos;
pos = (u8 *) (resp + 1);
if (len < sizeof(*resp) + 1 + 49 ||
resp->op_code != MSCHAPV2_OP_RESPONSE ||
pos[0] != 49) {
wpa_hexdump_buf(MSG_DEBUG, "EAP-MSCHAPV2: Invalid response",
respData);
data->state = FAILURE;
return;
}
data->resp_mschapv2_id = resp->mschapv2_id;
pos++;
peer_challenge = pos;
pos += 16 + 8;
nt_response = pos;
pos += 24;
flags = *pos++;
name = pos;
name_len = end - name;
if (data->peer_challenge) {
wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Using pre-configured "
"Peer-Challenge");
peer_challenge = data->peer_challenge;
}
wpa_hexdump(MSG_MSGDUMP, "EAP-MSCHAPV2: Peer-Challenge",
peer_challenge, 16);
wpa_hexdump(MSG_MSGDUMP, "EAP-MSCHAPV2: NT-Response", nt_response, 24);
wpa_printf(MSG_MSGDUMP, "EAP-MSCHAPV2: Flags 0x%x", flags);
wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-MSCHAPV2: Name", name, name_len);
/* MSCHAPv2 does not include optional domain name in the
* challenge-response calculation, so remove domain prefix
* (if present). */
username = sm->identity;
username_len = sm->identity_len;
for (i = 0; i < username_len; i++) {
if (username[i] == '\\') {
username_len -= i + 1;
username += i + 1;
break;
}
}
user = name;
user_len = name_len;
for (i = 0; i < user_len; i++) {
if (user[i] == '\\') {
user_len -= i + 1;
user += i + 1;
break;
}
}
if (username_len != user_len ||
os_memcmp(username, user, username_len) != 0) {
wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Mismatch in user names");
wpa_hexdump_ascii(MSG_DEBUG, "EAP-MSCHAPV2: Expected user "
"name", username, username_len);
wpa_hexdump_ascii(MSG_DEBUG, "EAP-MSCHAPV2: Received user "
"name", user, user_len);
data->state = FAILURE;
return;
}
wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-MSCHAPV2: User name",
username, username_len);
if (sm->user->password_hash) {
res = generate_nt_response_pwhash(data->auth_challenge,
peer_challenge,
username, username_len,
sm->user->password,
expected);
} else {
res = generate_nt_response(data->auth_challenge,
peer_challenge,
username, username_len,
sm->user->password,
sm->user->password_len,
expected);
}
if (res) {
data->state = FAILURE;
return;
}
if (os_memcmp(nt_response, expected, 24) == 0) {
const u8 *pw_hash;
u8 pw_hash_buf[16], pw_hash_hash[16];
wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Correct NT-Response");
data->state = SUCCESS_REQ;
/* Authenticator response is not really needed yet, but
* calculate it here so that peer_challenge and username need
* not be saved. */
if (sm->user->password_hash) {
pw_hash = sm->user->password;
} else {
nt_password_hash(sm->user->password,
sm->user->password_len,
pw_hash_buf);
pw_hash = pw_hash_buf;
}
generate_authenticator_response_pwhash(
pw_hash, peer_challenge, data->auth_challenge,
username, username_len, nt_response,
data->auth_response);
hash_nt_password_hash(pw_hash, pw_hash_hash);
get_master_key(pw_hash_hash, nt_response, data->master_key);
data->master_key_valid = 1;
wpa_hexdump_key(MSG_DEBUG, "EAP-MSCHAPV2: Derived Master Key",
data->master_key, MSCHAPV2_KEY_LEN);
} else {
wpa_hexdump(MSG_MSGDUMP, "EAP-MSCHAPV2: Expected NT-Response",
expected, 24);
wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Invalid NT-Response");
data->state = FAILURE_REQ;
}
}
static void eap_mschapv2_process_success_resp(struct eap_sm *sm,
struct eap_mschapv2_data *data,
struct wpabuf *respData)
{
struct eap_mschapv2_hdr *resp;
const u8 *pos;
size_t len;
pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, respData,
&len);
if (pos == NULL || len < 1)
return; /* Should not happen - frame already validated */
resp = (struct eap_mschapv2_hdr *) pos;
if (resp->op_code == MSCHAPV2_OP_SUCCESS) {
wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Received Success Response"
" - authentication completed successfully");
data->state = SUCCESS;
} else {
wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Did not receive Success "
"Response - peer rejected authentication");
data->state = FAILURE;
}
}
static void eap_mschapv2_process_failure_resp(struct eap_sm *sm,
struct eap_mschapv2_data *data,
struct wpabuf *respData)
{
struct eap_mschapv2_hdr *resp;
const u8 *pos;
size_t len;
pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, respData,
&len);
if (pos == NULL || len < 1)
return; /* Should not happen - frame already validated */
resp = (struct eap_mschapv2_hdr *) pos;
if (resp->op_code == MSCHAPV2_OP_FAILURE) {
wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Received Failure Response"
" - authentication failed");
} else {
wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Did not receive Failure "
"Response - authentication failed");
}
data->state = FAILURE;
}
static void eap_mschapv2_process(struct eap_sm *sm, void *priv,
struct wpabuf *respData)
{
struct eap_mschapv2_data *data = priv;
if (sm->user == NULL || sm->user->password == NULL) {
wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Password not configured");
data->state = FAILURE;
return;
}
switch (data->state) {
case CHALLENGE:
eap_mschapv2_process_response(sm, data, respData);
break;
case SUCCESS_REQ:
eap_mschapv2_process_success_resp(sm, data, respData);
break;
case FAILURE_REQ:
eap_mschapv2_process_failure_resp(sm, data, respData);
break;
default:
wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Unknown state %d in "
"process", data->state);
break;
}
}
static Boolean eap_mschapv2_isDone(struct eap_sm *sm, void *priv)
{
struct eap_mschapv2_data *data = priv;
return data->state == SUCCESS || data->state == FAILURE;
}
static u8 * eap_mschapv2_getKey(struct eap_sm *sm, void *priv, size_t *len)
{
struct eap_mschapv2_data *data = priv;
u8 *key;
if (data->state != SUCCESS || !data->master_key_valid)
return NULL;
*len = 2 * MSCHAPV2_KEY_LEN;
key = os_malloc(*len);
if (key == NULL)
return NULL;
/* MSK = server MS-MPPE-Recv-Key | MS-MPPE-Send-Key */
get_asymetric_start_key(data->master_key, key, MSCHAPV2_KEY_LEN, 0, 1);
get_asymetric_start_key(data->master_key, key + MSCHAPV2_KEY_LEN,
MSCHAPV2_KEY_LEN, 1, 1);
wpa_hexdump_key(MSG_DEBUG, "EAP-MSCHAPV2: Derived key", key, *len);
return key;
}
static Boolean eap_mschapv2_isSuccess(struct eap_sm *sm, void *priv)
{
struct eap_mschapv2_data *data = priv;
return data->state == SUCCESS;
}
int eap_server_mschapv2_register(void)
{
struct eap_method *eap;
int ret;
eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION,
EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2,
"MSCHAPV2");
if (eap == NULL)
return -1;
eap->init = eap_mschapv2_init;
eap->reset = eap_mschapv2_reset;
eap->buildReq = eap_mschapv2_buildReq;
eap->check = eap_mschapv2_check;
eap->process = eap_mschapv2_process;
eap->isDone = eap_mschapv2_isDone;
eap->getKey = eap_mschapv2_getKey;
eap->isSuccess = eap_mschapv2_isSuccess;
ret = eap_server_method_register(eap);
if (ret)
eap_server_method_free(eap);
return ret;
}

View file

@ -0,0 +1,570 @@
/*
* hostapd / EAP-PAX (RFC 4746) server
* Copyright (c) 2005-2007, Jouni Malinen <j@w1.fi>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* Alternatively, this software may be distributed under the terms of BSD
* license.
*
* See README and COPYING for more details.
*/
#include "includes.h"
#include "common.h"
#include "crypto/random.h"
#include "eap_server/eap_i.h"
#include "eap_common/eap_pax_common.h"
/*
* Note: only PAX_STD subprotocol is currently supported
*
* TODO: Add support with PAX_SEC with the mandatory to implement ciphersuite
* (HMAC_SHA1_128, IANA DH Group 14 (2048 bits), RSA-PKCS1-V1_5) and
* recommended ciphersuite (HMAC_SHA256_128, IANA DH Group 15 (3072 bits),
* RSAES-OAEP).
*/
struct eap_pax_data {
enum { PAX_STD_1, PAX_STD_3, SUCCESS, FAILURE } state;
u8 mac_id;
union {
u8 e[2 * EAP_PAX_RAND_LEN];
struct {
u8 x[EAP_PAX_RAND_LEN]; /* server rand */
u8 y[EAP_PAX_RAND_LEN]; /* client rand */
} r;
} rand;
u8 ak[EAP_PAX_AK_LEN];
u8 mk[EAP_PAX_MK_LEN];
u8 ck[EAP_PAX_CK_LEN];
u8 ick[EAP_PAX_ICK_LEN];
int keys_set;
char *cid;
size_t cid_len;
};
static void * eap_pax_init(struct eap_sm *sm)
{
struct eap_pax_data *data;
data = os_zalloc(sizeof(*data));
if (data == NULL)
return NULL;
data->state = PAX_STD_1;
/*
* TODO: make this configurable once EAP_PAX_HMAC_SHA256_128 is
* supported
*/
data->mac_id = EAP_PAX_MAC_HMAC_SHA1_128;
return data;
}
static void eap_pax_reset(struct eap_sm *sm, void *priv)
{
struct eap_pax_data *data = priv;
os_free(data->cid);
os_free(data);
}
static struct wpabuf * eap_pax_build_std_1(struct eap_sm *sm,
struct eap_pax_data *data, u8 id)
{
struct wpabuf *req;
struct eap_pax_hdr *pax;
u8 *pos;
wpa_printf(MSG_DEBUG, "EAP-PAX: PAX_STD-1 (sending)");
if (random_get_bytes(data->rand.r.x, EAP_PAX_RAND_LEN)) {
wpa_printf(MSG_ERROR, "EAP-PAX: Failed to get random data");
data->state = FAILURE;
return NULL;
}
req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_PAX,
sizeof(*pax) + 2 + EAP_PAX_RAND_LEN +
EAP_PAX_ICV_LEN, EAP_CODE_REQUEST, id);
if (req == NULL) {
wpa_printf(MSG_ERROR, "EAP-PAX: Failed to allocate memory "
"request");
data->state = FAILURE;
return NULL;
}
pax = wpabuf_put(req, sizeof(*pax));
pax->op_code = EAP_PAX_OP_STD_1;
pax->flags = 0;
pax->mac_id = data->mac_id;
pax->dh_group_id = EAP_PAX_DH_GROUP_NONE;
pax->public_key_id = EAP_PAX_PUBLIC_KEY_NONE;
wpabuf_put_be16(req, EAP_PAX_RAND_LEN);
wpabuf_put_data(req, data->rand.r.x, EAP_PAX_RAND_LEN);
wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: A = X (server rand)",
data->rand.r.x, EAP_PAX_RAND_LEN);
pos = wpabuf_put(req, EAP_PAX_MAC_LEN);
eap_pax_mac(data->mac_id, (u8 *) "", 0,
wpabuf_mhead(req), wpabuf_len(req) - EAP_PAX_ICV_LEN,
NULL, 0, NULL, 0, pos);
wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: ICV", pos, EAP_PAX_ICV_LEN);
return req;
}
static struct wpabuf * eap_pax_build_std_3(struct eap_sm *sm,
struct eap_pax_data *data, u8 id)
{
struct wpabuf *req;
struct eap_pax_hdr *pax;
u8 *pos;
wpa_printf(MSG_DEBUG, "EAP-PAX: PAX_STD-3 (sending)");
req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_PAX,
sizeof(*pax) + 2 + EAP_PAX_MAC_LEN +
EAP_PAX_ICV_LEN, EAP_CODE_REQUEST, id);
if (req == NULL) {
wpa_printf(MSG_ERROR, "EAP-PAX: Failed to allocate memory "
"request");
data->state = FAILURE;
return NULL;
}
pax = wpabuf_put(req, sizeof(*pax));
pax->op_code = EAP_PAX_OP_STD_3;
pax->flags = 0;
pax->mac_id = data->mac_id;
pax->dh_group_id = EAP_PAX_DH_GROUP_NONE;
pax->public_key_id = EAP_PAX_PUBLIC_KEY_NONE;
wpabuf_put_be16(req, EAP_PAX_MAC_LEN);
pos = wpabuf_put(req, EAP_PAX_MAC_LEN);
eap_pax_mac(data->mac_id, data->ck, EAP_PAX_CK_LEN,
data->rand.r.y, EAP_PAX_RAND_LEN,
(u8 *) data->cid, data->cid_len, NULL, 0, pos);
wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: MAC_CK(B, CID)",
pos, EAP_PAX_MAC_LEN);
pos += EAP_PAX_MAC_LEN;
/* Optional ADE could be added here, if needed */
pos = wpabuf_put(req, EAP_PAX_MAC_LEN);
eap_pax_mac(data->mac_id, data->ick, EAP_PAX_ICK_LEN,
wpabuf_mhead(req), wpabuf_len(req) - EAP_PAX_ICV_LEN,
NULL, 0, NULL, 0, pos);
wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: ICV", pos, EAP_PAX_ICV_LEN);
return req;
}
static struct wpabuf * eap_pax_buildReq(struct eap_sm *sm, void *priv, u8 id)
{
struct eap_pax_data *data = priv;
switch (data->state) {
case PAX_STD_1:
return eap_pax_build_std_1(sm, data, id);
case PAX_STD_3:
return eap_pax_build_std_3(sm, data, id);
default:
wpa_printf(MSG_DEBUG, "EAP-PAX: Unknown state %d in buildReq",
data->state);
break;
}
return NULL;
}
static Boolean eap_pax_check(struct eap_sm *sm, void *priv,
struct wpabuf *respData)
{
struct eap_pax_data *data = priv;
struct eap_pax_hdr *resp;
const u8 *pos;
size_t len, mlen;
u8 icvbuf[EAP_PAX_ICV_LEN], *icv;
pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_PAX, respData, &len);
if (pos == NULL || len < sizeof(*resp)) {
wpa_printf(MSG_INFO, "EAP-PAX: Invalid frame");
return TRUE;
}
mlen = sizeof(struct eap_hdr) + 1 + len;
resp = (struct eap_pax_hdr *) pos;
wpa_printf(MSG_DEBUG, "EAP-PAX: received frame: op_code 0x%x "
"flags 0x%x mac_id 0x%x dh_group_id 0x%x "
"public_key_id 0x%x",
resp->op_code, resp->flags, resp->mac_id, resp->dh_group_id,
resp->public_key_id);
wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: received payload",
(u8 *) (resp + 1), len - sizeof(*resp) - EAP_PAX_ICV_LEN);
if (data->state == PAX_STD_1 &&
resp->op_code != EAP_PAX_OP_STD_2) {
wpa_printf(MSG_DEBUG, "EAP-PAX: Expected PAX_STD-2 - "
"ignore op %d", resp->op_code);
return TRUE;
}
if (data->state == PAX_STD_3 &&
resp->op_code != EAP_PAX_OP_ACK) {
wpa_printf(MSG_DEBUG, "EAP-PAX: Expected PAX-ACK - "
"ignore op %d", resp->op_code);
return TRUE;
}
if (resp->op_code != EAP_PAX_OP_STD_2 &&
resp->op_code != EAP_PAX_OP_ACK) {
wpa_printf(MSG_DEBUG, "EAP-PAX: Unknown op_code 0x%x",
resp->op_code);
}
if (data->mac_id != resp->mac_id) {
wpa_printf(MSG_DEBUG, "EAP-PAX: Expected MAC ID 0x%x, "
"received 0x%x", data->mac_id, resp->mac_id);
return TRUE;
}
if (resp->dh_group_id != EAP_PAX_DH_GROUP_NONE) {
wpa_printf(MSG_INFO, "EAP-PAX: Expected DH Group ID 0x%x, "
"received 0x%x", EAP_PAX_DH_GROUP_NONE,
resp->dh_group_id);
return TRUE;
}
if (resp->public_key_id != EAP_PAX_PUBLIC_KEY_NONE) {
wpa_printf(MSG_INFO, "EAP-PAX: Expected Public Key ID 0x%x, "
"received 0x%x", EAP_PAX_PUBLIC_KEY_NONE,
resp->public_key_id);
return TRUE;
}
if (resp->flags & EAP_PAX_FLAGS_MF) {
/* TODO: add support for reassembling fragments */
wpa_printf(MSG_INFO, "EAP-PAX: fragmentation not supported");
return TRUE;
}
if (resp->flags & EAP_PAX_FLAGS_CE) {
wpa_printf(MSG_INFO, "EAP-PAX: Unexpected CE flag");
return TRUE;
}
if (data->keys_set) {
if (len - sizeof(*resp) < EAP_PAX_ICV_LEN) {
wpa_printf(MSG_INFO, "EAP-PAX: No ICV in the packet");
return TRUE;
}
icv = wpabuf_mhead_u8(respData) + mlen - EAP_PAX_ICV_LEN;
wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: ICV", icv, EAP_PAX_ICV_LEN);
eap_pax_mac(data->mac_id, data->ick, EAP_PAX_ICK_LEN,
wpabuf_mhead(respData),
wpabuf_len(respData) - EAP_PAX_ICV_LEN,
NULL, 0, NULL, 0, icvbuf);
if (os_memcmp(icvbuf, icv, EAP_PAX_ICV_LEN) != 0) {
wpa_printf(MSG_INFO, "EAP-PAX: Invalid ICV");
wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: Expected ICV",
icvbuf, EAP_PAX_ICV_LEN);
return TRUE;
}
}
return FALSE;
}
static void eap_pax_process_std_2(struct eap_sm *sm,
struct eap_pax_data *data,
struct wpabuf *respData)
{
struct eap_pax_hdr *resp;
u8 mac[EAP_PAX_MAC_LEN], icvbuf[EAP_PAX_ICV_LEN];
const u8 *pos;
size_t len, left;
int i;
if (data->state != PAX_STD_1)
return;
wpa_printf(MSG_DEBUG, "EAP-PAX: Received PAX_STD-2");
pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_PAX, respData, &len);
if (pos == NULL || len < sizeof(*resp) + EAP_PAX_ICV_LEN)
return;
resp = (struct eap_pax_hdr *) pos;
pos = (u8 *) (resp + 1);
left = len - sizeof(*resp);
if (left < 2 + EAP_PAX_RAND_LEN ||
WPA_GET_BE16(pos) != EAP_PAX_RAND_LEN) {
wpa_printf(MSG_INFO, "EAP-PAX: Too short PAX_STD-2 (B)");
return;
}
pos += 2;
left -= 2;
os_memcpy(data->rand.r.y, pos, EAP_PAX_RAND_LEN);
wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: Y (client rand)",
data->rand.r.y, EAP_PAX_RAND_LEN);
pos += EAP_PAX_RAND_LEN;
left -= EAP_PAX_RAND_LEN;
if (left < 2 || (size_t) 2 + WPA_GET_BE16(pos) > left) {
wpa_printf(MSG_INFO, "EAP-PAX: Too short PAX_STD-2 (CID)");
return;
}
data->cid_len = WPA_GET_BE16(pos);
os_free(data->cid);
data->cid = os_malloc(data->cid_len);
if (data->cid == NULL) {
wpa_printf(MSG_INFO, "EAP-PAX: Failed to allocate memory for "
"CID");
return;
}
os_memcpy(data->cid, pos + 2, data->cid_len);
pos += 2 + data->cid_len;
left -= 2 + data->cid_len;
wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-PAX: CID",
(u8 *) data->cid, data->cid_len);
if (left < 2 + EAP_PAX_MAC_LEN ||
WPA_GET_BE16(pos) != EAP_PAX_MAC_LEN) {
wpa_printf(MSG_INFO, "EAP-PAX: Too short PAX_STD-2 (MAC_CK)");
return;
}
pos += 2;
left -= 2;
wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: MAC_CK(A, B, CID)",
pos, EAP_PAX_MAC_LEN);
if (eap_user_get(sm, (u8 *) data->cid, data->cid_len, 0) < 0) {
wpa_hexdump_ascii(MSG_DEBUG, "EAP-PAX: unknown CID",
(u8 *) data->cid, data->cid_len);
data->state = FAILURE;
return;
}
for (i = 0;
i < EAP_MAX_METHODS &&
(sm->user->methods[i].vendor != EAP_VENDOR_IETF ||
sm->user->methods[i].method != EAP_TYPE_NONE);
i++) {
if (sm->user->methods[i].vendor == EAP_VENDOR_IETF &&
sm->user->methods[i].method == EAP_TYPE_PAX)
break;
}
if (i >= EAP_MAX_METHODS ||
sm->user->methods[i].vendor != EAP_VENDOR_IETF ||
sm->user->methods[i].method != EAP_TYPE_PAX) {
wpa_hexdump_ascii(MSG_DEBUG,
"EAP-PAX: EAP-PAX not enabled for CID",
(u8 *) data->cid, data->cid_len);
data->state = FAILURE;
return;
}
if (sm->user->password == NULL ||
sm->user->password_len != EAP_PAX_AK_LEN) {
wpa_hexdump_ascii(MSG_DEBUG, "EAP-PAX: invalid password in "
"user database for CID",
(u8 *) data->cid, data->cid_len);
data->state = FAILURE;
return;
}
os_memcpy(data->ak, sm->user->password, EAP_PAX_AK_LEN);
if (eap_pax_initial_key_derivation(data->mac_id, data->ak,
data->rand.e, data->mk, data->ck,
data->ick) < 0) {
wpa_printf(MSG_INFO, "EAP-PAX: Failed to complete initial "
"key derivation");
data->state = FAILURE;
return;
}
data->keys_set = 1;
eap_pax_mac(data->mac_id, data->ck, EAP_PAX_CK_LEN,
data->rand.r.x, EAP_PAX_RAND_LEN,
data->rand.r.y, EAP_PAX_RAND_LEN,
(u8 *) data->cid, data->cid_len, mac);
if (os_memcmp(mac, pos, EAP_PAX_MAC_LEN) != 0) {
wpa_printf(MSG_INFO, "EAP-PAX: Invalid MAC_CK(A, B, CID) in "
"PAX_STD-2");
wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: Expected MAC_CK(A, B, CID)",
mac, EAP_PAX_MAC_LEN);
data->state = FAILURE;
return;
}
pos += EAP_PAX_MAC_LEN;
left -= EAP_PAX_MAC_LEN;
if (left < EAP_PAX_ICV_LEN) {
wpa_printf(MSG_INFO, "EAP-PAX: Too short ICV (%lu) in "
"PAX_STD-2", (unsigned long) left);
return;
}
wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: ICV", pos, EAP_PAX_ICV_LEN);
eap_pax_mac(data->mac_id, data->ick, EAP_PAX_ICK_LEN,
wpabuf_head(respData),
wpabuf_len(respData) - EAP_PAX_ICV_LEN, NULL, 0, NULL, 0,
icvbuf);
if (os_memcmp(icvbuf, pos, EAP_PAX_ICV_LEN) != 0) {
wpa_printf(MSG_INFO, "EAP-PAX: Invalid ICV in PAX_STD-2");
wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: Expected ICV",
icvbuf, EAP_PAX_ICV_LEN);
return;
}
pos += EAP_PAX_ICV_LEN;
left -= EAP_PAX_ICV_LEN;
if (left > 0) {
wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: ignored extra payload",
pos, left);
}
data->state = PAX_STD_3;
}
static void eap_pax_process_ack(struct eap_sm *sm,
struct eap_pax_data *data,
struct wpabuf *respData)
{
if (data->state != PAX_STD_3)
return;
wpa_printf(MSG_DEBUG, "EAP-PAX: Received PAX-ACK - authentication "
"completed successfully");
data->state = SUCCESS;
}
static void eap_pax_process(struct eap_sm *sm, void *priv,
struct wpabuf *respData)
{
struct eap_pax_data *data = priv;
struct eap_pax_hdr *resp;
const u8 *pos;
size_t len;
if (sm->user == NULL || sm->user->password == NULL) {
wpa_printf(MSG_INFO, "EAP-PAX: Plaintext password not "
"configured");
data->state = FAILURE;
return;
}
pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_PAX, respData, &len);
if (pos == NULL || len < sizeof(*resp))
return;
resp = (struct eap_pax_hdr *) pos;
switch (resp->op_code) {
case EAP_PAX_OP_STD_2:
eap_pax_process_std_2(sm, data, respData);
break;
case EAP_PAX_OP_ACK:
eap_pax_process_ack(sm, data, respData);
break;
}
}
static Boolean eap_pax_isDone(struct eap_sm *sm, void *priv)
{
struct eap_pax_data *data = priv;
return data->state == SUCCESS || data->state == FAILURE;
}
static u8 * eap_pax_getKey(struct eap_sm *sm, void *priv, size_t *len)
{
struct eap_pax_data *data = priv;
u8 *key;
if (data->state != SUCCESS)
return NULL;
key = os_malloc(EAP_MSK_LEN);
if (key == NULL)
return NULL;
*len = EAP_MSK_LEN;
eap_pax_kdf(data->mac_id, data->mk, EAP_PAX_MK_LEN,
"Master Session Key", data->rand.e, 2 * EAP_PAX_RAND_LEN,
EAP_MSK_LEN, key);
return key;
}
static u8 * eap_pax_get_emsk(struct eap_sm *sm, void *priv, size_t *len)
{
struct eap_pax_data *data = priv;
u8 *key;
if (data->state != SUCCESS)
return NULL;
key = os_malloc(EAP_EMSK_LEN);
if (key == NULL)
return NULL;
*len = EAP_EMSK_LEN;
eap_pax_kdf(data->mac_id, data->mk, EAP_PAX_MK_LEN,
"Extended Master Session Key",
data->rand.e, 2 * EAP_PAX_RAND_LEN,
EAP_EMSK_LEN, key);
return key;
}
static Boolean eap_pax_isSuccess(struct eap_sm *sm, void *priv)
{
struct eap_pax_data *data = priv;
return data->state == SUCCESS;
}
int eap_server_pax_register(void)
{
struct eap_method *eap;
int ret;
eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION,
EAP_VENDOR_IETF, EAP_TYPE_PAX, "PAX");
if (eap == NULL)
return -1;
eap->init = eap_pax_init;
eap->reset = eap_pax_reset;
eap->buildReq = eap_pax_buildReq;
eap->check = eap_pax_check;
eap->process = eap_pax_process;
eap->isDone = eap_pax_isDone;
eap->getKey = eap_pax_getKey;
eap->isSuccess = eap_pax_isSuccess;
eap->get_emsk = eap_pax_get_emsk;
ret = eap_server_method_register(eap);
if (ret)
eap_server_method_free(eap);
return ret;
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,518 @@
/*
* hostapd / EAP-PSK (RFC 4764) server
* Copyright (c) 2005-2007, Jouni Malinen <j@w1.fi>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* Alternatively, this software may be distributed under the terms of BSD
* license.
*
* See README and COPYING for more details.
*
* Note: EAP-PSK is an EAP authentication method and as such, completely
* different from WPA-PSK. This file is not needed for WPA-PSK functionality.
*/
#include "includes.h"
#include "common.h"
#include "crypto/aes_wrap.h"
#include "crypto/random.h"
#include "eap_common/eap_psk_common.h"
#include "eap_server/eap_i.h"
struct eap_psk_data {
enum { PSK_1, PSK_3, SUCCESS, FAILURE } state;
u8 rand_s[EAP_PSK_RAND_LEN];
u8 rand_p[EAP_PSK_RAND_LEN];
u8 *id_p, *id_s;
size_t id_p_len, id_s_len;
u8 ak[EAP_PSK_AK_LEN], kdk[EAP_PSK_KDK_LEN], tek[EAP_PSK_TEK_LEN];
u8 msk[EAP_MSK_LEN];
u8 emsk[EAP_EMSK_LEN];
};
static void * eap_psk_init(struct eap_sm *sm)
{
struct eap_psk_data *data;
data = os_zalloc(sizeof(*data));
if (data == NULL)
return NULL;
data->state = PSK_1;
data->id_s = (u8 *) "hostapd";
data->id_s_len = 7;
return data;
}
static void eap_psk_reset(struct eap_sm *sm, void *priv)
{
struct eap_psk_data *data = priv;
os_free(data->id_p);
os_free(data);
}
static struct wpabuf * eap_psk_build_1(struct eap_sm *sm,
struct eap_psk_data *data, u8 id)
{
struct wpabuf *req;
struct eap_psk_hdr_1 *psk;
wpa_printf(MSG_DEBUG, "EAP-PSK: PSK-1 (sending)");
if (random_get_bytes(data->rand_s, EAP_PSK_RAND_LEN)) {
wpa_printf(MSG_ERROR, "EAP-PSK: Failed to get random data");
data->state = FAILURE;
return NULL;
}
wpa_hexdump(MSG_MSGDUMP, "EAP-PSK: RAND_S (server rand)",
data->rand_s, EAP_PSK_RAND_LEN);
req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_PSK,
sizeof(*psk) + data->id_s_len,
EAP_CODE_REQUEST, id);
if (req == NULL) {
wpa_printf(MSG_ERROR, "EAP-PSK: Failed to allocate memory "
"request");
data->state = FAILURE;
return NULL;
}
psk = wpabuf_put(req, sizeof(*psk));
psk->flags = EAP_PSK_FLAGS_SET_T(0); /* T=0 */
os_memcpy(psk->rand_s, data->rand_s, EAP_PSK_RAND_LEN);
wpabuf_put_data(req, data->id_s, data->id_s_len);
return req;
}
static struct wpabuf * eap_psk_build_3(struct eap_sm *sm,
struct eap_psk_data *data, u8 id)
{
struct wpabuf *req;
struct eap_psk_hdr_3 *psk;
u8 *buf, *pchannel, nonce[16];
size_t buflen;
wpa_printf(MSG_DEBUG, "EAP-PSK: PSK-3 (sending)");
req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_PSK,
sizeof(*psk) + 4 + 16 + 1, EAP_CODE_REQUEST, id);
if (req == NULL) {
wpa_printf(MSG_ERROR, "EAP-PSK: Failed to allocate memory "
"request");
data->state = FAILURE;
return NULL;
}
psk = wpabuf_put(req, sizeof(*psk));
psk->flags = EAP_PSK_FLAGS_SET_T(2); /* T=2 */
os_memcpy(psk->rand_s, data->rand_s, EAP_PSK_RAND_LEN);
/* MAC_S = OMAC1-AES-128(AK, ID_S||RAND_P) */
buflen = data->id_s_len + EAP_PSK_RAND_LEN;
buf = os_malloc(buflen);
if (buf == NULL)
goto fail;
os_memcpy(buf, data->id_s, data->id_s_len);
os_memcpy(buf + data->id_s_len, data->rand_p, EAP_PSK_RAND_LEN);
if (omac1_aes_128(data->ak, buf, buflen, psk->mac_s))
goto fail;
os_free(buf);
if (eap_psk_derive_keys(data->kdk, data->rand_p, data->tek, data->msk,
data->emsk))
goto fail;
wpa_hexdump_key(MSG_DEBUG, "EAP-PSK: TEK", data->tek, EAP_PSK_TEK_LEN);
wpa_hexdump_key(MSG_DEBUG, "EAP-PSK: MSK", data->msk, EAP_MSK_LEN);
wpa_hexdump_key(MSG_DEBUG, "EAP-PSK: EMSK", data->emsk, EAP_EMSK_LEN);
os_memset(nonce, 0, sizeof(nonce));
pchannel = wpabuf_put(req, 4 + 16 + 1);
os_memcpy(pchannel, nonce + 12, 4);
os_memset(pchannel + 4, 0, 16); /* Tag */
pchannel[4 + 16] = EAP_PSK_R_FLAG_DONE_SUCCESS << 6;
wpa_hexdump(MSG_DEBUG, "EAP-PSK: PCHANNEL (plaintext)",
pchannel, 4 + 16 + 1);
if (aes_128_eax_encrypt(data->tek, nonce, sizeof(nonce),
wpabuf_head(req), 22,
pchannel + 4 + 16, 1, pchannel + 4))
goto fail;
wpa_hexdump(MSG_DEBUG, "EAP-PSK: PCHANNEL (encrypted)",
pchannel, 4 + 16 + 1);
return req;
fail:
wpabuf_free(req);
data->state = FAILURE;
return NULL;
}
static struct wpabuf * eap_psk_buildReq(struct eap_sm *sm, void *priv, u8 id)
{
struct eap_psk_data *data = priv;
switch (data->state) {
case PSK_1:
return eap_psk_build_1(sm, data, id);
case PSK_3:
return eap_psk_build_3(sm, data, id);
default:
wpa_printf(MSG_DEBUG, "EAP-PSK: Unknown state %d in buildReq",
data->state);
break;
}
return NULL;
}
static Boolean eap_psk_check(struct eap_sm *sm, void *priv,
struct wpabuf *respData)
{
struct eap_psk_data *data = priv;
size_t len;
u8 t;
const u8 *pos;
pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_PSK, respData, &len);
if (pos == NULL || len < 1) {
wpa_printf(MSG_INFO, "EAP-PSK: Invalid frame");
return TRUE;
}
t = EAP_PSK_FLAGS_GET_T(*pos);
wpa_printf(MSG_DEBUG, "EAP-PSK: received frame: T=%d", t);
if (data->state == PSK_1 && t != 1) {
wpa_printf(MSG_DEBUG, "EAP-PSK: Expected PSK-2 - "
"ignore T=%d", t);
return TRUE;
}
if (data->state == PSK_3 && t != 3) {
wpa_printf(MSG_DEBUG, "EAP-PSK: Expected PSK-4 - "
"ignore T=%d", t);
return TRUE;
}
if ((t == 1 && len < sizeof(struct eap_psk_hdr_2)) ||
(t == 3 && len < sizeof(struct eap_psk_hdr_4))) {
wpa_printf(MSG_DEBUG, "EAP-PSK: Too short frame");
return TRUE;
}
return FALSE;
}
static void eap_psk_process_2(struct eap_sm *sm,
struct eap_psk_data *data,
struct wpabuf *respData)
{
const struct eap_psk_hdr_2 *resp;
u8 *pos, mac[EAP_PSK_MAC_LEN], *buf;
size_t left, buflen;
int i;
const u8 *cpos;
if (data->state != PSK_1)
return;
wpa_printf(MSG_DEBUG, "EAP-PSK: Received PSK-2");
cpos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_PSK, respData,
&left);
if (cpos == NULL || left < sizeof(*resp)) {
wpa_printf(MSG_INFO, "EAP-PSK: Invalid frame");
return;
}
resp = (const struct eap_psk_hdr_2 *) cpos;
cpos = (const u8 *) (resp + 1);
left -= sizeof(*resp);
os_free(data->id_p);
data->id_p = os_malloc(left);
if (data->id_p == NULL) {
wpa_printf(MSG_INFO, "EAP-PSK: Failed to allocate memory for "
"ID_P");
return;
}
os_memcpy(data->id_p, cpos, left);
data->id_p_len = left;
wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-PSK: ID_P",
data->id_p, data->id_p_len);
if (eap_user_get(sm, data->id_p, data->id_p_len, 0) < 0) {
wpa_hexdump_ascii(MSG_DEBUG, "EAP-PSK: unknown ID_P",
data->id_p, data->id_p_len);
data->state = FAILURE;
return;
}
for (i = 0;
i < EAP_MAX_METHODS &&
(sm->user->methods[i].vendor != EAP_VENDOR_IETF ||
sm->user->methods[i].method != EAP_TYPE_NONE);
i++) {
if (sm->user->methods[i].vendor == EAP_VENDOR_IETF &&
sm->user->methods[i].method == EAP_TYPE_PSK)
break;
}
if (i >= EAP_MAX_METHODS ||
sm->user->methods[i].vendor != EAP_VENDOR_IETF ||
sm->user->methods[i].method != EAP_TYPE_PSK) {
wpa_hexdump_ascii(MSG_DEBUG,
"EAP-PSK: EAP-PSK not enabled for ID_P",
data->id_p, data->id_p_len);
data->state = FAILURE;
return;
}
if (sm->user->password == NULL ||
sm->user->password_len != EAP_PSK_PSK_LEN) {
wpa_hexdump_ascii(MSG_DEBUG, "EAP-PSK: invalid password in "
"user database for ID_P",
data->id_p, data->id_p_len);
data->state = FAILURE;
return;
}
if (eap_psk_key_setup(sm->user->password, data->ak, data->kdk)) {
data->state = FAILURE;
return;
}
wpa_hexdump_key(MSG_DEBUG, "EAP-PSK: AK", data->ak, EAP_PSK_AK_LEN);
wpa_hexdump_key(MSG_DEBUG, "EAP-PSK: KDK", data->kdk, EAP_PSK_KDK_LEN);
wpa_hexdump(MSG_MSGDUMP, "EAP-PSK: RAND_P (client rand)",
resp->rand_p, EAP_PSK_RAND_LEN);
os_memcpy(data->rand_p, resp->rand_p, EAP_PSK_RAND_LEN);
/* MAC_P = OMAC1-AES-128(AK, ID_P||ID_S||RAND_S||RAND_P) */
buflen = data->id_p_len + data->id_s_len + 2 * EAP_PSK_RAND_LEN;
buf = os_malloc(buflen);
if (buf == NULL) {
data->state = FAILURE;
return;
}
os_memcpy(buf, data->id_p, data->id_p_len);
pos = buf + data->id_p_len;
os_memcpy(pos, data->id_s, data->id_s_len);
pos += data->id_s_len;
os_memcpy(pos, data->rand_s, EAP_PSK_RAND_LEN);
pos += EAP_PSK_RAND_LEN;
os_memcpy(pos, data->rand_p, EAP_PSK_RAND_LEN);
if (omac1_aes_128(data->ak, buf, buflen, mac)) {
os_free(buf);
data->state = FAILURE;
return;
}
os_free(buf);
wpa_hexdump(MSG_DEBUG, "EAP-PSK: MAC_P", resp->mac_p, EAP_PSK_MAC_LEN);
if (os_memcmp(mac, resp->mac_p, EAP_PSK_MAC_LEN) != 0) {
wpa_printf(MSG_INFO, "EAP-PSK: Invalid MAC_P");
wpa_hexdump(MSG_MSGDUMP, "EAP-PSK: Expected MAC_P",
mac, EAP_PSK_MAC_LEN);
data->state = FAILURE;
return;
}
data->state = PSK_3;
}
static void eap_psk_process_4(struct eap_sm *sm,
struct eap_psk_data *data,
struct wpabuf *respData)
{
const struct eap_psk_hdr_4 *resp;
u8 *decrypted, nonce[16];
size_t left;
const u8 *pos, *tag;
if (data->state != PSK_3)
return;
wpa_printf(MSG_DEBUG, "EAP-PSK: Received PSK-4");
pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_PSK, respData, &left);
if (pos == NULL || left < sizeof(*resp)) {
wpa_printf(MSG_INFO, "EAP-PSK: Invalid frame");
return;
}
resp = (const struct eap_psk_hdr_4 *) pos;
pos = (const u8 *) (resp + 1);
left -= sizeof(*resp);
wpa_hexdump(MSG_MSGDUMP, "EAP-PSK: Encrypted PCHANNEL", pos, left);
if (left < 4 + 16 + 1) {
wpa_printf(MSG_INFO, "EAP-PSK: Too short PCHANNEL data in "
"PSK-4 (len=%lu, expected 21)",
(unsigned long) left);
return;
}
if (pos[0] == 0 && pos[1] == 0 && pos[2] == 0 && pos[3] == 0) {
wpa_printf(MSG_DEBUG, "EAP-PSK: Nonce did not increase");
return;
}
os_memset(nonce, 0, 12);
os_memcpy(nonce + 12, pos, 4);
pos += 4;
left -= 4;
tag = pos;
pos += 16;
left -= 16;
decrypted = os_malloc(left);
if (decrypted == NULL)
return;
os_memcpy(decrypted, pos, left);
if (aes_128_eax_decrypt(data->tek, nonce, sizeof(nonce),
wpabuf_head(respData), 22, decrypted, left,
tag)) {
wpa_printf(MSG_WARNING, "EAP-PSK: PCHANNEL decryption failed");
os_free(decrypted);
data->state = FAILURE;
return;
}
wpa_hexdump(MSG_DEBUG, "EAP-PSK: Decrypted PCHANNEL message",
decrypted, left);
/* Verify R flag */
switch (decrypted[0] >> 6) {
case EAP_PSK_R_FLAG_CONT:
wpa_printf(MSG_DEBUG, "EAP-PSK: R flag - CONT - unsupported");
data->state = FAILURE;
break;
case EAP_PSK_R_FLAG_DONE_SUCCESS:
wpa_printf(MSG_DEBUG, "EAP-PSK: R flag - DONE_SUCCESS");
data->state = SUCCESS;
break;
case EAP_PSK_R_FLAG_DONE_FAILURE:
wpa_printf(MSG_DEBUG, "EAP-PSK: R flag - DONE_FAILURE");
data->state = FAILURE;
break;
}
os_free(decrypted);
}
static void eap_psk_process(struct eap_sm *sm, void *priv,
struct wpabuf *respData)
{
struct eap_psk_data *data = priv;
const u8 *pos;
size_t len;
if (sm->user == NULL || sm->user->password == NULL) {
wpa_printf(MSG_INFO, "EAP-PSK: Plaintext password not "
"configured");
data->state = FAILURE;
return;
}
pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_PSK, respData, &len);
if (pos == NULL || len < 1)
return;
switch (EAP_PSK_FLAGS_GET_T(*pos)) {
case 1:
eap_psk_process_2(sm, data, respData);
break;
case 3:
eap_psk_process_4(sm, data, respData);
break;
}
}
static Boolean eap_psk_isDone(struct eap_sm *sm, void *priv)
{
struct eap_psk_data *data = priv;
return data->state == SUCCESS || data->state == FAILURE;
}
static u8 * eap_psk_getKey(struct eap_sm *sm, void *priv, size_t *len)
{
struct eap_psk_data *data = priv;
u8 *key;
if (data->state != SUCCESS)
return NULL;
key = os_malloc(EAP_MSK_LEN);
if (key == NULL)
return NULL;
os_memcpy(key, data->msk, EAP_MSK_LEN);
*len = EAP_MSK_LEN;
return key;
}
static u8 * eap_psk_get_emsk(struct eap_sm *sm, void *priv, size_t *len)
{
struct eap_psk_data *data = priv;
u8 *key;
if (data->state != SUCCESS)
return NULL;
key = os_malloc(EAP_EMSK_LEN);
if (key == NULL)
return NULL;
os_memcpy(key, data->emsk, EAP_EMSK_LEN);
*len = EAP_EMSK_LEN;
return key;
}
static Boolean eap_psk_isSuccess(struct eap_sm *sm, void *priv)
{
struct eap_psk_data *data = priv;
return data->state == SUCCESS;
}
int eap_server_psk_register(void)
{
struct eap_method *eap;
int ret;
eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION,
EAP_VENDOR_IETF, EAP_TYPE_PSK, "PSK");
if (eap == NULL)
return -1;
eap->init = eap_psk_init;
eap->reset = eap_psk_reset;
eap->buildReq = eap_psk_buildReq;
eap->check = eap_psk_check;
eap->process = eap_psk_process;
eap->isDone = eap_psk_isDone;
eap->getKey = eap_psk_getKey;
eap->isSuccess = eap_psk_isSuccess;
eap->get_emsk = eap_psk_get_emsk;
ret = eap_server_method_register(eap);
if (ret)
eap_server_method_free(eap);
return ret;
}

View file

@ -0,0 +1,844 @@
/*
* hostapd / EAP-pwd (RFC 5931) server
* Copyright (c) 2010, Dan Harkins <dharkins@lounge.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the BSD license.
*
* Alternatively, this software may be distributed under the terms of the
* GNU General Public License version 2 as published by the Free Software
* Foundation.
*
* See README and COPYING for more details.
*/
#include "includes.h"
#include "common.h"
#include "eap_server/eap_i.h"
#include "eap_common/eap_pwd_common.h"
struct eap_pwd_data {
enum {
PWD_ID_Req, PWD_Commit_Req, PWD_Confirm_Req, SUCCESS, FAILURE
} state;
u8 *id_peer;
size_t id_peer_len;
u8 *id_server;
size_t id_server_len;
u8 *password;
size_t password_len;
u32 token;
u16 group_num;
EAP_PWD_group *grp;
BIGNUM *k;
BIGNUM *private_value;
BIGNUM *peer_scalar;
BIGNUM *my_scalar;
EC_POINT *my_element;
EC_POINT *peer_element;
u8 my_confirm[SHA256_DIGEST_LENGTH];
u8 msk[EAP_MSK_LEN];
u8 emsk[EAP_EMSK_LEN];
BN_CTX *bnctx;
};
static const char * eap_pwd_state_txt(int state)
{
switch (state) {
case PWD_ID_Req:
return "PWD-ID-Req";
case PWD_Commit_Req:
return "PWD-Commit-Req";
case PWD_Confirm_Req:
return "PWD-Confirm-Req";
case SUCCESS:
return "SUCCESS";
case FAILURE:
return "FAILURE";
default:
return "PWD-Unk";
}
}
static void eap_pwd_state(struct eap_pwd_data *data, int state)
{
wpa_printf(MSG_DEBUG, "EAP-pwd: %s -> %s",
eap_pwd_state_txt(data->state), eap_pwd_state_txt(state));
data->state = state;
}
static void * eap_pwd_init(struct eap_sm *sm)
{
struct eap_pwd_data *data;
if (sm->user == NULL || sm->user->password == NULL ||
sm->user->password_len == 0) {
wpa_printf(MSG_INFO, "EAP-PWD (server): Password is not "
"configured");
return NULL;
}
data = os_zalloc(sizeof(*data));
if (data == NULL)
return NULL;
data->group_num = sm->pwd_group;
wpa_printf(MSG_DEBUG, "EAP-pwd: Selected group number %d",
data->group_num);
data->state = PWD_ID_Req;
data->id_server = (u8 *) os_strdup("server");
if (data->id_server)
data->id_server_len = os_strlen((char *) data->id_server);
data->password = os_malloc(sm->user->password_len);
if (data->password == NULL) {
wpa_printf(MSG_INFO, "EAP-PWD: Memory allocation password "
"fail");
os_free(data->id_server);
os_free(data);
return NULL;
}
data->password_len = sm->user->password_len;
os_memcpy(data->password, sm->user->password, data->password_len);
data->bnctx = BN_CTX_new();
if (data->bnctx == NULL) {
wpa_printf(MSG_INFO, "EAP-PWD: bn context allocation fail");
os_free(data->password);
os_free(data->id_server);
os_free(data);
return NULL;
}
return data;
}
static void eap_pwd_reset(struct eap_sm *sm, void *priv)
{
struct eap_pwd_data *data = priv;
BN_free(data->private_value);
BN_free(data->peer_scalar);
BN_free(data->my_scalar);
BN_free(data->k);
BN_CTX_free(data->bnctx);
EC_POINT_free(data->my_element);
EC_POINT_free(data->peer_element);
os_free(data->id_peer);
os_free(data->id_server);
os_free(data->password);
if (data->grp) {
EC_GROUP_free(data->grp->group);
EC_POINT_free(data->grp->pwe);
BN_free(data->grp->order);
BN_free(data->grp->prime);
os_free(data->grp);
}
os_free(data);
}
static struct wpabuf *
eap_pwd_build_id_req(struct eap_sm *sm, struct eap_pwd_data *data, u8 id)
{
struct wpabuf *req;
wpa_printf(MSG_DEBUG, "EAP-pwd: ID/Request");
req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_PWD,
sizeof(struct eap_pwd_hdr) +
sizeof(struct eap_pwd_id) + data->id_server_len,
EAP_CODE_REQUEST, id);
if (req == NULL) {
eap_pwd_state(data, FAILURE);
return NULL;
}
/* an lfsr is good enough to generate unpredictable tokens */
data->token = os_random();
wpabuf_put_u8(req, EAP_PWD_OPCODE_ID_EXCH);
wpabuf_put_be16(req, data->group_num);
wpabuf_put_u8(req, EAP_PWD_DEFAULT_RAND_FUNC);
wpabuf_put_u8(req, EAP_PWD_DEFAULT_PRF);
wpabuf_put_data(req, &data->token, sizeof(data->token));
wpabuf_put_u8(req, EAP_PWD_PREP_NONE);
wpabuf_put_data(req, data->id_server, data->id_server_len);
return req;
}
static struct wpabuf *
eap_pwd_build_commit_req(struct eap_sm *sm, struct eap_pwd_data *data, u8 id)
{
struct wpabuf *req = NULL;
BIGNUM *mask = NULL, *x = NULL, *y = NULL;
u8 *scalar = NULL, *element = NULL;
u16 offset;
wpa_printf(MSG_DEBUG, "EAP-pwd: Commit/Request");
if (((data->private_value = BN_new()) == NULL) ||
((data->my_element = EC_POINT_new(data->grp->group)) == NULL) ||
((data->my_scalar = BN_new()) == NULL) ||
((mask = BN_new()) == NULL)) {
wpa_printf(MSG_INFO, "EAP-PWD (server): scalar allocation "
"fail");
goto fin;
}
BN_rand_range(data->private_value, data->grp->order);
BN_rand_range(mask, data->grp->order);
BN_add(data->my_scalar, data->private_value, mask);
BN_mod(data->my_scalar, data->my_scalar, data->grp->order,
data->bnctx);
if (!EC_POINT_mul(data->grp->group, data->my_element, NULL,
data->grp->pwe, mask, data->bnctx)) {
wpa_printf(MSG_INFO, "EAP-PWD (server): element allocation "
"fail");
eap_pwd_state(data, FAILURE);
goto fin;
}
if (!EC_POINT_invert(data->grp->group, data->my_element, data->bnctx))
{
wpa_printf(MSG_INFO, "EAP-PWD (server): element inversion "
"fail");
goto fin;
}
BN_free(mask);
if (((x = BN_new()) == NULL) ||
((y = BN_new()) == NULL)) {
wpa_printf(MSG_INFO, "EAP-PWD (server): point allocation "
"fail");
goto fin;
}
if (!EC_POINT_get_affine_coordinates_GFp(data->grp->group,
data->my_element, x, y,
data->bnctx)) {
wpa_printf(MSG_INFO, "EAP-PWD (server): point assignment "
"fail");
goto fin;
}
if (((scalar = os_malloc(BN_num_bytes(data->grp->order))) == NULL) ||
((element = os_malloc(BN_num_bytes(data->grp->prime) * 2)) ==
NULL)) {
wpa_printf(MSG_INFO, "EAP-PWD (server): data allocation fail");
goto fin;
}
/*
* bignums occupy as little memory as possible so one that is
* sufficiently smaller than the prime or order might need pre-pending
* with zeros.
*/
os_memset(scalar, 0, BN_num_bytes(data->grp->order));
os_memset(element, 0, BN_num_bytes(data->grp->prime) * 2);
offset = BN_num_bytes(data->grp->order) -
BN_num_bytes(data->my_scalar);
BN_bn2bin(data->my_scalar, scalar + offset);
offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(x);
BN_bn2bin(x, element + offset);
offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(y);
BN_bn2bin(y, element + BN_num_bytes(data->grp->prime) + offset);
req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_PWD,
sizeof(struct eap_pwd_hdr) +
(2 * BN_num_bytes(data->grp->prime)) +
BN_num_bytes(data->grp->order),
EAP_CODE_REQUEST, id);
if (req == NULL)
goto fin;
wpabuf_put_u8(req, EAP_PWD_OPCODE_COMMIT_EXCH);
/* We send the element as (x,y) followed by the scalar */
wpabuf_put_data(req, element, (2 * BN_num_bytes(data->grp->prime)));
wpabuf_put_data(req, scalar, BN_num_bytes(data->grp->order));
fin:
os_free(scalar);
os_free(element);
BN_free(x);
BN_free(y);
if (req == NULL)
eap_pwd_state(data, FAILURE);
return req;
}
static struct wpabuf *
eap_pwd_build_confirm_req(struct eap_sm *sm, struct eap_pwd_data *data, u8 id)
{
struct wpabuf *req = NULL;
BIGNUM *x = NULL, *y = NULL;
HMAC_CTX ctx;
u8 conf[SHA256_DIGEST_LENGTH], *cruft = NULL, *ptr;
u16 grp;
wpa_printf(MSG_DEBUG, "EAP-pwd: Confirm/Request");
/* Each component of the cruft will be at most as big as the prime */
if (((cruft = os_malloc(BN_num_bytes(data->grp->prime))) == NULL) ||
((x = BN_new()) == NULL) || ((y = BN_new()) == NULL)) {
wpa_printf(MSG_INFO, "EAP-PWD (server): debug allocation "
"fail");
goto fin;
}
/*
* commit is H(k | server_element | server_scalar | peer_element |
* peer_scalar | ciphersuite)
*/
H_Init(&ctx);
/*
* Zero the memory each time because this is mod prime math and some
* value may start with a few zeros and the previous one did not.
*
* First is k
*/
os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
BN_bn2bin(data->k, cruft);
H_Update(&ctx, cruft, BN_num_bytes(data->grp->prime));
/* server element: x, y */
if (!EC_POINT_get_affine_coordinates_GFp(data->grp->group,
data->my_element, x, y,
data->bnctx)) {
wpa_printf(MSG_INFO, "EAP-PWD (server): confirm point "
"assignment fail");
goto fin;
}
os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
BN_bn2bin(x, cruft);
H_Update(&ctx, cruft, BN_num_bytes(data->grp->prime));
os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
BN_bn2bin(y, cruft);
H_Update(&ctx, cruft, BN_num_bytes(data->grp->prime));
/* server scalar */
os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
BN_bn2bin(data->my_scalar, cruft);
H_Update(&ctx, cruft, BN_num_bytes(data->grp->order));
/* peer element: x, y */
if (!EC_POINT_get_affine_coordinates_GFp(data->grp->group,
data->peer_element, x, y,
data->bnctx)) {
wpa_printf(MSG_INFO, "EAP-PWD (server): confirm point "
"assignment fail");
goto fin;
}
os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
BN_bn2bin(x, cruft);
H_Update(&ctx, cruft, BN_num_bytes(data->grp->prime));
os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
BN_bn2bin(y, cruft);
H_Update(&ctx, cruft, BN_num_bytes(data->grp->prime));
/* peer scalar */
os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
BN_bn2bin(data->peer_scalar, cruft);
H_Update(&ctx, cruft, BN_num_bytes(data->grp->order));
/* ciphersuite */
grp = htons(data->group_num);
os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
ptr = cruft;
os_memcpy(ptr, &grp, sizeof(u16));
ptr += sizeof(u16);
*ptr = EAP_PWD_DEFAULT_RAND_FUNC;
ptr += sizeof(u8);
*ptr = EAP_PWD_DEFAULT_PRF;
ptr += sizeof(u8);
H_Update(&ctx, cruft, ptr-cruft);
/* all done with the random function */
H_Final(&ctx, conf);
os_memcpy(data->my_confirm, conf, SHA256_DIGEST_LENGTH);
req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_PWD,
sizeof(struct eap_pwd_hdr) + SHA256_DIGEST_LENGTH,
EAP_CODE_REQUEST, id);
if (req == NULL)
goto fin;
wpabuf_put_u8(req, EAP_PWD_OPCODE_CONFIRM_EXCH);
wpabuf_put_data(req, conf, SHA256_DIGEST_LENGTH);
fin:
os_free(cruft);
BN_free(x);
BN_free(y);
if (req == NULL)
eap_pwd_state(data, FAILURE);
return req;
}
static struct wpabuf *
eap_pwd_build_req(struct eap_sm *sm, void *priv, u8 id)
{
struct eap_pwd_data *data = priv;
switch (data->state) {
case PWD_ID_Req:
return eap_pwd_build_id_req(sm, data, id);
case PWD_Commit_Req:
return eap_pwd_build_commit_req(sm, data, id);
case PWD_Confirm_Req:
return eap_pwd_build_confirm_req(sm, data, id);
default:
wpa_printf(MSG_INFO, "EAP-pwd: Unknown state %d in build_req",
data->state);
break;
}
return NULL;
}
static Boolean eap_pwd_check(struct eap_sm *sm, void *priv,
struct wpabuf *respData)
{
struct eap_pwd_data *data = priv;
const u8 *pos;
size_t len;
pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_PWD, respData, &len);
if (pos == NULL || len < 1) {
wpa_printf(MSG_INFO, "EAP-pwd: Invalid frame");
return TRUE;
}
wpa_printf(MSG_DEBUG, "EAP-pwd: Received frame: opcode=%d", *pos);
if (data->state == PWD_ID_Req && *pos == EAP_PWD_OPCODE_ID_EXCH)
return FALSE;
if (data->state == PWD_Commit_Req &&
*pos == EAP_PWD_OPCODE_COMMIT_EXCH)
return FALSE;
if (data->state == PWD_Confirm_Req &&
*pos == EAP_PWD_OPCODE_CONFIRM_EXCH)
return FALSE;
wpa_printf(MSG_INFO, "EAP-pwd: Unexpected opcode=%d in state=%d",
*pos, data->state);
return TRUE;
}
static void eap_pwd_process_id_resp(struct eap_sm *sm,
struct eap_pwd_data *data,
const u8 *payload, size_t payload_len)
{
struct eap_pwd_id *id;
if (payload_len < sizeof(struct eap_pwd_id)) {
wpa_printf(MSG_INFO, "EAP-pwd: Invalid ID response");
return;
}
id = (struct eap_pwd_id *) payload;
if ((data->group_num != be_to_host16(id->group_num)) ||
(id->random_function != EAP_PWD_DEFAULT_RAND_FUNC) ||
(os_memcmp(id->token, (u8 *)&data->token, sizeof(data->token))) ||
(id->prf != EAP_PWD_DEFAULT_PRF)) {
wpa_printf(MSG_INFO, "EAP-pwd: peer changed parameters");
eap_pwd_state(data, FAILURE);
return;
}
data->id_peer = os_malloc(payload_len - sizeof(struct eap_pwd_id));
if (data->id_peer == NULL) {
wpa_printf(MSG_INFO, "EAP-PWD: memory allocation id fail");
return;
}
data->id_peer_len = payload_len - sizeof(struct eap_pwd_id);
os_memcpy(data->id_peer, id->identity, data->id_peer_len);
wpa_hexdump_ascii(MSG_DEBUG, "EAP-PWD (server): peer sent id of",
data->id_peer, data->id_peer_len);
if ((data->grp = os_malloc(sizeof(EAP_PWD_group))) == NULL) {
wpa_printf(MSG_INFO, "EAP-PWD: failed to allocate memory for "
"group");
return;
}
if (compute_password_element(data->grp, data->group_num,
data->password, data->password_len,
data->id_server, data->id_server_len,
data->id_peer, data->id_peer_len,
(u8 *) &data->token)) {
wpa_printf(MSG_INFO, "EAP-PWD (server): unable to compute "
"PWE");
return;
}
wpa_printf(MSG_DEBUG, "EAP-PWD (server): computed %d bit PWE...",
BN_num_bits(data->grp->prime));
eap_pwd_state(data, PWD_Commit_Req);
}
static void
eap_pwd_process_commit_resp(struct eap_sm *sm, struct eap_pwd_data *data,
const u8 *payload, size_t payload_len)
{
u8 *ptr;
BIGNUM *x = NULL, *y = NULL, *cofactor = NULL;
EC_POINT *K = NULL, *point = NULL;
int res = 0;
wpa_printf(MSG_DEBUG, "EAP-pwd: Received commit response");
if (((data->peer_scalar = BN_new()) == NULL) ||
((data->k = BN_new()) == NULL) ||
((cofactor = BN_new()) == NULL) ||
((x = BN_new()) == NULL) ||
((y = BN_new()) == NULL) ||
((point = EC_POINT_new(data->grp->group)) == NULL) ||
((K = EC_POINT_new(data->grp->group)) == NULL) ||
((data->peer_element = EC_POINT_new(data->grp->group)) == NULL)) {
wpa_printf(MSG_INFO, "EAP-PWD (server): peer data allocation "
"fail");
goto fin;
}
if (!EC_GROUP_get_cofactor(data->grp->group, cofactor, NULL)) {
wpa_printf(MSG_INFO, "EAP-PWD (server): unable to get "
"cofactor for curve");
goto fin;
}
/* element, x then y, followed by scalar */
ptr = (u8 *) payload;
BN_bin2bn(ptr, BN_num_bytes(data->grp->prime), x);
ptr += BN_num_bytes(data->grp->prime);
BN_bin2bn(ptr, BN_num_bytes(data->grp->prime), y);
ptr += BN_num_bytes(data->grp->prime);
BN_bin2bn(ptr, BN_num_bytes(data->grp->order), data->peer_scalar);
if (!EC_POINT_set_affine_coordinates_GFp(data->grp->group,
data->peer_element, x, y,
data->bnctx)) {
wpa_printf(MSG_INFO, "EAP-PWD (server): setting peer element "
"fail");
goto fin;
}
/* check to ensure peer's element is not in a small sub-group */
if (BN_cmp(cofactor, BN_value_one())) {
if (!EC_POINT_mul(data->grp->group, point, NULL,
data->peer_element, cofactor, NULL)) {
wpa_printf(MSG_INFO, "EAP-PWD (server): cannot "
"multiply peer element by order");
goto fin;
}
if (EC_POINT_is_at_infinity(data->grp->group, point)) {
wpa_printf(MSG_INFO, "EAP-PWD (server): peer element "
"is at infinity!\n");
goto fin;
}
}
/* compute the shared key, k */
if ((!EC_POINT_mul(data->grp->group, K, NULL, data->grp->pwe,
data->peer_scalar, data->bnctx)) ||
(!EC_POINT_add(data->grp->group, K, K, data->peer_element,
data->bnctx)) ||
(!EC_POINT_mul(data->grp->group, K, NULL, K, data->private_value,
data->bnctx))) {
wpa_printf(MSG_INFO, "EAP-PWD (server): computing shared key "
"fail");
goto fin;
}
/* ensure that the shared key isn't in a small sub-group */
if (BN_cmp(cofactor, BN_value_one())) {
if (!EC_POINT_mul(data->grp->group, K, NULL, K, cofactor,
NULL)) {
wpa_printf(MSG_INFO, "EAP-PWD (server): cannot "
"multiply shared key point by order!\n");
goto fin;
}
}
/*
* This check is strictly speaking just for the case above where
* co-factor > 1 but it was suggested that even though this is probably
* never going to happen it is a simple and safe check "just to be
* sure" so let's be safe.
*/
if (EC_POINT_is_at_infinity(data->grp->group, K)) {
wpa_printf(MSG_INFO, "EAP-PWD (server): shared key point is "
"at infinity");
goto fin;
}
if (!EC_POINT_get_affine_coordinates_GFp(data->grp->group, K, data->k,
NULL, data->bnctx)) {
wpa_printf(MSG_INFO, "EAP-PWD (server): unable to extract "
"shared secret from secret point");
goto fin;
}
res = 1;
fin:
EC_POINT_free(K);
EC_POINT_free(point);
BN_free(cofactor);
BN_free(x);
BN_free(y);
if (res)
eap_pwd_state(data, PWD_Confirm_Req);
else
eap_pwd_state(data, FAILURE);
}
static void
eap_pwd_process_confirm_resp(struct eap_sm *sm, struct eap_pwd_data *data,
const u8 *payload, size_t payload_len)
{
BIGNUM *x = NULL, *y = NULL;
HMAC_CTX ctx;
u32 cs;
u16 grp;
u8 conf[SHA256_DIGEST_LENGTH], *cruft = NULL, *ptr;
/* build up the ciphersuite: group | random_function | prf */
grp = htons(data->group_num);
ptr = (u8 *) &cs;
os_memcpy(ptr, &grp, sizeof(u16));
ptr += sizeof(u16);
*ptr = EAP_PWD_DEFAULT_RAND_FUNC;
ptr += sizeof(u8);
*ptr = EAP_PWD_DEFAULT_PRF;
/* each component of the cruft will be at most as big as the prime */
if (((cruft = os_malloc(BN_num_bytes(data->grp->prime))) == NULL) ||
((x = BN_new()) == NULL) || ((y = BN_new()) == NULL)) {
wpa_printf(MSG_INFO, "EAP-PWD (peer): allocation fail");
goto fin;
}
/*
* commit is H(k | peer_element | peer_scalar | server_element |
* server_scalar | ciphersuite)
*/
H_Init(&ctx);
/* k */
os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
BN_bn2bin(data->k, cruft);
H_Update(&ctx, cruft, BN_num_bytes(data->grp->prime));
/* peer element: x, y */
if (!EC_POINT_get_affine_coordinates_GFp(data->grp->group,
data->peer_element, x, y,
data->bnctx)) {
wpa_printf(MSG_INFO, "EAP-PWD (server): confirm point "
"assignment fail");
goto fin;
}
os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
BN_bn2bin(x, cruft);
H_Update(&ctx, cruft, BN_num_bytes(data->grp->prime));
os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
BN_bn2bin(y, cruft);
H_Update(&ctx, cruft, BN_num_bytes(data->grp->prime));
/* peer scalar */
os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
BN_bn2bin(data->peer_scalar, cruft);
H_Update(&ctx, cruft, BN_num_bytes(data->grp->order));
/* server element: x, y */
if (!EC_POINT_get_affine_coordinates_GFp(data->grp->group,
data->my_element, x, y,
data->bnctx)) {
wpa_printf(MSG_INFO, "EAP-PWD (server): confirm point "
"assignment fail");
goto fin;
}
os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
BN_bn2bin(x, cruft);
H_Update(&ctx, cruft, BN_num_bytes(data->grp->prime));
os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
BN_bn2bin(y, cruft);
H_Update(&ctx, cruft, BN_num_bytes(data->grp->prime));
/* server scalar */
os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
BN_bn2bin(data->my_scalar, cruft);
H_Update(&ctx, cruft, BN_num_bytes(data->grp->order));
/* ciphersuite */
os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
H_Update(&ctx, (u8 *)&cs, sizeof(u32));
/* all done */
H_Final(&ctx, conf);
ptr = (u8 *) payload;
if (os_memcmp(conf, ptr, SHA256_DIGEST_LENGTH)) {
wpa_printf(MSG_INFO, "EAP-PWD (server): confirm did not "
"verify");
goto fin;
}
wpa_printf(MSG_DEBUG, "EAP-pwd (server): confirm verified");
if (compute_keys(data->grp, data->bnctx, data->k,
data->peer_scalar, data->my_scalar, conf,
data->my_confirm, &cs, data->msk, data->emsk) < 0)
eap_pwd_state(data, FAILURE);
else
eap_pwd_state(data, SUCCESS);
fin:
os_free(cruft);
BN_free(x);
BN_free(y);
}
static void eap_pwd_process(struct eap_sm *sm, void *priv,
struct wpabuf *respData)
{
struct eap_pwd_data *data = priv;
const u8 *pos;
size_t len;
u8 exch;
pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_PWD, respData, &len);
if ((pos == NULL) || (len < 1)) {
wpa_printf(MSG_INFO, "Bad EAP header! pos %s and len = %d",
(pos == NULL) ? "is NULL" : "is not NULL",
(int) len);
return;
}
exch = *pos & 0x3f;
switch (exch) {
case EAP_PWD_OPCODE_ID_EXCH:
eap_pwd_process_id_resp(sm, data, pos + 1, len - 1);
break;
case EAP_PWD_OPCODE_COMMIT_EXCH:
eap_pwd_process_commit_resp(sm, data, pos + 1, len - 1);
break;
case EAP_PWD_OPCODE_CONFIRM_EXCH:
eap_pwd_process_confirm_resp(sm, data, pos + 1, len - 1);
break;
}
}
static u8 * eap_pwd_getkey(struct eap_sm *sm, void *priv, size_t *len)
{
struct eap_pwd_data *data = priv;
u8 *key;
if (data->state != SUCCESS)
return NULL;
key = os_malloc(EAP_MSK_LEN);
if (key == NULL)
return NULL;
os_memcpy(key, data->msk, EAP_MSK_LEN);
*len = EAP_MSK_LEN;
return key;
}
static u8 * eap_pwd_get_emsk(struct eap_sm *sm, void *priv, size_t *len)
{
struct eap_pwd_data *data = priv;
u8 *key;
if (data->state != SUCCESS)
return NULL;
key = os_malloc(EAP_EMSK_LEN);
if (key == NULL)
return NULL;
os_memcpy(key, data->emsk, EAP_EMSK_LEN);
*len = EAP_EMSK_LEN;
return key;
}
static Boolean eap_pwd_is_success(struct eap_sm *sm, void *priv)
{
struct eap_pwd_data *data = priv;
return data->state == SUCCESS;
}
static Boolean eap_pwd_is_done(struct eap_sm *sm, void *priv)
{
struct eap_pwd_data *data = priv;
return (data->state == SUCCESS) || (data->state == FAILURE);
}
int eap_server_pwd_register(void)
{
struct eap_method *eap;
int ret;
struct timeval tp;
struct timezone tz;
u32 sr;
EVP_add_digest(EVP_sha256());
sr = 0xdeaddada;
(void) gettimeofday(&tp, &tz);
sr ^= (tp.tv_sec ^ tp.tv_usec);
srandom(sr);
eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION,
EAP_VENDOR_IETF, EAP_TYPE_PWD,
"PWD");
if (eap == NULL)
return -1;
eap->init = eap_pwd_init;
eap->reset = eap_pwd_reset;
eap->buildReq = eap_pwd_build_req;
eap->check = eap_pwd_check;
eap->process = eap_pwd_process;
eap->isDone = eap_pwd_is_done;
eap->getKey = eap_pwd_getkey;
eap->get_emsk = eap_pwd_get_emsk;
eap->isSuccess = eap_pwd_is_success;
ret = eap_server_method_register(eap);
if (ret)
eap_server_method_free(eap);
return ret;
}

View file

@ -0,0 +1,543 @@
/*
* hostapd / EAP-SAKE (RFC 4763) server
* Copyright (c) 2006-2007, Jouni Malinen <j@w1.fi>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* Alternatively, this software may be distributed under the terms of BSD
* license.
*
* See README and COPYING for more details.
*/
#include "includes.h"
#include "common.h"
#include "crypto/random.h"
#include "eap_server/eap_i.h"
#include "eap_common/eap_sake_common.h"
struct eap_sake_data {
enum { IDENTITY, CHALLENGE, CONFIRM, SUCCESS, FAILURE } state;
u8 rand_s[EAP_SAKE_RAND_LEN];
u8 rand_p[EAP_SAKE_RAND_LEN];
struct {
u8 auth[EAP_SAKE_TEK_AUTH_LEN];
u8 cipher[EAP_SAKE_TEK_CIPHER_LEN];
} tek;
u8 msk[EAP_MSK_LEN];
u8 emsk[EAP_EMSK_LEN];
u8 session_id;
u8 *peerid;
size_t peerid_len;
u8 *serverid;
size_t serverid_len;
};
static const char * eap_sake_state_txt(int state)
{
switch (state) {
case IDENTITY:
return "IDENTITY";
case CHALLENGE:
return "CHALLENGE";
case CONFIRM:
return "CONFIRM";
case SUCCESS:
return "SUCCESS";
case FAILURE:
return "FAILURE";
default:
return "?";
}
}
static void eap_sake_state(struct eap_sake_data *data, int state)
{
wpa_printf(MSG_DEBUG, "EAP-SAKE: %s -> %s",
eap_sake_state_txt(data->state),
eap_sake_state_txt(state));
data->state = state;
}
static void * eap_sake_init(struct eap_sm *sm)
{
struct eap_sake_data *data;
data = os_zalloc(sizeof(*data));
if (data == NULL)
return NULL;
data->state = CHALLENGE;
if (os_get_random(&data->session_id, 1)) {
wpa_printf(MSG_ERROR, "EAP-SAKE: Failed to get random data");
os_free(data);
return NULL;
}
wpa_printf(MSG_DEBUG, "EAP-SAKE: Initialized Session ID %d",
data->session_id);
/* TODO: add support for configuring SERVERID */
data->serverid = (u8 *) os_strdup("hostapd");
if (data->serverid)
data->serverid_len = os_strlen((char *) data->serverid);
return data;
}
static void eap_sake_reset(struct eap_sm *sm, void *priv)
{
struct eap_sake_data *data = priv;
os_free(data->serverid);
os_free(data->peerid);
os_free(data);
}
static struct wpabuf * eap_sake_build_msg(struct eap_sake_data *data,
u8 id, size_t length, u8 subtype)
{
struct eap_sake_hdr *sake;
struct wpabuf *msg;
size_t plen;
plen = sizeof(struct eap_sake_hdr) + length;
msg = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_SAKE, plen,
EAP_CODE_REQUEST, id);
if (msg == NULL) {
wpa_printf(MSG_ERROR, "EAP-SAKE: Failed to allocate memory "
"request");
return NULL;
}
sake = wpabuf_put(msg, sizeof(*sake));
sake->version = EAP_SAKE_VERSION;
sake->session_id = data->session_id;
sake->subtype = subtype;
return msg;
}
static struct wpabuf * eap_sake_build_identity(struct eap_sm *sm,
struct eap_sake_data *data,
u8 id)
{
struct wpabuf *msg;
size_t plen;
wpa_printf(MSG_DEBUG, "EAP-SAKE: Request/Identity");
plen = 4;
if (data->serverid)
plen += 2 + data->serverid_len;
msg = eap_sake_build_msg(data, id, plen, EAP_SAKE_SUBTYPE_IDENTITY);
if (msg == NULL) {
data->state = FAILURE;
return NULL;
}
wpa_printf(MSG_DEBUG, "EAP-SAKE: * AT_PERM_ID_REQ");
eap_sake_add_attr(msg, EAP_SAKE_AT_PERM_ID_REQ, NULL, 2);
if (data->serverid) {
wpa_printf(MSG_DEBUG, "EAP-SAKE: * AT_SERVERID");
eap_sake_add_attr(msg, EAP_SAKE_AT_SERVERID,
data->serverid, data->serverid_len);
}
return msg;
}
static struct wpabuf * eap_sake_build_challenge(struct eap_sm *sm,
struct eap_sake_data *data,
u8 id)
{
struct wpabuf *msg;
size_t plen;
wpa_printf(MSG_DEBUG, "EAP-SAKE: Request/Challenge");
if (random_get_bytes(data->rand_s, EAP_SAKE_RAND_LEN)) {
wpa_printf(MSG_ERROR, "EAP-SAKE: Failed to get random data");
data->state = FAILURE;
return NULL;
}
wpa_hexdump(MSG_MSGDUMP, "EAP-SAKE: RAND_S (server rand)",
data->rand_s, EAP_SAKE_RAND_LEN);
plen = 2 + EAP_SAKE_RAND_LEN;
if (data->serverid)
plen += 2 + data->serverid_len;
msg = eap_sake_build_msg(data, id, plen, EAP_SAKE_SUBTYPE_CHALLENGE);
if (msg == NULL) {
data->state = FAILURE;
return NULL;
}
wpa_printf(MSG_DEBUG, "EAP-SAKE: * AT_RAND_S");
eap_sake_add_attr(msg, EAP_SAKE_AT_RAND_S,
data->rand_s, EAP_SAKE_RAND_LEN);
if (data->serverid) {
wpa_printf(MSG_DEBUG, "EAP-SAKE: * AT_SERVERID");
eap_sake_add_attr(msg, EAP_SAKE_AT_SERVERID,
data->serverid, data->serverid_len);
}
return msg;
}
static struct wpabuf * eap_sake_build_confirm(struct eap_sm *sm,
struct eap_sake_data *data,
u8 id)
{
struct wpabuf *msg;
u8 *mic;
wpa_printf(MSG_DEBUG, "EAP-SAKE: Request/Confirm");
msg = eap_sake_build_msg(data, id, 2 + EAP_SAKE_MIC_LEN,
EAP_SAKE_SUBTYPE_CONFIRM);
if (msg == NULL) {
data->state = FAILURE;
return NULL;
}
wpa_printf(MSG_DEBUG, "EAP-SAKE: * AT_MIC_S");
wpabuf_put_u8(msg, EAP_SAKE_AT_MIC_S);
wpabuf_put_u8(msg, 2 + EAP_SAKE_MIC_LEN);
mic = wpabuf_put(msg, EAP_SAKE_MIC_LEN);
if (eap_sake_compute_mic(data->tek.auth, data->rand_s, data->rand_p,
data->serverid, data->serverid_len,
data->peerid, data->peerid_len, 0,
wpabuf_head(msg), wpabuf_len(msg), mic, mic))
{
wpa_printf(MSG_INFO, "EAP-SAKE: Failed to compute MIC");
data->state = FAILURE;
os_free(msg);
return NULL;
}
return msg;
}
static struct wpabuf * eap_sake_buildReq(struct eap_sm *sm, void *priv, u8 id)
{
struct eap_sake_data *data = priv;
switch (data->state) {
case IDENTITY:
return eap_sake_build_identity(sm, data, id);
case CHALLENGE:
return eap_sake_build_challenge(sm, data, id);
case CONFIRM:
return eap_sake_build_confirm(sm, data, id);
default:
wpa_printf(MSG_DEBUG, "EAP-SAKE: Unknown state %d in buildReq",
data->state);
break;
}
return NULL;
}
static Boolean eap_sake_check(struct eap_sm *sm, void *priv,
struct wpabuf *respData)
{
struct eap_sake_data *data = priv;
struct eap_sake_hdr *resp;
size_t len;
u8 version, session_id, subtype;
const u8 *pos;
pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_SAKE, respData, &len);
if (pos == NULL || len < sizeof(struct eap_sake_hdr)) {
wpa_printf(MSG_INFO, "EAP-SAKE: Invalid frame");
return TRUE;
}
resp = (struct eap_sake_hdr *) pos;
version = resp->version;
session_id = resp->session_id;
subtype = resp->subtype;
if (version != EAP_SAKE_VERSION) {
wpa_printf(MSG_INFO, "EAP-SAKE: Unknown version %d", version);
return TRUE;
}
if (session_id != data->session_id) {
wpa_printf(MSG_INFO, "EAP-SAKE: Session ID mismatch (%d,%d)",
session_id, data->session_id);
return TRUE;
}
wpa_printf(MSG_DEBUG, "EAP-SAKE: Received frame: subtype=%d", subtype);
if (data->state == IDENTITY && subtype == EAP_SAKE_SUBTYPE_IDENTITY)
return FALSE;
if (data->state == CHALLENGE && subtype == EAP_SAKE_SUBTYPE_CHALLENGE)
return FALSE;
if (data->state == CONFIRM && subtype == EAP_SAKE_SUBTYPE_CONFIRM)
return FALSE;
if (subtype == EAP_SAKE_SUBTYPE_AUTH_REJECT)
return FALSE;
wpa_printf(MSG_INFO, "EAP-SAKE: Unexpected subtype=%d in state=%d",
subtype, data->state);
return TRUE;
}
static void eap_sake_process_identity(struct eap_sm *sm,
struct eap_sake_data *data,
const struct wpabuf *respData,
const u8 *payload, size_t payloadlen)
{
if (data->state != IDENTITY)
return;
wpa_printf(MSG_DEBUG, "EAP-SAKE: Received Response/Identity");
/* TODO: update identity and select new user data */
eap_sake_state(data, CHALLENGE);
}
static void eap_sake_process_challenge(struct eap_sm *sm,
struct eap_sake_data *data,
const struct wpabuf *respData,
const u8 *payload, size_t payloadlen)
{
struct eap_sake_parse_attr attr;
u8 mic_p[EAP_SAKE_MIC_LEN];
if (data->state != CHALLENGE)
return;
wpa_printf(MSG_DEBUG, "EAP-SAKE: Received Response/Challenge");
if (eap_sake_parse_attributes(payload, payloadlen, &attr))
return;
if (!attr.rand_p || !attr.mic_p) {
wpa_printf(MSG_INFO, "EAP-SAKE: Response/Challenge did not "
"include AT_RAND_P or AT_MIC_P");
return;
}
os_memcpy(data->rand_p, attr.rand_p, EAP_SAKE_RAND_LEN);
os_free(data->peerid);
data->peerid = NULL;
data->peerid_len = 0;
if (attr.peerid) {
data->peerid = os_malloc(attr.peerid_len);
if (data->peerid == NULL)
return;
os_memcpy(data->peerid, attr.peerid, attr.peerid_len);
data->peerid_len = attr.peerid_len;
}
if (sm->user == NULL || sm->user->password == NULL ||
sm->user->password_len != 2 * EAP_SAKE_ROOT_SECRET_LEN) {
wpa_printf(MSG_INFO, "EAP-SAKE: Plaintext password with "
"%d-byte key not configured",
2 * EAP_SAKE_ROOT_SECRET_LEN);
data->state = FAILURE;
return;
}
eap_sake_derive_keys(sm->user->password,
sm->user->password + EAP_SAKE_ROOT_SECRET_LEN,
data->rand_s, data->rand_p,
(u8 *) &data->tek, data->msk, data->emsk);
eap_sake_compute_mic(data->tek.auth, data->rand_s, data->rand_p,
data->serverid, data->serverid_len,
data->peerid, data->peerid_len, 1,
wpabuf_head(respData), wpabuf_len(respData),
attr.mic_p, mic_p);
if (os_memcmp(attr.mic_p, mic_p, EAP_SAKE_MIC_LEN) != 0) {
wpa_printf(MSG_INFO, "EAP-SAKE: Incorrect AT_MIC_P");
eap_sake_state(data, FAILURE);
return;
}
eap_sake_state(data, CONFIRM);
}
static void eap_sake_process_confirm(struct eap_sm *sm,
struct eap_sake_data *data,
const struct wpabuf *respData,
const u8 *payload, size_t payloadlen)
{
struct eap_sake_parse_attr attr;
u8 mic_p[EAP_SAKE_MIC_LEN];
if (data->state != CONFIRM)
return;
wpa_printf(MSG_DEBUG, "EAP-SAKE: Received Response/Confirm");
if (eap_sake_parse_attributes(payload, payloadlen, &attr))
return;
if (!attr.mic_p) {
wpa_printf(MSG_INFO, "EAP-SAKE: Response/Confirm did not "
"include AT_MIC_P");
return;
}
eap_sake_compute_mic(data->tek.auth, data->rand_s, data->rand_p,
data->serverid, data->serverid_len,
data->peerid, data->peerid_len, 1,
wpabuf_head(respData), wpabuf_len(respData),
attr.mic_p, mic_p);
if (os_memcmp(attr.mic_p, mic_p, EAP_SAKE_MIC_LEN) != 0) {
wpa_printf(MSG_INFO, "EAP-SAKE: Incorrect AT_MIC_P");
eap_sake_state(data, FAILURE);
} else
eap_sake_state(data, SUCCESS);
}
static void eap_sake_process_auth_reject(struct eap_sm *sm,
struct eap_sake_data *data,
const struct wpabuf *respData,
const u8 *payload, size_t payloadlen)
{
wpa_printf(MSG_DEBUG, "EAP-SAKE: Received Response/Auth-Reject");
eap_sake_state(data, FAILURE);
}
static void eap_sake_process(struct eap_sm *sm, void *priv,
struct wpabuf *respData)
{
struct eap_sake_data *data = priv;
struct eap_sake_hdr *resp;
u8 subtype;
size_t len;
const u8 *pos, *end;
pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_SAKE, respData, &len);
if (pos == NULL || len < sizeof(struct eap_sake_hdr))
return;
resp = (struct eap_sake_hdr *) pos;
end = pos + len;
subtype = resp->subtype;
pos = (u8 *) (resp + 1);
wpa_hexdump(MSG_DEBUG, "EAP-SAKE: Received attributes",
pos, end - pos);
switch (subtype) {
case EAP_SAKE_SUBTYPE_IDENTITY:
eap_sake_process_identity(sm, data, respData, pos, end - pos);
break;
case EAP_SAKE_SUBTYPE_CHALLENGE:
eap_sake_process_challenge(sm, data, respData, pos, end - pos);
break;
case EAP_SAKE_SUBTYPE_CONFIRM:
eap_sake_process_confirm(sm, data, respData, pos, end - pos);
break;
case EAP_SAKE_SUBTYPE_AUTH_REJECT:
eap_sake_process_auth_reject(sm, data, respData, pos,
end - pos);
break;
}
}
static Boolean eap_sake_isDone(struct eap_sm *sm, void *priv)
{
struct eap_sake_data *data = priv;
return data->state == SUCCESS || data->state == FAILURE;
}
static u8 * eap_sake_getKey(struct eap_sm *sm, void *priv, size_t *len)
{
struct eap_sake_data *data = priv;
u8 *key;
if (data->state != SUCCESS)
return NULL;
key = os_malloc(EAP_MSK_LEN);
if (key == NULL)
return NULL;
os_memcpy(key, data->msk, EAP_MSK_LEN);
*len = EAP_MSK_LEN;
return key;
}
static u8 * eap_sake_get_emsk(struct eap_sm *sm, void *priv, size_t *len)
{
struct eap_sake_data *data = priv;
u8 *key;
if (data->state != SUCCESS)
return NULL;
key = os_malloc(EAP_EMSK_LEN);
if (key == NULL)
return NULL;
os_memcpy(key, data->emsk, EAP_EMSK_LEN);
*len = EAP_EMSK_LEN;
return key;
}
static Boolean eap_sake_isSuccess(struct eap_sm *sm, void *priv)
{
struct eap_sake_data *data = priv;
return data->state == SUCCESS;
}
int eap_server_sake_register(void)
{
struct eap_method *eap;
int ret;
eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION,
EAP_VENDOR_IETF, EAP_TYPE_SAKE, "SAKE");
if (eap == NULL)
return -1;
eap->init = eap_sake_init;
eap->reset = eap_sake_reset;
eap->buildReq = eap_sake_buildReq;
eap->check = eap_sake_check;
eap->process = eap_sake_process;
eap->isDone = eap_sake_isDone;
eap->getKey = eap_sake_getKey;
eap->isSuccess = eap_sake_isSuccess;
eap->get_emsk = eap_sake_get_emsk;
ret = eap_server_method_register(eap);
if (ret)
eap_server_method_free(eap);
return ret;
}

View file

@ -0,0 +1,798 @@
/*
* hostapd / EAP-SIM (RFC 4186)
* Copyright (c) 2005-2008, Jouni Malinen <j@w1.fi>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* Alternatively, this software may be distributed under the terms of BSD
* license.
*
* See README and COPYING for more details.
*/
#include "includes.h"
#include "common.h"
#include "crypto/random.h"
#include "eap_server/eap_i.h"
#include "eap_common/eap_sim_common.h"
#include "eap_server/eap_sim_db.h"
struct eap_sim_data {
u8 mk[EAP_SIM_MK_LEN];
u8 nonce_mt[EAP_SIM_NONCE_MT_LEN];
u8 nonce_s[EAP_SIM_NONCE_S_LEN];
u8 k_aut[EAP_SIM_K_AUT_LEN];
u8 k_encr[EAP_SIM_K_ENCR_LEN];
u8 msk[EAP_SIM_KEYING_DATA_LEN];
u8 emsk[EAP_EMSK_LEN];
u8 kc[EAP_SIM_MAX_CHAL][EAP_SIM_KC_LEN];
u8 sres[EAP_SIM_MAX_CHAL][EAP_SIM_SRES_LEN];
u8 rand[EAP_SIM_MAX_CHAL][GSM_RAND_LEN];
int num_chal;
enum {
START, CHALLENGE, REAUTH, NOTIFICATION, SUCCESS, FAILURE
} state;
char *next_pseudonym;
char *next_reauth_id;
u16 counter;
struct eap_sim_reauth *reauth;
u16 notification;
int use_result_ind;
};
static const char * eap_sim_state_txt(int state)
{
switch (state) {
case START:
return "START";
case CHALLENGE:
return "CHALLENGE";
case REAUTH:
return "REAUTH";
case SUCCESS:
return "SUCCESS";
case FAILURE:
return "FAILURE";
case NOTIFICATION:
return "NOTIFICATION";
default:
return "Unknown?!";
}
}
static void eap_sim_state(struct eap_sim_data *data, int state)
{
wpa_printf(MSG_DEBUG, "EAP-SIM: %s -> %s",
eap_sim_state_txt(data->state),
eap_sim_state_txt(state));
data->state = state;
}
static void * eap_sim_init(struct eap_sm *sm)
{
struct eap_sim_data *data;
if (sm->eap_sim_db_priv == NULL) {
wpa_printf(MSG_WARNING, "EAP-SIM: eap_sim_db not configured");
return NULL;
}
data = os_zalloc(sizeof(*data));
if (data == NULL)
return NULL;
data->state = START;
return data;
}
static void eap_sim_reset(struct eap_sm *sm, void *priv)
{
struct eap_sim_data *data = priv;
os_free(data->next_pseudonym);
os_free(data->next_reauth_id);
os_free(data);
}
static struct wpabuf * eap_sim_build_start(struct eap_sm *sm,
struct eap_sim_data *data, u8 id)
{
struct eap_sim_msg *msg;
u8 ver[2];
wpa_printf(MSG_DEBUG, "EAP-SIM: Generating Start");
msg = eap_sim_msg_init(EAP_CODE_REQUEST, id, EAP_TYPE_SIM,
EAP_SIM_SUBTYPE_START);
if (eap_sim_db_identity_known(sm->eap_sim_db_priv, sm->identity,
sm->identity_len)) {
wpa_printf(MSG_DEBUG, " AT_PERMANENT_ID_REQ");
eap_sim_msg_add(msg, EAP_SIM_AT_PERMANENT_ID_REQ, 0, NULL, 0);
} else {
/*
* RFC 4186, Chap. 4.2.4 recommends that identity from EAP is
* ignored and the SIM/Start is used to request the identity.
*/
wpa_printf(MSG_DEBUG, " AT_ANY_ID_REQ");
eap_sim_msg_add(msg, EAP_SIM_AT_ANY_ID_REQ, 0, NULL, 0);
}
wpa_printf(MSG_DEBUG, " AT_VERSION_LIST");
ver[0] = 0;
ver[1] = EAP_SIM_VERSION;
eap_sim_msg_add(msg, EAP_SIM_AT_VERSION_LIST, sizeof(ver),
ver, sizeof(ver));
return eap_sim_msg_finish(msg, NULL, NULL, 0);
}
static int eap_sim_build_encr(struct eap_sm *sm, struct eap_sim_data *data,
struct eap_sim_msg *msg, u16 counter,
const u8 *nonce_s)
{
os_free(data->next_pseudonym);
data->next_pseudonym =
eap_sim_db_get_next_pseudonym(sm->eap_sim_db_priv, 0);
os_free(data->next_reauth_id);
if (data->counter <= EAP_SIM_MAX_FAST_REAUTHS) {
data->next_reauth_id =
eap_sim_db_get_next_reauth_id(sm->eap_sim_db_priv, 0);
} else {
wpa_printf(MSG_DEBUG, "EAP-SIM: Max fast re-authentication "
"count exceeded - force full authentication");
data->next_reauth_id = NULL;
}
if (data->next_pseudonym == NULL && data->next_reauth_id == NULL &&
counter == 0 && nonce_s == NULL)
return 0;
wpa_printf(MSG_DEBUG, " AT_IV");
wpa_printf(MSG_DEBUG, " AT_ENCR_DATA");
eap_sim_msg_add_encr_start(msg, EAP_SIM_AT_IV, EAP_SIM_AT_ENCR_DATA);
if (counter > 0) {
wpa_printf(MSG_DEBUG, " *AT_COUNTER (%u)", counter);
eap_sim_msg_add(msg, EAP_SIM_AT_COUNTER, counter, NULL, 0);
}
if (nonce_s) {
wpa_printf(MSG_DEBUG, " *AT_NONCE_S");
eap_sim_msg_add(msg, EAP_SIM_AT_NONCE_S, 0, nonce_s,
EAP_SIM_NONCE_S_LEN);
}
if (data->next_pseudonym) {
wpa_printf(MSG_DEBUG, " *AT_NEXT_PSEUDONYM (%s)",
data->next_pseudonym);
eap_sim_msg_add(msg, EAP_SIM_AT_NEXT_PSEUDONYM,
os_strlen(data->next_pseudonym),
(u8 *) data->next_pseudonym,
os_strlen(data->next_pseudonym));
}
if (data->next_reauth_id) {
wpa_printf(MSG_DEBUG, " *AT_NEXT_REAUTH_ID (%s)",
data->next_reauth_id);
eap_sim_msg_add(msg, EAP_SIM_AT_NEXT_REAUTH_ID,
os_strlen(data->next_reauth_id),
(u8 *) data->next_reauth_id,
os_strlen(data->next_reauth_id));
}
if (eap_sim_msg_add_encr_end(msg, data->k_encr, EAP_SIM_AT_PADDING)) {
wpa_printf(MSG_WARNING, "EAP-SIM: Failed to encrypt "
"AT_ENCR_DATA");
return -1;
}
return 0;
}
static struct wpabuf * eap_sim_build_challenge(struct eap_sm *sm,
struct eap_sim_data *data,
u8 id)
{
struct eap_sim_msg *msg;
wpa_printf(MSG_DEBUG, "EAP-SIM: Generating Challenge");
msg = eap_sim_msg_init(EAP_CODE_REQUEST, id, EAP_TYPE_SIM,
EAP_SIM_SUBTYPE_CHALLENGE);
wpa_printf(MSG_DEBUG, " AT_RAND");
eap_sim_msg_add(msg, EAP_SIM_AT_RAND, 0, (u8 *) data->rand,
data->num_chal * GSM_RAND_LEN);
if (eap_sim_build_encr(sm, data, msg, 0, NULL)) {
eap_sim_msg_free(msg);
return NULL;
}
if (sm->eap_sim_aka_result_ind) {
wpa_printf(MSG_DEBUG, " AT_RESULT_IND");
eap_sim_msg_add(msg, EAP_SIM_AT_RESULT_IND, 0, NULL, 0);
}
wpa_printf(MSG_DEBUG, " AT_MAC");
eap_sim_msg_add_mac(msg, EAP_SIM_AT_MAC);
return eap_sim_msg_finish(msg, data->k_aut, data->nonce_mt,
EAP_SIM_NONCE_MT_LEN);
}
static struct wpabuf * eap_sim_build_reauth(struct eap_sm *sm,
struct eap_sim_data *data, u8 id)
{
struct eap_sim_msg *msg;
wpa_printf(MSG_DEBUG, "EAP-SIM: Generating Re-authentication");
if (random_get_bytes(data->nonce_s, EAP_SIM_NONCE_S_LEN))
return NULL;
wpa_hexdump_key(MSG_MSGDUMP, "EAP-SIM: NONCE_S",
data->nonce_s, EAP_SIM_NONCE_S_LEN);
eap_sim_derive_keys(data->mk, data->k_encr, data->k_aut, data->msk,
data->emsk);
eap_sim_derive_keys_reauth(data->counter, sm->identity,
sm->identity_len, data->nonce_s, data->mk,
data->msk, data->emsk);
msg = eap_sim_msg_init(EAP_CODE_REQUEST, id, EAP_TYPE_SIM,
EAP_SIM_SUBTYPE_REAUTHENTICATION);
if (eap_sim_build_encr(sm, data, msg, data->counter, data->nonce_s)) {
eap_sim_msg_free(msg);
return NULL;
}
if (sm->eap_sim_aka_result_ind) {
wpa_printf(MSG_DEBUG, " AT_RESULT_IND");
eap_sim_msg_add(msg, EAP_SIM_AT_RESULT_IND, 0, NULL, 0);
}
wpa_printf(MSG_DEBUG, " AT_MAC");
eap_sim_msg_add_mac(msg, EAP_SIM_AT_MAC);
return eap_sim_msg_finish(msg, data->k_aut, NULL, 0);
}
static struct wpabuf * eap_sim_build_notification(struct eap_sm *sm,
struct eap_sim_data *data,
u8 id)
{
struct eap_sim_msg *msg;
wpa_printf(MSG_DEBUG, "EAP-SIM: Generating Notification");
msg = eap_sim_msg_init(EAP_CODE_REQUEST, id, EAP_TYPE_SIM,
EAP_SIM_SUBTYPE_NOTIFICATION);
wpa_printf(MSG_DEBUG, " AT_NOTIFICATION (%d)", data->notification);
eap_sim_msg_add(msg, EAP_SIM_AT_NOTIFICATION, data->notification,
NULL, 0);
if (data->use_result_ind) {
if (data->reauth) {
wpa_printf(MSG_DEBUG, " AT_IV");
wpa_printf(MSG_DEBUG, " AT_ENCR_DATA");
eap_sim_msg_add_encr_start(msg, EAP_SIM_AT_IV,
EAP_SIM_AT_ENCR_DATA);
wpa_printf(MSG_DEBUG, " *AT_COUNTER (%u)",
data->counter);
eap_sim_msg_add(msg, EAP_SIM_AT_COUNTER, data->counter,
NULL, 0);
if (eap_sim_msg_add_encr_end(msg, data->k_encr,
EAP_SIM_AT_PADDING)) {
wpa_printf(MSG_WARNING, "EAP-SIM: Failed to "
"encrypt AT_ENCR_DATA");
eap_sim_msg_free(msg);
return NULL;
}
}
wpa_printf(MSG_DEBUG, " AT_MAC");
eap_sim_msg_add_mac(msg, EAP_SIM_AT_MAC);
}
return eap_sim_msg_finish(msg, data->k_aut, NULL, 0);
}
static struct wpabuf * eap_sim_buildReq(struct eap_sm *sm, void *priv, u8 id)
{
struct eap_sim_data *data = priv;
switch (data->state) {
case START:
return eap_sim_build_start(sm, data, id);
case CHALLENGE:
return eap_sim_build_challenge(sm, data, id);
case REAUTH:
return eap_sim_build_reauth(sm, data, id);
case NOTIFICATION:
return eap_sim_build_notification(sm, data, id);
default:
wpa_printf(MSG_DEBUG, "EAP-SIM: Unknown state %d in "
"buildReq", data->state);
break;
}
return NULL;
}
static Boolean eap_sim_check(struct eap_sm *sm, void *priv,
struct wpabuf *respData)
{
struct eap_sim_data *data = priv;
const u8 *pos;
size_t len;
u8 subtype;
pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_SIM, respData, &len);
if (pos == NULL || len < 3) {
wpa_printf(MSG_INFO, "EAP-SIM: Invalid frame");
return TRUE;
}
subtype = *pos;
if (subtype == EAP_SIM_SUBTYPE_CLIENT_ERROR)
return FALSE;
switch (data->state) {
case START:
if (subtype != EAP_SIM_SUBTYPE_START) {
wpa_printf(MSG_INFO, "EAP-SIM: Unexpected response "
"subtype %d", subtype);
return TRUE;
}
break;
case CHALLENGE:
if (subtype != EAP_SIM_SUBTYPE_CHALLENGE) {
wpa_printf(MSG_INFO, "EAP-SIM: Unexpected response "
"subtype %d", subtype);
return TRUE;
}
break;
case REAUTH:
if (subtype != EAP_SIM_SUBTYPE_REAUTHENTICATION) {
wpa_printf(MSG_INFO, "EAP-SIM: Unexpected response "
"subtype %d", subtype);
return TRUE;
}
break;
case NOTIFICATION:
if (subtype != EAP_SIM_SUBTYPE_NOTIFICATION) {
wpa_printf(MSG_INFO, "EAP-SIM: Unexpected response "
"subtype %d", subtype);
return TRUE;
}
break;
default:
wpa_printf(MSG_INFO, "EAP-SIM: Unexpected state (%d) for "
"processing a response", data->state);
return TRUE;
}
return FALSE;
}
static int eap_sim_supported_ver(struct eap_sim_data *data, int version)
{
return version == EAP_SIM_VERSION;
}
static void eap_sim_process_start(struct eap_sm *sm,
struct eap_sim_data *data,
struct wpabuf *respData,
struct eap_sim_attrs *attr)
{
const u8 *identity;
size_t identity_len;
u8 ver_list[2];
wpa_printf(MSG_DEBUG, "EAP-SIM: Receive start response");
if (attr->identity) {
os_free(sm->identity);
sm->identity = os_malloc(attr->identity_len);
if (sm->identity) {
os_memcpy(sm->identity, attr->identity,
attr->identity_len);
sm->identity_len = attr->identity_len;
}
}
identity = NULL;
identity_len = 0;
if (sm->identity && sm->identity_len > 0 &&
sm->identity[0] == EAP_SIM_PERMANENT_PREFIX) {
identity = sm->identity;
identity_len = sm->identity_len;
} else {
identity = eap_sim_db_get_permanent(sm->eap_sim_db_priv,
sm->identity,
sm->identity_len,
&identity_len);
if (identity == NULL) {
data->reauth = eap_sim_db_get_reauth_entry(
sm->eap_sim_db_priv, sm->identity,
sm->identity_len);
if (data->reauth) {
wpa_printf(MSG_DEBUG, "EAP-SIM: Using fast "
"re-authentication");
identity = data->reauth->identity;
identity_len = data->reauth->identity_len;
data->counter = data->reauth->counter;
os_memcpy(data->mk, data->reauth->mk,
EAP_SIM_MK_LEN);
}
}
}
if (identity == NULL) {
wpa_printf(MSG_DEBUG, "EAP-SIM: Could not get proper permanent"
" user name");
eap_sim_state(data, FAILURE);
return;
}
wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM: Identity",
identity, identity_len);
if (data->reauth) {
eap_sim_state(data, REAUTH);
return;
}
if (attr->nonce_mt == NULL || attr->selected_version < 0) {
wpa_printf(MSG_DEBUG, "EAP-SIM: Start/Response missing "
"required attributes");
eap_sim_state(data, FAILURE);
return;
}
if (!eap_sim_supported_ver(data, attr->selected_version)) {
wpa_printf(MSG_DEBUG, "EAP-SIM: Peer selected unsupported "
"version %d", attr->selected_version);
eap_sim_state(data, FAILURE);
return;
}
data->counter = 0; /* reset re-auth counter since this is full auth */
data->reauth = NULL;
data->num_chal = eap_sim_db_get_gsm_triplets(
sm->eap_sim_db_priv, identity, identity_len,
EAP_SIM_MAX_CHAL,
(u8 *) data->rand, (u8 *) data->kc, (u8 *) data->sres, sm);
if (data->num_chal == EAP_SIM_DB_PENDING) {
wpa_printf(MSG_DEBUG, "EAP-SIM: GSM authentication triplets "
"not yet available - pending request");
sm->method_pending = METHOD_PENDING_WAIT;
return;
}
if (data->num_chal < 2) {
wpa_printf(MSG_INFO, "EAP-SIM: Failed to get GSM "
"authentication triplets for the peer");
eap_sim_state(data, FAILURE);
return;
}
identity_len = sm->identity_len;
while (identity_len > 0 && sm->identity[identity_len - 1] == '\0') {
wpa_printf(MSG_DEBUG, "EAP-SIM: Workaround - drop last null "
"character from identity");
identity_len--;
}
wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM: Identity for MK derivation",
sm->identity, identity_len);
os_memcpy(data->nonce_mt, attr->nonce_mt, EAP_SIM_NONCE_MT_LEN);
WPA_PUT_BE16(ver_list, EAP_SIM_VERSION);
eap_sim_derive_mk(sm->identity, identity_len, attr->nonce_mt,
attr->selected_version, ver_list, sizeof(ver_list),
data->num_chal, (const u8 *) data->kc, data->mk);
eap_sim_derive_keys(data->mk, data->k_encr, data->k_aut, data->msk,
data->emsk);
eap_sim_state(data, CHALLENGE);
}
static void eap_sim_process_challenge(struct eap_sm *sm,
struct eap_sim_data *data,
struct wpabuf *respData,
struct eap_sim_attrs *attr)
{
const u8 *identity;
size_t identity_len;
if (attr->mac == NULL ||
eap_sim_verify_mac(data->k_aut, respData, attr->mac,
(u8 *) data->sres,
data->num_chal * EAP_SIM_SRES_LEN)) {
wpa_printf(MSG_WARNING, "EAP-SIM: Challenge message "
"did not include valid AT_MAC");
eap_sim_state(data, FAILURE);
return;
}
wpa_printf(MSG_DEBUG, "EAP-SIM: Challenge response includes the "
"correct AT_MAC");
if (sm->eap_sim_aka_result_ind && attr->result_ind) {
data->use_result_ind = 1;
data->notification = EAP_SIM_SUCCESS;
eap_sim_state(data, NOTIFICATION);
} else
eap_sim_state(data, SUCCESS);
identity = eap_sim_db_get_permanent(sm->eap_sim_db_priv, sm->identity,
sm->identity_len, &identity_len);
if (identity == NULL) {
identity = sm->identity;
identity_len = sm->identity_len;
}
if (data->next_pseudonym) {
eap_sim_db_add_pseudonym(sm->eap_sim_db_priv, identity,
identity_len,
data->next_pseudonym);
data->next_pseudonym = NULL;
}
if (data->next_reauth_id) {
eap_sim_db_add_reauth(sm->eap_sim_db_priv, identity,
identity_len,
data->next_reauth_id, data->counter + 1,
data->mk);
data->next_reauth_id = NULL;
}
}
static void eap_sim_process_reauth(struct eap_sm *sm,
struct eap_sim_data *data,
struct wpabuf *respData,
struct eap_sim_attrs *attr)
{
struct eap_sim_attrs eattr;
u8 *decrypted = NULL;
const u8 *identity, *id2;
size_t identity_len, id2_len;
if (attr->mac == NULL ||
eap_sim_verify_mac(data->k_aut, respData, attr->mac, data->nonce_s,
EAP_SIM_NONCE_S_LEN)) {
wpa_printf(MSG_WARNING, "EAP-SIM: Re-authentication message "
"did not include valid AT_MAC");
goto fail;
}
if (attr->encr_data == NULL || attr->iv == NULL) {
wpa_printf(MSG_WARNING, "EAP-SIM: Reauthentication "
"message did not include encrypted data");
goto fail;
}
decrypted = eap_sim_parse_encr(data->k_encr, attr->encr_data,
attr->encr_data_len, attr->iv, &eattr,
0);
if (decrypted == NULL) {
wpa_printf(MSG_WARNING, "EAP-SIM: Failed to parse encrypted "
"data from reauthentication message");
goto fail;
}
if (eattr.counter != data->counter) {
wpa_printf(MSG_WARNING, "EAP-SIM: Re-authentication message "
"used incorrect counter %u, expected %u",
eattr.counter, data->counter);
goto fail;
}
os_free(decrypted);
decrypted = NULL;
wpa_printf(MSG_DEBUG, "EAP-SIM: Re-authentication response includes "
"the correct AT_MAC");
if (sm->eap_sim_aka_result_ind && attr->result_ind) {
data->use_result_ind = 1;
data->notification = EAP_SIM_SUCCESS;
eap_sim_state(data, NOTIFICATION);
} else
eap_sim_state(data, SUCCESS);
if (data->reauth) {
identity = data->reauth->identity;
identity_len = data->reauth->identity_len;
} else {
identity = sm->identity;
identity_len = sm->identity_len;
}
id2 = eap_sim_db_get_permanent(sm->eap_sim_db_priv, identity,
identity_len, &id2_len);
if (id2) {
identity = id2;
identity_len = id2_len;
}
if (data->next_pseudonym) {
eap_sim_db_add_pseudonym(sm->eap_sim_db_priv, identity,
identity_len, data->next_pseudonym);
data->next_pseudonym = NULL;
}
if (data->next_reauth_id) {
eap_sim_db_add_reauth(sm->eap_sim_db_priv, identity,
identity_len, data->next_reauth_id,
data->counter + 1, data->mk);
data->next_reauth_id = NULL;
} else {
eap_sim_db_remove_reauth(sm->eap_sim_db_priv, data->reauth);
data->reauth = NULL;
}
return;
fail:
eap_sim_state(data, FAILURE);
eap_sim_db_remove_reauth(sm->eap_sim_db_priv, data->reauth);
data->reauth = NULL;
os_free(decrypted);
}
static void eap_sim_process_client_error(struct eap_sm *sm,
struct eap_sim_data *data,
struct wpabuf *respData,
struct eap_sim_attrs *attr)
{
wpa_printf(MSG_DEBUG, "EAP-SIM: Client reported error %d",
attr->client_error_code);
if (data->notification == EAP_SIM_SUCCESS && data->use_result_ind)
eap_sim_state(data, SUCCESS);
else
eap_sim_state(data, FAILURE);
}
static void eap_sim_process_notification(struct eap_sm *sm,
struct eap_sim_data *data,
struct wpabuf *respData,
struct eap_sim_attrs *attr)
{
wpa_printf(MSG_DEBUG, "EAP-SIM: Client replied to notification");
if (data->notification == EAP_SIM_SUCCESS && data->use_result_ind)
eap_sim_state(data, SUCCESS);
else
eap_sim_state(data, FAILURE);
}
static void eap_sim_process(struct eap_sm *sm, void *priv,
struct wpabuf *respData)
{
struct eap_sim_data *data = priv;
const u8 *pos, *end;
u8 subtype;
size_t len;
struct eap_sim_attrs attr;
pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_SIM, respData, &len);
if (pos == NULL || len < 3)
return;
end = pos + len;
subtype = *pos;
pos += 3;
if (eap_sim_parse_attr(pos, end, &attr, 0, 0)) {
wpa_printf(MSG_DEBUG, "EAP-SIM: Failed to parse attributes");
eap_sim_state(data, FAILURE);
return;
}
if (subtype == EAP_SIM_SUBTYPE_CLIENT_ERROR) {
eap_sim_process_client_error(sm, data, respData, &attr);
return;
}
switch (data->state) {
case START:
eap_sim_process_start(sm, data, respData, &attr);
break;
case CHALLENGE:
eap_sim_process_challenge(sm, data, respData, &attr);
break;
case REAUTH:
eap_sim_process_reauth(sm, data, respData, &attr);
break;
case NOTIFICATION:
eap_sim_process_notification(sm, data, respData, &attr);
break;
default:
wpa_printf(MSG_DEBUG, "EAP-SIM: Unknown state %d in "
"process", data->state);
break;
}
}
static Boolean eap_sim_isDone(struct eap_sm *sm, void *priv)
{
struct eap_sim_data *data = priv;
return data->state == SUCCESS || data->state == FAILURE;
}
static u8 * eap_sim_getKey(struct eap_sm *sm, void *priv, size_t *len)
{
struct eap_sim_data *data = priv;
u8 *key;
if (data->state != SUCCESS)
return NULL;
key = os_malloc(EAP_SIM_KEYING_DATA_LEN);
if (key == NULL)
return NULL;
os_memcpy(key, data->msk, EAP_SIM_KEYING_DATA_LEN);
*len = EAP_SIM_KEYING_DATA_LEN;
return key;
}
static u8 * eap_sim_get_emsk(struct eap_sm *sm, void *priv, size_t *len)
{
struct eap_sim_data *data = priv;
u8 *key;
if (data->state != SUCCESS)
return NULL;
key = os_malloc(EAP_EMSK_LEN);
if (key == NULL)
return NULL;
os_memcpy(key, data->emsk, EAP_EMSK_LEN);
*len = EAP_EMSK_LEN;
return key;
}
static Boolean eap_sim_isSuccess(struct eap_sm *sm, void *priv)
{
struct eap_sim_data *data = priv;
return data->state == SUCCESS;
}
int eap_server_sim_register(void)
{
struct eap_method *eap;
int ret;
eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION,
EAP_VENDOR_IETF, EAP_TYPE_SIM, "SIM");
if (eap == NULL)
return -1;
eap->init = eap_sim_init;
eap->reset = eap_sim_reset;
eap->buildReq = eap_sim_buildReq;
eap->check = eap_sim_check;
eap->process = eap_sim_process;
eap->isDone = eap_sim_isDone;
eap->getKey = eap_sim_getKey;
eap->isSuccess = eap_sim_isSuccess;
eap->get_emsk = eap_sim_get_emsk;
ret = eap_server_method_register(eap);
if (ret)
eap_server_method_free(eap);
return ret;
}

View file

@ -0,0 +1,286 @@
/*
* hostapd / EAP-TLS (RFC 2716)
* Copyright (c) 2004-2008, Jouni Malinen <j@w1.fi>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* Alternatively, this software may be distributed under the terms of BSD
* license.
*
* See README and COPYING for more details.
*/
#include "includes.h"
#include "common.h"
#include "eap_i.h"
#include "eap_tls_common.h"
#include "crypto/tls.h"
static void eap_tls_reset(struct eap_sm *sm, void *priv);
struct eap_tls_data {
struct eap_ssl_data ssl;
enum { START, CONTINUE, SUCCESS, FAILURE } state;
int established;
};
static const char * eap_tls_state_txt(int state)
{
switch (state) {
case START:
return "START";
case CONTINUE:
return "CONTINUE";
case SUCCESS:
return "SUCCESS";
case FAILURE:
return "FAILURE";
default:
return "Unknown?!";
}
}
static void eap_tls_state(struct eap_tls_data *data, int state)
{
wpa_printf(MSG_DEBUG, "EAP-TLS: %s -> %s",
eap_tls_state_txt(data->state),
eap_tls_state_txt(state));
data->state = state;
}
static void * eap_tls_init(struct eap_sm *sm)
{
struct eap_tls_data *data;
data = os_zalloc(sizeof(*data));
if (data == NULL)
return NULL;
data->state = START;
if (eap_server_tls_ssl_init(sm, &data->ssl, 1)) {
wpa_printf(MSG_INFO, "EAP-TLS: Failed to initialize SSL.");
eap_tls_reset(sm, data);
return NULL;
}
return data;
}
static void eap_tls_reset(struct eap_sm *sm, void *priv)
{
struct eap_tls_data *data = priv;
if (data == NULL)
return;
eap_server_tls_ssl_deinit(sm, &data->ssl);
os_free(data);
}
static struct wpabuf * eap_tls_build_start(struct eap_sm *sm,
struct eap_tls_data *data, u8 id)
{
struct wpabuf *req;
req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_TLS, 1, EAP_CODE_REQUEST,
id);
if (req == NULL) {
wpa_printf(MSG_ERROR, "EAP-TLS: Failed to allocate memory for "
"request");
eap_tls_state(data, FAILURE);
return NULL;
}
wpabuf_put_u8(req, EAP_TLS_FLAGS_START);
eap_tls_state(data, CONTINUE);
return req;
}
static struct wpabuf * eap_tls_buildReq(struct eap_sm *sm, void *priv, u8 id)
{
struct eap_tls_data *data = priv;
struct wpabuf *res;
if (data->ssl.state == FRAG_ACK) {
return eap_server_tls_build_ack(id, EAP_TYPE_TLS, 0);
}
if (data->ssl.state == WAIT_FRAG_ACK) {
res = eap_server_tls_build_msg(&data->ssl, EAP_TYPE_TLS, 0,
id);
goto check_established;
}
switch (data->state) {
case START:
return eap_tls_build_start(sm, data, id);
case CONTINUE:
if (tls_connection_established(sm->ssl_ctx, data->ssl.conn))
data->established = 1;
break;
default:
wpa_printf(MSG_DEBUG, "EAP-TLS: %s - unexpected state %d",
__func__, data->state);
return NULL;
}
res = eap_server_tls_build_msg(&data->ssl, EAP_TYPE_TLS, 0, id);
check_established:
if (data->established && data->ssl.state != WAIT_FRAG_ACK) {
/* TLS handshake has been completed and there are no more
* fragments waiting to be sent out. */
wpa_printf(MSG_DEBUG, "EAP-TLS: Done");
eap_tls_state(data, SUCCESS);
}
return res;
}
static Boolean eap_tls_check(struct eap_sm *sm, void *priv,
struct wpabuf *respData)
{
const u8 *pos;
size_t len;
pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_TLS, respData, &len);
if (pos == NULL || len < 1) {
wpa_printf(MSG_INFO, "EAP-TLS: Invalid frame");
return TRUE;
}
return FALSE;
}
static void eap_tls_process_msg(struct eap_sm *sm, void *priv,
const struct wpabuf *respData)
{
struct eap_tls_data *data = priv;
if (data->state == SUCCESS && wpabuf_len(data->ssl.tls_in) == 0) {
wpa_printf(MSG_DEBUG, "EAP-TLS: Client acknowledged final TLS "
"handshake message");
return;
}
if (eap_server_tls_phase1(sm, &data->ssl) < 0)
eap_tls_state(data, FAILURE);
}
static void eap_tls_process(struct eap_sm *sm, void *priv,
struct wpabuf *respData)
{
struct eap_tls_data *data = priv;
if (eap_server_tls_process(sm, &data->ssl, respData, data,
EAP_TYPE_TLS, NULL, eap_tls_process_msg) <
0)
eap_tls_state(data, FAILURE);
}
static Boolean eap_tls_isDone(struct eap_sm *sm, void *priv)
{
struct eap_tls_data *data = priv;
return data->state == SUCCESS || data->state == FAILURE;
}
static u8 * eap_tls_getKey(struct eap_sm *sm, void *priv, size_t *len)
{
struct eap_tls_data *data = priv;
u8 *eapKeyData;
if (data->state != SUCCESS)
return NULL;
eapKeyData = eap_server_tls_derive_key(sm, &data->ssl,
"client EAP encryption",
EAP_TLS_KEY_LEN);
if (eapKeyData) {
*len = EAP_TLS_KEY_LEN;
wpa_hexdump(MSG_DEBUG, "EAP-TLS: Derived key",
eapKeyData, EAP_TLS_KEY_LEN);
} else {
wpa_printf(MSG_DEBUG, "EAP-TLS: Failed to derive key");
}
return eapKeyData;
}
static u8 * eap_tls_get_emsk(struct eap_sm *sm, void *priv, size_t *len)
{
struct eap_tls_data *data = priv;
u8 *eapKeyData, *emsk;
if (data->state != SUCCESS)
return NULL;
eapKeyData = eap_server_tls_derive_key(sm, &data->ssl,
"client EAP encryption",
EAP_TLS_KEY_LEN + EAP_EMSK_LEN);
if (eapKeyData) {
emsk = os_malloc(EAP_EMSK_LEN);
if (emsk)
os_memcpy(emsk, eapKeyData + EAP_TLS_KEY_LEN,
EAP_EMSK_LEN);
os_free(eapKeyData);
} else
emsk = NULL;
if (emsk) {
*len = EAP_EMSK_LEN;
wpa_hexdump(MSG_DEBUG, "EAP-TLS: Derived EMSK",
emsk, EAP_EMSK_LEN);
} else {
wpa_printf(MSG_DEBUG, "EAP-TLS: Failed to derive EMSK");
}
return emsk;
}
static Boolean eap_tls_isSuccess(struct eap_sm *sm, void *priv)
{
struct eap_tls_data *data = priv;
return data->state == SUCCESS;
}
int eap_server_tls_register(void)
{
struct eap_method *eap;
int ret;
eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION,
EAP_VENDOR_IETF, EAP_TYPE_TLS, "TLS");
if (eap == NULL)
return -1;
eap->init = eap_tls_init;
eap->reset = eap_tls_reset;
eap->buildReq = eap_tls_buildReq;
eap->check = eap_tls_check;
eap->process = eap_tls_process;
eap->isDone = eap_tls_isDone;
eap->getKey = eap_tls_getKey;
eap->isSuccess = eap_tls_isSuccess;
eap->get_emsk = eap_tls_get_emsk;
ret = eap_server_method_register(eap);
if (ret)
eap_server_method_free(eap);
return ret;
}

View file

@ -0,0 +1,400 @@
/*
* EAP-TLS/PEAP/TTLS/FAST server common functions
* Copyright (c) 2004-2009, Jouni Malinen <j@w1.fi>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* Alternatively, this software may be distributed under the terms of BSD
* license.
*
* See README and COPYING for more details.
*/
#include "includes.h"
#include "common.h"
#include "crypto/sha1.h"
#include "crypto/tls.h"
#include "eap_i.h"
#include "eap_tls_common.h"
static void eap_server_tls_free_in_buf(struct eap_ssl_data *data);
int eap_server_tls_ssl_init(struct eap_sm *sm, struct eap_ssl_data *data,
int verify_peer)
{
data->eap = sm;
data->phase2 = sm->init_phase2;
data->conn = tls_connection_init(sm->ssl_ctx);
if (data->conn == NULL) {
wpa_printf(MSG_INFO, "SSL: Failed to initialize new TLS "
"connection");
return -1;
}
if (tls_connection_set_verify(sm->ssl_ctx, data->conn, verify_peer)) {
wpa_printf(MSG_INFO, "SSL: Failed to configure verification "
"of TLS peer certificate");
tls_connection_deinit(sm->ssl_ctx, data->conn);
data->conn = NULL;
return -1;
}
data->tls_out_limit = sm->fragment_size > 0 ? sm->fragment_size : 1398;
if (data->phase2) {
/* Limit the fragment size in the inner TLS authentication
* since the outer authentication with EAP-PEAP does not yet
* support fragmentation */
if (data->tls_out_limit > 100)
data->tls_out_limit -= 100;
}
return 0;
}
void eap_server_tls_ssl_deinit(struct eap_sm *sm, struct eap_ssl_data *data)
{
tls_connection_deinit(sm->ssl_ctx, data->conn);
eap_server_tls_free_in_buf(data);
wpabuf_free(data->tls_out);
data->tls_out = NULL;
}
u8 * eap_server_tls_derive_key(struct eap_sm *sm, struct eap_ssl_data *data,
char *label, size_t len)
{
struct tls_keys keys;
u8 *rnd = NULL, *out;
out = os_malloc(len);
if (out == NULL)
return NULL;
if (tls_connection_prf(sm->ssl_ctx, data->conn, label, 0, out, len) ==
0)
return out;
if (tls_connection_get_keys(sm->ssl_ctx, data->conn, &keys))
goto fail;
if (keys.client_random == NULL || keys.server_random == NULL ||
keys.master_key == NULL)
goto fail;
rnd = os_malloc(keys.client_random_len + keys.server_random_len);
if (rnd == NULL)
goto fail;
os_memcpy(rnd, keys.client_random, keys.client_random_len);
os_memcpy(rnd + keys.client_random_len, keys.server_random,
keys.server_random_len);
if (tls_prf(keys.master_key, keys.master_key_len,
label, rnd, keys.client_random_len +
keys.server_random_len, out, len))
goto fail;
os_free(rnd);
return out;
fail:
os_free(out);
os_free(rnd);
return NULL;
}
struct wpabuf * eap_server_tls_build_msg(struct eap_ssl_data *data,
int eap_type, int version, u8 id)
{
struct wpabuf *req;
u8 flags;
size_t send_len, plen;
wpa_printf(MSG_DEBUG, "SSL: Generating Request");
if (data->tls_out == NULL) {
wpa_printf(MSG_ERROR, "SSL: tls_out NULL in %s", __func__);
return NULL;
}
flags = version;
send_len = wpabuf_len(data->tls_out) - data->tls_out_pos;
if (1 + send_len > data->tls_out_limit) {
send_len = data->tls_out_limit - 1;
flags |= EAP_TLS_FLAGS_MORE_FRAGMENTS;
if (data->tls_out_pos == 0) {
flags |= EAP_TLS_FLAGS_LENGTH_INCLUDED;
send_len -= 4;
}
}
plen = 1 + send_len;
if (flags & EAP_TLS_FLAGS_LENGTH_INCLUDED)
plen += 4;
req = eap_msg_alloc(EAP_VENDOR_IETF, eap_type, plen,
EAP_CODE_REQUEST, id);
if (req == NULL)
return NULL;
wpabuf_put_u8(req, flags); /* Flags */
if (flags & EAP_TLS_FLAGS_LENGTH_INCLUDED)
wpabuf_put_be32(req, wpabuf_len(data->tls_out));
wpabuf_put_data(req, wpabuf_head_u8(data->tls_out) + data->tls_out_pos,
send_len);
data->tls_out_pos += send_len;
if (data->tls_out_pos == wpabuf_len(data->tls_out)) {
wpa_printf(MSG_DEBUG, "SSL: Sending out %lu bytes "
"(message sent completely)",
(unsigned long) send_len);
wpabuf_free(data->tls_out);
data->tls_out = NULL;
data->tls_out_pos = 0;
data->state = MSG;
} else {
wpa_printf(MSG_DEBUG, "SSL: Sending out %lu bytes "
"(%lu more to send)", (unsigned long) send_len,
(unsigned long) wpabuf_len(data->tls_out) -
data->tls_out_pos);
data->state = WAIT_FRAG_ACK;
}
return req;
}
struct wpabuf * eap_server_tls_build_ack(u8 id, int eap_type, int version)
{
struct wpabuf *req;
req = eap_msg_alloc(EAP_VENDOR_IETF, eap_type, 1, EAP_CODE_REQUEST,
id);
if (req == NULL)
return NULL;
wpa_printf(MSG_DEBUG, "SSL: Building ACK");
wpabuf_put_u8(req, version); /* Flags */
return req;
}
static int eap_server_tls_process_cont(struct eap_ssl_data *data,
const u8 *buf, size_t len)
{
/* Process continuation of a pending message */
if (len > wpabuf_tailroom(data->tls_in)) {
wpa_printf(MSG_DEBUG, "SSL: Fragment overflow");
return -1;
}
wpabuf_put_data(data->tls_in, buf, len);
wpa_printf(MSG_DEBUG, "SSL: Received %lu bytes, waiting for %lu "
"bytes more", (unsigned long) len,
(unsigned long) wpabuf_tailroom(data->tls_in));
return 0;
}
static int eap_server_tls_process_fragment(struct eap_ssl_data *data,
u8 flags, u32 message_length,
const u8 *buf, size_t len)
{
/* Process a fragment that is not the last one of the message */
if (data->tls_in == NULL && !(flags & EAP_TLS_FLAGS_LENGTH_INCLUDED)) {
wpa_printf(MSG_DEBUG, "SSL: No Message Length field in a "
"fragmented packet");
return -1;
}
if (data->tls_in == NULL) {
/* First fragment of the message */
/* Limit length to avoid rogue peers from causing large
* memory allocations. */
if (message_length > 65536) {
wpa_printf(MSG_INFO, "SSL: Too long TLS fragment (size"
" over 64 kB)");
return -1;
}
data->tls_in = wpabuf_alloc(message_length);
if (data->tls_in == NULL) {
wpa_printf(MSG_DEBUG, "SSL: No memory for message");
return -1;
}
wpabuf_put_data(data->tls_in, buf, len);
wpa_printf(MSG_DEBUG, "SSL: Received %lu bytes in first "
"fragment, waiting for %lu bytes more",
(unsigned long) len,
(unsigned long) wpabuf_tailroom(data->tls_in));
}
return 0;
}
int eap_server_tls_phase1(struct eap_sm *sm, struct eap_ssl_data *data)
{
if (data->tls_out) {
/* This should not happen.. */
wpa_printf(MSG_INFO, "SSL: pending tls_out data when "
"processing new message");
wpabuf_free(data->tls_out);
WPA_ASSERT(data->tls_out == NULL);
}
data->tls_out = tls_connection_server_handshake(sm->ssl_ctx,
data->conn,
data->tls_in, NULL);
if (data->tls_out == NULL) {
wpa_printf(MSG_INFO, "SSL: TLS processing failed");
return -1;
}
if (tls_connection_get_failed(sm->ssl_ctx, data->conn)) {
/* TLS processing has failed - return error */
wpa_printf(MSG_DEBUG, "SSL: Failed - tls_out available to "
"report error");
return -1;
}
return 0;
}
static int eap_server_tls_reassemble(struct eap_ssl_data *data, u8 flags,
const u8 **pos, size_t *left)
{
unsigned int tls_msg_len = 0;
const u8 *end = *pos + *left;
if (flags & EAP_TLS_FLAGS_LENGTH_INCLUDED) {
if (*left < 4) {
wpa_printf(MSG_INFO, "SSL: Short frame with TLS "
"length");
return -1;
}
tls_msg_len = WPA_GET_BE32(*pos);
wpa_printf(MSG_DEBUG, "SSL: TLS Message Length: %d",
tls_msg_len);
*pos += 4;
*left -= 4;
}
wpa_printf(MSG_DEBUG, "SSL: Received packet: Flags 0x%x "
"Message Length %u", flags, tls_msg_len);
if (data->state == WAIT_FRAG_ACK) {
if (*left != 0) {
wpa_printf(MSG_DEBUG, "SSL: Unexpected payload in "
"WAIT_FRAG_ACK state");
return -1;
}
wpa_printf(MSG_DEBUG, "SSL: Fragment acknowledged");
return 1;
}
if (data->tls_in &&
eap_server_tls_process_cont(data, *pos, end - *pos) < 0)
return -1;
if (flags & EAP_TLS_FLAGS_MORE_FRAGMENTS) {
if (eap_server_tls_process_fragment(data, flags, tls_msg_len,
*pos, end - *pos) < 0)
return -1;
data->state = FRAG_ACK;
return 1;
}
if (data->state == FRAG_ACK) {
wpa_printf(MSG_DEBUG, "SSL: All fragments received");
data->state = MSG;
}
if (data->tls_in == NULL) {
/* Wrap unfragmented messages as wpabuf without extra copy */
wpabuf_set(&data->tmpbuf, *pos, end - *pos);
data->tls_in = &data->tmpbuf;
}
return 0;
}
static void eap_server_tls_free_in_buf(struct eap_ssl_data *data)
{
if (data->tls_in != &data->tmpbuf)
wpabuf_free(data->tls_in);
data->tls_in = NULL;
}
struct wpabuf * eap_server_tls_encrypt(struct eap_sm *sm,
struct eap_ssl_data *data,
const struct wpabuf *plain)
{
struct wpabuf *buf;
buf = tls_connection_encrypt(sm->ssl_ctx, data->conn,
plain);
if (buf == NULL) {
wpa_printf(MSG_INFO, "SSL: Failed to encrypt Phase 2 data");
return NULL;
}
return buf;
}
int eap_server_tls_process(struct eap_sm *sm, struct eap_ssl_data *data,
struct wpabuf *respData, void *priv, int eap_type,
int (*proc_version)(struct eap_sm *sm, void *priv,
int peer_version),
void (*proc_msg)(struct eap_sm *sm, void *priv,
const struct wpabuf *respData))
{
const u8 *pos;
u8 flags;
size_t left;
int ret, res = 0;
pos = eap_hdr_validate(EAP_VENDOR_IETF, eap_type, respData, &left);
if (pos == NULL || left < 1)
return 0; /* Should not happen - frame already validated */
flags = *pos++;
left--;
wpa_printf(MSG_DEBUG, "SSL: Received packet(len=%lu) - Flags 0x%02x",
(unsigned long) wpabuf_len(respData), flags);
if (proc_version &&
proc_version(sm, priv, flags & EAP_TLS_VERSION_MASK) < 0)
return -1;
ret = eap_server_tls_reassemble(data, flags, &pos, &left);
if (ret < 0) {
res = -1;
goto done;
} else if (ret == 1)
return 0;
if (proc_msg)
proc_msg(sm, priv, respData);
if (tls_connection_get_write_alerts(sm->ssl_ctx, data->conn) > 1) {
wpa_printf(MSG_INFO, "SSL: Locally detected fatal error in "
"TLS processing");
res = -1;
}
done:
eap_server_tls_free_in_buf(data);
return res;
}

View file

@ -0,0 +1,582 @@
/*
* EAP server method: EAP-TNC (Trusted Network Connect)
* Copyright (c) 2007-2010, Jouni Malinen <j@w1.fi>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* Alternatively, this software may be distributed under the terms of BSD
* license.
*
* See README and COPYING for more details.
*/
#include "includes.h"
#include "common.h"
#include "base64.h"
#include "eap_i.h"
#include "tncs.h"
struct eap_tnc_data {
enum eap_tnc_state {
START, CONTINUE, RECOMMENDATION, FRAG_ACK, WAIT_FRAG_ACK, DONE,
FAIL
} state;
enum { ALLOW, ISOLATE, NO_ACCESS, NO_RECOMMENDATION } recommendation;
struct tncs_data *tncs;
struct wpabuf *in_buf;
struct wpabuf *out_buf;
size_t out_used;
size_t fragment_size;
unsigned int was_done:1;
unsigned int was_fail:1;
};
/* EAP-TNC Flags */
#define EAP_TNC_FLAGS_LENGTH_INCLUDED 0x80
#define EAP_TNC_FLAGS_MORE_FRAGMENTS 0x40
#define EAP_TNC_FLAGS_START 0x20
#define EAP_TNC_VERSION_MASK 0x07
#define EAP_TNC_VERSION 1
static const char * eap_tnc_state_txt(enum eap_tnc_state state)
{
switch (state) {
case START:
return "START";
case CONTINUE:
return "CONTINUE";
case RECOMMENDATION:
return "RECOMMENDATION";
case FRAG_ACK:
return "FRAG_ACK";
case WAIT_FRAG_ACK:
return "WAIT_FRAG_ACK";
case DONE:
return "DONE";
case FAIL:
return "FAIL";
}
return "??";
}
static void eap_tnc_set_state(struct eap_tnc_data *data,
enum eap_tnc_state new_state)
{
wpa_printf(MSG_DEBUG, "EAP-TNC: %s -> %s",
eap_tnc_state_txt(data->state),
eap_tnc_state_txt(new_state));
data->state = new_state;
}
static void * eap_tnc_init(struct eap_sm *sm)
{
struct eap_tnc_data *data;
data = os_zalloc(sizeof(*data));
if (data == NULL)
return NULL;
eap_tnc_set_state(data, START);
data->tncs = tncs_init();
if (data->tncs == NULL) {
os_free(data);
return NULL;
}
data->fragment_size = sm->fragment_size > 100 ?
sm->fragment_size - 98 : 1300;
return data;
}
static void eap_tnc_reset(struct eap_sm *sm, void *priv)
{
struct eap_tnc_data *data = priv;
wpabuf_free(data->in_buf);
wpabuf_free(data->out_buf);
tncs_deinit(data->tncs);
os_free(data);
}
static struct wpabuf * eap_tnc_build_start(struct eap_sm *sm,
struct eap_tnc_data *data, u8 id)
{
struct wpabuf *req;
req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_TNC, 1, EAP_CODE_REQUEST,
id);
if (req == NULL) {
wpa_printf(MSG_ERROR, "EAP-TNC: Failed to allocate memory for "
"request");
eap_tnc_set_state(data, FAIL);
return NULL;
}
wpabuf_put_u8(req, EAP_TNC_FLAGS_START | EAP_TNC_VERSION);
eap_tnc_set_state(data, CONTINUE);
return req;
}
static struct wpabuf * eap_tnc_build(struct eap_sm *sm,
struct eap_tnc_data *data)
{
struct wpabuf *req;
u8 *rpos, *rpos1;
size_t rlen;
char *start_buf, *end_buf;
size_t start_len, end_len;
size_t imv_len;
imv_len = tncs_total_send_len(data->tncs);
start_buf = tncs_if_tnccs_start(data->tncs);
if (start_buf == NULL)
return NULL;
start_len = os_strlen(start_buf);
end_buf = tncs_if_tnccs_end();
if (end_buf == NULL) {
os_free(start_buf);
return NULL;
}
end_len = os_strlen(end_buf);
rlen = start_len + imv_len + end_len;
req = wpabuf_alloc(rlen);
if (req == NULL) {
os_free(start_buf);
os_free(end_buf);
return NULL;
}
wpabuf_put_data(req, start_buf, start_len);
os_free(start_buf);
rpos1 = wpabuf_put(req, 0);
rpos = tncs_copy_send_buf(data->tncs, rpos1);
wpabuf_put(req, rpos - rpos1);
wpabuf_put_data(req, end_buf, end_len);
os_free(end_buf);
wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-TNC: Request",
wpabuf_head(req), wpabuf_len(req));
return req;
}
static struct wpabuf * eap_tnc_build_recommendation(struct eap_sm *sm,
struct eap_tnc_data *data)
{
switch (data->recommendation) {
case ALLOW:
eap_tnc_set_state(data, DONE);
break;
case ISOLATE:
eap_tnc_set_state(data, FAIL);
/* TODO: support assignment to a different VLAN */
break;
case NO_ACCESS:
eap_tnc_set_state(data, FAIL);
break;
case NO_RECOMMENDATION:
eap_tnc_set_state(data, DONE);
break;
default:
wpa_printf(MSG_DEBUG, "EAP-TNC: Unknown recommendation");
return NULL;
}
return eap_tnc_build(sm, data);
}
static struct wpabuf * eap_tnc_build_frag_ack(u8 id, u8 code)
{
struct wpabuf *msg;
msg = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_TNC, 1, code, id);
if (msg == NULL) {
wpa_printf(MSG_ERROR, "EAP-TNC: Failed to allocate memory "
"for fragment ack");
return NULL;
}
wpabuf_put_u8(msg, EAP_TNC_VERSION); /* Flags */
wpa_printf(MSG_DEBUG, "EAP-TNC: Send fragment ack");
return msg;
}
static struct wpabuf * eap_tnc_build_msg(struct eap_tnc_data *data, u8 id)
{
struct wpabuf *req;
u8 flags;
size_t send_len, plen;
wpa_printf(MSG_DEBUG, "EAP-TNC: Generating Request");
flags = EAP_TNC_VERSION;
send_len = wpabuf_len(data->out_buf) - data->out_used;
if (1 + send_len > data->fragment_size) {
send_len = data->fragment_size - 1;
flags |= EAP_TNC_FLAGS_MORE_FRAGMENTS;
if (data->out_used == 0) {
flags |= EAP_TNC_FLAGS_LENGTH_INCLUDED;
send_len -= 4;
}
}
plen = 1 + send_len;
if (flags & EAP_TNC_FLAGS_LENGTH_INCLUDED)
plen += 4;
req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_TNC, plen,
EAP_CODE_REQUEST, id);
if (req == NULL)
return NULL;
wpabuf_put_u8(req, flags); /* Flags */
if (flags & EAP_TNC_FLAGS_LENGTH_INCLUDED)
wpabuf_put_be32(req, wpabuf_len(data->out_buf));
wpabuf_put_data(req, wpabuf_head_u8(data->out_buf) + data->out_used,
send_len);
data->out_used += send_len;
if (data->out_used == wpabuf_len(data->out_buf)) {
wpa_printf(MSG_DEBUG, "EAP-TNC: Sending out %lu bytes "
"(message sent completely)",
(unsigned long) send_len);
wpabuf_free(data->out_buf);
data->out_buf = NULL;
data->out_used = 0;
if (data->was_fail)
eap_tnc_set_state(data, FAIL);
else if (data->was_done)
eap_tnc_set_state(data, DONE);
} else {
wpa_printf(MSG_DEBUG, "EAP-TNC: Sending out %lu bytes "
"(%lu more to send)", (unsigned long) send_len,
(unsigned long) wpabuf_len(data->out_buf) -
data->out_used);
if (data->state == FAIL)
data->was_fail = 1;
else if (data->state == DONE)
data->was_done = 1;
eap_tnc_set_state(data, WAIT_FRAG_ACK);
}
return req;
}
static struct wpabuf * eap_tnc_buildReq(struct eap_sm *sm, void *priv, u8 id)
{
struct eap_tnc_data *data = priv;
switch (data->state) {
case START:
tncs_init_connection(data->tncs);
return eap_tnc_build_start(sm, data, id);
case CONTINUE:
if (data->out_buf == NULL) {
data->out_buf = eap_tnc_build(sm, data);
if (data->out_buf == NULL) {
wpa_printf(MSG_DEBUG, "EAP-TNC: Failed to "
"generate message");
return NULL;
}
data->out_used = 0;
}
return eap_tnc_build_msg(data, id);
case RECOMMENDATION:
if (data->out_buf == NULL) {
data->out_buf = eap_tnc_build_recommendation(sm, data);
if (data->out_buf == NULL) {
wpa_printf(MSG_DEBUG, "EAP-TNC: Failed to "
"generate recommendation message");
return NULL;
}
data->out_used = 0;
}
return eap_tnc_build_msg(data, id);
case WAIT_FRAG_ACK:
return eap_tnc_build_msg(data, id);
case FRAG_ACK:
return eap_tnc_build_frag_ack(id, EAP_CODE_REQUEST);
case DONE:
case FAIL:
return NULL;
}
return NULL;
}
static Boolean eap_tnc_check(struct eap_sm *sm, void *priv,
struct wpabuf *respData)
{
struct eap_tnc_data *data = priv;
const u8 *pos;
size_t len;
pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_TNC, respData,
&len);
if (pos == NULL) {
wpa_printf(MSG_INFO, "EAP-TNC: Invalid frame");
return TRUE;
}
if (len == 0 && data->state != WAIT_FRAG_ACK) {
wpa_printf(MSG_INFO, "EAP-TNC: Invalid frame (empty)");
return TRUE;
}
if (len == 0)
return FALSE; /* Fragment ACK does not include flags */
if ((*pos & EAP_TNC_VERSION_MASK) != EAP_TNC_VERSION) {
wpa_printf(MSG_DEBUG, "EAP-TNC: Unsupported version %d",
*pos & EAP_TNC_VERSION_MASK);
return TRUE;
}
if (*pos & EAP_TNC_FLAGS_START) {
wpa_printf(MSG_DEBUG, "EAP-TNC: Peer used Start flag");
return TRUE;
}
return FALSE;
}
static void tncs_process(struct eap_tnc_data *data, struct wpabuf *inbuf)
{
enum tncs_process_res res;
res = tncs_process_if_tnccs(data->tncs, wpabuf_head(inbuf),
wpabuf_len(inbuf));
switch (res) {
case TNCCS_RECOMMENDATION_ALLOW:
wpa_printf(MSG_DEBUG, "EAP-TNC: TNCS allowed access");
eap_tnc_set_state(data, RECOMMENDATION);
data->recommendation = ALLOW;
break;
case TNCCS_RECOMMENDATION_NO_RECOMMENDATION:
wpa_printf(MSG_DEBUG, "EAP-TNC: TNCS has no recommendation");
eap_tnc_set_state(data, RECOMMENDATION);
data->recommendation = NO_RECOMMENDATION;
break;
case TNCCS_RECOMMENDATION_ISOLATE:
wpa_printf(MSG_DEBUG, "EAP-TNC: TNCS requested isolation");
eap_tnc_set_state(data, RECOMMENDATION);
data->recommendation = ISOLATE;
break;
case TNCCS_RECOMMENDATION_NO_ACCESS:
wpa_printf(MSG_DEBUG, "EAP-TNC: TNCS rejected access");
eap_tnc_set_state(data, RECOMMENDATION);
data->recommendation = NO_ACCESS;
break;
case TNCCS_PROCESS_ERROR:
wpa_printf(MSG_DEBUG, "EAP-TNC: TNCS processing error");
eap_tnc_set_state(data, FAIL);
break;
default:
break;
}
}
static int eap_tnc_process_cont(struct eap_tnc_data *data,
const u8 *buf, size_t len)
{
/* Process continuation of a pending message */
if (len > wpabuf_tailroom(data->in_buf)) {
wpa_printf(MSG_DEBUG, "EAP-TNC: Fragment overflow");
eap_tnc_set_state(data, FAIL);
return -1;
}
wpabuf_put_data(data->in_buf, buf, len);
wpa_printf(MSG_DEBUG, "EAP-TNC: Received %lu bytes, waiting for %lu "
"bytes more", (unsigned long) len,
(unsigned long) wpabuf_tailroom(data->in_buf));
return 0;
}
static int eap_tnc_process_fragment(struct eap_tnc_data *data,
u8 flags, u32 message_length,
const u8 *buf, size_t len)
{
/* Process a fragment that is not the last one of the message */
if (data->in_buf == NULL && !(flags & EAP_TNC_FLAGS_LENGTH_INCLUDED)) {
wpa_printf(MSG_DEBUG, "EAP-TNC: No Message Length field in a "
"fragmented packet");
return -1;
}
if (data->in_buf == NULL) {
/* First fragment of the message */
data->in_buf = wpabuf_alloc(message_length);
if (data->in_buf == NULL) {
wpa_printf(MSG_DEBUG, "EAP-TNC: No memory for "
"message");
return -1;
}
wpabuf_put_data(data->in_buf, buf, len);
wpa_printf(MSG_DEBUG, "EAP-TNC: Received %lu bytes in first "
"fragment, waiting for %lu bytes more",
(unsigned long) len,
(unsigned long) wpabuf_tailroom(data->in_buf));
}
return 0;
}
static void eap_tnc_process(struct eap_sm *sm, void *priv,
struct wpabuf *respData)
{
struct eap_tnc_data *data = priv;
const u8 *pos, *end;
size_t len;
u8 flags;
u32 message_length = 0;
struct wpabuf tmpbuf;
pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_TNC, respData, &len);
if (pos == NULL)
return; /* Should not happen; message already verified */
end = pos + len;
if (len == 1 && (data->state == DONE || data->state == FAIL)) {
wpa_printf(MSG_DEBUG, "EAP-TNC: Peer acknowledged the last "
"message");
return;
}
if (len == 0) {
/* fragment ack */
flags = 0;
} else
flags = *pos++;
if (flags & EAP_TNC_FLAGS_LENGTH_INCLUDED) {
if (end - pos < 4) {
wpa_printf(MSG_DEBUG, "EAP-TNC: Message underflow");
eap_tnc_set_state(data, FAIL);
return;
}
message_length = WPA_GET_BE32(pos);
pos += 4;
if (message_length < (u32) (end - pos)) {
wpa_printf(MSG_DEBUG, "EAP-TNC: Invalid Message "
"Length (%d; %ld remaining in this msg)",
message_length, (long) (end - pos));
eap_tnc_set_state(data, FAIL);
return;
}
}
wpa_printf(MSG_DEBUG, "EAP-TNC: Received packet: Flags 0x%x "
"Message Length %u", flags, message_length);
if (data->state == WAIT_FRAG_ACK) {
if (len > 1) {
wpa_printf(MSG_DEBUG, "EAP-TNC: Unexpected payload "
"in WAIT_FRAG_ACK state");
eap_tnc_set_state(data, FAIL);
return;
}
wpa_printf(MSG_DEBUG, "EAP-TNC: Fragment acknowledged");
eap_tnc_set_state(data, CONTINUE);
return;
}
if (data->in_buf && eap_tnc_process_cont(data, pos, end - pos) < 0) {
eap_tnc_set_state(data, FAIL);
return;
}
if (flags & EAP_TNC_FLAGS_MORE_FRAGMENTS) {
if (eap_tnc_process_fragment(data, flags, message_length,
pos, end - pos) < 0)
eap_tnc_set_state(data, FAIL);
else
eap_tnc_set_state(data, FRAG_ACK);
return;
} else if (data->state == FRAG_ACK) {
wpa_printf(MSG_DEBUG, "EAP-TNC: All fragments received");
eap_tnc_set_state(data, CONTINUE);
}
if (data->in_buf == NULL) {
/* Wrap unfragmented messages as wpabuf without extra copy */
wpabuf_set(&tmpbuf, pos, end - pos);
data->in_buf = &tmpbuf;
}
wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-TNC: Received payload",
wpabuf_head(data->in_buf), wpabuf_len(data->in_buf));
tncs_process(data, data->in_buf);
if (data->in_buf != &tmpbuf)
wpabuf_free(data->in_buf);
data->in_buf = NULL;
}
static Boolean eap_tnc_isDone(struct eap_sm *sm, void *priv)
{
struct eap_tnc_data *data = priv;
return data->state == DONE || data->state == FAIL;
}
static Boolean eap_tnc_isSuccess(struct eap_sm *sm, void *priv)
{
struct eap_tnc_data *data = priv;
return data->state == DONE;
}
int eap_server_tnc_register(void)
{
struct eap_method *eap;
int ret;
eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION,
EAP_VENDOR_IETF, EAP_TYPE_TNC, "TNC");
if (eap == NULL)
return -1;
eap->init = eap_tnc_init;
eap->reset = eap_tnc_reset;
eap->buildReq = eap_tnc_buildReq;
eap->check = eap_tnc_check;
eap->process = eap_tnc_process;
eap->isDone = eap_tnc_isDone;
eap->isSuccess = eap_tnc_isSuccess;
ret = eap_server_method_register(eap);
if (ret)
eap_server_method_free(eap);
return ret;
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,198 @@
/*
* hostapd / Test method for vendor specific (expanded) EAP type
* Copyright (c) 2005-2007, Jouni Malinen <j@w1.fi>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* Alternatively, this software may be distributed under the terms of BSD
* license.
*
* See README and COPYING for more details.
*/
#include "includes.h"
#include "common.h"
#include "eap_i.h"
#define EAP_VENDOR_ID 0xfffefd
#define EAP_VENDOR_TYPE 0xfcfbfaf9
struct eap_vendor_test_data {
enum { INIT, CONFIRM, SUCCESS, FAILURE } state;
};
static const char * eap_vendor_test_state_txt(int state)
{
switch (state) {
case INIT:
return "INIT";
case CONFIRM:
return "CONFIRM";
case SUCCESS:
return "SUCCESS";
case FAILURE:
return "FAILURE";
default:
return "?";
}
}
static void eap_vendor_test_state(struct eap_vendor_test_data *data,
int state)
{
wpa_printf(MSG_DEBUG, "EAP-VENDOR-TEST: %s -> %s",
eap_vendor_test_state_txt(data->state),
eap_vendor_test_state_txt(state));
data->state = state;
}
static void * eap_vendor_test_init(struct eap_sm *sm)
{
struct eap_vendor_test_data *data;
data = os_zalloc(sizeof(*data));
if (data == NULL)
return NULL;
data->state = INIT;
return data;
}
static void eap_vendor_test_reset(struct eap_sm *sm, void *priv)
{
struct eap_vendor_test_data *data = priv;
os_free(data);
}
static struct wpabuf * eap_vendor_test_buildReq(struct eap_sm *sm, void *priv,
u8 id)
{
struct eap_vendor_test_data *data = priv;
struct wpabuf *req;
req = eap_msg_alloc(EAP_VENDOR_ID, EAP_VENDOR_TYPE, 1,
EAP_CODE_REQUEST, id);
if (req == NULL) {
wpa_printf(MSG_ERROR, "EAP-VENDOR-TEST: Failed to allocate "
"memory for request");
return NULL;
}
wpabuf_put_u8(req, data->state == INIT ? 1 : 3);
return req;
}
static Boolean eap_vendor_test_check(struct eap_sm *sm, void *priv,
struct wpabuf *respData)
{
const u8 *pos;
size_t len;
pos = eap_hdr_validate(EAP_VENDOR_ID, EAP_VENDOR_TYPE, respData, &len);
if (pos == NULL || len < 1) {
wpa_printf(MSG_INFO, "EAP-VENDOR-TEST: Invalid frame");
return TRUE;
}
return FALSE;
}
static void eap_vendor_test_process(struct eap_sm *sm, void *priv,
struct wpabuf *respData)
{
struct eap_vendor_test_data *data = priv;
const u8 *pos;
size_t len;
pos = eap_hdr_validate(EAP_VENDOR_ID, EAP_VENDOR_TYPE, respData, &len);
if (pos == NULL || len < 1)
return;
if (data->state == INIT) {
if (*pos == 2)
eap_vendor_test_state(data, CONFIRM);
else
eap_vendor_test_state(data, FAILURE);
} else if (data->state == CONFIRM) {
if (*pos == 4)
eap_vendor_test_state(data, SUCCESS);
else
eap_vendor_test_state(data, FAILURE);
} else
eap_vendor_test_state(data, FAILURE);
}
static Boolean eap_vendor_test_isDone(struct eap_sm *sm, void *priv)
{
struct eap_vendor_test_data *data = priv;
return data->state == SUCCESS;
}
static u8 * eap_vendor_test_getKey(struct eap_sm *sm, void *priv, size_t *len)
{
struct eap_vendor_test_data *data = priv;
u8 *key;
const int key_len = 64;
if (data->state != SUCCESS)
return NULL;
key = os_malloc(key_len);
if (key == NULL)
return NULL;
os_memset(key, 0x11, key_len / 2);
os_memset(key + key_len / 2, 0x22, key_len / 2);
*len = key_len;
return key;
}
static Boolean eap_vendor_test_isSuccess(struct eap_sm *sm, void *priv)
{
struct eap_vendor_test_data *data = priv;
return data->state == SUCCESS;
}
int eap_server_vendor_test_register(void)
{
struct eap_method *eap;
int ret;
eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION,
EAP_VENDOR_ID, EAP_VENDOR_TYPE,
"VENDOR-TEST");
if (eap == NULL)
return -1;
eap->init = eap_vendor_test_init;
eap->reset = eap_vendor_test_reset;
eap->buildReq = eap_vendor_test_buildReq;
eap->check = eap_vendor_test_check;
eap->process = eap_vendor_test_process;
eap->isDone = eap_vendor_test_isDone;
eap->getKey = eap_vendor_test_getKey;
eap->isSuccess = eap_vendor_test_isSuccess;
ret = eap_server_method_register(eap);
if (ret)
eap_server_method_free(eap);
return ret;
}

View file

@ -0,0 +1,517 @@
/*
* EAP-WSC server for Wi-Fi Protected Setup
* Copyright (c) 2007-2008, Jouni Malinen <j@w1.fi>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* Alternatively, this software may be distributed under the terms of BSD
* license.
*
* See README and COPYING for more details.
*/
#include "includes.h"
#include "common.h"
#include "eloop.h"
#include "eap_i.h"
#include "eap_common/eap_wsc_common.h"
#include "p2p/p2p.h"
#include "wps/wps.h"
struct eap_wsc_data {
enum { START, MESG, FRAG_ACK, WAIT_FRAG_ACK, DONE, FAIL } state;
int registrar;
struct wpabuf *in_buf;
struct wpabuf *out_buf;
enum wsc_op_code in_op_code, out_op_code;
size_t out_used;
size_t fragment_size;
struct wps_data *wps;
int ext_reg_timeout;
};
#ifndef CONFIG_NO_STDOUT_DEBUG
static const char * eap_wsc_state_txt(int state)
{
switch (state) {
case START:
return "START";
case MESG:
return "MESG";
case FRAG_ACK:
return "FRAG_ACK";
case WAIT_FRAG_ACK:
return "WAIT_FRAG_ACK";
case DONE:
return "DONE";
case FAIL:
return "FAIL";
default:
return "?";
}
}
#endif /* CONFIG_NO_STDOUT_DEBUG */
static void eap_wsc_state(struct eap_wsc_data *data, int state)
{
wpa_printf(MSG_DEBUG, "EAP-WSC: %s -> %s",
eap_wsc_state_txt(data->state),
eap_wsc_state_txt(state));
data->state = state;
}
static void eap_wsc_ext_reg_timeout(void *eloop_ctx, void *timeout_ctx)
{
struct eap_sm *sm = eloop_ctx;
struct eap_wsc_data *data = timeout_ctx;
if (sm->method_pending != METHOD_PENDING_WAIT)
return;
wpa_printf(MSG_DEBUG, "EAP-WSC: Timeout while waiting for an External "
"Registrar");
data->ext_reg_timeout = 1;
eap_sm_pending_cb(sm);
}
static void * eap_wsc_init(struct eap_sm *sm)
{
struct eap_wsc_data *data;
int registrar;
struct wps_config cfg;
if (sm->identity && sm->identity_len == WSC_ID_REGISTRAR_LEN &&
os_memcmp(sm->identity, WSC_ID_REGISTRAR, WSC_ID_REGISTRAR_LEN) ==
0)
registrar = 0; /* Supplicant is Registrar */
else if (sm->identity && sm->identity_len == WSC_ID_ENROLLEE_LEN &&
os_memcmp(sm->identity, WSC_ID_ENROLLEE, WSC_ID_ENROLLEE_LEN)
== 0)
registrar = 1; /* Supplicant is Enrollee */
else {
wpa_hexdump_ascii(MSG_INFO, "EAP-WSC: Unexpected identity",
sm->identity, sm->identity_len);
return NULL;
}
data = os_zalloc(sizeof(*data));
if (data == NULL)
return NULL;
data->state = registrar ? START : MESG;
data->registrar = registrar;
os_memset(&cfg, 0, sizeof(cfg));
cfg.wps = sm->wps;
cfg.registrar = registrar;
if (registrar) {
if (sm->wps == NULL || sm->wps->registrar == NULL) {
wpa_printf(MSG_INFO, "EAP-WSC: WPS Registrar not "
"initialized");
os_free(data);
return NULL;
}
} else {
if (sm->user == NULL || sm->user->password == NULL) {
/*
* In theory, this should not really be needed, but
* Windows 7 uses Registrar mode to probe AP's WPS
* capabilities before trying to use Enrollee and fails
* if the AP does not allow that probing to happen..
*/
wpa_printf(MSG_DEBUG, "EAP-WSC: No AP PIN (password) "
"configured for Enrollee functionality - "
"allow for probing capabilities (M1)");
} else {
cfg.pin = sm->user->password;
cfg.pin_len = sm->user->password_len;
}
}
cfg.assoc_wps_ie = sm->assoc_wps_ie;
cfg.peer_addr = sm->peer_addr;
#ifdef CONFIG_P2P
if (sm->assoc_p2p_ie) {
wpa_printf(MSG_DEBUG, "EAP-WSC: Prefer PSK format for P2P "
"client");
cfg.use_psk_key = 1;
cfg.p2p_dev_addr = p2p_get_go_dev_addr(sm->assoc_p2p_ie);
}
#endif /* CONFIG_P2P */
data->wps = wps_init(&cfg);
if (data->wps == NULL) {
os_free(data);
return NULL;
}
data->fragment_size = sm->fragment_size > 0 ? sm->fragment_size :
WSC_FRAGMENT_SIZE;
return data;
}
static void eap_wsc_reset(struct eap_sm *sm, void *priv)
{
struct eap_wsc_data *data = priv;
eloop_cancel_timeout(eap_wsc_ext_reg_timeout, sm, data);
wpabuf_free(data->in_buf);
wpabuf_free(data->out_buf);
wps_deinit(data->wps);
os_free(data);
}
static struct wpabuf * eap_wsc_build_start(struct eap_sm *sm,
struct eap_wsc_data *data, u8 id)
{
struct wpabuf *req;
req = eap_msg_alloc(EAP_VENDOR_WFA, EAP_VENDOR_TYPE_WSC, 2,
EAP_CODE_REQUEST, id);
if (req == NULL) {
wpa_printf(MSG_ERROR, "EAP-WSC: Failed to allocate memory for "
"request");
return NULL;
}
wpa_printf(MSG_DEBUG, "EAP-WSC: Send WSC/Start");
wpabuf_put_u8(req, WSC_Start); /* Op-Code */
wpabuf_put_u8(req, 0); /* Flags */
return req;
}
static struct wpabuf * eap_wsc_build_msg(struct eap_wsc_data *data, u8 id)
{
struct wpabuf *req;
u8 flags;
size_t send_len, plen;
flags = 0;
send_len = wpabuf_len(data->out_buf) - data->out_used;
if (2 + send_len > data->fragment_size) {
send_len = data->fragment_size - 2;
flags |= WSC_FLAGS_MF;
if (data->out_used == 0) {
flags |= WSC_FLAGS_LF;
send_len -= 2;
}
}
plen = 2 + send_len;
if (flags & WSC_FLAGS_LF)
plen += 2;
req = eap_msg_alloc(EAP_VENDOR_WFA, EAP_VENDOR_TYPE_WSC, plen,
EAP_CODE_REQUEST, id);
if (req == NULL) {
wpa_printf(MSG_ERROR, "EAP-WSC: Failed to allocate memory for "
"request");
return NULL;
}
wpabuf_put_u8(req, data->out_op_code); /* Op-Code */
wpabuf_put_u8(req, flags); /* Flags */
if (flags & WSC_FLAGS_LF)
wpabuf_put_be16(req, wpabuf_len(data->out_buf));
wpabuf_put_data(req, wpabuf_head_u8(data->out_buf) + data->out_used,
send_len);
data->out_used += send_len;
if (data->out_used == wpabuf_len(data->out_buf)) {
wpa_printf(MSG_DEBUG, "EAP-WSC: Sending out %lu bytes "
"(message sent completely)",
(unsigned long) send_len);
wpabuf_free(data->out_buf);
data->out_buf = NULL;
data->out_used = 0;
eap_wsc_state(data, MESG);
} else {
wpa_printf(MSG_DEBUG, "EAP-WSC: Sending out %lu bytes "
"(%lu more to send)", (unsigned long) send_len,
(unsigned long) wpabuf_len(data->out_buf) -
data->out_used);
eap_wsc_state(data, WAIT_FRAG_ACK);
}
return req;
}
static struct wpabuf * eap_wsc_buildReq(struct eap_sm *sm, void *priv, u8 id)
{
struct eap_wsc_data *data = priv;
switch (data->state) {
case START:
return eap_wsc_build_start(sm, data, id);
case MESG:
if (data->out_buf == NULL) {
data->out_buf = wps_get_msg(data->wps,
&data->out_op_code);
if (data->out_buf == NULL) {
wpa_printf(MSG_DEBUG, "EAP-WSC: Failed to "
"receive message from WPS");
return NULL;
}
data->out_used = 0;
}
/* pass through */
case WAIT_FRAG_ACK:
return eap_wsc_build_msg(data, id);
case FRAG_ACK:
return eap_wsc_build_frag_ack(id, EAP_CODE_REQUEST);
default:
wpa_printf(MSG_DEBUG, "EAP-WSC: Unexpected state %d in "
"buildReq", data->state);
return NULL;
}
}
static Boolean eap_wsc_check(struct eap_sm *sm, void *priv,
struct wpabuf *respData)
{
const u8 *pos;
size_t len;
pos = eap_hdr_validate(EAP_VENDOR_WFA, EAP_VENDOR_TYPE_WSC,
respData, &len);
if (pos == NULL || len < 2) {
wpa_printf(MSG_INFO, "EAP-WSC: Invalid frame");
return TRUE;
}
return FALSE;
}
static int eap_wsc_process_cont(struct eap_wsc_data *data,
const u8 *buf, size_t len, u8 op_code)
{
/* Process continuation of a pending message */
if (op_code != data->in_op_code) {
wpa_printf(MSG_DEBUG, "EAP-WSC: Unexpected Op-Code %d in "
"fragment (expected %d)",
op_code, data->in_op_code);
eap_wsc_state(data, FAIL);
return -1;
}
if (len > wpabuf_tailroom(data->in_buf)) {
wpa_printf(MSG_DEBUG, "EAP-WSC: Fragment overflow");
eap_wsc_state(data, FAIL);
return -1;
}
wpabuf_put_data(data->in_buf, buf, len);
wpa_printf(MSG_DEBUG, "EAP-WSC: Received %lu bytes, waiting for %lu "
"bytes more", (unsigned long) len,
(unsigned long) wpabuf_tailroom(data->in_buf));
return 0;
}
static int eap_wsc_process_fragment(struct eap_wsc_data *data,
u8 flags, u8 op_code, u16 message_length,
const u8 *buf, size_t len)
{
/* Process a fragment that is not the last one of the message */
if (data->in_buf == NULL && !(flags & WSC_FLAGS_LF)) {
wpa_printf(MSG_DEBUG, "EAP-WSC: No Message Length "
"field in a fragmented packet");
return -1;
}
if (data->in_buf == NULL) {
/* First fragment of the message */
data->in_buf = wpabuf_alloc(message_length);
if (data->in_buf == NULL) {
wpa_printf(MSG_DEBUG, "EAP-WSC: No memory for "
"message");
return -1;
}
data->in_op_code = op_code;
wpabuf_put_data(data->in_buf, buf, len);
wpa_printf(MSG_DEBUG, "EAP-WSC: Received %lu bytes in "
"first fragment, waiting for %lu bytes more",
(unsigned long) len,
(unsigned long) wpabuf_tailroom(data->in_buf));
}
return 0;
}
static void eap_wsc_process(struct eap_sm *sm, void *priv,
struct wpabuf *respData)
{
struct eap_wsc_data *data = priv;
const u8 *start, *pos, *end;
size_t len;
u8 op_code, flags;
u16 message_length = 0;
enum wps_process_res res;
struct wpabuf tmpbuf;
eloop_cancel_timeout(eap_wsc_ext_reg_timeout, sm, data);
if (data->ext_reg_timeout) {
eap_wsc_state(data, FAIL);
return;
}
pos = eap_hdr_validate(EAP_VENDOR_WFA, EAP_VENDOR_TYPE_WSC,
respData, &len);
if (pos == NULL || len < 2)
return; /* Should not happen; message already verified */
start = pos;
end = start + len;
op_code = *pos++;
flags = *pos++;
if (flags & WSC_FLAGS_LF) {
if (end - pos < 2) {
wpa_printf(MSG_DEBUG, "EAP-WSC: Message underflow");
return;
}
message_length = WPA_GET_BE16(pos);
pos += 2;
if (message_length < end - pos) {
wpa_printf(MSG_DEBUG, "EAP-WSC: Invalid Message "
"Length");
return;
}
}
wpa_printf(MSG_DEBUG, "EAP-WSC: Received packet: Op-Code %d "
"Flags 0x%x Message Length %d",
op_code, flags, message_length);
if (data->state == WAIT_FRAG_ACK) {
if (op_code != WSC_FRAG_ACK) {
wpa_printf(MSG_DEBUG, "EAP-WSC: Unexpected Op-Code %d "
"in WAIT_FRAG_ACK state", op_code);
eap_wsc_state(data, FAIL);
return;
}
wpa_printf(MSG_DEBUG, "EAP-WSC: Fragment acknowledged");
eap_wsc_state(data, MESG);
return;
}
if (op_code != WSC_ACK && op_code != WSC_NACK && op_code != WSC_MSG &&
op_code != WSC_Done) {
wpa_printf(MSG_DEBUG, "EAP-WSC: Unexpected Op-Code %d",
op_code);
eap_wsc_state(data, FAIL);
return;
}
if (data->in_buf &&
eap_wsc_process_cont(data, pos, end - pos, op_code) < 0) {
eap_wsc_state(data, FAIL);
return;
}
if (flags & WSC_FLAGS_MF) {
if (eap_wsc_process_fragment(data, flags, op_code,
message_length, pos, end - pos) <
0)
eap_wsc_state(data, FAIL);
else
eap_wsc_state(data, FRAG_ACK);
return;
}
if (data->in_buf == NULL) {
/* Wrap unfragmented messages as wpabuf without extra copy */
wpabuf_set(&tmpbuf, pos, end - pos);
data->in_buf = &tmpbuf;
}
res = wps_process_msg(data->wps, op_code, data->in_buf);
switch (res) {
case WPS_DONE:
wpa_printf(MSG_DEBUG, "EAP-WSC: WPS processing completed "
"successfully - report EAP failure");
eap_wsc_state(data, FAIL);
break;
case WPS_CONTINUE:
eap_wsc_state(data, MESG);
break;
case WPS_FAILURE:
wpa_printf(MSG_DEBUG, "EAP-WSC: WPS processing failed");
eap_wsc_state(data, FAIL);
break;
case WPS_PENDING:
eap_wsc_state(data, MESG);
sm->method_pending = METHOD_PENDING_WAIT;
eloop_cancel_timeout(eap_wsc_ext_reg_timeout, sm, data);
eloop_register_timeout(5, 0, eap_wsc_ext_reg_timeout,
sm, data);
break;
}
if (data->in_buf != &tmpbuf)
wpabuf_free(data->in_buf);
data->in_buf = NULL;
}
static Boolean eap_wsc_isDone(struct eap_sm *sm, void *priv)
{
struct eap_wsc_data *data = priv;
return data->state == FAIL;
}
static Boolean eap_wsc_isSuccess(struct eap_sm *sm, void *priv)
{
/* EAP-WSC will always result in EAP-Failure */
return FALSE;
}
static int eap_wsc_getTimeout(struct eap_sm *sm, void *priv)
{
/* Recommended retransmit times: retransmit timeout 5 seconds,
* per-message timeout 15 seconds, i.e., 3 tries. */
sm->MaxRetrans = 2; /* total 3 attempts */
return 5;
}
int eap_server_wsc_register(void)
{
struct eap_method *eap;
int ret;
eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION,
EAP_VENDOR_WFA, EAP_VENDOR_TYPE_WSC,
"WSC");
if (eap == NULL)
return -1;
eap->init = eap_wsc_init;
eap->reset = eap_wsc_reset;
eap->buildReq = eap_wsc_buildReq;
eap->check = eap_wsc_check;
eap->process = eap_wsc_process;
eap->isDone = eap_wsc_isDone;
eap->isSuccess = eap_wsc_isSuccess;
eap->getTimeout = eap_wsc_getTimeout;
ret = eap_server_method_register(eap);
if (ret)
eap_server_method_free(eap);
return ret;
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,91 @@
/*
* hostapd / EAP-SIM database/authenticator gateway
* Copyright (c) 2005-2007, Jouni Malinen <j@w1.fi>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* Alternatively, this software may be distributed under the terms of BSD
* license.
*
* See README and COPYING for more details.
*/
#ifndef EAP_SIM_DB_H
#define EAP_SIM_DB_H
#include "eap_common/eap_sim_common.h"
/* Identity prefixes */
#define EAP_SIM_PERMANENT_PREFIX '1'
#define EAP_SIM_PSEUDONYM_PREFIX '3'
#define EAP_SIM_REAUTH_ID_PREFIX '5'
#define EAP_AKA_PERMANENT_PREFIX '0'
#define EAP_AKA_PSEUDONYM_PREFIX '2'
#define EAP_AKA_REAUTH_ID_PREFIX '4'
void * eap_sim_db_init(const char *config,
void (*get_complete_cb)(void *ctx, void *session_ctx),
void *ctx);
void eap_sim_db_deinit(void *priv);
int eap_sim_db_get_gsm_triplets(void *priv, const u8 *identity,
size_t identity_len, int max_chal,
u8 *_rand, u8 *kc, u8 *sres,
void *cb_session_ctx);
#define EAP_SIM_DB_FAILURE -1
#define EAP_SIM_DB_PENDING -2
int eap_sim_db_identity_known(void *priv, const u8 *identity,
size_t identity_len);
char * eap_sim_db_get_next_pseudonym(void *priv, int aka);
char * eap_sim_db_get_next_reauth_id(void *priv, int aka);
int eap_sim_db_add_pseudonym(void *priv, const u8 *identity,
size_t identity_len, char *pseudonym);
int eap_sim_db_add_reauth(void *priv, const u8 *identity,
size_t identity_len, char *reauth_id, u16 counter,
const u8 *mk);
int eap_sim_db_add_reauth_prime(void *priv, const u8 *identity,
size_t identity_len, char *reauth_id,
u16 counter, const u8 *k_encr, const u8 *k_aut,
const u8 *k_re);
const u8 * eap_sim_db_get_permanent(void *priv, const u8 *identity,
size_t identity_len, size_t *len);
struct eap_sim_reauth {
struct eap_sim_reauth *next;
u8 *identity;
size_t identity_len;
char *reauth_id;
u16 counter;
int aka_prime;
u8 mk[EAP_SIM_MK_LEN];
u8 k_encr[EAP_SIM_K_ENCR_LEN];
u8 k_aut[EAP_AKA_PRIME_K_AUT_LEN];
u8 k_re[EAP_AKA_PRIME_K_RE_LEN];
};
struct eap_sim_reauth *
eap_sim_db_get_reauth_entry(void *priv, const u8 *identity,
size_t identity_len);
void eap_sim_db_remove_reauth(void *priv, struct eap_sim_reauth *reauth);
int eap_sim_db_get_aka_auth(void *priv, const u8 *identity,
size_t identity_len, u8 *_rand, u8 *autn, u8 *ik,
u8 *ck, u8 *res, size_t *res_len,
void *cb_session_ctx);
int eap_sim_db_resynchronize(void *priv, const u8 *identity,
size_t identity_len, const u8 *auts,
const u8 *_rand);
#endif /* EAP_SIM_DB_H */

View file

@ -0,0 +1,91 @@
/*
* EAP-TLS/PEAP/TTLS/FAST server common functions
* Copyright (c) 2004-2009, Jouni Malinen <j@w1.fi>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* Alternatively, this software may be distributed under the terms of BSD
* license.
*
* See README and COPYING for more details.
*/
#ifndef EAP_TLS_COMMON_H
#define EAP_TLS_COMMON_H
/**
* struct eap_ssl_data - TLS data for EAP methods
*/
struct eap_ssl_data {
/**
* conn - TLS connection context data from tls_connection_init()
*/
struct tls_connection *conn;
/**
* tls_out - TLS message to be sent out in fragments
*/
struct wpabuf *tls_out;
/**
* tls_out_pos - The current position in the outgoing TLS message
*/
size_t tls_out_pos;
/**
* tls_out_limit - Maximum fragment size for outgoing TLS messages
*/
size_t tls_out_limit;
/**
* tls_in - Received TLS message buffer for re-assembly
*/
struct wpabuf *tls_in;
/**
* phase2 - Whether this TLS connection is used in EAP phase 2 (tunnel)
*/
int phase2;
/**
* eap - EAP state machine allocated with eap_server_sm_init()
*/
struct eap_sm *eap;
enum { MSG, FRAG_ACK, WAIT_FRAG_ACK } state;
struct wpabuf tmpbuf;
};
/* EAP TLS Flags */
#define EAP_TLS_FLAGS_LENGTH_INCLUDED 0x80
#define EAP_TLS_FLAGS_MORE_FRAGMENTS 0x40
#define EAP_TLS_FLAGS_START 0x20
#define EAP_TLS_VERSION_MASK 0x07
/* could be up to 128 bytes, but only the first 64 bytes are used */
#define EAP_TLS_KEY_LEN 64
int eap_server_tls_ssl_init(struct eap_sm *sm, struct eap_ssl_data *data,
int verify_peer);
void eap_server_tls_ssl_deinit(struct eap_sm *sm, struct eap_ssl_data *data);
u8 * eap_server_tls_derive_key(struct eap_sm *sm, struct eap_ssl_data *data,
char *label, size_t len);
struct wpabuf * eap_server_tls_build_msg(struct eap_ssl_data *data,
int eap_type, int version, u8 id);
struct wpabuf * eap_server_tls_build_ack(u8 id, int eap_type, int version);
int eap_server_tls_phase1(struct eap_sm *sm, struct eap_ssl_data *data);
struct wpabuf * eap_server_tls_encrypt(struct eap_sm *sm,
struct eap_ssl_data *data,
const struct wpabuf *plain);
int eap_server_tls_process(struct eap_sm *sm, struct eap_ssl_data *data,
struct wpabuf *respData, void *priv, int eap_type,
int (*proc_version)(struct eap_sm *sm, void *priv,
int peer_version),
void (*proc_msg)(struct eap_sm *sm, void *priv,
const struct wpabuf *respData));
#endif /* EAP_TLS_COMMON_H */

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,67 @@
/*
* IKEv2 initiator (RFC 4306) for EAP-IKEV2
* Copyright (c) 2007, Jouni Malinen <j@w1.fi>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* Alternatively, this software may be distributed under the terms of BSD
* license.
*
* See README and COPYING for more details.
*/
#ifndef IKEV2_H
#define IKEV2_H
#include "eap_common/ikev2_common.h"
struct ikev2_proposal_data {
u8 proposal_num;
int integ;
int prf;
int encr;
int dh;
};
struct ikev2_initiator_data {
enum { SA_INIT, SA_AUTH, CHILD_SA, IKEV2_DONE } state;
u8 i_spi[IKEV2_SPI_LEN];
u8 r_spi[IKEV2_SPI_LEN];
u8 i_nonce[IKEV2_NONCE_MAX_LEN];
size_t i_nonce_len;
u8 r_nonce[IKEV2_NONCE_MAX_LEN];
size_t r_nonce_len;
struct wpabuf *r_dh_public;
struct wpabuf *i_dh_private;
struct ikev2_proposal_data proposal;
const struct dh_group *dh;
struct ikev2_keys keys;
u8 *IDi;
size_t IDi_len;
u8 *IDr;
size_t IDr_len;
u8 IDr_type;
struct wpabuf *r_sign_msg;
struct wpabuf *i_sign_msg;
u8 *shared_secret;
size_t shared_secret_len;
enum { PEER_AUTH_CERT, PEER_AUTH_SECRET } peer_auth;
u8 *key_pad;
size_t key_pad_len;
const u8 * (*get_shared_secret)(void *ctx, const u8 *IDr,
size_t IDr_len, size_t *secret_len);
void *cb_ctx;
int unknown_user;
};
void ikev2_initiator_deinit(struct ikev2_initiator_data *data);
int ikev2_initiator_process(struct ikev2_initiator_data *data,
const struct wpabuf *buf);
struct wpabuf * ikev2_initiator_build(struct ikev2_initiator_data *data);
#endif /* IKEV2_H */

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,49 @@
/*
* EAP-TNC - TNCS (IF-IMV, IF-TNCCS, and IF-TNCCS-SOH)
* Copyright (c) 2007-2008, Jouni Malinen <j@w1.fi>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* Alternatively, this software may be distributed under the terms of BSD
* license.
*
* See README and COPYING for more details.
*/
#ifndef TNCS_H
#define TNCS_H
struct tncs_data;
struct tncs_data * tncs_init(void);
void tncs_deinit(struct tncs_data *tncs);
void tncs_init_connection(struct tncs_data *tncs);
size_t tncs_total_send_len(struct tncs_data *tncs);
u8 * tncs_copy_send_buf(struct tncs_data *tncs, u8 *pos);
char * tncs_if_tnccs_start(struct tncs_data *tncs);
char * tncs_if_tnccs_end(void);
enum tncs_process_res {
TNCCS_PROCESS_ERROR = -1,
TNCCS_PROCESS_OK_NO_RECOMMENDATION = 0,
TNCCS_RECOMMENDATION_ERROR,
TNCCS_RECOMMENDATION_ALLOW,
TNCCS_RECOMMENDATION_NONE,
TNCCS_RECOMMENDATION_ISOLATE,
TNCCS_RECOMMENDATION_NO_ACCESS,
TNCCS_RECOMMENDATION_NO_RECOMMENDATION
};
enum tncs_process_res tncs_process_if_tnccs(struct tncs_data *tncs,
const u8 *msg, size_t len);
int tncs_global_init(void);
void tncs_global_deinit(void);
struct wpabuf * tncs_build_soh_request(void);
struct wpabuf * tncs_process_soh(const u8 *soh_tlv, size_t soh_tlv_len,
int *failure);
#endif /* TNCS_H */