Initializing repository

This commit is contained in:
2025-09-23 15:51:56 +03:00
commit 58130b21a9
871 changed files with 604586 additions and 0 deletions

8
src/eap_server/Makefile Normal file
View File

@@ -0,0 +1,8 @@
CFLAGS += -DCONFIG_HS20
LIB_OBJS= \
eap_server.o \
eap_server_identity.o \
eap_server_methods.o
include ../lib.rules

307
src/eap_server/eap.h Normal file
View File

@@ -0,0 +1,307 @@
/*
* hostapd / EAP Full Authenticator state machine (RFC 4137)
* Copyright (c) 2004-2014, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
*/
#ifndef EAP_H
#define EAP_H
#include "common/defs.h"
#include "utils/list.h"
#include "eap_common/eap_defs.h"
#include "eap_server/eap_methods.h"
#include "wpabuf.h"
struct eap_sm;
#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() */
u8 *salt;
size_t salt_len;
int phase2;
int force_version;
unsigned int remediation:1;
unsigned int macacl:1;
int ttls_auth; /* bitfield of
* EAP_TTLS_AUTH_{PAP,CHAP,MSCHAP,MSCHAPV2} */
struct hostapd_radius_attr *accept_attr;
u32 t_c_timestamp;
};
struct eap_eapol_interface {
/* Lower layer to full authenticator variables */
bool eapResp; /* shared with EAPOL Backend Authentication */
struct wpabuf *eapRespData;
bool portEnabled;
int retransWhile;
bool eapRestart; /* shared with EAPOL Authenticator PAE */
int eapSRTT;
int eapRTTVAR;
/* Full authenticator to lower layer variables */
bool eapReq; /* shared with EAPOL Backend Authentication */
bool eapNoReq; /* shared with EAPOL Backend Authentication */
bool eapSuccess;
bool eapFail;
bool eapTimeout;
struct wpabuf *eapReqData;
u8 *eapKeyData;
size_t eapKeyDataLen;
u8 *eapSessionId;
size_t eapSessionIdLen;
bool eapKeyAvailable; /* called keyAvailable in IEEE 802.1X-2004 */
/* AAA interface to full authenticator variables */
bool aaaEapReq;
bool aaaEapNoReq;
bool aaaSuccess;
bool aaaFail;
struct wpabuf *aaaEapReqData;
u8 *aaaEapKeyData;
size_t aaaEapKeyDataLen;
bool aaaEapKeyAvailable;
int aaaMethodTimeout;
/* Full authenticator to AAA interface variables */
bool aaaEapResp;
struct wpabuf *aaaEapRespData;
/* aaaIdentity -> eap_get_identity() */
bool aaaTimeout;
};
struct eap_server_erp_key {
struct dl_list list;
size_t rRK_len;
size_t rIK_len;
u8 rRK[ERP_MAX_KEY_LEN];
u8 rIK[ERP_MAX_KEY_LEN];
u32 recv_seq;
u8 cryptosuite;
char keyname_nai[];
};
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);
void (*log_msg)(void *ctx, const char *msg);
int (*get_erp_send_reauth_start)(void *ctx);
const char * (*get_erp_domain)(void *ctx);
struct eap_server_erp_key * (*erp_get_key)(void *ctx,
const char *keyname);
int (*erp_add_key)(void *ctx, struct eap_server_erp_key *erp);
};
struct eap_config {
/**
* ssl_ctx - TLS context
*
* This is passed to the EAP server implementation as a callback
* context for TLS operations.
*/
void *ssl_ctx;
void *msg_ctx;
/**
* eap_sim_db_priv - EAP-SIM/AKA database context
*
* This is passed to the EAP-SIM/AKA server implementation as a
* callback context.
*/
void *eap_sim_db_priv;
struct crypto_rsa_key *imsi_privacy_key;
bool backend_auth;
int eap_server;
/**
* pwd_group - The D-H group assigned for EAP-pwd
*
* If EAP-pwd is not used it can be set to zero.
*/
u16 pwd_group;
/**
* pac_opaque_encr_key - PAC-Opaque encryption key for EAP-FAST
*
* This parameter is used to set a key for EAP-FAST to encrypt the
* PAC-Opaque data. It can be set to %NULL if EAP-FAST is not used. If
* set, must point to a 16-octet key.
*/
u8 *pac_opaque_encr_key;
/**
* eap_fast_a_id - EAP-FAST authority identity (A-ID)
*
* If EAP-FAST is not used, this can be set to %NULL. In theory, this
* is a variable length field, but due to some existing implementations
* requiring A-ID to be 16 octets in length, it is recommended to use
* that length for the field to provide interoperability with deployed
* peer implementations.
*/
u8 *eap_fast_a_id;
/**
* eap_fast_a_id_len - Length of eap_fast_a_id buffer in octets
*/
size_t eap_fast_a_id_len;
/**
* eap_fast_a_id_info - EAP-FAST authority identifier information
*
* This A-ID-Info contains a user-friendly name for the A-ID. For
* example, this could be the enterprise and server names in
* human-readable format. This field is encoded as UTF-8. If EAP-FAST
* is not used, this can be set to %NULL.
*/
char *eap_fast_a_id_info;
/**
* eap_fast_prov - EAP-FAST provisioning modes
*
* 0 = provisioning disabled, 1 = only anonymous provisioning allowed,
* 2 = only authenticated provisioning allowed, 3 = both provisioning
* modes allowed.
*/
enum {
NO_PROV, ANON_PROV, AUTH_PROV, BOTH_PROV
} eap_fast_prov;
/**
* pac_key_lifetime - EAP-FAST PAC-Key lifetime in seconds
*
* This is the hard limit on how long a provisioned PAC-Key can be
* used.
*/
int pac_key_lifetime;
/**
* pac_key_refresh_time - EAP-FAST PAC-Key refresh time in seconds
*
* This is a soft limit on the PAC-Key. The server will automatically
* generate a new PAC-Key when this number of seconds (or fewer) of the
* lifetime remains.
*/
int pac_key_refresh_time;
int eap_teap_auth;
int eap_teap_pac_no_inner;
int eap_teap_separate_result;
enum eap_teap_id {
EAP_TEAP_ID_ALLOW_ANY = 0,
EAP_TEAP_ID_REQUIRE_USER = 1,
EAP_TEAP_ID_REQUIRE_MACHINE = 2,
EAP_TEAP_ID_REQUEST_USER_ACCEPT_MACHINE = 3,
EAP_TEAP_ID_REQUEST_MACHINE_ACCEPT_USER = 4,
EAP_TEAP_ID_REQUIRE_USER_AND_MACHINE = 5,
} eap_teap_id;
int eap_teap_method_sequence;
/**
* eap_sim_aka_result_ind - EAP-SIM/AKA protected success indication
*
* This controls whether the protected success/failure indication
* (AT_RESULT_IND) is used with EAP-SIM and EAP-AKA.
*/
int eap_sim_aka_result_ind;
int eap_sim_id;
/* Maximum number of fast re-authentications allowed after each full
* EAP-SIM/AKA authentication. */
int eap_sim_aka_fast_reauth_limit;
/**
* tnc - Trusted Network Connect (TNC)
*
* This controls whether TNC is enabled and will be required before the
* peer is allowed to connect. Note: This is only used with EAP-TTLS
* and EAP-FAST. If any other EAP method is enabled, the peer will be
* allowed to connect without TNC.
*/
int tnc;
/**
* wps - Wi-Fi Protected Setup context
*
* If WPS is used with an external RADIUS server (which is quite
* unlikely configuration), this is used to provide a pointer to WPS
* context data. Normally, this can be set to %NULL.
*/
struct wps_context *wps;
int fragment_size;
int pbc_in_m1;
/**
* server_id - Server identity
*/
u8 *server_id;
size_t server_id_len;
/**
* erp - Whether EAP Re-authentication Protocol (ERP) is enabled
*
* This controls whether the authentication server derives ERP key
* hierarchy (rRK and rIK) from full EAP authentication and allows
* these keys to be used to perform ERP to derive rMSK instead of full
* EAP authentication to derive MSK.
*/
int erp;
unsigned int tls_session_lifetime;
unsigned int tls_flags;
unsigned int max_auth_rounds;
unsigned int max_auth_rounds_short;
#ifdef CONFIG_TESTING_OPTIONS
bool skip_prot_success;
#endif /* CONFIG_TESTING_OPTIONS */
};
struct eap_session_data {
const struct wpabuf *assoc_wps_ie;
const struct wpabuf *assoc_p2p_ie;
const u8 *peer_addr;
#ifdef CONFIG_TESTING_OPTIONS
u32 tls_test_flags;
#endif /* CONFIG_TESTING_OPTIONS */
};
struct eap_sm * eap_server_sm_init(void *eapol_ctx,
const struct eapol_callbacks *eapol_cb,
const struct eap_config *conf,
const struct eap_session_data *sess);
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);
const char * eap_get_serial_num(struct eap_sm *sm);
const char * eap_get_method(struct eap_sm *sm);
const char * eap_get_imsi(struct eap_sm *sm);
struct eap_eapol_interface * eap_get_interface(struct eap_sm *sm);
void eap_server_clear_identity(struct eap_sm *sm);
void eap_server_mschap_rx_callback(struct eap_sm *sm, const char *source,
const u8 *username, size_t username_len,
const u8 *challenge, const u8 *response);
void eap_erp_update_identity(struct eap_sm *sm, const u8 *eap, size_t len);
void eap_user_free(struct eap_user *user);
void eap_server_config_free(struct eap_config *cfg);
#endif /* EAP_H */

210
src/eap_server/eap_i.h Normal file
View File

