mirror of
https://github.com/lwfinger/rtl8188eu.git
synced 2025-07-06 05:16:00 +00:00
rtl8188eu: Add hostapd and configurationb file
Signed-off-by: Larry Finger <Larry.Finger@lwfinger.net>
This commit is contained in:
parent
7638be5ee7
commit
e275d5ba1f
477 changed files with 220879 additions and 0 deletions
11
hostapd-0.8/src/eap_peer/Makefile
Normal file
11
hostapd-0.8/src/eap_peer/Makefile
Normal 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
|
2159
hostapd-0.8/src/eap_peer/eap.c
Normal file
2159
hostapd-0.8/src/eap_peer/eap.c
Normal file
File diff suppressed because it is too large
Load diff
292
hostapd-0.8/src/eap_peer/eap.h
Normal file
292
hostapd-0.8/src/eap_peer/eap.h
Normal 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 */
|
1389
hostapd-0.8/src/eap_peer/eap_aka.c
Normal file
1389
hostapd-0.8/src/eap_peer/eap_aka.c
Normal file
File diff suppressed because it is too large
Load diff
669
hostapd-0.8/src/eap_peer/eap_config.h
Normal file
669
hostapd-0.8/src/eap_peer/eap_config.h
Normal 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 */
|
1712
hostapd-0.8/src/eap_peer/eap_fast.c
Normal file
1712
hostapd-0.8/src/eap_peer/eap_fast.c
Normal file
File diff suppressed because it is too large
Load diff
923
hostapd-0.8/src/eap_peer/eap_fast_pac.c
Normal file
923
hostapd-0.8/src/eap_peer/eap_fast_pac.c
Normal 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;
|
||||
}
|
56
hostapd-0.8/src/eap_peer/eap_fast_pac.h
Normal file
56
hostapd-0.8/src/eap_peer/eap_fast_pac.h
Normal 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 */
|
738
hostapd-0.8/src/eap_peer/eap_gpsk.c
Normal file
738
hostapd-0.8/src/eap_peer/eap_gpsk.c
Normal 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;
|
||||
}
|
151
hostapd-0.8/src/eap_peer/eap_gtc.c
Normal file
151
hostapd-0.8/src/eap_peer/eap_gtc.c
Normal 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;
|
||||
}
|
356
hostapd-0.8/src/eap_peer/eap_i.h
Normal file
356
hostapd-0.8/src/eap_peer/eap_i.h
Normal 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 */
|
506
hostapd-0.8/src/eap_peer/eap_ikev2.c
Normal file
506
hostapd-0.8/src/eap_peer/eap_ikev2.c
Normal 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;
|
||||
}
|
416
hostapd-0.8/src/eap_peer/eap_leap.c
Normal file
416
hostapd-0.8/src/eap_peer/eap_leap.c
Normal 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;
|
||||
}
|
120
hostapd-0.8/src/eap_peer/eap_md5.c
Normal file
120
hostapd-0.8/src/eap_peer/eap_md5.c
Normal 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;
|
||||
}
|
373
hostapd-0.8/src/eap_peer/eap_methods.c
Normal file
373
hostapd-0.8/src/eap_peer/eap_methods.c
Normal 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 */
|
||||
}
|
||||
}
|
114
hostapd-0.8/src/eap_peer/eap_methods.h
Normal file
114
hostapd-0.8/src/eap_peer/eap_methods.h
Normal 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 */
|
883
hostapd-0.8/src/eap_peer/eap_mschapv2.c
Normal file
883
hostapd-0.8/src/eap_peer/eap_mschapv2.c
Normal 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;
|
||||
}
|
107
hostapd-0.8/src/eap_peer/eap_otp.c
Normal file
107
hostapd-0.8/src/eap_peer/eap_otp.c
Normal 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;
|
||||
}
|
531
hostapd-0.8/src/eap_peer/eap_pax.c
Normal file
531
hostapd-0.8/src/eap_peer/eap_pax.c
Normal 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;
|
||||
}
|
1288
hostapd-0.8/src/eap_peer/eap_peap.c
Normal file
1288
hostapd-0.8/src/eap_peer/eap_peap.c
Normal file
File diff suppressed because it is too large
Load diff
483
hostapd-0.8/src/eap_peer/eap_psk.c
Normal file
483
hostapd-0.8/src/eap_peer/eap_psk.c
Normal 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;
|
||||
}
|
744
hostapd-0.8/src/eap_peer/eap_pwd.c
Normal file
744
hostapd-0.8/src/eap_peer/eap_pwd.c
Normal 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;
|
||||
}
|
500
hostapd-0.8/src/eap_peer/eap_sake.c
Normal file
500
hostapd-0.8/src/eap_peer/eap_sake.c
Normal 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;
|
||||
}
|
1101
hostapd-0.8/src/eap_peer/eap_sim.c
Normal file
1101
hostapd-0.8/src/eap_peer/eap_sim.c
Normal file
File diff suppressed because it is too large
Load diff
289
hostapd-0.8/src/eap_peer/eap_tls.c
Normal file
289
hostapd-0.8/src/eap_peer/eap_tls.c
Normal 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;
|
||||
}
|
1021
hostapd-0.8/src/eap_peer/eap_tls_common.c
Normal file
1021
hostapd-0.8/src/eap_peer/eap_tls_common.c
Normal file
File diff suppressed because it is too large
Load diff
126
hostapd-0.8/src/eap_peer/eap_tls_common.h
Normal file
126
hostapd-0.8/src/eap_peer/eap_tls_common.h
Normal 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 */
|
434
hostapd-0.8/src/eap_peer/eap_tnc.c
Normal file
434
hostapd-0.8/src/eap_peer/eap_tnc.c
Normal 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;
|
||||
}
|
1986
hostapd-0.8/src/eap_peer/eap_ttls.c
Normal file
1986
hostapd-0.8/src/eap_peer/eap_ttls.c
Normal file
File diff suppressed because it is too large
Load diff
195
hostapd-0.8/src/eap_peer/eap_vendor_test.c
Normal file
195
hostapd-0.8/src/eap_peer/eap_vendor_test.c
Normal 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;
|
||||
}
|
553
hostapd-0.8/src/eap_peer/eap_wsc.c
Normal file
553
hostapd-0.8/src/eap_peer/eap_wsc.c
Normal 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;
|
||||
}
|
1304
hostapd-0.8/src/eap_peer/ikev2.c
Normal file
1304
hostapd-0.8/src/eap_peer/ikev2.c
Normal file
File diff suppressed because it is too large
Load diff
65
hostapd-0.8/src/eap_peer/ikev2.h
Normal file
65
hostapd-0.8/src/eap_peer/ikev2.h
Normal 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 */
|
123
hostapd-0.8/src/eap_peer/mschapv2.c
Normal file
123
hostapd-0.8/src/eap_peer/mschapv2.c
Normal 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;
|
||||
}
|
34
hostapd-0.8/src/eap_peer/mschapv2.h
Normal file
34
hostapd-0.8/src/eap_peer/mschapv2.h
Normal 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 */
|
1369
hostapd-0.8/src/eap_peer/tncc.c
Normal file
1369
hostapd-0.8/src/eap_peer/tncc.c
Normal file
File diff suppressed because it is too large
Load diff
42
hostapd-0.8/src/eap_peer/tncc.h
Normal file
42
hostapd-0.8/src/eap_peer/tncc.h
Normal 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 */
|
Loading…
Add table
Add a link
Reference in a new issue