rtl8188eu: Add hostapd and configurationb file

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

View file

@ -0,0 +1,11 @@
all:
@echo Nothing to be made.
clean:
rm -f *~ *.o *.so *.d
install:
if ls *.so >/dev/null 2>&1; then \
install -d $(DESTDIR)$(LIBDIR)/wpa_supplicant && \
cp *.so $(DESTDIR)$(LIBDIR)/wpa_supplicant \
; fi

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,292 @@
/*
* EAP peer state machine functions (RFC 4137)
* Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* Alternatively, this software may be distributed under the terms of BSD
* license.
*
* See README and COPYING for more details.
*/
#ifndef EAP_H
#define EAP_H
#include "common/defs.h"
#include "eap_common/eap_defs.h"
#include "eap_peer/eap_methods.h"
struct eap_sm;
struct wpa_config_blob;
struct wpabuf;
struct eap_method_type {
int vendor;
u32 method;
};
#ifdef IEEE8021X_EAPOL
/**
* enum eapol_bool_var - EAPOL boolean state variables for EAP state machine
*
* These variables are used in the interface between EAP peer state machine and
* lower layer. These are defined in RFC 4137, Sect. 4.1. Lower layer code is
* expected to maintain these variables and register a callback functions for
* EAP state machine to get and set the variables.
*/
enum eapol_bool_var {
/**
* EAPOL_eapSuccess - EAP SUCCESS state reached
*
* EAP state machine reads and writes this value.
*/
EAPOL_eapSuccess,
/**
* EAPOL_eapRestart - Lower layer request to restart authentication
*
* Set to TRUE in lower layer, FALSE in EAP state machine.
*/
EAPOL_eapRestart,
/**
* EAPOL_eapFail - EAP FAILURE state reached
*
* EAP state machine writes this value.
*/
EAPOL_eapFail,
/**
* EAPOL_eapResp - Response to send
*
* Set to TRUE in EAP state machine, FALSE in lower layer.
*/
EAPOL_eapResp,
/**
* EAPOL_eapNoResp - Request has been process; no response to send
*
* Set to TRUE in EAP state machine, FALSE in lower layer.
*/
EAPOL_eapNoResp,
/**
* EAPOL_eapReq - EAP request available from lower layer
*
* Set to TRUE in lower layer, FALSE in EAP state machine.
*/
EAPOL_eapReq,
/**
* EAPOL_portEnabled - Lower layer is ready for communication
*
* EAP state machines reads this value.
*/
EAPOL_portEnabled,
/**
* EAPOL_altAccept - Alternate indication of success (RFC3748)
*
* EAP state machines reads this value.
*/
EAPOL_altAccept,
/**
* EAPOL_altReject - Alternate indication of failure (RFC3748)
*
* EAP state machines reads this value.
*/
EAPOL_altReject
};
/**
* enum eapol_int_var - EAPOL integer state variables for EAP state machine
*
* These variables are used in the interface between EAP peer state machine and
* lower layer. These are defined in RFC 4137, Sect. 4.1. Lower layer code is
* expected to maintain these variables and register a callback functions for
* EAP state machine to get and set the variables.
*/
enum eapol_int_var {
/**
* EAPOL_idleWhile - Outside time for EAP peer timeout
*
* This integer variable is used to provide an outside timer that the
* external (to EAP state machine) code must decrement by one every
* second until the value reaches zero. This is used in the same way as
* EAPOL state machine timers. EAP state machine reads and writes this
* value.
*/
EAPOL_idleWhile
};
/**
* struct eapol_callbacks - Callback functions from EAP to lower layer
*
* This structure defines the callback functions that EAP state machine
* requires from the lower layer (usually EAPOL state machine) for updating
* state variables and requesting information. eapol_ctx from
* eap_peer_sm_init() call will be used as the ctx parameter for these
* callback functions.
*/
struct eapol_callbacks {
/**
* get_config - Get pointer to the current network configuration
* @ctx: eapol_ctx from eap_peer_sm_init() call
*/
struct eap_peer_config * (*get_config)(void *ctx);
/**
* get_bool - Get a boolean EAPOL state variable
* @variable: EAPOL boolean variable to get
* Returns: Value of the EAPOL variable
*/
Boolean (*get_bool)(void *ctx, enum eapol_bool_var variable);
/**
* set_bool - Set a boolean EAPOL state variable
* @ctx: eapol_ctx from eap_peer_sm_init() call
* @variable: EAPOL boolean variable to set
* @value: Value for the EAPOL variable
*/
void (*set_bool)(void *ctx, enum eapol_bool_var variable,
Boolean value);
/**
* get_int - Get an integer EAPOL state variable
* @ctx: eapol_ctx from eap_peer_sm_init() call
* @variable: EAPOL integer variable to get
* Returns: Value of the EAPOL variable
*/
unsigned int (*get_int)(void *ctx, enum eapol_int_var variable);
/**
* set_int - Set an integer EAPOL state variable
* @ctx: eapol_ctx from eap_peer_sm_init() call
* @variable: EAPOL integer variable to set
* @value: Value for the EAPOL variable
*/
void (*set_int)(void *ctx, enum eapol_int_var variable,
unsigned int value);
/**
* get_eapReqData - Get EAP-Request data
* @ctx: eapol_ctx from eap_peer_sm_init() call
* @len: Pointer to variable that will be set to eapReqDataLen
* Returns: Reference to eapReqData (EAP state machine will not free
* this) or %NULL if eapReqData not available.
*/
struct wpabuf * (*get_eapReqData)(void *ctx);
/**
* set_config_blob - Set named configuration blob
* @ctx: eapol_ctx from eap_peer_sm_init() call
* @blob: New value for the blob
*
* Adds a new configuration blob or replaces the current value of an
* existing blob.
*/
void (*set_config_blob)(void *ctx, struct wpa_config_blob *blob);
/**
* get_config_blob - Get a named configuration blob
* @ctx: eapol_ctx from eap_peer_sm_init() call
* @name: Name of the blob
* Returns: Pointer to blob data or %NULL if not found
*/
const struct wpa_config_blob * (*get_config_blob)(void *ctx,
const char *name);
/**
* notify_pending - Notify that a pending request can be retried
* @ctx: eapol_ctx from eap_peer_sm_init() call
*
* An EAP method can perform a pending operation (e.g., to get a
* response from an external process). Once the response is available,
* this callback function can be used to request EAPOL state machine to
* retry delivering the previously received (and still unanswered) EAP
* request to EAP state machine.
*/
void (*notify_pending)(void *ctx);
/**
* eap_param_needed - Notify that EAP parameter is needed
* @ctx: eapol_ctx from eap_peer_sm_init() call
* @field: Field name (e.g., "IDENTITY")
* @txt: User readable text describing the required parameter
*/
void (*eap_param_needed)(void *ctx, const char *field,
const char *txt);
};
/**
* struct eap_config - Configuration for EAP state machine
*/
struct eap_config {
/**
* opensc_engine_path - OpenSC engine for OpenSSL engine support
*
* Usually, path to engine_opensc.so.
*/
const char *opensc_engine_path;
/**
* pkcs11_engine_path - PKCS#11 engine for OpenSSL engine support
*
* Usually, path to engine_pkcs11.so.
*/
const char *pkcs11_engine_path;
/**
* pkcs11_module_path - OpenSC PKCS#11 module for OpenSSL engine
*
* Usually, path to opensc-pkcs11.so.
*/
const char *pkcs11_module_path;
/**
* wps - WPS context data
*
* This is only used by EAP-WSC and can be left %NULL if not available.
*/
struct wps_context *wps;
};
struct eap_sm * eap_peer_sm_init(void *eapol_ctx,
struct eapol_callbacks *eapol_cb,
void *msg_ctx, struct eap_config *conf);
void eap_peer_sm_deinit(struct eap_sm *sm);
int eap_peer_sm_step(struct eap_sm *sm);
void eap_sm_abort(struct eap_sm *sm);
int eap_sm_get_status(struct eap_sm *sm, char *buf, size_t buflen,
int verbose);
const char * eap_sm_get_method_name(struct eap_sm *sm);
struct wpabuf * eap_sm_buildIdentity(struct eap_sm *sm, int id, int encrypted);
void eap_sm_request_identity(struct eap_sm *sm);
void eap_sm_request_password(struct eap_sm *sm);
void eap_sm_request_new_password(struct eap_sm *sm);
void eap_sm_request_pin(struct eap_sm *sm);
void eap_sm_request_otp(struct eap_sm *sm, const char *msg, size_t msg_len);
void eap_sm_request_passphrase(struct eap_sm *sm);
void eap_sm_notify_ctrl_attached(struct eap_sm *sm);
u32 eap_get_phase2_type(const char *name, int *vendor);
struct eap_method_type * eap_get_phase2_types(struct eap_peer_config *config,
size_t *count);
void eap_set_fast_reauth(struct eap_sm *sm, int enabled);
void eap_set_workaround(struct eap_sm *sm, unsigned int workaround);
void eap_set_force_disabled(struct eap_sm *sm, int disabled);
int eap_key_available(struct eap_sm *sm);
void eap_notify_success(struct eap_sm *sm);
void eap_notify_lower_layer_success(struct eap_sm *sm);
const u8 * eap_get_eapKeyData(struct eap_sm *sm, size_t *len);
struct wpabuf * eap_get_eapRespData(struct eap_sm *sm);
void eap_register_scard_ctx(struct eap_sm *sm, void *ctx);
void eap_invalidate_cached_session(struct eap_sm *sm);
int eap_is_wps_pbc_enrollee(struct eap_peer_config *conf);
int eap_is_wps_pin_enrollee(struct eap_peer_config *conf);
#endif /* IEEE8021X_EAPOL */
#endif /* EAP_H */

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,669 @@
/*
* EAP peer configuration data
* Copyright (c) 2003-2008, Jouni Malinen <j@w1.fi>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* Alternatively, this software may be distributed under the terms of BSD
* license.
*
* See README and COPYING for more details.
*/
#ifndef EAP_CONFIG_H
#define EAP_CONFIG_H
/**
* struct eap_peer_config - EAP peer configuration/credentials
*/
struct eap_peer_config {
/**
* identity - EAP Identity
*
* This field is used to set the real user identity or NAI (for
* EAP-PSK/PAX/SAKE/GPSK).
*/
u8 *identity;
/**
* identity_len - EAP Identity length
*/
size_t identity_len;
/**
* anonymous_identity - Anonymous EAP Identity
*
* This field is used for unencrypted use with EAP types that support
* different tunnelled identity, e.g., EAP-TTLS, in order to reveal the
* real identity (identity field) only to the authentication server.
*
* If not set, the identity field will be used for both unencrypted and
* protected fields.
*/
u8 *anonymous_identity;
/**
* anonymous_identity_len - Length of anonymous_identity
*/
size_t anonymous_identity_len;
/**
* password - Password string for EAP
*
* This field can include either the plaintext password (default
* option) or a NtPasswordHash (16-byte MD4 hash of the unicode
* presentation of the password) if flags field has
* EAP_CONFIG_FLAGS_PASSWORD_NTHASH bit set to 1. NtPasswordHash can
* only be used with authentication mechanism that use this hash as the
* starting point for operation: MSCHAP and MSCHAPv2 (EAP-MSCHAPv2,
* EAP-TTLS/MSCHAPv2, EAP-TTLS/MSCHAP, LEAP).
*
* In addition, this field is used to configure a pre-shared key for
* EAP-PSK/PAX/SAKE/GPSK. The length of the PSK must be 16 for EAP-PSK
* and EAP-PAX and 32 for EAP-SAKE. EAP-GPSK can use a variable length
* PSK.
*/
u8 *password;
/**
* password_len - Length of password field
*/
size_t password_len;
/**
* ca_cert - File path to CA certificate file (PEM/DER)
*
* This file can have one or more trusted CA certificates. If ca_cert
* and ca_path are not included, server certificate will not be
* verified. This is insecure and a trusted CA certificate should
* always be configured when using EAP-TLS/TTLS/PEAP. Full path to the
* file should be used since working directory may change when
* wpa_supplicant is run in the background.
*
* Alternatively, a named configuration blob can be used by setting
* this to blob://blob_name.
*
* Alternatively, this can be used to only perform matching of the
* server certificate (SHA-256 hash of the DER encoded X.509
* certificate). In this case, the possible CA certificates in the
* server certificate chain are ignored and only the server certificate
* is verified. This is configured with the following format:
* hash:://server/sha256/cert_hash_in_hex
* For example: "hash://server/sha256/
* 5a1bc1296205e6fdbe3979728efe3920798885c1c4590b5f90f43222d239ca6a"
*
* On Windows, trusted CA certificates can be loaded from the system
* certificate store by setting this to cert_store://name, e.g.,
* ca_cert="cert_store://CA" or ca_cert="cert_store://ROOT".
* Note that when running wpa_supplicant as an application, the user
* certificate store (My user account) is used, whereas computer store
* (Computer account) is used when running wpasvc as a service.
*/
u8 *ca_cert;
/**
* ca_path - Directory path for CA certificate files (PEM)
*
* This path may contain multiple CA certificates in OpenSSL format.
* Common use for this is to point to system trusted CA list which is
* often installed into directory like /etc/ssl/certs. If configured,
* these certificates are added to the list of trusted CAs. ca_cert
* may also be included in that case, but it is not required.
*/
u8 *ca_path;
/**
* client_cert - File path to client certificate file (PEM/DER)
*
* This field is used with EAP method that use TLS authentication.
* Usually, this is only configured for EAP-TLS, even though this could
* in theory be used with EAP-TTLS and EAP-PEAP, too. Full path to the
* file should be used since working directory may change when
* wpa_supplicant is run in the background.
*
* Alternatively, a named configuration blob can be used by setting
* this to blob://blob_name.
*/
u8 *client_cert;
/**
* private_key - File path to client private key file (PEM/DER/PFX)
*
* When PKCS#12/PFX file (.p12/.pfx) is used, client_cert should be
* commented out. Both the private key and certificate will be read
* from the PKCS#12 file in this case. Full path to the file should be
* used since working directory may change when wpa_supplicant is run
* in the background.
*
* Windows certificate store can be used by leaving client_cert out and
* configuring private_key in one of the following formats:
*
* cert://substring_to_match
*
* hash://certificate_thumbprint_in_hex
*
* For example: private_key="hash://63093aa9c47f56ae88334c7b65a4"
*
* Note that when running wpa_supplicant as an application, the user
* certificate store (My user account) is used, whereas computer store
* (Computer account) is used when running wpasvc as a service.
*
* Alternatively, a named configuration blob can be used by setting
* this to blob://blob_name.
*/
u8 *private_key;
/**
* private_key_passwd - Password for private key file
*
* If left out, this will be asked through control interface.
*/
u8 *private_key_passwd;
/**
* dh_file - File path to DH/DSA parameters file (in PEM format)
*
* This is an optional configuration file for setting parameters for an
* ephemeral DH key exchange. In most cases, the default RSA
* authentication does not use this configuration. However, it is
* possible setup RSA to use ephemeral DH key exchange. In addition,
* ciphers with DSA keys always use ephemeral DH keys. This can be used
* to achieve forward secrecy. If the file is in DSA parameters format,
* it will be automatically converted into DH params. Full path to the
* file should be used since working directory may change when
* wpa_supplicant is run in the background.
*
* Alternatively, a named configuration blob can be used by setting
* this to blob://blob_name.
*/
u8 *dh_file;
/**
* subject_match - Constraint for server certificate subject
*
* This substring is matched against the subject of the authentication
* server certificate. If this string is set, the server sertificate is
* only accepted if it contains this string in the subject. The subject
* string is in following format:
*
* /C=US/ST=CA/L=San Francisco/CN=Test AS/emailAddress=as@n.example.com
*/
u8 *subject_match;
/**
* altsubject_match - Constraint for server certificate alt. subject
*
* Semicolon separated string of entries to be matched against the
* alternative subject name of the authentication server certificate.
* If this string is set, the server sertificate is only accepted if it
* contains one of the entries in an alternative subject name
* extension.
*
* altSubjectName string is in following format: TYPE:VALUE
*
* Example: EMAIL:server@example.com
* Example: DNS:server.example.com;DNS:server2.example.com
*
* Following types are supported: EMAIL, DNS, URI
*/
u8 *altsubject_match;
/**
* ca_cert2 - File path to CA certificate file (PEM/DER) (Phase 2)
*
* This file can have one or more trusted CA certificates. If ca_cert2
* and ca_path2 are not included, server certificate will not be
* verified. This is insecure and a trusted CA certificate should
* always be configured. Full path to the file should be used since
* working directory may change when wpa_supplicant is run in the
* background.
*
* This field is like ca_cert, but used for phase 2 (inside
* EAP-TTLS/PEAP/FAST tunnel) authentication.
*
* Alternatively, a named configuration blob can be used by setting
* this to blob://blob_name.
*/
u8 *ca_cert2;
/**
* ca_path2 - Directory path for CA certificate files (PEM) (Phase 2)
*
* This path may contain multiple CA certificates in OpenSSL format.
* Common use for this is to point to system trusted CA list which is
* often installed into directory like /etc/ssl/certs. If configured,
* these certificates are added to the list of trusted CAs. ca_cert
* may also be included in that case, but it is not required.
*
* This field is like ca_path, but used for phase 2 (inside
* EAP-TTLS/PEAP/FAST tunnel) authentication.
*/
u8 *ca_path2;
/**
* client_cert2 - File path to client certificate file
*
* This field is like client_cert, but used for phase 2 (inside
* EAP-TTLS/PEAP/FAST tunnel) authentication. Full path to the
* file should be used since working directory may change when
* wpa_supplicant is run in the background.
*
* Alternatively, a named configuration blob can be used by setting
* this to blob://blob_name.
*/
u8 *client_cert2;
/**
* private_key2 - File path to client private key file
*
* This field is like private_key, but used for phase 2 (inside
* EAP-TTLS/PEAP/FAST tunnel) authentication. Full path to the
* file should be used since working directory may change when
* wpa_supplicant is run in the background.
*
* Alternatively, a named configuration blob can be used by setting
* this to blob://blob_name.
*/
u8 *private_key2;
/**
* private_key2_passwd - Password for private key file
*
* This field is like private_key_passwd, but used for phase 2 (inside
* EAP-TTLS/PEAP/FAST tunnel) authentication.
*/
u8 *private_key2_passwd;
/**
* dh_file2 - File path to DH/DSA parameters file (in PEM format)
*
* This field is like dh_file, but used for phase 2 (inside
* EAP-TTLS/PEAP/FAST tunnel) authentication. Full path to the
* file should be used since working directory may change when
* wpa_supplicant is run in the background.
*
* Alternatively, a named configuration blob can be used by setting
* this to blob://blob_name.
*/
u8 *dh_file2;
/**
* subject_match2 - Constraint for server certificate subject
*
* This field is like subject_match, but used for phase 2 (inside
* EAP-TTLS/PEAP/FAST tunnel) authentication.
*/
u8 *subject_match2;
/**
* altsubject_match2 - Constraint for server certificate alt. subject
*
* This field is like altsubject_match, but used for phase 2 (inside
* EAP-TTLS/PEAP/FAST tunnel) authentication.
*/
u8 *altsubject_match2;
/**
* eap_methods - Allowed EAP methods
*
* (vendor=EAP_VENDOR_IETF,method=EAP_TYPE_NONE) terminated list of
* allowed EAP methods or %NULL if all methods are accepted.
*/
struct eap_method_type *eap_methods;
/**
* phase1 - Phase 1 (outer authentication) parameters
*
* String with field-value pairs, e.g., "peapver=0" or
* "peapver=1 peaplabel=1".
*
* 'peapver' can be used to force which PEAP version (0 or 1) is used.
*
* 'peaplabel=1' can be used to force new label, "client PEAP
* encryption", to be used during key derivation when PEAPv1 or newer.
*
* Most existing PEAPv1 implementation seem to be using the old label,
* "client EAP encryption", and wpa_supplicant is now using that as the
* default value.
*
* Some servers, e.g., Radiator, may require peaplabel=1 configuration
* to interoperate with PEAPv1; see eap_testing.txt for more details.
*
* 'peap_outer_success=0' can be used to terminate PEAP authentication
* on tunneled EAP-Success. This is required with some RADIUS servers
* that implement draft-josefsson-pppext-eap-tls-eap-05.txt (e.g.,
* Lucent NavisRadius v4.4.0 with PEAP in "IETF Draft 5" mode).
*
* include_tls_length=1 can be used to force wpa_supplicant to include
* TLS Message Length field in all TLS messages even if they are not
* fragmented.
*
* sim_min_num_chal=3 can be used to configure EAP-SIM to require three
* challenges (by default, it accepts 2 or 3).
*
* result_ind=1 can be used to enable EAP-SIM and EAP-AKA to use
* protected result indication.
*
* fast_provisioning option can be used to enable in-line provisioning
* of EAP-FAST credentials (PAC):
* 0 = disabled,
* 1 = allow unauthenticated provisioning,
* 2 = allow authenticated provisioning,
* 3 = allow both unauthenticated and authenticated provisioning
*
* fast_max_pac_list_len=num option can be used to set the maximum
* number of PAC entries to store in a PAC list (default: 10).
*
* fast_pac_format=binary option can be used to select binary format
* for storing PAC entries in order to save some space (the default
* text format uses about 2.5 times the size of minimal binary format).
*
* crypto_binding option can be used to control PEAPv0 cryptobinding
* behavior:
* 0 = do not use cryptobinding (default)
* 1 = use cryptobinding if server supports it
* 2 = require cryptobinding
*
* EAP-WSC (WPS) uses following options: pin=Device_Password and
* uuid=Device_UUID
*/
char *phase1;
/**
* phase2 - Phase2 (inner authentication with TLS tunnel) parameters
*
* String with field-value pairs, e.g., "auth=MSCHAPV2" for EAP-PEAP or
* "autheap=MSCHAPV2 autheap=MD5" for EAP-TTLS.
*/
char *phase2;
/**
* pcsc - Parameters for PC/SC smartcard interface for USIM and GSM SIM
*
* This field is used to configure PC/SC smartcard interface.
* Currently, the only configuration is whether this field is %NULL (do
* not use PC/SC) or non-NULL (e.g., "") to enable PC/SC.
*
* This field is used for EAP-SIM and EAP-AKA.
*/
char *pcsc;
/**
* pin - PIN for USIM, GSM SIM, and smartcards
*
* This field is used to configure PIN for SIM and smartcards for
* EAP-SIM and EAP-AKA. In addition, this is used with EAP-TLS if a
* smartcard is used for private key operations.
*
* If left out, this will be asked through control interface.
*/
char *pin;
/**
* engine - Enable OpenSSL engine (e.g., for smartcard access)
*
* This is used if private key operations for EAP-TLS are performed
* using a smartcard.
*/
int engine;
/**
* engine_id - Engine ID for OpenSSL engine
*
* "opensc" to select OpenSC engine or "pkcs11" to select PKCS#11
* engine.
*
* This is used if private key operations for EAP-TLS are performed
* using a smartcard.
*/
char *engine_id;
/**
* engine2 - Enable OpenSSL engine (e.g., for smartcard) (Phase 2)
*
* This is used if private key operations for EAP-TLS are performed
* using a smartcard.
*
* This field is like engine, but used for phase 2 (inside
* EAP-TTLS/PEAP/FAST tunnel) authentication.
*/
int engine2;
/**
* pin2 - PIN for USIM, GSM SIM, and smartcards (Phase 2)
*
* This field is used to configure PIN for SIM and smartcards for
* EAP-SIM and EAP-AKA. In addition, this is used with EAP-TLS if a
* smartcard is used for private key operations.
*
* This field is like pin2, but used for phase 2 (inside
* EAP-TTLS/PEAP/FAST tunnel) authentication.
*
* If left out, this will be asked through control interface.
*/
char *pin2;
/**
* engine2_id - Engine ID for OpenSSL engine (Phase 2)
*
* "opensc" to select OpenSC engine or "pkcs11" to select PKCS#11
* engine.
*
* This is used if private key operations for EAP-TLS are performed
* using a smartcard.
*
* This field is like engine_id, but used for phase 2 (inside
* EAP-TTLS/PEAP/FAST tunnel) authentication.
*/
char *engine2_id;
/**
* key_id - Key ID for OpenSSL engine
*
* This is used if private key operations for EAP-TLS are performed
* using a smartcard.
*/
char *key_id;
/**
* cert_id - Cert ID for OpenSSL engine
*
* This is used if the certificate operations for EAP-TLS are performed
* using a smartcard.
*/
char *cert_id;
/**
* ca_cert_id - CA Cert ID for OpenSSL engine
*
* This is used if the CA certificate for EAP-TLS is on a smartcard.
*/
char *ca_cert_id;
/**
* key2_id - Key ID for OpenSSL engine (phase2)
*
* This is used if private key operations for EAP-TLS are performed
* using a smartcard.
*/
char *key2_id;
/**
* cert2_id - Cert ID for OpenSSL engine (phase2)
*
* This is used if the certificate operations for EAP-TLS are performed
* using a smartcard.
*/
char *cert2_id;
/**
* ca_cert2_id - CA Cert ID for OpenSSL engine (phase2)
*
* This is used if the CA certificate for EAP-TLS is on a smartcard.
*/
char *ca_cert2_id;
/**
* otp - One-time-password
*
* This field should not be set in configuration step. It is only used
* internally when OTP is entered through the control interface.
*/
u8 *otp;
/**
* otp_len - Length of the otp field
*/
size_t otp_len;
/**
* pending_req_identity - Whether there is a pending identity request
*
* This field should not be set in configuration step. It is only used
* internally when control interface is used to request needed
* information.
*/
int pending_req_identity;
/**
* pending_req_password - Whether there is a pending password request
*
* This field should not be set in configuration step. It is only used
* internally when control interface is used to request needed
* information.
*/
int pending_req_password;
/**
* pending_req_pin - Whether there is a pending PIN request
*
* This field should not be set in configuration step. It is only used
* internally when control interface is used to request needed
* information.
*/
int pending_req_pin;
/**
* pending_req_new_password - Pending password update request
*
* This field should not be set in configuration step. It is only used
* internally when control interface is used to request needed
* information.
*/
int pending_req_new_password;
/**
* pending_req_passphrase - Pending passphrase request
*
* This field should not be set in configuration step. It is only used
* internally when control interface is used to request needed
* information.
*/
int pending_req_passphrase;
/**
* pending_req_otp - Whether there is a pending OTP request
*
* This field should not be set in configuration step. It is only used
* internally when control interface is used to request needed
* information.
*/
char *pending_req_otp;
/**
* pending_req_otp_len - Length of the pending OTP request
*/
size_t pending_req_otp_len;
/**
* pac_file - File path or blob name for the PAC entries (EAP-FAST)
*
* wpa_supplicant will need to be able to create this file and write
* updates to it when PAC is being provisioned or refreshed. Full path
* to the file should be used since working directory may change when
* wpa_supplicant is run in the background.
* Alternatively, a named configuration blob can be used by setting
* this to blob://blob_name.
*/
char *pac_file;
/**
* mschapv2_retry - MSCHAPv2 retry in progress
*
* This field is used internally by EAP-MSCHAPv2 and should not be set
* as part of configuration.
*/
int mschapv2_retry;
/**
* new_password - New password for password update
*
* This field is used during MSCHAPv2 password update. This is normally
* requested from the user through the control interface and not set
* from configuration.
*/
u8 *new_password;
/**
* new_password_len - Length of new_password field
*/
size_t new_password_len;
/**
* fragment_size - Maximum EAP fragment size in bytes (default 1398)
*
* This value limits the fragment size for EAP methods that support
* fragmentation (e.g., EAP-TLS and EAP-PEAP). This value should be set
* small enough to make the EAP messages fit in MTU of the network
* interface used for EAPOL. The default value is suitable for most
* cases.
*/
int fragment_size;
#define EAP_CONFIG_FLAGS_PASSWORD_NTHASH BIT(0)
/**
* flags - Network configuration flags (bitfield)
*
* This variable is used for internal flags to describe further details
* for the network parameters.
* bit 0 = password is represented as a 16-byte NtPasswordHash value
* instead of plaintext password
*/
u32 flags;
};
/**
* struct wpa_config_blob - Named configuration blob
*
* This data structure is used to provide storage for binary objects to store
* abstract information like certificates and private keys inlined with the
* configuration data.
*/
struct wpa_config_blob {
/**
* name - Blob name
*/
char *name;
/**
* data - Pointer to binary data
*/
u8 *data;
/**
* len - Length of binary data
*/
size_t len;
/**
* next - Pointer to next blob in the configuration
*/
struct wpa_config_blob *next;
};
#endif /* EAP_CONFIG_H */

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,923 @@
/*
* EAP peer method: EAP-FAST PAC file processing
* Copyright (c) 2004-2006, Jouni Malinen <j@w1.fi>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* Alternatively, this software may be distributed under the terms of BSD
* license.
*
* See README and COPYING for more details.
*/
#include "includes.h"
#include "common.h"
#include "eap_config.h"
#include "eap_i.h"
#include "eap_fast_pac.h"
/* TODO: encrypt PAC-Key in the PAC file */
/* Text data format */
static const char *pac_file_hdr =
"wpa_supplicant EAP-FAST PAC file - version 1";
/*
* Binary data format
* 4-octet magic value: 6A E4 92 0C
* 2-octet version (big endian)
* <version specific data>
*
* version=0:
* Sequence of PAC entries:
* 2-octet PAC-Type (big endian)
* 32-octet PAC-Key
* 2-octet PAC-Opaque length (big endian)
* <variable len> PAC-Opaque data (length bytes)
* 2-octet PAC-Info length (big endian)
* <variable len> PAC-Info data (length bytes)
*/
#define EAP_FAST_PAC_BINARY_MAGIC 0x6ae4920c
#define EAP_FAST_PAC_BINARY_FORMAT_VERSION 0
/**
* eap_fast_free_pac - Free PAC data
* @pac: Pointer to the PAC entry
*
* Note that the PAC entry must not be in a list since this function does not
* remove the list links.
*/
void eap_fast_free_pac(struct eap_fast_pac *pac)
{
os_free(pac->pac_opaque);
os_free(pac->pac_info);
os_free(pac->a_id);
os_free(pac->i_id);
os_free(pac->a_id_info);
os_free(pac);
}
/**
* eap_fast_get_pac - Get a PAC entry based on A-ID
* @pac_root: Pointer to root of the PAC list
* @a_id: A-ID to search for
* @a_id_len: Length of A-ID
* @pac_type: PAC-Type to search for
* Returns: Pointer to the PAC entry, or %NULL if A-ID not found
*/
struct eap_fast_pac * eap_fast_get_pac(struct eap_fast_pac *pac_root,
const u8 *a_id, size_t a_id_len,
u16 pac_type)
{
struct eap_fast_pac *pac = pac_root;
while (pac) {
if (pac->pac_type == pac_type && pac->a_id_len == a_id_len &&
os_memcmp(pac->a_id, a_id, a_id_len) == 0) {
return pac;
}
pac = pac->next;
}
return NULL;
}
static void eap_fast_remove_pac(struct eap_fast_pac **pac_root,
struct eap_fast_pac **pac_current,
const u8 *a_id, size_t a_id_len, u16 pac_type)
{
struct eap_fast_pac *pac, *prev;
pac = *pac_root;
prev = NULL;
while (pac) {
if (pac->pac_type == pac_type && pac->a_id_len == a_id_len &&
os_memcmp(pac->a_id, a_id, a_id_len) == 0) {
if (prev == NULL)
*pac_root = pac->next;
else
prev->next = pac->next;
if (*pac_current == pac)
*pac_current = NULL;
eap_fast_free_pac(pac);
break;
}
prev = pac;
pac = pac->next;
}
}
static int eap_fast_copy_buf(u8 **dst, size_t *dst_len,
const u8 *src, size_t src_len)
{
if (src) {
*dst = os_malloc(src_len);
if (*dst == NULL)
return -1;
os_memcpy(*dst, src, src_len);
*dst_len = src_len;
}
return 0;
}
/**
* eap_fast_add_pac - Add a copy of a PAC entry to a list
* @pac_root: Pointer to PAC list root pointer
* @pac_current: Pointer to the current PAC pointer
* @entry: New entry to clone and add to the list
* Returns: 0 on success, -1 on failure
*
* This function makes a clone of the given PAC entry and adds this copied
* entry to the list (pac_root). If an old entry for the same A-ID is found,
* it will be removed from the PAC list and in this case, pac_current entry
* is set to %NULL if it was the removed entry.
*/
int eap_fast_add_pac(struct eap_fast_pac **pac_root,
struct eap_fast_pac **pac_current,
struct eap_fast_pac *entry)
{
struct eap_fast_pac *pac;
if (entry == NULL || entry->a_id == NULL)
return -1;
/* Remove a possible old entry for the matching A-ID. */
eap_fast_remove_pac(pac_root, pac_current,
entry->a_id, entry->a_id_len, entry->pac_type);
/* Allocate a new entry and add it to the list of PACs. */
pac = os_zalloc(sizeof(*pac));
if (pac == NULL)
return -1;
pac->pac_type = entry->pac_type;
os_memcpy(pac->pac_key, entry->pac_key, EAP_FAST_PAC_KEY_LEN);
if (eap_fast_copy_buf(&pac->pac_opaque, &pac->pac_opaque_len,
entry->pac_opaque, entry->pac_opaque_len) < 0 ||
eap_fast_copy_buf(&pac->pac_info, &pac->pac_info_len,
entry->pac_info, entry->pac_info_len) < 0 ||
eap_fast_copy_buf(&pac->a_id, &pac->a_id_len,
entry->a_id, entry->a_id_len) < 0 ||
eap_fast_copy_buf(&pac->i_id, &pac->i_id_len,
entry->i_id, entry->i_id_len) < 0 ||
eap_fast_copy_buf(&pac->a_id_info, &pac->a_id_info_len,
entry->a_id_info, entry->a_id_info_len) < 0) {
eap_fast_free_pac(pac);
return -1;
}
pac->next = *pac_root;
*pac_root = pac;
return 0;
}
struct eap_fast_read_ctx {
FILE *f;
const char *pos;
const char *end;
int line;
char *buf;
size_t buf_len;
};
static int eap_fast_read_line(struct eap_fast_read_ctx *rc, char **value)
{
char *pos;
rc->line++;
if (rc->f) {
if (fgets(rc->buf, rc->buf_len, rc->f) == NULL)
return -1;
} else {
const char *l_end;
size_t len;
if (rc->pos >= rc->end)
return -1;
l_end = rc->pos;
while (l_end < rc->end && *l_end != '\n')
l_end++;
len = l_end - rc->pos;
if (len >= rc->buf_len)
len = rc->buf_len - 1;
os_memcpy(rc->buf, rc->pos, len);
rc->buf[len] = '\0';
rc->pos = l_end + 1;
}
rc->buf[rc->buf_len - 1] = '\0';
pos = rc->buf;
while (*pos != '\0') {
if (*pos == '\n' || *pos == '\r') {
*pos = '\0';
break;
}
pos++;
}
pos = os_strchr(rc->buf, '=');
if (pos)
*pos++ = '\0';
*value = pos;
return 0;
}
static u8 * eap_fast_parse_hex(const char *value, size_t *len)
{
int hlen;
u8 *buf;
if (value == NULL)
return NULL;
hlen = os_strlen(value);
if (hlen & 1)
return NULL;
*len = hlen / 2;
buf = os_malloc(*len);
if (buf == NULL)
return NULL;
if (hexstr2bin(value, buf, *len)) {
os_free(buf);
return NULL;
}
return buf;
}
static int eap_fast_init_pac_data(struct eap_sm *sm, const char *pac_file,
struct eap_fast_read_ctx *rc)
{
os_memset(rc, 0, sizeof(*rc));
rc->buf_len = 2048;
rc->buf = os_malloc(rc->buf_len);
if (rc->buf == NULL)
return -1;
if (os_strncmp(pac_file, "blob://", 7) == 0) {
const struct wpa_config_blob *blob;
blob = eap_get_config_blob(sm, pac_file + 7);
if (blob == NULL) {
wpa_printf(MSG_INFO, "EAP-FAST: No PAC blob '%s' - "
"assume no PAC entries have been "
"provisioned", pac_file + 7);
os_free(rc->buf);
return -1;
}
rc->pos = (char *) blob->data;
rc->end = (char *) blob->data + blob->len;
} else {
rc->f = fopen(pac_file, "rb");
if (rc->f == NULL) {
wpa_printf(MSG_INFO, "EAP-FAST: No PAC file '%s' - "
"assume no PAC entries have been "
"provisioned", pac_file);
os_free(rc->buf);
return -1;
}
}
return 0;
}
static void eap_fast_deinit_pac_data(struct eap_fast_read_ctx *rc)
{
os_free(rc->buf);
if (rc->f)
fclose(rc->f);
}
static const char * eap_fast_parse_start(struct eap_fast_pac **pac)
{
if (*pac)
return "START line without END";
*pac = os_zalloc(sizeof(struct eap_fast_pac));
if (*pac == NULL)
return "No memory for PAC entry";
(*pac)->pac_type = PAC_TYPE_TUNNEL_PAC;
return NULL;
}
static const char * eap_fast_parse_end(struct eap_fast_pac **pac_root,
struct eap_fast_pac **pac)
{
if (*pac == NULL)
return "END line without START";
if (*pac_root) {
struct eap_fast_pac *end = *pac_root;
while (end->next)
end = end->next;
end->next = *pac;
} else
*pac_root = *pac;
*pac = NULL;
return NULL;
}
static const char * eap_fast_parse_pac_type(struct eap_fast_pac *pac,
char *pos)
{
pac->pac_type = atoi(pos);
if (pac->pac_type != PAC_TYPE_TUNNEL_PAC &&
pac->pac_type != PAC_TYPE_USER_AUTHORIZATION &&
pac->pac_type != PAC_TYPE_MACHINE_AUTHENTICATION)
return "Unrecognized PAC-Type";
return NULL;
}
static const char * eap_fast_parse_pac_key(struct eap_fast_pac *pac, char *pos)
{
u8 *key;
size_t key_len;
key = eap_fast_parse_hex(pos, &key_len);
if (key == NULL || key_len != EAP_FAST_PAC_KEY_LEN) {
os_free(key);
return "Invalid PAC-Key";
}
os_memcpy(pac->pac_key, key, EAP_FAST_PAC_KEY_LEN);
os_free(key);
return NULL;
}
static const char * eap_fast_parse_pac_opaque(struct eap_fast_pac *pac,
char *pos)
{
os_free(pac->pac_opaque);
pac->pac_opaque = eap_fast_parse_hex(pos, &pac->pac_opaque_len);
if (pac->pac_opaque == NULL)
return "Invalid PAC-Opaque";
return NULL;
}
static const char * eap_fast_parse_a_id(struct eap_fast_pac *pac, char *pos)
{
os_free(pac->a_id);
pac->a_id = eap_fast_parse_hex(pos, &pac->a_id_len);
if (pac->a_id == NULL)
return "Invalid A-ID";
return NULL;
}
static const char * eap_fast_parse_i_id(struct eap_fast_pac *pac, char *pos)
{
os_free(pac->i_id);
pac->i_id = eap_fast_parse_hex(pos, &pac->i_id_len);
if (pac->i_id == NULL)
return "Invalid I-ID";
return NULL;
}
static const char * eap_fast_parse_a_id_info(struct eap_fast_pac *pac,
char *pos)
{
os_free(pac->a_id_info);
pac->a_id_info = eap_fast_parse_hex(pos, &pac->a_id_info_len);
if (pac->a_id_info == NULL)
return "Invalid A-ID-Info";
return NULL;
}
/**
* eap_fast_load_pac - Load PAC entries (text format)
* @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
* @pac_root: Pointer to root of the PAC list (to be filled)
* @pac_file: Name of the PAC file/blob to load
* Returns: 0 on success, -1 on failure
*/
int eap_fast_load_pac(struct eap_sm *sm, struct eap_fast_pac **pac_root,
const char *pac_file)
{
struct eap_fast_read_ctx rc;
struct eap_fast_pac *pac = NULL;
int count = 0;
char *pos;
const char *err = NULL;
if (pac_file == NULL)
return -1;
if (eap_fast_init_pac_data(sm, pac_file, &rc) < 0)
return 0;
if (eap_fast_read_line(&rc, &pos) < 0 ||
os_strcmp(pac_file_hdr, rc.buf) != 0)
err = "Unrecognized header line";
while (!err && eap_fast_read_line(&rc, &pos) == 0) {
if (os_strcmp(rc.buf, "START") == 0)
err = eap_fast_parse_start(&pac);
else if (os_strcmp(rc.buf, "END") == 0) {
err = eap_fast_parse_end(pac_root, &pac);
count++;
} else if (!pac)
err = "Unexpected line outside START/END block";
else if (os_strcmp(rc.buf, "PAC-Type") == 0)
err = eap_fast_parse_pac_type(pac, pos);
else if (os_strcmp(rc.buf, "PAC-Key") == 0)
err = eap_fast_parse_pac_key(pac, pos);
else if (os_strcmp(rc.buf, "PAC-Opaque") == 0)
err = eap_fast_parse_pac_opaque(pac, pos);
else if (os_strcmp(rc.buf, "A-ID") == 0)
err = eap_fast_parse_a_id(pac, pos);
else if (os_strcmp(rc.buf, "I-ID") == 0)
err = eap_fast_parse_i_id(pac, pos);
else if (os_strcmp(rc.buf, "A-ID-Info") == 0)
err = eap_fast_parse_a_id_info(pac, pos);
}
if (pac) {
err = "PAC block not terminated with END";
eap_fast_free_pac(pac);
}
eap_fast_deinit_pac_data(&rc);
if (err) {
wpa_printf(MSG_INFO, "EAP-FAST: %s in '%s:%d'",
err, pac_file, rc.line);
return -1;
}
wpa_printf(MSG_DEBUG, "EAP-FAST: Read %d PAC entries from '%s'",
count, pac_file);
return 0;
}
static void eap_fast_write(char **buf, char **pos, size_t *buf_len,
const char *field, const u8 *data,
size_t len, int txt)
{
size_t i, need;
int ret;
char *end;
if (data == NULL || buf == NULL || *buf == NULL ||
pos == NULL || *pos == NULL || *pos < *buf)
return;
need = os_strlen(field) + len * 2 + 30;
if (txt)
need += os_strlen(field) + len + 20;
if (*pos - *buf + need > *buf_len) {
char *nbuf = os_realloc(*buf, *buf_len + need);
if (nbuf == NULL) {
os_free(*buf);
*buf = NULL;
return;
}
*pos = nbuf + (*pos - *buf);
*buf = nbuf;
*buf_len += need;
}
end = *buf + *buf_len;
ret = os_snprintf(*pos, end - *pos, "%s=", field);
if (ret < 0 || ret >= end - *pos)
return;
*pos += ret;
*pos += wpa_snprintf_hex(*pos, end - *pos, data, len);
ret = os_snprintf(*pos, end - *pos, "\n");
if (ret < 0 || ret >= end - *pos)
return;
*pos += ret;
if (txt) {
ret = os_snprintf(*pos, end - *pos, "%s-txt=", field);
if (ret < 0 || ret >= end - *pos)
return;
*pos += ret;
for (i = 0; i < len; i++) {
ret = os_snprintf(*pos, end - *pos, "%c", data[i]);
if (ret < 0 || ret >= end - *pos)
return;
*pos += ret;
}
ret = os_snprintf(*pos, end - *pos, "\n");
if (ret < 0 || ret >= end - *pos)
return;
*pos += ret;
}
}
static int eap_fast_write_pac(struct eap_sm *sm, const char *pac_file,
char *buf, size_t len)
{
if (os_strncmp(pac_file, "blob://", 7) == 0) {
struct wpa_config_blob *blob;
blob = os_zalloc(sizeof(*blob));
if (blob == NULL)
return -1;
blob->data = (u8 *) buf;
blob->len = len;
buf = NULL;
blob->name = os_strdup(pac_file + 7);
if (blob->name == NULL) {
os_free(blob);
return -1;
}
eap_set_config_blob(sm, blob);
} else {
FILE *f;
f = fopen(pac_file, "wb");
if (f == NULL) {
wpa_printf(MSG_INFO, "EAP-FAST: Failed to open PAC "
"file '%s' for writing", pac_file);
return -1;
}
if (fwrite(buf, 1, len, f) != len) {
wpa_printf(MSG_INFO, "EAP-FAST: Failed to write all "
"PACs into '%s'", pac_file);
fclose(f);
return -1;
}
os_free(buf);
fclose(f);
}
return 0;
}
static int eap_fast_add_pac_data(struct eap_fast_pac *pac, char **buf,
char **pos, size_t *buf_len)
{
int ret;
ret = os_snprintf(*pos, *buf + *buf_len - *pos,
"START\nPAC-Type=%d\n", pac->pac_type);
if (ret < 0 || ret >= *buf + *buf_len - *pos)
return -1;
*pos += ret;
eap_fast_write(buf, pos, buf_len, "PAC-Key",
pac->pac_key, EAP_FAST_PAC_KEY_LEN, 0);
eap_fast_write(buf, pos, buf_len, "PAC-Opaque",
pac->pac_opaque, pac->pac_opaque_len, 0);
eap_fast_write(buf, pos, buf_len, "PAC-Info",
pac->pac_info, pac->pac_info_len, 0);
eap_fast_write(buf, pos, buf_len, "A-ID",
pac->a_id, pac->a_id_len, 0);
eap_fast_write(buf, pos, buf_len, "I-ID",
pac->i_id, pac->i_id_len, 1);
eap_fast_write(buf, pos, buf_len, "A-ID-Info",
pac->a_id_info, pac->a_id_info_len, 1);
if (*buf == NULL) {
wpa_printf(MSG_DEBUG, "EAP-FAST: No memory for PAC "
"data");
return -1;
}
ret = os_snprintf(*pos, *buf + *buf_len - *pos, "END\n");
if (ret < 0 || ret >= *buf + *buf_len - *pos)
return -1;
*pos += ret;
return 0;
}
/**
* eap_fast_save_pac - Save PAC entries (text format)
* @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
* @pac_root: Root of the PAC list
* @pac_file: Name of the PAC file/blob
* Returns: 0 on success, -1 on failure
*/
int eap_fast_save_pac(struct eap_sm *sm, struct eap_fast_pac *pac_root,
const char *pac_file)
{
struct eap_fast_pac *pac;
int ret, count = 0;
char *buf, *pos;
size_t buf_len;
if (pac_file == NULL)
return -1;
buf_len = 1024;
pos = buf = os_malloc(buf_len);
if (buf == NULL)
return -1;
ret = os_snprintf(pos, buf + buf_len - pos, "%s\n", pac_file_hdr);
if (ret < 0 || ret >= buf + buf_len - pos) {
os_free(buf);
return -1;
}
pos += ret;
pac = pac_root;
while (pac) {
if (eap_fast_add_pac_data(pac, &buf, &pos, &buf_len)) {
os_free(buf);
return -1;
}
count++;
pac = pac->next;
}
if (eap_fast_write_pac(sm, pac_file, buf, pos - buf)) {
os_free(buf);
return -1;
}
wpa_printf(MSG_DEBUG, "EAP-FAST: Wrote %d PAC entries into '%s'",
count, pac_file);
return 0;
}
/**
* eap_fast_pac_list_truncate - Truncate a PAC list to the given length
* @pac_root: Root of the PAC list
* @max_len: Maximum length of the list (>= 1)
* Returns: Number of PAC entries removed
*/
size_t eap_fast_pac_list_truncate(struct eap_fast_pac *pac_root,
size_t max_len)
{
struct eap_fast_pac *pac, *prev;
size_t count;
pac = pac_root;
prev = NULL;
count = 0;
while (pac) {
count++;
if (count > max_len)
break;
prev = pac;
pac = pac->next;
}
if (count <= max_len || prev == NULL)
return 0;
count = 0;
prev->next = NULL;
while (pac) {
prev = pac;
pac = pac->next;
eap_fast_free_pac(prev);
count++;
}
return count;
}
static void eap_fast_pac_get_a_id(struct eap_fast_pac *pac)
{
u8 *pos, *end;
u16 type, len;
pos = pac->pac_info;
end = pos + pac->pac_info_len;
while (pos + 4 < end) {
type = WPA_GET_BE16(pos);
pos += 2;
len = WPA_GET_BE16(pos);
pos += 2;
if (pos + len > end)
break;
if (type == PAC_TYPE_A_ID) {
os_free(pac->a_id);
pac->a_id = os_malloc(len);
if (pac->a_id == NULL)
break;
os_memcpy(pac->a_id, pos, len);
pac->a_id_len = len;
}
if (type == PAC_TYPE_A_ID_INFO) {
os_free(pac->a_id_info);
pac->a_id_info = os_malloc(len);
if (pac->a_id_info == NULL)
break;
os_memcpy(pac->a_id_info, pos, len);
pac->a_id_info_len = len;
}
pos += len;
}
}
/**
* eap_fast_load_pac_bin - Load PAC entries (binary format)
* @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
* @pac_root: Pointer to root of the PAC list (to be filled)
* @pac_file: Name of the PAC file/blob to load
* Returns: 0 on success, -1 on failure
*/
int eap_fast_load_pac_bin(struct eap_sm *sm, struct eap_fast_pac **pac_root,
const char *pac_file)
{
const struct wpa_config_blob *blob = NULL;
u8 *buf, *end, *pos;
size_t len, count = 0;
struct eap_fast_pac *pac, *prev;
*pac_root = NULL;
if (pac_file == NULL)
return -1;
if (os_strncmp(pac_file, "blob://", 7) == 0) {
blob = eap_get_config_blob(sm, pac_file + 7);
if (blob == NULL) {
wpa_printf(MSG_INFO, "EAP-FAST: No PAC blob '%s' - "
"assume no PAC entries have been "
"provisioned", pac_file + 7);
return 0;
}
buf = blob->data;
len = blob->len;
} else {
buf = (u8 *) os_readfile(pac_file, &len);
if (buf == NULL) {
wpa_printf(MSG_INFO, "EAP-FAST: No PAC file '%s' - "
"assume no PAC entries have been "
"provisioned", pac_file);
return 0;
}
}
if (len == 0) {
if (blob == NULL)
os_free(buf);
return 0;
}
if (len < 6 || WPA_GET_BE32(buf) != EAP_FAST_PAC_BINARY_MAGIC ||
WPA_GET_BE16(buf + 4) != EAP_FAST_PAC_BINARY_FORMAT_VERSION) {
wpa_printf(MSG_INFO, "EAP-FAST: Invalid PAC file '%s' (bin)",
pac_file);
if (blob == NULL)
os_free(buf);
return -1;
}
pac = prev = NULL;
pos = buf + 6;
end = buf + len;
while (pos < end) {
if (end - pos < 2 + 32 + 2 + 2)
goto parse_fail;
pac = os_zalloc(sizeof(*pac));
if (pac == NULL)
goto parse_fail;
pac->pac_type = WPA_GET_BE16(pos);
pos += 2;
os_memcpy(pac->pac_key, pos, EAP_FAST_PAC_KEY_LEN);
pos += EAP_FAST_PAC_KEY_LEN;
pac->pac_opaque_len = WPA_GET_BE16(pos);
pos += 2;
if (pos + pac->pac_opaque_len + 2 > end)
goto parse_fail;
pac->pac_opaque = os_malloc(pac->pac_opaque_len);
if (pac->pac_opaque == NULL)
goto parse_fail;
os_memcpy(pac->pac_opaque, pos, pac->pac_opaque_len);
pos += pac->pac_opaque_len;
pac->pac_info_len = WPA_GET_BE16(pos);
pos += 2;
if (pos + pac->pac_info_len > end)
goto parse_fail;
pac->pac_info = os_malloc(pac->pac_info_len);
if (pac->pac_info == NULL)
goto parse_fail;
os_memcpy(pac->pac_info, pos, pac->pac_info_len);
pos += pac->pac_info_len;
eap_fast_pac_get_a_id(pac);
count++;
if (prev)
prev->next = pac;
else
*pac_root = pac;
prev = pac;
}
if (blob == NULL)
os_free(buf);
wpa_printf(MSG_DEBUG, "EAP-FAST: Read %lu PAC entries from '%s' (bin)",
(unsigned long) count, pac_file);
return 0;
parse_fail:
wpa_printf(MSG_INFO, "EAP-FAST: Failed to parse PAC file '%s' (bin)",
pac_file);
if (blob == NULL)
os_free(buf);
if (pac)
eap_fast_free_pac(pac);
return -1;
}
/**
* eap_fast_save_pac_bin - Save PAC entries (binary format)
* @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
* @pac_root: Root of the PAC list
* @pac_file: Name of the PAC file/blob
* Returns: 0 on success, -1 on failure
*/
int eap_fast_save_pac_bin(struct eap_sm *sm, struct eap_fast_pac *pac_root,
const char *pac_file)
{
size_t len, count = 0;
struct eap_fast_pac *pac;
u8 *buf, *pos;
len = 6;
pac = pac_root;
while (pac) {
if (pac->pac_opaque_len > 65535 ||
pac->pac_info_len > 65535)
return -1;
len += 2 + EAP_FAST_PAC_KEY_LEN + 2 + pac->pac_opaque_len +
2 + pac->pac_info_len;
pac = pac->next;
}
buf = os_malloc(len);
if (buf == NULL)
return -1;
pos = buf;
WPA_PUT_BE32(pos, EAP_FAST_PAC_BINARY_MAGIC);
pos += 4;
WPA_PUT_BE16(pos, EAP_FAST_PAC_BINARY_FORMAT_VERSION);
pos += 2;
pac = pac_root;
while (pac) {
WPA_PUT_BE16(pos, pac->pac_type);
pos += 2;
os_memcpy(pos, pac->pac_key, EAP_FAST_PAC_KEY_LEN);
pos += EAP_FAST_PAC_KEY_LEN;
WPA_PUT_BE16(pos, pac->pac_opaque_len);
pos += 2;
os_memcpy(pos, pac->pac_opaque, pac->pac_opaque_len);
pos += pac->pac_opaque_len;
WPA_PUT_BE16(pos, pac->pac_info_len);
pos += 2;
os_memcpy(pos, pac->pac_info, pac->pac_info_len);
pos += pac->pac_info_len;
pac = pac->next;
count++;
}
if (eap_fast_write_pac(sm, pac_file, (char *) buf, len)) {
os_free(buf);
return -1;
}
wpa_printf(MSG_DEBUG, "EAP-FAST: Wrote %lu PAC entries into '%s' "
"(bin)", (unsigned long) count, pac_file);
return 0;
}