@@ -0,0 +1,210 @@
/*
* hostapd / EAP Authenticator state machine internal structures (RFC 4137)
* Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
*/
#ifndef EAP_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;
enum eap_type 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);
bool (*check)(struct eap_sm *sm, void *priv, struct wpabuf *respData);
void (*process)(struct eap_sm *sm, void *priv,
struct wpabuf *respData);
bool (*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() */
bool (*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);
/**
* getSessionId - Get EAP method specific Session-Id
* @sm: Pointer to EAP state machine allocated with eap_server_sm_init()
* @priv: Pointer to private EAP method data from eap_method::init()
* @len: Pointer to a variable to store Session-Id length
* Returns: Session-Id or %NULL if not available
*
* This function can be used to get the Session-Id from the EAP method.
* The Session-Id may already be stored in the method-specific private
* data or this function may derive the Session-Id.
*/
u8 * (*getSessionId)(struct eap_sm *sm, void *priv, size_t *len);
};
/**
* 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_INITIATE_REAUTH_START, EAP_INITIATE_RECEIVED
} EAP_state;
/* Constants */
int MaxRetrans;
struct eap_eapol_interface eap_if;
/* Full authenticator state machine local variables */
/* Long-term (maintained between packets) */
enum eap_type currentMethod;
int currentId;
enum {
METHOD_PROPOSED, METHOD_CONTINUE, METHOD_END
} methodState;
int retransCount;
struct wpabuf *lastReqData;
int methodTimeout;
/* Short-term (not maintained between packets) */
bool rxResp;
bool rxInitiate;
int respId;
enum eap_type respMethod;
int respVendor;
u32 respVendorMethod;
bool ignore;
enum {
DECISION_SUCCESS, DECISION_FAILURE, DECISION_CONTINUE,
DECISION_PASSTHROUGH, DECISION_INITIATE_REAUTH_START
} decision;
/* Miscellaneous variables */
const struct eap_method *m; /* selected EAP method */
/* not defined in RFC 4137 */
bool changed;
void *eapol_ctx;
const struct eapol_callbacks *eapol_cb;
void *eap_method_priv;
u8 *identity;
size_t identity_len;
char *serial_num;
char imsi[20];
char sim_aka_permanent[20];
/* 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;
const struct eap_config *cfg;
struct eap_config cfg_buf;
bool update_user;
unsigned int num_rounds;
unsigned int num_rounds_short;
enum {
METHOD_PENDING_NONE, METHOD_PENDING_WAIT, METHOD_PENDING_CONT
} method_pending;
/* Optional challenges generated in Phase 1 (EAP-FAST) */
u8 *auth_challenge;
u8 *peer_challenge;
/* Whether to use the EAP-FAST-MSCHAPv2 instantiation of EAP-MSCHAPv2.
* That variant is otherwise identical, but it generates the MSK using
* MS-MPPE keys in reverse order. */
bool eap_fast_mschapv2;
struct wpabuf *assoc_wps_ie;
struct wpabuf *assoc_p2p_ie;
bool start_reauth;
u8 peer_addr[ETH_ALEN];
bool initiate_reauth_start_sent;
bool try_initiate_reauth;
#ifdef CONFIG_TESTING_OPTIONS
u32 tls_test_flags;
#endif /* CONFIG_TESTING_OPTIONS */
};
int eap_user_get(struct eap_sm *sm, const u8 *identity, size_t identity_len,
int phase2);
void eap_log_msg(struct eap_sm *sm, const char *fmt, ...)
PRINTF_FORMAT(2, 3);
void eap_sm_process_nak(struct eap_sm *sm, const u8 *nak_list, size_t len);
#endif /* EAP_I_H */

View File

@@ -0,0 +1,52 @@
/*
* EAP server method registration
* Copyright (c) 2004-2009, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README 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,
enum eap_type method);
struct eap_method * eap_server_method_alloc(int version, int vendor,
enum eap_type method,
const char *name);
int eap_server_method_register(struct eap_method *method);
enum eap_type eap_server_get_type(const char *name, int *vendor);
void eap_server_unregister_methods(void);
const char * eap_server_get_name(int vendor, enum eap_type 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_unauth_tls_register(void);
int eap_server_wfa_unauth_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_teap_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);
int eap_server_eke_register(void);
#endif /* EAP_SERVER_METHODS_H */

