mirror of
https://github.com/lwfinger/rtl8188eu.git
synced 2025-06-23 16:44:20 +00:00
rtl8188eu: Add hostapd and configurationb file
Signed-off-by: Larry Finger <Larry.Finger@lwfinger.net>
This commit is contained in:
parent
7638be5ee7
commit
e275d5ba1f
477 changed files with 220879 additions and 0 deletions
8
hostapd-0.8/src/eap_server/Makefile
Normal file
8
hostapd-0.8/src/eap_server/Makefile
Normal file
|
@ -0,0 +1,8 @@
|
|||
all:
|
||||
@echo Nothing to be made.
|
||||
|
||||
clean:
|
||||
rm -f *~ *.o *.d
|
||||
|
||||
install:
|
||||
@echo Nothing to be made.
|
128
hostapd-0.8/src/eap_server/eap.h
Normal file
128
hostapd-0.8/src/eap_server/eap.h
Normal 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 */
|
201
hostapd-0.8/src/eap_server/eap_i.h
Normal file
201
hostapd-0.8/src/eap_server/eap_i.h
Normal 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 */
|
54
hostapd-0.8/src/eap_server/eap_methods.h
Normal file
54
hostapd-0.8/src/eap_server/eap_methods.h
Normal 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 */
|
1384
hostapd-0.8/src/eap_server/eap_server.c
Normal file
1384
hostapd-0.8/src/eap_server/eap_server.c
Normal file
File diff suppressed because it is too large
Load diff
1278
hostapd-0.8/src/eap_server/eap_server_aka.c
Normal file
1278
hostapd-0.8/src/eap_server/eap_server_aka.c
Normal file
File diff suppressed because it is too large
Load diff
1620
hostapd-0.8/src/eap_server/eap_server_fast.c
Normal file
1620
hostapd-0.8/src/eap_server/eap_server_fast.c
Normal file
File diff suppressed because it is too large
Load diff
634
hostapd-0.8/src/eap_server/eap_server_gpsk.c
Normal file
634
hostapd-0.8/src/eap_server/eap_server_gpsk.c
Normal 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;
|
||||
}
|
230
hostapd-0.8/src/eap_server/eap_server_gtc.c
Normal file
230
hostapd-0.8/src/eap_server/eap_server_gtc.c
Normal 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;
|
||||
}
|
180
hostapd-0.8/src/eap_server/eap_server_identity.c
Normal file
180
hostapd-0.8/src/eap_server/eap_server_identity.c
Normal 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;
|
||||
}
|
539
hostapd-0.8/src/eap_server/eap_server_ikev2.c
Normal file
539
hostapd-0.8/src/eap_server/eap_server_ikev2.c
Normal 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;
|
||||
}
|
177
hostapd-0.8/src/eap_server/eap_server_md5.c
Normal file
177
hostapd-0.8/src/eap_server/eap_server_md5.c
Normal 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;
|
||||
}
|
175
hostapd-0.8/src/eap_server/eap_server_methods.c
Normal file
175
hostapd-0.8/src/eap_server/eap_server_methods.c
Normal 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;
|
||||
}
|
575
hostapd-0.8/src/eap_server/eap_server_mschapv2.c
Normal file
575
hostapd-0.8/src/eap_server/eap_server_mschapv2.c
Normal 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;
|
||||
}
|
570
hostapd-0.8/src/eap_server/eap_server_pax.c
Normal file
570
hostapd-0.8/src/eap_server/eap_server_pax.c
Normal 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;
|
||||
}
|
1387
hostapd-0.8/src/eap_server/eap_server_peap.c
Normal file
1387
hostapd-0.8/src/eap_server/eap_server_peap.c
Normal file
File diff suppressed because it is too large
Load diff
518
hostapd-0.8/src/eap_server/eap_server_psk.c
Normal file
518
hostapd-0.8/src/eap_server/eap_server_psk.c
Normal 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;
|
||||
}
|
844
hostapd-0.8/src/eap_server/eap_server_pwd.c
Normal file
844
hostapd-0.8/src/eap_server/eap_server_pwd.c
Normal 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;
|
||||
}
|
||||
|
543
hostapd-0.8/src/eap_server/eap_server_sake.c
Normal file
543
hostapd-0.8/src/eap_server/eap_server_sake.c
Normal 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;
|
||||
}
|
798
hostapd-0.8/src/eap_server/eap_server_sim.c
Normal file
798
hostapd-0.8/src/eap_server/eap_server_sim.c
Normal 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;
|
||||
}
|
286
hostapd-0.8/src/eap_server/eap_server_tls.c
Normal file
286
hostapd-0.8/src/eap_server/eap_server_tls.c
Normal 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;
|
||||
}
|
400
hostapd-0.8/src/eap_server/eap_server_tls_common.c
Normal file
400
hostapd-0.8/src/eap_server/eap_server_tls_common.c
Normal 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;
|
||||
}
|
582
hostapd-0.8/src/eap_server/eap_server_tnc.c
Normal file
582
hostapd-0.8/src/eap_server/eap_server_tnc.c
Normal 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;
|
||||
}
|
1430
hostapd-0.8/src/eap_server/eap_server_ttls.c
Normal file
1430
hostapd-0.8/src/eap_server/eap_server_ttls.c
Normal file
File diff suppressed because it is too large
Load diff
198
hostapd-0.8/src/eap_server/eap_server_vendor_test.c
Normal file
198
hostapd-0.8/src/eap_server/eap_server_vendor_test.c
Normal 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;
|
||||
}
|
517
hostapd-0.8/src/eap_server/eap_server_wsc.c
Normal file
517
hostapd-0.8/src/eap_server/eap_server_wsc.c
Normal 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;
|
||||
}
|
1338
hostapd-0.8/src/eap_server/eap_sim_db.c
Normal file
1338
hostapd-0.8/src/eap_server/eap_sim_db.c
Normal file
File diff suppressed because it is too large
Load diff
91
hostapd-0.8/src/eap_server/eap_sim_db.h
Normal file
91
hostapd-0.8/src/eap_server/eap_sim_db.h
Normal 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 */
|
91
hostapd-0.8/src/eap_server/eap_tls_common.h
Normal file
91
hostapd-0.8/src/eap_server/eap_tls_common.h
Normal 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 */
|
1206
hostapd-0.8/src/eap_server/ikev2.c
Normal file
1206
hostapd-0.8/src/eap_server/ikev2.c
Normal file
File diff suppressed because it is too large
Load diff
67
hostapd-0.8/src/eap_server/ikev2.h
Normal file
67
hostapd-0.8/src/eap_server/ikev2.h
Normal 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 */
|
1273
hostapd-0.8/src/eap_server/tncs.c
Normal file
1273
hostapd-0.8/src/eap_server/tncs.c
Normal file
File diff suppressed because it is too large
Load diff
49
hostapd-0.8/src/eap_server/tncs.h
Normal file
49
hostapd-0.8/src/eap_server/tncs.h
Normal 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 */
|
Loading…
Add table
Add a link
Reference in a new issue