View file

@ -0,0 +1,56 @@
/*
* EAP peer method: EAP-FAST PAC file processing
* Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* Alternatively, this software may be distributed under the terms of BSD
* license.
*
* See README and COPYING for more details.
*/
#ifndef EAP_FAST_PAC_H
#define EAP_FAST_PAC_H
#include "eap_common/eap_fast_common.h"
struct eap_fast_pac {
struct eap_fast_pac *next;
u8 pac_key[EAP_FAST_PAC_KEY_LEN];
u8 *pac_opaque;
size_t pac_opaque_len;
u8 *pac_info;
size_t pac_info_len;
u8 *a_id;
size_t a_id_len;
u8 *i_id;
size_t i_id_len;
u8 *a_id_info;
size_t a_id_info_len;
u16 pac_type;
};
void eap_fast_free_pac(struct eap_fast_pac *pac);
struct eap_fast_pac * eap_fast_get_pac(struct eap_fast_pac *pac_root,
const u8 *a_id, size_t a_id_len,
u16 pac_type);
int eap_fast_add_pac(struct eap_fast_pac **pac_root,
struct eap_fast_pac **pac_current,
struct eap_fast_pac *entry);
int eap_fast_load_pac(struct eap_sm *sm, struct eap_fast_pac **pac_root,
const char *pac_file);
int eap_fast_save_pac(struct eap_sm *sm, struct eap_fast_pac *pac_root,
const char *pac_file);
size_t eap_fast_pac_list_truncate(struct eap_fast_pac *pac_root,
size_t max_len);
int eap_fast_load_pac_bin(struct eap_sm *sm, struct eap_fast_pac **pac_root,
const char *pac_file);
int eap_fast_save_pac_bin(struct eap_sm *sm, struct eap_fast_pac *pac_root,
const char *pac_file);
#endif /* EAP_FAST_PAC_H */

View file

@ -0,0 +1,738 @@
/*
* EAP peer method: EAP-GPSK (RFC 5433)
* Copyright (c) 2006-2008, Jouni Malinen <j@w1.fi>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* Alternatively, this software may be distributed under the terms of BSD
* license.
*
* See README and COPYING for more details.
*/
#include "includes.h"
#include "common.h"
#include "crypto/random.h"
#include "eap_peer/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;
int session_id_set;
u8 *id_peer;
size_t id_peer_len;
u8 *id_server;
size_t id_server_len;
int vendor; /* CSuite/Specifier */
int specifier; /* CSuite/Specifier */
u8 *psk;
size_t psk_len;
};
static struct wpabuf * eap_gpsk_send_gpsk_2(struct eap_gpsk_data *data,
u8 identifier,
const u8 *csuite_list,
size_t csuite_list_len);
static struct wpabuf * eap_gpsk_send_gpsk_4(struct eap_gpsk_data *data,
u8 identifier);
#ifndef CONFIG_NO_STDOUT_DEBUG
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 "?";
}
}
#endif /* CONFIG_NO_STDOUT_DEBUG */
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_deinit(struct eap_sm *sm, void *priv);
static void * eap_gpsk_init(struct eap_sm *sm)
{
struct eap_gpsk_data *data;
const u8 *identity, *password;
size_t identity_len, password_len;
password = eap_get_config_password(sm, &password_len);
if (password == NULL) {
wpa_printf(MSG_INFO, "EAP-GPSK: No key (password) configured");
return NULL;
}
data = os_zalloc(sizeof(*data));
if (data == NULL)
return NULL;
data->state = GPSK_1;
identity = eap_get_config_identity(sm, &identity_len);
if (identity) {
data->id_peer = os_malloc(identity_len);
if (data->id_peer == NULL) {
eap_gpsk_deinit(sm, data);
return NULL;
}
os_memcpy(data->id_peer, identity, identity_len);
data->id_peer_len = identity_len;
}
data->psk = os_malloc(password_len);
if (data->psk == NULL) {
eap_gpsk_deinit(sm, data);
return NULL;
}
os_memcpy(data->psk, password, password_len);
data->psk_len = password_len;
return data;
}
static void eap_gpsk_deinit(struct eap_sm *sm, void *priv)
{
struct eap_gpsk_data *data = priv;
os_free(data->id_server);
os_free(data->id_peer);
os_free(data->psk);
os_free(data);
}
static const u8 * eap_gpsk_process_id_server(struct eap_gpsk_data *data,
const u8 *pos, const u8 *end)
{
u16 alen;
if (end - pos < 2) {
wpa_printf(MSG_DEBUG, "EAP-GPSK: Too short GPSK-1 packet");
return NULL;
}
alen = WPA_GET_BE16(pos);
pos += 2;
if (end - pos < alen) {
wpa_printf(MSG_DEBUG, "EAP-GPSK: ID_Server overflow");
return NULL;
}
os_free(data->id_server);
data->id_server = os_malloc(alen);
if (data->id_server == NULL) {
wpa_printf(MSG_DEBUG, "EAP-GPSK: No memory for ID_Server");
return NULL;
}
os_memcpy(data->id_server, pos, alen);
data->id_server_len = alen;
wpa_hexdump_ascii(MSG_DEBUG, "EAP-GPSK: ID_Server",
data->id_server, data->id_server_len);
pos += alen;
return pos;
}
static const u8 * eap_gpsk_process_rand_server(struct eap_gpsk_data *data,
const u8 *pos, const u8 *end)
{
if (pos == NULL)
return NULL;
if (end - pos < EAP_GPSK_RAND_LEN) {
wpa_printf(MSG_DEBUG, "EAP-GPSK: RAND_Server overflow");
return NULL;
}
os_memcpy(data->rand_server, pos, EAP_GPSK_RAND_LEN);
wpa_hexdump(MSG_DEBUG, "EAP-GPSK: RAND_Server",
data->rand_server, EAP_GPSK_RAND_LEN);
pos += EAP_GPSK_RAND_LEN;
return pos;
}
static int eap_gpsk_select_csuite(struct eap_sm *sm,
struct eap_gpsk_data *data,
const u8 *csuite_list,
size_t csuite_list_len)
{
struct eap_gpsk_csuite *csuite;
int i, count;
count = csuite_list_len / sizeof(struct eap_gpsk_csuite);
data->vendor = EAP_GPSK_VENDOR_IETF;
data->specifier = EAP_GPSK_CIPHER_RESERVED;
csuite = (struct eap_gpsk_csuite *) csuite_list;
for (i = 0; i < count; i++) {
int vendor, specifier;
vendor = WPA_GET_BE32(csuite->vendor);
specifier = WPA_GET_BE16(csuite->specifier);
wpa_printf(MSG_DEBUG, "EAP-GPSK: CSuite[%d]: %d:%d",
i, vendor, specifier);
if (data->vendor == EAP_GPSK_VENDOR_IETF &&
data->specifier == EAP_GPSK_CIPHER_RESERVED &&
eap_gpsk_supported_ciphersuite(vendor, specifier)) {
data->vendor = vendor;
data->specifier = specifier;
}
csuite++;
}
if (data->vendor == EAP_GPSK_VENDOR_IETF &&
data->specifier == EAP_GPSK_CIPHER_RESERVED) {
wpa_msg(sm->msg_ctx, MSG_INFO, "EAP-GPSK: No supported "
"ciphersuite found");
return -1;
}
wpa_printf(MSG_DEBUG, "EAP-GPSK: Selected ciphersuite %d:%d",
data->vendor, data->specifier);
return 0;
}
static const u8 * eap_gpsk_process_csuite_list(struct eap_sm *sm,
struct eap_gpsk_data *data,
const u8 **list,
size_t *list_len,
const u8 *pos, const u8 *end)
{
if (pos == NULL)
return NULL;
if (end - pos < 2) {
wpa_printf(MSG_DEBUG, "EAP-GPSK: Too short GPSK-1 packet");
return NULL;
}
*list_len = WPA_GET_BE16(pos);
pos += 2;
if (end - pos < (int) *list_len) {
wpa_printf(MSG_DEBUG, "EAP-GPSK: CSuite_List overflow");
return NULL;
}
if (*list_len == 0 || (*list_len % sizeof(struct eap_gpsk_csuite))) {
wpa_printf(MSG_DEBUG, "EAP-GPSK: Invalid CSuite_List len %lu",
(unsigned long) *list_len);
return NULL;
}
*list = pos;
pos += *list_len;
if (eap_gpsk_select_csuite(sm, data, *list, *list_len) < 0)
return NULL;
return pos;
}
static struct wpabuf * eap_gpsk_process_gpsk_1(struct eap_sm *sm,
struct eap_gpsk_data *data,
struct eap_method_ret *ret,
const struct wpabuf *reqData,
const u8 *payload,
size_t payload_len)
{
size_t csuite_list_len;
const u8 *csuite_list, *pos, *end;
struct wpabuf *resp;
if (data->state != GPSK_1) {
ret->ignore = TRUE;
return NULL;
}
wpa_printf(MSG_DEBUG, "EAP-GPSK: Received Request/GPSK-1");
end = payload + payload_len;
pos = eap_gpsk_process_id_server(data, payload, end);
pos = eap_gpsk_process_rand_server(data, pos, end);
pos = eap_gpsk_process_csuite_list(sm, data, &csuite_list,
&csuite_list_len, pos, end);
if (pos == NULL) {
eap_gpsk_state(data, FAILURE);
return NULL;
}
resp = eap_gpsk_send_gpsk_2(data, eap_get_id(reqData),
csuite_list, csuite_list_len);
if (resp == NULL)
return NULL;
eap_gpsk_state(data, GPSK_3);
return resp;
}
static struct wpabuf * eap_gpsk_send_gpsk_2(struct eap_gpsk_data *data,
u8 identifier,
const u8 *csuite_list,
size_t csuite_list_len)
{
struct wpabuf *resp;
size_t len, miclen;
u8 *rpos, *start;
struct eap_gpsk_csuite *csuite;
wpa_printf(MSG_DEBUG, "EAP-GPSK: Sending Response/GPSK-2");
miclen = eap_gpsk_mic_len(data->vendor, data->specifier);
len = 1 + 2 + data->id_peer_len + 2 + data->id_server_len +
2 * EAP_GPSK_RAND_LEN + 2 + csuite_list_len +
sizeof(struct eap_gpsk_csuite) + 2 + miclen;
resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_GPSK, len,
EAP_CODE_RESPONSE, identifier);
if (resp == NULL)
return NULL;
wpabuf_put_u8(resp, EAP_GPSK_OPCODE_GPSK_2);
start = wpabuf_put(resp, 0);
wpa_hexdump_ascii(MSG_DEBUG, "EAP-GPSK: ID_Peer",
data->id_peer, data->id_peer_len);
wpabuf_put_be16(resp, data->id_peer_len);
wpabuf_put_data(resp, data->id_peer, data->id_peer_len);
wpabuf_put_be16(resp, data->id_server_len);
wpabuf_put_data(resp, data->id_server, data->id_server_len);
if (random_get_bytes(data->rand_peer, EAP_GPSK_RAND_LEN)) {
wpa_printf(MSG_DEBUG, "EAP-GPSK: Failed to get random data "
"for RAND_Peer");
eap_gpsk_state(data, FAILURE);
wpabuf_free(resp);
return NULL;
}
wpa_hexdump(MSG_DEBUG, "EAP-GPSK: RAND_Peer",
data->rand_peer, EAP_GPSK_RAND_LEN);
wpabuf_put_data(resp, data->rand_peer, EAP_GPSK_RAND_LEN);
wpabuf_put_data(resp, data->rand_server, EAP_GPSK_RAND_LEN);
wpabuf_put_be16(resp, csuite_list_len);
wpabuf_put_data(resp, csuite_list, csuite_list_len);
csuite = wpabuf_put(resp, sizeof(*csuite));
WPA_PUT_BE32(csuite->vendor, data->vendor);
WPA_PUT_BE16(csuite->specifier, data->specifier);
if (eap_gpsk_derive_keys(data->psk, data->psk_len,
data->vendor, data->specifier,
data->rand_peer, data->rand_server,
data->id_peer, data->id_peer_len,
data->id_server, data->id_server_len,
data->msk, data->emsk,
data->sk, &data->sk_len,
data->pk, &data->pk_len) < 0) {
wpa_printf(MSG_DEBUG, "EAP-GPSK: Failed to derive keys");
eap_gpsk_state(data, FAILURE);
wpabuf_free(resp);
return NULL;
}
/* No PD_Payload_1 */
wpabuf_put_be16(resp, 0);
rpos = wpabuf_put(resp, miclen);
if (eap_gpsk_compute_mic(data->sk, data->sk_len, data->vendor,
data->specifier, start, rpos - start, rpos) <
0) {
eap_gpsk_state(data, FAILURE);
wpabuf_free(resp);
return NULL;
}
return resp;
}
static const u8 * eap_gpsk_validate_rand(struct eap_gpsk_data *data,
const u8 *pos, const u8 *end)
{
if (end - pos < EAP_GPSK_RAND_LEN) {
wpa_printf(MSG_DEBUG, "EAP-GPSK: Message too short for "
"RAND_Peer");
return NULL;
}
if (os_memcmp(pos, data->rand_peer, EAP_GPSK_RAND_LEN) != 0) {
wpa_printf(MSG_DEBUG, "EAP-GPSK: RAND_Peer in GPSK-2 and "
"GPSK-3 did not match");
wpa_hexdump(MSG_DEBUG, "EAP-GPSK: RAND_Peer in GPSK-2",
data->rand_peer, EAP_GPSK_RAND_LEN);
wpa_hexdump(MSG_DEBUG, "EAP-GPSK: RAND_Peer in GPSK-3",
pos, EAP_GPSK_RAND_LEN);
return NULL;
}
pos += EAP_GPSK_RAND_LEN;
if (end - pos < EAP_GPSK_RAND_LEN) {
wpa_printf(MSG_DEBUG, "EAP-GPSK: Message too short for "
"RAND_Server");
return NULL;
}
if (os_memcmp(pos, data->rand_server, EAP_GPSK_RAND_LEN) != 0) {
wpa_printf(MSG_DEBUG, "EAP-GPSK: RAND_Server in GPSK-1 and "
"GPSK-3 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-3",
pos, EAP_GPSK_RAND_LEN);
return NULL;
}
pos += EAP_GPSK_RAND_LEN;
return pos;
}
static const u8 * eap_gpsk_validate_id_server(struct eap_gpsk_data *data,
const u8 *pos, const u8 *end)
{
size_t len;
if (pos == NULL)
return NULL;
if (end - pos < (int) 2) {
wpa_printf(MSG_DEBUG, "EAP-GPSK: Message too short for "
"length(ID_Server)");
return NULL;
}
len = WPA_GET_BE16(pos);
pos += 2;
if (end - pos < (int) len) {
wpa_printf(MSG_DEBUG, "EAP-GPSK: Message too short for "
"ID_Server");
return NULL;
}
if (len != data->id_server_len ||
os_memcmp(pos, data->id_server, len) != 0) {
wpa_printf(MSG_INFO, "EAP-GPSK: ID_Server did not match with "
"the one used in GPSK-1");
wpa_hexdump_ascii(MSG_DEBUG, "EAP-GPSK: ID_Server in GPSK-1",
data->id_server, data->id_server_len);
wpa_hexdump_ascii(MSG_DEBUG, "EAP-GPSK: ID_Server in GPSK-3",
pos, len);
return NULL;
}
pos += len;
return pos;
}
static const u8 * eap_gpsk_validate_csuite(struct eap_gpsk_data *data,
const u8 *pos, const u8 *end)
{
int vendor, specifier;
const struct eap_gpsk_csuite *csuite;
if (pos == NULL)
return NULL;
if (end - pos < (int) sizeof(*csuite)) {
wpa_printf(MSG_DEBUG, "EAP-GPSK: Message too short for "
"CSuite_Sel");
return NULL;
}
csuite = (const struct eap_gpsk_csuite *) pos;
vendor = WPA_GET_BE32(csuite->vendor);
specifier = WPA_GET_BE16(csuite->specifier);
pos += sizeof(*csuite);
if (vendor != data->vendor || specifier != data->specifier) {
wpa_printf(MSG_DEBUG, "EAP-GPSK: CSuite_Sel (%d:%d) does not "
"match with the one sent in GPSK-2 (%d:%d)",
vendor, specifier, data->vendor, data->specifier);
return NULL;
}
return pos;
}
static const u8 * eap_gpsk_validate_pd_payload_2(struct eap_gpsk_data *data,
const u8 *pos, const u8 *end)
{
u16 alen;
if (pos == NULL)
return NULL;
if (end - pos < 2) {
wpa_printf(MSG_DEBUG, "EAP-GPSK: Message too short for "
"PD_Payload_2 length");
return NULL;
}
alen = WPA_GET_BE16(pos);
pos += 2;
if (end - pos < alen) {
wpa_printf(MSG_DEBUG, "EAP-GPSK: Message too short for "
"%d-octet PD_Payload_2", alen);
return NULL;
}
wpa_hexdump(MSG_DEBUG, "EAP-GPSK: PD_Payload_2", pos, alen);
pos += alen;
return pos;
}
static const u8 * eap_gpsk_validate_gpsk_3_mic(struct eap_gpsk_data *data,
const u8 *payload,
const u8 *pos, const u8 *end)
{
size_t miclen;
u8 mic[EAP_GPSK_MAX_MIC_LEN];
if (pos == NULL)
return NULL;
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);
return NULL;
}
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");
return NULL;
}
if (os_memcmp(mic, pos, miclen) != 0) {
wpa_printf(MSG_INFO, "EAP-GPSK: Incorrect MIC in GPSK-3");
wpa_hexdump(MSG_DEBUG, "EAP-GPSK: Received MIC", pos, miclen);
wpa_hexdump(MSG_DEBUG, "EAP-GPSK: Computed MIC", mic, miclen);
return NULL;
}
pos += miclen;
return pos;
}
static struct wpabuf * eap_gpsk_process_gpsk_3(struct eap_sm *sm,
struct eap_gpsk_data *data,
struct eap_method_ret *ret,
const struct wpabuf *reqData,
const u8 *payload,
size_t payload_len)
{
struct wpabuf *resp;
const u8 *pos, *end;
if (data->state != GPSK_3) {
ret->ignore = TRUE;
return NULL;
}
wpa_printf(MSG_DEBUG, "EAP-GPSK: Received Request/GPSK-3");
end = payload + payload_len;
pos = eap_gpsk_validate_rand(data, payload, end);
pos = eap_gpsk_validate_id_server(data, pos, end);
pos = eap_gpsk_validate_csuite(data, pos, end);
pos = eap_gpsk_validate_pd_payload_2(data, pos, end);
pos = eap_gpsk_validate_gpsk_3_mic(data, payload, pos, end);
if (pos == NULL) {
eap_gpsk_state(data, FAILURE);
return NULL;
}
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));
}
resp = eap_gpsk_send_gpsk_4(data, eap_get_id(reqData));
if (resp == NULL)
return NULL;
eap_gpsk_state(data, SUCCESS);
ret->methodState = METHOD_DONE;
ret->decision = DECISION_UNCOND_SUCC;
return resp;
}
static struct wpabuf * eap_gpsk_send_gpsk_4(struct eap_gpsk_data *data,
u8 identifier)
{
struct wpabuf *resp;
u8 *rpos, *start;
size_t mlen;
wpa_printf(MSG_DEBUG, "EAP-GPSK: Sending Response/GPSK-4");
mlen = eap_gpsk_mic_len(data->vendor, data->specifier);
resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_GPSK, 1 + 2 + mlen,
EAP_CODE_RESPONSE, identifier);
if (resp == NULL)
return NULL;
wpabuf_put_u8(resp, EAP_GPSK_OPCODE_GPSK_4);
start = wpabuf_put(resp, 0);
/* No PD_Payload_3 */
wpabuf_put_be16(resp, 0);
rpos = wpabuf_put(resp, mlen);
if (eap_gpsk_compute_mic(data->sk, data->sk_len, data->vendor,
data->specifier, start, rpos - start, rpos) <
0) {
eap_gpsk_state(data, FAILURE);
wpabuf_free(resp);
return NULL;
}
return resp;
}
static struct wpabuf * eap_gpsk_process(struct eap_sm *sm, void *priv,
struct eap_method_ret *ret,
const struct wpabuf *reqData)
{
struct eap_gpsk_data *data = priv;
struct wpabuf *resp;
const u8 *pos;
size_t len;
pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_GPSK, reqData, &len);
if (pos == NULL || len < 1) {
ret->ignore = TRUE;
return NULL;
}
wpa_printf(MSG_DEBUG, "EAP-GPSK: Received frame: opcode %d", *pos);
ret->ignore = FALSE;
ret->methodState = METHOD_MAY_CONT;
ret->decision = DECISION_FAIL;
ret->allowNotifications = FALSE;
switch (*pos) {
case EAP_GPSK_OPCODE_GPSK_1:
resp = eap_gpsk_process_gpsk_1(sm, data, ret, reqData,
pos + 1, len - 1);
break;
case EAP_GPSK_OPCODE_GPSK_3:
resp = eap_gpsk_process_gpsk_3(sm, data, ret, reqData,
pos + 1, len - 1);
break;
default:
wpa_printf(MSG_DEBUG, "EAP-GPSK: Ignoring message with "
"unknown opcode %d", *pos);
ret->ignore = TRUE;
return NULL;
}
return resp;
}
static Boolean eap_gpsk_isKeyAvailable(struct eap_sm *sm, void *priv)
{
struct eap_gpsk_data *data = priv;
return data->state == SUCCESS;
}
static u8 * eap_gpsk_getKey(struct eap_sm *sm, void *priv, size_t *len)
{
struct eap_gpsk_data *data = priv;
u8 *key;
if (data->state != SUCCESS)
return NULL;
key = os_malloc(EAP_MSK_LEN);
if (key == NULL)
return NULL;
os_memcpy(key, data->msk, EAP_MSK_LEN);
*len = EAP_MSK_LEN;
return key;
}
static u8 * eap_gpsk_get_emsk(struct eap_sm *sm, void *priv, size_t *len)
{
struct eap_gpsk_data *data = priv;
u8 *key;
if (data->state != SUCCESS)
return NULL;
key = os_malloc(EAP_EMSK_LEN);
if (key == NULL)
return NULL;
os_memcpy(key, data->emsk, EAP_EMSK_LEN);
*len = EAP_EMSK_LEN;
return key;
}
int eap_peer_gpsk_register(void)
{
struct eap_method *eap;
int ret;
eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION,
EAP_VENDOR_IETF, EAP_TYPE_GPSK, "GPSK");
if (eap == NULL)
return -1;
eap->init = eap_gpsk_init;
eap->deinit = eap_gpsk_deinit;
eap->process = eap_gpsk_process;
eap->isKeyAvailable = eap_gpsk_isKeyAvailable;
eap->getKey = eap_gpsk_getKey;
eap->get_emsk = eap_gpsk_get_emsk;
ret = eap_peer_method_register(eap);
if (ret)
eap_peer_method_free(eap);
return ret;
}