2104
src/eap_server/eap_server.c Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,812 @@
/*
* hostapd / EAP-EKE (RFC 6124) server
* Copyright (c) 2013, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
*/
#include "includes.h"
#include "common.h"
#include "crypto/random.h"
#include "eap_server/eap_i.h"
#include "eap_common/eap_eke_common.h"
struct eap_eke_data {
enum {
IDENTITY, COMMIT, CONFIRM, FAILURE_REPORT, SUCCESS, FAILURE
} state;
u8 msk[EAP_MSK_LEN];
u8 emsk[EAP_EMSK_LEN];
u8 *peerid;
size_t peerid_len;
u8 peerid_type;
u8 serverid_type;
u8 dh_priv[EAP_EKE_MAX_DH_LEN];
u8 key[EAP_EKE_MAX_KEY_LEN];
struct eap_eke_session sess;
u8 nonce_p[EAP_EKE_MAX_NONCE_LEN];
u8 nonce_s[EAP_EKE_MAX_NONCE_LEN];
struct wpabuf *msgs;
int phase2;
u32 failure_code;
};
static const char * eap_eke_state_txt(int state)
{
switch (state) {
case IDENTITY:
return "IDENTITY";
case COMMIT:
return "COMMIT";
case CONFIRM:
return "CONFIRM";
case FAILURE_REPORT:
return "FAILURE_REPORT";
case SUCCESS:
return "SUCCESS";
case FAILURE:
return "FAILURE";
default:
return "?";
}
}
static void eap_eke_state(struct eap_eke_data *data, int state)
{
wpa_printf(MSG_DEBUG, "EAP-EKE: %s -> %s",
eap_eke_state_txt(data->state),
eap_eke_state_txt(state));
data->state = state;
}
static void eap_eke_fail(struct eap_eke_data *data, u32 code)
{
wpa_printf(MSG_DEBUG, "EAP-EKE: Failure - code 0x%x", code);
data->failure_code = code;
eap_eke_state(data, FAILURE_REPORT);
}
static void * eap_eke_init(struct eap_sm *sm)
{
struct eap_eke_data *data;
size_t i;
data = os_zalloc(sizeof(*data));
if (data == NULL)
return NULL;
eap_eke_state(data, IDENTITY);
data->serverid_type = EAP_EKE_ID_OPAQUE;
for (i = 0; i < sm->cfg->server_id_len; i++) {
if (sm->cfg->server_id[i] == '.' &&
data->serverid_type == EAP_EKE_ID_OPAQUE)
data->serverid_type = EAP_EKE_ID_FQDN;
if (sm->cfg->server_id[i] == '@')
data->serverid_type = EAP_EKE_ID_NAI;
}
data->phase2 = sm->init_phase2;
return data;
}
static void eap_eke_reset(struct eap_sm *sm, void *priv)
{
struct eap_eke_data *data = priv;
eap_eke_session_clean(&data->sess);
os_free(data->peerid);
wpabuf_free(data->msgs);
bin_clear_free(data, sizeof(*data));
}
static struct wpabuf * eap_eke_build_msg(struct eap_eke_data *data,
u8 id, size_t length, u8 eke_exch)
{
struct wpabuf *msg;
size_t plen;
plen = 1 + length;
msg = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_EKE, plen,
EAP_CODE_REQUEST, id);
if (msg == NULL) {
wpa_printf(MSG_ERROR, "EAP-EKE: Failed to allocate memory");
return NULL;
}
wpabuf_put_u8(msg, eke_exch);
return msg;
}
static int supported_proposal(const u8 *pos)
{
if (pos[0] == EAP_EKE_DHGROUP_EKE_16 &&
pos[1] == EAP_EKE_ENCR_AES128_CBC &&
pos[2] == EAP_EKE_PRF_HMAC_SHA2_256 &&
pos[3] == EAP_EKE_MAC_HMAC_SHA2_256)
return 1;
if (pos[0] == EAP_EKE_DHGROUP_EKE_15 &&
pos[1] == EAP_EKE_ENCR_AES128_CBC &&
pos[2] == EAP_EKE_PRF_HMAC_SHA2_256 &&
pos[3] == EAP_EKE_MAC_HMAC_SHA2_256)
return 1;
if (pos[0] == EAP_EKE_DHGROUP_EKE_14 &&
pos[1] == EAP_EKE_ENCR_AES128_CBC &&
pos[2] == EAP_EKE_PRF_HMAC_SHA2_256 &&
pos[3] == EAP_EKE_MAC_HMAC_SHA2_256)
return 1;
if (pos[0] == EAP_EKE_DHGROUP_EKE_14 &&
pos[1] == EAP_EKE_ENCR_AES128_CBC &&
pos[2] == EAP_EKE_PRF_HMAC_SHA1 &&
pos[3] == EAP_EKE_MAC_HMAC_SHA1)
return 1;
return 0;
}
static struct wpabuf * eap_eke_build_failure(struct eap_eke_data *data, u8 id)
{
struct wpabuf *msg;
wpa_printf(MSG_DEBUG, "EAP-EKE: Request/Failure: Failure-Code=0x%x",
data->failure_code);
msg = eap_eke_build_msg(data, id, 4, EAP_EKE_FAILURE);
if (msg == NULL) {
eap_eke_state(data, FAILURE);
return NULL;
}
wpabuf_put_be32(msg, data->failure_code);
return msg;
}
static struct wpabuf * eap_eke_build_identity(struct eap_sm *sm,
struct eap_eke_data *data,
u8 id)
{
struct wpabuf *msg;
size_t plen;
wpa_printf(MSG_DEBUG, "EAP-EKE: Request/Identity");
plen = 2 + 4 * 4 + 1 + sm->cfg->server_id_len;
msg = eap_eke_build_msg(data, id, plen, EAP_EKE_ID);
if (msg == NULL)
return NULL;
wpabuf_put_u8(msg, 4); /* NumProposals */
wpabuf_put_u8(msg, 0); /* Reserved */
/* Proposal - DH Group 16 with AES128-CBC and SHA256 */
wpabuf_put_u8(msg, EAP_EKE_DHGROUP_EKE_16); /* Group Description */
wpabuf_put_u8(msg, EAP_EKE_ENCR_AES128_CBC); /* Encryption */
wpabuf_put_u8(msg, EAP_EKE_PRF_HMAC_SHA2_256); /* PRF */
wpabuf_put_u8(msg, EAP_EKE_MAC_HMAC_SHA2_256); /* MAC */
/* Proposal - DH Group 15 with AES128-CBC and SHA256 */
wpabuf_put_u8(msg, EAP_EKE_DHGROUP_EKE_15); /* Group Description */
wpabuf_put_u8(msg, EAP_EKE_ENCR_AES128_CBC); /* Encryption */
wpabuf_put_u8(msg, EAP_EKE_PRF_HMAC_SHA2_256); /* PRF */
wpabuf_put_u8(msg, EAP_EKE_MAC_HMAC_SHA2_256); /* MAC */
/* Proposal - DH Group 14 with AES128-CBC and SHA256 */
wpabuf_put_u8(msg, EAP_EKE_DHGROUP_EKE_14); /* Group Description */
wpabuf_put_u8(msg, EAP_EKE_ENCR_AES128_CBC); /* Encryption */
wpabuf_put_u8(msg, EAP_EKE_PRF_HMAC_SHA2_256); /* PRF */
wpabuf_put_u8(msg, EAP_EKE_MAC_HMAC_SHA2_256); /* MAC */
/*
* Proposal - DH Group 14 with AES128-CBC and SHA1
* (mandatory to implement algorithms)
*/
wpabuf_put_u8(msg, EAP_EKE_DHGROUP_EKE_14); /* Group Description */
wpabuf_put_u8(msg, EAP_EKE_ENCR_AES128_CBC); /* Encryption */
wpabuf_put_u8(msg, EAP_EKE_PRF_HMAC_SHA1); /* PRF */
wpabuf_put_u8(msg, EAP_EKE_MAC_HMAC_SHA1); /* MAC */
/* Server IDType + Identity */
wpabuf_put_u8(msg, data->serverid_type);
wpabuf_put_data(msg, sm->cfg->server_id, sm->cfg->server_id_len);
wpabuf_free(data->msgs);
data->msgs = wpabuf_dup(msg);
if (data->msgs == NULL) {
wpabuf_free(msg);
return NULL;
}
return msg;
}
static struct wpabuf * eap_eke_build_commit(struct eap_sm *sm,
struct eap_eke_data *data, u8 id)
{
struct wpabuf *msg;
u8 pub[EAP_EKE_MAX_DH_LEN];
wpa_printf(MSG_DEBUG, "EAP-EKE: Request/Commit");
if (sm->user == NULL || sm->user->password == NULL) {
wpa_printf(MSG_INFO, "EAP-EKE: Password with not configured");
eap_eke_fail(data, EAP_EKE_FAIL_PASSWD_NOT_FOUND);
return eap_eke_build_failure(data, id);
}
if (eap_eke_derive_key(&data->sess, sm->user->password,
sm->user->password_len,
sm->cfg->server_id, sm->cfg->server_id_len,
data->peerid, data->peerid_len, data->key) < 0) {
wpa_printf(MSG_INFO, "EAP-EKE: Failed to derive key");
eap_eke_fail(data, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR);
return eap_eke_build_failure(data, id);
}
msg = eap_eke_build_msg(data, id, data->sess.dhcomp_len,
EAP_EKE_COMMIT);
if (msg == NULL) {
eap_eke_fail(data, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR);
return eap_eke_build_failure(data, id);
}
/*
* y_s = g ^ x_s (mod p)
* x_s = random number 2 .. p-1
* temp = prf(0+, password)
* key = prf+(temp, ID_S | ID_P)
* DHComponent_S = Encr(key, y_s)
*/
if (eap_eke_dh_init(data->sess.dhgroup, data->dh_priv, pub) < 0) {
wpa_printf(MSG_INFO, "EAP-EKE: Failed to initialize DH");
wpabuf_free(msg);
eap_eke_fail(data, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR);
return eap_eke_build_failure(data, id);
}
if (eap_eke_dhcomp(&data->sess, data->key, pub,
wpabuf_put(msg, data->sess.dhcomp_len))
< 0) {
wpa_printf(MSG_INFO, "EAP-EKE: Failed to build DHComponent_S");
wpabuf_free(msg);
eap_eke_fail(data, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR);
return eap_eke_build_failure(data, id);
}
if (wpabuf_resize(&data->msgs, wpabuf_len(msg)) < 0) {
wpabuf_free(msg);
eap_eke_fail(data, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR);
return eap_eke_build_failure(data, id);
}
wpabuf_put_buf(data->msgs, msg);
return msg;
}
static struct wpabuf * eap_eke_build_confirm(struct eap_sm *sm,
struct eap_eke_data *data, u8 id)
{
struct wpabuf *msg;
size_t plen, prot_len;
u8 nonces[2 * EAP_EKE_MAX_NONCE_LEN];
u8 *auth;
wpa_printf(MSG_DEBUG, "EAP-EKE: Request/Confirm");
plen = data->sess.pnonce_ps_len + data->sess.prf_len;
msg = eap_eke_build_msg(data, id, plen, EAP_EKE_CONFIRM);
if (msg == NULL) {
eap_eke_fail(data, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR);
return eap_eke_build_failure(data, id);
}
if (random_get_bytes(data->nonce_s, data->sess.nonce_len)) {
wpabuf_free(msg);
eap_eke_fail(data, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR);
return eap_eke_build_failure(data, id);
}
wpa_hexdump_key(MSG_DEBUG, "EAP-EKE: Nonce_S",
data->nonce_s, data->sess.nonce_len);
os_memcpy(nonces, data->nonce_p, data->sess.nonce_len);
os_memcpy(nonces + data->sess.nonce_len, data->nonce_s,
data->sess.nonce_len);
prot_len = wpabuf_tailroom(msg);
if (eap_eke_prot(&data->sess, nonces, 2 * data->sess.nonce_len,
wpabuf_put(msg, 0), &prot_len) < 0) {
wpabuf_free(msg);
eap_eke_fail(data, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR);
return eap_eke_build_failure(data, id);
}
wpabuf_put(msg, prot_len);
if (eap_eke_derive_ka(&data->sess,
sm->cfg->server_id, sm->cfg->server_id_len,
data->peerid, data->peerid_len,
data->nonce_p, data->nonce_s) < 0) {
wpabuf_free(msg);
eap_eke_fail(data, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR);
return eap_eke_build_failure(data, id);
}
auth = wpabuf_put(msg, data->sess.prf_len);
if (eap_eke_auth(&data->sess, "EAP-EKE server", data->msgs, auth) < 0) {
wpabuf_free(msg);
eap_eke_fail(data, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR);
return eap_eke_build_failure(data, id);
}
wpa_hexdump(MSG_DEBUG, "EAP-EKE: Auth_S", auth, data->sess.prf_len);
return msg;
}
static struct wpabuf * eap_eke_buildReq(struct eap_sm *sm, void *priv, u8 id)
{
struct eap_eke_data *data = priv;
switch (data->state) {
case IDENTITY:
return eap_eke_build_identity(sm, data, id);
case COMMIT:
return eap_eke_build_commit(sm, data, id);
case CONFIRM:
return eap_eke_build_confirm(sm, data, id);
case FAILURE_REPORT:
return eap_eke_build_failure(data, id);
default:
wpa_printf(MSG_DEBUG, "EAP-EKE: Unknown state %d in buildReq",
data->state);
break;
}
return NULL;
}
static bool eap_eke_check(struct eap_sm *sm, void *priv,
struct wpabuf *respData)
{
struct eap_eke_data *data = priv;
size_t len;
const u8 *pos;
u8 eke_exch;
pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_EKE, respData, &len);
if (pos == NULL || len < 1) {
wpa_printf(MSG_INFO, "EAP-EKE: Invalid frame");
return true;
}
eke_exch = *pos;
wpa_printf(MSG_DEBUG, "EAP-EKE: Received frame: EKE-Exch=%d", eke_exch);
if (data->state == IDENTITY && eke_exch == EAP_EKE_ID)
return false;
if (data->state == COMMIT && eke_exch == EAP_EKE_COMMIT)
return false;
if (data->state == CONFIRM && eke_exch == EAP_EKE_CONFIRM)
return false;
if (eke_exch == EAP_EKE_FAILURE)
return false;
wpa_printf(MSG_INFO, "EAP-EKE: Unexpected EKE-Exch=%d in state=%d",
eke_exch, data->state);
return true;
}
static void eap_eke_process_identity(struct eap_sm *sm,
struct eap_eke_data *data,
const struct wpabuf *respData,
const u8 *payload, size_t payloadlen)
{
const u8 *pos, *end;
int i;
wpa_printf(MSG_DEBUG, "EAP-EKE: Received Response/Identity");
if (data->state != IDENTITY) {
eap_eke_fail(data, EAP_EKE_FAIL_PROTO_ERROR);
return;
}
pos = payload;
end = payload + payloadlen;
if (pos + 2 + 4 + 1 > end) {
wpa_printf(MSG_INFO, "EAP-EKE: Too short EAP-EKE-ID payload");
eap_eke_fail(data, EAP_EKE_FAIL_PROTO_ERROR);
return;
}
if (*pos != 1) {
wpa_printf(MSG_INFO, "EAP-EKE: Unexpected NumProposals %d (expected 1)",
*pos);
eap_eke_fail(data, EAP_EKE_FAIL_PROTO_ERROR);
return;
}
pos += 2;
if (!supported_proposal(pos)) {
wpa_printf(MSG_INFO, "EAP-EKE: Unexpected Proposal (%u:%u:%u:%u)",
pos[0], pos[1], pos[2], pos[3]);
eap_eke_fail(data, EAP_EKE_FAIL_PROTO_ERROR);
return;
}
wpa_printf(MSG_DEBUG, "EAP-EKE: Selected Proposal (%u:%u:%u:%u)",
pos[0], pos[1], pos[2], pos[3]);
if (eap_eke_session_init(&data->sess, pos[0], pos[1], pos[2], pos[3]) <
0) {
eap_eke_fail(data, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR);
return;
}
pos += 4;
data->peerid_type = *pos++;
os_free(data->peerid);
data->peerid = os_memdup(pos, end - pos);
if (data->peerid == NULL) {
wpa_printf(MSG_INFO, "EAP-EKE: Failed to allocate memory for peerid");
eap_eke_fail(data, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR);
return;
}
data->peerid_len = end - pos;
wpa_printf(MSG_DEBUG, "EAP-EKE: Peer IDType %u", data->peerid_type);
wpa_hexdump_ascii(MSG_DEBUG, "EAP-EKE: Peer Identity",
data->peerid, data->peerid_len);
if (eap_user_get(sm, data->peerid, data->peerid_len, data->phase2)) {
wpa_printf(MSG_INFO, "EAP-EKE: Peer Identity not found from user database");
eap_eke_fail(data, EAP_EKE_FAIL_PASSWD_NOT_FOUND);
return;
}
for (i = 0; i < EAP_MAX_METHODS; i++) {
if (sm->user->methods[i].vendor == EAP_VENDOR_IETF &&
sm->user->methods[i].method == EAP_TYPE_EKE)
break;
}
if (i == EAP_MAX_METHODS) {
wpa_printf(MSG_INFO, "EAP-EKE: Matching user entry does not allow EAP-EKE");
eap_eke_fail(data, EAP_EKE_FAIL_PASSWD_NOT_FOUND);
return;
}
if (sm->user->password == NULL || sm->user->password_len == 0) {
wpa_printf(MSG_INFO, "EAP-EKE: No password configured for peer");
eap_eke_fail(data, EAP_EKE_FAIL_PASSWD_NOT_FOUND);
return;
}
if (wpabuf_resize(&data->msgs, wpabuf_len(respData)) < 0) {
eap_eke_fail(data, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR);
return;
}
wpabuf_put_buf(data->msgs, respData);
eap_eke_state(data, COMMIT);
}
static void eap_eke_process_commit(struct eap_sm *sm,
struct eap_eke_data *data,
const struct wpabuf *respData,
const u8 *payload, size_t payloadlen)
{
const u8 *pos, *end, *dhcomp, *pnonce;
size_t decrypt_len;
wpa_printf(MSG_DEBUG, "EAP-EKE: Received Response/Commit");
if (data->state != COMMIT) {
eap_eke_fail(data, EAP_EKE_FAIL_PROTO_ERROR);
return;
}
pos = payload;
end = payload + payloadlen;
if (pos + data->sess.dhcomp_len + data->sess.pnonce_len > end) {
wpa_printf(MSG_DEBUG, "EAP-EKE: Too short EAP-EKE-Commit");
eap_eke_fail(data, EAP_EKE_FAIL_PROTO_ERROR);
return;
}
wpa_hexdump(MSG_DEBUG, "EAP-EKE: DHComponent_P",
pos, data->sess.dhcomp_len);
dhcomp = pos;
pos += data->sess.dhcomp_len;
wpa_hexdump(MSG_DEBUG, "EAP-EKE: PNonce_P", pos, data->sess.pnonce_len);
pnonce = pos;
pos += data->sess.pnonce_len;
wpa_hexdump(MSG_DEBUG, "EAP-EKE: CBValue", pos, end - pos);
if (eap_eke_shared_secret(&data->sess, data->key, data->dh_priv, dhcomp)
< 0) {
wpa_printf(MSG_INFO, "EAP-EKE: Failed to derive shared secret");
eap_eke_fail(data, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR);
return;
}
if (eap_eke_derive_ke_ki(&data->sess,
sm->cfg->server_id, sm->cfg->server_id_len,
data->peerid, data->peerid_len) < 0) {
wpa_printf(MSG_INFO, "EAP-EKE: Failed to derive Ke/Ki");
eap_eke_fail(data, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR);
return;
}
decrypt_len = sizeof(data->nonce_p);
if (eap_eke_decrypt_prot(&data->sess, pnonce, data->sess.pnonce_len,
data->nonce_p, &decrypt_len) < 0) {
wpa_printf(MSG_INFO, "EAP-EKE: Failed to decrypt PNonce_P");
eap_eke_fail(data, EAP_EKE_FAIL_AUTHENTICATION_FAIL);
return;
}
if (decrypt_len < (size_t) data->sess.nonce_len) {
wpa_printf(MSG_INFO, "EAP-EKE: PNonce_P protected data too short to include Nonce_P");
eap_eke_fail(data, EAP_EKE_FAIL_AUTHENTICATION_FAIL);
return;
}
wpa_hexdump_key(MSG_DEBUG, "EAP-EKE: Nonce_P",
data->nonce_p, data->sess.nonce_len);
if (wpabuf_resize(&data->msgs, wpabuf_len(respData)) < 0) {
eap_eke_fail(data, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR);
return;
}
wpabuf_put_buf(data->msgs, respData);
eap_eke_state(data, CONFIRM);
}
static void eap_eke_process_confirm(struct eap_sm *sm,
struct eap_eke_data *data,
const struct wpabuf *respData,
const u8 *payload, size_t payloadlen)
{
size_t decrypt_len;
u8 nonce[EAP_EKE_MAX_NONCE_LEN];
u8 auth_p[EAP_EKE_MAX_HASH_LEN];
wpa_printf(MSG_DEBUG, "EAP-EKE: Received Response/Confirm");
if (data->state != CONFIRM) {
eap_eke_fail(data, EAP_EKE_FAIL_PROTO_ERROR);
return;
}
wpa_printf(MSG_DEBUG, "EAP-EKE: Received Response/Confirm");
if (payloadlen < (size_t) data->sess.pnonce_len + data->sess.prf_len) {
wpa_printf(MSG_DEBUG, "EAP-EKE: Too short EAP-EKE-Confirm");
eap_eke_fail(data, EAP_EKE_FAIL_PROTO_ERROR);
return;
}
decrypt_len = sizeof(nonce);
if (eap_eke_decrypt_prot(&data->sess, payload, data->sess.pnonce_len,
nonce, &decrypt_len) < 0) {
wpa_printf(MSG_INFO, "EAP-EKE: Failed to decrypt PNonce_S");
eap_eke_fail(data, EAP_EKE_FAIL_AUTHENTICATION_FAIL);
return;
}
if (decrypt_len < (size_t) data->sess.nonce_len) {
wpa_printf(MSG_INFO, "EAP-EKE: PNonce_S protected data too short to include Nonce_S");
eap_eke_fail(data, EAP_EKE_FAIL_AUTHENTICATION_FAIL);
return;
}
wpa_hexdump_key(MSG_DEBUG, "EAP-EKE: Received Nonce_S",
nonce, data->sess.nonce_len);
if (os_memcmp(nonce, data->nonce_s, data->sess.nonce_len) != 0) {
wpa_printf(MSG_INFO, "EAP-EKE: Received Nonce_S does not match previously sent Nonce_S");
eap_eke_fail(data, EAP_EKE_FAIL_AUTHENTICATION_FAIL);
return;
}
if (eap_eke_auth(&data->sess, "EAP-EKE peer", data->msgs, auth_p) < 0) {
wpa_printf(MSG_INFO, "EAP-EKE: Could not derive Auth_P");
eap_eke_fail(data, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR);
return;
}
wpa_hexdump(MSG_DEBUG, "EAP-EKE: Auth_P", auth_p, data->sess.prf_len);
if (os_memcmp_const(auth_p, payload + data->sess.pnonce_len,
data->sess.prf_len) != 0) {
wpa_printf(MSG_INFO, "EAP-EKE: Auth_P does not match");
eap_eke_fail(data, EAP_EKE_FAIL_AUTHENTICATION_FAIL);
return;
}
if (eap_eke_derive_msk(&data->sess, sm->cfg->server_id,
sm->cfg->server_id_len,
data->peerid, data->peerid_len,
data->nonce_s, data->nonce_p,
data->msk, data->emsk) < 0) {
wpa_printf(MSG_INFO, "EAP-EKE: Failed to derive MSK/EMSK");
eap_eke_fail(data, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR);
return;
}
os_memset(data->dh_priv, 0, sizeof(data->dh_priv));
os_memset(data->key, 0, sizeof(data->key));
eap_eke_session_clean(&data->sess);
eap_eke_state(data, SUCCESS);
}
static void eap_eke_process_failure(struct eap_sm *sm,
struct eap_eke_data *data,
const struct wpabuf *respData,
const u8 *payload, size_t payloadlen)
{
u32 code;
wpa_printf(MSG_DEBUG, "EAP-EKE: Received Response/Failure");
if (payloadlen < 4) {
wpa_printf(MSG_DEBUG, "EAP-EKE: Too short EAP-EKE-Failure");
eap_eke_state(data, FAILURE);
return;
}
code = WPA_GET_BE32(payload);
wpa_printf(MSG_DEBUG, "EAP-EKE: Peer reported failure code 0x%x", code);
eap_eke_state(data, FAILURE);
}
static void eap_eke_process(struct eap_sm *sm, void *priv,
struct wpabuf *respData)
{
struct eap_eke_data *data = priv;
u8 eke_exch;
size_t len;
const u8 *pos, *end;
pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_EKE, respData, &len);
if (pos == NULL || len < 1)
return;
eke_exch = *pos;
end = pos + len;
pos++;
wpa_hexdump(MSG_DEBUG, "EAP-EKE: Received payload", pos, end - pos);
switch (eke_exch) {
case EAP_EKE_ID:
eap_eke_process_identity(sm, data, respData, pos, end - pos);
break;
case EAP_EKE_COMMIT:
eap_eke_process_commit(sm, data, respData, pos, end - pos);
break;
case EAP_EKE_CONFIRM:
eap_eke_process_confirm(sm, data, respData, pos, end - pos);
break;
case EAP_EKE_FAILURE:
eap_eke_process_failure(sm, data, respData, pos, end - pos);
break;
}
}
static bool eap_eke_isDone(struct eap_sm *sm, void *priv)
{
struct eap_eke_data *data = priv;
return data->state == SUCCESS || data->state == FAILURE;
}
static u8 * eap_eke_getKey(struct eap_sm *sm, void *priv, size_t *len)
{
struct eap_eke_data *data = priv;
u8 *key;
if (data->state != SUCCESS)
return NULL;
key = os_memdup(data->msk, EAP_MSK_LEN);
if (key == NULL)
return NULL;
*len = EAP_MSK_LEN;
return key;
}
static u8 * eap_eke_get_emsk(struct eap_sm *sm, void *priv, size_t *len)
{
struct eap_eke_data *data = priv;
u8 *key;
if (data->state != SUCCESS)
return NULL;
key = os_memdup(data->emsk, EAP_EMSK_LEN);
if (key == NULL)
return NULL;
*len = EAP_EMSK_LEN;
return key;
}
static bool eap_eke_isSuccess(struct eap_sm *sm, void *priv)
{
struct eap_eke_data *data = priv;
return data->state == SUCCESS;
}
static u8 * eap_eke_get_session_id(struct eap_sm *sm, void *priv, size_t *len)
{
struct eap_eke_data *data = priv;
u8 *sid;
size_t sid_len;
if (data->state != SUCCESS)
return NULL;
sid_len = 1 + 2 * data->sess.nonce_len;
sid = os_malloc(sid_len);
if (sid == NULL)
return NULL;
sid[0] = EAP_TYPE_EKE;
os_memcpy(sid + 1, data->nonce_p, data->sess.nonce_len);
os_memcpy(sid + 1 + data->sess.nonce_len, data->nonce_s,
data->sess.nonce_len);
*len = sid_len;
return sid;
}
int eap_server_eke_register(void)
{
struct eap_method *eap;
eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION,
EAP_VENDOR_IETF, EAP_TYPE_EKE, "EKE");
if (eap == NULL)
return -1;
eap->init = eap_eke_init;
eap->reset = eap_eke_reset;
eap->buildReq = eap_eke_buildReq;
eap->check = eap_eke_check;
eap->process = eap_eke_process;
eap->isDone = eap_eke_isDone;
eap->getKey = eap_eke_getKey;
eap->isSuccess = eap_eke_isSuccess;
eap->get_emsk = eap_eke_get_emsk;
eap->getSessionId = eap_eke_get_session_id;
return eap_server_method_register(eap);
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,649 @@
/*
* hostapd / EAP-GPSK (RFC 5433) server
* Copyright (c) 2006-2007, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
*/
#include "includes.h"
#include "common.h"
#include "crypto/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 session_id[128];
size_t id_len;
u8 *id_peer;
size_t id_peer_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;
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_peer);
bin_clear_free(data, sizeof(*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 + sm->cfg->server_id_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, sm->cfg->server_id_len);
wpabuf_put_data(req, sm->cfg->server_id, sm->cfg->server_id_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 + sm->cfg->server_id_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, sm->cfg->server_id_len);
wpabuf_put_data(req, sm->cfg->server_id, sm->cfg->server_id_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)
{
wpabuf_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 bool 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_memdup(pos, alen);
if (data->id_peer == NULL) {
wpa_printf(MSG_DEBUG, "EAP-GPSK: Not enough memory to store "
"%d-octet ID_Peer", alen);
return;
}
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 != sm->cfg->server_id_len ||
os_memcmp(pos, sm->cfg->server_id, 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,
sm->cfg->server_id, sm->cfg->server_id_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;
}
if (eap_gpsk_derive_session_id(sm->user->password,
sm->user->password_len,
data->vendor, data->specifier,
data->rand_peer, data->rand_server,
data->id_peer, data->id_peer_len,
sm->cfg->server_id,
sm->cfg->server_id_len,
EAP_TYPE_GPSK,
data->session_id, &data->id_len) < 0) {
wpa_printf(MSG_DEBUG, "EAP-GPSK: Failed to derive Session-Id");
eap_gpsk_state(data, FAILURE);
return;
}
wpa_hexdump(MSG_DEBUG, "EAP-GPSK: Derived Session-Id",
data->session_id, data->id_len);
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_const(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_const(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 bool 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_memdup(data->msk, EAP_MSK_LEN);
if (key == NULL)
return NULL;
*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_memdup(data->emsk, EAP_EMSK_LEN);
if (key == NULL)
return NULL;
*len = EAP_EMSK_LEN;
return key;
}
static bool eap_gpsk_isSuccess(struct eap_sm *sm, void *priv)
{
struct eap_gpsk_data *data = priv;
return data->state == SUCCESS;
}
static u8 * eap_gpsk_get_session_id(struct eap_sm *sm, void *priv, size_t *len)
{
struct eap_gpsk_data *data = priv;
u8 *sid;
if (data->state != SUCCESS)
return NULL;
sid = os_memdup(data->session_id, data->id_len);
if (sid == NULL)
return NULL;
*len = data->id_len;
return sid;
}
int eap_server_gpsk_register(void)
{
struct eap_method *eap;
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;
eap->getSessionId = eap_gpsk_get_session_id;
return eap_server_method_register(eap);
}

View File

@@ -0,0 +1,219 @@
/*
* hostapd / EAP-GTC (RFC 3748)
* Copyright (c) 2004-2006, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
*/
#include "includes.h"
#include "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 bool 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_memdup(pos, sm->identity_len);
if (sm->identity == NULL) {
data->state = FAILURE;
return;
}
}
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_const(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 bool eap_gtc_isDone(struct eap_sm *sm, void *priv)
{
struct eap_gtc_data *data = priv;
return data->state != CONTINUE;
}
static bool 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;
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;
return eap_server_method_register(eap);
}

View File

@@ -0,0 +1,177 @@
/*
* hostapd / EAP-Identity
* Copyright (c) 2004-2006, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
*/
#include "includes.h"
#include "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 bool 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;
char *buf;
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);
buf = os_malloc(len * 4 + 1);
if (buf) {
printf_encode(buf, len * 4 + 1, pos, len);
eap_log_msg(sm, "EAP-Response/Identity '%s'", buf);
os_free(buf);
}
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 bool eap_identity_isDone(struct eap_sm *sm, void *priv)
{
struct eap_identity_data *data = priv;
return data->state != CONTINUE;
}
static bool 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;
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;
return eap_server_method_register(eap);
}

