Initializing repository
This commit is contained in:
8
src/eap_server/Makefile
Normal file
8
src/eap_server/Makefile
Normal 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
307
src/eap_server/eap.h
Normal 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
210
src/eap_server/eap_i.h
Normal 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 */
|
||||
52
src/eap_server/eap_methods.h
Normal file
52
src/eap_server/eap_methods.h
Normal 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
2104
src/eap_server/eap_server.c
Normal file
File diff suppressed because it is too large
Load Diff
1506
src/eap_server/eap_server_aka.c
Normal file
1506
src/eap_server/eap_server_aka.c
Normal file
File diff suppressed because it is too large
Load Diff
812
src/eap_server/eap_server_eke.c
Normal file
812
src/eap_server/eap_server_eke.c
Normal 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);
|
||||
}
|
||||
1647
src/eap_server/eap_server_fast.c
Normal file
1647
src/eap_server/eap_server_fast.c
Normal file
File diff suppressed because it is too large
Load Diff
649
src/eap_server/eap_server_gpsk.c
Normal file
649
src/eap_server/eap_server_gpsk.c
Normal 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);
|
||||
}
|
||||
219
src/eap_server/eap_server_gtc.c
Normal file
219
src/eap_server/eap_server_gtc.c
Normal 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);
|
||||
}
|
||||
177
src/eap_server/eap_server_identity.c
Normal file
177
src/eap_server/eap_server_identity.c
Normal 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);
|
||||
}
|
||||
571
src/eap_server/eap_server_ikev2.c
Normal file
571
src/eap_server/eap_server_ikev2.c
Normal 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);
|
||||
}
|
||||
171
src/eap_server/eap_server_md5.c
Normal file
171
src/eap_server/eap_server_md5.c
Normal 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);
|
||||
}
|
||||
178
src/eap_server/eap_server_methods.c
Normal file
178
src/eap_server/eap_server_methods.c
Normal 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";
|
||||
}
|
||||
616
src/eap_server/eap_server_mschapv2.c
Normal file
616
src/eap_server/eap_server_mschapv2.c
Normal 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);
|
||||
}
|
||||
614
src/eap_server/eap_server_pax.c
Normal file
614
src/eap_server/eap_server_pax.c
Normal 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);
|
||||
}
|
||||
1498
src/eap_server/eap_server_peap.c
Normal file
1498
src/eap_server/eap_server_peap.c
Normal file
File diff suppressed because it is too large
Load Diff
527
src/eap_server/eap_server_psk.c
Normal file
527
src/eap_server/eap_server_psk.c
Normal 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);
|
||||
}
|
||||
1079
src/eap_server/eap_server_pwd.c
Normal file
1079
src/eap_server/eap_server_pwd.c
Normal file
File diff suppressed because it is too large
Load Diff
550
src/eap_server/eap_server_sake.c
Normal file
550
src/eap_server/eap_server_sake.c
Normal 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);
|
||||
}
|
||||
1018
src/eap_server/eap_server_sim.c
Normal file
1018
src/eap_server/eap_server_sim.c
Normal file
File diff suppressed because it is too large
Load Diff
2141
src/eap_server/eap_server_teap.c
Normal file
2141
src/eap_server/eap_server_teap.c
Normal file
File diff suppressed because it is too large
Load Diff
503
src/eap_server/eap_server_tls.c
Normal file
503
src/eap_server/eap_server_tls.c
Normal 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 */
|
||||
582
src/eap_server/eap_server_tls_common.c
Normal file
582
src/eap_server/eap_server_tls_common.c
Normal 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;
|
||||
}
|
||||
572
src/eap_server/eap_server_tnc.c
Normal file
572
src/eap_server/eap_server_tnc.c
Normal 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);
|
||||
}
|
||||
1390
src/eap_server/eap_server_ttls.c
Normal file
1390
src/eap_server/eap_server_ttls.c
Normal file
File diff suppressed because it is too large
Load Diff
188
src/eap_server/eap_server_vendor_test.c
Normal file
188
src/eap_server/eap_server_vendor_test.c
Normal 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);
|
||||
}
|
||||
510
src/eap_server/eap_server_wsc.c
Normal file
510
src/eap_server/eap_server_wsc.c
Normal 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
1563
src/eap_server/eap_sim_db.c
Normal file
File diff suppressed because it is too large
Load Diff
95
src/eap_server/eap_sim_db.h
Normal file
95
src/eap_server/eap_sim_db.h
Normal 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 */
|
||||
103
src/eap_server/eap_tls_common.h
Normal file
103
src/eap_server/eap_tls_common.h
Normal 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
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
61
src/eap_server/ikev2.h
Normal 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
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
43
src/eap_server/tncs.h
Normal 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 */
|
||||
Reference in New Issue
Block a user