View file

@ -0,0 +1,151 @@
/*
* EAP peer method: EAP-GTC (RFC 3748)
* Copyright (c) 2004-2006, Jouni Malinen <j@w1.fi>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* Alternatively, this software may be distributed under the terms of BSD
* license.
*
* See README and COPYING for more details.
*/
#include "includes.h"
#include "common.h"
#include "eap_i.h"
struct eap_gtc_data {
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;
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;
}
return data;
}
static void eap_gtc_deinit(struct eap_sm *sm, void *priv)
{
struct eap_gtc_data *data = priv;
os_free(data);
}
static struct wpabuf * eap_gtc_process(struct eap_sm *sm, void *priv,
struct eap_method_ret *ret,
const struct wpabuf *reqData)
{
struct eap_gtc_data *data = priv;
struct wpabuf *resp;
const u8 *pos, *password, *identity;
size_t password_len, identity_len, len, plen;
int otp;
u8 id;
pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_GTC, reqData, &len);
if (pos == NULL) {
ret->ignore = TRUE;
return NULL;
}
id = eap_get_id(reqData);
wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-GTC: Request message", pos, len);
if (data->prefix &&
(len < 10 || os_memcmp(pos, "CHALLENGE=", 10) != 0)) {
wpa_printf(MSG_DEBUG, "EAP-GTC: Challenge did not start with "
"expected prefix");
/* Send an empty response in order to allow tunneled
* acknowledgement of the failure. This will also cover the
* error case which seems to use EAP-MSCHAPv2 like error
* reporting with EAP-GTC inside EAP-FAST tunnel. */
resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_GTC,
0, EAP_CODE_RESPONSE, id);
return resp;
}
password = eap_get_config_otp(sm, &password_len);
if (password)
otp = 1;
else {
password = eap_get_config_password(sm, &password_len);
otp = 0;
}
if (password == NULL) {
wpa_printf(MSG_INFO, "EAP-GTC: Password not configured");
eap_sm_request_otp(sm, (const char *) pos, len);
ret->ignore = TRUE;
return NULL;
}
ret->ignore = FALSE;
ret->methodState = data->prefix ? METHOD_MAY_CONT : METHOD_DONE;
ret->decision = DECISION_COND_SUCC;
ret->allowNotifications = FALSE;
plen = password_len;
identity = eap_get_config_identity(sm, &identity_len);
if (identity == NULL)
return NULL;
if (data->prefix)
plen += 9 + identity_len + 1;
resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_GTC, plen,
EAP_CODE_RESPONSE, id);
if (resp == NULL)
return NULL;
if (data->prefix) {
wpabuf_put_data(resp, "RESPONSE=", 9);
wpabuf_put_data(resp, identity, identity_len);
wpabuf_put_u8(resp, '\0');
}
wpabuf_put_data(resp, password, password_len);
wpa_hexdump_ascii_key(MSG_MSGDUMP, "EAP-GTC: Response",
wpabuf_head_u8(resp) + sizeof(struct eap_hdr) +
1, plen);
if (otp) {
wpa_printf(MSG_DEBUG, "EAP-GTC: Forgetting used password");
eap_clear_config_otp(sm);
}
return resp;
}
int eap_peer_gtc_register(void)
{
struct eap_method *eap;
int ret;
eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION,
EAP_VENDOR_IETF, EAP_TYPE_GTC, "GTC");
if (eap == NULL)
return -1;
eap->init = eap_gtc_init;
eap->deinit = eap_gtc_deinit;
eap->process = eap_gtc_process;
ret = eap_peer_method_register(eap);
if (ret)
eap_peer_method_free(eap);
return ret;
}

View file

@ -0,0 +1,356 @@
/*
* EAP peer state machines internal structures (RFC 4137)
* Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* Alternatively, this software may be distributed under the terms of BSD
* license.
*
* See README and COPYING for more details.
*/
#ifndef EAP_I_H
#define EAP_I_H
#include "wpabuf.h"
#include "eap_peer/eap.h"
#include "eap_common/eap_common.h"
/* RFC 4137 - EAP Peer state machine */
typedef enum {
DECISION_FAIL, DECISION_COND_SUCC, DECISION_UNCOND_SUCC
} EapDecision;
typedef enum {
METHOD_NONE, METHOD_INIT, METHOD_CONT, METHOD_MAY_CONT, METHOD_DONE
} EapMethodState;
/**
* struct eap_method_ret - EAP return values from struct eap_method::process()
*
* These structure contains OUT variables for the interface between peer state
* machine and methods (RFC 4137, Sect. 4.2). eapRespData will be returned as
* the return value of struct eap_method::process() so it is not included in
* this structure.
*/
struct eap_method_ret {
/**
* ignore - Whether method decided to drop the current packed (OUT)
*/
Boolean ignore;
/**
* methodState - Method-specific state (IN/OUT)
*/
EapMethodState methodState;
/**
* decision - Authentication decision (OUT)
*/
EapDecision decision;
/**
* allowNotifications - Whether method allows notifications (OUT)
*/
Boolean allowNotifications;
};
/**
* 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 4.4 of RFC 4137.
*/
struct eap_method {
/**
* vendor - EAP Vendor-ID (EAP_VENDOR_*) (0 = IETF)
*/
int vendor;
/**
* method - EAP type number (EAP_TYPE_*)
*/
EapType method;
/**
* name - Name of the method (e.g., "TLS")
*/
const char *name;
/**
* init - Initialize an EAP method
* @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
* Returns: Pointer to allocated private data, or %NULL on failure
*
* This function is used to initialize the EAP method explicitly
* instead of using METHOD_INIT state as specific in RFC 4137. The
* method is expected to initialize it method-specific state and return
* a pointer that will be used as the priv argument to other calls.
*/
void * (*init)(struct eap_sm *sm);
/**
* deinit - Deinitialize an EAP method
* @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
* @priv: Pointer to private EAP method data from eap_method::init()
*
* Deinitialize the EAP method and free any allocated private data.
*/
void (*deinit)(struct eap_sm *sm, void *priv);
/**
* process - Process an EAP request
* @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
* @priv: Pointer to private EAP method data from eap_method::init()
* @ret: Return values from EAP request validation and processing
* @reqData: EAP request to be processed (eapReqData)
* Returns: Pointer to allocated EAP response packet (eapRespData)
*
* This function is a combination of m.check(), m.process(), and
* m.buildResp() procedures defined in section 4.4 of RFC 4137 In other
* words, this function validates the incoming request, processes it,
* and build a response packet. m.check() and m.process() return values
* are returned through struct eap_method_ret *ret variable. Caller is
* responsible for freeing the returned EAP response packet.
*/
struct wpabuf * (*process)(struct eap_sm *sm, void *priv,
struct eap_method_ret *ret,
const struct wpabuf *reqData);
/**
* isKeyAvailable - Find out whether EAP method has keying material
* @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
* @priv: Pointer to private EAP method data from eap_method::init()
* Returns: %TRUE if key material (eapKeyData) is available
*/
Boolean (*isKeyAvailable)(struct eap_sm *sm, void *priv);
/**
* getKey - Get EAP method specific keying material (eapKeyData)
* @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
* @priv: Pointer to private EAP method data from eap_method::init()
* @len: Pointer to variable to store key length (eapKeyDataLen)
* Returns: Keying material (eapKeyData) or %NULL if not available
*
* This function can be used to get the 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 * (*getKey)(struct eap_sm *sm, void *priv, size_t *len);
/**
* get_status - Get EAP method status
* @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
* @priv: Pointer to private EAP method data from eap_method::init()
* @buf: Buffer for status information
* @buflen: Maximum buffer length
* @verbose: Whether to include verbose status information
* Returns: Number of bytes written to buf
*
* Query EAP method for status information. This function fills in a
* text area with current status information from the EAP method. If
* the buffer (buf) is not large enough, status information will be
* truncated to fit the buffer.
*/
int (*get_status)(struct eap_sm *sm, void *priv, char *buf,
size_t buflen, int verbose);
/**
* has_reauth_data - Whether method is ready for fast reauthentication
* @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
* @priv: Pointer to private EAP method data from eap_method::init()
* Returns: %TRUE or %FALSE based on whether fast reauthentication is
* possible
*
* This function is an optional handler that only EAP methods
* supporting fast re-authentication need to implement.
*/
Boolean (*has_reauth_data)(struct eap_sm *sm, void *priv);
/**
* deinit_for_reauth - Release data that is not needed for fast re-auth
* @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
* @priv: Pointer to private EAP method data from eap_method::init()
*
* This function is an optional handler that only EAP methods
* supporting fast re-authentication need to implement. This is called
* when authentication has been completed and EAP state machine is
* requesting that enough state information is maintained for fast
* re-authentication
*/
void (*deinit_for_reauth)(struct eap_sm *sm, void *priv);
/**
* init_for_reauth - Prepare for start of fast re-authentication
* @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
* @priv: Pointer to private EAP method data from eap_method::init()
*
* This function is an optional handler that only EAP methods
* supporting fast re-authentication need to implement. This is called
* when EAP authentication is started and EAP state machine is
* requesting fast re-authentication to be used.
*/
void * (*init_for_reauth)(struct eap_sm *sm, void *priv);
/**
* get_identity - Get method specific identity for re-authentication
* @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
* @priv: Pointer to private EAP method data from eap_method::init()
* @len: Length of the returned identity
* Returns: Pointer to the method specific identity or %NULL if default
* identity is to be used
*
* This function is an optional handler that only EAP methods
* that use method specific identity need to implement.
*/
const u8 * (*get_identity)(struct eap_sm *sm, void *priv, size_t *len);
/**
* free - Free EAP method data
* @method: Pointer to the method data registered with
* eap_peer_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_PEER_METHOD_INTERFACE_VERSION 1
/**
* version - Version of the EAP peer method interface
*
* The EAP peer method implementation should set this variable to
* EAP_PEER_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;
#ifdef CONFIG_DYNAMIC_EAP_METHODS
/**
* dl_handle - Handle for the dynamic library
*
* This variable is used internally in the EAP method registration code
* to store a handle for the dynamic library. If the method is linked
* in statically, this is %NULL.
*/
void *dl_handle;
#endif /* CONFIG_DYNAMIC_EAP_METHODS */
/**
* get_emsk - Get EAP method specific keying extended material (EMSK)
* @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
* @priv: Pointer to private EAP method data from eap_method::init()
* @len: Pointer to a variable to store EMSK length
* Returns: EMSK or %NULL if not available
*
* This function can be used to get the extended keying material from
* the EAP method. The key may already be stored in the method-specific
* private data or this function may derive the key.
*/
u8 * (*get_emsk)(struct eap_sm *sm, void *priv, size_t *len);
};
/**
* struct eap_sm - EAP state machine data
*/
struct eap_sm {
enum {
EAP_INITIALIZE, EAP_DISABLED, EAP_IDLE, EAP_RECEIVED,
EAP_GET_METHOD, EAP_METHOD, EAP_SEND_RESPONSE, EAP_DISCARD,
EAP_IDENTITY, EAP_NOTIFICATION, EAP_RETRANSMIT, EAP_SUCCESS,
EAP_FAILURE
} EAP_state;
/* Long-term local variables */
EapType selectedMethod;
EapMethodState methodState;
int lastId;
struct wpabuf *lastRespData;
EapDecision decision;
/* Short-term local variables */
Boolean rxReq;
Boolean rxSuccess;
Boolean rxFailure;
int reqId;
EapType reqMethod;
int reqVendor;
u32 reqVendorMethod;
Boolean ignore;
/* Constants */
int ClientTimeout;
/* Miscellaneous variables */
Boolean allowNotifications; /* peer state machine <-> methods */
struct wpabuf *eapRespData; /* peer to lower layer */
Boolean eapKeyAvailable; /* peer to lower layer */
u8 *eapKeyData; /* peer to lower layer */
size_t eapKeyDataLen; /* peer to lower layer */
const struct eap_method *m; /* selected EAP method */
/* not defined in RFC 4137 */
Boolean changed;
void *eapol_ctx;
struct eapol_callbacks *eapol_cb;
void *eap_method_priv;
int init_phase2;
int fast_reauth;
Boolean rxResp /* LEAP only */;
Boolean leap_done;
Boolean peap_done;
u8 req_md5[16]; /* MD5() of the current EAP packet */
u8 last_md5[16]; /* MD5() of the previously received EAP packet; used
* in duplicate request detection. */
void *msg_ctx;
void *scard_ctx;
void *ssl_ctx;
unsigned int workaround;
/* Optional challenges generated in Phase 1 (EAP-FAST) */
u8 *peer_challenge, *auth_challenge;
int num_rounds;
int force_disabled;
struct wps_context *wps;
int prev_failure;
};
const u8 * eap_get_config_identity(struct eap_sm *sm, size_t *len);
const u8 * eap_get_config_password(struct eap_sm *sm, size_t *len);
const u8 * eap_get_config_password2(struct eap_sm *sm, size_t *len, int *hash);
const u8 * eap_get_config_new_password(struct eap_sm *sm, size_t *len);
const u8 * eap_get_config_otp(struct eap_sm *sm, size_t *len);
void eap_clear_config_otp(struct eap_sm *sm);
const char * eap_get_config_phase1(struct eap_sm *sm);
const char * eap_get_config_phase2(struct eap_sm *sm);
int eap_get_config_fragment_size(struct eap_sm *sm);
struct eap_peer_config * eap_get_config(struct eap_sm *sm);
void eap_set_config_blob(struct eap_sm *sm, struct wpa_config_blob *blob);
const struct wpa_config_blob *
eap_get_config_blob(struct eap_sm *sm, const char *name);
void eap_notify_pending(struct eap_sm *sm);
int eap_allowed_method(struct eap_sm *sm, int vendor, u32 method);
#endif /* EAP_I_H */

View file

@ -0,0 +1,506 @@
/*
* EAP-IKEv2 peer (RFC 5106)
* Copyright (c) 2007, Jouni Malinen <j@w1.fi>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* Alternatively, this software may be distributed under the terms of BSD
* license.
*
* See README and COPYING for more details.
*/
#include "includes.h"
#include "common.h"
#include "eap_i.h"
#include "eap_common/eap_ikev2_common.h"
#include "ikev2.h"
struct eap_ikev2_data {
struct ikev2_responder_data ikev2;
enum { WAIT_START, PROC_MSG, 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 char * eap_ikev2_state_txt(int state)
{
switch (state) {
case WAIT_START:
return "WAIT_START";
case PROC_MSG:
return "PROC_MSG";
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;
const u8 *identity, *password;
size_t identity_len, password_len;
identity = eap_get_config_identity(sm, &identity_len);
if (identity == NULL) {
wpa_printf(MSG_INFO, "EAP-IKEV2: No identity available");
return NULL;
}
data = os_zalloc(sizeof(*data));
if (data == NULL)
return NULL;
data->state = WAIT_START;
data->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;
data->ikev2.IDr = os_malloc(identity_len);
if (data->ikev2.IDr == NULL)
goto failed;
os_memcpy(data->ikev2.IDr, identity, identity_len);
data->ikev2.IDr_len = identity_len;
password = eap_get_config_password(sm, &password_len);
if (password) {
data->ikev2.shared_secret = os_malloc(password_len);
if (data->ikev2.shared_secret == NULL)
goto failed;
os_memcpy(data->ikev2.shared_secret, password, password_len);
data->ikev2.shared_secret_len = password_len;
}
return data;
failed:
ikev2_responder_deinit(&data->ikev2);
os_free(data);
return NULL;
}
static void eap_ikev2_deinit(struct eap_sm *sm, void *priv)
{
struct eap_ikev2_data *data = priv;
wpabuf_free(data->in_buf);
wpabuf_free(data->out_buf);
ikev2_responder_deinit(&data->ikev2);
os_free(data);
}
static int eap_ikev2_peer_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 struct wpabuf * eap_ikev2_build_msg(struct eap_ikev2_data *data,
struct eap_method_ret *ret, u8 id)
{
struct wpabuf *resp;
u8 flags;
size_t send_len, plen, icv_len = 0;
ret->ignore = FALSE;
wpa_printf(MSG_DEBUG, "EAP-IKEV2: Generating Response");
ret->allowNotifications = TRUE;
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;
}
}
#ifdef CCNS_PL
/* Some issues figuring out the length of the message if Message Length
* field not included?! */
if (!(flags & IKEV2_FLAGS_LENGTH_INCLUDED))
flags |= IKEV2_FLAGS_LENGTH_INCLUDED;
#endif /* CCNS_PL */
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;
}
resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_IKEV2, plen,
EAP_CODE_RESPONSE, id);
if (resp == NULL)
return NULL;
wpabuf_put_u8(resp, flags); /* Flags */
if (flags & IKEV2_FLAGS_LENGTH_INCLUDED)
wpabuf_put_be32(resp, wpabuf_len(data->out_buf));
wpabuf_put_data(resp, 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(resp);
size_t len = wpabuf_len(resp);
ikev2_integ_hash(data->ikev2.proposal.integ,
data->ikev2.keys.SK_ar,
data->ikev2.keys.SK_integ_len,
msg, len, wpabuf_put(resp, icv_len));
}
ret->methodState = METHOD_MAY_CONT;
ret->decision = DECISION_FAIL;
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;
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:
ret->methodState = METHOD_DONE;
if (data->state == FAIL)
break;
ret->decision = DECISION_COND_SUCC;
wpa_printf(MSG_DEBUG, "EAP-IKEV2: Authentication "
"completed successfully");
if (eap_ikev2_peer_keymat(data))
break;
eap_ikev2_state(data, DONE);
break;
case IKEV2_FAILED:
wpa_printf(MSG_DEBUG, "EAP-IKEV2: Authentication "
"failed");
ret->methodState = METHOD_DONE;
ret->decision = DECISION_FAIL;
break;
default:
break;
}
} 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 resp;
}
static int eap_ikev2_process_icv(struct eap_ikev2_data *data,
const struct wpabuf *reqData,
u8 flags, const u8 *pos, const u8 **end)
{
if (flags & IKEV2_FLAGS_ICV_INCLUDED) {
int icv_len = eap_ikev2_validate_icv(
data->ikev2.proposal.integ, &data->ikev2.keys, 1,
reqData, pos, *end);
if (icv_len < 0)
return -1;
/* Hide Integrity Checksum Data from further processing */
*end -= icv_len;
} else if (data->keys_ready) {
wpa_printf(MSG_INFO, "EAP-IKEV2: The message should have "
"included integrity checksum");
return -1;
}
return 0;
}
static int eap_ikev2_process_cont(struct eap_ikev2_data *data,
const u8 *buf, size_t len)
{
/* Process continuation of a pending message */
if (len > wpabuf_tailroom(data->in_buf)) {
wpa_printf(MSG_DEBUG, "EAP-IKEV2: Fragment overflow");
eap_ikev2_state(data, FAIL);
return -1;
}
wpabuf_put_data(data->in_buf, buf, len);
wpa_printf(MSG_DEBUG, "EAP-IKEV2: Received %lu bytes, waiting "
"for %lu bytes more", (unsigned long) len,
(unsigned long) wpabuf_tailroom(data->in_buf));
return 0;
}
static struct wpabuf * eap_ikev2_process_fragment(struct eap_ikev2_data *data,
struct eap_method_ret *ret,
u8 id, 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");
ret->ignore = TRUE;
return NULL;
}
if (data->in_buf == NULL) {
/* First fragment of the message */
data->in_buf = wpabuf_alloc(message_length);
if (data->in_buf == NULL) {
wpa_printf(MSG_DEBUG, "EAP-IKEV2: No memory for "
"message");
ret->ignore = TRUE;
return NULL;
}
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 eap_ikev2_build_frag_ack(id, EAP_CODE_RESPONSE);
}
static struct wpabuf * eap_ikev2_process(struct eap_sm *sm, void *priv,
struct eap_method_ret *ret,
const struct wpabuf *reqData)
{
struct eap_ikev2_data *data = priv;
const u8 *start, *pos, *end;
size_t len;
u8 flags, id;
u32 message_length = 0;
struct wpabuf tmpbuf;
pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_IKEV2, reqData, &len);
if (pos == NULL) {
ret->ignore = TRUE;
return NULL;
}
id = eap_get_id(reqData);
start = pos;
end = start + len;
if (len == 0)
flags = 0; /* fragment ack */
else
flags = *pos++;
if (eap_ikev2_process_icv(data, reqData, flags, pos, &end) < 0) {
ret->ignore = TRUE;
return NULL;
}
if (flags & IKEV2_FLAGS_LENGTH_INCLUDED) {
if (end - pos < 4) {
wpa_printf(MSG_DEBUG, "EAP-IKEV2: Message underflow");
ret->ignore = TRUE;
return NULL;
}
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));
ret->ignore = TRUE;
return NULL;
}
}
wpa_printf(MSG_DEBUG, "EAP-IKEV2: Received packet: Flags 0x%x "
"Message Length %u", flags, message_length);
if (data->state == WAIT_FRAG_ACK) {
#ifdef CCNS_PL
if (len > 1) /* Empty Flags field included in ACK */
#else /* CCNS_PL */
if (len != 0)
#endif /* CCNS_PL */
{
wpa_printf(MSG_DEBUG, "EAP-IKEV2: Unexpected payload "
"in WAIT_FRAG_ACK state");
ret->ignore = TRUE;
return NULL;
}
wpa_printf(MSG_DEBUG, "EAP-IKEV2: Fragment acknowledged");
eap_ikev2_state(data, PROC_MSG);
return eap_ikev2_build_msg(data, ret, id);
}
if (data->in_buf && eap_ikev2_process_cont(data, pos, end - pos) < 0) {
ret->ignore = TRUE;
return NULL;
}
if (flags & IKEV2_FLAGS_MORE_FRAGMENTS) {
return eap_ikev2_process_fragment(data, ret, id, flags,
message_length, pos,
end - pos);
}
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_responder_process(&data->ikev2, data->in_buf) < 0) {
if (data->in_buf == &tmpbuf)
data->in_buf = NULL;
eap_ikev2_state(data, FAIL);
return NULL;
}
if (data->in_buf != &tmpbuf)
wpabuf_free(data->in_buf);
data->in_buf = NULL;
if (data->out_buf == NULL) {
data->out_buf = ikev2_responder_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;
}
eap_ikev2_state(data, PROC_MSG);
return eap_ikev2_build_msg(data, ret, id);
}
static Boolean eap_ikev2_isKeyAvailable(struct eap_sm *sm, void *priv)
{
struct eap_ikev2_data *data = priv;
return data->state == DONE && data->keymat_ok;
}
static u8 * eap_ikev2_getKey(struct eap_sm *sm, void *priv, size_t *len)
{
struct eap_ikev2_data *data = priv;
u8 *key;
if (data->state != DONE || !data->keymat_ok)
return NULL;
key = os_malloc(EAP_MSK_LEN);
if (key) {
os_memcpy(key, data->keymat, EAP_MSK_LEN);
*len = EAP_MSK_LEN;
}
return key;
}
static u8 * eap_ikev2_get_emsk(struct eap_sm *sm, void *priv, size_t *len)
{
struct eap_ikev2_data *data = priv;
u8 *key;
if (data->state != DONE || !data->keymat_ok)
return NULL;
key = os_malloc(EAP_EMSK_LEN);
if (key) {
os_memcpy(key, data->keymat + EAP_MSK_LEN, EAP_EMSK_LEN);
*len = EAP_EMSK_LEN;
}
return key;
}
int eap_peer_ikev2_register(void)
{
struct eap_method *eap;
int ret;
eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION,
EAP_VENDOR_IETF, EAP_TYPE_IKEV2,
"IKEV2");
if (eap == NULL)
return -1;
eap->init = eap_ikev2_init;
eap->deinit = eap_ikev2_deinit;
eap->process = eap_ikev2_process;
eap->isKeyAvailable = eap_ikev2_isKeyAvailable;
eap->getKey = eap_ikev2_getKey;
eap->get_emsk = eap_ikev2_get_emsk;
ret = eap_peer_method_register(eap);
if (ret)
eap_peer_method_free(eap);
return ret;
}

View file