View File

@@ -0,0 +1,571 @@
/*
* EAP-IKEv2 server (RFC 5106)
* Copyright (c) 2007, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
*/
#include "includes.h"
#include "common.h"
#include "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->cfg->fragment_size > 0 ?
sm->cfg->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 = os_memdup(sm->cfg->server_id, sm->cfg->server_id_len);
if (data->ikev2.IDi == NULL)
goto failed;
data->ikev2.IDi_len = sm->cfg->server_id_len;
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);
bin_clear_free(data, sizeof(*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;
}
/* fall 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 bool 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,
int frag_ack)
{
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 && !frag_ack) {
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 */
if (message_length > 50000) {
/* Limit maximum memory allocation */
wpa_printf(MSG_DEBUG,
"EAP-IKEV2: Ignore too long message");
return -1;
}
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,
data->state == WAIT_FRAG_ACK && len == 0) < 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 bool eap_ikev2_isDone(struct eap_sm *sm, void *priv)
{
struct eap_ikev2_data *data = priv;
return data->state == DONE || data->state == FAIL;
}
static bool 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;
}
static u8 * eap_ikev2_get_session_id(struct eap_sm *sm, void *priv, size_t *len)
{
struct eap_ikev2_data *data = priv;
u8 *sid;
size_t sid_len;
size_t offset;
if (data->state != DONE || !data->keymat_ok)
return NULL;
sid_len = 1 + data->ikev2.i_nonce_len + data->ikev2.r_nonce_len;
sid = os_malloc(sid_len);
if (sid) {
offset = 0;
sid[offset] = EAP_TYPE_IKEV2;
offset++;
os_memcpy(sid + offset, data->ikev2.i_nonce,
data->ikev2.i_nonce_len);
offset += data->ikev2.i_nonce_len;
os_memcpy(sid + offset, data->ikev2.r_nonce,
data->ikev2.r_nonce_len);
*len = sid_len;
wpa_hexdump(MSG_DEBUG, "EAP-IKEV2: Derived Session-Id",
sid, sid_len);
}
return sid;
}
int eap_server_ikev2_register(void)
{
struct eap_method *eap;
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;
eap->getSessionId = eap_ikev2_get_session_id;
return eap_server_method_register(eap);
}