@ -0,0 +1,416 @@
/*
* EAP peer method: LEAP
* Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* Alternatively, this software may be distributed under the terms of BSD
* license.
*
* See README and COPYING for more details.
*/
#include "includes.h"
#include "common.h"
#include "crypto/ms_funcs.h"
#include "crypto/crypto.h"
#include "crypto/random.h"
#include "eap_i.h"
#define LEAP_VERSION 1
#define LEAP_CHALLENGE_LEN 8
#define LEAP_RESPONSE_LEN 24
#define LEAP_KEY_LEN 16
struct eap_leap_data {
enum {
LEAP_WAIT_CHALLENGE,
LEAP_WAIT_SUCCESS,
LEAP_WAIT_RESPONSE,
LEAP_DONE
} state;
u8 peer_challenge[LEAP_CHALLENGE_LEN];
u8 peer_response[LEAP_RESPONSE_LEN];
u8 ap_challenge[LEAP_CHALLENGE_LEN];
u8 ap_response[LEAP_RESPONSE_LEN];
};
static void * eap_leap_init(struct eap_sm *sm)
{
struct eap_leap_data *data;
data = os_zalloc(sizeof(*data));
if (data == NULL)
return NULL;
data->state = LEAP_WAIT_CHALLENGE;
sm->leap_done = FALSE;
return data;
}
static void eap_leap_deinit(struct eap_sm *sm, void *priv)
{
os_free(priv);
}
static struct wpabuf * eap_leap_process_request(struct eap_sm *sm, void *priv,
struct eap_method_ret *ret,
const struct wpabuf *reqData)
{
struct eap_leap_data *data = priv;
struct wpabuf *resp;
const u8 *pos, *challenge, *identity, *password;
u8 challenge_len, *rpos;
size_t identity_len, password_len, len;
int pwhash;
wpa_printf(MSG_DEBUG, "EAP-LEAP: Processing EAP-Request");
identity = eap_get_config_identity(sm, &identity_len);
password = eap_get_config_password2(sm, &password_len, &pwhash);
if (identity == NULL || password == NULL)
return NULL;
pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_LEAP, reqData, &len);
if (pos == NULL || len < 3) {
wpa_printf(MSG_INFO, "EAP-LEAP: Invalid EAP-Request frame");
ret->ignore = TRUE;
return NULL;
}
if (*pos != LEAP_VERSION) {
wpa_printf(MSG_WARNING, "EAP-LEAP: Unsupported LEAP version "
"%d", *pos);
ret->ignore = TRUE;
return NULL;
}
pos++;
pos++; /* skip unused byte */
challenge_len = *pos++;
if (challenge_len != LEAP_CHALLENGE_LEN || challenge_len > len - 3) {
wpa_printf(MSG_INFO, "EAP-LEAP: Invalid challenge "
"(challenge_len=%d reqDataLen=%lu)",
challenge_len, (unsigned long) wpabuf_len(reqData));
ret->ignore = TRUE;
return NULL;
}
challenge = pos;
os_memcpy(data->peer_challenge, challenge, LEAP_CHALLENGE_LEN);
wpa_hexdump(MSG_MSGDUMP, "EAP-LEAP: Challenge from AP",
challenge, LEAP_CHALLENGE_LEN);
wpa_printf(MSG_DEBUG, "EAP-LEAP: Generating Challenge Response");
resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_LEAP,
3 + LEAP_RESPONSE_LEN + identity_len,
EAP_CODE_RESPONSE, eap_get_id(reqData));
if (resp == NULL)
return NULL;
wpabuf_put_u8(resp, LEAP_VERSION);
wpabuf_put_u8(resp, 0); /* unused */
wpabuf_put_u8(resp, LEAP_RESPONSE_LEN);
rpos = wpabuf_put(resp, LEAP_RESPONSE_LEN);
if (pwhash)
challenge_response(challenge, password, rpos);
else
nt_challenge_response(challenge, password, password_len, rpos);
os_memcpy(data->peer_response, rpos, LEAP_RESPONSE_LEN);
wpa_hexdump(MSG_MSGDUMP, "EAP-LEAP: Response",
rpos, LEAP_RESPONSE_LEN);
wpabuf_put_data(resp, identity, identity_len);
data->state = LEAP_WAIT_SUCCESS;
return resp;
}
static struct wpabuf * eap_leap_process_success(struct eap_sm *sm, void *priv,
struct eap_method_ret *ret,
const struct wpabuf *reqData)
{
struct eap_leap_data *data = priv;
struct wpabuf *resp;
u8 *pos;
const u8 *identity;
size_t identity_len;
wpa_printf(MSG_DEBUG, "EAP-LEAP: Processing EAP-Success");
identity = eap_get_config_identity(sm, &identity_len);
if (identity == NULL)
return NULL;
if (data->state != LEAP_WAIT_SUCCESS) {
wpa_printf(MSG_INFO, "EAP-LEAP: EAP-Success received in "
"unexpected state (%d) - ignored", data->state);
ret->ignore = TRUE;
return NULL;
}
resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_LEAP,
3 + LEAP_CHALLENGE_LEN + identity_len,
EAP_CODE_REQUEST, eap_get_id(reqData));
if (resp == NULL)
return NULL;
wpabuf_put_u8(resp, LEAP_VERSION);
wpabuf_put_u8(resp, 0); /* unused */
wpabuf_put_u8(resp, LEAP_CHALLENGE_LEN);
pos = wpabuf_put(resp, LEAP_CHALLENGE_LEN);
if (random_get_bytes(pos, LEAP_CHALLENGE_LEN)) {
wpa_printf(MSG_WARNING, "EAP-LEAP: Failed to read random data "
"for challenge");
wpabuf_free(resp);
ret->ignore = TRUE;
return NULL;
}
os_memcpy(data->ap_challenge, pos, LEAP_CHALLENGE_LEN);
wpa_hexdump(MSG_MSGDUMP, "EAP-LEAP: Challenge to AP/AS", pos,
LEAP_CHALLENGE_LEN);
wpabuf_put_data(resp, identity, identity_len);
data->state = LEAP_WAIT_RESPONSE;
return resp;
}
static struct wpabuf * eap_leap_process_response(struct eap_sm *sm, void *priv,
struct eap_method_ret *ret,
const struct wpabuf *reqData)
{
struct eap_leap_data *data = priv;
const u8 *pos, *password;
u8 response_len, pw_hash[16], pw_hash_hash[16],
expected[LEAP_RESPONSE_LEN];
size_t password_len, len;
int pwhash;
wpa_printf(MSG_DEBUG, "EAP-LEAP: Processing EAP-Response");
password = eap_get_config_password2(sm, &password_len, &pwhash);
if (password == NULL)
return NULL;
pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_LEAP, reqData, &len);
if (pos == NULL || len < 3) {
wpa_printf(MSG_INFO, "EAP-LEAP: Invalid EAP-Response frame");
ret->ignore = TRUE;
return NULL;
}
if (*pos != LEAP_VERSION) {
wpa_printf(MSG_WARNING, "EAP-LEAP: Unsupported LEAP version "
"%d", *pos);
ret->ignore = TRUE;
return NULL;
}
pos++;
pos++; /* skip unused byte */
response_len = *pos++;
if (response_len != LEAP_RESPONSE_LEN || response_len > len - 3) {
wpa_printf(MSG_INFO, "EAP-LEAP: Invalid response "
"(response_len=%d reqDataLen=%lu)",
response_len, (unsigned long) wpabuf_len(reqData));
ret->ignore = TRUE;
return NULL;
}
wpa_hexdump(MSG_DEBUG, "EAP-LEAP: Response from AP",
pos, LEAP_RESPONSE_LEN);
os_memcpy(data->ap_response, pos, LEAP_RESPONSE_LEN);
if (pwhash) {
if (hash_nt_password_hash(password, pw_hash_hash)) {
ret->ignore = TRUE;
return NULL;
}
} else {
if (nt_password_hash(password, password_len, pw_hash) ||
hash_nt_password_hash(pw_hash, pw_hash_hash)) {
ret->ignore = TRUE;
return NULL;
}
}
challenge_response(data->ap_challenge, pw_hash_hash, expected);
ret->methodState = METHOD_DONE;
ret->allowNotifications = FALSE;
if (os_memcmp(pos, expected, LEAP_RESPONSE_LEN) != 0) {
wpa_printf(MSG_WARNING, "EAP-LEAP: AP sent an invalid "
"response - authentication failed");
wpa_hexdump(MSG_DEBUG, "EAP-LEAP: Expected response from AP",
expected, LEAP_RESPONSE_LEN);
ret->decision = DECISION_FAIL;
return NULL;
}
ret->decision = DECISION_UNCOND_SUCC;
/* LEAP is somewhat odd method since it sends EAP-Success in the middle
* of the authentication. Use special variable to transit EAP state
* machine to SUCCESS state. */
sm->leap_done = TRUE;
data->state = LEAP_DONE;
/* No more authentication messages expected; AP will send EAPOL-Key
* frames if encryption is enabled. */
return NULL;
}
static struct wpabuf * eap_leap_process(struct eap_sm *sm, void *priv,
struct eap_method_ret *ret,
const struct wpabuf *reqData)
{
const struct eap_hdr *eap;
size_t password_len;
const u8 *password;
password = eap_get_config_password(sm, &password_len);
if (password == NULL) {
wpa_printf(MSG_INFO, "EAP-LEAP: Password not configured");
eap_sm_request_password(sm);
ret->ignore = TRUE;
return NULL;
}
/*
* LEAP needs to be able to handle EAP-Success frame which does not
* include Type field. Consequently, eap_hdr_validate() cannot be used
* here. This validation will be done separately for EAP-Request and
* EAP-Response frames.
*/
eap = wpabuf_head(reqData);
if (wpabuf_len(reqData) < sizeof(*eap) ||
be_to_host16(eap->length) > wpabuf_len(reqData)) {
wpa_printf(MSG_INFO, "EAP-LEAP: Invalid frame");
ret->ignore = TRUE;
return NULL;
}
ret->ignore = FALSE;
ret->allowNotifications = TRUE;
ret->methodState = METHOD_MAY_CONT;
ret->decision = DECISION_FAIL;
sm->leap_done = FALSE;
switch (eap->code) {
case EAP_CODE_REQUEST:
return eap_leap_process_request(sm, priv, ret, reqData);
case EAP_CODE_SUCCESS:
return eap_leap_process_success(sm, priv, ret, reqData);
case EAP_CODE_RESPONSE:
return eap_leap_process_response(sm, priv, ret, reqData);
default:
wpa_printf(MSG_INFO, "EAP-LEAP: Unexpected EAP code (%d) - "
"ignored", eap->code);
ret->ignore = TRUE;
return NULL;
}
}
static Boolean eap_leap_isKeyAvailable(struct eap_sm *sm, void *priv)
{
struct eap_leap_data *data = priv;
return data->state == LEAP_DONE;
}
static u8 * eap_leap_getKey(struct eap_sm *sm, void *priv, size_t *len)
{
struct eap_leap_data *data = priv;
u8 *key, pw_hash_hash[16], pw_hash[16];
const u8 *addr[5], *password;
size_t elen[5], password_len;
int pwhash;
if (data->state != LEAP_DONE)
return NULL;
password = eap_get_config_password2(sm, &password_len, &pwhash);
if (password == NULL)
return NULL;
key = os_malloc(LEAP_KEY_LEN);
if (key == NULL)
return NULL;
if (pwhash) {
if (hash_nt_password_hash(password, pw_hash_hash)) {
os_free(key);
return NULL;
}
} else {
if (nt_password_hash(password, password_len, pw_hash) ||
hash_nt_password_hash(pw_hash, pw_hash_hash)) {
os_free(key);
return NULL;
}
}
wpa_hexdump_key(MSG_DEBUG, "EAP-LEAP: pw_hash_hash",
pw_hash_hash, 16);
wpa_hexdump(MSG_DEBUG, "EAP-LEAP: peer_challenge",
data->peer_challenge, LEAP_CHALLENGE_LEN);
wpa_hexdump(MSG_DEBUG, "EAP-LEAP: peer_response",
data->peer_response, LEAP_RESPONSE_LEN);
wpa_hexdump(MSG_DEBUG, "EAP-LEAP: ap_challenge",
data->ap_challenge, LEAP_CHALLENGE_LEN);
wpa_hexdump(MSG_DEBUG, "EAP-LEAP: ap_response",
data->ap_response, LEAP_RESPONSE_LEN);
addr[0] = pw_hash_hash;
elen[0] = 16;
addr[1] = data->ap_challenge;
elen[1] = LEAP_CHALLENGE_LEN;
addr[2] = data->ap_response;
elen[2] = LEAP_RESPONSE_LEN;
addr[3] = data->peer_challenge;
elen[3] = LEAP_CHALLENGE_LEN;
addr[4] = data->peer_response;
elen[4] = LEAP_RESPONSE_LEN;
md5_vector(5, addr, elen, key);
wpa_hexdump_key(MSG_DEBUG, "EAP-LEAP: master key", key, LEAP_KEY_LEN);
*len = LEAP_KEY_LEN;
return key;
}
int eap_peer_leap_register(void)
{
struct eap_method *eap;
int ret;
eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION,
EAP_VENDOR_IETF, EAP_TYPE_LEAP, "LEAP");
if (eap == NULL)
return -1;
eap->init = eap_leap_init;
eap->deinit = eap_leap_deinit;
eap->process = eap_leap_process;
eap->isKeyAvailable = eap_leap_isKeyAvailable;
eap->getKey = eap_leap_getKey;
ret = eap_peer_method_register(eap);
if (ret)
eap_peer_method_free(eap);
return ret;
}

View file

@ -0,0 +1,120 @@
/*
* EAP peer method: EAP-MD5 (RFC 3748 and RFC 1994)
* Copyright (c) 2004-2006, Jouni Malinen <j@w1.fi>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* Alternatively, this software may be distributed under the terms of BSD
* license.
*
* See README and COPYING for more details.
*/
#include "includes.h"
#include "common.h"
#include "eap_i.h"
#include "eap_common/chap.h"
static void * eap_md5_init(struct eap_sm *sm)
{
/* No need for private data. However, must return non-NULL to indicate
* success. */
return (void *) 1;
}
static void eap_md5_deinit(struct eap_sm *sm, void *priv)
{
}
static struct wpabuf * eap_md5_process(struct eap_sm *sm, void *priv,
struct eap_method_ret *ret,
const struct wpabuf *reqData)
{
struct wpabuf *resp;
const u8 *pos, *challenge, *password;
u8 *rpos, id;
size_t len, challenge_len, password_len;
password = eap_get_config_password(sm, &password_len);
if (password == NULL) {
wpa_printf(MSG_INFO, "EAP-MD5: Password not configured");
eap_sm_request_password(sm);
ret->ignore = TRUE;
return NULL;
}
pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_MD5, reqData, &len);
if (pos == NULL || len == 0) {
wpa_printf(MSG_INFO, "EAP-MD5: Invalid frame (pos=%p len=%lu)",
pos, (unsigned long) len);
ret->ignore = TRUE;
return NULL;
}
/*
* CHAP Challenge:
* Value-Size (1 octet) | Value(Challenge) | Name(optional)
*/
challenge_len = *pos++;
if (challenge_len == 0 || challenge_len > len - 1) {
wpa_printf(MSG_INFO, "EAP-MD5: Invalid challenge "
"(challenge_len=%lu len=%lu)",
(unsigned long) challenge_len, (unsigned long) len);
ret->ignore = TRUE;
return NULL;
}
ret->ignore = FALSE;
challenge = pos;
wpa_hexdump(MSG_MSGDUMP, "EAP-MD5: Challenge",
challenge, challenge_len);
wpa_printf(MSG_DEBUG, "EAP-MD5: Generating Challenge Response");
ret->methodState = METHOD_DONE;
ret->decision = DECISION_COND_SUCC;
ret->allowNotifications = TRUE;
resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_MD5, 1 + CHAP_MD5_LEN,
EAP_CODE_RESPONSE, eap_get_id(reqData));
if (resp == NULL)
return NULL;
/*
* CHAP Response:
* Value-Size (1 octet) | Value(Response) | Name(optional)
*/
wpabuf_put_u8(resp, CHAP_MD5_LEN);
id = eap_get_id(resp);
rpos = wpabuf_put(resp, CHAP_MD5_LEN);
chap_md5(id, password, password_len, challenge, challenge_len, rpos);
wpa_hexdump(MSG_MSGDUMP, "EAP-MD5: Response", rpos, CHAP_MD5_LEN);
return resp;
}
int eap_peer_md5_register(void)
{
struct eap_method *eap;
int ret;
eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION,
EAP_VENDOR_IETF, EAP_TYPE_MD5, "MD5");
if (eap == NULL)
return -1;
eap->init = eap_md5_init;
eap->deinit = eap_md5_deinit;
eap->process = eap_md5_process;
ret = eap_peer_method_register(eap);
if (ret)
eap_peer_method_free(eap);
return ret;
}

View file

@ -0,0 +1,373 @@
/*
* EAP peer: Method registration
* Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* Alternatively, this software may be distributed under the terms of BSD
* license.
*
* See README and COPYING for more details.
*/
#include "includes.h"
#ifdef CONFIG_DYNAMIC_EAP_METHODS
#include <dlfcn.h>
#endif /* CONFIG_DYNAMIC_EAP_METHODS */
#include "common.h"
#include "eap_i.h"
#include "eap_methods.h"
static struct eap_method *eap_methods = NULL;
/**
* eap_peer_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_peer_get_eap_method(int vendor, EapType method)
{
struct eap_method *m;
for (m = eap_methods; m; m = m->next) {
if (m->vendor == vendor && m->method == method)
return m;
}
return NULL;
}
/**
* eap_peer_get_type - Get EAP type for the given EAP method name
* @name: EAP method name, e.g., TLS
* @vendor: Buffer for returning EAP Vendor-Id
* Returns: EAP method type or %EAP_TYPE_NONE if not found
*
* This function maps EAP type names into EAP type numbers based on the list of
* EAP methods included in the build.
*/
EapType eap_peer_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_get_name - Get EAP method name for the given EAP type
* @vendor: EAP Vendor-Id (0 = IETF)
* @type: EAP method type
* Returns: EAP method name, e.g., TLS, or %NULL if not found
*
* This function maps EAP type numbers into EAP type names based on the list of
* EAP methods included in the build.
*/
const char * eap_get_name(int vendor, EapType type)
{
struct eap_method *m;
for (m = eap_methods; m; m = m->next) {
if (m->vendor == vendor && m->method == type)
return m->name;
}
return NULL;
}
/**
* eap_get_names - Get space separated list of names for supported EAP methods
* @buf: Buffer for names
* @buflen: Buffer length
* Returns: Number of characters written into buf (not including nul
* termination)
*/
size_t eap_get_names(char *buf, size_t buflen)
{
char *pos, *end;
struct eap_method *m;
int ret;
if (buflen == 0)
return 0;
pos = buf;
end = pos + buflen;
for (m = eap_methods; m; m = m->next) {
ret = os_snprintf(pos, end - pos, "%s%s",
m == eap_methods ? "" : " ", m->name);
if (ret < 0 || ret >= end - pos)
break;
pos += ret;
}
buf[buflen - 1] = '\0';
return pos - buf;
}
/**
* eap_get_names_as_string_array - Get supported EAP methods as string array
* @num: Buffer for returning the number of items in array, not including %NULL
* terminator. This parameter can be %NULL if the length is not needed.
* Returns: A %NULL-terminated array of strings, or %NULL on error.
*
* This function returns the list of names for all supported EAP methods as an
* array of strings. The caller must free the returned array items and the
* array.
*/
char ** eap_get_names_as_string_array(size_t *num)
{
struct eap_method *m;
size_t array_len = 0;
char **array;
int i = 0, j;
for (m = eap_methods; m; m = m->next)
array_len++;
array = os_zalloc(sizeof(char *) * (array_len + 1));
if (array == NULL)
return NULL;
for (m = eap_methods; m; m = m->next) {
array[i++] = os_strdup(m->name);
if (array[i - 1] == NULL) {
for (j = 0; j < i; j++)
os_free(array[j]);
os_free(array);
return NULL;
}
}
array[i] = NULL;
if (num)
*num = array_len;
return array;
}
/**
* eap_peer_get_methods - Get a list of enabled EAP peer methods
* @count: Set to number of available methods
* Returns: List of enabled EAP peer methods
*/
const struct eap_method * eap_peer_get_methods(size_t *count)
{
int c = 0;
struct eap_method *m;
for (m = eap_methods; m; m = m->next)
c++;
*count = c;
return eap_methods;
}
#ifdef CONFIG_DYNAMIC_EAP_METHODS
/**
* eap_peer_method_load - Load a dynamic EAP method library (shared object)
* @so: File path for the shared object file to load
* Returns: 0 on success, -1 on failure
*/
int eap_peer_method_load(const char *so)
{
void *handle;
int (*dyn_init)(void);
int ret;
handle = dlopen(so, RTLD_LAZY);
if (handle == NULL) {
wpa_printf(MSG_ERROR, "EAP: Failed to open dynamic EAP method "
"'%s': %s", so, dlerror());
return -1;
}
dyn_init = dlsym(handle, "eap_peer_method_dynamic_init");
if (dyn_init == NULL) {
dlclose(handle);
wpa_printf(MSG_ERROR, "EAP: Invalid EAP method '%s' - no "
"eap_peer_method_dynamic_init()", so);
return -1;
}
ret = dyn_init();
if (ret) {
dlclose(handle);
wpa_printf(MSG_ERROR, "EAP: Failed to add EAP method '%s' - "
"ret %d", so, ret);
return ret;
}
/* Store the handle for this shared object. It will be freed with
* dlclose() when the EAP method is unregistered. */
eap_methods->dl_handle = handle;
wpa_printf(MSG_DEBUG, "EAP: Loaded dynamic EAP method: '%s'", so);
return 0;
}
/**
* eap_peer_method_unload - Unload a dynamic EAP method library (shared object)
* @method: Pointer to the dynamically loaded EAP method
* Returns: 0 on success, -1 on failure
*
* This function can be used to unload EAP methods that have been previously
* loaded with eap_peer_method_load(). Before unloading the method, all
* references to the method must be removed to make sure that no dereferences
* of freed memory will occur after unloading.
*/
int eap_peer_method_unload(struct eap_method *method)
{
struct eap_method *m, *prev;
void *handle;
m = eap_methods;
prev = NULL;
while (m) {
if (m == method)
break;
prev = m;
m = m->next;
}
if (m == NULL || m->dl_handle == NULL)
return -1;
if (prev)
prev->next = m->next;
else
eap_methods = m->next;
handle = m->dl_handle;
if (m->free)
m->free(m);
else
eap_peer_method_free(m);
dlclose(handle);
return 0;
}
#endif /* CONFIG_DYNAMIC_EAP_METHODS */
/**
* eap_peer_method_alloc - Allocate EAP peer method structure
* @version: Version of the EAP peer method interface (set to
* EAP_PEER_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_peer_method_free() when it
* is not needed anymore.
*/
struct eap_method * eap_peer_method_alloc(int version, int vendor,
EapType method, const char *name)
{
struct eap_method *eap;
eap = os_zalloc(sizeof(*eap));
if (eap == NULL)
return NULL;
eap->version = version;
eap->vendor = vendor;
eap->method = method;
eap->name = name;
return eap;
}
/**
* eap_peer_method_free - Free EAP peer method structure
* @method: Method structure allocated with eap_peer_method_alloc()
*/
void eap_peer_method_free(struct eap_method *method)
{
os_free(method);
}
/**
* eap_peer_method_register - Register an EAP peer method
* @method: EAP method to register
* Returns: 0 on success, -1 on invalid method, or -2 if a matching EAP method
* has already been registered
*
* Each EAP peer method needs to call this function to register itself as a
* supported EAP method.
*/
int eap_peer_method_register(struct eap_method *method)
{
struct eap_method *m, *last = NULL;
if (method == NULL || method->name == NULL ||
method->version != EAP_PEER_METHOD_INTERFACE_VERSION)
return -1;
for (m = eap_methods; m; m = m->next) {
if ((m->vendor == method->vendor &&
m->method == method->method) ||
os_strcmp(m->name, method->name) == 0)
return -2;
last = m;
}
if (last)
last->next = method;
else
eap_methods = method;
return 0;
}
/**
* eap_peer_unregister_methods - Unregister EAP peer methods
*
* This function is called at program termination to unregister all EAP peer
* methods.
*/
void eap_peer_unregister_methods(void)
{
struct eap_method *m;
#ifdef CONFIG_DYNAMIC_EAP_METHODS
void *handle;
#endif /* CONFIG_DYNAMIC_EAP_METHODS */
while (eap_methods) {
m = eap_methods;
eap_methods = eap_methods->next;
#ifdef CONFIG_DYNAMIC_EAP_METHODS
handle = m->dl_handle;
#endif /* CONFIG_DYNAMIC_EAP_METHODS */
if (m->free)
m->free(m);
else
eap_peer_method_free(m);
#ifdef CONFIG_DYNAMIC_EAP_METHODS
if (handle)
dlclose(handle);
#endif /* CONFIG_DYNAMIC_EAP_METHODS */
}
}

View file

@ -0,0 +1,114 @@
/*
* EAP peer: Method registration
* Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* Alternatively, this software may be distributed under the terms of BSD
* license.
*
* See README and COPYING for more details.
*/
#ifndef EAP_METHODS_H
#define EAP_METHODS_H
#include "eap_common/eap_defs.h"
const struct eap_method * eap_peer_get_eap_method(int vendor, EapType method);
const struct eap_method * eap_peer_get_methods(size_t *count);
struct eap_method * eap_peer_method_alloc(int version, int vendor,
EapType method, const char *name);
void eap_peer_method_free(struct eap_method *method);
int eap_peer_method_register(struct eap_method *method);
#ifdef IEEE8021X_EAPOL
EapType eap_peer_get_type(const char *name, int *vendor);
const char * eap_get_name(int vendor, EapType type);
size_t eap_get_names(char *buf, size_t buflen);
char ** eap_get_names_as_string_array(size_t *num);
void eap_peer_unregister_methods(void);
#else /* IEEE8021X_EAPOL */
static inline EapType eap_peer_get_type(const char *name, int *vendor)
{
*vendor = EAP_VENDOR_IETF;
return EAP_TYPE_NONE;
}
static inline const char * eap_get_name(int vendor, EapType type)
{
return NULL;
}
static inline size_t eap_get_names(char *buf, size_t buflen)
{
return 0;
}
static inline int eap_peer_register_methods(void)
{
return 0;
}
static inline void eap_peer_unregister_methods(void)
{
}
static inline char ** eap_get_names_as_string_array(size_t *num)
{
return NULL;
}
#endif /* IEEE8021X_EAPOL */
#ifdef CONFIG_DYNAMIC_EAP_METHODS
int eap_peer_method_load(const char *so);
int eap_peer_method_unload(struct eap_method *method);
#else /* CONFIG_DYNAMIC_EAP_METHODS */
static inline int eap_peer_method_load(const char *so)
{
return 0;
}
static inline int eap_peer_method_unload(struct eap_method *method)
{
return 0;
}
#endif /* CONFIG_DYNAMIC_EAP_METHODS */
/* EAP peer method registration calls for statically linked in methods */
int eap_peer_md5_register(void);
int eap_peer_tls_register(void);
int eap_peer_mschapv2_register(void);
int eap_peer_peap_register(void);
int eap_peer_ttls_register(void);
int eap_peer_gtc_register(void);
int eap_peer_otp_register(void);
int eap_peer_sim_register(void);
int eap_peer_leap_register(void);
int eap_peer_psk_register(void);
int eap_peer_aka_register(void);
int eap_peer_aka_prime_register(void);
int eap_peer_fast_register(void);
int eap_peer_pax_register(void);
int eap_peer_sake_register(void);
int eap_peer_gpsk_register(void);
int eap_peer_wsc_register(void);
int eap_peer_ikev2_register(void);
int eap_peer_vendor_test_register(void);
int eap_peer_tnc_register(void);
int eap_peer_pwd_register(void);
#endif /* EAP_METHODS_H */

View file

@ -0,0 +1,883 @@
/*
* EAP peer method: EAP-MSCHAPV2 (draft-kamath-pppext-eap-mschapv2-00.txt)
* Copyright (c) 2004-2008, Jouni Malinen <j@w1.fi>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* Alternatively, this software may be distributed under the terms of BSD
* license.
*
* See README and COPYING for more details.
*
* This file implements EAP peer part of EAP-MSCHAPV2 method (EAP type 26).
* draft-kamath-pppext-eap-mschapv2-00.txt defines the Microsoft EAP CHAP
* Extensions Protocol, Version 2, for mutual authentication and key
* derivation. This encapsulates MS-CHAP-v2 protocol which is defined in
* RFC 2759. Use of EAP-MSCHAPV2 derived keys with MPPE cipher is described in
* RFC 3079.
*/
#include "includes.h"
#include "common.h"
#include "crypto/ms_funcs.h"
#include "crypto/random.h"
#include "common/wpa_ctrl.h"
#include "mschapv2.h"
#include "eap_i.h"
#include "eap_config.h"
#ifdef _MSC_VER
#pragma pack(push, 1)
#endif /* _MSC_VER */
struct eap_mschapv2_hdr {
u8 op_code; /* MSCHAPV2_OP_* */
u8 mschapv2_id; /* usually same as EAP identifier; must be changed
* for challenges, but not for success/failure */
u8 ms_length[2]; /* Note: misaligned; length - 5 */
/* followed by data */
} STRUCT_PACKED;
/* Response Data field */
struct ms_response {
u8 peer_challenge[MSCHAPV2_CHAL_LEN];
u8 reserved[8];
u8 nt_response[MSCHAPV2_NT_RESPONSE_LEN];
u8 flags;
} STRUCT_PACKED;
/* Change-Password Data field */
struct ms_change_password {
u8 encr_password[516];
u8 encr_hash[16];
u8 peer_challenge[MSCHAPV2_CHAL_LEN];
u8 reserved[8];
u8 nt_response[MSCHAPV2_NT_RESPONSE_LEN];
u8 flags[2];
} STRUCT_PACKED;
#ifdef _MSC_VER
#pragma pack(pop)
#endif /* _MSC_VER */
#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 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
struct eap_mschapv2_data {
u8 auth_response[MSCHAPV2_AUTH_RESPONSE_LEN];
int auth_response_valid;
int prev_error;
u8 passwd_change_challenge[PASSWD_CHANGE_CHAL_LEN];
int passwd_change_challenge_valid;
int passwd_change_version;
/* Optional challenge values generated in EAP-FAST Phase 1 negotiation
*/
u8 *peer_challenge;
u8 *auth_challenge;
int phase2;
u8 master_key[MSCHAPV2_MASTER_KEY_LEN];
int master_key_valid;
int success;
struct wpabuf *prev_challenge;
};
static void eap_mschapv2_deinit(struct eap_sm *sm, void *priv);
static void * eap_mschapv2_init(struct eap_sm *sm)
{
struct eap_mschapv2_data *data;
data = os_zalloc(sizeof(*data));
if (data == NULL)
return NULL;
if (sm->peer_challenge) {
data->peer_challenge = os_malloc(MSCHAPV2_CHAL_LEN);
if (data->peer_challenge == NULL) {
eap_mschapv2_deinit(sm, data);
return NULL;
}
os_memcpy(data->peer_challenge, sm->peer_challenge,
MSCHAPV2_CHAL_LEN);
}
if (sm->auth_challenge) {
data->auth_challenge = os_malloc(MSCHAPV2_CHAL_LEN);
if (data->auth_challenge == NULL) {
eap_mschapv2_deinit(sm, data);
return NULL;
}
os_memcpy(data->auth_challenge, sm->auth_challenge,
MSCHAPV2_CHAL_LEN);
}
data->phase2 = sm->init_phase2;
return data;
}
static void eap_mschapv2_deinit(struct eap_sm *sm, void *priv)
{
struct eap_mschapv2_data *data = priv;
os_free(data->peer_challenge);
os_free(data->auth_challenge);
wpabuf_free(data->prev_challenge);
os_free(data);
}
static struct wpabuf * eap_mschapv2_challenge_reply(
struct eap_sm *sm, struct eap_mschapv2_data *data, u8 id,
u8 mschapv2_id, const u8 *auth_challenge)
{
struct wpabuf *resp;
struct eap_mschapv2_hdr *ms;
u8 *peer_challenge;
int ms_len;
struct ms_response *r;
size_t identity_len, password_len;
const u8 *identity, *password;
int pwhash;
wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Generating Challenge Response");
identity = eap_get_config_identity(sm, &identity_len);
password = eap_get_config_password2(sm, &password_len, &pwhash);
if (identity == NULL || password == NULL)
return NULL;
ms_len = sizeof(*ms) + 1 + sizeof(*r) + identity_len;
resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, ms_len,
EAP_CODE_RESPONSE, id);
if (resp == NULL)
return NULL;
ms = wpabuf_put(resp, sizeof(*ms));
ms->op_code = MSCHAPV2_OP_RESPONSE;
ms->mschapv2_id = mschapv2_id;
if (data->prev_error) {
/*
* TODO: this does not seem to be enough when processing two
* or more failure messages. IAS did not increment mschapv2_id
* in its own packets, but it seemed to expect the peer to
* increment this for all packets(?).
*/
ms->mschapv2_id++;
}
WPA_PUT_BE16(ms->ms_length, ms_len);
wpabuf_put_u8(resp, sizeof(*r)); /* Value-Size */
/* Response */
r = wpabuf_put(resp, sizeof(*r));
peer_challenge = r->peer_challenge;
if (data->peer_challenge) {
wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: peer_challenge generated "
"in Phase 1");
peer_challenge = data->peer_challenge;
os_memset(r->peer_challenge, 0, MSCHAPV2_CHAL_LEN);
} else if (random_get_bytes(peer_challenge, MSCHAPV2_CHAL_LEN)) {
wpabuf_free(resp);
return NULL;
}
os_memset(r->reserved, 0, 8);
if (data->auth_challenge) {
wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: auth_challenge generated "
"in Phase 1");
auth_challenge = data->auth_challenge;
}
if (mschapv2_derive_response(identity, identity_len, password,
password_len, pwhash, auth_challenge,
peer_challenge, r->nt_response,
data->auth_response, data->master_key)) {
wpa_printf(MSG_ERROR, "EAP-MSCHAPV2: Failed to derive "
"response");
wpabuf_free(resp);
return NULL;
}
data->auth_response_valid = 1;
data->master_key_valid = 1;
r->flags = 0; /* reserved, must be zero */
wpabuf_put_data(resp, identity, identity_len);
wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: TX identifier %d mschapv2_id %d "
"(response)", id, ms->mschapv2_id);
return resp;
}
/**
* eap_mschapv2_process - Process an EAP-MSCHAPv2 challenge message
* @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
* @data: Pointer to private EAP method data from eap_mschapv2_init()
* @ret: Return values from EAP request validation and processing
* @req: Pointer to EAP-MSCHAPv2 header from the request
* @req_len: Length of the EAP-MSCHAPv2 data
* @id: EAP identifier used in the request
* Returns: Pointer to allocated EAP response packet (eapRespData) or %NULL if
* no reply available
*/
static struct wpabuf * eap_mschapv2_challenge(
struct eap_sm *sm, struct eap_mschapv2_data *data,
struct eap_method_ret *ret, const struct eap_mschapv2_hdr *req,
size_t req_len, u8 id)
{
size_t len, challenge_len;
const u8 *pos, *challenge;
if (eap_get_config_identity(sm, &len) == NULL ||
eap_get_config_password(sm, &len) == NULL)
return NULL;
wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Received challenge");
if (req_len < sizeof(*req) + 1) {
wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Too short challenge data "
"(len %lu)", (unsigned long) req_len);
ret->ignore = TRUE;
return NULL;
}
pos = (const u8 *) (req + 1);
challenge_len = *pos++;
len = req_len - sizeof(*req) - 1;
if (challenge_len != MSCHAPV2_CHAL_LEN) {
wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Invalid challenge length "
"%lu", (unsigned long) challenge_len);
ret->ignore = TRUE;
return NULL;
}
if (len < challenge_len) {
wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Too short challenge"
" packet: len=%lu challenge_len=%lu",
(unsigned long) len, (unsigned long) challenge_len);
ret->ignore = TRUE;
return NULL;
}
if (data->passwd_change_challenge_valid) {
wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Using challenge from the "
"failure message");
challenge = data->passwd_change_challenge;
} else
challenge = pos;
pos += challenge_len;
len -= challenge_len;
wpa_hexdump_ascii(MSG_DEBUG, "EAP-MSCHAPV2: Authentication Servername",
pos, len);
ret->ignore = FALSE;
ret->methodState = METHOD_MAY_CONT;
ret->decision = DECISION_FAIL;
ret->allowNotifications = TRUE;
return eap_mschapv2_challenge_reply(sm, data, id, req->mschapv2_id,
challenge);
}
static void eap_mschapv2_password_changed(struct eap_sm *sm,
struct eap_mschapv2_data *data)
{
struct eap_peer_config *config = eap_get_config(sm);
if (config && config->new_password) {
wpa_msg(sm->msg_ctx, MSG_INFO,
WPA_EVENT_PASSWORD_CHANGED
"EAP-MSCHAPV2: Password changed successfully");
data->prev_error = 0;
os_free(config->password);
if (config->flags & EAP_CONFIG_FLAGS_PASSWORD_NTHASH) {
config->password = os_malloc(16);
config->password_len = 16;
if (config->password) {
nt_password_hash(config->new_password,
config->new_password_len,
config->password);
}
os_free(config->new_password);
} else {
config->password = config->new_password;
config->password_len = config->new_password_len;
}
config->new_password = NULL;
config->new_password_len = 0;
}
}
/**
* eap_mschapv2_process - Process an EAP-MSCHAPv2 success message
* @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
* @data: Pointer to private EAP method data from eap_mschapv2_init()
* @ret: Return values from EAP request validation and processing
* @req: Pointer to EAP-MSCHAPv2 header from the request
* @req_len: Length of the EAP-MSCHAPv2 data
* @id: EAP identifier used in th erequest
* Returns: Pointer to allocated EAP response packet (eapRespData) or %NULL if
* no reply available
*/
static struct wpabuf * eap_mschapv2_success(struct eap_sm *sm,
struct eap_mschapv2_data *data,
struct eap_method_ret *ret,
const struct eap_mschapv2_hdr *req,
size_t req_len, u8 id)
{
struct wpabuf *resp;
const u8 *pos;
size_t len;
wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Received success");
len = req_len - sizeof(*req);
pos = (const u8 *) (req + 1);
if (!data->auth_response_valid ||
mschapv2_verify_auth_response(data->auth_response, pos, len)) {
wpa_printf(MSG_WARNING, "EAP-MSCHAPV2: Invalid authenticator "
"response in success request");
ret->methodState = METHOD_DONE;
ret->decision = DECISION_FAIL;
return NULL;
}
pos += 2 + 2 * MSCHAPV2_AUTH_RESPONSE_LEN;
len -= 2 + 2 * MSCHAPV2_AUTH_RESPONSE_LEN;
while (len > 0 && *pos == ' ') {
pos++;
len--;
}
wpa_hexdump_ascii(MSG_DEBUG, "EAP-MSCHAPV2: Success message",
pos, len);
wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Authentication succeeded");
/* Note: Only op_code of the EAP-MSCHAPV2 header is included in success
* message. */
resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, 1,
EAP_CODE_RESPONSE, id);
if (resp == NULL) {
wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Failed to allocate "
"buffer for success response");
ret->ignore = TRUE;
return NULL;
}
wpabuf_put_u8(resp, MSCHAPV2_OP_SUCCESS); /* op_code */
ret->methodState = METHOD_DONE;
ret->decision = DECISION_UNCOND_SUCC;
ret->allowNotifications = FALSE;
data->success = 1;
if (data->prev_error == ERROR_PASSWD_EXPIRED)
eap_mschapv2_password_changed(sm, data);
return resp;
}
static int eap_mschapv2_failure_txt(struct eap_sm *sm,
struct eap_mschapv2_data *data, char *txt)
{
char *pos, *msg = "";
int retry = 1;
struct eap_peer_config *config = eap_get_config(sm);
/* For example:
* E=691 R=1 C=<32 octets hex challenge> V=3 M=Authentication Failure
*/
pos = txt;
if (pos && os_strncmp(pos, "E=", 2) == 0) {
pos += 2;
data->prev_error = atoi(pos);
wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: error %d",
data->prev_error);
pos = os_strchr(pos, ' ');
if (pos)
pos++;
}
if (pos && os_strncmp(pos, "R=", 2) == 0) {
pos += 2;
retry = atoi(pos);
wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: retry is %sallowed",
retry == 1 ? "" : "not ");
pos = os_strchr(pos, ' ');
if (pos)
pos++;
}
if (pos && os_strncmp(pos, "C=", 2) == 0) {
int hex_len;
pos += 2;
hex_len = os_strchr(pos, ' ') - (char *) pos;
if (hex_len == PASSWD_CHANGE_CHAL_LEN * 2) {
if (hexstr2bin(pos, data->passwd_change_challenge,
PASSWD_CHANGE_CHAL_LEN)) {
wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: invalid "
"failure challenge");
} else {
wpa_hexdump(MSG_DEBUG, "EAP-MSCHAPV2: failure "
"challenge",
data->passwd_change_challenge,
PASSWD_CHANGE_CHAL_LEN);
data->passwd_change_challenge_valid = 1;
}
} else {
wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: invalid failure "
"challenge len %d", hex_len);
}
pos = os_strchr(pos, ' ');
if (pos)
pos++;
} else {
wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: required challenge field "
"was not present in failure message");
}
if (pos && os_strncmp(pos, "V=", 2) == 0) {
pos += 2;
data->passwd_change_version = atoi(pos);
wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: password changing "
"protocol version %d", data->passwd_change_version);
pos = os_strchr(pos, ' ');
if (pos)
pos++;
}
if (pos && os_strncmp(pos, "M=", 2) == 0) {
pos += 2;
msg = pos;
}
wpa_msg(sm->msg_ctx, MSG_WARNING,
"EAP-MSCHAPV2: failure message: '%s' (retry %sallowed, error "
"%d)",
msg, retry == 1 ? "" : "not ", data->prev_error);
if (data->prev_error == ERROR_PASSWD_EXPIRED &&
data->passwd_change_version == 3 && config) {
if (config->new_password == NULL) {
wpa_msg(sm->msg_ctx, MSG_INFO,
"EAP-MSCHAPV2: Password expired - password "
"change required");
eap_sm_request_new_password(sm);
}
} else if (retry == 1 && config) {
/* TODO: could prevent the current password from being used
* again at least for some period of time */
if (!config->mschapv2_retry)
eap_sm_request_identity(sm);
eap_sm_request_password(sm);
config->mschapv2_retry = 1;
} else if (config) {
/* TODO: prevent retries using same username/password */
config->mschapv2_retry = 0;
}
return retry == 1;
}
static struct wpabuf * eap_mschapv2_change_password(
struct eap_sm *sm, struct eap_mschapv2_data *data,
struct eap_method_ret *ret, const struct eap_mschapv2_hdr *req, u8 id)
{
struct wpabuf *resp;
int ms_len;
const u8 *username, *password, *new_password;
size_t username_len, password_len, new_password_len;
struct eap_mschapv2_hdr *ms;
struct ms_change_password *cp;
u8 password_hash[16], password_hash_hash[16];
int pwhash;
username = eap_get_config_identity(sm, &username_len);
password = eap_get_config_password2(sm, &password_len, &pwhash);
new_password = eap_get_config_new_password(sm, &new_password_len);
if (username == NULL || password == NULL || new_password == NULL)
return NULL;
username = mschapv2_remove_domain(username, &username_len);
ret->ignore = FALSE;
ret->methodState = METHOD_MAY_CONT;
ret->decision = DECISION_COND_SUCC;
ret->allowNotifications = TRUE;
ms_len = sizeof(*ms) + sizeof(*cp);
resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, ms_len,
EAP_CODE_RESPONSE, id);
if (resp == NULL)
return NULL;
ms = wpabuf_put(resp, sizeof(*ms));
ms->op_code = MSCHAPV2_OP_CHANGE_PASSWORD;
ms->mschapv2_id = req->mschapv2_id + 1;
WPA_PUT_BE16(ms->ms_length, ms_len);
cp = wpabuf_put(resp, sizeof(*cp));
/* Encrypted-Password */
if (pwhash) {
if (encrypt_pw_block_with_password_hash(
new_password, new_password_len,
password, cp->encr_password))
goto fail;
} else {
if (new_password_encrypted_with_old_nt_password_hash(
new_password, new_password_len,
password, password_len, cp->encr_password))
goto fail;
}
/* Encrypted-Hash */
if (pwhash) {
u8 new_password_hash[16];
nt_password_hash(new_password, new_password_len,
new_password_hash);
nt_password_hash_encrypted_with_block(password,
new_password_hash,
cp->encr_hash);
} else {
old_nt_password_hash_encrypted_with_new_nt_password_hash(
new_password, new_password_len,
password, password_len, cp->encr_hash);
}
/* Peer-Challenge */
if (random_get_bytes(cp->peer_challenge, MSCHAPV2_CHAL_LEN))
goto fail;
/* Reserved, must be zero */
os_memset(cp->reserved, 0, 8);
/* NT-Response */
wpa_hexdump(MSG_DEBUG, "EAP-MSCHAPV2: auth_challenge",
data->passwd_change_challenge, PASSWD_CHANGE_CHAL_LEN);
wpa_hexdump(MSG_DEBUG, "EAP-MSCHAPV2: peer_challenge",
cp->peer_challenge, MSCHAPV2_CHAL_LEN);
wpa_hexdump_ascii(MSG_DEBUG, "EAP-MSCHAPV2: username",
username, username_len);
wpa_hexdump_ascii_key(MSG_DEBUG, "EAP-MSCHAPV2: new password",
new_password, new_password_len);
generate_nt_response(data->passwd_change_challenge, cp->peer_challenge,
username, username_len,
new_password, new_password_len,
cp->nt_response);
wpa_hexdump(MSG_DEBUG, "EAP-MSCHAPV2: NT-Response",
cp->nt_response, MSCHAPV2_NT_RESPONSE_LEN);
/* Authenticator response is not really needed yet, but calculate it
* here so that challenges need not be saved. */
generate_authenticator_response(new_password, new_password_len,
cp->peer_challenge,
data->passwd_change_challenge,
username, username_len,
cp->nt_response, data->auth_response);
data->auth_response_valid = 1;
/* Likewise, generate master_key here since we have the needed data
* available. */
nt_password_hash(new_password, new_password_len, password_hash);
hash_nt_password_hash(password_hash, password_hash_hash);
get_master_key(password_hash_hash, cp->nt_response, data->master_key);
data->master_key_valid = 1;
/* Flags */
os_memset(cp->flags, 0, 2);
wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: TX identifier %d mschapv2_id %d "
"(change pw)", id, ms->mschapv2_id);
return resp;
fail:
wpabuf_free(resp);
return NULL;
}
/**
* eap_mschapv2_process - Process an EAP-MSCHAPv2 failure message
* @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
* @data: Pointer to private EAP method data from eap_mschapv2_init()
* @ret: Return values from EAP request validation and processing
* @req: Pointer to EAP-MSCHAPv2 header from the request
* @req_len: Length of the EAP-MSCHAPv2 data
* @id: EAP identifier used in th erequest
* Returns: Pointer to allocated EAP response packet (eapRespData) or %NULL if
* no reply available
*/
static struct wpabuf * eap_mschapv2_failure(struct eap_sm *sm,
struct eap_mschapv2_data *data,
struct eap_method_ret *ret,
const struct eap_mschapv2_hdr *req,
size_t req_len, u8 id)
{
struct wpabuf *resp;
const u8 *msdata = (const u8 *) (req + 1);
char *buf;
size_t len = req_len - sizeof(*req);
int retry = 0;
wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Received failure");
wpa_hexdump_ascii(MSG_DEBUG, "EAP-MSCHAPV2: Failure data",
msdata, len);
/*
* eap_mschapv2_failure_txt() expects a nul terminated string, so we
* must allocate a large enough temporary buffer to create that since
* the received message does not include nul termination.
*/
buf = os_malloc(len + 1);
if (buf) {
os_memcpy(buf, msdata, len);
buf[len] = '\0';
retry = eap_mschapv2_failure_txt(sm, data, buf);
os_free(buf);
}
ret->ignore = FALSE;
ret->methodState = METHOD_DONE;
ret->decision = DECISION_FAIL;
ret->allowNotifications = FALSE;
if (data->prev_error == ERROR_PASSWD_EXPIRED &&
data->passwd_change_version == 3) {
struct eap_peer_config *config = eap_get_config(sm);
if (config && config->new_password)
return eap_mschapv2_change_password(sm, data, ret, req,
id);
if (config && config->pending_req_new_password)
return NULL;
} else if (retry && data->prev_error == ERROR_AUTHENTICATION_FAILURE) {
/* TODO: could try to retry authentication, e.g, after having
* changed the username/password. In this case, EAP MS-CHAP-v2
* Failure Response would not be sent here. */
return NULL;
}
/* Note: Only op_code of the EAP-MSCHAPV2 header is included in failure
* message. */
resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, 1,
EAP_CODE_RESPONSE, id);
if (resp == NULL)
return NULL;
wpabuf_put_u8(resp, MSCHAPV2_OP_FAILURE); /* op_code */
return resp;
}
static int eap_mschapv2_check_config(struct eap_sm *sm)
{
size_t len;
if (eap_get_config_identity(sm, &len) == NULL) {
wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Identity not configured");
eap_sm_request_identity(sm);
return -1;
}
if (eap_get_config_password(sm, &len) == NULL) {
wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Password not configured");
eap_sm_request_password(sm);
return -1;
}
return 0;
}
static int eap_mschapv2_check_mslen(struct eap_sm *sm, size_t len,
const struct eap_mschapv2_hdr *ms)
{
size_t ms_len = WPA_GET_BE16(ms->ms_length);
if (ms_len == len)
return 0;
wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Invalid header: len=%lu "
"ms_len=%lu", (unsigned long) len, (unsigned long) ms_len);
if (sm->workaround) {
/* Some authentication servers use invalid ms_len,
* ignore it for interoperability. */
wpa_printf(MSG_INFO, "EAP-MSCHAPV2: workaround, ignore"
" invalid ms_len %lu (len %lu)",
(unsigned long) ms_len,
(unsigned long) len);
return 0;
}
return -1;
}
static void eap_mschapv2_copy_challenge(struct eap_mschapv2_data *data,
const struct wpabuf *reqData)
{
/*
* Store a copy of the challenge message, so that it can be processed
* again in case retry is allowed after a possible failure.
*/
wpabuf_free(data->prev_challenge);
data->prev_challenge = wpabuf_dup(reqData);
}
/**
* eap_mschapv2_process - Process an EAP-MSCHAPv2 request
* @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
* @priv: Pointer to private EAP method data from eap_mschapv2_init()
* @ret: Return values from EAP request validation and processing
* @reqData: EAP request to be processed (eapReqData)
* Returns: Pointer to allocated EAP response packet (eapRespData) or %NULL if
* no reply available
*/
static struct wpabuf * eap_mschapv2_process(struct eap_sm *sm, void *priv,
struct eap_method_ret *ret,
const struct wpabuf *reqData)
{
struct eap_mschapv2_data *data = priv;
struct eap_peer_config *config = eap_get_config(sm);
const struct eap_mschapv2_hdr *ms;
int using_prev_challenge = 0;
const u8 *pos;
size_t len;
u8 id;
if (eap_mschapv2_check_config(sm)) {
ret->ignore = TRUE;
return NULL;
}
if (config->mschapv2_retry && data->prev_challenge &&
data->prev_error == ERROR_AUTHENTICATION_FAILURE) {
wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Replacing pending packet "
"with the previous challenge");
reqData = data->prev_challenge;
using_prev_challenge = 1;
config->mschapv2_retry = 0;
}
pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, reqData,
&len);
if (pos == NULL || len < sizeof(*ms) + 1) {
ret->ignore = TRUE;
return NULL;
}
ms = (const struct eap_mschapv2_hdr *) pos;
if (eap_mschapv2_check_mslen(sm, len, ms)) {
ret->ignore = TRUE;
return NULL;
}
id = eap_get_id(reqData);
wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: RX identifier %d mschapv2_id %d",
id, ms->mschapv2_id);
switch (ms->op_code) {
case MSCHAPV2_OP_CHALLENGE:
if (!using_prev_challenge)
eap_mschapv2_copy_challenge(data, reqData);
return eap_mschapv2_challenge(sm, data, ret, ms, len, id);
case MSCHAPV2_OP_SUCCESS:
return eap_mschapv2_success(sm, data, ret, ms, len, id);
case MSCHAPV2_OP_FAILURE:
return eap_mschapv2_failure(sm, data, ret, ms, len, id);
default:
wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Unknown op %d - ignored",
ms->op_code);
ret->ignore = TRUE;
return NULL;
}
}
static Boolean eap_mschapv2_isKeyAvailable(struct eap_sm *sm, void *priv)
{
struct eap_mschapv2_data *data = priv;
return data->success && data->master_key_valid;
}
static u8 * eap_mschapv2_getKey(struct eap_sm *sm, void *priv, size_t *len)
{
struct eap_mschapv2_data *data = priv;
u8 *key;
int key_len;
if (!data->master_key_valid || !data->success)
return NULL;
key_len = 2 * MSCHAPV2_KEY_LEN;
key = os_malloc(key_len);
if (key == NULL)
return NULL;
/* MSK = server MS-MPPE-Recv-Key | MS-MPPE-Send-Key, i.e.,
* peer MS-MPPE-Send-Key | MS-MPPE-Recv-Key */
get_asymetric_start_key(data->master_key, key, MSCHAPV2_KEY_LEN, 1, 0);
get_asymetric_start_key(data->master_key, key + MSCHAPV2_KEY_LEN,
MSCHAPV2_KEY_LEN, 0, 0);
wpa_hexdump_key(MSG_DEBUG, "EAP-MSCHAPV2: Derived key",
key, key_len);
*len = key_len;
return key;
}
/**
* eap_peer_mschapv2_register - Register EAP-MSCHAPv2 peer method
* Returns: 0 on success, -1 on failure
*
* This function is used to register EAP-MSCHAPv2 peer method into the EAP
* method list.
*/
int eap_peer_mschapv2_register(void)
{
struct eap_method *eap;
int ret;
eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION,
EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2,
"MSCHAPV2");
if (eap == NULL)
return -1;
eap->init = eap_mschapv2_init;
eap->deinit = eap_mschapv2_deinit;
eap->process = eap_mschapv2_process;
eap->isKeyAvailable = eap_mschapv2_isKeyAvailable;
eap->getKey = eap_mschapv2_getKey;
ret = eap_peer_method_register(eap);
if (ret)
eap_peer_method_free(eap);
return ret;
}

View file

@ -0,0 +1,107 @@
/*
* EAP peer method: EAP-OTP (RFC 3748)
* Copyright (c) 2004-2006, Jouni Malinen <j@w1.fi>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* Alternatively, this software may be distributed under the terms of BSD
* license.
*
* See README and COPYING for more details.
*/
#include "includes.h"
#include "common.h"
#include "eap_i.h"
static void * eap_otp_init(struct eap_sm *sm)
{
/* No need for private data. However, must return non-NULL to indicate
* success. */
return (void *) 1;
}
static void eap_otp_deinit(struct eap_sm *sm, void *priv)
{
}
static struct wpabuf * eap_otp_process(struct eap_sm *sm, void *priv,
struct eap_method_ret *ret,
const struct wpabuf *reqData)
{
struct wpabuf *resp;
const u8 *pos, *password;
size_t password_len, len;
int otp;
pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_OTP, reqData, &len);
if (pos == NULL) {
ret->ignore = TRUE;
return NULL;
}
wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-OTP: Request message",
pos, len);
password = eap_get_config_otp(sm, &password_len);
if (password)
otp = 1;
else {
password = eap_get_config_password(sm, &password_len);
otp = 0;
}
if (password == NULL) {
wpa_printf(MSG_INFO, "EAP-OTP: Password not configured");
eap_sm_request_otp(sm, (const char *) pos, len);
ret->ignore = TRUE;
return NULL;
}
ret->ignore = FALSE;
ret->methodState = METHOD_DONE;
ret->decision = DECISION_COND_SUCC;
ret->allowNotifications = FALSE;
resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_OTP, password_len,
EAP_CODE_RESPONSE, eap_get_id(reqData));
if (resp == NULL)
return NULL;
wpabuf_put_data(resp, password, password_len);
wpa_hexdump_ascii_key(MSG_MSGDUMP, "EAP-OTP: Response",
password, password_len);
if (otp) {
wpa_printf(MSG_DEBUG, "EAP-OTP: Forgetting used password");
eap_clear_config_otp(sm);
}
return resp;
}
int eap_peer_otp_register(void)
{
struct eap_method *eap;
int ret;
eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION,
EAP_VENDOR_IETF, EAP_TYPE_OTP, "OTP");
if (eap == NULL)
return -1;
eap->init = eap_otp_init;
eap->deinit = eap_otp_deinit;
eap->process = eap_otp_process;
ret = eap_peer_method_register(eap);
if (ret)
eap_peer_method_free(eap);
return ret;
}

View file