View File

@@ -0,0 +1,171 @@
/*
* hostapd / EAP-MD5 server
* Copyright (c) 2004-2012, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
*/
#include "includes.h"
#include "common.h"
#include "crypto/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 bool 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);
if (chap_md5(id, sm->user->password, sm->user->password_len,
data->challenge, CHALLENGE_LEN, hash)) {
wpa_printf(MSG_INFO, "EAP-MD5: CHAP MD5 operation failed");
data->state = FAILURE;
return;
}
if (os_memcmp_const(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 bool eap_md5_isDone(struct eap_sm *sm, void *priv)
{
struct eap_md5_data *data = priv;
return data->state != CONTINUE;
}
static bool 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;
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;
return eap_server_method_register(eap);
}

View File

@@ -0,0 +1,178 @@
/*
* EAP server method registration
* Copyright (c) 2004-2009, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
*/
#include "includes.h"
#include "common.h"
#include "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,
enum eap_type 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.
*/
enum eap_type 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,
enum eap_type 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()
*/
static 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 from eap_server_method_alloc()
* 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. The caller must not free the allocated method data
* regardless of the return value.
*/
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) {
eap_server_method_free(method);
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) {
eap_server_method_free(method);
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 "unknown" 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, enum eap_type type)
{
struct eap_method *m;
if (vendor == EAP_VENDOR_IETF && type == EAP_TYPE_EXPANDED)
return "expanded";
for (m = eap_methods; m; m = m->next) {
if (m->vendor == vendor && m->method == type)
return m->name;
}
return "unknown";
}

View File

@@ -0,0 +1,616 @@
/*
* hostapd / EAP-MSCHAPv2 (draft-kamath-pppext-eap-mschapv2-00.txt) server
* Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
*/
#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;
wpa_printf(MSG_DEBUG, "EAP-%sMSCHAPv2 init%s%s",
sm->eap_fast_mschapv2 ? "FAST-" : "",
sm->peer_challenge && sm->auth_challenge ?
" with preset challenges" : "",
sm->init_phase2 ? " for Phase 2" : "");
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_memdup(sm->peer_challenge,
CHALLENGE_LEN);
if (data->peer_challenge == NULL) {
os_free(data);
return NULL;
}
}
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);
bin_clear_free(data, sizeof(*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;
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 + sm->cfg->server_id_len;
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, sm->cfg->server_id, sm->cfg->server_id_len);
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 bool 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;
char *buf;
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);
buf = os_malloc(name_len * 4 + 1);
if (buf) {
printf_encode(buf, name_len * 4 + 1, name, name_len);
eap_log_msg(sm, "EAP-MSCHAPV2 Name '%s'", buf);
os_free(buf);
}
/* 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;
}
}
#ifdef CONFIG_TESTING_OPTIONS
{
u8 challenge[8];
if (challenge_hash(peer_challenge, data->auth_challenge,
username, username_len, challenge) == 0) {
eap_server_mschap_rx_callback(sm, "EAP-MSCHAPV2",
username, username_len,
challenge, nt_response);
}
}
#endif /* CONFIG_TESTING_OPTIONS */
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_const(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 {
if (nt_password_hash(sm->user->password,
sm->user->password_len,
pw_hash_buf) < 0) {
data->state = FAILURE;
return;
}
pw_hash = pw_hash_buf;
}
if (generate_authenticator_response_pwhash(
pw_hash, peer_challenge, data->auth_challenge,
username, username_len, nt_response,
data->auth_response) < 0 ||
hash_nt_password_hash(pw_hash, pw_hash_hash) < 0 ||
get_master_key(pw_hash_hash, nt_response,
data->master_key)) {
data->state = FAILURE;
return;
}
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 bool 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;
bool first_is_send;
if (data->state != SUCCESS || !data->master_key_valid)
return NULL;
*len = 2 * MSCHAPV2_KEY_LEN;
key = os_malloc(*len);
if (key == NULL)
return NULL;
/*
* [MS-CHAP], 3.1.5.1 (Master Session Key (MSK) Derivation
* MSK = MasterReceiveKey + MasterSendKey + 32 bytes zeros (padding)
* On an Authenticator:
* MS-MPPE-Recv-Key = MasterReceiveKey
* MS-MPPE-Send-Key = MasterSendKey
*
* RFC 5422, 3.2.3 (Authenticating Using EAP-FAST-MSCHAPv2)
* MSK = MasterSendKey + MasterReceiveKey
* (i.e., reverse order and no padding)
*
* On Peer, EAP-MSCHAPv2 starts with Send key and EAP-FAST-MSCHAPv2
* starts with Receive key.
*/
first_is_send = sm->eap_fast_mschapv2;
/* MSK = server MS-MPPE-Recv-Key | MS-MPPE-Send-Key */
if (get_asymetric_start_key(data->master_key, key, MSCHAPV2_KEY_LEN,
first_is_send, 1) < 0 ||
get_asymetric_start_key(data->master_key, key + MSCHAPV2_KEY_LEN,
MSCHAPV2_KEY_LEN, !first_is_send, 1) < 0) {
os_free(key);
return NULL;
}
wpa_hexdump_key(MSG_DEBUG, "EAP-MSCHAPV2: Derived key", key, *len);
return key;
}
static bool 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;
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;
return eap_server_method_register(eap);
}

View File

@@ -0,0 +1,614 @@
/*
* hostapd / EAP-PAX (RFC 4746) server
* Copyright (c) 2005-2007, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
*/
#include "includes.h"
#include "common.h"
#include "crypto/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];
u8 mid[EAP_PAX_MID_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);
bin_clear_free(data, sizeof(*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);
if (eap_pax_mac(data->mac_id, (u8 *) "", 0,
wpabuf_mhead(req), wpabuf_len(req) - EAP_PAX_ICV_LEN,
NULL, 0, NULL, 0, pos) < 0) {
wpa_printf(MSG_ERROR, "EAP-PAX: Failed to calculate ICV");
data->state = FAILURE;
wpabuf_free(req);
return NULL;
}
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);
if (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) < 0) {
wpa_printf(MSG_ERROR, "EAP-PAX: Failed to calculate MAC");
data->state = FAILURE;
wpabuf_free(req);
return NULL;
}
wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: MAC_CK(B, CID)",
pos, EAP_PAX_MAC_LEN);
/* Optional ADE could be added here, if needed */
pos = wpabuf_put(req, EAP_PAX_MAC_LEN);
if (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) < 0) {
wpa_printf(MSG_ERROR, "EAP-PAX: Failed to calculate ICV");
data->state = FAILURE;
wpabuf_free(req);
return NULL;
}
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 bool 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) + EAP_PAX_ICV_LEN) {
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);
if (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) < 0) {
wpa_printf(MSG_INFO,
"EAP-PAX: Failed to calculate ICV");
return true;
}
if (os_memcmp_const(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, cid_len;
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;
}
cid_len = WPA_GET_BE16(pos);
if (cid_len > 1500) {
wpa_printf(MSG_INFO, "EAP-PAX: Too long CID");
return;
}
data->cid_len = cid_len;
os_free(data->cid);
data->cid = os_memdup(pos + 2, data->cid_len);
if (data->cid == NULL) {
wpa_printf(MSG_INFO, "EAP-PAX: Failed to allocate memory for "
"CID");
return;
}
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, data->mid) < 0) {
wpa_printf(MSG_INFO, "EAP-PAX: Failed to complete initial "
"key derivation");
data->state = FAILURE;
return;
}
data->keys_set = 1;
if (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) < 0) {
wpa_printf(MSG_INFO, "EAP-PAX: Failed to calculate MAC_CK");
data->state = FAILURE;
return;
}
if (os_memcmp_const(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);
if (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) < 0) {
wpa_printf(MSG_INFO, "EAP-PAX: Failed to calculate ICV");
return;
}
if (os_memcmp_const(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 bool 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 bool eap_pax_isSuccess(struct eap_sm *sm, void *priv)
{
struct eap_pax_data *data = priv;
return data->state == SUCCESS;
}
static u8 * eap_pax_get_session_id(struct eap_sm *sm, void *priv, size_t *len)
{
struct eap_pax_data *data = priv;
u8 *sid;
if (data->state != SUCCESS)
return NULL;
sid = os_malloc(1 + EAP_PAX_MID_LEN);
if (sid == NULL)
return NULL;
*len = 1 + EAP_PAX_MID_LEN;
sid[0] = EAP_TYPE_PAX;
os_memcpy(sid + 1, data->mid, EAP_PAX_MID_LEN);
return sid;
}
int eap_server_pax_register(void)
{
struct eap_method *eap;
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;
eap->getSessionId = eap_pax_get_session_id;
return eap_server_method_register(eap);
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,527 @@
/*
* hostapd / EAP-PSK (RFC 4764) server
* Copyright (c) 2005-2007, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README 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;
size_t id_p_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;
return data;
}
static void eap_psk_reset(struct eap_sm *sm, void *priv)
{
struct eap_psk_data *data = priv;
os_free(data->id_p);
bin_clear_free(data, sizeof(*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) + sm->cfg->server_id_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, sm->cfg->server_id, sm->cfg->server_id_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 = sm->cfg->server_id_len + EAP_PSK_RAND_LEN;
buf = os_malloc(buflen);
if (buf == NULL)
goto fail;
os_memcpy(buf, sm->cfg->server_id, sm->cfg->server_id_len);
os_memcpy(buf + sm->cfg->server_id_len, data->rand_p, EAP_PSK_RAND_LEN);
if (omac1_aes_128(data->ak, buf, buflen, psk->mac_s)) {
os_free(buf);
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 bool 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_memdup(cpos, left);
if (data->id_p == NULL) {
wpa_printf(MSG_INFO, "EAP-PSK: Failed to allocate memory for "
"ID_P");
return;
}
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 + sm->cfg->server_id_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, sm->cfg->server_id, sm->cfg->server_id_len);
pos += sm->cfg->server_id_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_const(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_memdup(pos, left);
if (decrypted == NULL)
return;
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 bool 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_memdup(data->msk, EAP_MSK_LEN);
if (key == NULL)
return NULL;
*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_memdup(data->emsk, EAP_EMSK_LEN);
if (key == NULL)
return NULL;
*len = EAP_EMSK_LEN;
return key;
}
static bool eap_psk_isSuccess(struct eap_sm *sm, void *priv)
{
struct eap_psk_data *data = priv;
return data->state == SUCCESS;
}
static u8 * eap_psk_get_session_id(struct eap_sm *sm, void *priv, size_t *len)
{
struct eap_psk_data *data = priv;
u8 *id;
if (data->state != SUCCESS)
return NULL;
*len = 1 + 2 * EAP_PSK_RAND_LEN;
id = os_malloc(*len);
if (id == NULL)
return NULL;
id[0] = EAP_TYPE_PSK;
os_memcpy(id + 1, data->rand_p, EAP_PSK_RAND_LEN);
os_memcpy(id + 1 + EAP_PSK_RAND_LEN, data->rand_s, EAP_PSK_RAND_LEN);
wpa_hexdump(MSG_DEBUG, "EAP-PSK: Derived Session-Id", id, *len);
return id;
}
int eap_server_psk_register(void)
{
struct eap_method *eap;
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;
eap->getSessionId = eap_psk_get_session_id;
return eap_server_method_register(eap);
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,550 @@
/*
* hostapd / EAP-SAKE (RFC 4763) server
* Copyright (c) 2006-2007, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
*/
#include "includes.h"
#include "common.h"
#include "crypto/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;
};
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);
return data;
}
static void eap_sake_reset(struct eap_sm *sm, void *priv)
{
struct eap_sake_data *data = priv;
os_free(data->peerid);
bin_clear_free(data, sizeof(*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;
plen += 2 + sm->cfg->server_id_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);
wpa_printf(MSG_DEBUG, "EAP-SAKE: * AT_SERVERID");
eap_sake_add_attr(msg, EAP_SAKE_AT_SERVERID,
sm->cfg->server_id, sm->cfg->server_id_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 + 2 + sm->cfg->server_id_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);
wpa_printf(MSG_DEBUG, "EAP-SAKE: * AT_SERVERID");
eap_sake_add_attr(msg, EAP_SAKE_AT_SERVERID,
sm->cfg->server_id, sm->cfg->server_id_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,
sm->cfg->server_id, sm->cfg->server_id_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;
wpabuf_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 bool 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_memdup(attr.peerid, attr.peerid_len);
if (data->peerid == NULL)
return;
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;
}
if (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) < 0) {
wpa_printf(MSG_INFO, "EAP-SAKE: Failed to derive keys");
data->state = FAILURE;
return;
}
if (eap_sake_compute_mic(data->tek.auth, data->rand_s, data->rand_p,
sm->cfg->server_id, sm->cfg->server_id_len,
data->peerid, data->peerid_len, 1,
wpabuf_head(respData), wpabuf_len(respData),
attr.mic_p, mic_p) < 0) {
wpa_printf(MSG_INFO, "EAP-SAKE: Failed to compute MIC");
data->state = FAILURE;
return;
}
if (os_memcmp_const(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;
}
if (eap_sake_compute_mic(data->tek.auth, data->rand_s, data->rand_p,
sm->cfg->server_id, sm->cfg->server_id_len,
data->peerid, data->peerid_len, 1,
wpabuf_head(respData), wpabuf_len(respData),
attr.mic_p, mic_p) < 0) {
wpa_printf(MSG_INFO, "EAP-SAKE: Failed to compute MIC");
return;
}
if (os_memcmp_const(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 bool 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_memdup(data->msk, EAP_MSK_LEN);
if (key == NULL)
return NULL;
*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_memdup(data->emsk, EAP_EMSK_LEN);
if (key == NULL)
return NULL;
*len = EAP_EMSK_LEN;
return key;
}
static bool eap_sake_isSuccess(struct eap_sm *sm, void *priv)
{
struct eap_sake_data *data = priv;
return data->state == SUCCESS;
}
static u8 * eap_sake_get_session_id(struct eap_sm *sm, void *priv, size_t *len)
{
struct eap_sake_data *data = priv;
u8 *id;
if (data->state != SUCCESS)
return NULL;
*len = 1 + 2 * EAP_SAKE_RAND_LEN;
id = os_malloc(*len);
if (id == NULL)
return NULL;
id[0] = EAP_TYPE_SAKE;
os_memcpy(id + 1, data->rand_s, EAP_SAKE_RAND_LEN);
os_memcpy(id + 1 + EAP_SAKE_RAND_LEN, data->rand_s, EAP_SAKE_RAND_LEN);
wpa_hexdump(MSG_DEBUG, "EAP-SAKE: Derived Session-Id", id, *len);
return id;
}
int eap_server_sake_register(void)
{
struct eap_method *eap;
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;
eap->getSessionId = eap_sake_get_session_id;
return eap_server_method_register(eap);
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,503 @@
/*
* hostapd / EAP-TLS (RFC 5216, RFC 9190)
* Copyright (c) 2004-2008, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
*/
#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;
u8 eap_type;
int phase2;
};
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;
if (state == FAILURE)
tls_connection_remove_session(data->ssl.conn);
}
static void eap_tls_valid_session(struct eap_sm *sm, struct eap_tls_data *data)
{
struct wpabuf *buf;
if (!sm->cfg->tls_session_lifetime)
return;
buf = wpabuf_alloc(1);
if (!buf)
return;
wpabuf_put_u8(buf, data->eap_type);
tls_connection_set_success_data(data->ssl.conn, buf);
}
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, EAP_TYPE_TLS)) {
wpa_printf(MSG_INFO, "EAP-TLS: Failed to initialize SSL.");
eap_tls_reset(sm, data);
return NULL;
}
data->eap_type = EAP_TYPE_TLS;
data->phase2 = sm->init_phase2;
return data;
}
#ifdef EAP_SERVER_UNAUTH_TLS
static void * eap_unauth_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, 0, EAP_UNAUTH_TLS_TYPE)) {
wpa_printf(MSG_INFO, "EAP-TLS: Failed to initialize SSL.");
eap_tls_reset(sm, data);
return NULL;
}
data->eap_type = EAP_UNAUTH_TLS_TYPE;
return data;
}
#endif /* EAP_SERVER_UNAUTH_TLS */
#ifdef CONFIG_HS20
static void * eap_wfa_unauth_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, 0,
EAP_WFA_UNAUTH_TLS_TYPE)) {
wpa_printf(MSG_INFO, "EAP-TLS: Failed to initialize SSL.");
eap_tls_reset(sm, data);
return NULL;
}
data->eap_type = EAP_WFA_UNAUTH_TLS_TYPE;
return data;
}
#endif /* CONFIG_HS20 */
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_tls_msg_alloc(data->eap_type, 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, data->eap_type, 0);
}
if (data->ssl.state == WAIT_FRAG_ACK) {
res = eap_server_tls_build_msg(&data->ssl, data->eap_type, 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->cfg->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, data->eap_type, 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);
eap_tls_valid_session(sm, data);
if (sm->serial_num) {
char user[128];
int user_len;
user_len = os_snprintf(user, sizeof(user), "cert-%s",
sm->serial_num);
if (eap_user_get(sm, (const u8 *) user, user_len,
data->phase2) < 0)
wpa_printf(MSG_DEBUG,
"EAP-TLS: No user entry found based on the serial number of the client certificate ");
else
wpa_printf(MSG_DEBUG,
"EAP-TLS: Updated user entry based on the serial number of the client certificate ");
}
}
return res;
}
static bool eap_tls_check(struct eap_sm *sm, void *priv,
struct wpabuf *respData)
{
struct eap_tls_data *data = priv;
const u8 *pos;
size_t len;
if (data->eap_type == EAP_UNAUTH_TLS_TYPE)
pos = eap_hdr_validate(EAP_VENDOR_UNAUTH_TLS,
EAP_VENDOR_TYPE_UNAUTH_TLS, respData,
&len);
else if (data->eap_type == EAP_WFA_UNAUTH_TLS_TYPE)
pos = eap_hdr_validate(EAP_VENDOR_WFA_NEW,
EAP_VENDOR_WFA_UNAUTH_TLS, respData,
&len);
else
pos = eap_hdr_validate(EAP_VENDOR_IETF, data->eap_type,
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);
return;
}
}
static void eap_tls_process(struct eap_sm *sm, void *priv,
struct wpabuf *respData)
{
struct eap_tls_data *data = priv;
const struct wpabuf *buf;
const u8 *pos;
if (eap_server_tls_process(sm, &data->ssl, respData, data,
data->eap_type, NULL, eap_tls_process_msg) <
0) {
eap_tls_state(data, FAILURE);
return;
}
if (!tls_connection_established(sm->cfg->ssl_ctx, data->ssl.conn) ||
!tls_connection_resumed(sm->cfg->ssl_ctx, data->ssl.conn))
return;
buf = tls_connection_get_success_data(data->ssl.conn);
if (!buf || wpabuf_len(buf) < 1) {
wpa_printf(MSG_DEBUG,
"EAP-TLS: No success data in resumed session - reject attempt");
eap_tls_state(data, FAILURE);
return;
}
pos = wpabuf_head(buf);
if (*pos != data->eap_type) {
wpa_printf(MSG_DEBUG,
"EAP-TLS: Resumed session for another EAP type (%u) - reject attempt",
*pos);
eap_tls_state(data, FAILURE);
return;
}
wpa_printf(MSG_DEBUG,
"EAP-TLS: Resuming previous session");
if (data->ssl.tls_v13 && data->ssl.tls_out) {
wpa_hexdump_buf(MSG_DEBUG,
"EAP-TLS: Additional data to be sent for TLS 1.3",
data->ssl.tls_out);
return;
}
eap_tls_state(data, SUCCESS);
tls_connection_set_success_data_resumed(data->ssl.conn);
/* TODO: Cache serial number with session and update EAP user
* information based on the cached serial number */
}
static bool 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;
const char *label;
const u8 eap_tls13_context[] = { EAP_TYPE_TLS };
const u8 *context = NULL;
size_t context_len = 0;
if (data->state != SUCCESS)
return NULL;
if (data->ssl.tls_v13) {
label = "EXPORTER_EAP_TLS_Key_Material";
context = eap_tls13_context;
context_len = 1;
} else {
label = "client EAP encryption";
}
eapKeyData = eap_server_tls_derive_key(sm, &data->ssl, label,
context, context_len,
EAP_TLS_KEY_LEN + EAP_EMSK_LEN);
if (eapKeyData) {
*len = EAP_TLS_KEY_LEN;
wpa_hexdump(MSG_DEBUG, "EAP-TLS: Derived key",
eapKeyData, EAP_TLS_KEY_LEN);
os_memset(eapKeyData + EAP_TLS_KEY_LEN, 0, EAP_EMSK_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;
const char *label;
const u8 eap_tls13_context[] = { EAP_TYPE_TLS };
const u8 *context = NULL;
size_t context_len = 0;
if (data->state != SUCCESS)
return NULL;
if (data->ssl.tls_v13) {
label = "EXPORTER_EAP_TLS_Key_Material";
context = eap_tls13_context;
context_len = 1;
} else {
label = "client EAP encryption";
}
eapKeyData = eap_server_tls_derive_key(sm, &data->ssl, label,
context, context_len,
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);
bin_clear_free(eapKeyData, EAP_TLS_KEY_LEN + EAP_EMSK_LEN);
} 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 bool eap_tls_isSuccess(struct eap_sm *sm, void *priv)
{
struct eap_tls_data *data = priv;
return data->state == SUCCESS;
}
static u8 * eap_tls_get_session_id(struct eap_sm *sm, void *priv, size_t *len)
{
struct eap_tls_data *data = priv;
if (data->state != SUCCESS)
return NULL;
return eap_server_tls_derive_session_id(sm, &data->ssl, EAP_TYPE_TLS,
len);
}
int eap_server_tls_register(void)
{
struct eap_method *eap;
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;
eap->getSessionId = eap_tls_get_session_id;
return eap_server_method_register(eap);
}
#ifdef EAP_SERVER_UNAUTH_TLS
int eap_server_unauth_tls_register(void)
{
struct eap_method *eap;
eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION,
EAP_VENDOR_UNAUTH_TLS,
EAP_VENDOR_TYPE_UNAUTH_TLS,
"UNAUTH-TLS");
if (eap == NULL)
return -1;
eap->init = eap_unauth_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;
return eap_server_method_register(eap);
}
#endif /* EAP_SERVER_UNAUTH_TLS */
#ifdef CONFIG_HS20
int eap_server_wfa_unauth_tls_register(void)
{
struct eap_method *eap;
eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION,
EAP_VENDOR_WFA_NEW,
EAP_VENDOR_WFA_UNAUTH_TLS,
"WFA-UNAUTH-TLS");
if (eap == NULL)
return -1;
eap->init = eap_wfa_unauth_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;
return eap_server_method_register(eap);
}
#endif /* CONFIG_HS20 */