@ -0,0 +1,531 @@
/*
* EAP peer method: EAP-PAX (RFC 4746)
* Copyright (c) 2005-2008, Jouni Malinen <j@w1.fi>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* Alternatively, this software may be distributed under the terms of BSD
* license.
*
* See README and COPYING for more details.
*/
#include "includes.h"
#include "common.h"
#include "crypto/random.h"
#include "eap_common/eap_pax_common.h"
#include "eap_i.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_INIT, PAX_STD_2_SENT, PAX_DONE } state;
u8 mac_id, dh_group_id, public_key_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;
char *cid;
size_t cid_len;
u8 ak[EAP_PAX_AK_LEN];
u8 mk[EAP_PAX_MK_LEN];
u8 ck[EAP_PAX_CK_LEN];
u8 ick[EAP_PAX_ICK_LEN];
};
static void eap_pax_deinit(struct eap_sm *sm, void *priv);
static void * eap_pax_init(struct eap_sm *sm)
{
struct eap_pax_data *data;
const u8 *identity, *password;
size_t identity_len, password_len;
identity = eap_get_config_identity(sm, &identity_len);
password = eap_get_config_password(sm, &password_len);
if (!identity || !password) {
wpa_printf(MSG_INFO, "EAP-PAX: CID (nai) or key (password) "
"not configured");
return NULL;
}
if (password_len != EAP_PAX_AK_LEN) {
wpa_printf(MSG_INFO, "EAP-PAX: Invalid PSK length");
return NULL;
}
data = os_zalloc(sizeof(*data));
if (data == NULL)
return NULL;
data->state = PAX_INIT;
data->cid = os_malloc(identity_len);
if (data->cid == NULL) {
eap_pax_deinit(sm, data);
return NULL;
}
os_memcpy(data->cid, identity, identity_len);
data->cid_len = identity_len;
os_memcpy(data->ak, password, EAP_PAX_AK_LEN);
return data;
}
static void eap_pax_deinit(struct eap_sm *sm, void *priv)
{
struct eap_pax_data *data = priv;
os_free(data->cid);
os_free(data);
}
static struct wpabuf * eap_pax_alloc_resp(const struct eap_pax_hdr *req,
u8 id, u8 op_code, size_t plen)
{
struct wpabuf *resp;
struct eap_pax_hdr *pax;
resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_PAX,
sizeof(*pax) + plen, EAP_CODE_RESPONSE, id);
if (resp == NULL)
return NULL;
pax = wpabuf_put(resp, sizeof(*pax));
pax->op_code = op_code;
pax->flags = 0;
pax->mac_id = req->mac_id;
pax->dh_group_id = req->dh_group_id;
pax->public_key_id = req->public_key_id;
return resp;
}
static struct wpabuf * eap_pax_process_std_1(struct eap_pax_data *data,
struct eap_method_ret *ret, u8 id,
const struct eap_pax_hdr *req,
size_t req_plen)
{
struct wpabuf *resp;
const u8 *pos;
u8 *rpos;
size_t left, plen;
wpa_printf(MSG_DEBUG, "EAP-PAX: PAX_STD-1 (received)");
if (data->state != PAX_INIT) {
wpa_printf(MSG_INFO, "EAP-PAX: PAX_STD-1 received in "
"unexpected state (%d) - ignored", data->state);
ret->ignore = TRUE;
return NULL;
}
if (req->flags & EAP_PAX_FLAGS_CE) {
wpa_printf(MSG_INFO, "EAP-PAX: PAX_STD-1 with CE flag set - "
"ignored");
ret->ignore = TRUE;
return NULL;
}
left = req_plen - sizeof(*req);
if (left < 2 + EAP_PAX_RAND_LEN) {
wpa_printf(MSG_INFO, "EAP-PAX: PAX_STD-1 with too short "
"payload");
ret->ignore = TRUE;
return NULL;
}
pos = (const u8 *) (req + 1);
if (WPA_GET_BE16(pos) != EAP_PAX_RAND_LEN) {
wpa_printf(MSG_INFO, "EAP-PAX: PAX_STD-1 with incorrect A "
"length %d (expected %d)",
WPA_GET_BE16(pos), EAP_PAX_RAND_LEN);
ret->ignore = TRUE;
return NULL;
}
pos += 2;
left -= 2;
os_memcpy(data->rand.r.x, pos, EAP_PAX_RAND_LEN);
wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: X (server rand)",
data->rand.r.x, EAP_PAX_RAND_LEN);
pos += EAP_PAX_RAND_LEN;
left -= EAP_PAX_RAND_LEN;
if (left > 0) {
wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: ignored extra payload",
pos, left);
}
if (random_get_bytes(data->rand.r.y, EAP_PAX_RAND_LEN)) {
wpa_printf(MSG_ERROR, "EAP-PAX: Failed to get random data");
ret->ignore = TRUE;
return NULL;
}
wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: Y (client rand)",
data->rand.r.y, EAP_PAX_RAND_LEN);
if (eap_pax_initial_key_derivation(req->mac_id, data->ak, data->rand.e,
data->mk, data->ck, data->ick) < 0)
{
ret->ignore = TRUE;
return NULL;
}
wpa_printf(MSG_DEBUG, "EAP-PAX: PAX_STD-2 (sending)");
plen = 2 + EAP_PAX_RAND_LEN + 2 + data->cid_len + 2 + EAP_PAX_MAC_LEN +
EAP_PAX_ICV_LEN;
resp = eap_pax_alloc_resp(req, id, EAP_PAX_OP_STD_2, plen);
if (resp == NULL)
return NULL;
wpabuf_put_be16(resp, EAP_PAX_RAND_LEN);
wpabuf_put_data(resp, data->rand.r.y, EAP_PAX_RAND_LEN);
wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: B = Y (client rand)",
data->rand.r.y, EAP_PAX_RAND_LEN);
wpabuf_put_be16(resp, data->cid_len);
wpabuf_put_data(resp, data->cid, data->cid_len);
wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-PAX: CID",
(u8 *) data->cid, data->cid_len);
wpabuf_put_be16(resp, EAP_PAX_MAC_LEN);
rpos = wpabuf_put(resp, EAP_PAX_MAC_LEN);
eap_pax_mac(req->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, rpos);
wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: MAC_CK(A, B, CID)",
rpos, EAP_PAX_MAC_LEN);
/* Optional ADE could be added here, if needed */
rpos = wpabuf_put(resp, EAP_PAX_ICV_LEN);
eap_pax_mac(req->mac_id, data->ick, EAP_PAX_ICK_LEN,
wpabuf_head(resp), wpabuf_len(resp) - EAP_PAX_ICV_LEN,
NULL, 0, NULL, 0, rpos);
wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: ICV", rpos, EAP_PAX_ICV_LEN);
data->state = PAX_STD_2_SENT;
data->mac_id = req->mac_id;
data->dh_group_id = req->dh_group_id;
data->public_key_id = req->public_key_id;
return resp;
}
static struct wpabuf * eap_pax_process_std_3(struct eap_pax_data *data,
struct eap_method_ret *ret, u8 id,
const struct eap_pax_hdr *req,
size_t req_plen)
{
struct wpabuf *resp;
u8 *rpos, mac[EAP_PAX_MAC_LEN];
const u8 *pos;
size_t left;
wpa_printf(MSG_DEBUG, "EAP-PAX: PAX_STD-3 (received)");
if (data->state != PAX_STD_2_SENT) {
wpa_printf(MSG_INFO, "EAP-PAX: PAX_STD-3 received in "
"unexpected state (%d) - ignored", data->state);
ret->ignore = TRUE;
return NULL;
}
if (req->flags & EAP_PAX_FLAGS_CE) {
wpa_printf(MSG_INFO, "EAP-PAX: PAX_STD-3 with CE flag set - "
"ignored");
ret->ignore = TRUE;
return NULL;
}
left = req_plen - sizeof(*req);
if (left < 2 + EAP_PAX_MAC_LEN) {
wpa_printf(MSG_INFO, "EAP-PAX: PAX_STD-3 with too short "
"payload");
ret->ignore = TRUE;
return NULL;
}
pos = (const u8 *) (req + 1);
if (WPA_GET_BE16(pos) != EAP_PAX_MAC_LEN) {
wpa_printf(MSG_INFO, "EAP-PAX: PAX_STD-3 with incorrect "
"MAC_CK length %d (expected %d)",
WPA_GET_BE16(pos), EAP_PAX_MAC_LEN);
ret->ignore = TRUE;
return NULL;
}
pos += 2;
left -= 2;
wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: MAC_CK(B, CID)",
pos, EAP_PAX_MAC_LEN);
eap_pax_mac(data->mac_id, data->ck, EAP_PAX_CK_LEN,
data->rand.r.y, EAP_PAX_RAND_LEN,
(u8 *) data->cid, data->cid_len, NULL, 0, mac);
if (os_memcmp(pos, mac, EAP_PAX_MAC_LEN) != 0) {
wpa_printf(MSG_INFO, "EAP-PAX: Invalid MAC_CK(B, CID) "
"received");
wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: expected MAC_CK(B, CID)",
mac, EAP_PAX_MAC_LEN);
ret->methodState = METHOD_DONE;
ret->decision = DECISION_FAIL;
return NULL;
}
pos += EAP_PAX_MAC_LEN;
left -= EAP_PAX_MAC_LEN;
if (left > 0) {
wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: ignored extra payload",
pos, left);
}
wpa_printf(MSG_DEBUG, "EAP-PAX: PAX-ACK (sending)");
resp = eap_pax_alloc_resp(req, id, EAP_PAX_OP_ACK, EAP_PAX_ICV_LEN);
if (resp == NULL)
return NULL;
/* Optional ADE could be added here, if needed */
rpos = wpabuf_put(resp, EAP_PAX_ICV_LEN);
eap_pax_mac(data->mac_id, data->ick, EAP_PAX_ICK_LEN,
wpabuf_head(resp), wpabuf_len(resp) - EAP_PAX_ICV_LEN,
NULL, 0, NULL, 0, rpos);
wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: ICV", rpos, EAP_PAX_ICV_LEN);
data->state = PAX_DONE;
ret->methodState = METHOD_DONE;
ret->decision = DECISION_UNCOND_SUCC;
ret->allowNotifications = FALSE;
return resp;
}
static struct wpabuf * eap_pax_process(struct eap_sm *sm, void *priv,
struct eap_method_ret *ret,
const struct wpabuf *reqData)
{
struct eap_pax_data *data = priv;
const struct eap_pax_hdr *req;
struct wpabuf *resp;
u8 icvbuf[EAP_PAX_ICV_LEN], id;
const u8 *icv, *pos;
size_t len;
u16 flen, mlen;
pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_PAX, reqData, &len);
if (pos == NULL || len < EAP_PAX_ICV_LEN) {
ret->ignore = TRUE;
return NULL;
}
id = eap_get_id(reqData);
req = (const struct eap_pax_hdr *) pos;
flen = len - EAP_PAX_ICV_LEN;
mlen = wpabuf_len(reqData) - EAP_PAX_ICV_LEN;
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",
req->op_code, req->flags, req->mac_id, req->dh_group_id,
req->public_key_id);
wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: received payload",
pos, len - EAP_PAX_ICV_LEN);
if (data->state != PAX_INIT && data->mac_id != req->mac_id) {
wpa_printf(MSG_INFO, "EAP-PAX: MAC ID changed during "
"authentication (was 0x%d, is 0x%d)",
data->mac_id, req->mac_id);
ret->ignore = TRUE;
return NULL;
}
if (data->state != PAX_INIT && data->dh_group_id != req->dh_group_id) {
wpa_printf(MSG_INFO, "EAP-PAX: DH Group ID changed during "
"authentication (was 0x%d, is 0x%d)",
data->dh_group_id, req->dh_group_id);
ret->ignore = TRUE;
return NULL;
}
if (data->state != PAX_INIT &&
data->public_key_id != req->public_key_id) {
wpa_printf(MSG_INFO, "EAP-PAX: Public Key ID changed during "
"authentication (was 0x%d, is 0x%d)",
data->public_key_id, req->public_key_id);
ret->ignore = TRUE;
return NULL;
}
/* TODO: add support EAP_PAX_HMAC_SHA256_128 */
if (req->mac_id != EAP_PAX_MAC_HMAC_SHA1_128) {
wpa_printf(MSG_INFO, "EAP-PAX: Unsupported MAC ID 0x%x",
req->mac_id);
ret->ignore = TRUE;
return NULL;
}
if (req->dh_group_id != EAP_PAX_DH_GROUP_NONE) {
wpa_printf(MSG_INFO, "EAP-PAX: Unsupported DH Group ID 0x%x",
req->dh_group_id);
ret->ignore = TRUE;
return NULL;
}
if (req->public_key_id != EAP_PAX_PUBLIC_KEY_NONE) {
wpa_printf(MSG_INFO, "EAP-PAX: Unsupported Public Key ID 0x%x",
req->public_key_id);
ret->ignore = TRUE;
return NULL;
}
if (req->flags & EAP_PAX_FLAGS_MF) {
/* TODO: add support for reassembling fragments */
wpa_printf(MSG_INFO, "EAP-PAX: fragmentation not supported - "
"ignored packet");
ret->ignore = TRUE;
return NULL;
}
icv = pos + len - EAP_PAX_ICV_LEN;
wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: ICV", icv, EAP_PAX_ICV_LEN);
if (req->op_code == EAP_PAX_OP_STD_1) {
eap_pax_mac(req->mac_id, (u8 *) "", 0,
wpabuf_head(reqData), mlen, NULL, 0, NULL, 0,
icvbuf);
} else {
eap_pax_mac(req->mac_id, data->ick, EAP_PAX_ICK_LEN,
wpabuf_head(reqData), mlen, NULL, 0, NULL, 0,
icvbuf);
}
if (os_memcmp(icv, icvbuf, EAP_PAX_ICV_LEN) != 0) {
wpa_printf(MSG_DEBUG, "EAP-PAX: invalid ICV - ignoring the "
"message");
wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: expected ICV",
icvbuf, EAP_PAX_ICV_LEN);
ret->ignore = TRUE;
return NULL;
}
ret->ignore = FALSE;
ret->methodState = METHOD_MAY_CONT;
ret->decision = DECISION_FAIL;
ret->allowNotifications = TRUE;
switch (req->op_code) {
case EAP_PAX_OP_STD_1:
resp = eap_pax_process_std_1(data, ret, id, req, flen);
break;
case EAP_PAX_OP_STD_3:
resp = eap_pax_process_std_3(data, ret, id, req, flen);
break;
default:
wpa_printf(MSG_DEBUG, "EAP-PAX: ignoring message with unknown "
"op_code %d", req->op_code);
ret->ignore = TRUE;
return NULL;
}
if (ret->methodState == METHOD_DONE) {
ret->allowNotifications = FALSE;
}
return resp;
}
static Boolean eap_pax_isKeyAvailable(struct eap_sm *sm, void *priv)
{
struct eap_pax_data *data = priv;
return data->state == PAX_DONE;
}
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 != PAX_DONE)
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 != PAX_DONE)
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;
}
int eap_peer_pax_register(void)
{
struct eap_method *eap;
int ret;
eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION,
EAP_VENDOR_IETF, EAP_TYPE_PAX, "PAX");
if (eap == NULL)
return -1;
eap->init = eap_pax_init;
eap->deinit = eap_pax_deinit;
eap->process = eap_pax_process;
eap->isKeyAvailable = eap_pax_isKeyAvailable;
eap->getKey = eap_pax_getKey;
eap->get_emsk = eap_pax_get_emsk;
ret = eap_peer_method_register(eap);
if (ret)
eap_peer_method_free(eap);
return ret;
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,483 @@
/*
* EAP peer method: EAP-PSK (RFC 4764)
* Copyright (c) 2004-2008, Jouni Malinen <j@w1.fi>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* Alternatively, this software may be distributed under the terms of BSD
* license.
*
* See README and COPYING for more details.
*
* 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_i.h"
struct eap_psk_data {
enum { PSK_INIT, PSK_MAC_SENT, PSK_DONE } state;
u8 rand_p[EAP_PSK_RAND_LEN];
u8 ak[EAP_PSK_AK_LEN], kdk[EAP_PSK_KDK_LEN], tek[EAP_PSK_TEK_LEN];
u8 *id_s, *id_p;
size_t id_s_len, id_p_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;
const u8 *identity, *password;
size_t identity_len, password_len;
password = eap_get_config_password(sm, &password_len);
if (!password || password_len != 16) {
wpa_printf(MSG_INFO, "EAP-PSK: 16-octet pre-shared key not "
"configured");
return NULL;
}
data = os_zalloc(sizeof(*data));
if (data == NULL)
return NULL;
if (eap_psk_key_setup(password, data->ak, data->kdk)) {
os_free(data);
return NULL;
}
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);
data->state = PSK_INIT;
identity = eap_get_config_identity(sm, &identity_len);
if (identity) {
data->id_p = os_malloc(identity_len);
if (data->id_p)
os_memcpy(data->id_p, identity, identity_len);
data->id_p_len = identity_len;
}
if (data->id_p == NULL) {
wpa_printf(MSG_INFO, "EAP-PSK: could not get own identity");
os_free(data);
return NULL;
}
return data;
}
static void eap_psk_deinit(struct eap_sm *sm, void *priv)
{
struct eap_psk_data *data = priv;
os_free(data->id_s);
os_free(data->id_p);
os_free(data);
}
static struct wpabuf * eap_psk_process_1(struct eap_psk_data *data,
struct eap_method_ret *ret,
const struct wpabuf *reqData)
{
const struct eap_psk_hdr_1 *hdr1;
struct eap_psk_hdr_2 *hdr2;
struct wpabuf *resp;
u8 *buf, *pos;
size_t buflen, len;
const u8 *cpos;
wpa_printf(MSG_DEBUG, "EAP-PSK: in INIT state");
cpos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_PSK, reqData, &len);
hdr1 = (const struct eap_psk_hdr_1 *) cpos;
if (cpos == NULL || len < sizeof(*hdr1)) {
wpa_printf(MSG_INFO, "EAP-PSK: Invalid first message "
"length (%lu; expected %lu or more)",
(unsigned long) len,
(unsigned long) sizeof(*hdr1));
ret->ignore = TRUE;
return NULL;
}
wpa_printf(MSG_DEBUG, "EAP-PSK: Flags=0x%x", hdr1->flags);
if (EAP_PSK_FLAGS_GET_T(hdr1->flags) != 0) {
wpa_printf(MSG_INFO, "EAP-PSK: Unexpected T=%d (expected 0)",
EAP_PSK_FLAGS_GET_T(hdr1->flags));
ret->methodState = METHOD_DONE;
ret->decision = DECISION_FAIL;
return NULL;
}
wpa_hexdump(MSG_DEBUG, "EAP-PSK: RAND_S", hdr1->rand_s,
EAP_PSK_RAND_LEN);
os_free(data->id_s);
data->id_s_len = len - sizeof(*hdr1);
data->id_s = os_malloc(data->id_s_len);
if (data->id_s == NULL) {
wpa_printf(MSG_ERROR, "EAP-PSK: Failed to allocate memory for "
"ID_S (len=%lu)", (unsigned long) data->id_s_len);
ret->ignore = TRUE;
return NULL;
}
os_memcpy(data->id_s, (u8 *) (hdr1 + 1), data->id_s_len);
wpa_hexdump_ascii(MSG_DEBUG, "EAP-PSK: ID_S",
data->id_s, data->id_s_len);
if (random_get_bytes(data->rand_p, EAP_PSK_RAND_LEN)) {
wpa_printf(MSG_ERROR, "EAP-PSK: Failed to get random data");
ret->ignore = TRUE;
return NULL;
}
resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_PSK,
sizeof(*hdr2) + data->id_p_len, EAP_CODE_RESPONSE,
eap_get_id(reqData));
if (resp == NULL)
return NULL;
hdr2 = wpabuf_put(resp, sizeof(*hdr2));
hdr2->flags = EAP_PSK_FLAGS_SET_T(1); /* T=1 */
os_memcpy(hdr2->rand_s, hdr1->rand_s, EAP_PSK_RAND_LEN);
os_memcpy(hdr2->rand_p, data->rand_p, EAP_PSK_RAND_LEN);
wpabuf_put_data(resp, data->id_p, data->id_p_len);
/* MAC_P = OMAC1-AES-128(AK, ID_P||ID_S||RAND_S||RAND_P) */
buflen = data->id_p_len + data->id_s_len + 2 * EAP_PSK_RAND_LEN;
buf = os_malloc(buflen);
if (buf == NULL) {
wpabuf_free(resp);
return NULL;
}
os_memcpy(buf, data->id_p, data->id_p_len);
pos = buf + data->id_p_len;
os_memcpy(pos, data->id_s, data->id_s_len);
pos += data->id_s_len;
os_memcpy(pos, hdr1->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, hdr2->mac_p)) {
os_free(buf);
wpabuf_free(resp);
return NULL;
}
os_free(buf);
wpa_hexdump(MSG_DEBUG, "EAP-PSK: RAND_P", hdr2->rand_p,
EAP_PSK_RAND_LEN);
wpa_hexdump(MSG_DEBUG, "EAP-PSK: MAC_P", hdr2->mac_p, EAP_PSK_MAC_LEN);
wpa_hexdump_ascii(MSG_DEBUG, "EAP-PSK: ID_P",
data->id_p, data->id_p_len);
data->state = PSK_MAC_SENT;
return resp;
}
static struct wpabuf * eap_psk_process_3(struct eap_psk_data *data,
struct eap_method_ret *ret,
const struct wpabuf *reqData)
{
const struct eap_psk_hdr_3 *hdr3;
struct eap_psk_hdr_4 *hdr4;
struct wpabuf *resp;
u8 *buf, *rpchannel, nonce[16], *decrypted;
const u8 *pchannel, *tag, *msg;
u8 mac[EAP_PSK_MAC_LEN];
size_t buflen, left, data_len, len, plen;
int failed = 0;
const u8 *pos;
wpa_printf(MSG_DEBUG, "EAP-PSK: in MAC_SENT state");
pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_PSK,
reqData, &len);
hdr3 = (const struct eap_psk_hdr_3 *) pos;
if (pos == NULL || len < sizeof(*hdr3)) {
wpa_printf(MSG_INFO, "EAP-PSK: Invalid third message "
"length (%lu; expected %lu or more)",
(unsigned long) len,
(unsigned long) sizeof(*hdr3));
ret->ignore = TRUE;
return NULL;
}
left = len - sizeof(*hdr3);
pchannel = (const u8 *) (hdr3 + 1);
wpa_printf(MSG_DEBUG, "EAP-PSK: Flags=0x%x", hdr3->flags);
if (EAP_PSK_FLAGS_GET_T(hdr3->flags) != 2) {
wpa_printf(MSG_INFO, "EAP-PSK: Unexpected T=%d (expected 2)",
EAP_PSK_FLAGS_GET_T(hdr3->flags));
ret->methodState = METHOD_DONE;
ret->decision = DECISION_FAIL;
return NULL;
}
wpa_hexdump(MSG_DEBUG, "EAP-PSK: RAND_S", hdr3->rand_s,
EAP_PSK_RAND_LEN);
wpa_hexdump(MSG_DEBUG, "EAP-PSK: MAC_S", hdr3->mac_s, EAP_PSK_MAC_LEN);
wpa_hexdump(MSG_DEBUG, "EAP-PSK: PCHANNEL", pchannel, left);
if (left < 4 + 16 + 1) {
wpa_printf(MSG_INFO, "EAP-PSK: Too short PCHANNEL data in "
"third message (len=%lu, expected 21)",
(unsigned long) left);
ret->ignore = TRUE;
return NULL;
}
/* MAC_S = OMAC1-AES-128(AK, ID_S||RAND_P) */
buflen = data->id_s_len + EAP_PSK_RAND_LEN;
buf = os_malloc(buflen);
if (buf == NULL)
return NULL;
os_memcpy(buf, data->id_s, data->id_s_len);
os_memcpy(buf + data->id_s_len, data->rand_p, EAP_PSK_RAND_LEN);
if (omac1_aes_128(data->ak, buf, buflen, mac)) {
os_free(buf);
return NULL;
}
os_free(buf);
if (os_memcmp(mac, hdr3->mac_s, EAP_PSK_MAC_LEN) != 0) {
wpa_printf(MSG_WARNING, "EAP-PSK: Invalid MAC_S in third "
"message");
ret->methodState = METHOD_DONE;
ret->decision = DECISION_FAIL;
return NULL;
}
wpa_printf(MSG_DEBUG, "EAP-PSK: MAC_S verified successfully");
if (eap_psk_derive_keys(data->kdk, data->rand_p, data->tek,
data->msk, data->emsk)) {
ret->methodState = METHOD_DONE;
ret->decision = DECISION_FAIL;
return NULL;
}
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, 12);
os_memcpy(nonce + 12, pchannel, 4);
pchannel += 4;
left -= 4;
tag = pchannel;
pchannel += 16;
left -= 16;
msg = pchannel;
wpa_hexdump(MSG_MSGDUMP, "EAP-PSK: PCHANNEL - nonce",
nonce, sizeof(nonce));
wpa_hexdump(MSG_MSGDUMP, "EAP-PSK: PCHANNEL - hdr",
wpabuf_head(reqData), 5);
wpa_hexdump(MSG_MSGDUMP, "EAP-PSK: PCHANNEL - cipher msg", msg, left);
decrypted = os_malloc(left);
if (decrypted == NULL) {
ret->methodState = METHOD_DONE;
ret->decision = DECISION_FAIL;
return NULL;
}
os_memcpy(decrypted, msg, left);
if (aes_128_eax_decrypt(data->tek, nonce, sizeof(nonce),
wpabuf_head(reqData),
sizeof(struct eap_hdr) + 1 +
sizeof(*hdr3) - EAP_PSK_MAC_LEN, decrypted,
left, tag)) {
wpa_printf(MSG_WARNING, "EAP-PSK: PCHANNEL decryption failed");
os_free(decrypted);
return NULL;
}
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");
failed = 1;
break;
case EAP_PSK_R_FLAG_DONE_SUCCESS:
wpa_printf(MSG_DEBUG, "EAP-PSK: R flag - DONE_SUCCESS");
break;
case EAP_PSK_R_FLAG_DONE_FAILURE:
wpa_printf(MSG_DEBUG, "EAP-PSK: R flag - DONE_FAILURE");
wpa_printf(MSG_INFO, "EAP-PSK: Authentication server rejected "
"authentication");
failed = 1;
break;
}
data_len = 1;
if ((decrypted[0] & EAP_PSK_E_FLAG) && left > 1)
data_len++;
plen = sizeof(*hdr4) + 4 + 16 + data_len;
resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_PSK, plen,
EAP_CODE_RESPONSE, eap_get_id(reqData));
if (resp == NULL) {
os_free(decrypted);
return NULL;
}
hdr4 = wpabuf_put(resp, sizeof(*hdr4));
hdr4->flags = EAP_PSK_FLAGS_SET_T(3); /* T=3 */
os_memcpy(hdr4->rand_s, hdr3->rand_s, EAP_PSK_RAND_LEN);
rpchannel = wpabuf_put(resp, 4 + 16 + data_len);
/* nonce++ */
inc_byte_array(nonce, sizeof(nonce));
os_memcpy(rpchannel, nonce + 12, 4);
if (decrypted[0] & EAP_PSK_E_FLAG) {
wpa_printf(MSG_DEBUG, "EAP-PSK: Unsupported E (Ext) flag");
failed = 1;
rpchannel[4 + 16] = (EAP_PSK_R_FLAG_DONE_FAILURE << 6) |
EAP_PSK_E_FLAG;
if (left > 1) {
/* Add empty EXT_Payload with same EXT_Type */
rpchannel[4 + 16 + 1] = decrypted[1];
}
} else if (failed)
rpchannel[4 + 16] = EAP_PSK_R_FLAG_DONE_FAILURE << 6;
else
rpchannel[4 + 16] = EAP_PSK_R_FLAG_DONE_SUCCESS << 6;
wpa_hexdump(MSG_DEBUG, "EAP-PSK: reply message (plaintext)",
rpchannel + 4 + 16, data_len);
if (aes_128_eax_encrypt(data->tek, nonce, sizeof(nonce),
wpabuf_head(resp),
sizeof(struct eap_hdr) + 1 + sizeof(*hdr4),
rpchannel + 4 + 16, data_len, rpchannel + 4)) {
os_free(decrypted);
wpabuf_free(resp);
return NULL;
}
wpa_hexdump(MSG_DEBUG, "EAP-PSK: reply message (PCHANNEL)",
rpchannel, 4 + 16 + data_len);
wpa_printf(MSG_DEBUG, "EAP-PSK: Completed %ssuccessfully",
failed ? "un" : "");
data->state = PSK_DONE;
ret->methodState = METHOD_DONE;
ret->decision = failed ? DECISION_FAIL : DECISION_UNCOND_SUCC;
os_free(decrypted);
return resp;
}
static struct wpabuf * eap_psk_process(struct eap_sm *sm, void *priv,
struct eap_method_ret *ret,
const struct wpabuf *reqData)
{
struct eap_psk_data *data = priv;
const u8 *pos;
struct wpabuf *resp = NULL;
size_t len;
pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_PSK, reqData, &len);
if (pos == NULL) {
ret->ignore = TRUE;
return NULL;
}
ret->ignore = FALSE;
ret->methodState = METHOD_MAY_CONT;
ret->decision = DECISION_FAIL;
ret->allowNotifications = TRUE;
switch (data->state) {
case PSK_INIT:
resp = eap_psk_process_1(data, ret, reqData);
break;
case PSK_MAC_SENT:
resp = eap_psk_process_3(data, ret, reqData);
break;
case PSK_DONE:
wpa_printf(MSG_DEBUG, "EAP-PSK: in DONE state - ignore "
"unexpected message");
ret->ignore = TRUE;
return NULL;
}
if (ret->methodState == METHOD_DONE) {
ret->allowNotifications = FALSE;
}
return resp;
}
static Boolean eap_psk_isKeyAvailable(struct eap_sm *sm, void *priv)
{
struct eap_psk_data *data = priv;
return data->state == PSK_DONE;
}
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 != PSK_DONE)
return NULL;
key = os_malloc(EAP_MSK_LEN);
if (key == NULL)
return NULL;
*len = EAP_MSK_LEN;
os_memcpy(key, data->msk, 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 != PSK_DONE)
return NULL;
key = os_malloc(EAP_EMSK_LEN);
if (key == NULL)
return NULL;
*len = EAP_EMSK_LEN;
os_memcpy(key, data->emsk, EAP_EMSK_LEN);
return key;
}
int eap_peer_psk_register(void)
{
struct eap_method *eap;
int ret;
eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION,
EAP_VENDOR_IETF, EAP_TYPE_PSK, "PSK");
if (eap == NULL)
return -1;
eap->init = eap_psk_init;
eap->deinit = eap_psk_deinit;
eap->process = eap_psk_process;
eap->isKeyAvailable = eap_psk_isKeyAvailable;
eap->getKey = eap_psk_getKey;
eap->get_emsk = eap_psk_get_emsk;
ret = eap_peer_method_register(eap);
if (ret)
eap_peer_method_free(eap);
return ret;
}

View file

@ -0,0 +1,744 @@
/*
* EAP peer method: EAP-pwd (RFC 5931)
* Copyright (c) 2010, Dan Harkins <dharkins@lounge.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the BSD license.
*
* Alternatively, this software may be distributed under the terms of the
* GNU General Public License version 2 as published by the Free Software
* Foundation.
*
* See README and COPYING for more details.
*/
#include "includes.h"
#include "common.h"
#include "eap_peer/eap_i.h"
#include "eap_common/eap_pwd_common.h"
struct eap_pwd_data {
enum {
PWD_ID_Req, PWD_Commit_Req, PWD_Confirm_Req, SUCCESS, FAILURE
} state;
u8 *id_peer;
size_t id_peer_len;
u8 *id_server;
size_t id_server_len;
u8 *password;
size_t password_len;
u16 group_num;
EAP_PWD_group *grp;
BIGNUM *k;
BIGNUM *private_value;
BIGNUM *server_scalar;
BIGNUM *my_scalar;
EC_POINT *my_element;
EC_POINT *server_element;
u8 msk[EAP_MSK_LEN];
u8 emsk[EAP_EMSK_LEN];
BN_CTX *bnctx;
};
#ifndef CONFIG_NO_STDOUT_DEBUG
static const char * eap_pwd_state_txt(int state)
{
switch (state) {
case PWD_ID_Req:
return "PWD-ID-Req";
case PWD_Commit_Req:
return "PWD-Commit-Req";
case PWD_Confirm_Req:
return "PWD-Confirm-Req";
case SUCCESS:
return "SUCCESS";
case FAILURE:
return "FAILURE";
default:
return "PWD-UNK";
}
}
#endif /* CONFIG_NO_STDOUT_DEBUG */
static void eap_pwd_state(struct eap_pwd_data *data, int state)
{
wpa_printf(MSG_INFO, "EAP-PWD: %s -> %s",
eap_pwd_state_txt(data->state), eap_pwd_state_txt(state));
data->state = state;
}
static void * eap_pwd_init(struct eap_sm *sm)
{
struct eap_pwd_data *data;
const u8 *identity, *password;
size_t identity_len, password_len;
password = eap_get_config_password(sm, &password_len);
if (password == NULL) {
wpa_printf(MSG_INFO, "EAP-PWD: No password configured!");
return NULL;
}
identity = eap_get_config_identity(sm, &identity_len);
if (identity == NULL) {
wpa_printf(MSG_INFO, "EAP-PWD: No identity configured!");
return NULL;
}
if ((data = os_zalloc(sizeof(*data))) == NULL) {
wpa_printf(MSG_INFO, "EAP-PWD: memory allocation data fail");
return NULL;
}
if ((data->bnctx = BN_CTX_new()) == NULL) {
wpa_printf(MSG_INFO, "EAP-PWD: bn context allocation fail");
os_free(data);
return NULL;
}
if ((data->id_peer = os_malloc(identity_len)) == NULL) {
wpa_printf(MSG_INFO, "EAP-PWD: memory allocation id fail");
BN_CTX_free(data->bnctx);
os_free(data);
return NULL;
}
os_memcpy(data->id_peer, identity, identity_len);
data->id_peer_len = identity_len;
if ((data->password = os_malloc(password_len)) == NULL) {
wpa_printf(MSG_INFO, "EAP-PWD: memory allocation psk fail");
BN_CTX_free(data->bnctx);
os_free(data->id_peer);
os_free(data);
return NULL;
}
os_memcpy(data->password, password, password_len);
data->password_len = password_len;
data->state = PWD_ID_Req;
return data;
}
static void eap_pwd_deinit(struct eap_sm *sm, void *priv)
{
struct eap_pwd_data *data = priv;
BN_free(data->private_value);
BN_free(data->server_scalar);
BN_free(data->my_scalar);
BN_free(data->k);
BN_CTX_free(data->bnctx);
EC_POINT_free(data->my_element);
EC_POINT_free(data->server_element);
os_free(data->id_peer);
os_free(data->id_server);
os_free(data->password);
if (data->grp) {
EC_GROUP_free(data->grp->group);
EC_POINT_free(data->grp->pwe);
BN_free(data->grp->order);
BN_free(data->grp->prime);
os_free(data->grp);
}
os_free(data);
}
static u8 * eap_pwd_getkey(struct eap_sm *sm, void *priv, size_t *len)
{
struct eap_pwd_data *data = priv;
u8 *key;
if (data->state != SUCCESS)
return NULL;
key = os_malloc(EAP_MSK_LEN);
if (key == NULL)
return NULL;
os_memcpy(key, data->msk, EAP_MSK_LEN);
*len = EAP_MSK_LEN;
return key;
}
static struct wpabuf *
eap_pwd_perform_id_exchange(struct eap_sm *sm, struct eap_pwd_data *data,
struct eap_method_ret *ret,
const struct wpabuf *reqData,
const u8 *payload, size_t payload_len)
{
struct eap_pwd_id *id;
struct wpabuf *resp;
if (data->state != PWD_ID_Req) {
ret->ignore = TRUE;
return NULL;
}
if (payload_len < sizeof(struct eap_pwd_id)) {
ret->ignore = TRUE;
return NULL;
}
id = (struct eap_pwd_id *) payload;
data->group_num = be_to_host16(id->group_num);
if ((id->random_function != EAP_PWD_DEFAULT_RAND_FUNC) ||
(id->prf != EAP_PWD_DEFAULT_PRF)) {
ret->ignore = TRUE;
return NULL;
}
wpa_printf(MSG_DEBUG, "EAP-PWD (peer): server said group %d",
data->group_num);
data->id_server = os_malloc(payload_len - sizeof(struct eap_pwd_id));
if (data->id_server == NULL) {
wpa_printf(MSG_INFO, "EAP-PWD: memory allocation id fail");
return NULL;
}
data->id_server_len = payload_len - sizeof(struct eap_pwd_id);
os_memcpy(data->id_server, id->identity, data->id_server_len);
wpa_hexdump_ascii(MSG_INFO, "EAP-PWD (peer): server sent id of",
data->id_server, data->id_server_len);
if ((data->grp = (EAP_PWD_group *) os_malloc(sizeof(EAP_PWD_group))) ==
NULL) {
wpa_printf(MSG_INFO, "EAP-PWD: failed to allocate memory for "
"group");
return NULL;
}
/* compute PWE */
if (compute_password_element(data->grp, data->group_num,
data->password, data->password_len,
data->id_server, data->id_server_len,
data->id_peer, data->id_peer_len,
id->token)) {
wpa_printf(MSG_INFO, "EAP-PWD (peer): unable to compute PWE");
return NULL;
}
wpa_printf(MSG_INFO, "EAP-PWD (peer): computed %d bit PWE...",
BN_num_bits(data->grp->prime));
resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_PWD,
1 + sizeof(struct eap_pwd_id) + data->id_peer_len,
EAP_CODE_RESPONSE, eap_get_id(reqData));
if (resp == NULL)
return NULL;
wpabuf_put_u8(resp, EAP_PWD_OPCODE_ID_EXCH);
wpabuf_put_be16(resp, data->group_num);
wpabuf_put_u8(resp, EAP_PWD_DEFAULT_RAND_FUNC);
wpabuf_put_u8(resp, EAP_PWD_DEFAULT_PRF);
wpabuf_put_data(resp, id->token, sizeof(id->token));
wpabuf_put_u8(resp, EAP_PWD_PREP_NONE);
wpabuf_put_data(resp, data->id_peer, data->id_peer_len);
eap_pwd_state(data, PWD_Commit_Req);
return resp;
}
static struct wpabuf *
eap_pwd_perform_commit_exchange(struct eap_sm *sm, struct eap_pwd_data *data,
struct eap_method_ret *ret,
const struct wpabuf *reqData,
const u8 *payload, size_t payload_len)
{
struct wpabuf *resp = NULL;
EC_POINT *K = NULL, *point = NULL;
BIGNUM *mask = NULL, *x = NULL, *y = NULL, *cofactor = NULL;
u16 offset;
u8 *ptr, *scalar = NULL, *element = NULL;
if (((data->private_value = BN_new()) == NULL) ||
((data->my_element = EC_POINT_new(data->grp->group)) == NULL) ||
((cofactor = BN_new()) == NULL) ||
((data->my_scalar = BN_new()) == NULL) ||
((mask = BN_new()) == NULL)) {
wpa_printf(MSG_INFO, "EAP-PWD (peer): scalar allocation fail");
goto fin;
}
if (!EC_GROUP_get_cofactor(data->grp->group, cofactor, NULL)) {
wpa_printf(MSG_INFO, "EAP-pwd (peer): unable to get cofactor "
"for curve");
goto fin;
}
BN_rand_range(data->private_value, data->grp->order);
BN_rand_range(mask, data->grp->order);
BN_add(data->my_scalar, data->private_value, mask);
BN_mod(data->my_scalar, data->my_scalar, data->grp->order,
data->bnctx);
if (!EC_POINT_mul(data->grp->group, data->my_element, NULL,
data->grp->pwe, mask, data->bnctx)) {
wpa_printf(MSG_INFO, "EAP-PWD (peer): element allocation "
"fail");
eap_pwd_state(data, FAILURE);
goto fin;
}
if (!EC_POINT_invert(data->grp->group, data->my_element, data->bnctx))
{
wpa_printf(MSG_INFO, "EAP-PWD (peer): element inversion fail");
goto fin;
}
BN_free(mask);
if (((x = BN_new()) == NULL) ||
((y = BN_new()) == NULL)) {
wpa_printf(MSG_INFO, "EAP-PWD (peer): point allocation fail");
goto fin;
}
/* process the request */
if (((data->server_scalar = BN_new()) == NULL) ||
((data->k = BN_new()) == NULL) ||
((K = EC_POINT_new(data->grp->group)) == NULL) ||
((point = EC_POINT_new(data->grp->group)) == NULL) ||
((data->server_element = EC_POINT_new(data->grp->group)) == NULL))
{
wpa_printf(MSG_INFO, "EAP-PWD (peer): peer data allocation "
"fail");
goto fin;
}
/* element, x then y, followed by scalar */
ptr = (u8 *) payload;
BN_bin2bn(ptr, BN_num_bytes(data->grp->prime), x);
ptr += BN_num_bytes(data->grp->prime);
BN_bin2bn(ptr, BN_num_bytes(data->grp->prime), y);
ptr += BN_num_bytes(data->grp->prime);
BN_bin2bn(ptr, BN_num_bytes(data->grp->order), data->server_scalar);
if (!EC_POINT_set_affine_coordinates_GFp(data->grp->group,
data->server_element, x, y,
data->bnctx)) {
wpa_printf(MSG_INFO, "EAP-PWD (peer): setting peer element "
"fail");
goto fin;
}
/* check to ensure server's element is not in a small sub-group */
if (BN_cmp(cofactor, BN_value_one())) {
if (!EC_POINT_mul(data->grp->group, point, NULL,
data->server_element, cofactor, NULL)) {
wpa_printf(MSG_INFO, "EAP-PWD (peer): cannot multiply "
"server element by order!\n");
goto fin;
}
if (EC_POINT_is_at_infinity(data->grp->group, point)) {
wpa_printf(MSG_INFO, "EAP-PWD (peer): server element "
"is at infinity!\n");
goto fin;
}
}
/* compute the shared key, k */
if ((!EC_POINT_mul(data->grp->group, K, NULL, data->grp->pwe,
data->server_scalar, data->bnctx)) ||
(!EC_POINT_add(data->grp->group, K, K, data->server_element,
data->bnctx)) ||
(!EC_POINT_mul(data->grp->group, K, NULL, K, data->private_value,
data->bnctx))) {
wpa_printf(MSG_INFO, "EAP-PWD (peer): computing shared key "
"fail");
goto fin;
}
/* ensure that the shared key isn't in a small sub-group */
if (BN_cmp(cofactor, BN_value_one())) {
if (!EC_POINT_mul(data->grp->group, K, NULL, K, cofactor,
NULL)) {
wpa_printf(MSG_INFO, "EAP-PWD (peer): cannot multiply "
"shared key point by order");
goto fin;
}
}
/*
* This check is strictly speaking just for the case above where
* co-factor > 1 but it was suggested that even though this is probably
* never going to happen it is a simple and safe check "just to be
* sure" so let's be safe.
*/
if (EC_POINT_is_at_infinity(data->grp->group, K)) {
wpa_printf(MSG_INFO, "EAP-PWD (peer): shared key point is at "
"infinity!\n");
goto fin;
}
if (!EC_POINT_get_affine_coordinates_GFp(data->grp->group, K, data->k,
NULL, data->bnctx)) {
wpa_printf(MSG_INFO, "EAP-PWD (peer): unable to extract "
"shared secret from point");
goto fin;
}
/* now do the response */
if (!EC_POINT_get_affine_coordinates_GFp(data->grp->group,
data->my_element, x, y,
data->bnctx)) {
wpa_printf(MSG_INFO, "EAP-PWD (peer): point assignment fail");
goto fin;
}
if (((scalar = os_malloc(BN_num_bytes(data->grp->order))) == NULL) ||
((element = os_malloc(BN_num_bytes(data->grp->prime) * 2)) ==
NULL)) {
wpa_printf(MSG_INFO, "EAP-PWD (peer): data allocation fail");
goto fin;
}
/*
* bignums occupy as little memory as possible so one that is
* sufficiently smaller than the prime or order might need pre-pending
* with zeros.
*/
os_memset(scalar, 0, BN_num_bytes(data->grp->order));
os_memset(element, 0, BN_num_bytes(data->grp->prime) * 2);
offset = BN_num_bytes(data->grp->order) -
BN_num_bytes(data->my_scalar);
BN_bn2bin(data->my_scalar, scalar + offset);
offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(x);
BN_bn2bin(x, element + offset);
offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(y);
BN_bn2bin(y, element + BN_num_bytes(data->grp->prime) + offset);
resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_PWD,
sizeof(struct eap_pwd_hdr) +
BN_num_bytes(data->grp->order) +
(2 * BN_num_bytes(data->grp->prime)),
EAP_CODE_RESPONSE, eap_get_id(reqData));
if (resp == NULL)
goto fin;
wpabuf_put_u8(resp, EAP_PWD_OPCODE_COMMIT_EXCH);
/* we send the element as (x,y) follwed by the scalar */
wpabuf_put_data(resp, element, (2 * BN_num_bytes(data->grp->prime)));
wpabuf_put_data(resp, scalar, BN_num_bytes(data->grp->order));
fin:
os_free(scalar);
os_free(element);
BN_free(x);
BN_free(y);
BN_free(cofactor);
EC_POINT_free(K);
EC_POINT_free(point);
if (resp == NULL)
eap_pwd_state(data, FAILURE);
else
eap_pwd_state(data, PWD_Confirm_Req);
return resp;
}
static struct wpabuf *
eap_pwd_perform_confirm_exchange(struct eap_sm *sm, struct eap_pwd_data *data,
struct eap_method_ret *ret,
const struct wpabuf *reqData,
const u8 *payload, size_t payload_len)
{
struct wpabuf *resp = NULL;
BIGNUM *x = NULL, *y = NULL;
HMAC_CTX ctx;
u32 cs;
u16 grp;
u8 conf[SHA256_DIGEST_LENGTH], *cruft = NULL, *ptr;
/*
* first build up the ciphersuite which is group | random_function |
* prf
*/
grp = htons(data->group_num);
ptr = (u8 *) &cs;
os_memcpy(ptr, &grp, sizeof(u16));
ptr += sizeof(u16);
*ptr = EAP_PWD_DEFAULT_RAND_FUNC;
ptr += sizeof(u8);
*ptr = EAP_PWD_DEFAULT_PRF;
/* each component of the cruft will be at most as big as the prime */
if (((cruft = os_malloc(BN_num_bytes(data->grp->prime))) == NULL) ||
((x = BN_new()) == NULL) || ((y = BN_new()) == NULL)) {
wpa_printf(MSG_INFO, "EAP-PWD (server): debug allocation "
"fail");
goto fin;
}
/*
* server's commit is H(k | server_element | server_scalar |
* peer_element | peer_scalar | ciphersuite)
*/
H_Init(&ctx);
/*
* zero the memory each time because this is mod prime math and some
* value may start with a few zeros and the previous one did not.
*/
os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
BN_bn2bin(data->k, cruft);
H_Update(&ctx, cruft, BN_num_bytes(data->grp->prime));
/* server element: x, y */
if (!EC_POINT_get_affine_coordinates_GFp(data->grp->group,
data->server_element, x, y,
data->bnctx)) {
wpa_printf(MSG_INFO, "EAP-PWD (server): confirm point "
"assignment fail");
goto fin;
}
os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
BN_bn2bin(x, cruft);
H_Update(&ctx, cruft, BN_num_bytes(data->grp->prime));
os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
BN_bn2bin(y, cruft);
H_Update(&ctx, cruft, BN_num_bytes(data->grp->prime));
/* server scalar */
os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
BN_bn2bin(data->server_scalar, cruft);
H_Update(&ctx, cruft, BN_num_bytes(data->grp->order));
/* my element: x, y */
if (!EC_POINT_get_affine_coordinates_GFp(data->grp->group,
data->my_element, x, y,
data->bnctx)) {
wpa_printf(MSG_INFO, "EAP-PWD (server): confirm point "
"assignment fail");
goto fin;
}
os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
BN_bn2bin(x, cruft);
H_Update(&ctx, cruft, BN_num_bytes(data->grp->prime));
os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
BN_bn2bin(y, cruft);
H_Update(&ctx, cruft, BN_num_bytes(data->grp->prime));
/* my scalar */
os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
BN_bn2bin(data->my_scalar, cruft);
H_Update(&ctx, cruft, BN_num_bytes(data->grp->order));
/* the ciphersuite */
H_Update(&ctx, (u8 *) &cs, sizeof(u32));
/* random function fin */
H_Final(&ctx, conf);
ptr = (u8 *) payload;
if (os_memcmp(conf, ptr, SHA256_DIGEST_LENGTH)) {
wpa_printf(MSG_INFO, "EAP-PWD (peer): confirm did not verify");
goto fin;
}
wpa_printf(MSG_DEBUG, "EAP-pwd (peer): confirm verified");
/*
* compute confirm:
* H(k | peer_element | peer_scalar | server_element | server_scalar |
* ciphersuite)
*/
H_Init(&ctx);
/* k */
os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
BN_bn2bin(data->k, cruft);
H_Update(&ctx, cruft, BN_num_bytes(data->grp->prime));
/* my element */
if (!EC_POINT_get_affine_coordinates_GFp(data->grp->group,
data->my_element, x, y,
data->bnctx)) {
wpa_printf(MSG_INFO, "EAP-PWD (peer): confirm point "
"assignment fail");
goto fin;
}
os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
BN_bn2bin(x, cruft);
H_Update(&ctx, cruft, BN_num_bytes(data->grp->prime));
os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
BN_bn2bin(y, cruft);
H_Update(&ctx, cruft, BN_num_bytes(data->grp->prime));
/* my scalar */
os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
BN_bn2bin(data->my_scalar, cruft);
H_Update(&ctx, cruft, BN_num_bytes(data->grp->order));
/* server element: x, y */
if (!EC_POINT_get_affine_coordinates_GFp(data->grp->group,
data->server_element, x, y,
data->bnctx)) {
wpa_printf(MSG_INFO, "EAP-PWD (peer): confirm point "
"assignment fail");
goto fin;
}
os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
BN_bn2bin(x, cruft);
H_Update(&ctx, cruft, BN_num_bytes(data->grp->prime));
os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
BN_bn2bin(y, cruft);
H_Update(&ctx, cruft, BN_num_bytes(data->grp->prime));
/* server scalar */
os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
BN_bn2bin(data->server_scalar, cruft);
H_Update(&ctx, cruft, BN_num_bytes(data->grp->order));
/* the ciphersuite */
H_Update(&ctx, (u8 *) &cs, sizeof(u32));
/* all done */
H_Final(&ctx, conf);
resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_PWD,
sizeof(struct eap_pwd_hdr) + SHA256_DIGEST_LENGTH,
EAP_CODE_RESPONSE, eap_get_id(reqData));
if (resp == NULL)
goto fin;
wpabuf_put_u8(resp, EAP_PWD_OPCODE_CONFIRM_EXCH);
wpabuf_put_data(resp, conf, SHA256_DIGEST_LENGTH);
if (compute_keys(data->grp, data->bnctx, data->k,
data->my_scalar, data->server_scalar, conf, ptr,
&cs, data->msk, data->emsk) < 0) {
wpa_printf(MSG_INFO, "EAP-PWD (peer): unable to compute MSK | "
"EMSK");
goto fin;
}
fin:
os_free(cruft);
BN_free(x);
BN_free(y);
ret->methodState = METHOD_DONE;
if (resp == NULL) {
ret->decision = DECISION_FAIL;
eap_pwd_state(data, FAILURE);
} else {
ret->decision = DECISION_UNCOND_SUCC;
eap_pwd_state(data, SUCCESS);
}
return resp;
}
static struct wpabuf *
eap_pwd_process(struct eap_sm *sm, void *priv, struct eap_method_ret *ret,
const struct wpabuf *reqData)
{
struct eap_pwd_data *data = priv;
struct wpabuf *resp = NULL;
const u8 *pos;
size_t len;
u8 exch;
pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_PWD, reqData, &len);
if ((pos == NULL) || (len < 1)) {
ret->ignore = TRUE;
return NULL;
}
wpa_printf(MSG_INFO, "EAP-pwd: Received frame: opcode %d", *pos);
ret->ignore = FALSE;
ret->methodState = METHOD_MAY_CONT;
ret->decision = DECISION_FAIL;
ret->allowNotifications = FALSE;
exch = *pos & 0x3f;
switch (exch) {
case EAP_PWD_OPCODE_ID_EXCH:
resp = eap_pwd_perform_id_exchange(sm, data, ret, reqData,
pos + 1, len - 1);
break;
case EAP_PWD_OPCODE_COMMIT_EXCH:
resp = eap_pwd_perform_commit_exchange(sm, data, ret, reqData,
pos + 1, len - 1);
break;
case EAP_PWD_OPCODE_CONFIRM_EXCH:
resp = eap_pwd_perform_confirm_exchange(sm, data, ret, reqData,
pos + 1, len - 1);
break;
default:
wpa_printf(MSG_INFO, "EAP-pwd: Ignoring message with unknown "
"opcode %d", exch);
break;
}
return resp;
}
static Boolean eap_pwd_key_available(struct eap_sm *sm, void *priv)
{
struct eap_pwd_data *data = priv;
return data->state == SUCCESS;
}
static u8 * eap_pwd_get_emsk(struct eap_sm *sm, void *priv, size_t *len)
{
struct eap_pwd_data *data = priv;
u8 *key;
if (data->state != SUCCESS)
return NULL;
if ((key = os_malloc(EAP_EMSK_LEN)) == NULL)
return NULL;
os_memcpy(key, data->emsk, EAP_EMSK_LEN);
*len = EAP_EMSK_LEN;
return key;
}
int eap_peer_pwd_register(void)
{
struct eap_method *eap;
int ret;
EVP_add_digest(EVP_sha256());
eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION,
EAP_VENDOR_IETF, EAP_TYPE_PWD, "PWD");
if (eap == NULL)
return -1;
eap->init = eap_pwd_init;
eap->deinit = eap_pwd_deinit;
eap->process = eap_pwd_process;
eap->isKeyAvailable = eap_pwd_key_available;
eap->getKey = eap_pwd_getkey;
eap->get_emsk = eap_pwd_get_emsk;
ret = eap_peer_method_register(eap);
if (ret)
eap_peer_method_free(eap);
return ret;
}