View File

@@ -0,0 +1,582 @@
/*
* EAP-TLS/PEAP/TTLS/FAST server common functions
* Copyright (c) 2004-2009, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
*/
#include "includes.h"
#include "common.h"
#include "crypto/sha1.h"
#include "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);
struct wpabuf * eap_tls_msg_alloc(enum eap_type type, size_t payload_len,
u8 code, u8 identifier)
{
if (type == EAP_UNAUTH_TLS_TYPE)
return eap_msg_alloc(EAP_VENDOR_UNAUTH_TLS,
EAP_VENDOR_TYPE_UNAUTH_TLS, payload_len,
code, identifier);
else if (type == EAP_WFA_UNAUTH_TLS_TYPE)
return eap_msg_alloc(EAP_VENDOR_WFA_NEW,
EAP_VENDOR_WFA_UNAUTH_TLS, payload_len,
code, identifier);
return eap_msg_alloc(EAP_VENDOR_IETF, type, payload_len, code,
identifier);
}
#ifdef CONFIG_TLS_INTERNAL
static void eap_server_tls_log_cb(void *ctx, const char *msg)
{
struct eap_sm *sm = ctx;
eap_log_msg(sm, "TLS: %s", msg);
}
#endif /* CONFIG_TLS_INTERNAL */
int eap_server_tls_ssl_init(struct eap_sm *sm, struct eap_ssl_data *data,
int verify_peer, int eap_type)
{
u8 session_ctx[8];
unsigned int flags = sm->cfg->tls_flags;
if (!sm->cfg->ssl_ctx) {
wpa_printf(MSG_ERROR, "TLS context not initialized - cannot use TLS-based EAP method");
return -1;
}
data->eap = sm;
data->phase2 = sm->init_phase2;
data->conn = tls_connection_init(sm->cfg->ssl_ctx);
if (data->conn == NULL) {
wpa_printf(MSG_INFO, "SSL: Failed to initialize new TLS "
"connection");
return -1;
}
#ifdef CONFIG_TLS_INTERNAL
tls_connection_set_log_cb(data->conn, eap_server_tls_log_cb, sm);
#ifdef CONFIG_TESTING_OPTIONS
tls_connection_set_test_flags(data->conn, sm->tls_test_flags);
#endif /* CONFIG_TESTING_OPTIONS */
#endif /* CONFIG_TLS_INTERNAL */
if (eap_type != EAP_TYPE_FAST)
flags |= TLS_CONN_DISABLE_SESSION_TICKET;
os_memcpy(session_ctx, "hostapd", 7);
session_ctx[7] = (u8) eap_type;
if (tls_connection_set_verify(sm->cfg->ssl_ctx, data->conn, verify_peer,
flags, session_ctx,
sizeof(session_ctx))) {
wpa_printf(MSG_INFO, "SSL: Failed to configure verification "
"of TLS peer certificate");
tls_connection_deinit(sm->cfg->ssl_ctx, data->conn);
data->conn = NULL;
return -1;
}
data->tls_out_limit = sm->cfg->fragment_size > 0 ?
sm->cfg->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;
}
#ifdef CONFIG_TESTING_OPTIONS
data->skip_prot_success = sm->cfg->skip_prot_success;
#endif /* CONFIG_TESTING_OPTIONS */
return 0;
}
void eap_server_tls_ssl_deinit(struct eap_sm *sm, struct eap_ssl_data *data)
{
tls_connection_deinit(sm->cfg->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,
const char *label, const u8 *context,
size_t context_len, size_t len)
{
u8 *out;
out = os_malloc(len);
if (out == NULL)
return NULL;
if (tls_connection_export_key(sm->cfg->ssl_ctx, data->conn, label,
context, context_len, out, len)) {
os_free(out);
return NULL;
}
return out;
}
/**
* eap_server_tls_derive_session_id - Derive a Session-Id based on TLS data
* @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
* @data: Data for TLS processing
* @eap_type: EAP method used in Phase 1 (EAP_TYPE_TLS/PEAP/TTLS/FAST)
* @len: Pointer to length of the session ID generated
* Returns: Pointer to allocated Session-Id on success or %NULL on failure
*
* This function derive the Session-Id based on the TLS session data
* (client/server random and method type).
*
* The caller is responsible for freeing the returned buffer.
*/
u8 * eap_server_tls_derive_session_id(struct eap_sm *sm,
struct eap_ssl_data *data, u8 eap_type,
size_t *len)
{
struct tls_random keys;
u8 *out;
if (data->tls_v13) {
u8 *id, *method_id;
const u8 context[] = { eap_type };
/* Session-Id = <EAP-Type> || Method-Id
* Method-Id = TLS-Exporter("EXPORTER_EAP_TLS_Method-Id",
* Type-Code, 64)
*/
*len = 1 + 64;
id = os_malloc(*len);
if (!id)
return NULL;
method_id = eap_server_tls_derive_key(
sm, data, "EXPORTER_EAP_TLS_Method-Id", context, 1, 64);
if (!method_id) {
os_free(id);
return NULL;
}
id[0] = eap_type;
os_memcpy(id + 1, method_id, 64);
os_free(method_id);
return id;
}
if (tls_connection_get_random(sm->cfg->ssl_ctx, data->conn, &keys))
return NULL;
if (keys.client_random == NULL || keys.server_random == NULL)
return NULL;
*len = 1 + keys.client_random_len + keys.server_random_len;
out = os_malloc(*len);
if (out == NULL)
return NULL;
/* Session-Id = EAP type || client.random || server.random */
out[0] = eap_type;
os_memcpy(out + 1, keys.client_random, keys.client_random_len);
os_memcpy(out + 1 + keys.client_random_len, keys.server_random,
keys.server_random_len);
return out;
}
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_tls_msg_alloc(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_tls_msg_alloc(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;
}
if (len > message_length) {
wpa_printf(MSG_INFO, "SSL: Too much data (%d bytes) in "
"first fragment of frame (TLS Message "
"Length %d bytes)",
(int) len, (int) message_length);
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)
{
char buf[20];
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->cfg->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->cfg->ssl_ctx, data->conn)) {
/* TLS processing has failed - return error */
wpa_printf(MSG_DEBUG, "SSL: Failed - tls_out available to "
"report error");
return -1;
}
if (tls_get_version(sm->cfg->ssl_ctx, data->conn,
buf, sizeof(buf)) == 0) {
wpa_printf(MSG_DEBUG, "SSL: Using TLS version %s", buf);
data->tls_v13 = os_strcmp(buf, "TLSv1.3") == 0;
}
if (!sm->serial_num &&
tls_connection_established(sm->cfg->ssl_ctx, data->conn))
sm->serial_num = tls_connection_peer_serial_num(
sm->cfg->ssl_ctx, data->conn);
/*
* RFC 9190 Section 2.5
*
* We need to signal the other end that TLS negotiation is done. We
* can't send a zero-length application data message, so we send
* application data which is one byte of zero.
*
* Note this is only done for when there is no application data to be
* sent. So this is done always for EAP-TLS but notably not for PEAP
* even on resumption.
*/
if (data->tls_v13 &&
tls_connection_established(sm->cfg->ssl_ctx, data->conn)) {
struct wpabuf *plain, *encr;
switch (sm->currentMethod) {
case EAP_TYPE_PEAP:
break;
default:
if (!tls_connection_resumed(sm->cfg->ssl_ctx,
data->conn))
break;
/* fallthrough */
case EAP_TYPE_TLS:
#ifdef CONFIG_TESTING_OPTIONS
if (data->skip_prot_success) {
wpa_printf(MSG_INFO,
"TESTING: Do not send protected success indication");
break;
}
#endif /* CONFIG_TESTING_OPTIONS */
wpa_printf(MSG_DEBUG,
"EAP-TLS: Send protected success indication (appl data 0x00)");
plain = wpabuf_alloc(1);
if (!plain)
return -1;
wpabuf_put_u8(plain, 0);
encr = eap_server_tls_encrypt(sm, data, plain);
wpabuf_free(plain);
if (!encr)
return -1;
if (wpabuf_resize(&data->tls_out, wpabuf_len(encr)) < 0)
{
wpa_printf(MSG_INFO,
"EAP-TLS: Failed to resize output buffer");
wpabuf_free(encr);
return -1;
}
wpabuf_put_buf(data->tls_out, encr);
wpa_hexdump_buf(MSG_DEBUG,
"EAP-TLS: Data appended to the message",
encr);
wpabuf_free(encr);
}
}
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;
wpa_hexdump(MSG_MSGDUMP, "SSL: Received data", *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;
if (*left > tls_msg_len) {
wpa_printf(MSG_INFO, "SSL: TLS Message Length (%d "
"bytes) smaller than this fragment (%d "
"bytes)", (int) tls_msg_len, (int) *left);
return -1;
}
}
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->cfg->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;
if (eap_type == EAP_UNAUTH_TLS_TYPE)
pos = eap_hdr_validate(EAP_VENDOR_UNAUTH_TLS,
EAP_VENDOR_TYPE_UNAUTH_TLS, respData,
&left);
else if (eap_type == EAP_WFA_UNAUTH_TLS_TYPE)
pos = eap_hdr_validate(EAP_VENDOR_WFA_NEW,
EAP_VENDOR_WFA_UNAUTH_TLS, respData,
&left);
else
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->cfg->ssl_ctx, data->conn) > 1) {
wpa_printf(MSG_INFO, "SSL: Locally detected fatal error in "
"TLS processing");
res = -1;
}
done:
eap_server_tls_free_in_buf(data);
return res;
}