View file

@ -0,0 +1,500 @@
/*
* EAP peer method: EAP-SAKE (RFC 4763)
* Copyright (c) 2006-2008, Jouni Malinen <j@w1.fi>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* Alternatively, this software may be distributed under the terms of BSD
* license.
*
* See README and COPYING for more details.
*/
#include "includes.h"
#include "common.h"
#include "crypto/random.h"
#include "eap_peer/eap_i.h"
#include "eap_common/eap_sake_common.h"
struct eap_sake_data {
enum { IDENTITY, CHALLENGE, CONFIRM, SUCCESS, FAILURE } state;
u8 root_secret_a[EAP_SAKE_ROOT_SECRET_LEN];
u8 root_secret_b[EAP_SAKE_ROOT_SECRET_LEN];
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;
int session_id_set;
u8 *peerid;
size_t peerid_len;
u8 *serverid;
size_t serverid_len;
};
static const char * eap_sake_state_txt(int state)
{
switch (state) {
case IDENTITY:
return "IDENTITY";
case CHALLENGE:
return "CHALLENGE";
case CONFIRM:
return "CONFIRM";
case SUCCESS:
return "SUCCESS";
case FAILURE:
return "FAILURE";
default:
return "?";
}
}
static void eap_sake_state(struct eap_sake_data *data, int state)
{
wpa_printf(MSG_DEBUG, "EAP-SAKE: %s -> %s",
eap_sake_state_txt(data->state),
eap_sake_state_txt(state));
data->state = state;
}
static void eap_sake_deinit(struct eap_sm *sm, void *priv);
static void * eap_sake_init(struct eap_sm *sm)
{
struct eap_sake_data *data;
const u8 *identity, *password;
size_t identity_len, password_len;
password = eap_get_config_password(sm, &password_len);
if (!password || password_len != 2 * EAP_SAKE_ROOT_SECRET_LEN) {
wpa_printf(MSG_INFO, "EAP-SAKE: No key of correct length "
"configured");
return NULL;
}
data = os_zalloc(sizeof(*data));
if (data == NULL)
return NULL;
data->state = IDENTITY;
identity = eap_get_config_identity(sm, &identity_len);
if (identity) {
data->peerid = os_malloc(identity_len);
if (data->peerid == NULL) {
eap_sake_deinit(sm, data);
return NULL;
}
os_memcpy(data->peerid, identity, identity_len);
data->peerid_len = identity_len;
}
os_memcpy(data->root_secret_a, password, EAP_SAKE_ROOT_SECRET_LEN);
os_memcpy(data->root_secret_b,
password + EAP_SAKE_ROOT_SECRET_LEN,
EAP_SAKE_ROOT_SECRET_LEN);
return data;
}
static void eap_sake_deinit(struct eap_sm *sm, void *priv)
{
struct eap_sake_data *data = priv;
os_free(data->serverid);
os_free(data->peerid);
os_free(data);
}
static struct wpabuf * eap_sake_build_msg(struct eap_sake_data *data,
int id, size_t length, u8 subtype)
{
struct eap_sake_hdr *sake;
struct wpabuf *msg;
size_t plen;
plen = length + sizeof(struct eap_sake_hdr);
msg = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_SAKE, plen,
EAP_CODE_RESPONSE, 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_process_identity(struct eap_sm *sm,
struct eap_sake_data *data,
struct eap_method_ret *ret,
const struct wpabuf *reqData,
const u8 *payload,
size_t payload_len)
{
struct eap_sake_parse_attr attr;
struct wpabuf *resp;
if (data->state != IDENTITY) {
ret->ignore = TRUE;
return NULL;
}
wpa_printf(MSG_DEBUG, "EAP-SAKE: Received Request/Identity");
if (eap_sake_parse_attributes(payload, payload_len, &attr))
return NULL;
if (!attr.perm_id_req && !attr.any_id_req) {
wpa_printf(MSG_INFO, "EAP-SAKE: No AT_PERM_ID_REQ or "
"AT_ANY_ID_REQ in Request/Identity");
return NULL;
}
wpa_printf(MSG_DEBUG, "EAP-SAKE: Sending Response/Identity");
resp = eap_sake_build_msg(data, eap_get_id(reqData),
2 + data->peerid_len,
EAP_SAKE_SUBTYPE_IDENTITY);
if (resp == NULL)
return NULL;
wpa_printf(MSG_DEBUG, "EAP-SAKE: * AT_PEERID");
eap_sake_add_attr(resp, EAP_SAKE_AT_PEERID,
data->peerid, data->peerid_len);
eap_sake_state(data, CHALLENGE);
return resp;
}
static struct wpabuf * eap_sake_process_challenge(struct eap_sm *sm,
struct eap_sake_data *data,
struct eap_method_ret *ret,
const struct wpabuf *reqData,
const u8 *payload,
size_t payload_len)
{
struct eap_sake_parse_attr attr;
struct wpabuf *resp;
u8 *rpos;
size_t rlen;
if (data->state != IDENTITY && data->state != CHALLENGE) {
wpa_printf(MSG_DEBUG, "EAP-SAKE: Request/Challenge received "
"in unexpected state (%d)", data->state);
ret->ignore = TRUE;
return NULL;
}
if (data->state == IDENTITY)
eap_sake_state(data, CHALLENGE);
wpa_printf(MSG_DEBUG, "EAP-SAKE: Received Request/Challenge");
if (eap_sake_parse_attributes(payload, payload_len, &attr))
return NULL;
if (!attr.rand_s) {
wpa_printf(MSG_INFO, "EAP-SAKE: Request/Challenge did not "
"include AT_RAND_S");
return NULL;
}
os_memcpy(data->rand_s, attr.rand_s, EAP_SAKE_RAND_LEN);
wpa_hexdump(MSG_MSGDUMP, "EAP-SAKE: RAND_S (server rand)",
data->rand_s, EAP_SAKE_RAND_LEN);
if (random_get_bytes(data->rand_p, EAP_SAKE_RAND_LEN)) {
wpa_printf(MSG_ERROR, "EAP-SAKE: Failed to get random data");
return NULL;
}
wpa_hexdump(MSG_MSGDUMP, "EAP-SAKE: RAND_P (peer rand)",
data->rand_p, EAP_SAKE_RAND_LEN);
os_free(data->serverid);
data->serverid = NULL;
data->serverid_len = 0;
if (attr.serverid) {
wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-SAKE: SERVERID",
attr.serverid, attr.serverid_len);
data->serverid = os_malloc(attr.serverid_len);
if (data->serverid == NULL)
return NULL;
os_memcpy(data->serverid, attr.serverid, attr.serverid_len);
data->serverid_len = attr.serverid_len;
}
eap_sake_derive_keys(data->root_secret_a, data->root_secret_b,
data->rand_s, data->rand_p,
(u8 *) &data->tek, data->msk, data->emsk);
wpa_printf(MSG_DEBUG, "EAP-SAKE: Sending Response/Challenge");
rlen = 2 + EAP_SAKE_RAND_LEN + 2 + EAP_SAKE_MIC_LEN;
if (data->peerid)
rlen += 2 + data->peerid_len;
resp = eap_sake_build_msg(data, eap_get_id(reqData), rlen,
EAP_SAKE_SUBTYPE_CHALLENGE);
if (resp == NULL)
return NULL;
wpa_printf(MSG_DEBUG, "EAP-SAKE: * AT_RAND_P");
eap_sake_add_attr(resp, EAP_SAKE_AT_RAND_P,
data->rand_p, EAP_SAKE_RAND_LEN);
if (data->peerid) {
wpa_printf(MSG_DEBUG, "EAP-SAKE: * AT_PEERID");
eap_sake_add_attr(resp, EAP_SAKE_AT_PEERID,
data->peerid, data->peerid_len);
}
wpa_printf(MSG_DEBUG, "EAP-SAKE: * AT_MIC_P");
wpabuf_put_u8(resp, EAP_SAKE_AT_MIC_P);
wpabuf_put_u8(resp, 2 + EAP_SAKE_MIC_LEN);
rpos = wpabuf_put(resp, EAP_SAKE_MIC_LEN);
if (eap_sake_compute_mic(data->tek.auth, data->rand_s, data->rand_p,
data->serverid, data->serverid_len,
data->peerid, data->peerid_len, 1,
wpabuf_head(resp), wpabuf_len(resp), rpos,
rpos)) {
wpa_printf(MSG_INFO, "EAP-SAKE: Failed to compute MIC");
wpabuf_free(resp);
return NULL;
}
eap_sake_state(data, CONFIRM);
return resp;
}
static struct wpabuf * eap_sake_process_confirm(struct eap_sm *sm,
struct eap_sake_data *data,
struct eap_method_ret *ret,
const struct wpabuf *reqData,
const u8 *payload,
size_t payload_len)
{
struct eap_sake_parse_attr attr;
u8 mic_s[EAP_SAKE_MIC_LEN];
struct wpabuf *resp;
u8 *rpos;
if (data->state != CONFIRM) {
ret->ignore = TRUE;
return NULL;
}
wpa_printf(MSG_DEBUG, "EAP-SAKE: Received Request/Confirm");
if (eap_sake_parse_attributes(payload, payload_len, &attr))
return NULL;
if (!attr.mic_s) {
wpa_printf(MSG_INFO, "EAP-SAKE: Request/Confirm did not "
"include AT_MIC_S");
return NULL;
}
eap_sake_compute_mic(data->tek.auth, data->rand_s, data->rand_p,
data->serverid, data->serverid_len,
data->peerid, data->peerid_len, 0,
wpabuf_head(reqData), wpabuf_len(reqData),
attr.mic_s, mic_s);
if (os_memcmp(attr.mic_s, mic_s, EAP_SAKE_MIC_LEN) != 0) {
wpa_printf(MSG_INFO, "EAP-SAKE: Incorrect AT_MIC_S");
eap_sake_state(data, FAILURE);
ret->methodState = METHOD_DONE;
ret->decision = DECISION_FAIL;
ret->allowNotifications = FALSE;
wpa_printf(MSG_DEBUG, "EAP-SAKE: Sending "
"Response/Auth-Reject");
return eap_sake_build_msg(data, eap_get_id(reqData), 0,
EAP_SAKE_SUBTYPE_AUTH_REJECT);
}
wpa_printf(MSG_DEBUG, "EAP-SAKE: Sending Response/Confirm");
resp = eap_sake_build_msg(data, eap_get_id(reqData),
2 + EAP_SAKE_MIC_LEN,
EAP_SAKE_SUBTYPE_CONFIRM);
if (resp == NULL)
return NULL;
wpa_printf(MSG_DEBUG, "EAP-SAKE: * AT_MIC_P");
wpabuf_put_u8(resp, EAP_SAKE_AT_MIC_P);
wpabuf_put_u8(resp, 2 + EAP_SAKE_MIC_LEN);
rpos = wpabuf_put(resp, EAP_SAKE_MIC_LEN);
if (eap_sake_compute_mic(data->tek.auth, data->rand_s, data->rand_p,
data->serverid, data->serverid_len,
data->peerid, data->peerid_len, 1,
wpabuf_head(resp), wpabuf_len(resp), rpos,
rpos)) {
wpa_printf(MSG_INFO, "EAP-SAKE: Failed to compute MIC");
wpabuf_free(resp);
return NULL;
}
eap_sake_state(data, SUCCESS);
ret->methodState = METHOD_DONE;
ret->decision = DECISION_UNCOND_SUCC;
ret->allowNotifications = FALSE;
return resp;
}
static struct wpabuf * eap_sake_process(struct eap_sm *sm, void *priv,
struct eap_method_ret *ret,
const struct wpabuf *reqData)
{
struct eap_sake_data *data = priv;
const struct eap_sake_hdr *req;
struct wpabuf *resp;
const u8 *pos, *end;
size_t len;
u8 subtype, session_id;
pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_SAKE, reqData, &len);
if (pos == NULL || len < sizeof(struct eap_sake_hdr)) {
ret->ignore = TRUE;
return NULL;
}
req = (const struct eap_sake_hdr *) pos;
end = pos + len;
subtype = req->subtype;
session_id = req->session_id;
pos = (const u8 *) (req + 1);
wpa_printf(MSG_DEBUG, "EAP-SAKE: Received frame: subtype %d "
"session_id %d", subtype, session_id);
wpa_hexdump(MSG_DEBUG, "EAP-SAKE: Received attributes",
pos, end - pos);
if (data->session_id_set && data->session_id != session_id) {
wpa_printf(MSG_INFO, "EAP-SAKE: Session ID mismatch (%d,%d)",
session_id, data->session_id);
ret->ignore = TRUE;
return NULL;
}
data->session_id = session_id;
data->session_id_set = 1;
ret->ignore = FALSE;
ret->methodState = METHOD_MAY_CONT;
ret->decision = DECISION_FAIL;
ret->allowNotifications = TRUE;
switch (subtype) {
case EAP_SAKE_SUBTYPE_IDENTITY:
resp = eap_sake_process_identity(sm, data, ret, reqData,
pos, end - pos);
break;
case EAP_SAKE_SUBTYPE_CHALLENGE:
resp = eap_sake_process_challenge(sm, data, ret, reqData,
pos, end - pos);
break;
case EAP_SAKE_SUBTYPE_CONFIRM:
resp = eap_sake_process_confirm(sm, data, ret, reqData,
pos, end - pos);
break;
default:
wpa_printf(MSG_DEBUG, "EAP-SAKE: Ignoring message with "
"unknown subtype %d", subtype);
ret->ignore = TRUE;
return NULL;
}
if (ret->methodState == METHOD_DONE)
ret->allowNotifications = FALSE;
return resp;
}
static Boolean eap_sake_isKeyAvailable(struct eap_sm *sm, void *priv)
{
struct eap_sake_data *data = priv;
return data->state == SUCCESS;
}
static u8 * eap_sake_getKey(struct eap_sm *sm, void *priv, size_t *len)
{
struct eap_sake_data *data = priv;
u8 *key;
if (data->state != SUCCESS)
return NULL;
key = os_malloc(EAP_MSK_LEN);
if (key == NULL)
return NULL;
os_memcpy(key, data->msk, EAP_MSK_LEN);
*len = EAP_MSK_LEN;
return key;
}
static u8 * eap_sake_get_emsk(struct eap_sm *sm, void *priv, size_t *len)
{
struct eap_sake_data *data = priv;
u8 *key;
if (data->state != SUCCESS)
return NULL;
key = os_malloc(EAP_EMSK_LEN);
if (key == NULL)
return NULL;
os_memcpy(key, data->emsk, EAP_EMSK_LEN);
*len = EAP_EMSK_LEN;
return key;
}
int eap_peer_sake_register(void)
{
struct eap_method *eap;
int ret;
eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION,
EAP_VENDOR_IETF, EAP_TYPE_SAKE, "SAKE");
if (eap == NULL)
return -1;
eap->init = eap_sake_init;
eap->deinit = eap_sake_deinit;
eap->process = eap_sake_process;
eap->isKeyAvailable = eap_sake_isKeyAvailable;
eap->getKey = eap_sake_getKey;
eap->get_emsk = eap_sake_get_emsk;
ret = eap_peer_method_register(eap);
if (ret)
eap_peer_method_free(eap);
return ret;
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,289 @@
/*
* EAP peer method: EAP-TLS (RFC 2716)
* Copyright (c) 2004-2008, Jouni Malinen <j@w1.fi>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* Alternatively, this software may be distributed under the terms of BSD
* license.
*
* See README and COPYING for more details.
*/
#include "includes.h"
#include "common.h"
#include "crypto/tls.h"
#include "eap_i.h"
#include "eap_tls_common.h"
#include "eap_config.h"
static void eap_tls_deinit(struct eap_sm *sm, void *priv);
struct eap_tls_data {
struct eap_ssl_data ssl;
u8 *key_data;
};
static void * eap_tls_init(struct eap_sm *sm)
{
struct eap_tls_data *data;
struct eap_peer_config *config = eap_get_config(sm);
if (config == NULL ||
((sm->init_phase2 ? config->private_key2 : config->private_key)
== NULL &&
(sm->init_phase2 ? config->engine2 : config->engine) == 0)) {
wpa_printf(MSG_INFO, "EAP-TLS: Private key not configured");
return NULL;
}
data = os_zalloc(sizeof(*data));
if (data == NULL)
return NULL;
if (eap_peer_tls_ssl_init(sm, &data->ssl, config)) {
wpa_printf(MSG_INFO, "EAP-TLS: Failed to initialize SSL.");
eap_tls_deinit(sm, data);
if (config->engine) {
wpa_printf(MSG_DEBUG, "EAP-TLS: Requesting Smartcard "
"PIN");
eap_sm_request_pin(sm);
sm->ignore = TRUE;
} else if (config->private_key && !config->private_key_passwd)
{
wpa_printf(MSG_DEBUG, "EAP-TLS: Requesting private "
"key passphrase");
eap_sm_request_passphrase(sm);
sm->ignore = TRUE;
}
return NULL;
}
return data;
}
static void eap_tls_deinit(struct eap_sm *sm, void *priv)
{
struct eap_tls_data *data = priv;
if (data == NULL)
return;
eap_peer_tls_ssl_deinit(sm, &data->ssl);
os_free(data->key_data);
os_free(data);
}
static struct wpabuf * eap_tls_failure(struct eap_sm *sm,
struct eap_tls_data *data,
struct eap_method_ret *ret, int res,
struct wpabuf *resp, u8 id)
{
wpa_printf(MSG_DEBUG, "EAP-TLS: TLS processing failed");
ret->methodState = METHOD_DONE;
ret->decision = DECISION_FAIL;
if (res == -1) {
struct eap_peer_config *config = eap_get_config(sm);
if (config) {
/*
* The TLS handshake failed. So better forget the old
* PIN. It may be wrong, we cannot be sure but trying
* the wrong one again might block it on the card--so
* better ask the user again.
*/
os_free(config->pin);
config->pin = NULL;
}
}
if (resp) {
/*
* This is likely an alert message, so send it instead of just
* ACKing the error.
*/
return resp;
}
return eap_peer_tls_build_ack(id, EAP_TYPE_TLS, 0);
}
static void eap_tls_success(struct eap_sm *sm, struct eap_tls_data *data,
struct eap_method_ret *ret)
{
wpa_printf(MSG_DEBUG, "EAP-TLS: Done");
ret->methodState = METHOD_DONE;
ret->decision = DECISION_UNCOND_SUCC;
os_free(data->key_data);
data->key_data = eap_peer_tls_derive_key(sm, &data->ssl,
"client EAP encryption",
EAP_TLS_KEY_LEN +
EAP_EMSK_LEN);
if (data->key_data) {
wpa_hexdump_key(MSG_DEBUG, "EAP-TLS: Derived key",
data->key_data, EAP_TLS_KEY_LEN);
wpa_hexdump_key(MSG_DEBUG, "EAP-TLS: Derived EMSK",
data->key_data + EAP_TLS_KEY_LEN,
EAP_EMSK_LEN);
} else {
wpa_printf(MSG_INFO, "EAP-TLS: Failed to derive key");
}
}
static struct wpabuf * eap_tls_process(struct eap_sm *sm, void *priv,
struct eap_method_ret *ret,
const struct wpabuf *reqData)
{
size_t left;
int res;
struct wpabuf *resp;
u8 flags, id;
const u8 *pos;
struct eap_tls_data *data = priv;
pos = eap_peer_tls_process_init(sm, &data->ssl, EAP_TYPE_TLS, ret,
reqData, &left, &flags);
if (pos == NULL)
return NULL;
id = eap_get_id(reqData);
if (flags & EAP_TLS_FLAGS_START) {
wpa_printf(MSG_DEBUG, "EAP-TLS: Start");
left = 0; /* make sure that this frame is empty, even though it
* should always be, anyway */
}
resp = NULL;
res = eap_peer_tls_process_helper(sm, &data->ssl, EAP_TYPE_TLS, 0, id,
pos, left, &resp);
if (res < 0) {
return eap_tls_failure(sm, data, ret, res, resp, id);
}
if (tls_connection_established(sm->ssl_ctx, data->ssl.conn))
eap_tls_success(sm, data, ret);
if (res == 1) {
wpabuf_free(resp);
return eap_peer_tls_build_ack(id, EAP_TYPE_TLS, 0);
}
return resp;
}
static Boolean eap_tls_has_reauth_data(struct eap_sm *sm, void *priv)
{
struct eap_tls_data *data = priv;
return tls_connection_established(sm->ssl_ctx, data->ssl.conn);
}
static void eap_tls_deinit_for_reauth(struct eap_sm *sm, void *priv)
{
}
static void * eap_tls_init_for_reauth(struct eap_sm *sm, void *priv)
{
struct eap_tls_data *data = priv;
os_free(data->key_data);
data->key_data = NULL;
if (eap_peer_tls_reauth_init(sm, &data->ssl)) {
os_free(data);
return NULL;
}
return priv;
}
static int eap_tls_get_status(struct eap_sm *sm, void *priv, char *buf,
size_t buflen, int verbose)
{
struct eap_tls_data *data = priv;
return eap_peer_tls_status(sm, &data->ssl, buf, buflen, verbose);
}
static Boolean eap_tls_isKeyAvailable(struct eap_sm *sm, void *priv)
{
struct eap_tls_data *data = priv;
return data->key_data != NULL;
}
static u8 * eap_tls_getKey(struct eap_sm *sm, void *priv, size_t *len)
{
struct eap_tls_data *data = priv;
u8 *key;
if (data->key_data == NULL)
return NULL;
key = os_malloc(EAP_TLS_KEY_LEN);
if (key == NULL)
return NULL;
*len = EAP_TLS_KEY_LEN;
os_memcpy(key, data->key_data, EAP_TLS_KEY_LEN);
return key;
}
static u8 * eap_tls_get_emsk(struct eap_sm *sm, void *priv, size_t *len)
{
struct eap_tls_data *data = priv;
u8 *key;
if (data->key_data == NULL)
return NULL;
key = os_malloc(EAP_EMSK_LEN);
if (key == NULL)
return NULL;
*len = EAP_EMSK_LEN;
os_memcpy(key, data->key_data + EAP_TLS_KEY_LEN, EAP_EMSK_LEN);
return key;
}
int eap_peer_tls_register(void)
{
struct eap_method *eap;
int ret;
eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION,
EAP_VENDOR_IETF, EAP_TYPE_TLS, "TLS");
if (eap == NULL)
return -1;
eap->init = eap_tls_init;
eap->deinit = eap_tls_deinit;
eap->process = eap_tls_process;
eap->isKeyAvailable = eap_tls_isKeyAvailable;
eap->getKey = eap_tls_getKey;
eap->get_status = eap_tls_get_status;
eap->has_reauth_data = eap_tls_has_reauth_data;
eap->deinit_for_reauth = eap_tls_deinit_for_reauth;
eap->init_for_reauth = eap_tls_init_for_reauth;
eap->get_emsk = eap_tls_get_emsk;
ret = eap_peer_method_register(eap);
if (ret)
eap_peer_method_free(eap);
return ret;
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,126 @@
/*
* EAP peer: EAP-TLS/PEAP/TTLS/FAST common functions
* Copyright (c) 2004-2009, Jouni Malinen <j@w1.fi>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* Alternatively, this software may be distributed under the terms of BSD
* license.
*
* See README and COPYING for more details.
*/
#ifndef EAP_TLS_COMMON_H
#define EAP_TLS_COMMON_H
/**
* struct eap_ssl_data - TLS data for EAP methods
*/
struct eap_ssl_data {
/**
* conn - TLS connection context data from tls_connection_init()
*/
struct tls_connection *conn;
/**
* tls_out - TLS message to be sent out in fragments
*/
struct wpabuf *tls_out;
/**
* tls_out_pos - The current position in the outgoing TLS message
*/
size_t tls_out_pos;
/**
* tls_out_limit - Maximum fragment size for outgoing TLS messages
*/
size_t tls_out_limit;
/**
* tls_in - Received TLS message buffer for re-assembly
*/
struct wpabuf *tls_in;
/**
* tls_in_left - Number of remaining bytes in the incoming TLS message
*/
size_t tls_in_left;
/**
* tls_in_total - Total number of bytes in the incoming TLS message
*/
size_t tls_in_total;
/**
* phase2 - Whether this TLS connection is used in EAP phase 2 (tunnel)
*/
int phase2;
/**
* include_tls_length - Whether the TLS length field is included even
* if the TLS data is not fragmented
*/
int include_tls_length;
/**
* tls_ia - Whether TLS/IA is enabled for this TLS connection
*/
int tls_ia;
/**
* eap - EAP state machine allocated with eap_peer_sm_init()
*/
struct eap_sm *eap;
};
/* EAP TLS Flags */
#define EAP_TLS_FLAGS_LENGTH_INCLUDED 0x80
#define EAP_TLS_FLAGS_MORE_FRAGMENTS 0x40
#define EAP_TLS_FLAGS_START 0x20
#define EAP_TLS_VERSION_MASK 0x07
/* could be up to 128 bytes, but only the first 64 bytes are used */
#define EAP_TLS_KEY_LEN 64
int eap_peer_tls_ssl_init(struct eap_sm *sm, struct eap_ssl_data *data,
struct eap_peer_config *config);
void eap_peer_tls_ssl_deinit(struct eap_sm *sm, struct eap_ssl_data *data);
u8 * eap_peer_tls_derive_key(struct eap_sm *sm, struct eap_ssl_data *data,
const char *label, size_t len);
int eap_peer_tls_process_helper(struct eap_sm *sm, struct eap_ssl_data *data,
EapType eap_type, int peap_version,
u8 id, const u8 *in_data, size_t in_len,
struct wpabuf **out_data);
struct wpabuf * eap_peer_tls_build_ack(u8 id, EapType eap_type,
int peap_version);
int eap_peer_tls_reauth_init(struct eap_sm *sm, struct eap_ssl_data *data);
int eap_peer_tls_status(struct eap_sm *sm, struct eap_ssl_data *data,
char *buf, size_t buflen, int verbose);
const u8 * eap_peer_tls_process_init(struct eap_sm *sm,
struct eap_ssl_data *data,
EapType eap_type,
struct eap_method_ret *ret,
const struct wpabuf *reqData,
size_t *len, u8 *flags);
void eap_peer_tls_reset_input(struct eap_ssl_data *data);
void eap_peer_tls_reset_output(struct eap_ssl_data *data);
int eap_peer_tls_decrypt(struct eap_sm *sm, struct eap_ssl_data *data,
const struct wpabuf *in_data,
struct wpabuf **in_decrypted);
int eap_peer_tls_encrypt(struct eap_sm *sm, struct eap_ssl_data *data,
EapType eap_type, int peap_version, u8 id,
const struct wpabuf *in_data,
struct wpabuf **out_data);
int eap_peer_select_phase2_methods(struct eap_peer_config *config,
const char *prefix,
struct eap_method_type **types,
size_t *num_types);
int eap_peer_tls_phase2_nak(struct eap_method_type *types, size_t num_types,
struct eap_hdr *hdr, struct wpabuf **resp);
#endif /* EAP_TLS_COMMON_H */