View File

@@ -0,0 +1,572 @@
/*
* EAP server method: EAP-TNC (Trusted Network Connect)
* Copyright (c) 2007-2010, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
*/
#include "includes.h"
#include "common.h"
#include "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->cfg->fragment_size > 100 ?
sm->cfg->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 bool 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) ||
message_length > 75000) {
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 bool eap_tnc_isDone(struct eap_sm *sm, void *priv)
{
struct eap_tnc_data *data = priv;
return data->state == DONE || data->state == FAIL;
}
static bool 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;
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;
return eap_server_method_register(eap);
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,188 @@
/*
* hostapd / Test method for vendor specific (expanded) EAP type
* Copyright (c) 2005-2007, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
*/
#include "includes.h"
#include "common.h"
#include "eap_i.h"
#define EAP_VENDOR_ID EAP_VENDOR_HOSTAP
#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 bool 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 bool 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 bool 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;
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;
return eap_server_method_register(eap);
}

View File

@@ -0,0 +1,510 @@
/*
* EAP-WSC server for Wi-Fi Protected Setup
* Copyright (c) 2007-2008, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
*/
#include "includes.h"
#include "common.h"
#include "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->cfg->wps;
cfg.registrar = registrar;
if (registrar) {
if (!sm->cfg->wps || !sm->cfg->wps->registrar) {
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) {
if (!sm->cfg->wps->use_passphrase) {
wpa_printf(MSG_DEBUG,
"EAP-WSC: Prefer PSK format for non-6 GHz P2P client");
cfg.use_psk_key = 1;
}
cfg.p2p_dev_addr = p2p_get_go_dev_addr(sm->assoc_p2p_ie);
}
#endif /* CONFIG_P2P */
cfg.pbc_in_m1 = sm->cfg->pbc_in_m1;
data->wps = wps_init(&cfg);
if (data->wps == NULL) {
os_free(data);
return NULL;
}
data->fragment_size = sm->cfg->fragment_size > 0 ?
sm->cfg->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;
}
/* fall 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 bool 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 || message_length > 50000) {
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 bool eap_wsc_isDone(struct eap_sm *sm, void *priv)
{
struct eap_wsc_data *data = priv;
return data->state == FAIL;
}
static bool 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;
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;
return eap_server_method_register(eap);
}

1563
src/eap_server/eap_sim_db.c Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,95 @@
/*
* hostapd / EAP-SIM database/authenticator gateway
* Copyright (c) 2005-2008, 2012, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README 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'
#define EAP_AKA_PRIME_PERMANENT_PREFIX '6'
#define EAP_AKA_PRIME_PSEUDONYM_PREFIX '7'
#define EAP_AKA_PRIME_REAUTH_ID_PREFIX '8'
enum eap_sim_db_method {
EAP_SIM_DB_SIM,
EAP_SIM_DB_AKA,
EAP_SIM_DB_AKA_PRIME
};
struct eap_sim_db_data;
struct eap_sim_db_data *
eap_sim_db_init(const char *config, unsigned int db_timeout,
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(struct eap_sim_db_data *data,
const char *username, 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
char * eap_sim_db_get_next_pseudonym(struct eap_sim_db_data *data,
enum eap_sim_db_method method);
char * eap_sim_db_get_next_reauth_id(struct eap_sim_db_data *data,
enum eap_sim_db_method method);
int eap_sim_db_add_pseudonym(struct eap_sim_db_data *data,
const char *permanent, char *pseudonym);
int eap_sim_db_add_reauth(struct eap_sim_db_data *data, const char *permanent,
char *reauth_id, u16 counter, const u8 *mk);
int eap_sim_db_add_reauth_prime(struct eap_sim_db_data *data,
const char *permanent,
char *reauth_id, u16 counter, const u8 *k_encr,
const u8 *k_aut, const u8 *k_re);
const char * eap_sim_db_get_permanent(struct eap_sim_db_data *data,
const char *pseudonym);
struct eap_sim_reauth {
struct eap_sim_reauth *next;
char *permanent; /* Permanent username */
char *reauth_id; /* Fast re-authentication username */
u16 counter;
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(struct eap_sim_db_data *data,
const char *reauth_id);
void eap_sim_db_remove_reauth(struct eap_sim_db_data *data,
struct eap_sim_reauth *reauth);
int eap_sim_db_get_aka_auth(struct eap_sim_db_data *data, const char *username,
u8 *_rand, u8 *autn, u8 *ik, u8 *ck,
u8 *res, size_t *res_len, void *cb_session_ctx);
int eap_sim_db_resynchronize(struct eap_sim_db_data *data,
const char *username, const u8 *auts,
const u8 *_rand);
char * sim_get_username(const u8 *identity, size_t identity_len);
#endif /* EAP_SIM_DB_H */

View File

@@ -0,0 +1,103 @@
/*
* EAP-TLS/PEAP/TTLS/FAST server common functions
* Copyright (c) 2004-2009, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README 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;
/**
* tls_v13 - Whether TLS v1.3 or newer is used
*/
int tls_v13;
bool skip_prot_success; /* testing behavior only for TLS v1.3 */
};
/* 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_TEAP_FLAGS_OUTER_TLV_LEN 0x10
#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
/* stub type used as a flag for UNAUTH-TLS */
#define EAP_UNAUTH_TLS_TYPE 255
#define EAP_WFA_UNAUTH_TLS_TYPE 254
struct wpabuf * eap_tls_msg_alloc(enum eap_type type, size_t payload_len,
u8 code, u8 identifier);
int eap_server_tls_ssl_init(struct eap_sm *sm, struct eap_ssl_data *data,
int verify_peer, int eap_type);
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,
const char *label, const u8 *context,
size_t context_len, size_t len);
u8 * eap_server_tls_derive_session_id(struct eap_sm *sm,
struct eap_ssl_data *data, u8 eap_type,
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 */

1198
src/eap_server/ikev2.c Normal file

File diff suppressed because it is too large Load Diff

61
src/eap_server/ikev2.h Normal file
View File

@@ -0,0 +1,61 @@
/*
* IKEv2 initiator (RFC 4306) for EAP-IKEV2
* Copyright (c) 2007, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README 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 */

1199
src/eap_server/tncs.c Normal file

File diff suppressed because it is too large Load Diff

43
src/eap_server/tncs.h Normal file
View File

@@ -0,0 +1,43 @@
/*
* EAP-TNC - TNCS (IF-IMV, IF-TNCCS, and IF-TNCCS-SOH)
* Copyright (c) 2007-2008, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README 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 */