View file

@ -0,0 +1,434 @@
/*
* EAP peer method: EAP-TNC (Trusted Network Connect)
* Copyright (c) 2007, Jouni Malinen <j@w1.fi>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* Alternatively, this software may be distributed under the terms of BSD
* license.
*
* See README and COPYING for more details.
*/
#include "includes.h"
#include "common.h"
#include "base64.h"
#include "eap_i.h"
#include "tncc.h"
struct eap_tnc_data {
enum { WAIT_START, PROC_MSG, WAIT_FRAG_ACK, DONE, FAIL } state;
struct tncc_data *tncc;
struct wpabuf *in_buf;
struct wpabuf *out_buf;
size_t out_used;
size_t fragment_size;
};
/* 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 void * eap_tnc_init(struct eap_sm *sm)
{
struct eap_tnc_data *data;
data = os_zalloc(sizeof(*data));
if (data == NULL)
return NULL;
data->state = WAIT_START;
data->fragment_size = 1300;
data->tncc = tncc_init();
if (data->tncc == NULL) {
os_free(data);
return NULL;
}
return data;
}
static void eap_tnc_deinit(struct eap_sm *sm, void *priv)
{
struct eap_tnc_data *data = priv;
wpabuf_free(data->in_buf);
wpabuf_free(data->out_buf);
tncc_deinit(data->tncc);
os_free(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,
struct eap_method_ret *ret, u8 id)
{
struct wpabuf *resp;
u8 flags;
size_t send_len, plen;
ret->ignore = FALSE;
wpa_printf(MSG_DEBUG, "EAP-TNC: Generating Response");
ret->allowNotifications = TRUE;
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;
resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_TNC, plen,
EAP_CODE_RESPONSE, id);
if (resp == NULL)
return NULL;
wpabuf_put_u8(resp, flags); /* Flags */
if (flags & EAP_TNC_FLAGS_LENGTH_INCLUDED)
wpabuf_put_be32(resp, wpabuf_len(data->out_buf));
wpabuf_put_data(resp, wpabuf_head_u8(data->out_buf) + data->out_used,
send_len);
data->out_used += send_len;
ret->methodState = METHOD_MAY_CONT;
ret->decision = DECISION_FAIL;
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;
} 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);
data->state = WAIT_FRAG_ACK;
}
return resp;
}
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");
data->state = 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 struct wpabuf * eap_tnc_process_fragment(struct eap_tnc_data *data,
struct eap_method_ret *ret,
u8 id, 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");
ret->ignore = TRUE;
return NULL;
}
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");
ret->ignore = TRUE;
return NULL;
}
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 eap_tnc_build_frag_ack(id, EAP_CODE_RESPONSE);
}
static struct wpabuf * eap_tnc_process(struct eap_sm *sm, void *priv,
struct eap_method_ret *ret,
const struct wpabuf *reqData)
{
struct eap_tnc_data *data = priv;
struct wpabuf *resp;
const u8 *pos, *end;
u8 *rpos, *rpos1;
size_t len, rlen;
size_t imc_len;
char *start_buf, *end_buf;
size_t start_len, end_len;
int tncs_done = 0;
u8 flags, id;
u32 message_length = 0;
struct wpabuf tmpbuf;
pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_TNC, reqData, &len);
if (pos == NULL) {
wpa_printf(MSG_INFO, "EAP-TNC: Invalid frame (pos=%p len=%lu)",
pos, (unsigned long) len);
ret->ignore = TRUE;
return NULL;
}
id = eap_get_id(reqData);
end = pos + len;
if (len == 0)
flags = 0; /* fragment ack */
else
flags = *pos++;
if (len > 0 && (flags & EAP_TNC_VERSION_MASK) != EAP_TNC_VERSION) {
wpa_printf(MSG_DEBUG, "EAP-TNC: Unsupported version %d",
flags & EAP_TNC_VERSION_MASK);
ret->ignore = TRUE;
return NULL;
}
if (flags & EAP_TNC_FLAGS_LENGTH_INCLUDED) {
if (end - pos < 4) {
wpa_printf(MSG_DEBUG, "EAP-TNC: Message underflow");
ret->ignore = TRUE;
return NULL;
}
message_length = WPA_GET_BE32(pos);
pos += 4;
if (message_length < (u32) (end - pos)) {
wpa_printf(MSG_DEBUG, "EAP-TNC: Invalid Message "
"Length (%d; %ld remaining in this msg)",
message_length, (long) (end - pos));
ret->ignore = TRUE;
return NULL;
}
}
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");
ret->ignore = TRUE;
return NULL;
}
wpa_printf(MSG_DEBUG, "EAP-TNC: Fragment acknowledged");
data->state = PROC_MSG;
return eap_tnc_build_msg(data, ret, id);
}
if (data->in_buf && eap_tnc_process_cont(data, pos, end - pos) < 0) {
ret->ignore = TRUE;
return NULL;
}
if (flags & EAP_TNC_FLAGS_MORE_FRAGMENTS) {
return eap_tnc_process_fragment(data, ret, id, flags,
message_length, pos,
end - pos);
}
if (data->in_buf == NULL) {
/* Wrap unfragmented messages as wpabuf without extra copy */
wpabuf_set(&tmpbuf, pos, end - pos);
data->in_buf = &tmpbuf;
}
if (data->state == WAIT_START) {
if (!(flags & EAP_TNC_FLAGS_START)) {
wpa_printf(MSG_DEBUG, "EAP-TNC: Server did not use "
"start flag in the first message");
ret->ignore = TRUE;
goto fail;
}
tncc_init_connection(data->tncc);
data->state = PROC_MSG;
} else {
enum tncc_process_res res;
if (flags & EAP_TNC_FLAGS_START) {
wpa_printf(MSG_DEBUG, "EAP-TNC: Server used start "
"flag again");
ret->ignore = TRUE;
goto fail;
}
res = tncc_process_if_tnccs(data->tncc,
wpabuf_head(data->in_buf),
wpabuf_len(data->in_buf));
switch (res) {
case TNCCS_PROCESS_ERROR:
ret->ignore = TRUE;
goto fail;
case TNCCS_PROCESS_OK_NO_RECOMMENDATION:
case TNCCS_RECOMMENDATION_ERROR:
wpa_printf(MSG_DEBUG, "EAP-TNC: No "
"TNCCS-Recommendation received");
break;
case TNCCS_RECOMMENDATION_ALLOW:
wpa_msg(sm->msg_ctx, MSG_INFO,
"TNC: Recommendation = allow");
tncs_done = 1;
break;
case TNCCS_RECOMMENDATION_NONE:
wpa_msg(sm->msg_ctx, MSG_INFO,
"TNC: Recommendation = none");
tncs_done = 1;
break;
case TNCCS_RECOMMENDATION_ISOLATE:
wpa_msg(sm->msg_ctx, MSG_INFO,
"TNC: Recommendation = isolate");
tncs_done = 1;
break;
}
}
if (data->in_buf != &tmpbuf)
wpabuf_free(data->in_buf);
data->in_buf = NULL;
ret->ignore = FALSE;
ret->methodState = METHOD_MAY_CONT;
ret->decision = DECISION_UNCOND_SUCC;
ret->allowNotifications = TRUE;
if (data->out_buf) {
data->state = PROC_MSG;
return eap_tnc_build_msg(data, ret, id);
}
if (tncs_done) {
resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_TNC, 1,
EAP_CODE_RESPONSE, eap_get_id(reqData));
if (resp == NULL)
return NULL;
wpabuf_put_u8(resp, EAP_TNC_VERSION);
wpa_printf(MSG_DEBUG, "EAP-TNC: TNCS done - reply with an "
"empty ACK message");
return resp;
}
imc_len = tncc_total_send_len(data->tncc);
start_buf = tncc_if_tnccs_start(data->tncc);
if (start_buf == NULL)
return NULL;
start_len = os_strlen(start_buf);
end_buf = tncc_if_tnccs_end();
if (end_buf == NULL) {
os_free(start_buf);
return NULL;
}
end_len = os_strlen(end_buf);
rlen = start_len + imc_len + end_len;
resp = wpabuf_alloc(rlen);
if (resp == NULL) {
os_free(start_buf);
os_free(end_buf);
return NULL;
}
wpabuf_put_data(resp, start_buf, start_len);
os_free(start_buf);
rpos1 = wpabuf_put(resp, 0);
rpos = tncc_copy_send_buf(data->tncc, rpos1);
wpabuf_put(resp, rpos - rpos1);
wpabuf_put_data(resp, end_buf, end_len);
os_free(end_buf);
wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-TNC: Response",
wpabuf_head(resp), wpabuf_len(resp));
data->out_buf = resp;
data->state = PROC_MSG;
return eap_tnc_build_msg(data, ret, id);
fail:
if (data->in_buf == &tmpbuf)
data->in_buf = NULL;
return NULL;
}
int eap_peer_tnc_register(void)
{
struct eap_method *eap;
int ret;
eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION,
EAP_VENDOR_IETF, EAP_TYPE_TNC, "TNC");
if (eap == NULL)
return -1;
eap->init = eap_tnc_init;
eap->deinit = eap_tnc_deinit;
eap->process = eap_tnc_process;
ret = eap_peer_method_register(eap);
if (ret)
eap_peer_method_free(eap);
return ret;
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,195 @@
/*
* EAP peer method: Test method for vendor specific (expanded) EAP type
* Copyright (c) 2005-2006, Jouni Malinen <j@w1.fi>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* Alternatively, this software may be distributed under the terms of BSD
* license.
*
* See README and COPYING for more details.
*
* This file implements a vendor specific test method using EAP expanded types.
* This is only for test use and must not be used for authentication since no
* security is provided.
*/
#include "includes.h"
#include "common.h"
#include "eap_i.h"
#ifdef TEST_PENDING_REQUEST
#include "eloop.h"
#endif /* TEST_PENDING_REQUEST */
#define EAP_VENDOR_ID 0xfffefd
#define EAP_VENDOR_TYPE 0xfcfbfaf9
/* #define TEST_PENDING_REQUEST */
struct eap_vendor_test_data {
enum { INIT, CONFIRM, SUCCESS } state;
int first_try;
};
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;
data->first_try = 1;
return data;
}
static void eap_vendor_test_deinit(struct eap_sm *sm, void *priv)
{
struct eap_vendor_test_data *data = priv;
os_free(data);
}
#ifdef TEST_PENDING_REQUEST
static void eap_vendor_ready(void *eloop_ctx, void *timeout_ctx)
{
struct eap_sm *sm = eloop_ctx;
wpa_printf(MSG_DEBUG, "EAP-VENDOR-TEST: Ready to re-process pending "
"request");
eap_notify_pending(sm);
}
#endif /* TEST_PENDING_REQUEST */
static struct wpabuf * eap_vendor_test_process(struct eap_sm *sm, void *priv,
struct eap_method_ret *ret,
const struct wpabuf *reqData)
{
struct eap_vendor_test_data *data = priv;
struct wpabuf *resp;
const u8 *pos;
size_t len;
pos = eap_hdr_validate(EAP_VENDOR_ID, EAP_VENDOR_TYPE, reqData, &len);
if (pos == NULL || len < 1) {
ret->ignore = TRUE;
return NULL;
}
if (data->state == INIT && *pos != 1) {
wpa_printf(MSG_DEBUG, "EAP-VENDOR-TEST: Unexpected message "
"%d in INIT state", *pos);
ret->ignore = TRUE;
return NULL;
}
if (data->state == CONFIRM && *pos != 3) {
wpa_printf(MSG_DEBUG, "EAP-VENDOR-TEST: Unexpected message "
"%d in CONFIRM state", *pos);
ret->ignore = TRUE;
return NULL;
}
if (data->state == SUCCESS) {
wpa_printf(MSG_DEBUG, "EAP-VENDOR-TEST: Unexpected message "
"in SUCCESS state");
ret->ignore = TRUE;
return NULL;
}
if (data->state == CONFIRM) {
#ifdef TEST_PENDING_REQUEST
if (data->first_try) {
data->first_try = 0;
wpa_printf(MSG_DEBUG, "EAP-VENDOR-TEST: Testing "
"pending request");
ret->ignore = TRUE;
eloop_register_timeout(1, 0, eap_vendor_ready, sm,
NULL);
return NULL;
}
#endif /* TEST_PENDING_REQUEST */
}
ret->ignore = FALSE;
wpa_printf(MSG_DEBUG, "EAP-VENDOR-TEST: Generating Response");
ret->allowNotifications = TRUE;
resp = eap_msg_alloc(EAP_VENDOR_ID, EAP_VENDOR_TYPE, 1,
EAP_CODE_RESPONSE, eap_get_id(reqData));
if (resp == NULL)
return NULL;
if (data->state == INIT) {
wpabuf_put_u8(resp, 2);
data->state = CONFIRM;
ret->methodState = METHOD_CONT;
ret->decision = DECISION_FAIL;
} else {
wpabuf_put_u8(resp, 4);
data->state = SUCCESS;
ret->methodState = METHOD_DONE;
ret->decision = DECISION_UNCOND_SUCC;
}
return resp;
}
static Boolean eap_vendor_test_isKeyAvailable(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;
}
int eap_peer_vendor_test_register(void)
{
struct eap_method *eap;
int ret;
eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION,
EAP_VENDOR_ID, EAP_VENDOR_TYPE,
"VENDOR-TEST");
if (eap == NULL)
return -1;
eap->init = eap_vendor_test_init;
eap->deinit = eap_vendor_test_deinit;
eap->process = eap_vendor_test_process;
eap->isKeyAvailable = eap_vendor_test_isKeyAvailable;
eap->getKey = eap_vendor_test_getKey;
ret = eap_peer_method_register(eap);
if (ret)
eap_peer_method_free(eap);
return ret;
}

View file

@ -0,0 +1,553 @@
/*
* EAP-WSC peer for Wi-Fi Protected Setup
* Copyright (c) 2007-2009, Jouni Malinen <j@w1.fi>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* Alternatively, this software may be distributed under the terms of BSD
* license.
*
* See README and COPYING for more details.
*/
#include "includes.h"
#include "common.h"
#include "uuid.h"
#include "eap_i.h"
#include "eap_common/eap_wsc_common.h"
#include "wps/wps.h"
#include "wps/wps_defs.h"
struct eap_wsc_data {
enum { WAIT_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;
struct wps_context *wps_ctx;
};
static const char * eap_wsc_state_txt(int state)
{
switch (state) {
case WAIT_START:
return "WAIT_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 "?";
}
}
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 int eap_wsc_new_ap_settings(struct wps_credential *cred,
const char *params)
{
const char *pos, *end;
size_t len;
os_memset(cred, 0, sizeof(*cred));
pos = os_strstr(params, "new_ssid=");
if (pos == NULL)
return 0;
pos += 9;
end = os_strchr(pos, ' ');
if (end == NULL)
len = os_strlen(pos);
else
len = end - pos;
if ((len & 1) || len > 2 * sizeof(cred->ssid) ||
hexstr2bin(pos, cred->ssid, len / 2))
return -1;
cred->ssid_len = len / 2;
pos = os_strstr(params, "new_auth=");
if (pos == NULL)
return -1;
if (os_strncmp(pos + 9, "OPEN", 4) == 0)
cred->auth_type = WPS_AUTH_OPEN;
else if (os_strncmp(pos + 9, "WPAPSK", 6) == 0)
cred->auth_type = WPS_AUTH_WPAPSK;
else if (os_strncmp(pos + 9, "WPA2PSK", 7) == 0)
cred->auth_type = WPS_AUTH_WPA2PSK;
else
return -1;
pos = os_strstr(params, "new_encr=");
if (pos == NULL)
return -1;
if (os_strncmp(pos + 9, "NONE", 4) == 0)
cred->encr_type = WPS_ENCR_NONE;
else if (os_strncmp(pos + 9, "WEP", 3) == 0)
cred->encr_type = WPS_ENCR_WEP;
else if (os_strncmp(pos + 9, "TKIP", 4) == 0)
cred->encr_type = WPS_ENCR_TKIP;
else if (os_strncmp(pos + 9, "CCMP", 4) == 0)
cred->encr_type = WPS_ENCR_AES;
else
return -1;
pos = os_strstr(params, "new_key=");
if (pos == NULL)
return 0;
pos += 8;
end = os_strchr(pos, ' ');
if (end == NULL)
len = os_strlen(pos);
else
len = end - pos;
if ((len & 1) || len > 2 * sizeof(cred->key) ||
hexstr2bin(pos, cred->key, len / 2))
return -1;
cred->key_len = len / 2;
return 1;
}
static void * eap_wsc_init(struct eap_sm *sm)
{
struct eap_wsc_data *data;
const u8 *identity;
size_t identity_len;
int registrar;
struct wps_config cfg;
const char *pos;
const char *phase1;
struct wps_context *wps;
struct wps_credential new_ap_settings;
int res;
wps = sm->wps;
if (wps == NULL) {
wpa_printf(MSG_ERROR, "EAP-WSC: WPS context not available");
return NULL;
}
identity = eap_get_config_identity(sm, &identity_len);
if (identity && identity_len == WSC_ID_REGISTRAR_LEN &&
os_memcmp(identity, WSC_ID_REGISTRAR, WSC_ID_REGISTRAR_LEN) == 0)
registrar = 1; /* Supplicant is Registrar */
else if (identity && identity_len == WSC_ID_ENROLLEE_LEN &&
os_memcmp(identity, WSC_ID_ENROLLEE, WSC_ID_ENROLLEE_LEN) == 0)
registrar = 0; /* Supplicant is Enrollee */
else {
wpa_hexdump_ascii(MSG_INFO, "EAP-WSC: Unexpected identity",
identity, identity_len);
return NULL;
}
data = os_zalloc(sizeof(*data));
if (data == NULL)
return NULL;
data->state = registrar ? MESG : WAIT_START;
data->registrar = registrar;
data->wps_ctx = wps;
os_memset(&cfg, 0, sizeof(cfg));
cfg.wps = wps;
cfg.registrar = registrar;
phase1 = eap_get_config_phase1(sm);
if (phase1 == NULL) {
wpa_printf(MSG_INFO, "EAP-WSC: phase1 configuration data not "
"set");
os_free(data);
return NULL;
}
pos = os_strstr(phase1, "pin=");
if (pos) {
pos += 4;
cfg.pin = (const u8 *) pos;
while (*pos != '\0' && *pos != ' ')
pos++;
cfg.pin_len = pos - (const char *) cfg.pin;
} else {
pos = os_strstr(phase1, "pbc=1");
if (pos)
cfg.pbc = 1;
}
if (cfg.pin == NULL && !cfg.pbc) {
wpa_printf(MSG_INFO, "EAP-WSC: PIN or PBC not set in phase1 "
"configuration data");
os_free(data);
return NULL;
}
pos = os_strstr(phase1, "dev_pw_id=");
if (pos && cfg.pin)
cfg.dev_pw_id = atoi(pos + 10);
res = eap_wsc_new_ap_settings(&new_ap_settings, phase1);
if (res < 0) {
os_free(data);
return NULL;
}
if (res == 1) {
wpa_printf(MSG_DEBUG, "EAP-WSC: Provide new AP settings for "
"WPS");
cfg.new_ap_settings = &new_ap_settings;
}
data->wps = wps_init(&cfg);
if (data->wps == NULL) {
os_free(data);
return NULL;
}
res = eap_get_config_fragment_size(sm);
if (res > 0)
data->fragment_size = res;
else
data->fragment_size = WSC_FRAGMENT_SIZE;
wpa_printf(MSG_DEBUG, "EAP-WSC: Fragment size limit %u",
(unsigned int) data->fragment_size);
if (registrar && cfg.pin) {
wps_registrar_add_pin(data->wps_ctx->registrar, NULL, NULL,
cfg.pin, cfg.pin_len, 0);
}
/* Use reduced client timeout for WPS to avoid long wait */
if (sm->ClientTimeout > 30)
sm->ClientTimeout = 30;
return data;
}
static void eap_wsc_deinit(struct eap_sm *sm, void *priv)
{
struct eap_wsc_data *data = priv;
wpabuf_free(data->in_buf);
wpabuf_free(data->out_buf);
wps_deinit(data->wps);
os_free(data->wps_ctx->network_key);
data->wps_ctx->network_key = NULL;
os_free(data);
}
static struct wpabuf * eap_wsc_build_msg(struct eap_wsc_data *data,
struct eap_method_ret *ret, u8 id)
{
struct wpabuf *resp;
u8 flags;
size_t send_len, plen;
ret->ignore = FALSE;
wpa_printf(MSG_DEBUG, "EAP-WSC: Generating Response");
ret->allowNotifications = TRUE;
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;
resp = eap_msg_alloc(EAP_VENDOR_WFA, EAP_VENDOR_TYPE_WSC, plen,
EAP_CODE_RESPONSE, id);
if (resp == NULL)
return NULL;
wpabuf_put_u8(resp, data->out_op_code); /* Op-Code */
wpabuf_put_u8(resp, flags); /* Flags */
if (flags & WSC_FLAGS_LF)
wpabuf_put_be16(resp, wpabuf_len(data->out_buf));
wpabuf_put_data(resp, wpabuf_head_u8(data->out_buf) + data->out_used,
send_len);
data->out_used += send_len;
ret->methodState = METHOD_MAY_CONT;
ret->decision = DECISION_FAIL;
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;
if ((data->state == FAIL && data->out_op_code == WSC_ACK) ||
data->out_op_code == WSC_NACK ||
data->out_op_code == WSC_Done) {
eap_wsc_state(data, FAIL);
ret->methodState = METHOD_DONE;
} else
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 resp;
}
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);
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 struct wpabuf * eap_wsc_process_fragment(struct eap_wsc_data *data,
struct eap_method_ret *ret,
u8 id, 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");
ret->ignore = TRUE;
return NULL;
}
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");
ret->ignore = TRUE;
return NULL;
}
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 eap_wsc_build_frag_ack(id, EAP_CODE_RESPONSE);
}
static struct wpabuf * eap_wsc_process(struct eap_sm *sm, void *priv,
struct eap_method_ret *ret,
const struct wpabuf *reqData)
{
struct eap_wsc_data *data = priv;
const u8 *start, *pos, *end;
size_t len;
u8 op_code, flags, id;
u16 message_length = 0;
enum wps_process_res res;
struct wpabuf tmpbuf;
struct wpabuf *r;
pos = eap_hdr_validate(EAP_VENDOR_WFA, EAP_VENDOR_TYPE_WSC, reqData,
&len);
if (pos == NULL || len < 2) {
ret->ignore = TRUE;
return NULL;
}
id = eap_get_id(reqData);
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");
ret->ignore = TRUE;
return NULL;
}
message_length = WPA_GET_BE16(pos);
pos += 2;
if (message_length < end - pos) {
wpa_printf(MSG_DEBUG, "EAP-WSC: Invalid Message "
"Length");
ret->ignore = TRUE;
return NULL;
}
}
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);
ret->ignore = TRUE;
return NULL;
}
wpa_printf(MSG_DEBUG, "EAP-WSC: Fragment acknowledged");
eap_wsc_state(data, MESG);
return eap_wsc_build_msg(data, ret, id);
}
if (op_code != WSC_ACK && op_code != WSC_NACK && op_code != WSC_MSG &&
op_code != WSC_Done && op_code != WSC_Start) {
wpa_printf(MSG_DEBUG, "EAP-WSC: Unexpected Op-Code %d",
op_code);
ret->ignore = TRUE;
return NULL;
}
if (data->state == WAIT_START) {
if (op_code != WSC_Start) {
wpa_printf(MSG_DEBUG, "EAP-WSC: Unexpected Op-Code %d "
"in WAIT_START state", op_code);
ret->ignore = TRUE;
return NULL;
}
wpa_printf(MSG_DEBUG, "EAP-WSC: Received start");
eap_wsc_state(data, MESG);
/* Start message has empty payload, skip processing */
goto send_msg;
} else if (op_code == WSC_Start) {
wpa_printf(MSG_DEBUG, "EAP-WSC: Unexpected Op-Code %d",
op_code);
ret->ignore = TRUE;
return NULL;
}
if (data->in_buf &&
eap_wsc_process_cont(data, pos, end - pos, op_code) < 0) {
ret->ignore = TRUE;
return NULL;
}
if (flags & WSC_FLAGS_MF) {
return eap_wsc_process_fragment(data, ret, id, flags, op_code,
message_length, pos,
end - pos);
}
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 - wait for EAP failure");
eap_wsc_state(data, FAIL);
break;
case WPS_CONTINUE:
eap_wsc_state(data, MESG);
break;
case WPS_FAILURE:
case WPS_PENDING:
wpa_printf(MSG_DEBUG, "EAP-WSC: WPS processing failed");
eap_wsc_state(data, FAIL);
break;
}
if (data->in_buf != &tmpbuf)
wpabuf_free(data->in_buf);
data->in_buf = NULL;
send_msg:
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;
}
eap_wsc_state(data, MESG);
r = eap_wsc_build_msg(data, ret, id);
if (data->state == FAIL && ret->methodState == METHOD_DONE) {
/* Use reduced client timeout for WPS to avoid long wait */
if (sm->ClientTimeout > 2)
sm->ClientTimeout = 2;
}
return r;
}
int eap_peer_wsc_register(void)
{
struct eap_method *eap;
int ret;
eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION,
EAP_VENDOR_WFA, EAP_VENDOR_TYPE_WSC,
"WSC");
if (eap == NULL)
return -1;
eap->init = eap_wsc_init;
eap->deinit = eap_wsc_deinit;
eap->process = eap_wsc_process;
ret = eap_peer_method_register(eap);
if (ret)
eap_peer_method_free(eap);
return ret;
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,65 @@
/*
* IKEv2 responder (RFC 4306) for EAP-IKEV2
* Copyright (c) 2007, Jouni Malinen <j@w1.fi>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* Alternatively, this software may be distributed under the terms of BSD
* license.
*
* See README and COPYING for more details.
*/
#ifndef IKEV2_H
#define IKEV2_H
#include "eap_common/ikev2_common.h"
struct ikev2_proposal_data {
u8 proposal_num;
int integ;
int prf;
int encr;
int dh;
};
struct ikev2_responder_data {
enum { SA_INIT, SA_AUTH, CHILD_SA, NOTIFY, IKEV2_DONE, IKEV2_FAILED }
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 *i_dh_public;
struct wpabuf *r_dh_private;
struct ikev2_proposal_data proposal;
const struct dh_group *dh;
struct ikev2_keys keys;
u8 *IDi;
size_t IDi_len;
u8 IDi_type;
u8 *IDr;
size_t IDr_len;
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;
u16 error_type;
enum { LAST_MSG_SA_INIT, LAST_MSG_SA_AUTH } last_msg;
};
void ikev2_responder_deinit(struct ikev2_responder_data *data);
int ikev2_responder_process(struct ikev2_responder_data *data,
const struct wpabuf *buf);
struct wpabuf * ikev2_responder_build(struct ikev2_responder_data *data);
#endif /* IKEV2_H */

View file

@ -0,0 +1,123 @@
/*
* MSCHAPV2 (RFC 2759)
* Copyright (c) 2004-2008, Jouni Malinen <j@w1.fi>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* Alternatively, this software may be distributed under the terms of BSD
* license.
*
* See README and COPYING for more details.
*/
#include "includes.h"
#include "common.h"
#include "crypto/ms_funcs.h"
#include "mschapv2.h"
const u8 * mschapv2_remove_domain(const u8 *username, size_t *len)
{
size_t i;
/*
* MSCHAPv2 does not include optional domain name in the
* challenge-response calculation, so remove domain prefix
* (if present).
*/
for (i = 0; i < *len; i++) {
if (username[i] == '\\') {
*len -= i + 1;
return username + i + 1;
}
}
return username;
}
int mschapv2_derive_response(const u8 *identity, size_t identity_len,
const u8 *password, size_t password_len,
int pwhash,
const u8 *auth_challenge,
const u8 *peer_challenge,
u8 *nt_response, u8 *auth_response,
u8 *master_key)
{
const u8 *username;
size_t username_len;
u8 password_hash[16], password_hash_hash[16];
wpa_hexdump_ascii(MSG_DEBUG, "MSCHAPV2: Identity",
identity, identity_len);
username_len = identity_len;
username = mschapv2_remove_domain(identity, &username_len);
wpa_hexdump_ascii(MSG_DEBUG, "MSCHAPV2: Username",
username, username_len);
wpa_hexdump(MSG_DEBUG, "MSCHAPV2: auth_challenge",
auth_challenge, MSCHAPV2_CHAL_LEN);
wpa_hexdump(MSG_DEBUG, "MSCHAPV2: peer_challenge",
peer_challenge, MSCHAPV2_CHAL_LEN);
wpa_hexdump_ascii(MSG_DEBUG, "MSCHAPV2: username",
username, username_len);
/* Authenticator response is not really needed yet, but calculate it
* here so that challenges need not be saved. */
if (pwhash) {
wpa_hexdump_key(MSG_DEBUG, "MSCHAPV2: password hash",
password, password_len);
generate_nt_response_pwhash(auth_challenge, peer_challenge,
username, username_len,
password, nt_response);
generate_authenticator_response_pwhash(
password, peer_challenge, auth_challenge,
username, username_len, nt_response, auth_response);
} else {
wpa_hexdump_ascii_key(MSG_DEBUG, "MSCHAPV2: password",
password, password_len);
generate_nt_response(auth_challenge, peer_challenge,
username, username_len,
password, password_len, nt_response);
generate_authenticator_response(password, password_len,
peer_challenge, auth_challenge,
username, username_len,
nt_response, auth_response);
}
wpa_hexdump(MSG_DEBUG, "MSCHAPV2: NT Response",
nt_response, MSCHAPV2_NT_RESPONSE_LEN);
wpa_hexdump(MSG_DEBUG, "MSCHAPV2: Auth Response",
auth_response, MSCHAPV2_AUTH_RESPONSE_LEN);
/* Generate master_key here since we have the needed data available. */
if (pwhash) {
if (hash_nt_password_hash(password, password_hash_hash))
return -1;
} else {
if (nt_password_hash(password, password_len, password_hash) ||
hash_nt_password_hash(password_hash, password_hash_hash))
return -1;
}
get_master_key(password_hash_hash, nt_response, master_key);
wpa_hexdump_key(MSG_DEBUG, "MSCHAPV2: Master Key",
master_key, MSCHAPV2_MASTER_KEY_LEN);
return 0;
}
int mschapv2_verify_auth_response(const u8 *auth_response,
const u8 *buf, size_t buf_len)
{
u8 recv_response[MSCHAPV2_AUTH_RESPONSE_LEN];
if (buf_len < 2 + 2 * MSCHAPV2_AUTH_RESPONSE_LEN ||
buf[0] != 'S' || buf[1] != '=' ||
hexstr2bin((char *) (buf + 2), recv_response,
MSCHAPV2_AUTH_RESPONSE_LEN) ||
os_memcmp(auth_response, recv_response,
MSCHAPV2_AUTH_RESPONSE_LEN) != 0)
return -1;
return 0;
}

View file

@ -0,0 +1,34 @@
/*
* MSCHAPV2 (RFC 2759)
* Copyright (c) 2004-2008, Jouni Malinen <j@w1.fi>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* Alternatively, this software may be distributed under the terms of BSD
* license.
*
* See README and COPYING for more details.
*/
#ifndef MSCHAPV2_H
#define MSCHAPV2_H
#define MSCHAPV2_CHAL_LEN 16
#define MSCHAPV2_NT_RESPONSE_LEN 24
#define MSCHAPV2_AUTH_RESPONSE_LEN 20
#define MSCHAPV2_MASTER_KEY_LEN 16
const u8 * mschapv2_remove_domain(const u8 *username, size_t *len);
int mschapv2_derive_response(const u8 *username, size_t username_len,
const u8 *password, size_t password_len,
int pwhash,
const u8 *auth_challenge,
const u8 *peer_challenge,
u8 *nt_response, u8 *auth_response,
u8 *master_key);
int mschapv2_verify_auth_response(const u8 *auth_response,
const u8 *buf, size_t buf_len);
#endif /* MSCHAPV2_H */

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,42 @@
/*
* EAP-TNC - TNCC (IF-IMC and IF-TNCCS)
* Copyright (c) 2007, Jouni Malinen <j@w1.fi>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* Alternatively, this software may be distributed under the terms of BSD
* license.
*
* See README and COPYING for more details.
*/
#ifndef TNCC_H
#define TNCC_H
struct tncc_data;
struct tncc_data * tncc_init(void);
void tncc_deinit(struct tncc_data *tncc);
void tncc_init_connection(struct tncc_data *tncc);
size_t tncc_total_send_len(struct tncc_data *tncc);
u8 * tncc_copy_send_buf(struct tncc_data *tncc, u8 *pos);
char * tncc_if_tnccs_start(struct tncc_data *tncc);
char * tncc_if_tnccs_end(void);
enum tncc_process_res {
TNCCS_PROCESS_ERROR = -1,
TNCCS_PROCESS_OK_NO_RECOMMENDATION = 0,
TNCCS_RECOMMENDATION_ERROR,
TNCCS_RECOMMENDATION_ALLOW,
TNCCS_RECOMMENDATION_NONE,
TNCCS_RECOMMENDATION_ISOLATE
};
enum tncc_process_res tncc_process_if_tnccs(struct tncc_data *tncc,
const u8 *msg, size_t len);
struct wpabuf * tncc_process_soh_request(int ver, const u8 *data, size_t len);
#endif /* TNCC_H */