#ifndef ESP_WPA2_H
#define ESP_WPA2_H
+#include <stdbool.h>
+
#include "esp_err.h"
#include "esp_wifi_crypto_types.h"
* @attention 1. The API only passes the parameter password to the global pointer variable in wpa2 enterprise module.
* @attention 2. The new password is used to substitute the old password when eap-mschapv2 failure request message with error code ERROR_PASSWD_EXPIRED is received.
*
- * @param password: point to address where stores the password;
+ * @param new_password: point to address where stores the password;
* @param len: length of password
*
* @return
* - ESP_ERR_NO_MEM: fail(internal memory malloc fail)
*/
-esp_err_t esp_wifi_sta_wpa2_ent_set_new_password(const unsigned char *password, int len);
+esp_err_t esp_wifi_sta_wpa2_ent_set_new_password(const unsigned char *new_password, int len);
/**
* @brief Clear new password for MSCHAPv2 method..
* @attention 2. The ca_cert should be zero terminated.
*
* @param ca_cert: point to address where stores the CA certificate;
- * @param len: length of ca_cert
+ * @param ca_cert_len: length of ca_cert
*
* @return
* - ESP_OK: succeed
*/
-esp_err_t esp_wifi_sta_wpa2_ent_set_ca_cert(const unsigned char *ca_cert, int len);
+esp_err_t esp_wifi_sta_wpa2_ent_set_ca_cert(const unsigned char *ca_cert, int ca_cert_len);
/**
* @brief Clear CA certificate for PEAP/TTLS method.
-Subproject commit f46327a4c68b1e4e6a3fb3febd3ba3312df16ff1
+Subproject commit 92a091649f7c8476580177e2a769b15d9c4c191f
COMPONENT_ADD_INCLUDEDIRS := include port/include ../esp32/include
-COMPONENT_SRCDIRS := src/crypto port src/fast_crypto
+COMPONENT_SRCDIRS := src/crypto port src/fast_crypto src/wpa2/eap_peer src/wpa2/tls src/wpa2/utils src/wps
-CFLAGS += -DEMBEDDED_SUPP -D__ets__ -Wno-strict-aliasing
+CFLAGS += -DEMBEDDED_SUPP -DIEEE8021X_EAPOL -DEAP_PEER_METHOD -DEAP_MSCHAPv2 -DEAP_TTLS -DEAP_TLS -DEAP_PEAP -DUSE_WPA2_TASK -DCONFIG_WPS2 -DCONFIG_WPS_PIN -DUSE_WPS_TASK -DESPRESSIF_USE -DESP32_WORKAROUND -D__ets__ -Wno-strict-aliasing
#ifndef LIST_H
#define LIST_H
+#include <stddef.h>
+
/**
* struct dl_list - Doubly-linked list
*/
#ifndef WPA_H
#define WPA_H
-#include "c_types.h"
-#include "os_type.h"
+#include "rom/ets_sys.h"
#include "common.h"
-#include "ets_sys.h"
#include "wpa/defs.h"
#include "wpa/wpa_common.h"
-//#include "net80211/ieee80211_var.h"
-//#include "net80211/ieee80211_node.h"
#define WPA_SM_STATE(_sm) ((_sm)->wpa_state)
u8 pmk[PMK_LEN];
size_t pmk_len;
-// char *passphrase; //wlan password
-// u8 *ssid; //wlan network name
-// size_t ssid_len;
-
struct wpa_ptk ptk, tptk;
int ptk_set, tptk_set;
u8 snonce[WPA_NONCE_LEN];
int rx_replay_counter_set;
u8 request_counter[WPA_REPLAY_COUNTER_LEN];
-// void *network_ctx;
-
unsigned int pairwise_cipher;
unsigned int group_cipher;
unsigned int key_mgmt;
int rsn_enabled; /* Whether RSN is enabled in configuration */
int countermeasures; /*TKIP countermeasures state flag, 1:in countermeasures state*/
- os_timer_t cm_timer;
+ ETSTimer cm_timer;
u8 *assoc_wpa_ie; /* Own WPA/RSN IE from (Re)AssocReq */
size_t assoc_wpa_ie_len;
struct install_key install_ptk;
struct install_key install_gtk;
int key_entry_valid; //present current avaliable entry for bssid, for pairkey:0,5,10,15,20, gtk: pairkey_no+i (i:1~4)
-
-// char *msg; //send eapol msg buff
-// size_t msg_len; //msg length:6 + sizeof(eth) + data_len
-// struct netif *ifp;
struct pbuf *pb;
void (* sendto) (struct pbuf *pb);
- void (*config_assoc_ie) (uint8 proto, u8 *assoc_buf, u32 assoc_wpa_ie_len);
- void (*install_ppkey) (enum wpa_alg alg, uint8 *addr, int key_idx, int set_tx,
- uint8 *seq, size_t seq_len, uint8 *key, size_t key_len, int key_entry_valid);
- void (*wpa_deauthenticate)(uint8 reason_code);
+ void (*config_assoc_ie) (u8 proto, u8 *assoc_buf, u32 assoc_wpa_ie_len);
+ void (*install_ppkey) (enum wpa_alg alg, u8 *addr, int key_idx, int set_tx,
+ u8 *seq, unsigned int seq_len, u8 *key, unsigned int key_len, int key_entry_valid);
+ void (*wpa_deauthenticate)(u8 reason_code);
void (*wpa_neg_complete)();
struct wpa_gtk_data gd; //used for calllback save param
- uint16 key_info; //used for txcallback param
+ u16 key_info; //used for txcallback param
};
struct l2_ethhdr {
#define KEYENTRY_TABLE_MAP(key_entry_valid) ((key_entry_valid)%5)
-void pp_michael_mic_failure(uint16 isunicast);
-
void wpa_sm_set_state(enum wpa_states state);
+char * dup_binstr(const void *src, size_t len);
+
#endif /* WPA_H */
#ifndef WPA_DEBUG_H
#define WPA_DEBUG_H
+#include "wpabuf.h"
+#include "esp_log.h"
+#ifdef ESPRESSIF_USE
+
+#define TAG "wpa"
+
+#define MSG_ERROR ESP_LOG_ERROR
+#define MSG_WARNING ESP_LOG_WARN
+#define MSG_INFO ESP_LOG_INFO
+#define MSG_DEBUG ESP_LOG_DEBUG
+#define MSG_MSGDUMP ESP_LOG_VERBOSE
+
+#else
enum { MSG_MSGDUMP, MSG_DEBUG, MSG_INFO, MSG_WARNING, MSG_ERROR };
+#endif
/** EAP authentication completed successfully */
#define WPA_EVENT_EAP_SUCCESS "CTRL-EVENT-EAP-SUCCESS "
*
* Note: New line '\n' is added to the end of the text when printing to stdout.
*/
-//#define DEBUG_PRINT
-//#define MSG_PRINT
+#define DEBUG_PRINT
+#define MSG_PRINT
/**
* wpa_hexdump - conditional hex dump
* configuration. The contents of buf is printed out has hex dump.
*/
#ifdef DEBUG_PRINT
-#define wpa_printf(level,fmt, args...) ets_printf(fmt,## args)
+#define wpa_printf(level,fmt, args...) ESP_LOG_LEVEL_LOCAL(level, TAG, fmt, ##args)
static inline void wpa_hexdump_ascii(int level, const char *title, const u8 *buf, size_t len)
{
#define EAP_H
#include "wpa/defs.h"
-#include "eap/eap_defs.h"
+#include "wpa2/eap_peer/eap_defs.h"
struct eap_sm;
struct eap_method_type {
int vendor;
- u32 method;
+ EapType method;
};
+u8 *g_wpa_anonymous_identity;
+int g_wpa_anonymous_identity_len;
+u8 *g_wpa_username;
+int g_wpa_username_len;
+const u8 *g_wpa_client_cert;
+int g_wpa_client_cert_len;
+const u8 *g_wpa_private_key;
+int g_wpa_private_key_len;
+const u8 *g_wpa_private_key_passwd;
+int g_wpa_private_key_passwd_len;
+
+const u8 *g_wpa_ca_cert;
+int g_wpa_ca_cert_len;
+
+u8 *g_wpa_password;
+int g_wpa_password_len;
+
+u8 *g_wpa_new_password;
+int g_wpa_new_password_len;
+
const u8 * eap_get_eapKeyData(struct eap_sm *sm, size_t *len);
+void eap_deinit_prev_method(struct eap_sm *sm, const char *txt);
+struct wpabuf * eap_sm_build_nak(struct eap_sm *sm, EapType type, u8 id);
+int eap_peer_blob_init(struct eap_sm *sm);
+void eap_peer_blob_deinit(struct eap_sm *sm);
+int eap_peer_config_init(
+ struct eap_sm *sm, u8 *private_key_passwd,
+ int private_key_passwd_len);
+void eap_peer_config_deinit(struct eap_sm *sm);
+void eap_sm_abort(struct eap_sm *sm);
+int eap_peer_register_methods(void);
#endif /* EAP_H */
*/
size_t identity_len;
+ u8 *anonymous_identity;
+
+ size_t anonymous_identity_len;
+
/**
* password - Password string for EAP
*
*/
u8 *private_key_passwd;
+ /**
+ * Phase 2
+ */
+ u8 *ca_cert2;
+
+ u8 *ca_path2;
+
+ u8 *client_cert2;
+
+ u8 *private_key2;
+
+ u8 *private_key2_password;
+
+ /**
+ * eap_methods - Allowed EAP methods
+ */
+ struct eap_method_type *eap_methods;
+
+
char *phase1;
+ char *phase2;
+
/**
* pin - PIN for USIM, GSM SIM, and smartcards
*
*/
char *pin;
+ int mschapv2_retry;
+ u8 *new_password;
+ size_t new_password_len;
+
/**
* fragment_size - Maximum EAP fragment size in bytes (default 1398)
*
/**
* data - Pointer to binary data
*/
- u8 *data;
+ const u8 *data;
/**
* len - Length of binary data
#include "eap.h"
#include "eap_common.h"
#include "eap_config.h"
+#include "esp_wpa2.h"
/* RFC 4137 - EAP Peer state machine */
Boolean allowNotifications;
};
+struct eap_sm;
+
+struct eap_method {
+ /**
+ * vendor -EAP Vendor-ID
+ */
+ int vendor;
+
+ /**
+ * method - EAP type number
+ */
+ EapType method;
+
+ /**
+ * name - Name of the method (e.g., "TLS")
+ */
+ const char *name;
+
+ struct eap_method *next;
+
+ void * (*init)(struct eap_sm *sm);
+ void (*deinit)(struct eap_sm *sm, void *priv);
+ struct wpabuf * (*process)(struct eap_sm *sm, void *priv,
+ struct eap_method_ret *ret,
+ const struct wpabuf *reqData);
+ bool (*isKeyAvailable)(struct eap_sm *sm, void *priv);
+ u8 * (*getKey)(struct eap_sm *sm, void *priv, size_t *len);
+ int (*get_status)(struct eap_sm *sm, void *priv, char *buf,
+ size_t buflen, int verbose);
+ const u8 * (*get_identity)(struct eap_sm *sm, void *priv, size_t *len);
+ void (*free)(struct eap_method *method);
+ bool (*has_reauth_data)(struct eap_sm *sm, void *priv);
+ void (*deinit_for_reauth)(struct eap_sm *sm, void *priv);
+ void * (*init_for_reauth)(struct eap_sm *sm, void *priv);
+ u8 * (*getSessionId)(struct eap_sm *sm, void *priv, size_t *len);
+};
+
#define CLIENT_CERT_NAME "CLC"
#define CA_CERT_NAME "CAC"
#define PRIVATE_KEY_NAME "PVK"
#define BLOB_NAME_LEN 3
-#define BLOB_NUM 2
+#define BLOB_NUM 3
/**
* struct eap_sm - EAP state machine data
u8 wpa2_sig_cnt[SIG_WPA2_NUM];
#endif
u8 finish_state;
+
+ int init_phase2;
+ bool peap_done;
+
+ u8 *eapKeyData;
+ size_t eapKeyDataLen;
+ struct wpabuf *lastRespData;
+ const struct eap_method *m;
};
+wpa2_crypto_funcs_t wpa2_crypto_funcs;
+
+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);
struct eap_peer_config * eap_get_config(struct eap_sm *sm);
const struct wpa_config_blob * eap_get_config_blob(struct eap_sm *sm, const char *name);
+bool wifi_sta_get_enterprise_disable_time_check(void);
+
+struct wpabuf * eap_sm_build_identity_resp(struct eap_sm *sm, u8 id, int encrypted);
#endif /* EAP_I_H */
--- /dev/null
+/*
+ * EAP peer: Method registration
+ * Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef EAP_METHODS_H
+#define EAP_METHODS_H
+
+#include "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);
+
+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);
+
+struct eap_method * eap_peer_method_alloc(int verdor, EapType method,
+ const char *name);
+
+void eap_peer_method_free(struct eap_method *method);
+int eap_peer_method_register(struct eap_method *method);
+
+void eap_peer_unregister_methods(void);
+
+//int eap_peer_md5_register(void);
+int eap_peer_tls_register(void);
+int eap_peer_peap_register(void);
+int eap_peer_ttls_register(void);
+int eap_peer_mschapv2_register(void);
+
+#endif /* EAP_METHODS_H */
--- /dev/null
+/*\r
+ * EAP-PEAP common routines\r
+ * Copyright (c) 2008-2011, Jouni Malinen <j@w1.fi>\r
+ *\r
+ * This software may be distributed under the terms of the BSD license.\r
+ * See README for more details.\r
+ */\r
+\r
+#ifndef EAP_PEAP_COMMON_H\r
+#define EAP_PEAP_COMMON_H\r
+\r
+int peap_prfplus(int version, const u8 *key, size_t key_len,\r
+ const char *label, const u8 *seed, size_t seed_len,\r
+ u8 *buf, size_t buf_len);\r
+\r
+#endif /* EAP_PEAP_COMMON_H */\r
--- /dev/null
+/*\r
+ * EAP-TLV definitions (draft-josefsson-pppext-eap-tls-eap-10.txt)\r
+ * Copyright (c) 2004-2008, Jouni Malinen <j@w1.fi>\r
+ *\r
+ * This software may be distributed under the terms of the BSD license.\r
+ * See README for more details.\r
+ */\r
+\r
+#ifndef EAP_TLV_COMMON_H\r
+#define EAP_TLV_COMMON_H\r
+\r
+/* EAP-TLV TLVs (draft-josefsson-ppext-eap-tls-eap-10.txt) */\r
+#define EAP_TLV_RESULT_TLV 3 /* Acknowledged Result */\r
+#define EAP_TLV_NAK_TLV 4\r
+#define EAP_TLV_ERROR_CODE_TLV 5\r
+#define EAP_TLV_CONNECTION_BINDING_TLV 6\r
+#define EAP_TLV_VENDOR_SPECIFIC_TLV 7\r
+#define EAP_TLV_URI_TLV 8\r
+#define EAP_TLV_EAP_PAYLOAD_TLV 9\r
+#define EAP_TLV_INTERMEDIATE_RESULT_TLV 10\r
+#define EAP_TLV_PAC_TLV 11 /* RFC 5422, Section 4.2 */\r
+#define EAP_TLV_CRYPTO_BINDING_TLV 12\r
+#define EAP_TLV_CALLING_STATION_ID_TLV 13\r
+#define EAP_TLV_CALLED_STATION_ID_TLV 14\r
+#define EAP_TLV_NAS_PORT_TYPE_TLV 15\r
+#define EAP_TLV_SERVER_IDENTIFIER_TLV 16\r
+#define EAP_TLV_IDENTITY_TYPE_TLV 17\r
+#define EAP_TLV_SERVER_TRUSTED_ROOT_TLV 18\r
+#define EAP_TLV_REQUEST_ACTION_TLV 19\r
+#define EAP_TLV_PKCS7_TLV 20\r
+\r
+#define EAP_TLV_RESULT_SUCCESS 1\r
+#define EAP_TLV_RESULT_FAILURE 2\r
+\r
+#define EAP_TLV_TYPE_MANDATORY 0x8000\r
+#define EAP_TLV_TYPE_MASK 0x3fff\r
+\r
+#ifdef _MSC_VER\r
+#pragma pack(push, 1)\r
+#endif /* _MSC_VER */\r
+\r
+struct eap_tlv_hdr {\r
+ be16 tlv_type;\r
+ be16 length;\r
+} STRUCT_PACKED;\r
+\r
+struct eap_tlv_nak_tlv {\r
+ be16 tlv_type;\r
+ be16 length;\r
+ be32 vendor_id;\r
+ be16 nak_type;\r
+} STRUCT_PACKED;\r
+\r
+struct eap_tlv_result_tlv {\r
+ be16 tlv_type;\r
+ be16 length;\r
+ be16 status;\r
+} STRUCT_PACKED;\r
+\r
+/* RFC 4851, Section 4.2.7 - Intermediate-Result TLV */\r
+struct eap_tlv_intermediate_result_tlv {\r
+ be16 tlv_type;\r
+ be16 length;\r
+ be16 status;\r
+ /* Followed by optional TLVs */\r
+} STRUCT_PACKED;\r
+\r
+/* RFC 4851, Section 4.2.8 - Crypto-Binding TLV */\r
+struct eap_tlv_crypto_binding_tlv {\r
+ be16 tlv_type;\r
+ be16 length;\r
+ u8 reserved;\r
+ u8 version;\r
+ u8 received_version;\r
+ u8 subtype;\r
+ u8 nonce[32];\r
+ u8 compound_mac[20];\r
+} STRUCT_PACKED;\r
+\r
+struct eap_tlv_pac_ack_tlv {\r
+ be16 tlv_type;\r
+ be16 length;\r
+ be16 pac_type;\r
+ be16 pac_len;\r
+ be16 result;\r
+} STRUCT_PACKED;\r
+\r
+/* RFC 4851, Section 4.2.9 - Request-Action TLV */\r
+struct eap_tlv_request_action_tlv {\r
+ be16 tlv_type;\r
+ be16 length;\r
+ be16 action;\r
+} STRUCT_PACKED;\r
+\r
+/* RFC 5422, Section 4.2.6 - PAC-Type TLV */\r
+struct eap_tlv_pac_type_tlv {\r
+ be16 tlv_type; /* PAC_TYPE_PAC_TYPE */\r
+ be16 length;\r
+ be16 pac_type;\r
+} STRUCT_PACKED;\r
+\r
+#ifdef _MSC_VER\r
+#pragma pack(pop)\r
+#endif /* _MSC_VER */\r
+\r
+#define EAP_TLV_CRYPTO_BINDING_SUBTYPE_REQUEST 0\r
+#define EAP_TLV_CRYPTO_BINDING_SUBTYPE_RESPONSE 1\r
+\r
+#define EAP_TLV_ACTION_PROCESS_TLV 1\r
+#define EAP_TLV_ACTION_NEGOTIATE_EAP 2\r
+\r
+#endif /* EAP_TLV_COMMON_H */\r
--- /dev/null
+/*\r
+ * EAP server/peer: EAP-TTLS (RFC 5281)\r
+ * Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi>\r
+ *\r
+ * This software may be distributed under the terms of the BSD license.\r
+ * See README for more details.\r
+ */\r
+\r
+#ifndef EAP_TTLS_H\r
+#define EAP_TTLS_H\r
+\r
+struct ttls_avp {\r
+ be32 avp_code;\r
+ be32 avp_length; /* 8-bit flags, 24-bit length;\r
+ * length includes AVP header */\r
+ /* optional 32-bit Vendor-ID */\r
+ /* Data */\r
+};\r
+\r
+struct ttls_avp_vendor {\r
+ be32 avp_code;\r
+ be32 avp_length; /* 8-bit flags, 24-bit length;\r
+ * length includes AVP header */\r
+ be32 vendor_id;\r
+ /* Data */\r
+};\r
+\r
+#define AVP_FLAGS_VENDOR 0x80\r
+#define AVP_FLAGS_MANDATORY 0x40\r
+\r
+#define AVP_PAD(start, pos) \\r
+do { \\r
+ int __pad; \\r
+ __pad = (4 - (((pos) - (start)) & 3)) & 3; \\r
+ os_memset((pos), 0, __pad); \\r
+ pos += __pad; \\r
+} while (0)\r
+\r
+\r
+/* RFC 2865 */\r
+#define RADIUS_ATTR_USER_NAME 1\r
+#define RADIUS_ATTR_USER_PASSWORD 2\r
+#define RADIUS_ATTR_CHAP_PASSWORD 3\r
+#define RADIUS_ATTR_REPLY_MESSAGE 18\r
+#define RADIUS_ATTR_CHAP_CHALLENGE 60\r
+#define RADIUS_ATTR_EAP_MESSAGE 79\r
+\r
+/* RFC 2548 */\r
+#define RADIUS_VENDOR_ID_MICROSOFT 311\r
+#define RADIUS_ATTR_MS_CHAP_RESPONSE 1\r
+#define RADIUS_ATTR_MS_CHAP_ERROR 2\r
+#define RADIUS_ATTR_MS_CHAP_NT_ENC_PW 6\r
+#define RADIUS_ATTR_MS_CHAP_CHALLENGE 11\r
+#define RADIUS_ATTR_MS_CHAP2_RESPONSE 25\r
+#define RADIUS_ATTR_MS_CHAP2_SUCCESS 26\r
+#define RADIUS_ATTR_MS_CHAP2_CPW 27\r
+\r
+#define EAP_TTLS_MSCHAPV2_CHALLENGE_LEN 16\r
+#define EAP_TTLS_MSCHAPV2_RESPONSE_LEN 50\r
+#define EAP_TTLS_MSCHAP_CHALLENGE_LEN 8\r
+#define EAP_TTLS_MSCHAP_RESPONSE_LEN 50\r
+#define EAP_TTLS_CHAP_CHALLENGE_LEN 16\r
+#define EAP_TTLS_CHAP_PASSWORD_LEN 16\r
+\r
+#endif /* EAP_TTLS_H */\r
--- /dev/null
+/*
+ * MSCHAPV2
+ */
+
+
+#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 */
* If CONFIG_INTERNAL_LIBTOMMATH is defined, bignum.c includes this
* libtommath.c file instead of using the external LibTomMath library.
*/
-#include "c_types.h"
#include "os.h"
#include "stdarg.h"
/* reverse an array, used for radix code */
-static void ICACHE_FLASH_ATTR
+static void
bn_reverse (unsigned char *s, int len)
{
int ix, iy;
/* low level addition, based on HAC pp.594, Algorithm 14.7 */
-static int ICACHE_FLASH_ATTR
+static int
s_mp_add (mp_int * a, mp_int * b, mp_int * c)
{
mp_int *x;
/* low level subtraction (assumes |a| > |b|), HAC pp.595 Algorithm 14.9 */
-static int ICACHE_FLASH_ATTR
+static int
s_mp_sub (mp_int * a, mp_int * b, mp_int * c)
{
int olduse, res, min, max;
/* init a new mp_int */
-static int ICACHE_FLASH_ATTR
+static int
mp_init (mp_int * a)
{
int i;
/* clear one (frees) */
-static void ICACHE_FLASH_ATTR
+static void
mp_clear (mp_int * a)
{
int i;
/* high level addition (handles signs) */
-static int ICACHE_FLASH_ATTR
+static int
mp_add (mp_int * a, mp_int * b, mp_int * c)
{
int sa, sb, res;
/* high level subtraction (handles signs) */
-static int ICACHE_FLASH_ATTR
+static int
mp_sub (mp_int * a, mp_int * b, mp_int * c)
{
int sa, sb, res;
/* high level multiplication (handles sign) */
-static int ICACHE_FLASH_ATTR
+static int
mp_mul (mp_int * a, mp_int * b, mp_int * c)
{
int res, neg;
/* d = a * b (mod c) */
-static int ICACHE_FLASH_ATTR
+static int
mp_mulmod (mp_int * a, mp_int * b, mp_int * c, mp_int * d)
{
int res;
/* c = a mod b, 0 <= c < b */
-static int ICACHE_FLASH_ATTR
+static int
mp_mod (mp_int * a, mp_int * b, mp_int * c)
{
mp_int t;
* embedded in the normal function but that wasted a lot of stack space
* for nothing (since 99% of the time the Montgomery code would be called)
*/
-static int ICACHE_FLASH_ATTR
+static int
mp_exptmod (mp_int * G, mp_int * X, mp_int * P, mp_int * Y)
{
- int dr;
+#if defined(BN_MP_DR_IS_MODULUS_C)||defined(BN_MP_REDUCE_IS_2K_C)||defined(BN_MP_EXPTMOD_FAST_C)
+ int dr = 0;
+#endif
/* modulus P must be positive */
if (P->sign == MP_NEG) {
#ifdef BN_MP_DR_IS_MODULUS_C
/* is it a DR modulus? */
dr = mp_dr_is_modulus(P);
-#else
- /* default to no */
- dr = 0;
#endif
#ifdef BN_MP_REDUCE_IS_2K_C
/* compare two ints (signed)*/
-static int ICACHE_FLASH_ATTR
+static int
mp_cmp (mp_int * a, mp_int * b)
{
/* compare based on sign */
/* compare a digit */
-static int ICACHE_FLASH_ATTR
+static int
mp_cmp_d(mp_int * a, mp_digit b)
{
/* compare based on sign */
#ifndef LTM_NO_NEG_EXP
/* hac 14.61, pp608 */
-static int ICACHE_FLASH_ATTR
+static int
mp_invmod (mp_int * a, mp_int * b, mp_int * c)
{
/* b cannot be negative */
/* get the size for an unsigned equivalent */
-static int ICACHE_FLASH_ATTR
+static int
mp_unsigned_bin_size (mp_int * a)
{
int size = mp_count_bits (a);
#ifndef LTM_NO_NEG_EXP
/* hac 14.61, pp608 */
-static int ICACHE_FLASH_ATTR
+static int
mp_invmod_slow (mp_int * a, mp_int * b, mp_int * c)
{
mp_int x, y, u, v, A, B, C, D;
/* compare maginitude of two ints (unsigned) */
-static int ICACHE_FLASH_ATTR
+static int
mp_cmp_mag (mp_int * a, mp_int * b)
{
int n;
/* reads a unsigned char array, assumes the msb is stored first [big endian] */
-static int ICACHE_FLASH_ATTR
+static int
mp_read_unsigned_bin (mp_int * a, const unsigned char *b, int c)
{
int res;
/* store in unsigned [big endian] format */
-static int ICACHE_FLASH_ATTR
+static int
mp_to_unsigned_bin (mp_int * a, unsigned char *b)
{
int x, res;
/* shift right by a certain bit count (store quotient in c, optional remainder in d) */
-static int ICACHE_FLASH_ATTR
+static int
mp_div_2d (mp_int * a, int b, mp_int * c, mp_int * d)
{
mp_digit D, r, rr;
}
-static int ICACHE_FLASH_ATTR
+static int
mp_init_copy (mp_int * a, mp_int * b)
{
int res;
/* set to zero */
-static void ICACHE_FLASH_ATTR
+static void
mp_zero (mp_int * a)
{
int n;
/* copy, b = a */
-static int ICACHE_FLASH_ATTR
+static int
mp_copy (mp_int * a, mp_int * b)
{
int res, n;
/* shift right a certain amount of digits */
-static void ICACHE_FLASH_ATTR
+static void
mp_rshd (mp_int * a, int b)
{
int x;
/* swap the elements of two integers, for cases where you can't simply swap the
* mp_int pointers around
*/
-static void ICACHE_FLASH_ATTR
+static void
mp_exch (mp_int * a, mp_int * b)
{
mp_int t;
* Typically very fast. Also fixes the sign if there
* are no more leading digits
*/
-static void ICACHE_FLASH_ATTR
+static void
mp_clamp (mp_int * a)
{
/* decrease used while the most significant digit is
/* grow as required */
-static int ICACHE_FLASH_ATTR
+static int
mp_grow (mp_int * a, int size)
{
int i;
*
* Simple function copies the input and fixes the sign to positive
*/
-static int ICACHE_FLASH_ATTR
+static int
mp_abs (mp_int * a, mp_int * b)
{
int res;
/* set to a digit */
-static void ICACHE_FLASH_ATTR
+static void
mp_set (mp_int * a, mp_digit b)
{
mp_zero (a);
#ifndef LTM_NO_NEG_EXP
/* b = a/2 */
-static int ICACHE_FLASH_ATTR
+static int
mp_div_2(mp_int * a, mp_int * b)
{
int x, res, oldused;
/* shift left by a certain bit count */
-static int ICACHE_FLASH_ATTR
+static int
mp_mul_2d (mp_int * a, int b, mp_int * c)
{
mp_digit d;
#ifdef BN_MP_INIT_MULTI_C
-static int ICACHE_FLASH_ATTR
+static int
mp_init_multi(mp_int *mp, ...)
{
mp_err res = MP_OKAY; /* Assume ok until proven otherwise */
#ifdef BN_MP_CLEAR_MULTI_C
-static void ICACHE_FLASH_ATTR
+static void
mp_clear_multi(mp_int *mp, ...)
{
mp_int* next_mp = mp;
/* shift left a certain amount of digits */
-static int ICACHE_FLASH_ATTR
+static int
mp_lshd (mp_int * a, int b)
{
int x, res;
/* returns the number of bits in an int */
-static int ICACHE_FLASH_ATTR
+static int
mp_count_bits (mp_int * a)
{
int r;
/* calc a value mod 2**b */
-static int ICACHE_FLASH_ATTR
+static int
mp_mod_2d (mp_int * a, int b, mp_int * c)
{
int x, res;
#ifdef BN_MP_DIV_SMALL
/* slower bit-bang division... also smaller */
-static int ICACHE_FLASH_ATTR
+static int
mp_div(mp_int * a, mp_int * b, mp_int * c, mp_int * d)
{
mp_int ta, tb, tq, q;
* The overall algorithm is as described as
* 14.20 from HAC but fixed to treat these cases.
*/
-static int ICACHE_FLASH_ATTR
+static int
mp_div (mp_int * a, mp_int * b, mp_int * c, mp_int * d)
{
mp_int q, x, y, t1, t2;
#define TAB_SIZE 256
#endif
-static int ICACHE_FLASH_ATTR
+static int
s_mp_exptmod (mp_int * G, mp_int * X, mp_int * P, mp_int * Y, int redmode)
{
mp_int M[TAB_SIZE], res, mu;
/* computes b = a*a */
-static int ICACHE_FLASH_ATTR
+static int
mp_sqr (mp_int * a, mp_int * b)
{
int res;
This differs from reduce_2k since "d" can be larger
than a single digit.
*/
-static int ICACHE_FLASH_ATTR
+static int
mp_reduce_2k_l(mp_int *a, mp_int *n, mp_int *d)
{
mp_int q;
/* determines the setup value */
-static int ICACHE_FLASH_ATTR
+static int
mp_reduce_2k_setup_l(mp_int *a, mp_int *d)
{
int res;
* Simple algorithm which zeroes the int, grows it then just sets one bit
* as required.
*/
-static int ICACHE_FLASH_ATTR
+static int
mp_2expt (mp_int * a, int b)
{
int res;
/* pre-calculate the value required for Barrett reduction
* For a given modulus "b" it calulates the value required in "a"
*/
-static int ICACHE_FLASH_ATTR
+static int
mp_reduce_setup (mp_int * a, mp_int * b)
{
int res;
* precomputed via mp_reduce_setup.
* From HAC pp.604 Algorithm 14.42
*/
-static int ICACHE_FLASH_ATTR
+static int
mp_reduce (mp_int * x, mp_int * m, mp_int * mu)
{
mp_int q;
* HAC pp. 595, Algorithm 14.12 Modified so you can control how
* many digits of output are created.
*/
-static int ICACHE_FLASH_ATTR
+static int
s_mp_mul_digs (mp_int * a, mp_int * b, mp_int * c, int digs)
{
mp_int t;
* Based on Algorithm 14.12 on pp.595 of HAC.
*
*/
-static int ICACHE_FLASH_ATTR
+static int
fast_s_mp_mul_digs (mp_int * a, mp_int * b, mp_int * c, int digs)
{
int olduse, res, pa, ix, iz;
/* init an mp_init for a given size */
-static int ICACHE_FLASH_ATTR
+static int
mp_init_size (mp_int * a, int size)
{
int x;
/* low level squaring, b = a*a, HAC pp.596-597, Algorithm 14.16 */
-static int ICACHE_FLASH_ATTR
+static int
s_mp_sqr (mp_int * a, mp_int * b)
{
mp_int t;
/* multiplies |a| * |b| and does not compute the lower digs digits
* [meant to get the higher part of the product]
*/
-static int ICACHE_FLASH_ATTR
+static int
s_mp_mul_high_digs (mp_int * a, mp_int * b, mp_int * c, int digs)
{
mp_int t;
#ifdef BN_MP_MONTGOMERY_SETUP_C
/* setups the montgomery reduction stuff */
-static int ICACHE_FLASH_ATTR
+static int
mp_montgomery_setup (mp_int * n, mp_digit * rho)
{
mp_digit x, b;
*
* Based on Algorithm 14.32 on pp.601 of HAC.
*/
-int ICACHE_FLASH_ATTR
+int
fast_mp_montgomery_reduce (mp_int * x, mp_int * n, mp_digit rho)
{
int ix, res, olduse;
#ifdef BN_MP_MUL_2_C
/* b = a*2 */
-static int ICACHE_FLASH_ATTR
+static int
mp_mul_2(mp_int * a, mp_int * b)
{
int x, res, oldused;
* The method is slightly modified to shift B unconditionally up to just under
* the leading bit of b. This saves a lot of multiple precision shifting.
*/
-static int ICACHE_FLASH_ATTR
+static int
mp_montgomery_calc_normalization (mp_int * a, mp_int * b)
{
int x, bits, res;
* Uses Montgomery or Diminished Radix reduction [whichever appropriate]
*/
-static int ICACHE_FLASH_ATTR
+static int
mp_exptmod_fast (mp_int * G, mp_int * X, mp_int * P, mp_int * Y, int redmode)
{
mp_int M[TAB_SIZE], res;
After that loop you do the squares and add them in.
*/
-static int ICACHE_FLASH_ATTR
+static int
fast_s_mp_sqr (mp_int * a, mp_int * b)
{
int olduse, res, pa, ix, iz;
#ifdef BN_MP_MUL_D_C
/* multiply by a digit */
-static int ICACHE_FLASH_ATTR
+static int
mp_mul_d (mp_int * a, mp_digit b, mp_int * c)
{
mp_digit u, *tmpa, *tmpc;
#ifndef BASE64_H
#define BASE64_H
-unsigned char * _base64_encode(const unsigned char *src, size_t len,
+unsigned char * base64_encode(const unsigned char *src, size_t len,
size_t *out_len);
-unsigned char * _base64_decode(const unsigned char *src, size_t len,
+unsigned char * base64_decode(const unsigned char *src, size_t len,
size_t *out_len);
#endif /* BASE64_H */
#else /* CONFIG_EXT_PASSWORD */
-#define ext_password_init(b, p) ((void *) 1)
-#define ext_password_deinit(d) do { } while (0)
-#define ext_password_get(d, n) (NULL)
-#define ext_password_free(p) do { } while (0)
+#define ext_password_init(b, p)
+#define ext_password_deinit(d)
+#define ext_password_get(d, n)
+#define ext_password_free(p)
#endif /* CONFIG_EXT_PASSWORD */
--- /dev/null
+/*
+ * Universally Unique IDentifier (UUID)
+ * Copyright (c) 2008, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef UUID_H
+#define UUID_H
+
+#define UUID_LEN 16
+
+int uuid_str2bin(const char *str, u8 *bin);
+int uuid_bin2str(const u8 *bin, char *str, size_t max_len);
+int is_nil_uuid(const u8 *uuid);
+
+#endif /* UUID_H */
--- /dev/null
+/*
+ * Wi-Fi Protected Setup
+ * Copyright (c) 2007-2012, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef WPS_H
+#define WPS_H
+
+#include "rom/ets_sys.h"
+#include "wps_defs.h"
+#include "esp_wifi_types.h"
+
+/**
+ * enum wsc_op_code - EAP-WSC OP-Code values
+ */
+enum wsc_op_code {
+ WSC_UPnP = 0 /* No OP Code in UPnP transport */,
+ WSC_Start = 0x01,
+ WSC_ACK = 0x02,
+ WSC_NACK = 0x03,
+ WSC_MSG = 0x04,
+ WSC_Done = 0x05,
+ WSC_FRAG_ACK = 0x06
+};
+
+struct wps_registrar;
+//struct upnp_wps_device_sm;
+struct wps_er;
+struct wps_parse_attr;
+
+/**
+ * struct wps_credential - WPS Credential
+ * @ssid: SSID
+ * @ssid_len: Length of SSID
+ * @auth_type: Authentication Type (WPS_WIFI_AUTH_OPEN, .. flags)
+ * @encr_type: Encryption Type (WPS_ENCR_NONE, .. flags)
+ * @key_idx: Key index
+ * @key: Key
+ * @key_len: Key length in octets
+ * @mac_addr: MAC address of the Credential receiver
+ * @cred_attr: Unparsed Credential attribute data (used only in cred_cb());
+ * this may be %NULL, if not used
+ * @cred_attr_len: Length of cred_attr in octets
+ * @ap_channel: AP channel
+ */
+struct wps_credential {
+ u8 ssid[32];
+ size_t ssid_len;
+ u16 auth_type;
+ u16 encr_type;
+ u8 key_idx;
+ u8 key[64];
+ size_t key_len;
+ u8 mac_addr[ETH_ALEN];
+ const u8 *cred_attr;
+ size_t cred_attr_len;
+ u16 ap_channel;
+};
+
+#define WPS_DEV_TYPE_LEN 8
+#define WPS_DEV_TYPE_BUFSIZE 21
+#define WPS_SEC_DEV_TYPE_MAX_LEN 128
+/* maximum number of advertised WPS vendor extension attributes */
+#define MAX_WPS_VENDOR_EXTENSIONS 10
+/* maximum size of WPS Vendor extension attribute */
+#define WPS_MAX_VENDOR_EXT_LEN 1024
+/* maximum number of parsed WPS vendor extension attributes */
+#define MAX_WPS_PARSE_VENDOR_EXT 10
+
+/**
+ * struct wps_device_data - WPS Device Data
+ * @mac_addr: Device MAC address
+ * @device_name: Device Name (0..32 octets encoded in UTF-8)
+ * @manufacturer: Manufacturer (0..64 octets encoded in UTF-8)
+ * @model_name: Model Name (0..32 octets encoded in UTF-8)
+ * @model_number: Model Number (0..32 octets encoded in UTF-8)
+ * @serial_number: Serial Number (0..32 octets encoded in UTF-8)
+ * @pri_dev_type: Primary Device Type
+ * @sec_dev_type: Array of secondary device types
+ * @num_sec_dev_type: Number of secondary device types
+ * @os_version: OS Version
+ * @rf_bands: RF bands (WPS_RF_24GHZ, WPS_RF_50GHZ flags)
+ * @p2p: Whether the device is a P2P device
+ */
+struct wps_device_data {
+ u8 mac_addr[ETH_ALEN];
+ char *device_name;
+ char *manufacturer;
+ char *model_name;
+ char *model_number;
+ char *serial_number;
+ u8 pri_dev_type[WPS_DEV_TYPE_LEN];
+#define WPS_SEC_DEVICE_TYPES 5
+ u8 sec_dev_type[WPS_SEC_DEVICE_TYPES][WPS_DEV_TYPE_LEN];
+ u8 num_sec_dev_types;
+ u32 os_version;
+ u8 rf_bands;
+ u16 config_methods;
+ struct wpabuf *vendor_ext_m1;
+ struct wpabuf *vendor_ext[MAX_WPS_VENDOR_EXTENSIONS];
+
+ int p2p;
+};
+
+/**
+ * struct wps_config - WPS configuration for a single registration protocol run
+ */
+struct wps_config {
+ /**
+ * wps - Pointer to long term WPS context
+ */
+ struct wps_context *wps;
+
+ /**
+ * registrar - Whether this end is a Registrar
+ */
+ int registrar;
+
+ /**
+ * pin - Enrollee Device Password (%NULL for Registrar or PBC)
+ */
+ const u8 *pin;
+
+ /**
+ * pin_len - Length on pin in octets
+ */
+ size_t pin_len;
+
+ /**
+ * pbc - Whether this is protocol run uses PBC
+ */
+ int pbc;
+
+ /**
+ * assoc_wps_ie: (Re)AssocReq WPS IE (in AP; %NULL if not AP)
+ */
+ const struct wpabuf *assoc_wps_ie;
+
+ /**
+ * new_ap_settings - New AP settings (%NULL if not used)
+ *
+ * This parameter provides new AP settings when using a wireless
+ * stations as a Registrar to configure the AP. %NULL means that AP
+ * will not be reconfigured, i.e., the station will only learn the
+ * current AP settings by using AP PIN.
+ */
+ const struct wps_credential *new_ap_settings;
+
+ /**
+ * peer_addr: MAC address of the peer in AP; %NULL if not AP
+ */
+ const u8 *peer_addr;
+
+ /**
+ * use_psk_key - Use PSK format key in Credential
+ *
+ * Force PSK format to be used instead of ASCII passphrase when
+ * building Credential for an Enrollee. The PSK value is set in
+ * struct wpa_context::psk.
+ */
+ int use_psk_key;
+
+ /**
+ * dev_pw_id - Device Password ID for Enrollee when PIN is used
+ */
+ u16 dev_pw_id;
+
+ /**
+ * p2p_dev_addr - P2P Device Address from (Re)Association Request
+ *
+ * On AP/GO, this is set to the P2P Device Address of the associating
+ * P2P client if a P2P IE is included in the (Re)Association Request
+ * frame and the P2P Device Address is included. Otherwise, this is set
+ * to %NULL to indicate the station does not have a P2P Device Address.
+ */
+ const u8 *p2p_dev_addr;
+
+ /**
+ * pbc_in_m1 - Do not remove PushButton config method in M1 (AP)
+ *
+ * This can be used to enable a workaround to allow Windows 7 to use
+ * PBC with the AP.
+ */
+ int pbc_in_m1;
+};
+
+/* Bssid of the discard AP which is discarded for not select reg or other reason */
+struct discard_ap_list_t{
+ u8 bssid[6];
+};
+
+//struct wps_data * wps_init(const struct wps_config *cfg);
+struct wps_data * wps_init(void);
+
+//void wps_deinit(struct wps_data *data);
+void wps_deinit(void);
+
+/**
+ * enum wps_process_res - WPS message processing result
+ */
+enum wps_process_res {
+ /**
+ * WPS_DONE - Processing done
+ */
+ WPS_DONE,
+
+ /**
+ * WPS_CONTINUE - Processing continues
+ */
+ WPS_CONTINUE,
+
+ /**
+ * WPS_FAILURE - Processing failed
+ */
+ WPS_FAILURE,
+
+ /**
+ * WPS_PENDING - Processing continues, but waiting for an external
+ * event (e.g., UPnP message from an external Registrar)
+ */
+ WPS_PENDING,
+ WPS_IGNORE, /* snake, ignore the re-packge */
+
+ WPS_FRAGMENT /* Tim, send wsc fragment ack */
+};
+enum wps_process_res wps_process_msg(struct wps_data *wps,
+ enum wsc_op_code op_code,
+ const struct wpabuf *msg);
+
+struct wpabuf * wps_get_msg(struct wps_data *wps, enum wsc_op_code *op_code);
+
+int wps_is_selected_pbc_registrar(const struct wpabuf *msg, u8 *bssid);
+#ifdef CONFIG_WPS_PIN
+int wps_is_selected_pin_registrar(const struct wpabuf *msg, u8 *bssid);
+#endif
+int wps_ap_priority_compar(const struct wpabuf *wps_a,
+ const struct wpabuf *wps_b);
+int wps_is_addr_authorized(const struct wpabuf *msg, const u8 *addr,
+ int ver1_compat);
+const u8 * wps_get_uuid_e(const struct wpabuf *msg);
+int wps_is_20(const struct wpabuf *msg);
+
+struct wpabuf * wps_build_assoc_req_ie(enum wps_request_type req_type);
+struct wpabuf * wps_build_assoc_resp_ie(void);
+struct wpabuf * wps_build_probe_req_ie(u16 pw_id, struct wps_device_data *dev,
+ const u8 *uuid,
+ enum wps_request_type req_type,
+ unsigned int num_req_dev_types,
+ const u8 *req_dev_types);
+
+
+/**
+ * struct wps_registrar_config - WPS Registrar configuration
+ */
+struct wps_registrar_config {
+ /**
+ * new_psk_cb - Callback for new PSK
+ * @ctx: Higher layer context data (cb_ctx)
+ * @mac_addr: MAC address of the Enrollee
+ * @psk: The new PSK
+ * @psk_len: The length of psk in octets
+ * Returns: 0 on success, -1 on failure
+ *
+ * This callback is called when a new per-device PSK is provisioned.
+ */
+ int (*new_psk_cb)(void *ctx, const u8 *mac_addr, const u8 *psk,
+ size_t psk_len);
+
+ /**
+ * set_ie_cb - Callback for WPS IE changes
+ * @ctx: Higher layer context data (cb_ctx)
+ * @beacon_ie: WPS IE for Beacon
+ * @probe_resp_ie: WPS IE for Probe Response
+ * Returns: 0 on success, -1 on failure
+ *
+ * This callback is called whenever the WPS IE in Beacon or Probe
+ * Response frames needs to be changed (AP only). Callee is responsible
+ * for freeing the buffers.
+ */
+ int (*set_ie_cb)(void *ctx, struct wpabuf *beacon_ie,
+ struct wpabuf *probe_resp_ie);
+
+ /**
+ * pin_needed_cb - Callback for requesting a PIN
+ * @ctx: Higher layer context data (cb_ctx)
+ * @uuid_e: UUID-E of the unknown Enrollee
+ * @dev: Device Data from the unknown Enrollee
+ *
+ * This callback is called whenever an unknown Enrollee requests to use
+ * PIN method and a matching PIN (Device Password) is not found in
+ * Registrar data.
+ */
+ void (*pin_needed_cb)(void *ctx, const u8 *uuid_e,
+ const struct wps_device_data *dev);
+
+ /**
+ * reg_success_cb - Callback for reporting successful registration
+ * @ctx: Higher layer context data (cb_ctx)
+ * @mac_addr: MAC address of the Enrollee
+ * @uuid_e: UUID-E of the Enrollee
+ * @dev_pw: Device Password (PIN) used during registration
+ * @dev_pw_len: Length of dev_pw in octets
+ *
+ * This callback is called whenever an Enrollee completes registration
+ * successfully.
+ */
+ void (*reg_success_cb)(void *ctx, const u8 *mac_addr,
+ const u8 *uuid_e, const u8 *dev_pw,
+ size_t dev_pw_len);
+
+ /**
+ * set_sel_reg_cb - Callback for reporting selected registrar changes
+ * @ctx: Higher layer context data (cb_ctx)
+ * @sel_reg: Whether the Registrar is selected
+ * @dev_passwd_id: Device Password ID to indicate with method or
+ * specific password the Registrar intends to use
+ * @sel_reg_config_methods: Bit field of active config methods
+ *
+ * This callback is called whenever the Selected Registrar state
+ * changes (e.g., a new PIN becomes available or PBC is invoked). This
+ * callback is only used by External Registrar implementation;
+ * set_ie_cb() is used by AP implementation in similar caes, but it
+ * provides the full WPS IE data instead of just the minimal Registrar
+ * state information.
+ */
+ void (*set_sel_reg_cb)(void *ctx, int sel_reg, u16 dev_passwd_id,
+ u16 sel_reg_config_methods);
+
+ /**
+ * enrollee_seen_cb - Callback for reporting Enrollee based on ProbeReq
+ * @ctx: Higher layer context data (cb_ctx)
+ * @addr: MAC address of the Enrollee
+ * @uuid_e: UUID of the Enrollee
+ * @pri_dev_type: Primary device type
+ * @config_methods: Config Methods
+ * @dev_password_id: Device Password ID
+ * @request_type: Request Type
+ * @dev_name: Device Name (if available)
+ */
+ void (*enrollee_seen_cb)(void *ctx, const u8 *addr, const u8 *uuid_e,
+ const u8 *pri_dev_type, u16 config_methods,
+ u16 dev_password_id, u8 request_type,
+ const char *dev_name);
+
+ /**
+ * cb_ctx: Higher layer context data for Registrar callbacks
+ */
+ void *cb_ctx;
+
+ /**
+ * skip_cred_build: Do not build credential
+ *
+ * This option can be used to disable internal code that builds
+ * Credential attribute into M8 based on the current network
+ * configuration and Enrollee capabilities. The extra_cred data will
+ * then be used as the Credential(s).
+ */
+ int skip_cred_build;
+
+ /**
+ * extra_cred: Additional Credential attribute(s)
+ *
+ * This optional data (set to %NULL to disable) can be used to add
+ * Credential attribute(s) for other networks into M8. If
+ * skip_cred_build is set, this will also override the automatically
+ * generated Credential attribute.
+ */
+ const u8 *extra_cred;
+
+ /**
+ * extra_cred_len: Length of extra_cred in octets
+ */
+ size_t extra_cred_len;
+
+ /**
+ * disable_auto_conf - Disable auto-configuration on first registration
+ *
+ * By default, the AP that is started in not configured state will
+ * generate a random PSK and move to configured state when the first
+ * registration protocol run is completed successfully. This option can
+ * be used to disable this functionality and leave it up to an external
+ * program to take care of configuration. This requires the extra_cred
+ * to be set with a suitable Credential and skip_cred_build being used.
+ */
+ int disable_auto_conf;
+
+ /**
+ * static_wep_only - Whether the BSS supports only static WEP
+ */
+ int static_wep_only;
+
+ /**
+ * dualband - Whether this is a concurrent dualband AP
+ */
+ int dualband;
+};
+
+
+/**
+ * enum wps_event - WPS event types
+ */
+enum wps_event {
+ /**
+ * WPS_EV_M2D - M2D received (Registrar did not know us)
+ */
+ WPS_EV_M2D,
+
+ /**
+ * WPS_EV_FAIL - Registration failed
+ */
+ WPS_EV_FAIL,
+
+ /**
+ * WPS_EV_SUCCESS - Registration succeeded
+ */
+ WPS_EV_SUCCESS,
+
+ /**
+ * WPS_EV_PWD_AUTH_FAIL - Password authentication failed
+ */
+ WPS_EV_PWD_AUTH_FAIL,
+
+ /**
+ * WPS_EV_PBC_OVERLAP - PBC session overlap detected
+ */
+ WPS_EV_PBC_OVERLAP,
+
+ /**
+ * WPS_EV_PBC_TIMEOUT - PBC walktime expired before protocol run start
+ */
+ WPS_EV_PBC_TIMEOUT,
+
+ /**
+ * WPS_EV_ER_AP_ADD - ER: AP added
+ */
+ WPS_EV_ER_AP_ADD,
+
+ /**
+ * WPS_EV_ER_AP_REMOVE - ER: AP removed
+ */
+ WPS_EV_ER_AP_REMOVE,
+
+ /**
+ * WPS_EV_ER_ENROLLEE_ADD - ER: Enrollee added
+ */
+ WPS_EV_ER_ENROLLEE_ADD,
+
+ /**
+ * WPS_EV_ER_ENROLLEE_REMOVE - ER: Enrollee removed
+ */
+ WPS_EV_ER_ENROLLEE_REMOVE,
+
+ /**
+ * WPS_EV_ER_AP_SETTINGS - ER: AP Settings learned
+ */
+ WPS_EV_ER_AP_SETTINGS,
+
+ /**
+ * WPS_EV_ER_SET_SELECTED_REGISTRAR - ER: SetSelectedRegistrar event
+ */
+ WPS_EV_ER_SET_SELECTED_REGISTRAR,
+
+ /**
+ * WPS_EV_AP_PIN_SUCCESS - External Registrar used correct AP PIN
+ */
+ WPS_EV_AP_PIN_SUCCESS
+};
+
+/**
+ * union wps_event_data - WPS event data
+ */
+union wps_event_data {
+ /**
+ * struct wps_event_m2d - M2D event data
+ */
+ struct wps_event_m2d {
+ u16 config_methods;
+ const u8 *manufacturer;
+ size_t manufacturer_len;
+ const u8 *model_name;
+ size_t model_name_len;
+ const u8 *model_number;
+ size_t model_number_len;
+ const u8 *serial_number;
+ size_t serial_number_len;
+ const u8 *dev_name;
+ size_t dev_name_len;
+ const u8 *primary_dev_type; /* 8 octets */
+ u16 config_error;
+ u16 dev_password_id;
+ } m2d;
+
+ /**
+ * struct wps_event_fail - Registration failure information
+ * @msg: enum wps_msg_type
+ */
+ struct wps_event_fail {
+ int msg;
+ u16 config_error;
+ u16 error_indication;
+ } fail;
+
+ struct wps_event_pwd_auth_fail {
+ int enrollee;
+ int part;
+ } pwd_auth_fail;
+
+ struct wps_event_er_ap {
+ const u8 *uuid;
+ const u8 *mac_addr;
+ const char *friendly_name;
+ const char *manufacturer;
+ const char *manufacturer_url;
+ const char *model_description;
+ const char *model_name;
+ const char *model_number;
+ const char *model_url;
+ const char *serial_number;
+ const char *upc;
+ const u8 *pri_dev_type;
+ u8 wps_state;
+ } ap;
+
+ struct wps_event_er_enrollee {
+ const u8 *uuid;
+ const u8 *mac_addr;
+ int m1_received;
+ u16 config_methods;
+ u16 dev_passwd_id;
+ const u8 *pri_dev_type;
+ const char *dev_name;
+ const char *manufacturer;
+ const char *model_name;
+ const char *model_number;
+ const char *serial_number;
+ } enrollee;
+
+ struct wps_event_er_ap_settings {
+ const u8 *uuid;
+ const struct wps_credential *cred;
+ } ap_settings;
+
+ struct wps_event_er_set_selected_registrar {
+ const u8 *uuid;
+ int sel_reg;
+ u16 dev_passwd_id;
+ u16 sel_reg_config_methods;
+ enum {
+ WPS_ER_SET_SEL_REG_START,
+ WPS_ER_SET_SEL_REG_DONE,
+ WPS_ER_SET_SEL_REG_FAILED
+ } state;
+ } set_sel_reg;
+};
+#ifdef CONFIG_WPS_UPNP
+/**
+ * struct upnp_pending_message - Pending PutWLANResponse messages
+ * @next: Pointer to next pending message or %NULL
+ * @addr: NewWLANEventMAC
+ * @msg: NewMessage
+ * @type: Message Type
+ */
+struct upnp_pending_message {
+ struct upnp_pending_message *next;
+ u8 addr[ETH_ALEN];
+ struct wpabuf *msg;
+ enum wps_msg_type type;
+};
+void wps_free_pending_msgs(struct upnp_pending_message *msgs);
+#endif
+/**
+ * struct wps_context - Long term WPS context data
+ *
+ * This data is stored at the higher layer Authenticator or Supplicant data
+ * structures and it is maintained over multiple registration protocol runs.
+ */
+struct wps_context {
+ /**
+ * ap - Whether the local end is an access point
+ */
+ int ap;
+
+ /**
+ * registrar - Pointer to WPS registrar data from wps_registrar_init()
+ */
+ struct wps_registrar *registrar;
+
+ /**
+ * wps_state - Current WPS state
+ */
+ enum wps_state wps_state;
+
+ /**
+ * ap_setup_locked - Whether AP setup is locked (only used at AP)
+ */
+ int ap_setup_locked;
+
+ /**
+ * uuid - Own UUID
+ */
+ u8 uuid[16];
+
+ /**
+ * ssid - SSID
+ *
+ * This SSID is used by the Registrar to fill in information for
+ * Credentials. In addition, AP uses it when acting as an Enrollee to
+ * notify Registrar of the current configuration.
+ */
+ u8 ssid[32];
+
+ /**
+ * ssid_len - Length of ssid in octets
+ */
+ size_t ssid_len;
+
+ /**
+ * dev - Own WPS device data
+ */
+ struct wps_device_data dev;
+
+ /**
+ * dh_ctx - Context data for Diffie-Hellman operation
+ */
+ void *dh_ctx;
+
+ /**
+ * dh_privkey - Diffie-Hellman private key
+ */
+ struct wpabuf *dh_privkey;
+
+ /**
+ * dh_pubkey_oob - Diffie-Hellman public key
+ */
+ struct wpabuf *dh_pubkey;
+
+ /**
+ * config_methods - Enabled configuration methods
+ *
+ * Bit field of WPS_CONFIG_*
+ */
+ u16 config_methods;
+
+ /**
+ * encr_types - Enabled encryption types (bit field of WPS_ENCR_*)
+ */
+ u16 encr_types;
+
+ /**
+ * auth_types - Authentication types (bit field of WPS_AUTH_*)
+ */
+ u16 auth_types;
+
+ /**
+ * network_key - The current Network Key (PSK) or %NULL to generate new
+ *
+ * If %NULL, Registrar will generate per-device PSK. In addition, AP
+ * uses this when acting as an Enrollee to notify Registrar of the
+ * current configuration.
+ *
+ * When using WPA/WPA2-Person, this key can be either the ASCII
+ * passphrase (8..63 characters) or the 32-octet PSK (64 hex
+ * characters). When this is set to the ASCII passphrase, the PSK can
+ * be provided in the psk buffer and used per-Enrollee to control which
+ * key type is included in the Credential (e.g., to reduce calculation
+ * need on low-powered devices by provisioning PSK while still allowing
+ * other devices to get the passphrase).
+ */
+ u8 *network_key;
+
+ /**
+ * network_key_len - Length of network_key in octets
+ */
+ size_t network_key_len;
+
+ /**
+ * psk - The current network PSK
+ *
+ * This optional value can be used to provide the current PSK if
+ * network_key is set to the ASCII passphrase.
+ */
+ u8 psk[32];
+
+ /**
+ * psk_set - Whether psk value is set
+ */
+ int psk_set;
+
+ /**
+ * ap_settings - AP Settings override for M7 (only used at AP)
+ *
+ * If %NULL, AP Settings attributes will be generated based on the
+ * current network configuration.
+ */
+ u8 *ap_settings;
+
+ /**
+ * ap_settings_len - Length of ap_settings in octets
+ */
+ size_t ap_settings_len;
+
+ /**
+ * friendly_name - Friendly Name (required for UPnP)
+ */
+ char *friendly_name;
+
+ /**
+ * manufacturer_url - Manufacturer URL (optional for UPnP)
+ */
+ char *manufacturer_url;
+
+ /**
+ * model_description - Model Description (recommended for UPnP)
+ */
+ char *model_description;
+
+ /**
+ * model_url - Model URL (optional for UPnP)
+ */
+ char *model_url;
+
+ /**
+ * upc - Universal Product Code (optional for UPnP)
+ */
+ char *upc;
+
+ /**
+ * cred_cb - Callback to notify that new Credentials were received
+ * @ctx: Higher layer context data (cb_ctx)
+ * @cred: The received Credential
+ * Return: 0 on success, -1 on failure
+ */
+ int (*cred_cb)(void *ctx, const struct wps_credential *cred);
+
+ /**
+ * event_cb - Event callback (state information about progress)
+ * @ctx: Higher layer context data (cb_ctx)
+ * @event: Event type
+ * @data: Event data
+ */
+ void (*event_cb)(void *ctx, enum wps_event event,
+ union wps_event_data *data);
+
+ /**
+ * cb_ctx: Higher layer context data for callbacks
+ */
+ void *cb_ctx;
+
+ //struct upnp_wps_device_sm *wps_upnp;
+
+ /* Pending messages from UPnP PutWLANResponse */
+ //struct upnp_pending_message *upnp_msgs;
+#ifdef CONFIG_WPS_NFC
+
+ u16 ap_nfc_dev_pw_id;
+ struct wpabuf *ap_nfc_dh_pubkey;
+ struct wpabuf *ap_nfc_dh_privkey;
+ struct wpabuf *ap_nfc_dev_pw;
+#endif
+};
+
+struct wps_registrar *
+wps_registrar_init(struct wps_context *wps,
+ const struct wps_registrar_config *cfg);
+void wps_registrar_deinit(struct wps_registrar *reg);
+#ifdef CONFIG_WPS_PIN
+
+int wps_registrar_add_pin(struct wps_registrar *reg, const u8 *addr,
+ const u8 *uuid, const u8 *pin, size_t pin_len,
+ int timeout);
+int wps_registrar_invalidate_pin(struct wps_registrar *reg, const u8 *uuid);
+int wps_registrar_unlock_pin(struct wps_registrar *reg, const u8 *uuid);
+#endif
+int wps_registrar_wps_cancel(struct wps_registrar *reg);
+
+int wps_registrar_button_pushed(struct wps_registrar *reg,
+ const u8 *p2p_dev_addr);
+void wps_registrar_complete(struct wps_registrar *registrar, const u8 *uuid_e,
+ const u8 *dev_pw, size_t dev_pw_len);
+void wps_registrar_probe_req_rx(struct wps_registrar *reg, const u8 *addr,
+ const struct wpabuf *wps_data,
+ int p2p_wildcard);
+int wps_registrar_update_ie(struct wps_registrar *reg);
+int wps_registrar_get_info(struct wps_registrar *reg, const u8 *addr,
+ char *buf, size_t buflen);
+int wps_registrar_config_ap(struct wps_registrar *reg,
+ struct wps_credential *cred);
+#ifdef CONFIG_WPS_NFC
+
+int wps_registrar_add_nfc_pw_token(struct wps_registrar *reg,
+ const u8 *pubkey_hash, u16 pw_id,
+ const u8 *dev_pw, size_t dev_pw_len);
+int wps_registrar_add_nfc_password_token(struct wps_registrar *reg,
+ const u8 *oob_dev_pw,
+ size_t oob_dev_pw_len);
+#endif
+int wps_build_credential_wrap(struct wpabuf *msg,
+ const struct wps_credential *cred);
+#ifdef CONFIG_WPS_PIN
+
+unsigned int wps_pin_checksum(unsigned int pin);
+unsigned int wps_pin_valid(unsigned int pin);
+unsigned int wps_generate_pin(void);
+int wps_pin_str_valid(const char *pin);
+#endif
+
+#ifdef CONFIG_WPS_OOB
+
+struct wpabuf * wps_get_oob_cred(struct wps_context *wps);
+int wps_oob_use_cred(struct wps_context *wps, struct wps_parse_attr *attr);
+#endif
+int wps_attr_text(struct wpabuf *data, char *buf, char *end);
+
+struct wps_er * wps_er_init(struct wps_context *wps, const char *ifname,
+ const char *filter);
+void wps_er_refresh(struct wps_er *er);
+void wps_er_deinit(struct wps_er *er, void (*cb)(void *ctx), void *ctx);
+void wps_er_set_sel_reg(struct wps_er *er, int sel_reg, u16 dev_passwd_id,
+ u16 sel_reg_config_methods);
+int wps_er_pbc(struct wps_er *er, const u8 *uuid);
+int wps_er_learn(struct wps_er *er, const u8 *uuid, const u8 *pin,
+ size_t pin_len);
+int wps_er_set_config(struct wps_er *er, const u8 *uuid,
+ const struct wps_credential *cred);
+int wps_er_config(struct wps_er *er, const u8 *uuid, const u8 *pin,
+ size_t pin_len, const struct wps_credential *cred);
+#ifdef CONFIG_WPS_NFC
+
+struct wpabuf * wps_er_nfc_config_token(struct wps_er *er, const u8 *uuid);
+
+#endif
+
+int wps_dev_type_str2bin(const char *str, u8 dev_type[WPS_DEV_TYPE_LEN]);
+char * wps_dev_type_bin2str(const u8 dev_type[WPS_DEV_TYPE_LEN], char *buf,
+ size_t buf_len);
+void uuid_gen_mac_addr(const u8 *mac_addr, u8 *uuid);
+u16 wps_config_methods_str2bin(const char *str);
+
+#ifdef CONFIG_WPS_NFC
+
+struct wpabuf * wps_build_nfc_pw_token(u16 dev_pw_id,
+ const struct wpabuf *pubkey,
+ const struct wpabuf *dev_pw);
+struct wpabuf * wps_nfc_token_gen(int ndef, int *id, struct wpabuf **pubkey,
+ struct wpabuf **privkey,
+ struct wpabuf **dev_pw);
+#endif
+
+/* ndef.c */
+struct wpabuf * ndef_parse_wifi(const struct wpabuf *buf);
+struct wpabuf * ndef_build_wifi(const struct wpabuf *buf);
+struct wpabuf * ndef_build_wifi_hr(void);
+
+#ifdef CONFIG_WPS_STRICT
+int wps_validate_beacon(const struct wpabuf *wps_ie);
+int wps_validate_beacon_probe_resp(const struct wpabuf *wps_ie, int probe,
+ const u8 *addr);
+int wps_validate_probe_req(const struct wpabuf *wps_ie, const u8 *addr);
+int wps_validate_assoc_req(const struct wpabuf *wps_ie);
+int wps_validate_assoc_resp(const struct wpabuf *wps_ie);
+int wps_validate_m1(const struct wpabuf *tlvs);
+int wps_validate_m2(const struct wpabuf *tlvs);
+int wps_validate_m2d(const struct wpabuf *tlvs);
+int wps_validate_m3(const struct wpabuf *tlvs);
+int wps_validate_m4(const struct wpabuf *tlvs);
+int wps_validate_m4_encr(const struct wpabuf *tlvs, int wps2);
+int wps_validate_m5(const struct wpabuf *tlvs);
+int wps_validate_m5_encr(const struct wpabuf *tlvs, int wps2);
+int wps_validate_m6(const struct wpabuf *tlvs);
+int wps_validate_m6_encr(const struct wpabuf *tlvs, int wps2);
+int wps_validate_m7(const struct wpabuf *tlvs);
+int wps_validate_m7_encr(const struct wpabuf *tlvs, int ap, int wps2);
+int wps_validate_m8(const struct wpabuf *tlvs);
+int wps_validate_m8_encr(const struct wpabuf *tlvs, int ap, int wps2);
+int wps_validate_wsc_ack(const struct wpabuf *tlvs);
+int wps_validate_wsc_nack(const struct wpabuf *tlvs);
+int wps_validate_wsc_done(const struct wpabuf *tlvs);
+int wps_validate_upnp_set_selected_registrar(const struct wpabuf *tlvs);
+#else /* CONFIG_WPS_STRICT */
+static inline int wps_validate_beacon(const struct wpabuf *wps_ie){
+ return 0;
+}
+
+static inline int wps_validate_beacon_probe_resp(const struct wpabuf *wps_ie,
+ int probe, const u8 *addr)
+{
+ return 0;
+}
+
+static inline int wps_validate_probe_req(const struct wpabuf *wps_ie,
+ const u8 *addr)
+{
+ return 0;
+}
+
+static inline int wps_validate_assoc_req(const struct wpabuf *wps_ie)
+{
+ return 0;
+}
+
+static inline int wps_validate_assoc_resp(const struct wpabuf *wps_ie)
+{
+ return 0;
+}
+
+static inline int wps_validate_m1(const struct wpabuf *tlvs)
+{
+ return 0;
+}
+
+static inline int wps_validate_m2(const struct wpabuf *tlvs)
+{
+ return 0;
+}
+
+static inline int wps_validate_m2d(const struct wpabuf *tlvs)
+{
+ return 0;
+}
+
+static inline int wps_validate_m3(const struct wpabuf *tlvs)
+{
+ return 0;
+}
+
+static inline int wps_validate_m4(const struct wpabuf *tlvs)
+{
+ return 0;
+}
+
+static inline int wps_validate_m4_encr(const struct wpabuf *tlvs, int wps2)
+{
+ return 0;
+}
+
+static inline int wps_validate_m5(const struct wpabuf *tlvs)
+{
+ return 0;
+}
+
+static inline int wps_validate_m5_encr(const struct wpabuf *tlvs, int wps2)
+{
+ return 0;
+}
+
+static inline int wps_validate_m6(const struct wpabuf *tlvs)
+{
+ return 0;
+}
+
+static inline int wps_validate_m6_encr(const struct wpabuf *tlvs, int wps2)
+{
+ return 0;
+}
+
+static inline int wps_validate_m7(const struct wpabuf *tlvs)
+{
+ return 0;
+}
+
+static inline int wps_validate_m7_encr(const struct wpabuf *tlvs, int ap,
+ int wps2)
+{
+ return 0;
+}
+
+static inline int wps_validate_m8(const struct wpabuf *tlvs)
+{
+ return 0;
+}
+
+static inline int wps_validate_m8_encr(const struct wpabuf *tlvs, int ap,
+ int wps2)
+{
+ return 0;
+}
+
+static inline int wps_validate_wsc_ack(const struct wpabuf *tlvs)
+{
+ return 0;
+}
+
+static inline int wps_validate_wsc_nack(const struct wpabuf *tlvs)
+{
+ return 0;
+}
+
+static inline int wps_validate_wsc_done(const struct wpabuf *tlvs)
+{
+ return 0;
+}
+
+static inline int wps_validate_upnp_set_selected_registrar(
+ const struct wpabuf *tlvs)
+{
+ return 0;
+}
+#endif /* CONFIG_WPS_STRICT */
+
+enum wps_cb_status {
+ WPS_CB_ST_SUCCESS = 0,
+ WPS_CB_ST_FAILED,
+ WPS_CB_ST_TIMEOUT,
+ WPS_CB_ST_WEP,
+ WPS_CB_ST_SCAN_ERR,
+};
+
+typedef void (*wps_st_cb_t)(int status);
+
+#ifdef USE_WPS_TASK
+#define SIG_WPS_START 0
+#define SIG_WPS_RX 1
+#define SIG_WPS_NUM 2
+#endif
+
+#define WPS_EAP_EXT_VENDOR_TYPE "WFA-SimpleConfig-Enrollee-1-0"
+#define WPS_OUTBUF_SIZE 500
+struct wps_sm {
+ struct wps_config *wps_cfg;
+ struct wps_context *wps_ctx;
+ struct wps_data *wps;
+ char identity[32];
+ u8 identity_len;
+ u8 ownaddr[ETH_ALEN];
+ u8 bssid[ETH_ALEN];
+ u8 ssid[32];
+ u8 ssid_len;
+ struct wps_device_data *dev;
+ u8 uuid[16];
+ u8 eapol_version;
+ char key[64];
+ u8 key_len;
+ ETSTimer wps_timeout_timer;
+ ETSTimer wps_msg_timeout_timer;
+ ETSTimer wps_scan_timer;
+ ETSTimer wps_success_cb_timer;
+ ETSTimer wps_eapol_start_timer;
+ wps_st_cb_t st_cb;
+ u8 current_identifier;
+ bool is_wps_scan;
+ u8 channel;
+ u8 scan_cnt;
+#ifdef USE_WPS_TASK
+ u8 wps_sig_cnt[SIG_WPS_NUM];
+#endif
+ u8 discover_ssid_cnt;
+ bool ignore_sel_reg;
+ struct discard_ap_list_t dis_ap_list[WPS_MAX_DIS_AP_NUM];
+ u8 discard_ap_cnt;
+ wifi_sta_config_t config;
+};
+
+#define IEEE80211_CAPINFO_PRIVACY 0x0010
+
+struct wps_sm *wps_sm_get(void);
+int wps_ssid_save(u8 *ssid, u8 ssid_len);
+int wps_key_save(char *key, u8 key_len);
+int wps_station_wps_register_cb(wps_st_cb_t cb);
+int wps_station_wps_unregister_cb(void);
+int wps_start_pending(void);
+int wps_sm_rx_eapol(u8 *src_addr, u8 *buf, u32 len);
+
+int wps_dev_deinit(struct wps_device_data *dev);
+#endif /* WPS_H */
--- /dev/null
+/*
+ * Wi-Fi Protected Setup - attribute parsing
+ * Copyright (c) 2008-2012, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef WPS_ATTR_PARSE_H
+#define WPS_ATTR_PARSE_H
+
+#include "wps/wps.h"
+
+struct wps_parse_attr {
+ /* fixed length fields */
+ const u8 *version; /* 1 octet */
+ const u8 *version2; /* 1 octet */
+ const u8 *msg_type; /* 1 octet */
+ const u8 *enrollee_nonce; /* WPS_NONCE_LEN (16) octets */
+ const u8 *registrar_nonce; /* WPS_NONCE_LEN (16) octets */
+ const u8 *uuid_r; /* WPS_UUID_LEN (16) octets */
+ const u8 *uuid_e; /* WPS_UUID_LEN (16) octets */
+ const u8 *auth_type_flags; /* 2 octets */
+ const u8 *encr_type_flags; /* 2 octets */
+ const u8 *conn_type_flags; /* 1 octet */
+ const u8 *config_methods; /* 2 octets */
+ const u8 *sel_reg_config_methods; /* 2 octets */
+ const u8 *primary_dev_type; /* 8 octets */
+ const u8 *rf_bands; /* 1 octet */
+ const u8 *assoc_state; /* 2 octets */
+ const u8 *config_error; /* 2 octets */
+ const u8 *dev_password_id; /* 2 octets */
+ const u8 *os_version; /* 4 octets */
+ const u8 *wps_state; /* 1 octet */
+ const u8 *authenticator; /* WPS_AUTHENTICATOR_LEN (8) octets */
+ const u8 *r_hash1; /* WPS_HASH_LEN (32) octets */
+ const u8 *r_hash2; /* WPS_HASH_LEN (32) octets */
+ const u8 *e_hash1; /* WPS_HASH_LEN (32) octets */
+ const u8 *e_hash2; /* WPS_HASH_LEN (32) octets */
+ const u8 *r_snonce1; /* WPS_SECRET_NONCE_LEN (16) octets */
+ const u8 *r_snonce2; /* WPS_SECRET_NONCE_LEN (16) octets */
+ const u8 *e_snonce1; /* WPS_SECRET_NONCE_LEN (16) octets */
+ const u8 *e_snonce2; /* WPS_SECRET_NONCE_LEN (16) octets */
+ const u8 *key_wrap_auth; /* WPS_KWA_LEN (8) octets */
+ const u8 *auth_type; /* 2 octets */
+ const u8 *encr_type; /* 2 octets */
+ const u8 *network_idx; /* 1 octet */
+ const u8 *network_key_idx; /* 1 octet */
+ const u8 *mac_addr; /* ETH_ALEN (6) octets */
+ const u8 *key_prov_auto; /* 1 octet (Bool) */
+ const u8 *dot1x_enabled; /* 1 octet (Bool) */
+ const u8 *selected_registrar; /* 1 octet (Bool) */
+ const u8 *request_type; /* 1 octet */
+ const u8 *response_type; /* 1 octet */
+ const u8 *ap_setup_locked; /* 1 octet */
+ const u8 *settings_delay_time; /* 1 octet */
+ const u8 *network_key_shareable; /* 1 octet (Bool) */
+ const u8 *request_to_enroll; /* 1 octet (Bool) */
+ const u8 *ap_channel; /* 2 octets */
+
+ /* variable length fields */
+ const u8 *manufacturer;
+ size_t manufacturer_len;
+ const u8 *model_name;
+ size_t model_name_len;
+ const u8 *model_number;
+ size_t model_number_len;
+ const u8 *serial_number;
+ size_t serial_number_len;
+ const u8 *dev_name;
+ size_t dev_name_len;
+ const u8 *public_key;
+ size_t public_key_len;
+ const u8 *encr_settings;
+ size_t encr_settings_len;
+ const u8 *ssid; /* <= 32 octets */
+ size_t ssid_len;
+ const u8 *network_key; /* <= 64 octets */
+ size_t network_key_len;
+ const u8 *eap_type; /* <= 8 octets */
+ size_t eap_type_len;
+ const u8 *eap_identity; /* <= 64 octets */
+ size_t eap_identity_len;
+ const u8 *authorized_macs; /* <= 30 octets */
+ size_t authorized_macs_len;
+ const u8 *sec_dev_type_list; /* <= 128 octets */
+ size_t sec_dev_type_list_len;
+ const u8 *oob_dev_password; /* 38..54 octets */
+ size_t oob_dev_password_len;
+
+ /* attributes that can occur multiple times */
+#define MAX_CRED_COUNT 10
+ const u8 *cred[MAX_CRED_COUNT];
+ size_t cred_len[MAX_CRED_COUNT];
+ size_t num_cred;
+
+#define MAX_REQ_DEV_TYPE_COUNT 10
+ const u8 *req_dev_type[MAX_REQ_DEV_TYPE_COUNT];
+ size_t num_req_dev_type;
+
+ const u8 *vendor_ext[MAX_WPS_PARSE_VENDOR_EXT];
+ size_t vendor_ext_len[MAX_WPS_PARSE_VENDOR_EXT];
+ size_t num_vendor_ext;
+};
+
+int wps_parse_msg(const struct wpabuf *msg, struct wps_parse_attr *attr);
+
+#endif /* WPS_ATTR_PARSE_H */
--- /dev/null
+/*
+ * Wi-Fi Protected Setup - message definitions
+ * Copyright (c) 2008, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef WPS_DEFS_H
+#define WPS_DEFS_H
+
+#ifdef CONFIG_WPS_TESTING
+
+extern int wps_version_number;
+extern int wps_testing_dummy_cred;
+#define WPS_VERSION wps_version_number
+
+#else /* CONFIG_WPS_TESTING */
+
+#ifdef CONFIG_WPS2
+#define WPS_VERSION 0x20
+#else /* CONFIG_WPS2 */
+#define WPS_VERSION 0x10
+#endif /* CONFIG_WPS2 */
+
+#endif /* CONFIG_WPS_TESTING */
+
+#define CONFIG_WPS_STRICT
+
+/* Diffie-Hellman 1536-bit MODP Group; RFC 3526, Group 5 */
+#define WPS_DH_GROUP 5
+
+#define WPS_UUID_LEN 16
+#define WPS_NONCE_LEN 16
+#define WPS_AUTHENTICATOR_LEN 8
+#define WPS_AUTHKEY_LEN 32
+#define WPS_KEYWRAPKEY_LEN 16
+#define WPS_EMSK_LEN 32
+#define WPS_PSK_LEN 16
+#define WPS_SECRET_NONCE_LEN 16
+#define WPS_HASH_LEN 32
+#define WPS_KWA_LEN 8
+#define WPS_MGMTAUTHKEY_LEN 32
+#define WPS_MGMTENCKEY_LEN 16
+#define WPS_MGMT_KEY_ID_LEN 16
+#define WPS_OOB_DEVICE_PASSWORD_MIN_LEN 16
+#define WPS_OOB_DEVICE_PASSWORD_LEN 32
+#define WPS_OOB_PUBKEY_HASH_LEN 20
+
+/* Attribute Types */
+enum wps_attribute {
+ ATTR_AP_CHANNEL = 0x1001,
+ ATTR_ASSOC_STATE = 0x1002,
+ ATTR_AUTH_TYPE = 0x1003,
+ ATTR_AUTH_TYPE_FLAGS = 0x1004,
+ ATTR_AUTHENTICATOR = 0x1005,
+ ATTR_CONFIG_METHODS = 0x1008,
+ ATTR_CONFIG_ERROR = 0x1009,
+ ATTR_CONFIRM_URL4 = 0x100a,
+ ATTR_CONFIRM_URL6 = 0x100b,
+ ATTR_CONN_TYPE = 0x100c,
+ ATTR_CONN_TYPE_FLAGS = 0x100d,
+ ATTR_CRED = 0x100e,
+ ATTR_ENCR_TYPE = 0x100f,
+ ATTR_ENCR_TYPE_FLAGS = 0x1010,
+ ATTR_DEV_NAME = 0x1011,
+ ATTR_DEV_PASSWORD_ID = 0x1012,
+ ATTR_E_HASH1 = 0x1014,
+ ATTR_E_HASH2 = 0x1015,
+ ATTR_E_SNONCE1 = 0x1016,
+ ATTR_E_SNONCE2 = 0x1017,
+ ATTR_ENCR_SETTINGS = 0x1018,
+ ATTR_ENROLLEE_NONCE = 0x101a,
+ ATTR_FEATURE_ID = 0x101b,
+ ATTR_IDENTITY = 0x101c,
+ ATTR_IDENTITY_PROOF = 0x101d,
+ ATTR_KEY_WRAP_AUTH = 0x101e,
+ ATTR_KEY_ID = 0x101f,
+ ATTR_MAC_ADDR = 0x1020,
+ ATTR_MANUFACTURER = 0x1021,
+ ATTR_MSG_TYPE = 0x1022,
+ ATTR_MODEL_NAME = 0x1023,
+ ATTR_MODEL_NUMBER = 0x1024,
+ ATTR_NETWORK_INDEX = 0x1026,
+ ATTR_NETWORK_KEY = 0x1027,
+ ATTR_NETWORK_KEY_INDEX = 0x1028,
+ ATTR_NEW_DEVICE_NAME = 0x1029,
+ ATTR_NEW_PASSWORD = 0x102a,
+ ATTR_OOB_DEVICE_PASSWORD = 0x102c,
+ ATTR_OS_VERSION = 0x102d,
+ ATTR_POWER_LEVEL = 0x102f,
+ ATTR_PSK_CURRENT = 0x1030,
+ ATTR_PSK_MAX = 0x1031,
+ ATTR_PUBLIC_KEY = 0x1032,
+ ATTR_RADIO_ENABLE = 0x1033,
+ ATTR_REBOOT = 0x1034,
+ ATTR_REGISTRAR_CURRENT = 0x1035,
+ ATTR_REGISTRAR_ESTABLISHED = 0x1036,
+ ATTR_REGISTRAR_LIST = 0x1037,
+ ATTR_REGISTRAR_MAX = 0x1038,
+ ATTR_REGISTRAR_NONCE = 0x1039,
+ ATTR_REQUEST_TYPE = 0x103a,
+ ATTR_RESPONSE_TYPE = 0x103b,
+ ATTR_RF_BANDS = 0x103c,
+ ATTR_R_HASH1 = 0x103d,
+ ATTR_R_HASH2 = 0x103e,
+ ATTR_R_SNONCE1 = 0x103f,
+ ATTR_R_SNONCE2 = 0x1040,
+ ATTR_SELECTED_REGISTRAR = 0x1041,
+ ATTR_SERIAL_NUMBER = 0x1042,
+ ATTR_WPS_STATE = 0x1044,
+ ATTR_SSID = 0x1045,
+ ATTR_TOTAL_NETWORKS = 0x1046,
+ ATTR_UUID_E = 0x1047,
+ ATTR_UUID_R = 0x1048,
+ ATTR_VENDOR_EXT = 0x1049,
+ ATTR_VERSION = 0x104a,
+ ATTR_X509_CERT_REQ = 0x104b,
+ ATTR_X509_CERT = 0x104c,
+ ATTR_EAP_IDENTITY = 0x104d,
+ ATTR_MSG_COUNTER = 0x104e,
+ ATTR_PUBKEY_HASH = 0x104f,
+ ATTR_REKEY_KEY = 0x1050,
+ ATTR_KEY_LIFETIME = 0x1051,
+ ATTR_PERMITTED_CFG_METHODS = 0x1052,
+ ATTR_SELECTED_REGISTRAR_CONFIG_METHODS = 0x1053,
+ ATTR_PRIMARY_DEV_TYPE = 0x1054,
+ ATTR_SECONDARY_DEV_TYPE_LIST = 0x1055,
+ ATTR_PORTABLE_DEV = 0x1056,
+ ATTR_AP_SETUP_LOCKED = 0x1057,
+ ATTR_APPLICATION_EXT = 0x1058,
+ ATTR_EAP_TYPE = 0x1059,
+ ATTR_IV = 0x1060,
+ ATTR_KEY_PROVIDED_AUTO = 0x1061,
+ ATTR_802_1X_ENABLED = 0x1062,
+ ATTR_APPSESSIONKEY = 0x1063,
+ ATTR_WEPTRANSMITKEY = 0x1064,
+ ATTR_REQUESTED_DEV_TYPE = 0x106a,
+ ATTR_EXTENSIBILITY_TEST = 0x10fa /* _NOT_ defined in the spec */
+};
+
+#define WPS_VENDOR_ID_WFA 14122
+
+/* WFA Vendor Extension subelements */
+enum {
+ WFA_ELEM_VERSION2 = 0x00,
+ WFA_ELEM_AUTHORIZEDMACS = 0x01,
+ WFA_ELEM_NETWORK_KEY_SHAREABLE = 0x02,
+ WFA_ELEM_REQUEST_TO_ENROLL = 0x03,
+ WFA_ELEM_SETTINGS_DELAY_TIME = 0x04
+};
+
+/* Device Password ID */
+enum wps_dev_password_id {
+ DEV_PW_DEFAULT = 0x0000,
+ DEV_PW_USER_SPECIFIED = 0x0001,
+ DEV_PW_MACHINE_SPECIFIED = 0x0002,
+ DEV_PW_REKEY = 0x0003,
+ DEV_PW_PUSHBUTTON = 0x0004,
+ DEV_PW_REGISTRAR_SPECIFIED = 0x0005
+};
+
+/* WPS message flag */
+enum wps_msg_flag {
+ WPS_MSG_FLAG_MORE = 0x01,
+ WPS_MSG_FLAG_LEN = 0x02
+};
+
+/* Message Type */
+enum wps_msg_type {
+ WPS_Beacon = 0x01,
+ WPS_ProbeRequest = 0x02,
+ WPS_ProbeResponse = 0x03,
+ WPS_M1 = 0x04,
+ WPS_M2 = 0x05,
+ WPS_M2D = 0x06,
+ WPS_M3 = 0x07,
+ WPS_M4 = 0x08,
+ WPS_M5 = 0x09,
+ WPS_M6 = 0x0a,
+ WPS_M7 = 0x0b,
+ WPS_M8 = 0x0c,
+ WPS_WSC_ACK = 0x0d,
+ WPS_WSC_NACK = 0x0e,
+ WPS_WSC_DONE = 0x0f
+};
+
+/* Authentication Type Flags */
+#define WPS_WIFI_AUTH_OPEN 0x0001
+#define WPS_AUTH_WPAPSK 0x0002
+#define WPS_AUTH_SHARED 0x0004
+#define WPS_AUTH_WPA 0x0008
+#define WPS_AUTH_WPA2 0x0010
+#define WPS_AUTH_WPA2PSK 0x0020
+#define WPS_AUTH_TYPES (WPS_WIFI_AUTH_OPEN | WPS_AUTH_WPAPSK | WPS_AUTH_SHARED | \
+ WPS_AUTH_WPA | WPS_AUTH_WPA2 | WPS_AUTH_WPA2PSK)
+
+/* Encryption Type Flags */
+#define WPS_ENCR_NONE 0x0001
+#define WPS_ENCR_WEP 0x0002
+#define WPS_ENCR_TKIP 0x0004
+#define WPS_ENCR_AES 0x0008
+#define WPS_ENCR_TYPES (WPS_ENCR_NONE | WPS_ENCR_WEP | WPS_ENCR_TKIP | \
+ WPS_ENCR_AES)
+
+/* Configuration Error */
+enum wps_config_error {
+ WPS_CFG_NO_ERROR = 0,
+ WPS_CFG_OOB_IFACE_READ_ERROR = 1,
+ WPS_CFG_DECRYPTION_CRC_FAILURE = 2,
+ WPS_CFG_24_CHAN_NOT_SUPPORTED = 3,
+ WPS_CFG_50_CHAN_NOT_SUPPORTED = 4,
+ WPS_CFG_SIGNAL_TOO_WEAK = 5,
+ WPS_CFG_NETWORK_AUTH_FAILURE = 6,
+ WPS_CFG_NETWORK_ASSOC_FAILURE = 7,
+ WPS_CFG_NO_DHCP_RESPONSE = 8,
+ WPS_CFG_FAILED_DHCP_CONFIG = 9,
+ WPS_CFG_IP_ADDR_CONFLICT = 10,
+ WPS_CFG_NO_CONN_TO_REGISTRAR = 11,
+ WPS_CFG_MULTIPLE_PBC_DETECTED = 12,
+ WPS_CFG_ROGUE_SUSPECTED = 13,
+ WPS_CFG_DEVICE_BUSY = 14,
+ WPS_CFG_SETUP_LOCKED = 15,
+ WPS_CFG_MSG_TIMEOUT = 16,
+ WPS_CFG_REG_SESS_TIMEOUT = 17,
+ WPS_CFG_DEV_PASSWORD_AUTH_FAILURE = 18
+};
+
+/* Vendor specific Error Indication for WPS event messages */
+enum wps_error_indication {
+ WPS_EI_NO_ERROR,
+ WPS_EI_SECURITY_TKIP_ONLY_PROHIBITED,
+ WPS_EI_SECURITY_WEP_PROHIBITED,
+ NUM_WPS_EI_VALUES
+};
+
+/* RF Bands */
+#define WPS_RF_24GHZ 0x01
+#define WPS_RF_50GHZ 0x02
+
+/* Config Methods */
+#define WPS_CONFIG_USBA 0x0001
+#define WPS_CONFIG_ETHERNET 0x0002
+#define WPS_CONFIG_LABEL 0x0004
+#define WPS_CONFIG_DISPLAY 0x0008
+#define WPS_CONFIG_EXT_NFC_TOKEN 0x0010
+#define WPS_CONFIG_INT_NFC_TOKEN 0x0020
+#define WPS_CONFIG_NFC_INTERFACE 0x0040
+#define WPS_CONFIG_PUSHBUTTON 0x0080
+#define WPS_CONFIG_KEYPAD 0x0100
+#ifdef CONFIG_WPS2
+#define WPS_CONFIG_VIRT_PUSHBUTTON 0x0280
+#define WPS_CONFIG_PHY_PUSHBUTTON 0x0480
+#define WPS_CONFIG_VIRT_DISPLAY 0x2008
+#define WPS_CONFIG_PHY_DISPLAY 0x4008
+#endif /* CONFIG_WPS2 */
+
+/* Connection Type Flags */
+#define WPS_CONN_ESS 0x01
+#define WPS_CONN_IBSS 0x02
+
+/* Wi-Fi Protected Setup State */
+enum wps_state {
+ WPS_STATE_NOT_CONFIGURED = 1,
+ WPS_STATE_CONFIGURED = 2
+};
+
+/* Association State */
+enum wps_assoc_state {
+ WPS_ASSOC_NOT_ASSOC = 0,
+ WPS_ASSOC_CONN_SUCCESS = 1,
+ WPS_ASSOC_CFG_FAILURE = 2,
+ WPS_ASSOC_FAILURE = 3,
+ WPS_ASSOC_IP_FAILURE = 4
+};
+
+
+#define WPS_DEV_OUI_WFA 0x0050f204
+
+enum wps_dev_categ {
+ WPS_DEV_COMPUTER = 1,
+ WPS_DEV_INPUT = 2,
+ WPS_DEV_PRINTER = 3,
+ WPS_DEV_CAMERA = 4,
+ WPS_DEV_STORAGE = 5,
+ WPS_DEV_NETWORK_INFRA = 6,
+ WPS_DEV_DISPLAY = 7,
+ WPS_DEV_MULTIMEDIA = 8,
+ WPS_DEV_GAMING = 9,
+ WPS_DEV_PHONE = 10
+};
+
+enum wps_dev_subcateg {
+ WPS_DEV_COMPUTER_PC = 1,
+ WPS_DEV_COMPUTER_SERVER = 2,
+ WPS_DEV_COMPUTER_MEDIA_CENTER = 3,
+ WPS_DEV_PRINTER_PRINTER = 1,
+ WPS_DEV_PRINTER_SCANNER = 2,
+ WPS_DEV_CAMERA_DIGITAL_STILL_CAMERA = 1,
+ WPS_DEV_STORAGE_NAS = 1,
+ WPS_DEV_NETWORK_INFRA_AP = 1,
+ WPS_DEV_NETWORK_INFRA_ROUTER = 2,
+ WPS_DEV_NETWORK_INFRA_SWITCH = 3,
+ WPS_DEV_DISPLAY_TV = 1,
+ WPS_DEV_DISPLAY_PICTURE_FRAME = 2,
+ WPS_DEV_DISPLAY_PROJECTOR = 3,
+ WPS_DEV_MULTIMEDIA_DAR = 1,
+ WPS_DEV_MULTIMEDIA_PVR = 2,
+ WPS_DEV_MULTIMEDIA_MCX = 3,
+ WPS_DEV_GAMING_XBOX = 1,
+ WPS_DEV_GAMING_XBOX360 = 2,
+ WPS_DEV_GAMING_PLAYSTATION = 3,
+ WPS_DEV_PHONE_WINDOWS_MOBILE = 1
+};
+
+
+/* Request Type */
+enum wps_request_type {
+ WPS_REQ_ENROLLEE_INFO = 0,
+ WPS_REQ_ENROLLEE = 1,
+ WPS_REQ_REGISTRAR = 2,
+ WPS_REQ_WLAN_MANAGER_REGISTRAR = 3
+};
+
+/* Response Type */
+enum wps_response_type {
+ WPS_RESP_ENROLLEE_INFO = 0,
+ WPS_RESP_ENROLLEE = 1,
+ WPS_RESP_REGISTRAR = 2,
+ WPS_RESP_AP = 3
+};
+
+/* Walk Time for push button configuration (in seconds) */
+#define WPS_PBC_WALK_TIME 120
+
+#define WPS_MAX_AUTHORIZED_MACS 5
+
+#define WPS_IGNORE_SEL_REG_MAX_CNT 4
+
+#define WPS_MAX_DIS_AP_NUM 10
+
+#endif /* WPS_DEFS_H */
--- /dev/null
+/*
+ * Wi-Fi Protected Setup - device attributes
+ * Copyright (c) 2008, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef WPS_DEV_ATTR_H
+#define WPS_DEV_ATTR_H
+
+struct wps_parse_attr;
+
+int wps_build_manufacturer(struct wps_device_data *dev, struct wpabuf *msg);
+int wps_build_model_name(struct wps_device_data *dev, struct wpabuf *msg);
+int wps_build_model_number(struct wps_device_data *dev, struct wpabuf *msg);
+int wps_build_dev_name(struct wps_device_data *dev, struct wpabuf *msg);
+int wps_build_device_attrs(struct wps_device_data *dev, struct wpabuf *msg);
+int wps_build_os_version(struct wps_device_data *dev, struct wpabuf *msg);
+int wps_build_vendor_ext_m1(struct wps_device_data *dev, struct wpabuf *msg);
+int wps_build_rf_bands(struct wps_device_data *dev, struct wpabuf *msg);
+int wps_build_primary_dev_type(struct wps_device_data *dev,
+ struct wpabuf *msg);
+int wps_build_secondary_dev_type(struct wps_device_data *dev,
+ struct wpabuf *msg);
+int wps_build_dev_name(struct wps_device_data *dev, struct wpabuf *msg);
+int wps_process_device_attrs(struct wps_device_data *dev,
+ struct wps_parse_attr *attr);
+int wps_process_os_version(struct wps_device_data *dev, const u8 *ver);
+int wps_process_rf_bands(struct wps_device_data *dev, const u8 *bands);
+void wps_device_data_dup(struct wps_device_data *dst,
+ const struct wps_device_data *src);
+void wps_device_data_free(struct wps_device_data *dev);
+int wps_build_vendor_ext(struct wps_device_data *dev, struct wpabuf *msg);
+int wps_build_req_dev_type(struct wps_device_data *dev, struct wpabuf *msg,
+ unsigned int num_req_dev_types,
+ const u8 *req_dev_types);
+
+#endif /* WPS_DEV_ATTR_H */
--- /dev/null
+/*
+ * Wi-Fi Protected Setup - internal definitions
+ * Copyright (c) 2008-2012, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef WPS_I_H
+#define WPS_I_H
+
+#include "wps.h"
+#include "wps_attr_parse.h"
+#include "esp_wifi_crypto_types.h"
+
+#ifdef CONFIG_WPS_NFC
+struct wps_nfc_pw_token;
+#endif
+/**
+ * struct wps_data - WPS registration protocol data
+ *
+ * This data is stored at the EAP-WSC server/peer method and it is kept for a
+ * single registration protocol run.
+ */
+struct wps_data {
+ /**
+ * wps - Pointer to long term WPS context
+ */
+ struct wps_context *wps;
+
+ /**
+ * registrar - Whether this end is a Registrar
+ */
+ int registrar;
+
+ /**
+ * er - Whether the local end is an external registrar
+ */
+ int er;
+
+ enum {
+ /* Enrollee states */
+ SEND_M1, RECV_M2, SEND_M3, RECV_M4, SEND_M5, RECV_M6, SEND_M7,
+ RECV_M8, RECEIVED_M2D, WPS_MSG_DONE, RECV_ACK, WPS_FINISHED,
+ SEND_WSC_NACK,
+
+ /* Registrar states */
+ RECV_M1, SEND_M2, RECV_M3, SEND_M4, RECV_M5, SEND_M6,
+ RECV_M7, SEND_M8, RECV_DONE, SEND_M2D, RECV_M2D_ACK
+ } state;
+
+ u8 uuid_e[WPS_UUID_LEN];
+ u8 uuid_r[WPS_UUID_LEN];
+ u8 mac_addr_e[ETH_ALEN];
+ u8 nonce_e[WPS_NONCE_LEN];
+ u8 nonce_r[WPS_NONCE_LEN];
+ u8 psk1[WPS_PSK_LEN];
+ u8 psk2[WPS_PSK_LEN];
+ u8 snonce[2 * WPS_SECRET_NONCE_LEN];
+ u8 peer_hash1[WPS_HASH_LEN];
+ u8 peer_hash2[WPS_HASH_LEN];
+
+ struct wpabuf *dh_privkey;
+ struct wpabuf *dh_pubkey_e;
+ struct wpabuf *dh_pubkey_r;
+ u8 authkey[WPS_AUTHKEY_LEN];
+ u8 keywrapkey[WPS_KEYWRAPKEY_LEN];
+ u8 emsk[WPS_EMSK_LEN];
+
+ struct wpabuf *last_msg;
+
+ u8 *dev_password;
+ size_t dev_password_len;
+ u16 dev_pw_id;
+ int pbc;
+
+ /**
+ * request_type - Request Type attribute from (Re)AssocReq
+ */
+ u8 request_type;
+
+ /**
+ * encr_type - Available encryption types
+ */
+ u16 encr_type;
+
+ /**
+ * auth_type - Available authentication types
+ */
+ u16 auth_type;
+
+ u8 *new_psk;
+ size_t new_psk_len;
+
+ int wps_pin_revealed;
+ struct wps_credential cred;
+
+ struct wps_device_data peer_dev;
+
+ /**
+ * config_error - Configuration Error value to be used in NACK
+ */
+ u16 config_error;
+ u16 error_indication;
+
+ int ext_reg;
+ int int_reg;
+
+ struct wps_credential *new_ap_settings;
+
+ void *dh_ctx;
+
+ void (*ap_settings_cb)(void *ctx, const struct wps_credential *cred);
+ void *ap_settings_cb_ctx;
+
+ struct wps_credential *use_cred;
+
+ int use_psk_key;
+ u8 p2p_dev_addr[ETH_ALEN]; /* P2P Device Address of the client or
+ * 00:00:00:00:00:00 if not a P2p client */
+ int pbc_in_m1;
+#ifdef CONFIG_WPS_NFC
+ struct wps_nfc_pw_token *nfc_pw_token;
+#endif
+};
+
+wps_crypto_funcs_t wps_crypto_funcs;
+
+/* wps_common.c */
+void wps_kdf(const u8 *key, const u8 *label_prefix, size_t label_prefix_len,
+ const char *label, u8 *res, size_t res_len);
+int wps_derive_keys(struct wps_data *wps);
+void wps_derive_psk(struct wps_data *wps, const u8 *dev_passwd,
+ size_t dev_passwd_len);
+struct wpabuf * wps_decrypt_encr_settings(struct wps_data *wps, const u8 *encr,
+ size_t encr_len);
+void wps_fail_event(struct wps_context *wps, enum wps_msg_type msg,
+ u16 config_error, u16 error_indication);
+void wps_success_event(struct wps_context *wps);
+void wps_pwd_auth_fail_event(struct wps_context *wps, int enrollee, int part);
+void wps_pbc_overlap_event(struct wps_context *wps);
+void wps_pbc_timeout_event(struct wps_context *wps);
+
+struct wpabuf * wps_build_wsc_ack(struct wps_data *wps);
+struct wpabuf * wps_build_wsc_nack(struct wps_data *wps);
+
+typedef enum wps_calc_key_mode {
+ WPS_CALC_KEY_NORMAL = 0,
+ WPS_CALC_KEY_NO_CALC,
+ WPS_CALC_KEY_PRE_CALC,
+ WPS_CALC_KEY_MAX,
+} wps_key_mode_t;
+
+/* wps_attr_build.c */
+int wps_build_public_key(struct wps_data *wps, struct wpabuf *msg, wps_key_mode_t mode);
+int wps_build_req_type(struct wpabuf *msg, enum wps_request_type type);
+int wps_build_resp_type(struct wpabuf *msg, enum wps_response_type type);
+int wps_build_config_methods(struct wpabuf *msg, u16 methods);
+int wps_build_uuid_e(struct wpabuf *msg, const u8 *uuid);
+int wps_build_dev_password_id(struct wpabuf *msg, u16 id);
+int wps_build_config_error(struct wpabuf *msg, u16 err);
+int wps_build_authenticator(struct wps_data *wps, struct wpabuf *msg);
+int wps_build_key_wrap_auth(struct wps_data *wps, struct wpabuf *msg);
+int wps_build_encr_settings(struct wps_data *wps, struct wpabuf *msg,
+ struct wpabuf *plain);
+int wps_build_version(struct wpabuf *msg);
+int wps_build_wfa_ext(struct wpabuf *msg, int req_to_enroll,
+ const u8 *auth_macs, size_t auth_macs_count);
+int wps_build_msg_type(struct wpabuf *msg, enum wps_msg_type msg_type);
+int wps_build_enrollee_nonce(struct wps_data *wps, struct wpabuf *msg);
+int wps_build_registrar_nonce(struct wps_data *wps, struct wpabuf *msg);
+int wps_build_auth_type_flags(struct wps_data *wps, struct wpabuf *msg);
+int wps_build_encr_type_flags(struct wps_data *wps, struct wpabuf *msg);
+int wps_build_conn_type_flags(struct wps_data *wps, struct wpabuf *msg);
+int wps_build_assoc_state(struct wps_data *wps, struct wpabuf *msg);
+int wps_build_oob_dev_pw(struct wpabuf *msg, u16 dev_pw_id,
+ const struct wpabuf *pubkey, const u8 *dev_pw,
+ size_t dev_pw_len);
+struct wpabuf * wps_ie_encapsulate(struct wpabuf *data);
+
+/* wps_attr_process.c */
+int wps_process_authenticator(struct wps_data *wps, const u8 *authenticator,
+ const struct wpabuf *msg);
+int wps_process_key_wrap_auth(struct wps_data *wps, struct wpabuf *msg,
+ const u8 *key_wrap_auth);
+int wps_process_cred(struct wps_parse_attr *attr,
+ struct wps_credential *cred);
+int wps_process_ap_settings(struct wps_parse_attr *attr,
+ struct wps_credential *cred);
+
+/* wps_enrollee.c */
+struct wpabuf * wps_enrollee_get_msg(struct wps_data *wps,
+ enum wsc_op_code *op_code);
+enum wps_process_res wps_enrollee_process_msg(struct wps_data *wps,
+ enum wsc_op_code op_code,
+ const struct wpabuf *msg);
+
+/* wps_registrar.c */
+struct wpabuf * wps_registrar_get_msg(struct wps_data *wps,
+ enum wsc_op_code *op_code);
+enum wps_process_res wps_registrar_process_msg(struct wps_data *wps,
+ enum wsc_op_code op_code,
+ const struct wpabuf *msg);
+int wps_build_cred(struct wps_data *wps, struct wpabuf *msg);
+int wps_device_store(struct wps_registrar *reg,
+ struct wps_device_data *dev, const u8 *uuid);
+void wps_registrar_selected_registrar_changed(struct wps_registrar *reg);
+const u8 * wps_authorized_macs(struct wps_registrar *reg, size_t *count);
+int wps_registrar_pbc_overlap(struct wps_registrar *reg,
+ const u8 *addr, const u8 *uuid_e);
+#ifdef CONFIG_WPS_NFC
+
+void wps_registrar_remove_nfc_pw_token(struct wps_registrar *reg,
+ struct wps_nfc_pw_token *token);
+#endif
+
+#endif /* WPS_I_H */
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
+#include "esp_err.h"
#include "rom/ets_sys.h"
typedef long os_time_t;
#define os_free(p) free((p))
#endif
+#ifndef os_bzero
+#define os_bzero(s, n) bzero(s, n)
+#endif
+
#ifndef os_strdup
#ifdef _MSC_VER
--- /dev/null
+/*
+ * CHAP-MD5
+ *
+ */
+#ifdef CHAP_MD5
+
+#include "wpa/includes.h"
+#include "wpa/common.h"
+#include "crypto/crypto.h"
+#include "wpa2/eap_peer/chap.h"
+
+int chap_md5(u8 id, const u8 *secret, size_t secret_len, const u8 *challenge,
+ size_t challenge_len, u8 *response)
+{
+ const u8 *addr[3];
+ size_t len[3];
+
+ addr[0] = &id;
+ len[0] = 1;
+ addr[1] = secret;
+ len[1] = secret_len;
+ addr[2] = challenge;
+ len[2] = challenge_len;
+ return md5_vector(3, addr, len, response);
+}
+
+#endif /* CHAP_MD5 */
--- /dev/null
+/*
+ * EAP peer state machines (RFC 4137)
+ * Copyright (c) 2004-2012, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ *
+ * This file implements the Peer State Machine as defined in RFC 4137. The used
+ * states and state transitions match mostly with the RFC. However, there are
+ * couple of additional transitions for working around small issues noticed
+ * during testing. These exceptions are explained in comments within the
+ * functions in this file. The method functions, m.func(), are similar to the
+ * ones used in RFC 4137, but some small changes have used here to optimize
+ * operations and to add functionality needed for fast re-authentication
+ * (session resumption).
+ */
+#include <string.h>
+
+#include "esp_err.h"
+
+#include "wpa/includes.h"
+#include "wpa/common.h"
+#include "wpa/wpa_debug.h"
+#include "wpa/eapol_common.h"
+#include "wpa/ieee802_11_defs.h"
+#include "wpa/state_machine.h"
+#include "wpa/wpa.h"
+
+#include "crypto/crypto.h"
+
+#include "wpa2/utils/ext_password.h"
+#include "wpa2/tls/tls.h"
+#include "wpa2/eap_peer/eap_i.h"
+#include "wpa2/eap_peer/eap_config.h"
+#include "wpa2/eap_peer/eap.h"
+#include "wpa2/eap_peer/eap_tls.h"
+#ifdef EAP_PEER_METHOD
+#include "wpa2/eap_peer/eap_methods.h"
+#endif
+
+
+static bool gl_disable_time_check = true;
+void eap_peer_config_deinit(struct eap_sm *sm);
+void eap_peer_blob_deinit(struct eap_sm *sm);
+void eap_deinit_prev_method(struct eap_sm *sm, const char *txt);
+
+extern bool ieee80211_unregister_wpa2_cb(void);
+
+#ifdef EAP_PEER_METHOD
+static struct eap_method *eap_methods = NULL;
+
+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;
+}
+
+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;
+}
+
+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;
+}
+
+static int
+eap_allowed_phase2_type(int vendor, int type)
+{
+ if (vendor != EAP_VENDOR_IETF)
+ return 0;
+ return type != EAP_TYPE_PEAP && type != EAP_TYPE_TTLS &&
+ type != EAP_TYPE_FAST;
+}
+
+u32 eap_get_phase2_type(const char *name, int *vendor)
+{
+ int v;
+ u8 type = eap_peer_get_type(name, &v);
+ if (eap_allowed_phase2_type(v, type)) {
+ *vendor = v;
+ return type;
+ }
+ *vendor = EAP_VENDOR_IETF;
+ return EAP_TYPE_NONE;
+}
+
+struct eap_method_type * eap_get_phase2_types(struct eap_peer_config *config,
+ size_t *count)
+{
+ struct eap_method_type *buf;
+ u32 method;
+ int vendor;
+ size_t mcount;
+ const struct eap_method *methods, *m;
+
+ methods = eap_peer_get_methods(&mcount);
+ if (methods == NULL)
+ return NULL;
+ *count = 0;
+ buf = os_malloc(mcount * sizeof(struct eap_method_type));
+ if (buf == NULL)
+ return NULL;
+
+ for (m = methods; m; m = m->next) {
+ vendor = m->vendor;
+ method = m->method;
+ if (eap_allowed_phase2_type(vendor, method)) {
+ if (vendor == EAP_VENDOR_IETF &&
+ method == EAP_TYPE_TLS && config &&
+ config->private_key2 == NULL)
+ continue;
+ buf[*count].vendor = vendor;
+ buf[*count].method = method;
+ (*count)++;
+ }
+ }
+
+ return buf;
+}
+
+struct eap_method * eap_peer_method_alloc(int vendor, EapType method,
+ const char *name)
+{
+ struct eap_method *eap;
+ eap = (struct eap_method *)os_zalloc(sizeof(*eap));
+ if (eap == NULL)
+ return NULL;
+ eap->vendor = vendor;
+ eap->method = method;
+ eap->name = name;
+ return eap;
+}
+
+void eap_peer_method_free(struct eap_method *method)
+{
+ os_free(method);
+}
+
+int eap_peer_method_register(struct eap_method *method)
+{
+ struct eap_method *m, *last = NULL;
+
+ if (method == NULL || method->name == NULL)
+ 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))
+ return -2;
+ last = m;
+ }
+ if (last)
+ last->next = method;
+ else
+ eap_methods = method;
+ return 0;
+}
+
+void eap_peer_unregister_methods(void)
+{
+ struct eap_method *m;
+ while (eap_methods) {
+ m = eap_methods;
+ eap_methods = eap_methods->next;
+
+ if (m->free)
+ m->free(m);
+ else
+ eap_peer_method_free(m);
+ }
+}
+
+int eap_peer_register_methods(void)
+{
+ int ret = 0;
+
+#ifdef EAP_MD5
+ if (ret == 0)
+ ret = eap_peer_md5_register();
+#endif
+
+#ifdef EAP_TLS
+ if (ret == 0)
+ ret = eap_peer_tls_register();
+#endif
+
+#ifdef EAP_MSCHAPv2
+ if (ret == 0)
+ ret = eap_peer_mschapv2_register();
+#endif
+
+#ifdef EAP_PEAP
+ if (ret == 0)
+ ret = eap_peer_peap_register();
+#endif
+
+#ifdef EAP_TTLS
+ if (ret == 0)
+ ret = eap_peer_ttls_register();
+#endif
+
+ return ret;
+}
+
+void eap_deinit_prev_method(struct eap_sm *sm, const char *txt)
+{
+ if (sm->m == NULL || sm->eap_method_priv == NULL)
+ return;
+ sm->m->deinit(sm, sm->eap_method_priv);
+ sm->eap_method_priv = NULL;
+ sm->m = NULL;
+}
+
+struct wpabuf * eap_sm_build_identity_resp(struct eap_sm *sm, u8 id, int encrypted)
+{
+ const u8 *identity;
+ size_t identity_len;
+ struct wpabuf *eap_buf = NULL;
+ struct eap_peer_config *config = eap_get_config(sm);
+
+ if (config == NULL) {
+ wpa_printf(MSG_ERROR, "EAP: Build Identity Resp-> configuration was not available\n");
+ return NULL;
+ }
+
+ if (sm->m && sm->m->get_identity) {
+ identity = sm->m->get_identity(sm,
+ sm->eap_method_priv,
+ &identity_len);
+ } else if (!encrypted && config->anonymous_identity) {
+ identity = config->anonymous_identity;
+ identity_len = config->anonymous_identity_len;
+ } else {
+ identity = config->identity;
+ identity_len = config->identity_len;
+ }
+
+ if (identity == NULL) {
+ wpa_printf(MSG_ERROR, "EAP: Build Identity Resp-> identity was not available\n");
+ return NULL;
+ }
+
+ eap_buf = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_IDENTITY,
+ identity_len, EAP_CODE_RESPONSE, id);
+ if (!eap_buf) {
+ return NULL;
+ }
+
+ wpabuf_put_data(eap_buf, identity, identity_len);
+ return eap_buf;
+}
+
+struct wpabuf * eap_sm_build_nak(struct eap_sm *sm, EapType type, u8 id)
+{
+ size_t count = 0;
+ int found = 0;
+ struct wpabuf *resp;
+ const struct eap_method *methods, *m;
+
+ methods = eap_peer_get_methods(&count);
+ if (methods == NULL)
+ return NULL;
+
+ if (type == EAP_TYPE_EXPANDED) {
+ /*Build Expanded NAK*/
+ resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_EXPANDED,
+ 8 + 8 * (count + 1), EAP_CODE_RESPONSE, id);
+ if (resp == NULL)
+ return NULL;
+ wpabuf_put_be24(resp, EAP_VENDOR_IETF);
+ wpabuf_put_be32(resp, EAP_TYPE_NAK);
+ } else {
+ resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_NAK,
+ sizeof(struct eap_hdr) + 1 + count + 1,
+ EAP_CODE_RESPONSE, id);
+ if (resp == NULL)
+ return NULL;
+ wpabuf_put(resp, 0);
+ }
+
+ for (m = methods; m; m = m->next) {
+ if (type == EAP_TYPE_EXPANDED) {
+ wpabuf_put_u8(resp, EAP_TYPE_EXPANDED);
+ wpabuf_put_be24(resp, m->vendor);
+ wpabuf_put_be32(resp, m->method);
+ } else
+ wpabuf_put_u8(resp, EAP_TYPE_NONE);
+ found++;
+ }
+ if (!found) {
+ if (type == EAP_TYPE_EXPANDED) {
+ wpabuf_put_u8(resp, EAP_TYPE_EXPANDED);
+ wpabuf_put_be24(resp, EAP_VENDOR_IETF);
+ wpabuf_put_be32(resp, EAP_TYPE_NONE);
+ } else
+ wpabuf_put_u8(resp, EAP_TYPE_NONE);
+ }
+ eap_update_len(resp);
+ return resp;
+}
+#endif
+
+int eap_peer_config_init(
+ struct eap_sm *sm, u8 *private_key_passwd,
+ int private_key_passwd_len)
+{
+ if (!sm)
+ return -1;
+
+ sm->config.anonymous_identity = NULL;
+ sm->config.identity = NULL;
+ sm->config.password = NULL;
+ sm->config.new_password = NULL;
+
+ sm->config.private_key_passwd = private_key_passwd;
+ sm->config.client_cert = (u8 *)sm->blob[0].name;
+ sm->config.private_key = (u8 *)sm->blob[1].name;
+ sm->config.ca_cert = (u8 *)sm->blob[2].name;
+
+ sm->config.ca_path = NULL;
+
+ sm->config.fragment_size = 1400; /* fragment size */
+
+ /* anonymous identity */
+ if (g_wpa_anonymous_identity && g_wpa_anonymous_identity_len > 0) {
+ sm->config.anonymous_identity_len = g_wpa_anonymous_identity_len;
+ sm->config.anonymous_identity = (u8 *)os_zalloc(sm->config.anonymous_identity_len);
+ if (sm->config.anonymous_identity == NULL)
+ return -2;
+ os_memcpy(sm->config.anonymous_identity, g_wpa_anonymous_identity, g_wpa_anonymous_identity_len);
+ }
+
+ /* Configre identity */
+ if (g_wpa_username && g_wpa_username_len > 0) {
+ sm->config.identity_len = g_wpa_username_len;
+ sm->config.identity = (u8 *)os_zalloc(sm->config.identity_len);
+ if (sm->config.identity == NULL) {
+ return -2;
+ }
+ os_memcpy(sm->config.identity, g_wpa_username, g_wpa_username_len);
+ }
+
+ if (g_wpa_password && g_wpa_password_len) {
+ sm->config.password_len = g_wpa_password_len;
+ sm->config.password = (u8 *)os_zalloc(sm->config.password_len);
+ if (sm->config.password == NULL)
+ return -2;
+ os_memcpy(sm->config.password, g_wpa_password, sm->config.password_len);
+ }
+
+ if (g_wpa_new_password && g_wpa_new_password_len) {
+ sm->config.new_password_len = g_wpa_new_password_len;
+ sm->config.new_password = (u8 *)os_zalloc(sm->config.new_password_len);
+ if (sm->config.new_password == NULL)
+ return -2;
+ os_memcpy(sm->config.new_password, g_wpa_new_password,
+ sm->config.new_password_len);
+ }
+
+ return 0;
+
+}
+
+void eap_peer_config_deinit(struct eap_sm *sm)
+{
+ if (!sm)
+ return;
+
+ os_free(sm->config.anonymous_identity);
+ os_free(sm->config.identity);
+ os_free(sm->config.password);
+ os_free(sm->config.new_password);
+ os_bzero(&sm->config, sizeof(struct eap_peer_config));
+}
+
+int eap_peer_blob_init(struct eap_sm *sm)
+{
+ int i, ret;
+
+ if (!sm)
+ return -1;
+
+ if (g_wpa_client_cert && g_wpa_client_cert_len) {
+ sm->blob[0].name = (char *)os_zalloc(BLOB_NAME_LEN+1);
+ if (sm->blob[0].name == NULL) {
+ ret = -2;
+ goto _out;
+ }
+ os_strncpy(sm->blob[0].name, CLIENT_CERT_NAME, BLOB_NAME_LEN);
+ sm->blob[0].len = g_wpa_client_cert_len;
+ sm->blob[0].data = g_wpa_client_cert;
+ }
+
+ if (g_wpa_private_key && g_wpa_private_key_len) {
+ sm->blob[1].name = (char *)os_zalloc(BLOB_NAME_LEN+1);
+ if (sm->blob[1].name == NULL) {
+ ret = -2;
+ goto _out;
+ }
+ os_strncpy(sm->blob[1].name, PRIVATE_KEY_NAME, BLOB_NAME_LEN);
+ sm->blob[1].len = g_wpa_private_key_len;
+ sm->blob[1].data = g_wpa_private_key;
+ }
+
+ if (g_wpa_ca_cert && g_wpa_ca_cert_len) {
+ sm->blob[2].name = (char *)os_zalloc(BLOB_NAME_LEN+1);
+ if (sm->blob[2].name == NULL) {
+ ret = -2;
+ goto _out;
+ }
+ os_strncpy(sm->blob[2].name, CA_CERT_NAME, BLOB_NAME_LEN);
+ sm->blob[2].len = g_wpa_ca_cert_len;
+ sm->blob[2].data = g_wpa_ca_cert;
+ }
+
+ return 0;
+_out:
+ for (i = 0; i < BLOB_NUM; i++) {
+ if (sm->blob[i].name) {
+ os_free(sm->blob[i].name);
+ sm->blob[i].name = NULL;
+ }
+ }
+ os_bzero(&sm->blob[0], sizeof(struct wpa_config_blob)*BLOB_NUM);
+
+ return ret;
+}
+
+void eap_peer_blob_deinit(struct eap_sm *sm)
+{
+ int i;
+ for (i = 0; i < BLOB_NUM; i++) {
+ if (sm->blob[i].name) {
+ os_free(sm->blob[i].name);
+ sm->blob[i].name = NULL;
+ }
+ }
+ os_bzero(&sm->blob[0], sizeof(struct wpa_config_blob)*BLOB_NUM);
+
+ sm->config.client_cert = NULL;
+ sm->config.private_key = NULL;
+ sm->config.ca_cert = NULL;
+}
+
+void eap_sm_abort(struct eap_sm *sm)
+{
+ wpabuf_free(sm->lastRespData);
+ sm->lastRespData = NULL;
+ //os_free(sm->eapKeyData);
+ //sm->eapKeyData = NULL;
+}
+
+/**
+ * eap_get_config - Get current network configuration
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * Returns: Pointer to the current network configuration or %NULL if not found
+ *
+ * EAP peer methods should avoid using this function if they can use other
+ * access functions, like eap_get_config_identity() and
+ * eap_get_config_password(), that do not require direct access to
+ * struct eap_peer_config.
+ */
+struct eap_peer_config * eap_get_config(struct eap_sm *sm)
+{
+ return &sm->config;
+}
+
+const u8 * eap_get_config_identity(struct eap_sm *sm, size_t *len)
+{
+ struct eap_peer_config *config = eap_get_config(sm);
+ if (config == NULL)
+ return NULL;
+ *len = config->identity_len;
+ return config->identity;
+}
+
+const u8 * eap_get_config_password(struct eap_sm *sm, size_t *len)
+{
+ struct eap_peer_config *config = eap_get_config(sm);
+ if (config == NULL)
+ return NULL;
+ *len = config->password_len;
+ return config->password;
+}
+
+const u8 * eap_get_config_password2(struct eap_sm *sm, size_t *len, int *hash)
+{
+ struct eap_peer_config *config = eap_get_config(sm);
+ if (config == NULL)
+ return NULL;
+
+ *len = config->password_len;
+ if (hash)
+ *hash = !!(config->flags & EAP_CONFIG_FLAGS_PASSWORD_NTHASH);
+ return config->password;
+}
+
+const u8 * eap_get_config_new_password(struct eap_sm *sm, size_t *len)
+{
+ struct eap_peer_config *config = eap_get_config(sm);
+ if (config == NULL)
+ return NULL;
+ *len = config->new_password_len;
+ return config->new_password;
+}
+/**
+ * eap_get_config_blob - Get a named configuration blob
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @name: Name of the blob
+ * Returns: Pointer to blob data or %NULL if not found
+ */
+const struct wpa_config_blob * eap_get_config_blob(struct eap_sm *sm,
+ const char *name)
+{
+ int i;
+
+ if (!sm)
+ return NULL;
+
+ for (i = 0; i < BLOB_NUM; i++) {
+ if (sm->blob[i].name == NULL)
+ continue;
+ if (os_strncmp(name, sm->blob[i].name, BLOB_NAME_LEN) == 0) {
+ return &sm->blob[i];
+ }
+ }
+ return NULL;
+}
+
+esp_err_t esp_wifi_sta_wpa2_ent_set_cert_key(const unsigned char *client_cert, int client_cert_len, const unsigned char *private_key, int private_key_len, const unsigned char *private_key_passwd, int private_key_passwd_len)
+{
+ if (client_cert && client_cert_len > 0) {
+ g_wpa_client_cert = client_cert;
+ g_wpa_client_cert_len = client_cert_len;
+ }
+ if (private_key && private_key_len > 0) {
+ g_wpa_private_key = private_key;
+ g_wpa_private_key_len = private_key_len;
+ }
+ if (private_key_passwd && private_key_passwd_len > 0) {
+ g_wpa_private_key_passwd = private_key_passwd;
+ g_wpa_private_key_passwd_len = private_key_passwd_len;
+ }
+
+ return ESP_OK;
+}
+
+void esp_wifi_sta_wpa2_ent_clear_cert_key(void)
+{
+ ieee80211_unregister_wpa2_cb();
+
+ g_wpa_client_cert = NULL;
+ g_wpa_client_cert_len = 0;
+ g_wpa_private_key = NULL;
+ g_wpa_private_key_len = 0;
+ g_wpa_private_key_passwd = NULL;
+ g_wpa_private_key_passwd_len = 0;
+}
+
+esp_err_t esp_wifi_sta_wpa2_ent_set_ca_cert(const unsigned char *ca_cert, int ca_cert_len)
+{
+ if (ca_cert && ca_cert_len > 0) {
+ g_wpa_ca_cert = ca_cert;
+ g_wpa_ca_cert_len = ca_cert_len;
+ }
+
+ return ESP_OK;
+}
+
+void esp_wifi_sta_wpa2_ent_clear_ca_cert(void)
+{
+ g_wpa_ca_cert = NULL;
+ g_wpa_ca_cert_len = 0;
+}
+
+#define ANONYMOUS_ID_LEN_MAX 128
+esp_err_t esp_wifi_sta_wpa2_ent_set_identity(const unsigned char *identity, int len)
+{
+ if (len <= 0 || len > ANONYMOUS_ID_LEN_MAX) {
+ return ESP_ERR_INVALID_ARG;
+ }
+
+ if (g_wpa_anonymous_identity) {
+ os_free(g_wpa_anonymous_identity);
+ g_wpa_anonymous_identity = NULL;
+ }
+
+ g_wpa_anonymous_identity = (u8 *)os_zalloc(len);
+ if (g_wpa_anonymous_identity == NULL) {
+ return ESP_ERR_NO_MEM;
+ }
+
+ os_memcpy(g_wpa_anonymous_identity, identity, len);
+ g_wpa_anonymous_identity_len = len;
+
+ return ESP_OK;
+}
+
+void esp_wifi_sta_wpa2_ent_clear_identity(void)
+{
+ if (g_wpa_anonymous_identity)
+ os_free(g_wpa_anonymous_identity);
+
+ g_wpa_anonymous_identity = NULL;
+ g_wpa_anonymous_identity_len = 0;
+}
+
+#define USERNAME_LEN_MAX 128
+esp_err_t esp_wifi_sta_wpa2_ent_set_username(const unsigned char *username, int len)
+{
+ if (len <= 0 || len > USERNAME_LEN_MAX)
+ return ESP_ERR_INVALID_ARG;
+
+ if (g_wpa_username) {
+ os_free(g_wpa_username);
+ g_wpa_username = NULL;
+ }
+
+ g_wpa_username = (u8 *)os_zalloc(len);
+ if (g_wpa_username == NULL)
+ return ESP_ERR_NO_MEM;
+
+ os_memcpy(g_wpa_username, username, len);
+ g_wpa_username_len = len;
+
+ return ESP_OK;
+}
+
+void esp_wifi_sta_wpa2_ent_clear_username(void)
+{
+ if (g_wpa_username)
+ os_free(g_wpa_username);
+
+ g_wpa_username = NULL;
+ g_wpa_username_len = 0;
+}
+
+esp_err_t esp_wifi_sta_wpa2_ent_set_password(const unsigned char *password, int len)
+{
+ if (len <= 0)
+ return ESP_ERR_INVALID_ARG;
+
+ if (g_wpa_password) {
+ os_free(g_wpa_password);
+ g_wpa_password = NULL;
+ }
+
+ g_wpa_password = (u8 *)os_zalloc(len);
+ if (g_wpa_password == NULL)
+ return ESP_ERR_NO_MEM;
+
+ os_memcpy(g_wpa_password, password, len);
+ g_wpa_password_len = len;
+
+ return ESP_OK;
+}
+
+void esp_wifi_sta_wpa2_ent_clear_password(void)
+{
+ if (g_wpa_password)
+ os_free(g_wpa_password);
+ g_wpa_password = NULL;
+ g_wpa_password_len = 0;
+}
+
+esp_err_t esp_wifi_sta_wpa2_ent_set_new_password(const unsigned char *new_password, int len)
+{
+ if (len <= 0)
+ return ESP_ERR_INVALID_ARG;
+
+ if (g_wpa_new_password) {
+ os_free(g_wpa_new_password);
+ g_wpa_new_password = NULL;
+ }
+
+ g_wpa_new_password = (u8 *)os_zalloc(len);
+ if (g_wpa_new_password == NULL)
+ return ESP_ERR_NO_MEM;
+
+ os_memcpy(g_wpa_new_password, new_password, len);
+ g_wpa_password_len = len;
+
+ return ESP_OK;
+}
+
+void esp_wifi_sta_wpa2_ent_clear_new_password(void)
+{
+ if (g_wpa_new_password)
+ os_free(g_wpa_new_password);
+ g_wpa_new_password = NULL;
+ g_wpa_new_password_len = 0;
+}
+
+esp_err_t esp_wifi_sta_wpa2_ent_set_disable_time_check(bool disable)
+{
+ gl_disable_time_check = disable;
+ return ESP_OK;
+}
+
+bool wifi_sta_get_enterprise_disable_time_check(void)
+{
+ return gl_disable_time_check;
+}
+
+esp_err_t esp_wifi_sta_wpa2_ent_get_disable_time_check(bool *disable)
+{
+ *disable = wifi_sta_get_enterprise_disable_time_check();
+ return ESP_OK;
+}
+
--- /dev/null
+/*
+ * EAP common peer/server definitions
+ * Copyright (c) 2004-2012, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "wpa/includes.h"
+
+#include "wpa/common.h"
+#include "wpa2/eap_peer/eap_defs.h"
+#include "wpa2/eap_peer/eap_common.h"
+
+/**
+ * eap_hdr_len_valid - Validate EAP header length field
+ * @msg: EAP frame (starting with EAP header)
+ * @min_payload: Minimum payload length needed
+ * Returns: 1 for valid header, 0 for invalid
+ *
+ * This is a helper function that does minimal validation of EAP messages. The
+ * length field is verified to be large enough to include the header and not
+ * too large to go beyond the end of the buffer.
+ */
+int eap_hdr_len_valid(const struct wpabuf *msg, size_t min_payload)
+{
+ const struct eap_hdr *hdr;
+ size_t len;
+
+ if (msg == NULL)
+ return 0;
+
+ hdr = wpabuf_head(msg);
+
+ if (wpabuf_len(msg) < sizeof(*hdr)) {
+ wpa_printf(MSG_INFO, "EAP: Too short EAP frame");
+ return 0;
+ }
+
+ len = be_to_host16(hdr->length);
+ if (len < sizeof(*hdr) + min_payload || len > wpabuf_len(msg)) {
+ wpa_printf(MSG_INFO, "EAP: Invalid EAP length");
+ return 0;
+ }
+
+ return 1;
+}
+
+
+/**
+ * eap_hdr_validate - Validate EAP header
+ * @vendor: Expected EAP Vendor-Id (0 = IETF)
+ * @eap_type: Expected EAP type number
+ * @msg: EAP frame (starting with EAP header)
+ * @plen: Pointer to variable to contain the returned payload length
+ * Returns: Pointer to EAP payload (after type field), or %NULL on failure
+ *
+ * This is a helper function for EAP method implementations. This is usually
+ * called in the beginning of struct eap_method::process() function to verify
+ * that the received EAP request packet has a valid header. This function is
+ * able to process both legacy and expanded EAP headers and in most cases, the
+ * caller can just use the returned payload pointer (into *plen) for processing
+ * the payload regardless of whether the packet used the expanded EAP header or
+ * not.
+ */
+const u8 * eap_hdr_validate(int vendor, EapType eap_type,
+ const struct wpabuf *msg, size_t *plen)
+{
+ const struct eap_hdr *hdr;
+ const u8 *pos;
+ size_t len;
+
+ if (!eap_hdr_len_valid(msg, 1))
+ return NULL;
+
+ hdr = wpabuf_head(msg);
+ len = be_to_host16(hdr->length);
+ pos = (const u8 *) (hdr + 1);
+
+ if (*pos == EAP_TYPE_EXPANDED) {
+ int exp_vendor;
+ u32 exp_type;
+ if (len < sizeof(*hdr) + 8) {
+ wpa_printf(MSG_INFO, "EAP: Invalid expanded EAP "
+ "length");
+ return NULL;
+ }
+ pos++;
+ exp_vendor = WPA_GET_BE24(pos);
+ pos += 3;
+ exp_type = WPA_GET_BE32(pos);
+ pos += 4;
+ if (exp_vendor != vendor || exp_type != (u32) eap_type) {
+ wpa_printf(MSG_INFO, "EAP: Invalid expanded frame "
+ "type");
+ return NULL;
+ }
+
+ *plen = len - sizeof(*hdr) - 8;
+ return pos;
+ } else {
+ if (vendor != EAP_VENDOR_IETF || *pos != eap_type) {
+ wpa_printf(MSG_INFO, "EAP: Invalid frame type");
+ return NULL;
+ }
+ *plen = len - sizeof(*hdr) - 1;
+ return pos + 1;
+ }
+}
+
+
+/**
+ * eap_msg_alloc - Allocate a buffer for an EAP message
+ * @vendor: Vendor-Id (0 = IETF)
+ * @type: EAP type
+ * @payload_len: Payload length in bytes (data after Type)
+ * @code: Message Code (EAP_CODE_*)
+ * @identifier: Identifier
+ * Returns: Pointer to the allocated message buffer or %NULL on error
+ *
+ * This function can be used to allocate a buffer for an EAP message and fill
+ * in the EAP header. This function is automatically using expanded EAP header
+ * if the selected Vendor-Id is not IETF. In other words, most EAP methods do
+ * not need to separately select which header type to use when using this
+ * function to allocate the message buffers. The returned buffer has room for
+ * payload_len bytes and has the EAP header and Type field already filled in.
+ */
+struct wpabuf * eap_msg_alloc(int vendor, EapType type, size_t payload_len,
+ u8 code, u8 identifier)
+{
+ struct wpabuf *buf;
+ struct eap_hdr *hdr;
+ size_t len;
+
+ len = sizeof(struct eap_hdr) + (vendor == EAP_VENDOR_IETF ? 1 : 8) +
+ payload_len;
+ buf = wpabuf_alloc(len);
+ if (buf == NULL)
+ return NULL;
+
+ hdr = wpabuf_put(buf, sizeof(*hdr));
+ hdr->code = code;
+ hdr->identifier = identifier;
+ hdr->length = host_to_be16(len);
+
+ if (vendor == EAP_VENDOR_IETF) {
+ wpabuf_put_u8(buf, type);
+ } else {
+ wpabuf_put_u8(buf, EAP_TYPE_EXPANDED);
+ wpabuf_put_be24(buf, vendor);
+ wpabuf_put_be32(buf, type);
+ }
+
+ return buf;
+}
+
+
+/**
+ * eap_update_len - Update EAP header length
+ * @msg: EAP message from eap_msg_alloc
+ *
+ * This function updates the length field in the EAP header to match with the
+ * current length for the buffer. This allows eap_msg_alloc() to be used to
+ * allocate a larger buffer than the exact message length (e.g., if exact
+ * message length is not yet known).
+ */
+void eap_update_len(struct wpabuf *msg)
+{
+ struct eap_hdr *hdr;
+ hdr = wpabuf_mhead(msg);
+ if (wpabuf_len(msg) < sizeof(*hdr))
+ return;
+ hdr->length = host_to_be16(wpabuf_len(msg));
+}
+
+
+/**
+ * eap_get_id - Get EAP Identifier from wpabuf
+ * @msg: Buffer starting with an EAP header
+ * Returns: The Identifier field from the EAP header
+ */
+u8 eap_get_id(const struct wpabuf *msg)
+{
+ const struct eap_hdr *eap;
+
+ if (wpabuf_len(msg) < sizeof(*eap))
+ return 0;
+
+ eap = wpabuf_head(msg);
+ return eap->identifier;
+}
+
+
+/**
+ * eap_get_id - Get EAP Type from wpabuf
+ * @msg: Buffer starting with an EAP header
+ * Returns: The EAP Type after the EAP header
+ */
+EapType eap_get_type(const struct wpabuf *msg)
+{
+ if (wpabuf_len(msg) < sizeof(struct eap_hdr) + 1)
+ return EAP_TYPE_NONE;
+
+ return ((const u8 *) wpabuf_head(msg))[sizeof(struct eap_hdr)];
+}
--- /dev/null
+/*
+ * EAP peer method: EAP-MSCHAPV2 (draft-kamath-pppext-eap-mschapv2-00.txt)
+ * Copyright (c) 2004-2008, 2012, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+
+#ifdef EAP_MSCHAPv2
+
+#include "wpa/wpa.h"
+#include "wpa/includes.h"
+#include "wpa/common.h"
+#include "crypto/random.h"
+#include "crypto/ms_funcs.h"
+#include "wpa2/tls/tls.h"
+#include "wpa2/eap_peer/eap_i.h"
+#include "wpa2/eap_peer/eap_defs.h"
+#include "wpa2/eap_peer/eap_tls_common.h"
+#include "wpa2/eap_peer/eap_config.h"
+#include "wpa2/eap_peer/mschapv2.h"
+#include "wpa2/eap_peer/eap_methods.h"
+
+#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 PASSWD_CHANGE_CHAL_LEN 16
+#define MSCHAPV2_KEY_LEN 16
+
+#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
+
+struct eap_mschapv2_hdr {
+ u8 op_code;
+ u8 mschapv2_id;
+ u8 ms_length[2];
+} __packed;
+
+struct ms_response {
+ u8 peer_challenge[MSCHAPV2_CHAL_LEN];
+ u8 reserved[8];
+ u8 nt_response[MSCHAPV2_NT_RESPONSE_LEN];
+ u8 flags;
+} __packed;
+
+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];
+} __packed;
+
+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;
+
+ 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)
+{
+ 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 void *
+eap_mschapv2_init(struct eap_sm *sm)
+{
+ struct eap_mschapv2_data *data;
+ data = (struct eap_mschapv2_data *)os_zalloc(sizeof(*data));
+ if (data == NULL)
+ return NULL;
+
+ data->phase2 = sm->init_phase2;
+
+ return 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: Generate Challenge Response\n");
+
+ 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)
+ ms->mschapv2_id++;
+ WPA_PUT_BE16(ms->ms_length, ms_len);
+ wpabuf_put_u8(resp, sizeof(*r));
+
+ /* Response */
+ r = wpabuf_put(resp, sizeof(*r));
+ peer_challenge = r->peer_challenge;
+ if (data->peer_challenge) {
+ 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)
+ 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)) {
+ wpabuf_free(resp);
+ return NULL;
+ }
+ data->auth_response_valid = 1;
+ data->master_key_valid = 1;
+
+ r->flags = 0;
+
+ wpabuf_put_data(resp, identity, identity_len);
+ return resp;
+}
+
+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;
+
+ if (req_len < sizeof(*req) + 1) {
+ 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) {
+ ret->ignore = true;
+ return NULL;
+ }
+
+ if (len < challenge_len) {
+ ret->ignore = true;
+ return NULL;
+ }
+
+ if (data->passwd_change_challenge_valid)
+ challenge = data->passwd_change_challenge;
+ else
+ challenge = pos;
+ pos += challenge_len;
+ len -= challenge_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) {
+ data->prev_error = 0;
+ os_free(config->password);
+ if (config->flags & EAP_CONFIG_FLAGS_EXT_PASSWORD) {
+ } else 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;
+ }
+}
+
+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;
+
+ len = req_len - sizeof(*req);
+ pos = (const u8 *)(req + 1);
+ if (!data->auth_response_valid ||
+ mschapv2_verify_auth_response(data->auth_response, pos, len)) {
+ ret->methodState = METHOD_NONE;
+ 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--;
+ }
+
+ resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, 1,
+ EAP_CODE_RESPONSE, id);
+ if (resp == NULL) {
+ ret->ignore = true;
+ return NULL;
+ }
+
+ wpabuf_put_u8(resp, MSCHAPV2_OP_SUCCESS);
+ 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;
+ //char *msg = "";
+ int retry = 1;
+ struct eap_peer_config *config = eap_get_config(sm);
+
+ pos = txt;
+
+ if (pos && os_strncmp(pos, "E=", 2) == 0) {
+ pos += 2;
+ data->prev_error = atoi(pos);
+ pos = (char *)os_strchr(pos, ' ');
+ if (pos)
+ pos++;
+ }
+
+ if (pos && os_strncmp(pos, "R=", 2) == 0) {
+ pos += 2;
+ retry = atoi(pos);
+ pos = (char *)os_strchr(pos, ' ');
+ if (pos)
+ pos++;
+ }
+
+ if (pos && os_strncmp(pos, "C=", 2) == 0) {
+ int hex_len;
+ pos += 2;
+ hex_len = (char *)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_ERROR, "EAP-MSCHAPV2: invalid failure challenge\n");
+ } else {
+ data->passwd_change_challenge_valid = 1;
+ }
+ } else {
+ wpa_printf(MSG_ERROR, "EAP-MSCHAPV2: required challenge field "
+ "was not present in failure message\n");
+ }
+ }
+
+ if (pos && os_strncmp(pos, "V=", 2) == 0) {
+ pos += 2;
+ data->passwd_change_version = atoi(pos);
+ pos = (char *)os_strchr(pos, ' ');
+ if (pos)
+ pos++;
+ }
+
+ if (pos && os_strncmp(pos, "M=", 2) == 0) {
+ pos += 2;
+ //msg = pos;
+ }
+ #if 0
+ wpa_printf(MSG_WARNING, "EAP-MSCHAPV2: failure message: '%s' (retry %sallowed, error %d)",
+ msg, retry == 1? "" : "not ", data->prev_error);
+ #endif
+ if (data->prev_error == ERROR_PASSWD_EXPIRED &&
+ data->passwd_change_version == 3 && config) {
+ if (config->new_password == NULL) {
+ wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Password expired - "
+ "password change reqired\n");
+ //eap_sm_request_new_password(sm);
+ }
+ } else if (retry == 1 && config) {
+ if (!config->mschapv2_retry)
+ //eap_sm_request_identity(sm);
+ //eap_sm_request_password(sm);
+ config->mschapv2_retry = 1;
+ } else if (config) {
+ 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));
+
+ 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;
+ }
+
+ 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);
+ }
+
+ if (random_get_bytes(cp->peer_challenge, MSCHAPV2_CHAL_LEN))
+ goto fail;
+
+ os_memset(cp->reserved, 0, 8);
+
+ generate_nt_response(data->passwd_change_challenge, cp->peer_challenge,
+ username, username_len, new_password,
+ new_password_len, cp->nt_response);
+
+ 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;
+
+ 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;
+
+ os_memset(cp->flags, 0, 2);
+
+ return resp;
+
+fail:
+ wpabuf_free(resp);
+ return NULL;
+}
+
+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;
+
+ buf = (char *)dup_binstr(msdata, len);
+ if (buf) {
+ 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) {
+ return NULL;
+ }
+
+ 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);
+ return resp;
+}
+
+static int
+eap_mschapv2_check_config(struct eap_sm *sm)
+{
+ struct eap_peer_config *config = eap_get_config(sm);
+
+ if (config == NULL)
+ return -1;
+
+ if (config->identity == NULL ||
+ config->identity_len == 0) {
+ wpa_printf(MSG_ERROR, "EAP-MSCHAPV2: idetity not configured\n");
+ return -1;
+ }
+
+ if (config->password == NULL ||
+ config->password_len == 0) {
+ wpa_printf(MSG_ERROR, "EAP-MSCHAPV2: Password not configured\n");
+ 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;
+
+ if (sm->workaround) {
+ wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Workaround, ignore Invalid"
+ " header len=%lu ms_len=%lu\n",
+ (unsigned long)len, (unsigned long)ms_len);
+ return 0;
+ }
+ wpa_printf(MSG_ERROR, "EAP-MSCHAPV2: Invalid header len=%lu ms_len=%lu\n",
+ (unsigned long)len, (unsigned long)ms_len);
+
+ return -1;
+}
+
+static void
+eap_mschapv2_copy_challenge(struct eap_mschapv2_data *data,
+ const struct wpabuf *reqData)
+{
+ wpabuf_free(data->prev_challenge);
+ data->prev_challenge = wpabuf_dup(reqData);
+}
+
+static struct wpabuf *
+eap_mschapv2_process(struct eap_sm *sm, void *priv,
+ struct eap_method_ret *ret,
+ const struct wpabuf *reqData)
+{
+ u8 id;
+ size_t len;
+ const u8 *pos;
+ int using_prev_challenge = 0;
+ const struct eap_mschapv2_hdr *ms;
+ struct eap_mschapv2_data *data = priv;
+ struct eap_peer_config *config = eap_get_config(sm);
+
+ if (eap_mschapv2_check_config(sm)) {
+ ret->ignore = true;
+ return NULL;
+ }
+
+ if (config->mschapv2_retry && data->prev_challenge &&
+ data->prev_error == ERROR_AUTHENTICATION_FAILURE) {
+ 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\n",
+ 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_ERROR, "EAP-MSCHAPV2: Unknow op code %d -ignored\n",
+ ms->op_code);
+ return NULL;
+ }
+}
+
+static bool
+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);
+
+ /* MSK = server MS-MPPE-Recv-Key | MS-MPPE-Send-Key,
+ * 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);
+
+ *len = key_len;
+ return key;
+}
+
+int
+eap_peer_mschapv2_register(void)
+{
+ struct eap_method *eap;
+ int ret;
+
+ eap = eap_peer_method_alloc(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;
+}
+
+#endif /* EAP_MSCHAPv2 */
--- /dev/null
+/*\r
+ * EAP peer method: EAP-PEAP (draft-josefsson-pppext-eap-tls-eap-10.txt)\r
+ * Copyright (c) 2004-2008, Jouni Malinen <j@w1.fi>\r
+ *\r
+ * This software may be distributed under the terms of the BSD license.\r
+ * See README for more details.\r
+ */\r
+#ifdef EAP_PEAP\r
+\r
+#include "wpa/includes.h"\r
+\r
+#include "wpa/common.h"\r
+#include "crypto/sha1.h"\r
+#include "wpa2/tls/tls.h"\r
+#include "wpa2/eap_peer/eap_tlv_common.h"\r
+#include "wpa2/eap_peer/eap_peap_common.h"\r
+#include "wpa2/eap_peer/eap_i.h"\r
+#include "wpa2/eap_peer/eap_tls_common.h"\r
+#include "wpa2/eap_peer/eap_config.h"\r
+#include "wpa2/eap_peer/eap_methods.h"\r
+//#include "tncc.h"\r
+\r
+/* Maximum supported PEAP version\r
+ * 0 = Microsoft's PEAP version 0; draft-kamath-pppext-peapv0-00.txt\r
+ * 1 = draft-josefsson-ppext-eap-tls-eap-05.txt\r
+ * 2 = draft-josefsson-ppext-eap-tls-eap-10.txt\r
+ */\r
+#define EAP_PEAP_VERSION 1\r
+\r
+\r
+static void eap_peap_deinit(struct eap_sm *sm, void *priv);\r
+\r
+\r
+struct eap_peap_data {\r
+ struct eap_ssl_data ssl;\r
+\r
+ int peap_version, force_peap_version, force_new_label;\r
+\r
+ const struct eap_method *phase2_method;\r
+ void *phase2_priv;\r
+ int phase2_success;\r
+ int phase2_eap_success;\r
+ int phase2_eap_started;\r
+\r
+ struct eap_method_type phase2_type;\r
+ struct eap_method_type *phase2_types;\r
+ size_t num_phase2_types;\r
+\r
+ int peap_outer_success; /* 0 = PEAP terminated on Phase 2 inner\r
+ * EAP-Success\r
+ * 1 = reply with tunneled EAP-Success to inner\r
+ * EAP-Success and expect AS to send outer\r
+ * (unencrypted) EAP-Success after this\r
+ * 2 = reply with PEAP/TLS ACK to inner\r
+ * EAP-Success and expect AS to send outer\r
+ * (unencrypted) EAP-Success after this */\r
+ int resuming; /* starting a resumed session */\r
+ int reauth; /* reauthentication */\r
+ u8 *key_data;\r
+ u8 *session_id;\r
+ size_t id_len;\r
+\r
+ struct wpabuf *pending_phase2_req;\r
+ enum { NO_BINDING, OPTIONAL_BINDING, REQUIRE_BINDING } crypto_binding;\r
+ int crypto_binding_used;\r
+ u8 binding_nonce[32];\r
+ u8 ipmk[40];\r
+ u8 cmk[20];\r
+ int soh; /* Whether IF-TNCCS-SOH (Statement of Health; Microsoft NAP)\r
+ * is enabled. */\r
+};\r
+\r
+\r
+static int\r
+eap_peap_parse_phase1(struct eap_peap_data *data,\r
+ const char *phase1)\r
+{\r
+ const char *pos;\r
+\r
+ pos = os_strstr(phase1, "peapver=");\r
+ if (pos) {\r
+ data->force_peap_version = atoi(pos + 8);\r
+ data->peap_version = data->force_peap_version;\r
+ wpa_printf(MSG_DEBUG, "EAP-PEAP: Forced PEAP version %d",\r
+ data->force_peap_version);\r
+ }\r
+\r
+ if (os_strstr(phase1, "peaplabel=1")) {\r
+ data->force_new_label = 1;\r
+ wpa_printf(MSG_DEBUG, "EAP-PEAP: Force new label for key "\r
+ "derivation");\r
+ }\r
+\r
+ if (os_strstr(phase1, "peap_outer_success=0")) {\r
+ data->peap_outer_success = 0;\r
+ wpa_printf(MSG_DEBUG, "EAP-PEAP: terminate authentication on "\r
+ "tunneled EAP-Success");\r
+ } else if (os_strstr(phase1, "peap_outer_success=1")) {\r
+ data->peap_outer_success = 1;\r
+ wpa_printf(MSG_DEBUG, "EAP-PEAP: send tunneled EAP-Success "\r
+ "after receiving tunneled EAP-Success");\r
+ } else if (os_strstr(phase1, "peap_outer_success=2")) {\r
+ data->peap_outer_success = 2;\r
+ wpa_printf(MSG_DEBUG, "EAP-PEAP: send PEAP/TLS ACK after "\r
+ "receiving tunneled EAP-Success");\r
+ }\r
+\r
+ if (os_strstr(phase1, "crypto_binding=0")) {\r
+ data->crypto_binding = NO_BINDING;\r
+ wpa_printf(MSG_DEBUG, "EAP-PEAP: Do not use cryptobinding");\r
+ } else if (os_strstr(phase1, "crypto_binding=1")) {\r
+ data->crypto_binding = OPTIONAL_BINDING;\r
+ wpa_printf(MSG_DEBUG, "EAP-PEAP: Optional cryptobinding");\r
+ } else if (os_strstr(phase1, "crypto_binding=2")) {\r
+ data->crypto_binding = REQUIRE_BINDING;\r
+ wpa_printf(MSG_DEBUG, "EAP-PEAP: Require cryptobinding");\r
+ }\r
+\r
+#ifdef EAP_TNC\r
+ if (os_strstr(phase1, "tnc=soh2")) {\r
+ data->soh = 2;\r
+ wpa_printf(MSG_DEBUG, "EAP-PEAP: SoH version 2 enabled");\r
+ } else if (os_strstr(phase1, "tnc=soh1")) {\r
+ data->soh = 1;\r
+ wpa_printf(MSG_DEBUG, "EAP-PEAP: SoH version 1 enabled");\r
+ } else if (os_strstr(phase1, "tnc=soh")) {\r
+ data->soh = 2;\r
+ wpa_printf(MSG_DEBUG, "EAP-PEAP: SoH version 2 enabled");\r
+ }\r
+#endif /* EAP_TNC */\r
+\r
+ return 0;\r
+}\r
+\r
+\r
+static void *\r
+eap_peap_init(struct eap_sm *sm)\r
+{\r
+ struct eap_peap_data *data;\r
+ struct eap_peer_config *config = eap_get_config(sm);\r
+\r
+ data = (struct eap_peap_data *)os_zalloc(sizeof(*data));\r
+ if (data == NULL)\r
+ return NULL;\r
+ sm->peap_done = FALSE;\r
+ data->peap_version = EAP_PEAP_VERSION;\r
+ data->force_peap_version = -1;\r
+ data->peap_outer_success = 2;\r
+ data->crypto_binding = OPTIONAL_BINDING;\r
+\r
+ if (config && config->phase1 &&\r
+ eap_peap_parse_phase1(data, config->phase1) < 0) {\r
+ eap_peap_deinit(sm, data);\r
+ return NULL;\r
+ }\r
+\r
+ if (eap_peer_select_phase2_methods(config, "auth=",\r
+ &data->phase2_types,\r
+ &data->num_phase2_types) < 0) {\r
+ eap_peap_deinit(sm, data);\r
+ return NULL;\r
+ }\r
+\r
+ data->phase2_type.vendor = EAP_VENDOR_IETF;\r
+ data->phase2_type.method = EAP_TYPE_NONE;\r
+\r
+ if (eap_peer_tls_ssl_init(sm, &data->ssl, config, EAP_TYPE_PEAP)) {\r
+ wpa_printf(MSG_INFO, "EAP-PEAP: Failed to initialize SSL.");\r
+ eap_peap_deinit(sm, data);\r
+ return NULL;\r
+ }\r
+\r
+ return data;\r
+}\r
+\r
+\r
+static void\r
+eap_peap_deinit(struct eap_sm *sm, void *priv)\r
+{\r
+ struct eap_peap_data *data = priv;\r
+ if (data == NULL)\r
+ return;\r
+ if (data->phase2_priv && data->phase2_method)\r
+ data->phase2_method->deinit(sm, data->phase2_priv);\r
+ os_free(data->phase2_types);\r
+ eap_peer_tls_ssl_deinit(sm, &data->ssl);\r
+ os_free(data->key_data);\r
+ os_free(data->session_id);\r
+ wpabuf_free(data->pending_phase2_req);\r
+ os_free(data);\r
+}\r
+\r
+\r
+/**\r
+ * eap_tlv_build_nak - Build EAP-TLV NAK message\r
+ * @id: EAP identifier for the header\r
+ * @nak_type: TLV type (EAP_TLV_*)\r
+ * Returns: Buffer to the allocated EAP-TLV NAK message or %NULL on failure\r
+ *\r
+ * This function builds an EAP-TLV NAK message. The caller is responsible for\r
+ * freeing the returned buffer.\r
+ */\r
+static struct wpabuf *\r
+eap_tlv_build_nak(int id, u16 nak_type)\r
+{\r
+ struct wpabuf *msg;\r
+\r
+ msg = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_TLV, 10,\r
+ EAP_CODE_RESPONSE, id);\r
+ if (msg == NULL)\r
+ return NULL;\r
+\r
+ wpabuf_put_u8(msg, 0x80); /* Mandatory */\r
+ wpabuf_put_u8(msg, EAP_TLV_NAK_TLV);\r
+ wpabuf_put_be16(msg, 6); /* Length */\r
+ wpabuf_put_be32(msg, 0); /* Vendor-Id */\r
+ wpabuf_put_be16(msg, nak_type); /* NAK-Type */\r
+\r
+ return msg;\r
+}\r
+\r
+\r
+static int\r
+eap_peap_get_isk(struct eap_sm *sm, struct eap_peap_data *data,\r
+ u8 *isk, size_t isk_len)\r
+{\r
+ u8 *key;\r
+ size_t key_len;\r
+\r
+ os_memset(isk, 0, isk_len);\r
+ if (data->phase2_method == NULL || data->phase2_priv == NULL ||\r
+ data->phase2_method->isKeyAvailable == NULL ||\r
+ data->phase2_method->getKey == NULL)\r
+ return 0;\r
+\r
+ if (!data->phase2_method->isKeyAvailable(sm, data->phase2_priv) ||\r
+ (key = data->phase2_method->getKey(sm, data->phase2_priv,\r
+ &key_len)) == NULL) {\r
+ wpa_printf(MSG_DEBUG, "EAP-PEAP: Could not get key material "\r
+ "from Phase 2");\r
+ return -1;\r
+ }\r
+\r
+ if (key_len > isk_len)\r
+ key_len = isk_len;\r
+ os_memcpy(isk, key, key_len);\r
+ os_free(key);\r
+\r
+ return 0;\r
+}\r
+\r
+\r
+static int\r
+eap_peap_derive_cmk(struct eap_sm *sm, struct eap_peap_data *data)\r
+{\r
+ u8 *tk;\r
+ u8 isk[32], imck[60];\r
+\r
+ /*\r
+ * Tunnel key (TK) is the first 60 octets of the key generated by\r
+ * phase 1 of PEAP (based on TLS).\r
+ */\r
+ tk = data->key_data;\r
+ if (tk == NULL)\r
+ return -1;\r
+ wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: TK", tk, 60);\r
+\r
+ if (data->reauth &&\r
+ tls_connection_resumed(sm->ssl_ctx, data->ssl.conn)) {\r
+ /* Fast-connect: IPMK|CMK = TK */\r
+ os_memcpy(data->ipmk, tk, 40);\r
+ wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: IPMK from TK",\r
+ data->ipmk, 40);\r
+ os_memcpy(data->cmk, tk + 40, 20);\r
+ wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: CMK from TK",\r
+ data->cmk, 20);\r
+ return 0;\r
+ }\r
+\r
+ if (eap_peap_get_isk(sm, data, isk, sizeof(isk)) < 0)\r
+ return -1;\r
+ wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: ISK", isk, sizeof(isk));\r
+\r
+ /*\r
+ * IPMK Seed = "Inner Methods Compound Keys" | ISK\r
+ * TempKey = First 40 octets of TK\r
+ * IPMK|CMK = PRF+(TempKey, IPMK Seed, 60)\r
+ * (note: draft-josefsson-pppext-eap-tls-eap-10.txt includes a space\r
+ * in the end of the label just before ISK; is that just a typo?)\r
+ */\r
+ wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: TempKey", tk, 40);\r
+ if (peap_prfplus(data->peap_version, tk, 40,\r
+ "Inner Methods Compound Keys",\r
+ isk, sizeof(isk), imck, sizeof(imck)) < 0)\r
+ return -1;\r
+ wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: IMCK (IPMKj)",\r
+ imck, sizeof(imck));\r
+\r
+ os_memcpy(data->ipmk, imck, 40);\r
+ wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: IPMK (S-IPMKj)", data->ipmk, 40);\r
+ os_memcpy(data->cmk, imck + 40, 20);\r
+ wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: CMK (CMKj)", data->cmk, 20);\r
+\r
+ return 0;\r
+}\r
+\r
+\r
+static int\r
+eap_tlv_add_cryptobinding(struct eap_sm *sm,\r
+ struct eap_peap_data *data,\r
+ struct wpabuf *buf)\r
+{\r
+ u8 *mac;\r
+ u8 eap_type = EAP_TYPE_PEAP;\r
+ const u8 *addr[2];\r
+ size_t len[2];\r
+ u16 tlv_type;\r
+\r
+ /* Compound_MAC: HMAC-SHA1-160(cryptobinding TLV | EAP type) */\r
+ addr[0] = wpabuf_put(buf, 0);\r
+ len[0] = 60;\r
+ addr[1] = &eap_type;\r
+ len[1] = 1;\r
+\r
+ tlv_type = EAP_TLV_CRYPTO_BINDING_TLV;\r
+ if (data->peap_version >= 2)\r
+ tlv_type |= EAP_TLV_TYPE_MANDATORY;\r
+ wpabuf_put_be16(buf, tlv_type);\r
+ wpabuf_put_be16(buf, 56);\r
+\r
+ wpabuf_put_u8(buf, 0); /* Reserved */\r
+ wpabuf_put_u8(buf, data->peap_version); /* Version */\r
+ wpabuf_put_u8(buf, data->peap_version); /* RecvVersion */\r
+ wpabuf_put_u8(buf, 1); /* SubType: 0 = Request, 1 = Response */\r
+ wpabuf_put_data(buf, data->binding_nonce, 32); /* Nonce */\r
+ mac = wpabuf_put(buf, 20); /* Compound_MAC */\r
+ wpa_hexdump(MSG_MSGDUMP, "EAP-PEAP: Compound_MAC CMK", data->cmk, 20);\r
+ wpa_hexdump(MSG_MSGDUMP, "EAP-PEAP: Compound_MAC data 1",\r
+ addr[0], len[0]);\r
+ wpa_hexdump(MSG_MSGDUMP, "EAP-PEAP: Compound_MAC data 2",\r
+ addr[1], len[1]);\r
+ hmac_sha1_vector(data->cmk, 20, 2, addr, len, mac);\r
+ wpa_hexdump(MSG_MSGDUMP, "EAP-PEAP: Compound_MAC", mac, SHA1_MAC_LEN);\r
+ data->crypto_binding_used = 1;\r
+\r
+ return 0;\r
+}\r
+\r
+\r
+/**\r
+ * eap_tlv_build_result - Build EAP-TLV Result message\r
+ * @id: EAP identifier for the header\r
+ * @status: Status (EAP_TLV_RESULT_SUCCESS or EAP_TLV_RESULT_FAILURE)\r
+ * Returns: Buffer to the allocated EAP-TLV Result message or %NULL on failure\r
+ *\r
+ * This function builds an EAP-TLV Result message. The caller is responsible\r
+ * for freeing the returned buffer.\r
+ */\r
+static struct wpabuf *\r
+eap_tlv_build_result(struct eap_sm *sm,\r
+ struct eap_peap_data *data,\r
+ int crypto_tlv_used,\r
+ int id, u16 status)\r
+{\r
+ struct wpabuf *msg;\r
+ size_t len;\r
+\r
+ if (data->crypto_binding == NO_BINDING)\r
+ crypto_tlv_used = 0;\r
+\r
+ len = 6;\r
+ if (crypto_tlv_used)\r
+ len += 60; /* Cryptobinding TLV */\r
+ msg = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_TLV, len,\r
+ EAP_CODE_RESPONSE, id);\r
+ if (msg == NULL)\r
+ return NULL;\r
+\r
+ wpabuf_put_u8(msg, 0x80); /* Mandatory */\r
+ wpabuf_put_u8(msg, EAP_TLV_RESULT_TLV);\r
+ wpabuf_put_be16(msg, 2); /* Length */\r
+ wpabuf_put_be16(msg, status); /* Status */\r
+\r
+ if (crypto_tlv_used && eap_tlv_add_cryptobinding(sm, data, msg)) {\r
+ wpabuf_free(msg);\r
+ return NULL;\r
+ }\r
+\r
+ return msg;\r
+}\r
+\r
+\r
+static int\r
+eap_tlv_validate_cryptobinding(struct eap_sm *sm,\r
+ struct eap_peap_data *data,\r
+ const u8 *crypto_tlv,\r
+ size_t crypto_tlv_len)\r
+{\r
+ u8 buf[61], mac[SHA1_MAC_LEN];\r
+ const u8 *pos;\r
+\r
+ if (eap_peap_derive_cmk(sm, data) < 0) {\r
+ wpa_printf(MSG_DEBUG, "EAP-PEAP: Could not derive CMK");\r
+ return -1;\r
+ }\r
+\r
+ if (crypto_tlv_len != 4 + 56) {\r
+ wpa_printf(MSG_DEBUG, "EAP-PEAP: Invalid cryptobinding TLV "\r
+ "length %d", (int) crypto_tlv_len);\r
+ return -1;\r
+ }\r
+\r
+ pos = crypto_tlv;\r
+ pos += 4; /* TLV header */\r
+ if (pos[1] != data->peap_version) {\r
+ wpa_printf(MSG_DEBUG, "EAP-PEAP: Cryptobinding TLV Version "\r
+ "mismatch (was %d; expected %d)",\r
+ pos[1], data->peap_version);\r
+ return -1;\r
+ }\r
+\r
+ if (pos[3] != 0) {\r
+ wpa_printf(MSG_DEBUG, "EAP-PEAP: Unexpected Cryptobinding TLV "\r
+ "SubType %d", pos[3]);\r
+ return -1;\r
+ }\r
+ pos += 4;\r
+ os_memcpy(data->binding_nonce, pos, 32);\r
+ pos += 32; /* Nonce */\r
+\r
+ /* Compound_MAC: HMAC-SHA1-160(cryptobinding TLV | EAP type) */\r
+ os_memcpy(buf, crypto_tlv, 60);\r
+ os_memset(buf + 4 + 4 + 32, 0, 20); /* Compound_MAC */\r
+ buf[60] = EAP_TYPE_PEAP;\r
+ wpa_hexdump(MSG_DEBUG, "EAP-PEAP: Compound_MAC data",\r
+ buf, sizeof(buf));\r
+ hmac_sha1(data->cmk, 20, buf, sizeof(buf), mac);\r
+\r
+ if (os_memcmp(mac, pos, SHA1_MAC_LEN) != 0) {\r
+ wpa_printf(MSG_DEBUG, "EAP-PEAP: Invalid Compound_MAC in "\r
+ "cryptobinding TLV");\r
+ wpa_hexdump(MSG_DEBUG, "EAP-PEAP: Received MAC",\r
+ pos, SHA1_MAC_LEN);\r
+ wpa_hexdump(MSG_DEBUG, "EAP-PEAP: Expected MAC",\r
+ mac, SHA1_MAC_LEN);\r
+ return -1;\r
+ }\r
+\r
+ wpa_printf(MSG_DEBUG, "EAP-PEAP: Valid cryptobinding TLV received");\r
+\r
+ return 0;\r
+}\r
+\r
+\r
+/**\r
+ * eap_tlv_process - Process a received EAP-TLV message and generate a response\r
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()\r
+ * @ret: Return values from EAP request validation and processing\r
+ * @req: EAP-TLV request to be processed. The caller must have validated that\r
+ * the buffer is large enough to contain full request (hdr->length bytes) and\r
+ * that the EAP type is EAP_TYPE_TLV.\r
+ * @resp: Buffer to return a pointer to the allocated response message. This\r
+ * field should be initialized to %NULL before the call. The value will be\r
+ * updated if a response message is generated. The caller is responsible for\r
+ * freeing the allocated message.\r
+ * @force_failure: Force negotiation to fail\r
+ * Returns: 0 on success, -1 on failure\r
+ */\r
+static int\r
+eap_tlv_process(struct eap_sm *sm, struct eap_peap_data *data,\r
+ struct eap_method_ret *ret,\r
+ const struct wpabuf *req, struct wpabuf **resp,\r
+ int force_failure)\r
+{\r
+ size_t left, tlv_len;\r
+ const u8 *pos;\r
+ const u8 *result_tlv = NULL, *crypto_tlv = NULL;\r
+ size_t result_tlv_len = 0, crypto_tlv_len = 0;\r
+ int tlv_type, mandatory;\r
+\r
+ /* Parse TLVs */\r
+ pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_TLV, req, &left);\r
+ if (pos == NULL)\r
+ return -1;\r
+ wpa_hexdump(MSG_DEBUG, "EAP-TLV: Received TLVs", pos, left);\r
+ while (left >= 4) {\r
+ mandatory = !!(pos[0] & 0x80);\r
+ tlv_type = WPA_GET_BE16(pos) & 0x3fff;\r
+ pos += 2;\r
+ tlv_len = WPA_GET_BE16(pos);\r
+ pos += 2;\r
+ left -= 4;\r
+ if (tlv_len > left) {\r
+ wpa_printf(MSG_DEBUG, "EAP-TLV: TLV underrun "\r
+ "(tlv_len=%lu left=%lu)",\r
+ (unsigned long) tlv_len,\r
+ (unsigned long) left);\r
+ return -1;\r
+ }\r
+ switch (tlv_type) {\r
+ case EAP_TLV_RESULT_TLV:\r
+ result_tlv = pos;\r
+ result_tlv_len = tlv_len;\r
+ break;\r
+ case EAP_TLV_CRYPTO_BINDING_TLV:\r
+ crypto_tlv = pos;\r
+ crypto_tlv_len = tlv_len;\r
+ break;\r
+ default:\r
+ wpa_printf(MSG_DEBUG, "EAP-TLV: Unsupported TLV Type "\r
+ "%d%s", tlv_type,\r
+ mandatory ? " (mandatory)" : "");\r
+ if (mandatory) {\r
+ /* NAK TLV and ignore all TLVs in this packet.\r
+ */\r
+ *resp = eap_tlv_build_nak(eap_get_id(req),\r
+ tlv_type);\r
+ return *resp == NULL ? -1 : 0;\r
+ }\r
+ /* Ignore this TLV, but process other TLVs */\r
+ break;\r
+ }\r
+\r
+ pos += tlv_len;\r
+ left -= tlv_len;\r
+ }\r
+ if (left) {\r
+ wpa_printf(MSG_DEBUG, "EAP-TLV: Last TLV too short in "\r
+ "Request (left=%lu)", (unsigned long) left);\r
+ return -1;\r
+ }\r
+\r
+ /* Process supported TLVs */\r
+ if (crypto_tlv && data->crypto_binding != NO_BINDING) {\r
+ wpa_hexdump(MSG_DEBUG, "EAP-PEAP: Cryptobinding TLV",\r
+ crypto_tlv, crypto_tlv_len);\r
+ if (eap_tlv_validate_cryptobinding(sm, data, crypto_tlv - 4,\r
+ crypto_tlv_len + 4) < 0) {\r
+ if (result_tlv == NULL)\r
+ return -1;\r
+ force_failure = 1;\r
+ crypto_tlv = NULL; /* do not include Cryptobinding TLV\r
+ * in response, if the received\r
+ * cryptobinding was invalid. */\r
+ }\r
+ } else if (!crypto_tlv && data->crypto_binding == REQUIRE_BINDING) {\r
+ wpa_printf(MSG_DEBUG, "EAP-PEAP: No cryptobinding TLV");\r
+ return -1;\r
+ }\r
+\r
+ if (result_tlv) {\r
+ int status, resp_status;\r
+ wpa_hexdump(MSG_DEBUG, "EAP-TLV: Result TLV",\r
+ result_tlv, result_tlv_len);\r
+ if (result_tlv_len < 2) {\r
+ wpa_printf(MSG_INFO, "EAP-TLV: Too short Result TLV "\r
+ "(len=%lu)",\r
+ (unsigned long) result_tlv_len);\r
+ return -1;\r
+ }\r
+ status = WPA_GET_BE16(result_tlv);\r
+ if (status == EAP_TLV_RESULT_SUCCESS) {\r
+ wpa_printf(MSG_INFO, "EAP-TLV: TLV Result - Success "\r
+ "- EAP-TLV/Phase2 Completed");\r
+ if (force_failure) {\r
+ wpa_printf(MSG_INFO, "EAP-TLV: Earlier failure"\r
+ " - force failed Phase 2");\r
+ resp_status = EAP_TLV_RESULT_FAILURE;\r
+ ret->decision = DECISION_FAIL;\r
+ } else {\r
+ resp_status = EAP_TLV_RESULT_SUCCESS;\r
+ ret->decision = DECISION_UNCOND_SUCC;\r
+ }\r
+ } else if (status == EAP_TLV_RESULT_FAILURE) {\r
+ wpa_printf(MSG_INFO, "EAP-TLV: TLV Result - Failure");\r
+ resp_status = EAP_TLV_RESULT_FAILURE;\r
+ ret->decision = DECISION_FAIL;\r
+ } else {\r
+ wpa_printf(MSG_INFO, "EAP-TLV: Unknown TLV Result "\r
+ "Status %d", status);\r
+ resp_status = EAP_TLV_RESULT_FAILURE;\r
+ ret->decision = DECISION_FAIL;\r
+ }\r
+ ret->methodState = METHOD_DONE;\r
+\r
+ *resp = eap_tlv_build_result(sm, data, crypto_tlv != NULL,\r
+ eap_get_id(req), resp_status);\r
+ }\r
+\r
+ return 0;\r
+}\r
+\r
+\r
+static struct wpabuf *\r
+eap_peapv2_tlv_eap_payload(struct wpabuf *buf)\r
+{\r
+ struct wpabuf *e;\r
+ struct eap_tlv_hdr *tlv;\r
+\r
+ if (buf == NULL)\r
+ return NULL;\r
+\r
+ /* Encapsulate EAP packet in EAP-Payload TLV */\r
+ wpa_printf(MSG_DEBUG, "EAP-PEAPv2: Add EAP-Payload TLV");\r
+ e = wpabuf_alloc(sizeof(*tlv) + wpabuf_len(buf));\r
+ if (e == NULL) {\r
+ wpa_printf(MSG_DEBUG, "EAP-PEAPv2: Failed to allocate memory "\r
+ "for TLV encapsulation");\r
+ wpabuf_free(buf);\r
+ return NULL;\r
+ }\r
+ tlv = wpabuf_put(e, sizeof(*tlv));\r
+ tlv->tlv_type = host_to_be16(EAP_TLV_TYPE_MANDATORY |\r
+ EAP_TLV_EAP_PAYLOAD_TLV);\r
+ tlv->length = host_to_be16(wpabuf_len(buf));\r
+ wpabuf_put_buf(e, buf);\r
+ wpabuf_free(buf);\r
+ return e;\r
+}\r
+\r
+\r
+static int eap_peap_phase2_request(struct eap_sm *sm,\r
+ struct eap_peap_data *data,\r
+ struct eap_method_ret *ret,\r
+ struct wpabuf *req,\r
+ struct wpabuf **resp)\r
+{\r
+ struct eap_hdr *hdr = wpabuf_mhead(req);\r
+ size_t len = be_to_host16(hdr->length);\r
+ u8 *pos;\r
+ struct eap_method_ret iret;\r
+ //struct eap_peer_config *config = eap_get_config(sm);\r
+\r
+ if (len <= sizeof(struct eap_hdr)) {\r
+ wpa_printf(MSG_INFO, "EAP-PEAP: too short "\r
+ "Phase 2 request (len=%lu)", (unsigned long) len);\r
+ return -1;\r
+ }\r
+ pos = (u8 *) (hdr + 1);\r
+ wpa_printf(MSG_DEBUG, "EAP-PEAP: Phase 2 Request: type=%d\n", *pos);\r
+ switch (*pos) {\r
+ case EAP_TYPE_IDENTITY:\r
+ *resp = eap_sm_build_identity_resp(sm, hdr->identifier, 1);\r
+ break;\r
+ case EAP_TYPE_TLV:\r
+ os_memset(&iret, 0, sizeof(iret));\r
+ if (eap_tlv_process(sm, data, &iret, req, resp,\r
+ data->phase2_eap_started &&\r
+ !data->phase2_eap_success)) {\r
+ ret->methodState = METHOD_DONE;\r
+ ret->decision = DECISION_FAIL;\r
+ return -1;\r
+ }\r
+ if (iret.methodState == METHOD_DONE ||\r
+ iret.methodState == METHOD_MAY_CONT) {\r
+ ret->methodState = iret.methodState;\r
+ ret->decision = iret.decision;\r
+ data->phase2_success = 1;\r
+ }\r
+ break;\r
+ case EAP_TYPE_EXPANDED:\r
+#ifdef EAP_TNC\r
+ if (data->soh) {\r
+ const u8 *epos;\r
+ size_t eleft;\r
+\r
+ epos = eap_hdr_validate(EAP_VENDOR_MICROSOFT, 0x21,\r
+ req, &eleft);\r
+ if (epos) {\r
+ struct wpabuf *buf;\r
+ wpa_printf(MSG_DEBUG, "EAP-PEAP: SoH EAP Extensions");\r
+ buf = tncc_process_soh_request(data->soh,\r
+ epos, eleft);\r
+ if (buf) {\r
+ *resp = eap_msg_alloc(\r
+ EAP_VENDOR_MICROSOFT, 0x21,\r
+ wpabuf_len(buf),\r
+ EAP_CODE_RESPONSE,\r
+ hdr->identifier);\r
+ if (*resp == NULL) {\r
+ ret->methodState = METHOD_DONE;\r
+ ret->decision = DECISION_FAIL;\r
+ return -1;\r
+ }\r
+ wpabuf_put_buf(*resp, buf);\r
+ wpabuf_free(buf);\r
+ break;\r
+ }\r
+ }\r
+ }\r
+#endif /* EAP_TNC */\r
+ /* fall through */\r
+ default:\r
+ if (data->phase2_type.vendor == EAP_VENDOR_IETF &&\r
+ data->phase2_type.method == EAP_TYPE_NONE) {\r
+ size_t i;\r
+ for (i = 0; i < data->num_phase2_types; i++) {\r
+ if (data->phase2_types[i].vendor !=\r
+ EAP_VENDOR_IETF ||\r
+ data->phase2_types[i].method != *pos)\r
+ continue;\r
+\r
+ data->phase2_type.vendor =\r
+ data->phase2_types[i].vendor;\r
+ data->phase2_type.method =\r
+ data->phase2_types[i].method;\r
+ wpa_printf(MSG_DEBUG, "EAP-PEAP: Selected "\r
+ "Phase 2 EAP vendor %d method %d\n",\r
+ data->phase2_type.vendor,\r
+ data->phase2_type.method);\r
+ break;\r
+ }\r
+ }\r
+ if (*pos != data->phase2_type.method ||\r
+ *pos == EAP_TYPE_NONE) {\r
+ if (eap_peer_tls_phase2_nak(data->phase2_types,\r
+ data->num_phase2_types,\r
+ hdr, resp))\r
+ return -1;\r
+ return 0;\r
+ }\r
+\r
+ if (data->phase2_priv == NULL) {\r
+ data->phase2_method = eap_peer_get_eap_method(\r
+ data->phase2_type.vendor,\r
+ data->phase2_type.method);\r
+ if (data->phase2_method) {\r
+ sm->init_phase2 = 1;\r
+ data->phase2_priv =\r
+ data->phase2_method->init(sm);\r
+ sm->init_phase2 = 0;\r
+ }\r
+ }\r
+ if (data->phase2_priv == NULL || data->phase2_method == NULL) {\r
+ wpa_printf(MSG_ERROR, "EAP-PEAP: failed to initialize "\r
+ "Phase 2 EAP method %d\n", *pos);\r
+ ret->methodState = METHOD_DONE;\r
+ ret->decision = DECISION_FAIL;\r
+ return -1;\r
+ }\r
+ data->phase2_eap_started = 1;\r
+ os_memset(&iret, 0, sizeof(iret));\r
+ *resp = data->phase2_method->process(sm, data->phase2_priv,\r
+ &iret, req);\r
+ if ((iret.methodState == METHOD_DONE ||\r
+ iret.methodState == METHOD_MAY_CONT) &&\r
+ (iret.decision == DECISION_UNCOND_SUCC ||\r
+ iret.decision == DECISION_COND_SUCC)) {\r
+ data->phase2_eap_success = 1;\r
+ data->phase2_success = 1;\r
+ }\r
+ break;\r
+ }\r
+\r
+ if (*resp == NULL) {\r
+ wpa_printf(MSG_ERROR, "phase 2 response failure\n");\r
+ wpabuf_free(data->pending_phase2_req);\r
+ data->pending_phase2_req = wpabuf_alloc_copy(hdr, len);\r
+ }\r
+/* \r
+ if (*resp == NULL &&\r
+ (config->pending_req_identity || config->pending_req_password ||\r
+ config->pending_req_otp || config->pending_req_new_password)) {\r
+ wpabuf_free(data->pending_phase2_req);\r
+ data->pending_phase2_req = wpabuf_alloc_copy(hdr, len);\r
+ }\r
+*/\r
+\r
+ return 0;\r
+}\r
+\r
+\r
+static int\r
+eap_peap_decrypt(struct eap_sm *sm, struct eap_peap_data *data,\r
+ struct eap_method_ret *ret,\r
+ const struct eap_hdr *req,\r
+ const struct wpabuf *in_data,\r
+ struct wpabuf **out_data)\r
+{\r
+ struct wpabuf *in_decrypted = NULL;\r
+ int res, skip_change = 0;\r
+ struct eap_hdr *hdr, *rhdr;\r
+ struct wpabuf *resp = NULL;\r
+ size_t len;\r
+\r
+ wpa_printf(MSG_DEBUG, "EAP-PEAP: received %lu bytes encrypted data for"\r
+ " Phase 2\n", (unsigned long) wpabuf_len(in_data));\r
+\r
+ if (data->pending_phase2_req) {\r
+ wpa_printf(MSG_DEBUG, "EAP-PEAP: Pending Phase 2 request - "\r
+ "skip decryption and use old data");\r
+ /* Clear TLS reassembly state. */\r
+ eap_peer_tls_reset_input(&data->ssl);\r
+ in_decrypted = data->pending_phase2_req;\r
+ data->pending_phase2_req = NULL;\r
+ skip_change = 1;\r
+ goto continue_req;\r
+ }\r
+\r
+ if (wpabuf_len(in_data) == 0 && sm->workaround &&\r
+ data->phase2_success) {\r
+ /*\r
+ * Cisco ACS seems to be using TLS ACK to terminate\r
+ * EAP-PEAPv0/GTC. Try to reply with TLS ACK.\r
+ */\r
+ wpa_printf(MSG_DEBUG, "EAP-PEAP: Received TLS ACK, but "\r
+ "expected data - acknowledge with TLS ACK since "\r
+ "Phase 2 has been completed");\r
+ ret->decision = DECISION_COND_SUCC;\r
+ ret->methodState = METHOD_DONE;\r
+ return 1;\r
+ } else if (wpabuf_len(in_data) == 0) {\r
+ /* Received TLS ACK - requesting more fragments */\r
+ return eap_peer_tls_encrypt(sm, &data->ssl, EAP_TYPE_PEAP,\r
+ data->peap_version,\r
+ req->identifier, NULL, out_data);\r
+ }\r
+\r
+ res = eap_peer_tls_decrypt(sm, &data->ssl, in_data, &in_decrypted);\r
+ if (res)\r
+ return res;\r
+\r
+continue_req:\r
+ wpa_hexdump_buf(MSG_DEBUG, "EAP-PEAP: Decrypted Phase 2 EAP",\r
+ in_decrypted);\r
+\r
+ hdr = wpabuf_mhead(in_decrypted);\r
+ if (wpabuf_len(in_decrypted) == 5 && hdr->code == EAP_CODE_REQUEST &&\r
+ be_to_host16(hdr->length) == 5 &&\r
+ eap_get_type(in_decrypted) == EAP_TYPE_IDENTITY) {\r
+ /* At least FreeRADIUS seems to send full EAP header with\r
+ * EAP Request Identity */\r
+ skip_change = 1;\r
+ }\r
+ if (wpabuf_len(in_decrypted) >= 5 && hdr->code == EAP_CODE_REQUEST &&\r
+ eap_get_type(in_decrypted) == EAP_TYPE_TLV) {\r
+ skip_change = 1;\r
+ }\r
+\r
+ if (data->peap_version == 0 && !skip_change) {\r
+ struct eap_hdr *nhdr;\r
+ struct wpabuf *nmsg = wpabuf_alloc(sizeof(struct eap_hdr) +\r
+ wpabuf_len(in_decrypted));\r
+ if (nmsg == NULL) {\r
+ wpabuf_free(in_decrypted);\r
+ return 0;\r
+ }\r
+ nhdr = wpabuf_put(nmsg, sizeof(*nhdr));\r
+ wpabuf_put_buf(nmsg, in_decrypted);\r
+ nhdr->code = req->code;\r
+ nhdr->identifier = req->identifier;\r
+ nhdr->length = host_to_be16(sizeof(struct eap_hdr) +\r
+ wpabuf_len(in_decrypted));\r
+\r
+ wpabuf_free(in_decrypted);\r
+ in_decrypted = nmsg;\r
+ }\r
+\r
+ if (data->peap_version >= 2) {\r
+ struct eap_tlv_hdr *tlv;\r
+ struct wpabuf *nmsg;\r
+\r
+ if (wpabuf_len(in_decrypted) < sizeof(*tlv) + sizeof(*hdr)) {\r
+ wpa_printf(MSG_INFO, "EAP-PEAPv2: Too short Phase 2 "\r
+ "EAP TLV");\r
+ wpabuf_free(in_decrypted);\r
+ return 0;\r
+ }\r
+ tlv = wpabuf_mhead(in_decrypted);\r
+ if ((be_to_host16(tlv->tlv_type) & 0x3fff) !=\r
+ EAP_TLV_EAP_PAYLOAD_TLV) {\r
+ wpa_printf(MSG_INFO, "EAP-PEAPv2: Not an EAP TLV");\r
+ wpabuf_free(in_decrypted);\r
+ return 0;\r
+ }\r
+ if (sizeof(*tlv) + be_to_host16(tlv->length) >\r
+ wpabuf_len(in_decrypted)) {\r
+ wpa_printf(MSG_INFO, "EAP-PEAPv2: Invalid EAP TLV "\r
+ "length");\r
+ wpabuf_free(in_decrypted);\r
+ return 0;\r
+ }\r
+ hdr = (struct eap_hdr *) (tlv + 1);\r
+ if (be_to_host16(hdr->length) > be_to_host16(tlv->length)) {\r
+ wpa_printf(MSG_INFO, "EAP-PEAPv2: No room for full "\r
+ "EAP packet in EAP TLV");\r
+ wpabuf_free(in_decrypted);\r
+ return 0;\r
+ }\r
+\r
+ nmsg = wpabuf_alloc(be_to_host16(hdr->length));\r
+ if (nmsg == NULL) {\r
+ wpabuf_free(in_decrypted);\r
+ return 0;\r
+ }\r
+\r
+ wpabuf_put_data(nmsg, hdr, be_to_host16(hdr->length));\r
+ wpabuf_free(in_decrypted);\r
+ in_decrypted = nmsg;\r
+ }\r
+\r
+ hdr = wpabuf_mhead(in_decrypted);\r
+ if (wpabuf_len(in_decrypted) < sizeof(*hdr)) {\r
+ wpa_printf(MSG_INFO, "EAP-PEAP: Too short Phase 2 "\r
+ "EAP frame (len=%lu)",\r
+ (unsigned long) wpabuf_len(in_decrypted));\r
+ wpabuf_free(in_decrypted);\r
+ return 0;\r
+ }\r
+ len = be_to_host16(hdr->length);\r
+ if (len > wpabuf_len(in_decrypted)) {\r
+ wpa_printf(MSG_INFO, "EAP-PEAP: Length mismatch in "\r
+ "Phase 2 EAP frame (len=%lu hdr->length=%lu)",\r
+ (unsigned long) wpabuf_len(in_decrypted),\r
+ (unsigned long) len);\r
+ wpabuf_free(in_decrypted);\r
+ return 0;\r
+ }\r
+ if (len < wpabuf_len(in_decrypted)) {\r
+ wpa_printf(MSG_INFO, "EAP-PEAP: Odd.. Phase 2 EAP header has "\r
+ "shorter length than full decrypted data "\r
+ "(%lu < %lu)",\r
+ (unsigned long) len,\r
+ (unsigned long) wpabuf_len(in_decrypted));\r
+ }\r
+ wpa_printf(MSG_DEBUG, "EAP-PEAP: received Phase 2: code=%d "\r
+ "identifier=%d length=%lu\n", hdr->code, hdr->identifier,\r
+ (unsigned long) len);\r
+ switch (hdr->code) {\r
+ case EAP_CODE_REQUEST:\r
+ if (eap_peap_phase2_request(sm, data, ret, in_decrypted,\r
+ &resp)) {\r
+ wpabuf_free(in_decrypted);\r
+ wpa_printf(MSG_ERROR, "EAP-PEAP: Phase2 Request "\r
+ "processing failed\n");\r
+ return 0;\r
+ }\r
+ break;\r
+ case EAP_CODE_SUCCESS:\r
+ wpa_printf(MSG_DEBUG, "EAP-PEAP: Phase 2 Success\n");\r
+ if (data->peap_version == 1) {\r
+ /* EAP-Success within TLS tunnel is used to indicate\r
+ * shutdown of the TLS channel. The authentication has\r
+ * been completed. */\r
+ if (data->phase2_eap_started &&\r
+ !data->phase2_eap_success) {\r
+ wpa_printf(MSG_DEBUG, "EAP-PEAP: Phase 2 "\r
+ "Success used to indicate success, "\r
+ "but Phase 2 EAP was not yet "\r
+ "completed successfully");\r
+ ret->methodState = METHOD_DONE;\r
+ ret->decision = DECISION_FAIL;\r
+ wpabuf_free(in_decrypted);\r
+ return 0;\r
+ }\r
+ wpa_printf(MSG_DEBUG, "EAP-PEAP: Version 1 - "\r
+ "EAP-Success within TLS tunnel - "\r
+ "authentication completed");\r
+ ret->decision = DECISION_UNCOND_SUCC;\r
+ ret->methodState = METHOD_DONE;\r
+ data->phase2_success = 1;\r
+ if (data->peap_outer_success == 2) {\r
+ wpabuf_free(in_decrypted);\r
+ wpa_printf(MSG_DEBUG, "EAP-PEAP: Use TLS ACK "\r
+ "to finish authentication");\r
+ return 1;\r
+ } else if (data->peap_outer_success == 1) {\r
+ /* Reply with EAP-Success within the TLS\r
+ * channel to complete the authentication. */\r
+ resp = wpabuf_alloc(sizeof(struct eap_hdr));\r
+ if (resp) {\r
+ rhdr = wpabuf_put(resp, sizeof(*rhdr));\r
+ rhdr->code = EAP_CODE_SUCCESS;\r
+ rhdr->identifier = hdr->identifier;\r
+ rhdr->length =\r
+ host_to_be16(sizeof(*rhdr));\r
+ }\r
+ } else {\r
+ /* No EAP-Success expected for Phase 1 (outer,\r
+ * unencrypted auth), so force EAP state\r
+ * machine to SUCCESS state. */\r
+ sm->peap_done = TRUE;\r
+ }\r
+ } else {\r
+ /* FIX: ? */\r
+ }\r
+ break;\r
+ case EAP_CODE_FAILURE:\r
+ wpa_printf(MSG_DEBUG, "EAP-PEAP: Phase 2 Failure\n");\r
+ ret->decision = DECISION_FAIL;\r
+ ret->methodState = METHOD_MAY_CONT;\r
+ ret->allowNotifications = FALSE;\r
+ /* Reply with EAP-Failure within the TLS channel to complete\r
+ * failure reporting. */\r
+ resp = wpabuf_alloc(sizeof(struct eap_hdr));\r
+ if (resp) {\r
+ rhdr = wpabuf_put(resp, sizeof(*rhdr));\r
+ rhdr->code = EAP_CODE_FAILURE;\r
+ rhdr->identifier = hdr->identifier;\r
+ rhdr->length = host_to_be16(sizeof(*rhdr));\r
+ }\r
+ break;\r
+ default:\r
+ wpa_printf(MSG_INFO, "EAP-PEAP: Unexpected code=%d in "\r
+ "Phase 2 EAP header", hdr->code);\r
+ break;\r
+ }\r
+\r
+ wpabuf_free(in_decrypted);\r
+\r
+ if (resp) {\r
+ int skip_change2 = 0;\r
+ struct wpabuf *rmsg, buf;\r
+\r
+ wpa_hexdump_buf_key(MSG_DEBUG,\r
+ "EAP-PEAP: Encrypting Phase 2 data", resp);\r
+ /* PEAP version changes */\r
+ if (data->peap_version >= 2) {\r
+ resp = eap_peapv2_tlv_eap_payload(resp);\r
+ if (resp == NULL)\r
+ return -1;\r
+ }\r
+ if (wpabuf_len(resp) >= 5 &&\r
+ wpabuf_head_u8(resp)[0] == EAP_CODE_RESPONSE &&\r
+ eap_get_type(resp) == EAP_TYPE_TLV)\r
+ skip_change2 = 1;\r
+ rmsg = resp;\r
+ if (data->peap_version == 0 && !skip_change2) {\r
+ wpabuf_set(&buf, wpabuf_head_u8(resp) +\r
+ sizeof(struct eap_hdr),\r
+ wpabuf_len(resp) - sizeof(struct eap_hdr));\r
+ rmsg = &buf;\r
+ }\r
+\r
+ if (eap_peer_tls_encrypt(sm, &data->ssl, EAP_TYPE_PEAP,\r
+ data->peap_version, req->identifier,\r
+ rmsg, out_data)) {\r
+ wpa_printf(MSG_INFO, "EAP-PEAP: Failed to encrypt "\r
+ "a Phase 2 frame");\r
+ }\r
+ wpabuf_free(resp);\r
+ }\r
+\r
+ return 0;\r
+}\r
+\r
+\r
+static struct wpabuf *\r
+eap_peap_process(struct eap_sm *sm, void *priv,\r
+ struct eap_method_ret *ret,\r
+ const struct wpabuf *reqData)\r
+{\r
+ const struct eap_hdr *req;\r
+ size_t left;\r
+ int res;\r
+ u8 flags, id;\r
+ struct wpabuf *resp;\r
+ const u8 *pos;\r
+ struct eap_peap_data *data = priv;\r
+\r
+ pos = eap_peer_tls_process_init(sm, &data->ssl, EAP_TYPE_PEAP, ret,\r
+ reqData, &left, &flags);\r
+ if (pos == NULL)\r
+ return NULL;\r
+ req = wpabuf_head(reqData);\r
+ id = req->identifier;\r
+\r
+ if (flags & EAP_TLS_FLAGS_START) {\r
+ wpa_printf(MSG_DEBUG, "EAP-PEAP: Start (server ver=%d, own "\r
+ "ver=%d)", flags & EAP_TLS_VERSION_MASK,\r
+ data->peap_version);\r
+ if ((flags & EAP_TLS_VERSION_MASK) < data->peap_version)\r
+ data->peap_version = flags & EAP_TLS_VERSION_MASK;\r
+ if (data->force_peap_version >= 0 &&\r
+ data->force_peap_version != data->peap_version) {\r
+ wpa_printf(MSG_WARNING, "EAP-PEAP: Failed to select "\r
+ "forced PEAP version %d",\r
+ data->force_peap_version);\r
+ ret->methodState = METHOD_DONE;\r
+ ret->decision = DECISION_FAIL;\r
+ ret->allowNotifications = FALSE;\r
+ return NULL;\r
+ }\r
+ wpa_printf(MSG_DEBUG, "EAP-PEAP: Using PEAP version %d",\r
+ data->peap_version);\r
+ left = 0; /* make sure that this frame is empty, even though it\r
+ * should always be, anyway */\r
+ }\r
+\r
+ resp = NULL;\r
+ if (tls_connection_established(sm->ssl_ctx, data->ssl.conn) &&\r
+ !data->resuming) {\r
+ struct wpabuf msg;\r
+ wpabuf_set(&msg, pos, left);\r
+ res = eap_peap_decrypt(sm, data, ret, req, &msg, &resp);\r
+ } else {\r
+ res = eap_peer_tls_process_helper(sm, &data->ssl,\r
+ EAP_TYPE_PEAP,\r
+ data->peap_version, id, pos,\r
+ left, &resp);\r
+\r
+ if (tls_connection_established(sm->ssl_ctx, data->ssl.conn)) {\r
+ char label[24];\r
+ wpa_printf(MSG_DEBUG, "EAP-PEAP: TLS done, proceed to Phase 2");\r
+ os_free(data->key_data);\r
+ /* draft-josefsson-ppext-eap-tls-eap-05.txt\r
+ * specifies that PEAPv1 would use "client PEAP\r
+ * encryption" as the label. However, most existing\r
+ * PEAPv1 implementations seem to be using the old\r
+ * label, "client EAP encryption", instead. Use the old\r
+ * label by default, but allow it to be configured with\r
+ * phase1 parameter peaplabel=1. */\r
+ if (data->peap_version > 1 || data->force_new_label)\r
+ //label = "client PEAP encryption";\r
+ strcpy(label, "client PEAP encryption");\r
+ else\r
+ //label = "client EAP encryption";\r
+ strcpy(label, "client EAP encryption");\r
+ wpa_printf(MSG_DEBUG, "EAP-PEAP: using label '%s' in "\r
+ "key derivation", label);\r
+ data->key_data =\r
+ eap_peer_tls_derive_key(sm, &data->ssl, label,\r
+ EAP_TLS_KEY_LEN);\r
+ if (data->key_data) {\r
+ wpa_hexdump_key(MSG_DEBUG, \r
+ "EAP-PEAP: Derived key",\r
+ data->key_data,\r
+ EAP_TLS_KEY_LEN);\r
+ } else {\r
+ wpa_printf(MSG_DEBUG, "EAP-PEAP: Failed to "\r
+ "derive key");\r
+ }\r
+\r
+ os_free(data->session_id);\r
+ data->session_id =\r
+ eap_peer_tls_derive_session_id(sm, &data->ssl,\r
+ EAP_TYPE_PEAP,\r
+ &data->id_len);\r
+ if (data->session_id) {\r
+ wpa_hexdump(MSG_DEBUG,\r
+ "EAP-PEAP: Derived Session-Id",\r
+ data->session_id, data->id_len);\r
+ } else {\r
+ wpa_printf(MSG_ERROR, "EAP-PEAP: Failed to "\r
+ "derive Session-Id");\r
+ }\r
+\r
+ if (sm->workaround && data->resuming) {\r
+ /*\r
+ * At least few RADIUS servers (Aegis v1.1.6;\r
+ * but not v1.1.4; and Cisco ACS) seem to be\r
+ * terminating PEAPv1 (Aegis) or PEAPv0 (Cisco\r
+ * ACS) session resumption with outer\r
+ * EAP-Success. This does not seem to follow\r
+ * draft-josefsson-pppext-eap-tls-eap-05.txt\r
+ * section 4.2, so only allow this if EAP\r
+ * workarounds are enabled.\r
+ */\r
+ wpa_printf(MSG_DEBUG, "EAP-PEAP: Workaround - "\r
+ "allow outer EAP-Success to "\r
+ "terminate PEAP resumption");\r
+ ret->decision = DECISION_COND_SUCC;\r
+ data->phase2_success = 1;\r
+ }\r
+\r
+ data->resuming = 0;\r
+ }\r
+\r
+ if (res == 2) {\r
+ struct wpabuf msg;\r
+ /*\r
+ * Application data included in the handshake message.\r
+ */\r
+ wpabuf_free(data->pending_phase2_req);\r
+ data->pending_phase2_req = resp;\r
+ resp = NULL;\r
+ wpabuf_set(&msg, pos, left);\r
+ res = eap_peap_decrypt(sm, data, ret, req, &msg,\r
+ &resp);\r
+ }\r
+ }\r
+\r
+ if (ret->methodState == METHOD_DONE) {\r
+ ret->allowNotifications = FALSE;\r
+ }\r
+\r
+ if (res == 1) {\r
+ wpabuf_free(resp);\r
+ return eap_peer_tls_build_ack(id, EAP_TYPE_PEAP,\r
+ data->peap_version);\r
+ }\r
+\r
+ return resp;\r
+}\r
+\r
+\r
+static bool\r
+eap_peap_has_reauth_data(struct eap_sm *sm, void *priv)\r
+{\r
+ struct eap_peap_data *data = priv;\r
+ return tls_connection_established(sm->ssl_ctx, data->ssl.conn) &&\r
+ data->phase2_success;\r
+}\r
+\r
+\r
+static void\r
+eap_peap_deinit_for_reauth(struct eap_sm *sm, void *priv)\r
+{\r
+ struct eap_peap_data *data = priv;\r
+ wpabuf_free(data->pending_phase2_req);\r
+ data->pending_phase2_req = NULL;\r
+ data->crypto_binding_used = 0;\r
+}\r
+\r
+\r
+static void *\r
+eap_peap_init_for_reauth(struct eap_sm *sm, void *priv)\r
+{\r
+ struct eap_peap_data *data = priv;\r
+ os_free(data->key_data);\r
+ data->key_data = NULL;\r
+ os_free(data->session_id);\r
+ data->session_id = NULL;\r
+ if (eap_peer_tls_reauth_init(sm, &data->ssl)) {\r
+ os_free(data);\r
+ return NULL;\r
+ }\r
+ if (data->phase2_priv && data->phase2_method &&\r
+ data->phase2_method->init_for_reauth)\r
+ data->phase2_method->init_for_reauth(sm, data->phase2_priv);\r
+ data->phase2_success = 0;\r
+ data->phase2_eap_success = 0;\r
+ data->phase2_eap_started = 0;\r
+ data->resuming = 1;\r
+ data->reauth = 1;\r
+ sm->peap_done = FALSE;\r
+ return priv;\r
+}\r
+\r
+\r
+static int\r
+eap_peap_get_status(struct eap_sm *sm, void *priv, char *buf,\r
+ size_t buflen, int verbose)\r
+{\r
+ struct eap_peap_data *data = priv;\r
+ int len, ret;\r
+\r
+ len = eap_peer_tls_status(sm, &data->ssl, buf, buflen, verbose);\r
+ if (data->phase2_method) {\r
+ ret = snprintf(buf + len, buflen - len,\r
+ "EAP-PEAPv%d Phase2 method=%d\n",\r
+ data->peap_version,\r
+ data->phase2_method->method);\r
+ if (ret < 0 || (size_t) ret >= buflen - len)\r
+ return len;\r
+ len += ret;\r
+ }\r
+ return len;\r
+}\r
+\r
+\r
+static bool\r
+eap_peap_isKeyAvailable(struct eap_sm *sm, void *priv)\r
+{\r
+ struct eap_peap_data *data = priv;\r
+ return data->key_data != NULL && data->phase2_success;\r
+}\r
+\r
+\r
+static u8 *\r
+eap_peap_getKey(struct eap_sm *sm, void *priv, size_t *len)\r
+{\r
+ struct eap_peap_data *data = priv;\r
+ u8 *key;\r
+\r
+ if (data->key_data == NULL || !data->phase2_success)\r
+ return NULL;\r
+\r
+ key = os_malloc(EAP_TLS_KEY_LEN);\r
+ if (key == NULL)\r
+ return NULL;\r
+\r
+ *len = EAP_TLS_KEY_LEN;\r
+\r
+ if (data->crypto_binding_used) {\r
+ u8 csk[128];\r
+ /*\r
+ * Note: It looks like Microsoft implementation requires null\r
+ * termination for this label while the one used for deriving\r
+ * IPMK|CMK did not use null termination.\r
+ */\r
+ if (peap_prfplus(data->peap_version, data->ipmk, 40,\r
+ "Session Key Generating Function",\r
+ (u8 *) "\00", 1, csk, sizeof(csk)) < 0) {\r
+ os_free(key);\r
+ return NULL;\r
+ }\r
+ wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: CSK", csk, sizeof(csk));\r
+ os_memcpy(key, csk, EAP_TLS_KEY_LEN);\r
+ wpa_hexdump(MSG_DEBUG, "EAP-PEAP: Derived key",\r
+ key, EAP_TLS_KEY_LEN);\r
+ } else\r
+ os_memcpy(key, data->key_data, EAP_TLS_KEY_LEN);\r
+\r
+ return key;\r
+}\r
+\r
+\r
+static u8 *\r
+eap_peap_get_session_id(struct eap_sm *sm, void *priv, size_t *len)\r
+{\r
+ struct eap_peap_data *data = priv;\r
+ u8 *id;\r
+\r
+ if (data->session_id == NULL || !data->phase2_success)\r
+ return NULL;\r
+\r
+ id = os_malloc(data->id_len);\r
+ if (id == NULL)\r
+ return NULL;\r
+\r
+ *len = data->id_len;\r
+ os_memcpy(id, data->session_id, data->id_len);\r
+\r
+ return id;\r
+}\r
+\r
+\r
+int\r
+eap_peer_peap_register(void)\r
+{\r
+ struct eap_method *eap;\r
+ int ret;\r
+\r
+ eap = eap_peer_method_alloc(EAP_VENDOR_IETF, EAP_TYPE_PEAP,\r
+ "PEAP");\r
+ if (eap == NULL)\r
+ return -1;\r
+\r
+ eap->init = eap_peap_init;\r
+ eap->deinit = eap_peap_deinit;\r
+ eap->process = eap_peap_process;\r
+ eap->isKeyAvailable = eap_peap_isKeyAvailable;\r
+ eap->getKey = eap_peap_getKey;\r
+ eap->get_status = eap_peap_get_status;\r
+ eap->has_reauth_data = eap_peap_has_reauth_data;\r
+ eap->deinit_for_reauth = eap_peap_deinit_for_reauth;\r
+ eap->init_for_reauth = eap_peap_init_for_reauth;\r
+ eap->getSessionId = eap_peap_get_session_id;\r
+\r
+ ret = eap_peer_method_register(eap);\r
+ if (ret)\r
+ eap_peer_method_free(eap);\r
+ return ret;\r
+}\r
+\r
+#endif /* EAP_PEAP */\r
--- /dev/null
+/*\r
+ * EAP-PEAP common routines\r
+ * Copyright (c) 2008-2011, Jouni Malinen <j@w1.fi>\r
+ *\r
+ * This software may be distributed under the terms of the BSD license.\r
+ * See README for more details.\r
+ */\r
+\r
+#ifdef EAP_PEAP\r
+\r
+#include "wpa/includes.h"\r
+\r
+#include "wpa/common.h"\r
+#include "crypto/sha1.h"\r
+#include "wpa2/eap_peer/eap_peap_common.h"\r
+\r
+int\r
+peap_prfplus(int version, const u8 *key, size_t key_len,\r
+ const char *label, const u8 *seed, size_t seed_len,\r
+ u8 *buf, size_t buf_len)\r
+{\r
+ unsigned char counter = 0;\r
+ size_t pos, plen;\r
+ u8 hash[SHA1_MAC_LEN];\r
+ size_t label_len = os_strlen(label);\r
+ u8 extra[2];\r
+ const unsigned char *addr[5];\r
+ size_t len[5];\r
+\r
+ addr[0] = hash;\r
+ len[0] = 0;\r
+ addr[1] = (unsigned char *) label;\r
+ len[1] = label_len;\r
+ addr[2] = seed;\r
+ len[2] = seed_len;\r
+\r
+ if (version == 0) {\r
+ /*\r
+ * PRF+(K, S, LEN) = T1 | T2 | ... | Tn\r
+ * T1 = HMAC-SHA1(K, S | 0x01 | 0x00 | 0x00)\r
+ * T2 = HMAC-SHA1(K, T1 | S | 0x02 | 0x00 | 0x00)\r
+ * ...\r
+ * Tn = HMAC-SHA1(K, Tn-1 | S | n | 0x00 | 0x00)\r
+ */\r
+\r
+ extra[0] = 0;\r
+ extra[1] = 0;\r
+\r
+ addr[3] = &counter;\r
+ len[3] = 1;\r
+ addr[4] = extra;\r
+ len[4] = 2;\r
+ } else {\r
+ /*\r
+ * PRF (K,S,LEN) = T1 | T2 | T3 | T4 | ... where:\r
+ * T1 = HMAC-SHA1(K, S | LEN | 0x01)\r
+ * T2 = HMAC-SHA1 (K, T1 | S | LEN | 0x02)\r
+ * T3 = HMAC-SHA1 (K, T2 | S | LEN | 0x03)\r
+ * T4 = HMAC-SHA1 (K, T3 | S | LEN | 0x04)\r
+ * ...\r
+ */\r
+\r
+ extra[0] = buf_len & 0xff;\r
+\r
+ addr[3] = extra;\r
+ len[3] = 1;\r
+ addr[4] = &counter;\r
+ len[4] = 1;\r
+ }\r
+\r
+ pos = 0;\r
+ while (pos < buf_len) {\r
+ counter++;\r
+ plen = buf_len - pos;\r
+ if (hmac_sha1_vector(key, key_len, 5, addr, len, hash) < 0)\r
+ return -1;\r
+ if (plen >= SHA1_MAC_LEN) {\r
+ os_memcpy(&buf[pos], hash, SHA1_MAC_LEN);\r
+ pos += SHA1_MAC_LEN;\r
+ } else {\r
+ os_memcpy(&buf[pos], hash, plen);\r
+ break;\r
+ }\r
+ len[0] = SHA1_MAC_LEN;\r
+ }\r
+\r
+ return 0;\r
+}\r
+\r
+#endif /* EAP_PEAP */\r
--- /dev/null
+/*
+ * EAP peer method: EAP-TLS (RFC 2716)
+ * Copyright (c) 2004-2008, 2012, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+#ifdef EAP_TLS
+
+#include "wpa/includes.h"
+
+#include "wpa/common.h"
+#include "wpa2/tls/tls.h"
+#include "wpa2/eap_peer/eap_i.h"
+#include "wpa2/eap_peer/eap_defs.h"
+#include "wpa2/eap_peer/eap_tls_common.h"
+#include "wpa2/eap_peer/eap_config.h"
+#include "wpa2/eap_peer/eap_methods.h"
+
+struct eap_tls_data {
+ struct eap_ssl_data ssl;
+ u8 *key_data;
+ u8 *session_id;
+ size_t id_len;
+ void *ssl_ctx;
+ u8 eap_type;
+};
+
+
+
+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->session_id);
+ os_free(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 ||
+ config->private_key == 0) {
+ wpa_printf(MSG_INFO, "EAP-TLS: Private key not configured");
+ return NULL;
+ }
+
+ data = (struct eap_tls_data *)os_zalloc(sizeof(*data));
+ if (data == NULL)
+ return NULL;
+
+ data->ssl_ctx = sm->ssl_ctx;
+
+ if (eap_peer_tls_ssl_init(sm, &data->ssl, config, EAP_TYPE_TLS)) {
+ wpa_printf(MSG_INFO, "EAP-TLS: Failed to initialize SSL.");
+ eap_tls_deinit(sm, data);
+ return NULL;
+ }
+
+ data->eap_type = EAP_TYPE_TLS;
+
+ return 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, data->eap_type, 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");
+ }
+
+ os_free(data->session_id);
+ data->session_id = eap_peer_tls_derive_session_id(sm, &data->ssl,
+ EAP_TYPE_TLS,
+ &data->id_len);
+ if (data->session_id) {
+ wpa_hexdump(MSG_DEBUG, "EAP-TLS: Derived Session-Id",
+ data->session_id, data->id_len);
+ } else {
+ wpa_printf(MSG_ERROR, "EAP-TLS: Failed to derive Session-Id");
+ }
+}
+
+
+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, data->eap_type, 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, data->eap_type, 0,
+ id, pos, left, &resp);
+
+ if (res < 0) {
+ return eap_tls_failure(sm, data, ret, res, resp, id);
+ }
+
+ if (tls_connection_established(data->ssl_ctx, data->ssl.conn))
+ eap_tls_success(sm, data, ret);
+
+ if (res == 1) {
+ wpabuf_free(resp);
+ return eap_peer_tls_build_ack(id, data->eap_type, 0);
+ }
+
+ return resp;
+}
+
+static bool 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;
+}
+
+int eap_peer_tls_register(void)
+{
+ struct eap_method *eap;
+ int ret;
+
+ eap = eap_peer_method_alloc(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;
+
+ ret = eap_peer_method_register(eap);
+ if (ret)
+ eap_peer_method_free(eap);
+ return ret;
+}
+
+
+#endif /* EAP_TLS */
--- /dev/null
+/*
+ * EAP peer: EAP-TLS/PEAP/TTLS/FAST common functions
+ * Copyright (c) 2004-2013, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "wpa/includes.h"
+
+#include "wpa/common.h"
+#include "crypto/sha1.h"
+#include "wpa2/tls/tls.h"
+#include "wpa2/eap_peer/eap_i.h"
+#include "wpa2/eap_peer/eap_tls_common.h"
+#include "wpa2/eap_peer/eap_config.h"
+#include "wpa2/eap_peer/eap_methods.h"
+
+static struct wpabuf * eap_tls_msg_alloc(EapType type, size_t payload_len,
+ u8 code, u8 identifier)
+{
+ if (type == EAP_UNAUTH_TLS_TYPE)
+ return eap_msg_alloc(EAP_VENDOR_UNAUTH_TLS,
+ EAP_VENDOR_TYPE_UNAUTH_TLS, payload_len,
+ code, identifier);
+ return eap_msg_alloc(EAP_VENDOR_IETF, type, payload_len, code,
+ identifier);
+}
+
+
+static int eap_tls_check_blob(struct eap_sm *sm, const char **name,
+ const u8 **data, size_t *data_len)
+{
+ const struct wpa_config_blob *blob;
+
+ if (*name == NULL)// || os_strncmp(*name, "blob://", 7) != 0)
+ return 0;
+
+ blob = eap_get_config_blob(sm, *name);// + 7);
+ if (blob == NULL) {
+ wpa_printf(MSG_ERROR, "%s: Named configuration blob '%s' not "
+ "found", __func__, *name);// + 7);
+ return -1;
+ }
+
+ *name = NULL;
+ *data = blob->data;
+ *data_len = blob->len;
+
+ return 0;
+}
+
+
+static void eap_tls_params_flags(struct tls_connection_params *params,
+ const char *txt)
+{
+ if (txt == NULL)
+ return;
+ if (os_strstr(txt, "tls_allow_md5=1"))
+ params->flags |= TLS_CONN_ALLOW_SIGN_RSA_MD5;
+ if (os_strstr(txt, "tls_disable_time_checks=1"))
+ params->flags |= TLS_CONN_DISABLE_TIME_CHECKS;
+ if (os_strstr(txt, "tls_disable_session_ticket=1"))
+ params->flags |= TLS_CONN_DISABLE_SESSION_TICKET;
+ if (os_strstr(txt, "tls_disable_session_ticket=0"))
+ params->flags &= ~TLS_CONN_DISABLE_SESSION_TICKET;
+}
+
+static void eap_tls_params_from_conf1(struct tls_connection_params *params,
+ struct eap_peer_config *config)
+{
+ params->ca_cert = (char *) config->ca_cert;
+ params->ca_path = (char *) config->ca_path;
+ params->client_cert = (char *) config->client_cert;
+ params->private_key = (char *) config->private_key;
+ params->private_key_passwd = (char *) config->private_key_passwd;
+ eap_tls_params_flags(params, config->phase1);
+ if (wifi_sta_get_enterprise_disable_time_check())
+ params->flags |= TLS_CONN_DISABLE_TIME_CHECKS;
+ else
+ params->flags &= (~TLS_CONN_DISABLE_TIME_CHECKS);
+}
+
+static int eap_tls_params_from_conf(struct eap_sm *sm,
+ struct eap_ssl_data *data,
+ struct tls_connection_params *params,
+ struct eap_peer_config *config)
+{
+ os_memset(params, 0, sizeof(*params));
+ if (sm->workaround && data->eap_type != EAP_TYPE_FAST) {
+ /*
+ * Some deployed authentication servers seem to be unable to
+ * handle the TLS Session Ticket extension (they are supposed
+ * to ignore unrecognized TLS extensions, but end up rejecting
+ * the ClientHello instead). As a workaround, disable use of
+ * TLS Sesson Ticket extension for EAP-TLS, EAP-PEAP, and
+ * EAP-TTLS (EAP-FAST uses session ticket, so any server that
+ * supports EAP-FAST does not need this workaround).
+ */
+ params->flags |= TLS_CONN_DISABLE_SESSION_TICKET;
+ }
+
+ wpa_printf(MSG_DEBUG, "TLS: using phase1 config options");
+ eap_tls_params_from_conf1(params, config);
+
+ /*
+ * Use blob data, if available. Otherwise, leave reference to external
+ * file as-is.
+ */
+ if (eap_tls_check_blob(sm, ¶ms->ca_cert, ¶ms->ca_cert_blob,
+ ¶ms->ca_cert_blob_len) ||
+ eap_tls_check_blob(sm, ¶ms->client_cert,
+ ¶ms->client_cert_blob,
+ ¶ms->client_cert_blob_len) ||
+ eap_tls_check_blob(sm, ¶ms->private_key,
+ ¶ms->private_key_blob,
+ ¶ms->private_key_blob_len)) {
+ wpa_printf(MSG_INFO, "SSL: Failed to get configuration blobs");
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static int eap_tls_init_connection(struct eap_sm *sm,
+ struct eap_ssl_data *data,
+ struct eap_peer_config *config,
+ struct tls_connection_params *params)
+{
+ int res;
+
+ if (config->ocsp)
+ params->flags |= TLS_CONN_REQUEST_OCSP;
+ if (config->ocsp == 2)
+ params->flags |= TLS_CONN_REQUIRE_OCSP;
+ data->conn = tls_connection_init(data->ssl_ctx);
+ if (data->conn == NULL) {
+ wpa_printf(MSG_INFO, "SSL: Failed to initialize new TLS "
+ "connection");
+ return -1;
+ }
+
+ res = tls_connection_set_params(data->ssl_ctx, data->conn, params);
+
+ if (res == TLS_SET_PARAMS_ENGINE_PRV_INIT_FAILED) {
+ /*
+ * At this point with the pkcs11 engine the PIN might be wrong.
+ * We reset the PIN in the configuration to be sure to not use
+ * it again and the calling function must request a new one.
+ */
+ os_free(config->pin);
+ config->pin = NULL;
+ } else if (res == TLS_SET_PARAMS_ENGINE_PRV_VERIFY_FAILED) {
+ wpa_printf(MSG_INFO, "TLS: Failed to load private key");
+ /*
+ * We do not know exactly but maybe the PIN was wrong,
+ * so ask for a new one.
+ */
+ os_free(config->pin);
+ config->pin = NULL;
+ tls_connection_deinit(data->ssl_ctx, data->conn);
+ data->conn = NULL;
+ return -1;
+ } else if (res) {
+ wpa_printf(MSG_INFO, "TLS: Failed to set TLS connection "
+ "parameters");
+ tls_connection_deinit(data->ssl_ctx, data->conn);
+ data->conn = NULL;
+ return -1;
+ }
+
+ return 0;
+}
+
+
+/**
+ * eap_peer_tls_ssl_init - Initialize shared TLS functionality
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @data: Data for TLS processing
+ * @config: Pointer to the network configuration
+ * @eap_type: EAP method used in Phase 1 (EAP_TYPE_TLS/PEAP/TTLS/FAST)
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function is used to initialize shared TLS functionality for EAP-TLS,
+ * EAP-PEAP, EAP-TTLS, and EAP-FAST.
+ */
+int eap_peer_tls_ssl_init(struct eap_sm *sm, struct eap_ssl_data *data,
+ struct eap_peer_config *config, u8 eap_type)
+{
+ struct tls_connection_params params;
+
+ if (config == NULL)
+ return -1;
+
+ data->eap = sm;
+ data->eap_type = eap_type;
+ data->ssl_ctx = sm->ssl_ctx;
+ if (eap_tls_params_from_conf(sm, data, ¶ms, config) < 0) /* no phase2 */
+ return -1;
+
+ if (eap_tls_init_connection(sm, data, config, ¶ms) < 0)
+ return -1;
+
+ data->tls_out_limit = config->fragment_size;
+
+ if (config->phase1 &&
+ os_strstr(config->phase1, "include_tls_length=1")) {
+ wpa_printf(MSG_INFO, "TLS: Include TLS Message Length in "
+ "unfragmented packets");
+ data->include_tls_length = 1;
+ }
+
+ return 0;
+}
+
+
+/**
+ * eap_peer_tls_ssl_deinit - Deinitialize shared TLS functionality
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @data: Data for TLS processing
+ *
+ * This function deinitializes shared TLS functionality that was initialized
+ * with eap_peer_tls_ssl_init().
+ */
+void eap_peer_tls_ssl_deinit(struct eap_sm *sm, struct eap_ssl_data *data)
+{
+ tls_connection_deinit(data->ssl_ctx, data->conn);
+ eap_peer_tls_reset_input(data);
+ eap_peer_tls_reset_output(data);
+}
+
+
+/**
+ * eap_peer_tls_derive_key - Derive a key based on TLS session data
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @data: Data for TLS processing
+ * @label: Label string for deriving the keys, e.g., "client EAP encryption"
+ * @len: Length of the key material to generate (usually 64 for MSK)
+ * Returns: Pointer to allocated key on success or %NULL on failure
+ *
+ * This function uses TLS-PRF to generate pseudo-random data based on the TLS
+ * session data (client/server random and master key). Each key type may use a
+ * different label to bind the key usage into the generated material.
+ *
+ * The caller is responsible for freeing the returned buffer.
+ */
+u8 * eap_peer_tls_derive_key(struct eap_sm *sm, struct eap_ssl_data *data,
+ const char *label, size_t len)
+{
+ struct tls_keys keys;
+ u8 *rnd = NULL, *out;
+
+ out = os_malloc(len);
+ if (out == NULL)
+ return NULL;
+
+ /* First, try to use TLS library function for PRF, if available. */
+ if (tls_connection_prf(data->ssl_ctx, data->conn, label, 0, out, len)
+ == 0)
+ return out;
+
+ /*
+ * TLS library did not support key generation, so get the needed TLS
+ * session parameters and use an internal implementation of TLS PRF to
+ * derive the key.
+ */
+ if (tls_connection_get_keys(data->ssl_ctx, data->conn, &keys))
+ goto fail;
+
+ if (keys.client_random == NULL || keys.server_random == NULL ||
+ keys.master_key == NULL)
+ goto fail;
+
+ rnd = os_malloc(keys.client_random_len + keys.server_random_len);
+ if (rnd == NULL)
+ goto fail;
+ os_memcpy(rnd, keys.client_random, keys.client_random_len);
+ os_memcpy(rnd + keys.client_random_len, keys.server_random,
+ keys.server_random_len);
+
+ if (tls_prf_sha1_md5(keys.master_key, keys.master_key_len,
+ label, rnd, keys.client_random_len +
+ keys.server_random_len, out, len)) {
+ goto fail;
+ }
+
+ os_free(rnd);
+ return out;
+
+fail:
+ os_free(out);
+ os_free(rnd);
+ return NULL;
+}
+
+
+/**
+ * eap_peer_tls_derive_session_id - Derive a Session-Id based on TLS data
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @data: Data for TLS processing
+ * @eap_type: EAP method used in Phase 1 (EAP_TYPE_TLS/PEAP/TTLS/FAST)
+ * @len: Pointer to length of the session ID generated
+ * Returns: Pointer to allocated Session-Id on success or %NULL on failure
+ *
+ * This function derive the Session-Id based on the TLS session data
+ * (client/server random and method type).
+ *
+ * The caller is responsible for freeing the returned buffer.
+ */
+u8 * eap_peer_tls_derive_session_id(struct eap_sm *sm,
+ struct eap_ssl_data *data, u8 eap_type,
+ size_t *len)
+{
+ struct tls_keys keys;
+ u8 *out;
+
+ /*
+ * TLS library did not support session ID generation,
+ * so get the needed TLS session parameters
+ */
+ if (tls_connection_get_keys(sm->ssl_ctx, data->conn, &keys))
+ return NULL;
+
+ if (keys.client_random == NULL || keys.server_random == NULL ||
+ keys.master_key == NULL)
+ return NULL;
+
+ *len = 1 + keys.client_random_len + keys.server_random_len;
+ out = os_malloc(*len);
+ if (out == NULL)
+ return NULL;
+
+ /* Session-Id = EAP type || client.random || server.random */
+ out[0] = eap_type;
+ os_memcpy(out + 1, keys.client_random, keys.client_random_len);
+ os_memcpy(out + 1 + keys.client_random_len, keys.server_random,
+ keys.server_random_len);
+
+ return out;
+}
+
+
+/**
+ * eap_peer_tls_reassemble_fragment - Reassemble a received fragment
+ * @data: Data for TLS processing
+ * @in_data: Next incoming TLS segment
+ * Returns: 0 on success, 1 if more data is needed for the full message, or
+ * -1 on error
+ */
+static int eap_peer_tls_reassemble_fragment(struct eap_ssl_data *data,
+ const struct wpabuf *in_data)
+{
+ size_t tls_in_len, in_len;
+
+ tls_in_len = data->tls_in ? wpabuf_len(data->tls_in) : 0;
+ in_len = in_data ? wpabuf_len(in_data) : 0;
+
+ if (tls_in_len + in_len == 0) {
+ /* No message data received?! */
+ wpa_printf(MSG_WARNING, "SSL: Invalid reassembly state: "
+ "tls_in_left=%lu tls_in_len=%lu in_len=%lu",
+ (unsigned long) data->tls_in_left,
+ (unsigned long) tls_in_len,
+ (unsigned long) in_len);
+ eap_peer_tls_reset_input(data);
+ return -1;
+ }
+
+ if (tls_in_len + in_len > 65536) {
+ /*
+ * Limit length to avoid rogue servers from causing large
+ * memory allocations.
+ */
+ wpa_printf(MSG_INFO, "SSL: Too long TLS fragment (size over "
+ "64 kB)");
+ eap_peer_tls_reset_input(data);
+ return -1;
+ }
+
+ if (in_len > data->tls_in_left) {
+ /* Sender is doing something odd - reject message */
+ wpa_printf(MSG_INFO, "SSL: more data than TLS message length "
+ "indicated");
+ eap_peer_tls_reset_input(data);
+ return -1;
+ }
+
+ if (wpabuf_resize(&data->tls_in, in_len) < 0) {
+ wpa_printf(MSG_INFO, "SSL: Could not allocate memory for TLS "
+ "data");
+ eap_peer_tls_reset_input(data);
+ return -1;
+ }
+ if (in_data)
+ wpabuf_put_buf(data->tls_in, in_data);
+ data->tls_in_left -= in_len;
+
+ if (data->tls_in_left > 0) {
+ wpa_printf(MSG_INFO, "SSL: Need %lu bytes more input "
+ "data", (unsigned long) data->tls_in_left);
+ return 1;
+ }
+
+ return 0;
+}
+
+
+/**
+ * eap_peer_tls_data_reassemble - Reassemble TLS data
+ * @data: Data for TLS processing
+ * @in_data: Next incoming TLS segment
+ * @need_more_input: Variable for returning whether more input data is needed
+ * to reassemble this TLS packet
+ * Returns: Pointer to output data, %NULL on error or when more data is needed
+ * for the full message (in which case, *need_more_input is also set to 1).
+ *
+ * This function reassembles TLS fragments. Caller must not free the returned
+ * data buffer since an internal pointer to it is maintained.
+ */
+static const struct wpabuf * eap_peer_tls_data_reassemble(
+ struct eap_ssl_data *data, const struct wpabuf *in_data,
+ int *need_more_input)
+{
+ *need_more_input = 0;
+
+ if (data->tls_in_left > wpabuf_len(in_data) || data->tls_in) {
+ /* Message has fragments */
+ int res = eap_peer_tls_reassemble_fragment(data, in_data);
+ if (res) {
+ if (res == 1)
+ *need_more_input = 1;
+ return NULL;
+ }
+
+ /* Message is now fully reassembled. */
+ } else {
+ /* No fragments in this message, so just make a copy of it. */
+ data->tls_in_left = 0;
+ data->tls_in = wpabuf_dup(in_data);
+ if (data->tls_in == NULL)
+ return NULL;
+ }
+
+ return data->tls_in;
+}
+
+
+/**
+ * eap_tls_process_input - Process incoming TLS message
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @data: Data for TLS processing
+ * @in_data: Message received from the server
+ * @in_len: Length of in_data
+ * @out_data: Buffer for returning a pointer to application data (if available)
+ * Returns: 0 on success, 1 if more input data is needed, 2 if application data
+ * is available, -1 on failure
+ */
+static int eap_tls_process_input(struct eap_sm *sm, struct eap_ssl_data *data,
+ const u8 *in_data, size_t in_len,
+ struct wpabuf **out_data)
+{
+ const struct wpabuf *msg;
+ int need_more_input;
+ struct wpabuf *appl_data;
+ struct wpabuf buf;
+
+ wpabuf_set(&buf, in_data, in_len);
+ msg = eap_peer_tls_data_reassemble(data, &buf, &need_more_input);
+ if (msg == NULL)
+ return need_more_input ? 1 : -1;
+
+ /* Full TLS message reassembled - continue handshake processing */
+ if (data->tls_out) {
+ /* This should not happen.. */
+ wpa_printf(MSG_INFO, "SSL: eap_tls_process_input - pending "
+ "tls_out data even though tls_out_len = 0");
+ wpabuf_free(data->tls_out);
+ //WPA_ASSERT(data->tls_out == NULL);
+ }
+ appl_data = NULL;
+ data->tls_out = tls_connection_handshake(data->ssl_ctx, data->conn,
+ msg, &appl_data);
+
+ eap_peer_tls_reset_input(data);
+ if (appl_data &&
+ tls_connection_established(data->ssl_ctx, data->conn) &&
+ !tls_connection_get_failed(data->ssl_ctx, data->conn)) {
+ wpa_hexdump_buf_key(MSG_MSGDUMP, "SSL: Application data",
+ appl_data);
+ *out_data = appl_data;
+ return 2;
+ }
+
+ wpabuf_free(appl_data);
+
+ return 0;
+}
+
+
+/**
+ * eap_tls_process_output - Process outgoing TLS message
+ * @data: Data for TLS processing
+ * @eap_type: EAP type (EAP_TYPE_TLS, EAP_TYPE_PEAP, ...)
+ * @peap_version: Version number for EAP-PEAP/TTLS
+ * @id: EAP identifier for the response
+ * @ret: Return value to use on success
+ * @out_data: Buffer for returning the allocated output buffer
+ * Returns: ret (0 or 1) on success, -1 on failure
+ */
+static int eap_tls_process_output(struct eap_ssl_data *data, EapType eap_type,
+ int peap_version, u8 id, int ret,
+ struct wpabuf **out_data)
+{
+ size_t len;
+ u8 *flags;
+ int more_fragments, length_included;
+
+ if (data->tls_out == NULL)
+ return -1;
+ len = wpabuf_len(data->tls_out) - data->tls_out_pos;
+ wpa_printf(MSG_DEBUG, "SSL: %lu bytes left to be sent out (of total "
+ "%lu bytes)",
+ (unsigned long) len,
+ (unsigned long) wpabuf_len(data->tls_out));
+
+ /*
+ * Limit outgoing message to the configured maximum size. Fragment
+ * message if needed.
+ */
+ if (len > data->tls_out_limit) {
+ more_fragments = 1;
+ len = data->tls_out_limit;
+ wpa_printf(MSG_DEBUG, "SSL: sending %lu bytes, more fragments "
+ "will follow", (unsigned long) len);
+ } else
+ more_fragments = 0;
+
+ length_included = data->tls_out_pos == 0 &&
+ (wpabuf_len(data->tls_out) > data->tls_out_limit ||
+ data->include_tls_length);
+ if (!length_included &&
+ eap_type == EAP_TYPE_PEAP && peap_version == 0 &&
+ !tls_connection_established(data->eap->ssl_ctx, data->conn)) {
+ /*
+ * Windows Server 2008 NPS really wants to have the TLS Message
+ * length included in phase 0 even for unfragmented frames or
+ * it will get very confused with Compound MAC calculation and
+ * Outer TLVs.
+ */
+ length_included = 1;
+ }
+
+ *out_data = eap_tls_msg_alloc(eap_type, 1 + length_included * 4 + len,
+ EAP_CODE_RESPONSE, id);
+ if (*out_data == NULL) {
+ printf("[Debug] out_data is null, return \n");
+ return -1;
+ }
+
+ flags = wpabuf_put(*out_data, 1);
+ *flags = peap_version;
+ if (more_fragments)
+ *flags |= EAP_TLS_FLAGS_MORE_FRAGMENTS;
+ if (length_included) {
+ *flags |= EAP_TLS_FLAGS_LENGTH_INCLUDED;
+ wpabuf_put_be32(*out_data, wpabuf_len(data->tls_out));
+ }
+ wpabuf_put_data(*out_data,
+ wpabuf_head_u8(data->tls_out) + data->tls_out_pos,
+ len);
+ data->tls_out_pos += len;
+
+ if (!more_fragments)
+ eap_peer_tls_reset_output(data);
+
+ return ret;
+}
+
+
+/**
+ * eap_peer_tls_process_helper - Process TLS handshake message
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @data: Data for TLS processing
+ * @eap_type: EAP type (EAP_TYPE_TLS, EAP_TYPE_PEAP, ...)
+ * @peap_version: Version number for EAP-PEAP/TTLS
+ * @id: EAP identifier for the response
+ * @in_data: Message received from the server
+ * @in_len: Length of in_data
+ * @out_data: Buffer for returning a pointer to the response message
+ * Returns: 0 on success, 1 if more input data is needed, 2 if application data
+ * is available, or -1 on failure
+ *
+ * This function can be used to process TLS handshake messages. It reassembles
+ * the received fragments and uses a TLS library to process the messages. The
+ * response data from the TLS library is fragmented to suitable output messages
+ * that the caller can send out.
+ *
+ * out_data is used to return the response message if the return value of this
+ * function is 0, 2, or -1. In case of failure, the message is likely a TLS
+ * alarm message. The caller is responsible for freeing the allocated buffer if
+ * *out_data is not %NULL.
+ *
+ * This function is called for each received TLS message during the TLS
+ * handshake after eap_peer_tls_process_init() call and possible processing of
+ * TLS Flags field. Once the handshake has been completed, i.e., when
+ * tls_connection_established() returns 1, EAP method specific decrypting of
+ * the tunneled data is used.
+ */
+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)
+{
+ int ret = 0;
+
+ *out_data = NULL;
+
+ if (data->tls_out && wpabuf_len(data->tls_out) > 0 && in_len > 0) {
+ wpa_printf(MSG_DEBUG, "SSL: Received non-ACK when output "
+ "fragments are waiting to be sent out");
+ return -1;
+ }
+
+ if (data->tls_out == NULL || wpabuf_len(data->tls_out) == 0) {
+ /*
+ * No more data to send out - expect to receive more data from
+ * the AS.
+ */
+ int res = eap_tls_process_input(sm, data, in_data, in_len,
+ out_data);
+ if (res) {
+ /*
+ * Input processing failed (res = -1) or more data is
+ * needed (res = 1).
+ */
+ return res;
+ }
+
+ /*
+ * The incoming message has been reassembled and processed. The
+ * response was allocated into data->tls_out buffer.
+ */
+ }
+
+ if (data->tls_out == NULL) {
+ /*
+ * No outgoing fragments remaining from the previous message
+ * and no new message generated. This indicates an error in TLS
+ * processing.
+ */
+ eap_peer_tls_reset_output(data);
+ return -1;
+ }
+
+ if (tls_connection_get_failed(data->ssl_ctx, data->conn)) {
+ /* TLS processing has failed - return error */
+ wpa_printf(MSG_DEBUG, "SSL: Failed - tls_out available to "
+ "report error");
+ ret = -1;
+ /* TODO: clean pin if engine used? */
+ }
+
+ if (data->tls_out == NULL || wpabuf_len(data->tls_out) == 0) {
+ /*
+ * TLS negotiation should now be complete since all other cases
+ * needing more data should have been caught above based on
+ * the TLS Message Length field.
+ */
+ wpa_printf(MSG_DEBUG, "SSL: No data to be sent out");
+ wpabuf_free(data->tls_out);
+ data->tls_out = NULL;
+ return 1;
+ }
+
+ /* Send the pending message (in fragments, if needed). */
+ return eap_tls_process_output(data, eap_type, peap_version, id, ret,
+ out_data);
+}
+
+
+/**
+ * eap_peer_tls_build_ack - Build a TLS ACK frame
+ * @id: EAP identifier for the response
+ * @eap_type: EAP type (EAP_TYPE_TLS, EAP_TYPE_PEAP, ...)
+ * @peap_version: Version number for EAP-PEAP/TTLS
+ * Returns: Pointer to the allocated ACK frame or %NULL on failure
+ */
+struct wpabuf * eap_peer_tls_build_ack(u8 id, EapType eap_type,
+ int peap_version)
+{
+ struct wpabuf *resp;
+
+ resp = eap_tls_msg_alloc(eap_type, 1, EAP_CODE_RESPONSE, id);
+ if (resp == NULL)
+ return NULL;
+ wpa_printf(MSG_DEBUG, "SSL: Building ACK (type=%d id=%d ver=%d) \n",
+ (int) eap_type, id, peap_version);
+ wpabuf_put_u8(resp, peap_version); /* Flags */
+ return resp;
+}
+
+
+/**
+ * eap_peer_tls_reauth_init - Re-initialize shared TLS for session resumption
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @data: Data for TLS processing
+ * Returns: 0 on success, -1 on failure
+ */
+int eap_peer_tls_reauth_init(struct eap_sm *sm, struct eap_ssl_data *data)
+{
+ eap_peer_tls_reset_input(data);
+ eap_peer_tls_reset_output(data);
+ return tls_connection_shutdown(data->ssl_ctx, data->conn);
+}
+
+
+/**
+ * eap_peer_tls_status - Get TLS status
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @data: Data for TLS processing
+ * @buf: Buffer for status information
+ * @buflen: Maximum buffer length
+ * @verbose: Whether to include verbose status information
+ * Returns: Number of bytes written to buf.
+ */
+int eap_peer_tls_status(struct eap_sm *sm, struct eap_ssl_data *data,
+ char *buf, size_t buflen, int verbose)
+{
+ char name[128];
+ int len = 0, ret;
+
+ if (tls_get_cipher(data->ssl_ctx, data->conn, name, sizeof(name)) == 0)
+ {
+ //ret = os_snprintf(buf + len, buflen - len,
+ ret = sprintf(buf + len,
+ "EAP TLS cipher=%s\n", name);
+ if (ret < 0 || (size_t) ret >= buflen - len)
+ return len;
+ len += ret;
+ }
+
+ return len;
+}
+
+
+/**
+ * eap_peer_tls_process_init - Initial validation/processing of EAP requests
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @data: Data for TLS processing
+ * @eap_type: EAP type (EAP_TYPE_TLS, EAP_TYPE_PEAP, ...)
+ * @ret: Return values from EAP request validation and processing
+ * @reqData: EAP request to be processed (eapReqData)
+ * @len: Buffer for returning length of the remaining payload
+ * @flags: Buffer for returning TLS flags
+ * Returns: Pointer to payload after TLS flags and length or %NULL on failure
+ *
+ * This function validates the EAP header and processes the optional TLS
+ * Message Length field. If this is the first fragment of a TLS message, the
+ * TLS reassembly code is initialized to receive the indicated number of bytes.
+ *
+ * EAP-TLS, EAP-PEAP, EAP-TTLS, and EAP-FAST methods are expected to use this
+ * function as the first step in processing received messages. They will need
+ * to process the flags (apart from Message Length Included) that are returned
+ * through the flags pointer and the message payload that will be returned (and
+ * the length is returned through the len pointer). Return values (ret) are set
+ * for continuation of EAP method processing. The caller is responsible for
+ * setting these to indicate completion (either success or failure) based on
+ * the authentication result.
+ */
+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)
+{
+ const u8 *pos;
+ size_t left;
+ unsigned int tls_msg_len;
+
+ if (tls_get_errors(data->ssl_ctx)) {
+ wpa_printf(MSG_INFO, "SSL: TLS errors detected");
+ ret->ignore = TRUE;
+ return NULL;
+ }
+
+ if (eap_type == EAP_UNAUTH_TLS_TYPE)
+ pos = eap_hdr_validate(EAP_VENDOR_UNAUTH_TLS,
+ EAP_VENDOR_TYPE_UNAUTH_TLS, reqData,
+ &left);
+ else
+ pos = eap_hdr_validate(EAP_VENDOR_IETF, eap_type, reqData,
+ &left);
+ if (pos == NULL) {
+ ret->ignore = TRUE;
+ return NULL;
+ }
+ if (left == 0) {
+ wpa_printf(MSG_DEBUG, "SSL: Invalid TLS message: no Flags "
+ "octet included");
+ if (!sm->workaround) {
+ ret->ignore = TRUE;
+ return NULL;
+ }
+
+ wpa_printf(MSG_DEBUG, "SSL: Workaround - assume no Flags "
+ "indicates ACK frame");
+ *flags = 0;
+ } else {
+ *flags = *pos++;
+ left--;
+ }
+ wpa_printf(MSG_DEBUG, "SSL: Received packet(len=%lu) - "
+ "Flags 0x%02x", (unsigned long) wpabuf_len(reqData),
+ *flags);
+ if (*flags & EAP_TLS_FLAGS_LENGTH_INCLUDED) {
+ if (left < 4) {
+ wpa_printf(MSG_INFO, "SSL: Short frame with TLS "
+ "length");
+ ret->ignore = TRUE;
+ return NULL;
+ }
+ tls_msg_len = WPA_GET_BE32(pos);
+ wpa_printf(MSG_DEBUG, "SSL: TLS Message Length: %d",
+ tls_msg_len);
+ if (data->tls_in_left == 0) {
+ data->tls_in_total = tls_msg_len;
+ data->tls_in_left = tls_msg_len;
+ wpabuf_free(data->tls_in);
+ data->tls_in = NULL;
+ }
+ pos += 4;
+ left -= 4;
+
+ if (left > tls_msg_len) {
+ wpa_printf(MSG_INFO, "SSL: TLS Message Length (%d "
+ "bytes) smaller than this fragment (%d "
+ "bytes)", (int) tls_msg_len, (int) left);
+ ret->ignore = TRUE;
+ return NULL;
+ }
+ }
+
+ ret->ignore = FALSE;
+ ret->methodState = METHOD_MAY_CONT;
+ ret->decision = DECISION_FAIL;
+ ret->allowNotifications = TRUE;
+
+ *len = left;
+ return pos;
+}
+
+
+/**
+ * eap_peer_tls_reset_input - Reset input buffers
+ * @data: Data for TLS processing
+ *
+ * This function frees any allocated memory for input buffers and resets input
+ * state.
+ */
+void eap_peer_tls_reset_input(struct eap_ssl_data *data)
+{
+ data->tls_in_left = data->tls_in_total = 0;
+ wpabuf_free(data->tls_in);
+ data->tls_in = NULL;
+}
+
+
+/**
+ * eap_peer_tls_reset_output - Reset output buffers
+ * @data: Data for TLS processing
+ *
+ * This function frees any allocated memory for output buffers and resets
+ * output state.
+ */
+void eap_peer_tls_reset_output(struct eap_ssl_data *data)
+{
+ data->tls_out_pos = 0;
+ wpabuf_free(data->tls_out);
+ data->tls_out = NULL;
+}
+
+
+/**
+ * eap_peer_tls_decrypt - Decrypt received phase 2 TLS message
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @data: Data for TLS processing
+ * @in_data: Message received from the server
+ * @in_decrypted: Buffer for returning a pointer to the decrypted message
+ * Returns: 0 on success, 1 if more input data is needed, or -1 on failure
+ */
+int eap_peer_tls_decrypt(struct eap_sm *sm, struct eap_ssl_data *data,
+ const struct wpabuf *in_data,
+ struct wpabuf **in_decrypted)
+{
+ const struct wpabuf *msg;
+ int need_more_input;
+
+ msg = eap_peer_tls_data_reassemble(data, in_data, &need_more_input);
+ if (msg == NULL)
+ return need_more_input ? 1 : -1;
+
+ *in_decrypted = tls_connection_decrypt(data->ssl_ctx, data->conn, msg);
+ eap_peer_tls_reset_input(data);
+ if (*in_decrypted == NULL) {
+ wpa_printf(MSG_INFO, "SSL: Failed to decrypt Phase 2 data");
+ return -1;
+ }
+ return 0;
+}
+
+
+/**
+ * eap_peer_tls_encrypt - Encrypt phase 2 TLS message
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @data: Data for TLS processing
+ * @eap_type: EAP type (EAP_TYPE_TLS, EAP_TYPE_PEAP, ...)
+ * @peap_version: Version number for EAP-PEAP/TTLS
+ * @id: EAP identifier for the response
+ * @in_data: Plaintext phase 2 data to encrypt or %NULL to continue fragments
+ * @out_data: Buffer for returning a pointer to the encrypted response message
+ * Returns: 0 on success, -1 on failure
+ */
+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)
+{
+ if (in_data) {
+ eap_peer_tls_reset_output(data);
+ data->tls_out = tls_connection_encrypt(data->ssl_ctx,
+ data->conn, in_data);
+ if (data->tls_out == NULL) {
+ wpa_printf(MSG_INFO, "SSL: Failed to encrypt Phase 2 "
+ "data (in_len=%lu)",
+ (unsigned long) wpabuf_len(in_data));
+ eap_peer_tls_reset_output(data);
+ return -1;
+ }
+ }
+
+ return eap_tls_process_output(data, eap_type, peap_version, id, 0,
+ out_data);
+}
+
+/**
+ * eap_peer_select_phase2_methods - Select phase 2 EAP method
+ * @config: Pointer to the network configuration
+ * @prefix: 'phase2' configuration prefix, e.g., "auth="
+ * @types: Buffer for returning allocated list of allowed EAP methods
+ * @num_types: Buffer for returning number of allocated EAP methods
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function is used to parse EAP method list and select allowed methods
+ * for Phase2 authentication.
+ */
+int eap_peer_select_phase2_methods(struct eap_peer_config *config,
+ const char *prefix,
+ struct eap_method_type **types,
+ size_t *num_types)
+{
+ char *start, *pos, *buf;
+ struct eap_method_type *methods = NULL, *_methods;
+ u8 method;
+ size_t num_methods = 0, prefix_len;
+
+ if (config == NULL || config->phase2 == NULL)
+ goto get_defaults;
+
+ start = buf = os_strdup(config->phase2);
+ if (buf == NULL)
+ return -1;
+
+ prefix_len = os_strlen(prefix);
+
+ while (start && *start != '\0') {
+ int vendor;
+ pos = os_strstr(start, prefix);
+ if (pos == NULL)
+ break;
+ if (start != pos && *(pos - 1) != ' ') {
+ start = pos + prefix_len;
+ continue;
+ }
+
+ start = pos + prefix_len;
+ pos = (char *)os_strchr(start, ' ');
+ if (pos)
+ *pos++ = '\0';
+ method = eap_get_phase2_type(start, &vendor);
+ if (vendor == EAP_VENDOR_IETF && method == EAP_TYPE_NONE) {
+ wpa_printf(MSG_INFO, "TLS: Unsupported Phase2 EAP "
+ "method '%s'\n", start);
+ } else {
+ num_methods++;
+ _methods = (struct eap_method_type *)os_realloc(methods,
+ num_methods * sizeof(*methods));
+ if (_methods == NULL) {
+ os_free(methods);
+ os_free(buf);
+ return -1;
+ }
+ methods = _methods;
+ methods[num_methods - 1].vendor = vendor;
+ methods[num_methods - 1].method = method;
+ }
+
+ start = pos;
+ }
+
+ os_free(buf);
+
+get_defaults:
+ if (methods == NULL)
+ methods = eap_get_phase2_types(config, &num_methods);
+ if (methods == NULL) {
+ wpa_printf(MSG_ERROR, "TLS: No Phase EAP methods available\n");
+ return -1;
+ }
+ wpa_hexdump(MSG_DEBUG, "TLS: Phase2 EAP types",
+ (u8 *) methods,
+ num_methods * sizeof(struct eap_method_type));
+
+ *types = methods;
+ *num_types = num_methods;
+
+ return 0;
+}
+
+/**
+ * eap_peer_tls_phase2_nak - Generate EAP-Nak for Phase 2
+ * @types: Buffer for returning allocated list of allowed EAP methods
+ * @num_types: Buffer for returning number of allocated EAP methods
+ * @hdr: EAP-Request header (and the following EAP type octet)
+ * @resp: Buffer for returning the EAP-Nak message
+ * Returns: 0 on success, -1 on failure
+ */
+int eap_peer_tls_phase2_nak(struct eap_method_type *types, size_t num_types,
+ struct eap_hdr *hdr, struct wpabuf **resp)
+{
+ u8 *pos = (u8 *) (hdr + 1);
+ size_t i;
+
+ /* TODO: add support for expanded Nak */
+ wpa_printf(MSG_DEBUG, "TLS: Phase Request: Nak type=%d\n", *pos);
+ wpa_hexdump(MSG_DEBUG, "TLS: Allowed Phase2 EAP types",
+ (u8 *) types, num_types * sizeof(struct eap_method_type));
+ *resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_NAK, num_types,
+ EAP_CODE_RESPONSE, hdr->identifier);
+ if (*resp == NULL)
+ return -1;
+
+ for (i = 0; i < num_types; i++) {
+ if (types[i].vendor == EAP_VENDOR_IETF &&
+ types[i].method < 256)
+ wpabuf_put_u8(*resp, types[i].method);
+ }
+
+ eap_update_len(*resp);
+
+ return 0;
+}
--- /dev/null
+/*\r
+ * EAP peer method: EAP-TTLS (RFC 5281)\r
+ * Copyright (c) 2004-2011, Jouni Malinen <j@w1.fi>\r
+ *\r
+ * This software may be distributed under the terms of the BSD license.\r
+ * See README for more details.\r
+ */\r
+\r
+#ifdef EAP_TTLS\r
+\r
+#include "wpa/includes.h"\r
+\r
+#include "wpa/common.h"\r
+#include "crypto/ms_funcs.h"\r
+#include "crypto/sha1.h"\r
+#include "wpa2/tls/tls.h"\r
+//#include "eap_common/chap.h"\r
+#include "wpa2/eap_peer/eap.h"\r
+#include "wpa2/eap_peer/eap_ttls.h"\r
+#include "wpa2/eap_peer/mschapv2.h"\r
+//#include "wpa2/eap_peer/chap.h"\r
+#include "wpa2/eap_peer/eap_i.h"\r
+#include "wpa2/eap_peer/eap_tls_common.h"\r
+#include "wpa2/eap_peer/eap_config.h"\r
+#include "wpa2/eap_peer/eap_methods.h"\r
+\r
+\r
+#define EAP_TTLS_VERSION 0\r
+\r
+static void eap_ttls_deinit(struct eap_sm *sm, void *priv);\r
+\r
+struct eap_ttls_data {\r
+ struct eap_ssl_data ssl;\r
+\r
+ int ttls_version;\r
+\r
+ const struct eap_method *phase2_method;\r
+ void *phase2_priv;\r
+ int phase2_success;\r
+ int phase2_start;\r
+\r
+ enum phase2_types {\r
+ EAP_TTLS_PHASE2_EAP,\r
+ EAP_TTLS_PHASE2_MSCHAPV2,\r
+ EAP_TTLS_PHASE2_MSCHAP,\r
+ EAP_TTLS_PHASE2_PAP,\r
+ EAP_TTLS_PHASE2_CHAP\r
+ } phase2_type;\r
+ struct eap_method_type phase2_eap_type;\r
+ struct eap_method_type *phase2_eap_types;\r
+ size_t num_phase2_eap_types;\r
+\r
+ u8 auth_response[MSCHAPV2_AUTH_RESPONSE_LEN];\r
+ int auth_response_valid;\r
+ u8 master_key[MSCHAPV2_MASTER_KEY_LEN]; /* MSCHAPv2 master key */\r
+ u8 ident;\r
+ int resuming; /* starting a resumed session */\r
+ int reauth; /* reauthentication */\r
+ u8 *key_data;\r
+ u8 *session_id;\r
+ size_t id_len;\r
+\r
+ struct wpabuf *pending_phase2_req;\r
+\r
+#ifdef EAP_TNC\r
+ int ready_for_tnc;\r
+ int tnc_started;\r
+#endif /* EAP_TNC */\r
+};\r
+\r
+\r
+static void * eap_ttls_init(struct eap_sm *sm)\r
+{\r
+ struct eap_ttls_data *data;\r
+ struct eap_peer_config *config = eap_get_config(sm);\r
+ //char *selected;\r
+\r
+ data = (struct eap_ttls_data *)os_zalloc(sizeof(*data));\r
+ if (data == NULL)\r
+ return NULL;\r
+ data->ttls_version = EAP_TTLS_VERSION;\r
+ data->phase2_type = EAP_TTLS_PHASE2_MSCHAPV2;\r
+ \r
+/* \r
+ selected = "MSCHAPV2";\r
+ //TODO: Now only support EAP-TTLS/MSCHAPV2\r
+ if (config && config->phase2) {\r
+ if (os_strstr(config->phase2, "autheap=")) {\r
+ selected = "EAP";\r
+ data->phase2_type = EAP_TTLS_PHASE2_EAP;\r
+ } else if (os_strstr(config->phase2, "auth=MSCHAPV2")) {\r
+ selected = "MSCHAPV2";\r
+ data->phase2_type = EAP_TTLS_PHASE2_MSCHAPV2;\r
+ } else if (os_strstr(config->phase2, "auth=MSCHAP")) {\r
+ selected = "MSCHAP";\r
+ data->phase2_type = EAP_TTLS_PHASE2_MSCHAP;\r
+ } else if (os_strstr(config->phase2, "auth=PAP")) {\r
+ selected = "PAP";\r
+ data->phase2_type = EAP_TTLS_PHASE2_PAP;\r
+ } else if (os_strstr(config->phase2, "auth=CHAP")) {\r
+ selected = "CHAP";\r
+ data->phase2_type = EAP_TTLS_PHASE2_CHAP;\r
+ }\r
+ }\r
+ wpa_printf(MSG_DEBUG, "EAP-TTLS: Phase2 type: %s", selected);\r
+\r
+ if (data->phase2_type == EAP_TTLS_PHASE2_EAP) {\r
+ if (eap_peer_select_phase2_methods(config, "autheap=",\r
+ &data->phase2_eap_types,\r
+ &data->num_phase2_eap_types)\r
+ < 0) {\r
+ eap_ttls_deinit(sm, data);\r
+ return NULL;\r
+ }\r
+\r
+ data->phase2_eap_type.vendor = EAP_VENDOR_IETF;\r
+ data->phase2_eap_type.method = EAP_TYPE_NONE;\r
+ }\r
+*/\r
+\r
+ if (eap_peer_tls_ssl_init(sm, &data->ssl, config, EAP_TYPE_TTLS)) {\r
+ wpa_printf(MSG_ERROR, "EAP-TTLS: Failed to initialize SSL.\n");\r
+ eap_ttls_deinit(sm, data);\r
+ return NULL;\r
+ }\r
+\r
+ return data;\r
+}\r
+\r
+static void eap_ttls_phase2_eap_deinit(struct eap_sm *sm,\r
+ struct eap_ttls_data *data)\r
+{\r
+ if (data->phase2_priv && data->phase2_method) {\r
+ data->phase2_method->deinit(sm, data->phase2_priv);\r
+ data->phase2_method = NULL;\r
+ data->phase2_priv = NULL;\r
+ }\r
+}\r
+\r
+static void eap_ttls_deinit(struct eap_sm *sm, void *priv)\r
+{\r
+ struct eap_ttls_data *data = priv;\r
+ if (data == NULL)\r
+ return;\r
+ eap_ttls_phase2_eap_deinit(sm, data);\r
+ os_free(data->phase2_eap_types);\r
+ eap_peer_tls_ssl_deinit(sm, &data->ssl);\r
+ os_free(data->key_data);\r
+ os_free(data->session_id);\r
+ wpabuf_free(data->pending_phase2_req);\r
+ os_free(data);\r
+}\r
+\r
+static u8 * eap_ttls_avp_hdr(u8 *avphdr, u32 avp_code, u32 vendor_id,\r
+ int mandatory, size_t len)\r
+{\r
+ struct ttls_avp_vendor *avp;\r
+ u8 flags;\r
+ size_t hdrlen;\r
+\r
+ avp = (struct ttls_avp_vendor *) avphdr;\r
+ flags = mandatory ? AVP_FLAGS_MANDATORY : 0;\r
+ if (vendor_id) {\r
+ flags |= AVP_FLAGS_VENDOR;\r
+ hdrlen = sizeof(*avp);\r
+ avp->vendor_id = host_to_be32(vendor_id);\r
+ } else {\r
+ hdrlen = sizeof(struct ttls_avp);\r
+ }\r
+\r
+ avp->avp_code = host_to_be32(avp_code);\r
+ avp->avp_length = host_to_be32((flags << 24) | (u32) (hdrlen + len));\r
+\r
+ return avphdr + hdrlen;\r
+}\r
+\r
+\r
+static u8 * eap_ttls_avp_add(u8 *start, u8 *avphdr, u32 avp_code,\r
+ u32 vendor_id, int mandatory,\r
+ const u8 *data, size_t len)\r
+{\r
+ u8 *pos;\r
+ pos = eap_ttls_avp_hdr(avphdr, avp_code, vendor_id, mandatory, len);\r
+ os_memcpy(pos, data, len);\r
+ pos += len;\r
+ AVP_PAD(start, pos);\r
+ return pos;\r
+}\r
+\r
+#if 0\r
+static int eap_ttls_avp_encapsulate(struct wpabuf **resp, u32 avp_code,\r
+ int mandatory)\r
+{\r
+ struct wpabuf *msg;\r
+ u8 *avp, *pos;\r
+\r
+ msg = wpabuf_alloc(sizeof(struct ttls_avp) + wpabuf_len(*resp) + 4);\r
+ if (msg == NULL) {\r
+ wpabuf_free(*resp);\r
+ *resp = NULL;\r
+ return -1;\r
+ }\r
+\r
+ avp = wpabuf_mhead(msg);\r
+ pos = eap_ttls_avp_hdr(avp, avp_code, 0, mandatory, wpabuf_len(*resp));\r
+ os_memcpy(pos, wpabuf_head(*resp), wpabuf_len(*resp));\r
+ pos += wpabuf_len(*resp);\r
+ AVP_PAD(avp, pos);\r
+ wpabuf_free(*resp);\r
+ wpabuf_put(msg, pos - avp);\r
+ *resp = msg;\r
+ return 0;\r
+}\r
+#endif\r
+\r
+static int eap_ttls_v0_derive_key(struct eap_sm *sm,\r
+ struct eap_ttls_data *data)\r
+{\r
+ os_free(data->key_data);\r
+ data->key_data = eap_peer_tls_derive_key(sm, &data->ssl,\r
+ "ttls keying material",\r
+ EAP_TLS_KEY_LEN);\r
+ if (!data->key_data) {\r
+ wpa_printf(MSG_ERROR, "EAP-TTLS: Failed to derive key\n");\r
+ return -1;\r
+ }\r
+\r
+ os_free(data->session_id);\r
+ data->session_id = eap_peer_tls_derive_session_id(sm, &data->ssl,\r
+ EAP_TYPE_TTLS,\r
+ &data->id_len);\r
+ if (data->session_id) {\r
+ } else {\r
+ wpa_printf(MSG_ERROR, "EAP-TTLS: Failed to derive Session-Id\n");\r
+ }\r
+\r
+ return 0;\r
+}\r
+\r
+\r
+static u8 * eap_ttls_implicit_challenge(struct eap_sm *sm,\r
+ struct eap_ttls_data *data, size_t len)\r
+{\r
+ return eap_peer_tls_derive_key(sm, &data->ssl, "ttls challenge", len);\r
+}\r
+\r
+#if 0\r
+static void eap_ttls_phase2_select_eap_method(struct eap_ttls_data *data,\r
+ u8 method)\r
+{\r
+ size_t i;\r
+ for (i = 0; i < data->num_phase2_eap_types; i++) {\r
+ if (data->phase2_eap_types[i].vendor != EAP_VENDOR_IETF ||\r
+ data->phase2_eap_types[i].method != method)\r
+ continue;\r
+\r
+ data->phase2_eap_type.vendor =\r
+ data->phase2_eap_types[i].vendor;\r
+ data->phase2_eap_type.method =\r
+ data->phase2_eap_types[i].method;\r
+ wpa_printf(MSG_DEBUG, "EAP-TTLS: Selected "\r
+ "Phase 2 EAP vendor %d method %d\n",\r
+ data->phase2_eap_type.vendor,\r
+ data->phase2_eap_type.method);\r
+ break;\r
+ }\r
+}\r
+\r
+static int eap_ttls_phase2_eap_process(struct eap_sm *sm,\r
+ struct eap_ttls_data *data,\r
+ struct eap_method_ret *ret,\r
+ struct eap_hdr *hdr, size_t len,\r
+ struct wpabuf **resp)\r
+{\r
+ struct wpabuf msg;\r
+ struct eap_method_ret iret;\r
+\r
+ os_memset(&iret, 0, sizeof(iret));\r
+ wpabuf_set(&msg, hdr, len);\r
+ *resp = data->phase2_method->process(sm, data->phase2_priv, &iret,\r
+ &msg);\r
+ if ((iret.methodState == METHOD_DONE ||\r
+ iret.methodState == METHOD_MAY_CONT) &&\r
+ (iret.decision == DECISION_UNCOND_SUCC ||\r
+ iret.decision == DECISION_COND_SUCC ||\r
+ iret.decision == DECISION_FAIL)) {\r
+ ret->methodState = iret.methodState;\r
+ ret->decision = iret.decision;\r
+ }\r
+\r
+ return 0;\r
+}\r
+\r
+\r
+static int eap_ttls_phase2_request_eap_method(struct eap_sm *sm,\r
+ struct eap_ttls_data *data,\r
+ struct eap_method_ret *ret,\r
+ struct eap_hdr *hdr, size_t len,\r
+ u8 method, struct wpabuf **resp)\r
+{\r
+#ifdef EAP_TNC\r
+ if (data->tnc_started && data->phase2_method &&\r
+ data->phase2_priv && method == EAP_TYPE_TNC &&\r
+ data->phase2_eap_type.method == EAP_TYPE_TNC)\r
+ return eap_ttls_phase2_eap_process(sm, data, ret, hdr, len,\r
+ resp);\r
+\r
+ if (data->ready_for_tnc && !data->tnc_started &&\r
+ method == EAP_TYPE_TNC) {\r
+ wpa_printf(MSG_DEBUG, "EAP-TTLS: Start TNC after completed "\r
+ "EAP method\n");\r
+ data->tnc_started = 1;\r
+ }\r
+\r
+ if (data->tnc_started) {\r
+ if (data->phase2_eap_type.vendor != EAP_VENDOR_IETF ||\r
+ data->phase2_eap_type.method == EAP_TYPE_TNC) {\r
+ wpa_printf(MSG_ERROR, "EAP-TTLS: Unexpected EAP "\r
+ "type %d for TNC\n", method);\r
+ return -1;\r
+ }\r
+\r
+ data->phase2_eap_type.vendor = EAP_VENDOR_IETF;\r
+ data->phase2_eap_type.method = method;\r
+ wpa_printf(MSG_DEBUG, "EAP-TTLS: Selected "\r
+ "Phase 2 EAP vendor %d method %d (TNC)\n",\r
+ data->phase2_eap_type.vendor,\r
+ data->phase2_eap_type.method);\r
+\r
+ if (data->phase2_type == EAP_TTLS_PHASE2_EAP)\r
+ eap_ttls_phase2_eap_deinit(sm, data);\r
+ }\r
+#endif /* EAP_TNC */\r
+\r
+ if (data->phase2_eap_type.vendor == EAP_VENDOR_IETF &&\r
+ data->phase2_eap_type.method == EAP_TYPE_NONE)\r
+ eap_ttls_phase2_select_eap_method(data, method);\r
+\r
+ if (method != data->phase2_eap_type.method || method == EAP_TYPE_NONE)\r
+ {\r
+ return -1;\r
+ if (eap_peer_tls_phase2_nak(data->phase2_eap_types,\r
+ data->num_phase2_eap_types,\r
+ hdr, resp))\r
+ return -1;\r
+ return 0;\r
+\r
+ }\r
+\r
+ if (data->phase2_priv == NULL) {\r
+ data->phase2_method = eap_peer_get_eap_method(\r
+ EAP_VENDOR_IETF, method);\r
+ if (data->phase2_method) {\r
+ sm->init_phase2 = 1;\r
+ data->phase2_priv = data->phase2_method->init(sm);\r
+ sm->init_phase2 = 0;\r
+ }\r
+ }\r
+ if (data->phase2_priv == NULL || data->phase2_method == NULL) {\r
+ wpa_printf(MSG_ERROR, "EAP-TTLS: failed to initialize "\r
+ "Phase 2 EAP method %d\n", method);\r
+ return -1;\r
+ }\r
+\r
+ return eap_ttls_phase2_eap_process(sm, data, ret, hdr, len, resp);\r
+}\r
+\r
+#if 0\r
+static int eap_ttls_phase2_request_eap(struct eap_sm *sm,\r
+ struct eap_ttls_data *data,\r
+ struct eap_method_ret *ret,\r
+ struct eap_hdr *hdr,\r
+ struct wpabuf **resp)\r
+{\r
+ size_t len = be_to_host16(hdr->length);\r
+ u8 *pos;\r
+ struct eap_peer_config *config = eap_get_config(sm);\r
+\r
+ if (len <= sizeof(struct eap_hdr)) {\r
+ wpa_printf(MSG_ERROR, "EAP-TTLS: too short "\r
+ "Phase 2 request (len=%lu)\n", (unsigned long) len);\r
+ return -1;\r
+ }\r
+ pos = (u8 *) (hdr + 1);\r
+ wpa_printf(MSG_DEBUG, "EAP-TTLS: Phase 2 EAP Request: type=%d\n", *pos);\r
+ switch (*pos) {\r
+ case EAP_TYPE_IDENTITY:\r
+ *resp = eap_sm_build_identity_resp(sm, hdr->identifier, 1);\r
+ break;\r
+ default:\r
+ if (eap_ttls_phase2_request_eap_method(sm, data, ret, hdr, len,\r
+ *pos, resp) < 0)\r
+ return -1;\r
+ break;\r
+ }\r
+\r
+ if (*resp == NULL &&\r
+ (config->pending_req_identity || config->pending_req_password ||\r
+ config->pending_req_otp)) {\r
+ return 0;\r
+ }\r
+\r
+ if (*resp == NULL)\r
+ return -1;\r
+\r
+ return eap_ttls_avp_encapsulate(resp, RADIUS_ATTR_EAP_MESSAGE, 1);\r
+}\r
+#endif\r
+#endif\r
+\r
+static int eap_ttls_phase2_request_mschapv2(struct eap_sm *sm,\r
+ struct eap_ttls_data *data,\r
+ struct eap_method_ret *ret,\r
+ struct wpabuf **resp)\r
+{\r
+#ifdef EAP_MSCHAPv2\r
+ struct wpabuf *msg;\r
+ u8 *buf, *pos, *challenge, *peer_challenge;\r
+ const u8 *identity, *password;\r
+ size_t identity_len, password_len;\r
+ int pwhash;\r
+\r
+ wpa_printf(MSG_DEBUG, "EAP-TTLS: Phase 2 MSCHAPV2 Request\n");\r
+\r
+ identity = eap_get_config_identity(sm, &identity_len);\r
+ password = eap_get_config_password2(sm, &password_len, &pwhash);\r
+ if (identity == NULL || password == NULL)\r
+ return -1;\r
+\r
+ msg = wpabuf_alloc(identity_len + 1000);\r
+ if (msg == NULL) {\r
+ wpa_printf(MSG_ERROR, "EAP-TTLS/MSCHAPV2: Failed to allocate memory\n");\r
+ return -1;\r
+ }\r
+ pos = buf = wpabuf_mhead(msg);\r
+\r
+ /* User-Name */\r
+ pos = eap_ttls_avp_add(buf, pos, RADIUS_ATTR_USER_NAME, 0, 1,\r
+ identity, identity_len);\r
+\r
+ /* MS-CHAP-Challenge */\r
+ challenge = eap_ttls_implicit_challenge(\r
+ sm, data, EAP_TTLS_MSCHAPV2_CHALLENGE_LEN + 1);\r
+ if (challenge == NULL) {\r
+ wpabuf_free(msg);\r
+ wpa_printf(MSG_ERROR, "EAP-TTLS/MSCHAPV2: Failed to derive "\r
+ "implicit challenge\n");\r
+ return -1;\r
+ }\r
+\r
+ pos = eap_ttls_avp_add(buf, pos, RADIUS_ATTR_MS_CHAP_CHALLENGE,\r
+ RADIUS_VENDOR_ID_MICROSOFT, 1,\r
+ challenge, EAP_TTLS_MSCHAPV2_CHALLENGE_LEN);\r
+\r
+ /* MS-CHAP2-Response */\r
+ pos = eap_ttls_avp_hdr(pos, RADIUS_ATTR_MS_CHAP2_RESPONSE,\r
+ RADIUS_VENDOR_ID_MICROSOFT, 1,\r
+ EAP_TTLS_MSCHAPV2_RESPONSE_LEN);\r
+ data->ident = challenge[EAP_TTLS_MSCHAPV2_CHALLENGE_LEN];\r
+ *pos++ = data->ident;\r
+ *pos++ = 0; /* Flags */\r
+ if (os_get_random(pos, EAP_TTLS_MSCHAPV2_CHALLENGE_LEN) < 0) {\r
+ os_free(challenge);\r
+ wpabuf_free(msg);\r
+ wpa_printf(MSG_ERROR, "EAP-TTLS/MSCHAPV2: Failed to get "\r
+ "random data for peer challenge\n");\r
+ return -1;\r
+ }\r
+ peer_challenge = pos;\r
+ pos += EAP_TTLS_MSCHAPV2_CHALLENGE_LEN;\r
+ os_memset(pos, 0, 8); /* Reserved, must be zero */\r
+ pos += 8;\r
+ if (mschapv2_derive_response(identity, identity_len, password,\r
+ password_len, pwhash, challenge,\r
+ peer_challenge, pos, data->auth_response,\r
+ data->master_key)) {\r
+ os_free(challenge);\r
+ wpabuf_free(msg);\r
+ wpa_printf(MSG_ERROR, "EAP-TTLS/MSCHAPV2: Failed to derive "\r
+ "response\n");\r
+ return -1;\r
+ }\r
+ data->auth_response_valid = 1;\r
+\r
+ pos += 24;\r
+ os_free(challenge);\r
+ AVP_PAD(buf, pos);\r
+\r
+ wpabuf_put(msg, pos - buf);\r
+ *resp = msg;\r
+\r
+ if (sm->workaround) {\r
+ /* At least FreeRADIUS seems to be terminating\r
+ * EAP-TTLS/MSHCAPV2 without the expected MS-CHAP-v2 Success\r
+ * packet. */\r
+ wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAPV2: EAP workaround - "\r
+ "allow success without tunneled response\n");\r
+ ret->methodState = METHOD_MAY_CONT;\r
+ ret->decision = DECISION_COND_SUCC;\r
+ }\r
+\r
+ return 0;\r
+#else /* EAP_MSCHAPv2 */\r
+ printf("[Debug] Set EEEEE \n");\r
+ wpa_printf(MSG_ERROR, "EAP-TTLS: MSCHAPv2 not included in the build\n");\r
+ return -1;\r
+#endif /* EAP_MSCHAPv2 */\r
+}\r
+\r
+#if 0\r
+//only support MSCHAPv2\r
+static int eap_ttls_phase2_request_mschap(struct eap_sm *sm,\r
+ struct eap_ttls_data *data,\r
+ struct eap_method_ret *ret,\r
+ struct wpabuf **resp)\r
+{\r
+ struct wpabuf *msg;\r
+ u8 *buf, *pos, *challenge;\r
+ const u8 *identity, *password;\r
+ size_t identity_len, password_len;\r
+ int pwhash;\r
+\r
+ wpa_printf(MSG_DEBUG, "EAP-TTLS: Phase 2 MSCHAP Request\n");\r
+\r
+ identity = eap_get_config_identity(sm, &identity_len);\r
+ password = eap_get_config_password2(sm, &password_len, &pwhash);\r
+ if (identity == NULL || password == NULL)\r
+ return -1;\r
+\r
+ msg = wpabuf_alloc(identity_len + 1000);\r
+ if (msg == NULL) {\r
+ wpa_printf(MSG_ERROR, "EAP-TTLS/MSCHAP: Failed to allocate memory\n");\r
+ return -1;\r
+ }\r
+ pos = buf = wpabuf_mhead(msg);\r
+\r
+ // User-Name\r
+ pos = eap_ttls_avp_add(buf, pos, RADIUS_ATTR_USER_NAME, 0, 1,\r
+ identity, identity_len);\r
+\r
+ // MS-CHAP-Challenge\r
+ challenge = eap_ttls_implicit_challenge(\r
+ sm, data, EAP_TTLS_MSCHAP_CHALLENGE_LEN + 1);\r
+ if (challenge == NULL) {\r
+ wpabuf_free(msg);\r
+ wpa_printf(MSG_ERROR, "EAP-TTLS/MSCHAP: Failed to derive "\r
+ "implicit challenge\n");\r
+ return -1;\r
+ }\r
+\r
+ pos = eap_ttls_avp_add(buf, pos, RADIUS_ATTR_MS_CHAP_CHALLENGE,\r
+ RADIUS_VENDOR_ID_MICROSOFT, 1,\r
+ challenge, EAP_TTLS_MSCHAP_CHALLENGE_LEN);\r
+\r
+ // MS-CHAP-Response\r
+ pos = eap_ttls_avp_hdr(pos, RADIUS_ATTR_MS_CHAP_RESPONSE,\r
+ RADIUS_VENDOR_ID_MICROSOFT, 1,\r
+ EAP_TTLS_MSCHAP_RESPONSE_LEN);\r
+ data->ident = challenge[EAP_TTLS_MSCHAP_CHALLENGE_LEN];\r
+ *pos++ = data->ident;\r
+ *pos++ = 1; // Flags: Use NT style passwords\r
+ os_memset(pos, 0, 24); // LM-Response\r
+ pos += 24;\r
+ if (pwhash) {\r
+ challenge_response(challenge, password, pos); // NT-Response\r
+ } else {\r
+ nt_challenge_response(challenge, password, password_len,\r
+ pos); // NT-Response\r
+ }\r
+ pos += 24;\r
+ os_free(challenge);\r
+ AVP_PAD(buf, pos);\r
+\r
+ wpabuf_put(msg, pos - buf);\r
+ *resp = msg;\r
+\r
+ // EAP-TTLS/MSCHAP does not provide tunneled success\r
+ // notification, so assume that Phase2 succeeds.\r
+ ret->methodState = METHOD_DONE;\r
+ ret->decision = DECISION_COND_SUCC;\r
+\r
+ return 0;\r
+}\r
+\r
+\r
+static int eap_ttls_phase2_request_pap(struct eap_sm *sm,\r
+ struct eap_ttls_data *data,\r
+ struct eap_method_ret *ret,\r
+ struct wpabuf **resp)\r
+{\r
+ struct wpabuf *msg;\r
+ u8 *buf, *pos;\r
+ size_t pad;\r
+ const u8 *identity, *password;\r
+ size_t identity_len, password_len;\r
+\r
+ wpa_printf(MSG_DEBUG, "EAP-TTLS: Phase 2 PAP Request\n");\r
+\r
+ identity = eap_get_config_identity(sm, &identity_len);\r
+ password = eap_get_config_password(sm, &password_len);\r
+ if (identity == NULL || password == NULL)\r
+ return -1;\r
+\r
+ msg = wpabuf_alloc(identity_len + password_len + 100);\r
+ if (msg == NULL) {\r
+ wpa_printf(MSG_ERROR, "EAP-TTLS/PAP: Failed to allocate memory\n");\r
+ return -1;\r
+ }\r
+ pos = buf = wpabuf_mhead(msg);\r
+\r
+ // User-Name\r
+ pos = eap_ttls_avp_add(buf, pos, RADIUS_ATTR_USER_NAME, 0, 1,\r
+ identity, identity_len);\r
+\r
+ // User-Password; in RADIUS, this is encrypted, but EAP-TTLS encrypts\r
+ // the data, so no separate encryption is used in the AVP itself.\r
+ // However, the password is padded to obfuscate its length.\r
+ pad = password_len == 0 ? 16 : (16 - (password_len & 15)) & 15;\r
+ pos = eap_ttls_avp_hdr(pos, RADIUS_ATTR_USER_PASSWORD, 0, 1,\r
+ password_len + pad);\r
+ os_memcpy(pos, password, password_len);\r
+ pos += password_len;\r
+ os_memset(pos, 0, pad);\r
+ pos += pad;\r
+ AVP_PAD(buf, pos);\r
+\r
+ wpabuf_put(msg, pos - buf);\r
+ *resp = msg;\r
+\r
+ // EAP-TTLS/PAP does not provide tunneled success notification,\r
+ // so assume that Phase2 succeeds.\r
+ ret->methodState = METHOD_DONE;\r
+ ret->decision = DECISION_COND_SUCC;\r
+\r
+ return 0;\r
+}\r
+\r
+\r
+static int eap_ttls_phase2_request_chap(struct eap_sm *sm,\r
+ struct eap_ttls_data *data,\r
+ struct eap_method_ret *ret,\r
+ struct wpabuf **resp)\r
+{\r
+ struct wpabuf *msg;\r
+ u8 *buf, *pos, *challenge;\r
+ const u8 *identity, *password;\r
+ size_t identity_len, password_len;\r
+\r
+ wpa_printf(MSG_DEBUG, "EAP-TTLS: Phase 2 CHAP Request\n");\r
+\r
+ identity = eap_get_config_identity(sm, &identity_len);\r
+ password = eap_get_config_password(sm, &password_len);\r
+ if (identity == NULL || password == NULL)\r
+ return -1;\r
+\r
+ msg = wpabuf_alloc(identity_len + 1000);\r
+ if (msg == NULL) {\r
+ wpa_printf(MSG_ERROR, "EAP-TTLS/CHAP: Failed to allocate memory\n");\r
+ return -1;\r
+ }\r
+ pos = buf = wpabuf_mhead(msg);\r
+\r
+ // User-Name\r
+ pos = eap_ttls_avp_add(buf, pos, RADIUS_ATTR_USER_NAME, 0, 1,\r
+ identity, identity_len);\r
+\r
+ // CHAP-Challenge\r
+ challenge = eap_ttls_implicit_challenge(\r
+ sm, data, EAP_TTLS_CHAP_CHALLENGE_LEN + 1);\r
+ if (challenge == NULL) {\r
+ wpabuf_free(msg);\r
+ wpa_printf(MSG_ERROR, "EAP-TTLS/CHAP: Failed to derive "\r
+ "implicit challenge\n");\r
+ return -1;\r
+ }\r
+\r
+ pos = eap_ttls_avp_add(buf, pos, RADIUS_ATTR_CHAP_CHALLENGE, 0, 1,\r
+ challenge, EAP_TTLS_CHAP_CHALLENGE_LEN);\r
+\r
+ // CHAP-Password\r
+ pos = eap_ttls_avp_hdr(pos, RADIUS_ATTR_CHAP_PASSWORD, 0, 1,\r
+ 1 + EAP_TTLS_CHAP_PASSWORD_LEN);\r
+ data->ident = challenge[EAP_TTLS_CHAP_CHALLENGE_LEN];\r
+ *pos++ = data->ident;\r
+\r
+ // MD5(Ident + Password + Challenge)\r
+ chap_md5(data->ident, password, password_len, challenge,\r
+ EAP_TTLS_CHAP_CHALLENGE_LEN, pos);\r
+\r
+ pos += EAP_TTLS_CHAP_PASSWORD_LEN;\r
+ os_free(challenge);\r
+ AVP_PAD(buf, pos);\r
+\r
+ wpabuf_put(msg, pos - buf);\r
+ *resp = msg;\r
+\r
+ // EAP-TTLS/CHAP does not provide tunneled success\r
+ // notification, so assume that Phase2 succeeds.\r
+ ret->methodState = METHOD_DONE;\r
+ ret->decision = DECISION_COND_SUCC;\r
+\r
+ return 0;\r
+}\r
+#endif\r
+\r
+static int eap_ttls_phase2_request(struct eap_sm *sm,\r
+ struct eap_ttls_data *data,\r
+ struct eap_method_ret *ret,\r
+ struct eap_hdr *hdr,\r
+ struct wpabuf **resp)\r
+{\r
+ int res = 0;\r
+ size_t len;\r
+ enum phase2_types phase2_type = data->phase2_type;\r
+\r
+#ifdef EAP_TNC\r
+ if (data->tnc_started) {\r
+ printf("[debug] set phase2_type \n");\r
+ wpa_printf(MSG_DEBUG, "EAP-TTLS: Processing TNC\n");\r
+ phase2_type = EAP_TTLS_PHASE2_EAP;\r
+ }\r
+#endif /* EAP_TNC */\r
+\r
+ if (phase2_type == EAP_TTLS_PHASE2_MSCHAPV2) {\r
+ if (eap_get_config_identity(sm, &len) == NULL) {\r
+ wpa_printf(MSG_ERROR, "EAP-TTLS: Identity not configured\n");\r
+ printf("[Debug] Return because no identity EAP_TTLS_PHASE2_MSCHAPV2\n");\r
+ return 0;\r
+ }\r
+ if (eap_get_config_password(sm, &len) == NULL) {\r
+ wpa_printf(MSG_ERROR, "EAP-TTLS: Password not configured\n");\r
+ printf("[Debug] Return because no password EAP_TTLS_PHASE2_MSCHAPV2\n");\r
+ return 0;\r
+ }\r
+\r
+ res = eap_ttls_phase2_request_mschapv2(sm, data, ret, resp);\r
+ } else {\r
+ wpa_printf(MSG_ERROR, "EAP-TTLS: Phase 2 - Unknown type %d\n", phase2_type);\r
+ res = -1;\r
+ }\r
+\r
+/* if (phase2_type == EAP_TTLS_PHASE2_MSCHAPV2 ||\r
+ phase2_type == EAP_TTLS_PHASE2_MSCHAP ||\r
+ phase2_type == EAP_TTLS_PHASE2_PAP ||\r
+ phase2_type == EAP_TTLS_PHASE2_CHAP) {\r
+ if (eap_get_config_identity(sm, &len) == NULL) {\r
+ wpa_printf(MSG_ERROR, "EAP-TTLS: Identity not configured\n");\r
+ //eap_sm_request_identity(sm);\r
+ if (eap_get_config_password(sm, &len) == NULL);\r
+ // eap_sm_request_password(sm);\r
+ printf("[Debug] Return because no identity EAP_TTLS_PHASE2_MSCHAPV2 EAP_TTLS_PHASE2_MSCHAP\n");\r
+ return 0;\r
+ }\r
+\r
+ if (eap_get_config_password(sm, &len) == NULL) {\r
+ wpa_printf(MSG_ERROR, "EAP-TTLS: Password not configured\n"); \r
+ printf("[Debug] Return because no password EAP_TTLS_PHASE2_MSCHAPV2 EAP_TTLS_PHASE2_MSCHAP\n");\r
+ //eap_sm_request_password(sm);\r
+ return 0;\r
+ }\r
+ }\r
+\r
+ switch (phase2_type) {\r
+ case EAP_TTLS_PHASE2_EAP:\r
+ printf("[Debug] EAP_TTLS_PHASE2_EAP typed \n");\r
+ res = eap_ttls_phase2_request_eap(sm, data, ret, hdr, resp);\r
+ break;\r
+ case EAP_TTLS_PHASE2_MSCHAPV2:\r
+ printf("[Debug] EAP_TTLS_PHASE2_MSCHAPV2 typed \n");\r
+ res = eap_ttls_phase2_request_mschapv2(sm, data, ret, resp);\r
+ break;\r
+ case EAP_TTLS_PHASE2_MSCHAP:\r
+ printf("[Debug] EAP_TTLS_PHASE2_MSCHAP typed \n");\r
+ res = eap_ttls_phase2_request_mschap(sm, data, ret, resp);\r
+ break;\r
+ case EAP_TTLS_PHASE2_PAP:\r
+ printf("[Debug] EAP_TTLS_PHASE2_PAP typed \n");\r
+ res = eap_ttls_phase2_request_pap(sm, data, ret, resp);\r
+ break;\r
+ case EAP_TTLS_PHASE2_CHAP:\r
+ printf("[Debug] EAP_TTLS_PHASE2_CHAP typed \n");\r
+ res = eap_ttls_phase2_request_chap(sm, data, ret, resp);\r
+ break;\r
+ default:\r
+ printf("[Debug] Default typed \n");\r
+ wpa_printf(MSG_ERROR, "EAP-TTLS: Phase 2 - Unknown\n");\r
+ res = -1;\r
+ break;\r
+ }*/\r
+\r
+ if (res < 0) {\r
+ ret->methodState = METHOD_DONE;\r
+ ret->decision = DECISION_FAIL;\r
+ }\r
+\r
+ return res;\r
+}\r
+\r
+\r
+struct ttls_parse_avp {\r
+ u8 *mschapv2;\r
+ u8 *eapdata;\r
+ size_t eap_len;\r
+ int mschapv2_error;\r
+};\r
+\r
+\r
+static int eap_ttls_parse_attr_eap(const u8 *dpos, size_t dlen,\r
+ struct ttls_parse_avp *parse)\r
+{\r
+ wpa_printf(MSG_DEBUG, "EAP-TTLS: AVP - EAP Message\n");\r
+ if (parse->eapdata == NULL) {\r
+ parse->eapdata = os_malloc(dlen);\r
+ if (parse->eapdata == NULL) {\r
+ wpa_printf(MSG_ERROR, "EAP-TTLS: Failed to allocate "\r
+ "memory for Phase 2 EAP data\n");\r
+ return -1;\r
+ }\r
+ os_memcpy(parse->eapdata, dpos, dlen);\r
+ parse->eap_len = dlen;\r
+ } else {\r
+ u8 *neweap = (u8 *)os_realloc(parse->eapdata, parse->eap_len + dlen);\r
+ if (neweap == NULL) {\r
+ wpa_printf(MSG_ERROR, "EAP-TTLS: Failed to allocate "\r
+ "memory for Phase 2 EAP data\n");\r
+ return -1;\r
+ }\r
+ os_memcpy(neweap + parse->eap_len, dpos, dlen);\r
+ parse->eapdata = neweap;\r
+ parse->eap_len += dlen;\r
+ }\r
+\r
+ return 0;\r
+}\r
+\r
+\r
+static int eap_ttls_parse_avp(u8 *pos, size_t left,\r
+ struct ttls_parse_avp *parse)\r
+{\r
+ struct ttls_avp *avp;\r
+ u32 avp_code, avp_length, vendor_id = 0;\r
+ u8 avp_flags, *dpos;\r
+ size_t dlen;\r
+\r
+ avp = (struct ttls_avp *) pos;\r
+ avp_code = be_to_host32(avp->avp_code);\r
+ avp_length = be_to_host32(avp->avp_length);\r
+ avp_flags = (avp_length >> 24) & 0xff;\r
+ avp_length &= 0xffffff;\r
+ wpa_printf(MSG_DEBUG, "EAP-TTLS: AVP: code=%d flags=0x%02x "\r
+ "length=%d\n", (int) avp_code, avp_flags,\r
+ (int) avp_length);\r
+\r
+ if (avp_length > left) {\r
+ wpa_printf(MSG_ERROR, "EAP-TTLS: AVP overflow "\r
+ "(len=%d, left=%lu) - dropped\n",\r
+ (int) avp_length, (unsigned long) left);\r
+ return -1;\r
+ }\r
+\r
+ if (avp_length < sizeof(*avp)) {\r
+ wpa_printf(MSG_ERROR, "EAP-TTLS: Invalid AVP length %d\n",\r
+ avp_length);\r
+ return -1;\r
+ }\r
+\r
+ dpos = (u8 *) (avp + 1);\r
+ dlen = avp_length - sizeof(*avp);\r
+ if (avp_flags & AVP_FLAGS_VENDOR) {\r
+ if (dlen < 4) {\r
+ wpa_printf(MSG_ERROR, "EAP-TTLS: Vendor AVP underflow\n");\r
+ return -1;\r
+ }\r
+ vendor_id = WPA_GET_BE32(dpos);\r
+ wpa_printf(MSG_DEBUG, "EAP-TTLS: AVP vendor_id %d\n",\r
+ (int) vendor_id);\r
+ dpos += 4;\r
+ dlen -= 4;\r
+ }\r
+\r
+ if (vendor_id == 0 && avp_code == RADIUS_ATTR_EAP_MESSAGE) {\r
+ if (eap_ttls_parse_attr_eap(dpos, dlen, parse) < 0)\r
+ return -1;\r
+ } else if (vendor_id == 0 && avp_code == RADIUS_ATTR_REPLY_MESSAGE) {\r
+ /* This is an optional message that can be displayed to\r
+ * the user. */\r
+ } else if (vendor_id == RADIUS_VENDOR_ID_MICROSOFT &&\r
+ avp_code == RADIUS_ATTR_MS_CHAP2_SUCCESS) {\r
+ if (dlen != 43) {\r
+ wpa_printf(MSG_ERROR, "EAP-TTLS: Unexpected "\r
+ "MS-CHAP2-Success length "\r
+ "(len=%lu, expected 43)\n",\r
+ (unsigned long) dlen);\r
+ return -1;\r
+ }\r
+ parse->mschapv2 = dpos;\r
+ } else if (vendor_id == RADIUS_VENDOR_ID_MICROSOFT &&\r
+ avp_code == RADIUS_ATTR_MS_CHAP_ERROR) {\r
+ parse->mschapv2_error = 1;\r
+ } else if (avp_flags & AVP_FLAGS_MANDATORY) {\r
+ wpa_printf(MSG_ERROR, "EAP-TTLS: Unsupported mandatory AVP "\r
+ "code %d vendor_id %d - dropped\n",\r
+ (int) avp_code, (int) vendor_id);\r
+ return -1;\r
+ } else {\r
+ wpa_printf(MSG_INFO, "EAP-TTLS: Ignoring unsupported AVP "\r
+ "code %d vendor_id %d\n",\r
+ (int) avp_code, (int) vendor_id);\r
+ }\r
+\r
+ return avp_length;\r
+}\r
+\r
+\r
+static int eap_ttls_parse_avps(struct wpabuf *in_decrypted,\r
+ struct ttls_parse_avp *parse)\r
+{\r
+ u8 *pos;\r
+ size_t left, pad;\r
+ int avp_length;\r
+\r
+ pos = wpabuf_mhead(in_decrypted);\r
+ left = wpabuf_len(in_decrypted);\r
+ if (left < sizeof(struct ttls_avp)) {\r
+ wpa_printf(MSG_ERROR, "EAP-TTLS: Too short Phase 2 AVP frame"\r
+ " len=%lu expected %lu or more - dropped\n",\r
+ (unsigned long) left,\r
+ (unsigned long) sizeof(struct ttls_avp));\r
+ return -1;\r
+ }\r
+\r
+ /* Parse AVPs */\r
+ os_memset(parse, 0, sizeof(*parse));\r
+\r
+ while (left > 0) {\r
+ avp_length = eap_ttls_parse_avp(pos, left, parse);\r
+ if (avp_length < 0)\r
+ return -1;\r
+\r
+ pad = (4 - (avp_length & 3)) & 3;\r
+ pos += avp_length + pad;\r
+ if (left < avp_length + pad)\r
+ left = 0;\r
+ else\r
+ left -= avp_length + pad;\r
+ }\r
+\r
+ return 0;\r
+}\r
+\r
+\r
+static u8 * eap_ttls_fake_identity_request(void)\r
+{\r
+ struct eap_hdr *hdr;\r
+ u8 *buf;\r
+\r
+ wpa_printf(MSG_DEBUG, "EAP-TTLS: empty data in beginning of "\r
+ "Phase 2 - use fake EAP-Request Identity\n");\r
+ buf = os_malloc(sizeof(*hdr) + 1);\r
+ if (buf == NULL) {\r
+ wpa_printf(MSG_ERROR, "EAP-TTLS: failed to allocate "\r
+ "memory for fake EAP-Identity Request\n");\r
+ return NULL;\r
+ }\r
+\r
+ hdr = (struct eap_hdr *) buf;\r
+ hdr->code = EAP_CODE_REQUEST;\r
+ hdr->identifier = 0;\r
+ hdr->length = host_to_be16(sizeof(*hdr) + 1);\r
+ buf[sizeof(*hdr)] = EAP_TYPE_IDENTITY;\r
+\r
+ return buf;\r
+}\r
+\r
+\r
+static int eap_ttls_encrypt_response(struct eap_sm *sm,\r
+ struct eap_ttls_data *data,\r
+ struct wpabuf *resp, u8 identifier,\r
+ struct wpabuf **out_data)\r
+{\r
+ if (resp == NULL)\r
+ return 0;\r
+\r
+ if (eap_peer_tls_encrypt(sm, &data->ssl, EAP_TYPE_TTLS,\r
+ data->ttls_version, identifier,\r
+ resp, out_data)) {\r
+ wpa_printf(MSG_ERROR, "EAP-TTLS: Failed to encrypt a Phase 2 frame\n");\r
+ return -1;\r
+ }\r
+ wpabuf_free(resp);\r
+\r
+ return 0;\r
+}\r
+\r
+#if 0\r
+static int eap_ttls_process_phase2_eap(struct eap_sm *sm,\r
+ struct eap_ttls_data *data,\r
+ struct eap_method_ret *ret,\r
+ struct ttls_parse_avp *parse,\r
+ struct wpabuf **resp)\r
+{\r
+ struct eap_hdr *hdr;\r
+ size_t len;\r
+\r
+ if (parse->eapdata == NULL) {\r
+ wpa_printf(MSG_ERROR, "EAP-TTLS: No EAP Message in the "\r
+ "packet - dropped\n");\r
+ return -1;\r
+ }\r
+\r
+ hdr = (struct eap_hdr *) parse->eapdata;\r
+\r
+ if (parse->eap_len < sizeof(*hdr)) {\r
+ wpa_printf(MSG_ERROR, "EAP-TTLS: Too short Phase 2 EAP "\r
+ "frame (len=%lu, expected %lu or more) - dropped\n",\r
+ (unsigned long) parse->eap_len,\r
+ (unsigned long) sizeof(*hdr));\r
+ return -1;\r
+ }\r
+ len = be_to_host16(hdr->length);\r
+ if (len > parse->eap_len) {\r
+ wpa_printf(MSG_ERROR, "EAP-TTLS: Length mismatch in Phase 2 "\r
+ "EAP frame (EAP hdr len=%lu, EAP data len in "\r
+ "AVP=%lu)\n",\r
+ (unsigned long) len,\r
+ (unsigned long) parse->eap_len);\r
+ return -1;\r
+ }\r
+ wpa_printf(MSG_DEBUG, "EAP-TTLS: received Phase 2: code=%d "\r
+ "identifier=%d length=%lu\n",\r
+ hdr->code, hdr->identifier, (unsigned long) len);\r
+ switch (hdr->code) {\r
+ case EAP_CODE_REQUEST:\r
+ if (eap_ttls_phase2_request(sm, data, ret, hdr, resp)) {\r
+ wpa_printf(MSG_ERROR, "EAP-TTLS: Phase2 Request "\r
+ "processing failed\n");\r
+ return -1;\r
+ }\r
+ break;\r
+ default:\r
+ wpa_printf(MSG_ERROR, "EAP-TTLS: Unexpected code=%d in "\r
+ "Phase 2 EAP header\n", hdr->code);\r
+ return -1;\r
+ }\r
+\r
+ return 0;\r
+}\r
+#endif\r
+\r
+static int eap_ttls_process_phase2_mschapv2(struct eap_sm *sm,\r
+ struct eap_ttls_data *data,\r
+ struct eap_method_ret *ret,\r
+ struct ttls_parse_avp *parse)\r
+{\r
+#ifdef EAP_MSCHAPv2\r
+ if (parse->mschapv2_error) {\r
+ wpa_printf(MSG_ERROR, "EAP-TTLS/MSCHAPV2: Received "\r
+ "MS-CHAP-Error - failed\n");\r
+ ret->methodState = METHOD_DONE;\r
+ ret->decision = DECISION_FAIL;\r
+ /* Reply with empty data to ACK error */\r
+ return 1;\r
+ }\r
+\r
+ if (parse->mschapv2 == NULL) {\r
+#ifdef EAP_TNC\r
+ if (data->phase2_success && parse->eapdata) {\r
+ /*\r
+ * Allow EAP-TNC to be started after successfully\r
+ * completed MSCHAPV2.\r
+ */\r
+ return 1;\r
+ }\r
+#endif /* EAP_TNC */\r
+ wpa_printf(MSG_ERROR, "EAP-TTLS: no MS-CHAP2-Success AVP "\r
+ "received for Phase2 MSCHAPV2\n");\r
+ return -1;\r
+ }\r
+ if (parse->mschapv2[0] != data->ident) {\r
+ wpa_printf(MSG_ERROR, "EAP-TTLS: Ident mismatch for Phase 2 "\r
+ "MSCHAPV2 (received Ident 0x%02x, expected 0x%02x)\n",\r
+ parse->mschapv2[0], data->ident);\r
+ return -1;\r
+ }\r
+ if (!data->auth_response_valid ||\r
+ mschapv2_verify_auth_response(data->auth_response,\r
+ parse->mschapv2 + 1, 42)) {\r
+ wpa_printf(MSG_ERROR, "EAP-TTLS: Invalid authenticator "\r
+ "response in Phase 2 MSCHAPV2 success request\n");\r
+ return -1;\r
+ }\r
+\r
+ wpa_printf(MSG_DEBUG, "EAP-TTLS: Phase 2 MSCHAPV2 "\r
+ "authentication succeeded\n");\r
+ ret->methodState = METHOD_DONE;\r
+ ret->decision = DECISION_UNCOND_SUCC;\r
+ data->phase2_success = 1;\r
+\r
+ /*\r
+ * Reply with empty data; authentication server will reply\r
+ * with EAP-Success after this.\r
+ */\r
+ return 1;\r
+#else /* EAP_MSCHAPv2 */\r
+ wpa_printf(MSG_ERROR, "EAP-TTLS: MSCHAPv2 not included in the build\n");\r
+ return -1;\r
+#endif /* EAP_MSCHAPv2 */\r
+}\r
+\r
+\r
+#ifdef EAP_TNC\r
+static int eap_ttls_process_tnc_start(struct eap_sm *sm,\r
+ struct eap_ttls_data *data,\r
+ struct eap_method_ret *ret,\r
+ struct ttls_parse_avp *parse,\r
+ struct wpabuf **resp)\r
+{\r
+ /* TNC uses inner EAP method after non-EAP TTLS phase 2. */\r
+ if (parse->eapdata == NULL) {\r
+ wpa_printf(MSG_ERROR, "EAP-TTLS: Phase 2 received "\r
+ "unexpected tunneled data (no EAP)\n");\r
+ return -1;\r
+ }\r
+\r
+ if (!data->ready_for_tnc) {\r
+ wpa_printf(MSG_ERROR, "EAP-TTLS: Phase 2 received "\r
+ "EAP after non-EAP, but not ready for TNC\n");\r
+ return -1;\r
+ }\r
+\r
+ wpa_printf(MSG_DEBUG, "EAP-TTLS: Start TNC after completed "\r
+ "non-EAP method\n");\r
+ data->tnc_started = 1;\r
+\r
+ if (eap_ttls_process_phase2_eap(sm, data, ret, parse, resp) < 0)\r
+ return -1;\r
+\r
+ return 0;\r
+}\r
+#endif /* EAP_TNC */\r
+\r
+\r
+static int eap_ttls_process_decrypted(struct eap_sm *sm,\r
+ struct eap_ttls_data *data,\r
+ struct eap_method_ret *ret,\r
+ u8 identifier,\r
+ struct ttls_parse_avp *parse,\r
+ struct wpabuf *in_decrypted,\r
+ struct wpabuf **out_data)\r
+{\r
+ struct wpabuf *resp = NULL;\r
+ //struct eap_peer_config *config = eap_get_config(sm);\r
+ int res;\r
+ enum phase2_types phase2_type = data->phase2_type;\r
+\r
+#ifdef EAP_TNC\r
+ if (data->tnc_started)\r
+ phase2_type = EAP_TTLS_PHASE2_EAP;\r
+#endif /* EAP_TNC */\r
+\r
+ switch (phase2_type) {\r
+ case EAP_TTLS_PHASE2_MSCHAPV2:\r
+ res = eap_ttls_process_phase2_mschapv2(sm, data, ret, parse);\r
+#ifdef EAP_TNC\r
+ if (res == 1 && parse->eapdata && data->phase2_success) {\r
+ /*\r
+ * TNC may be required as the next\r
+ * authentication method within the tunnel.\r
+ */\r
+ ret->methodState = METHOD_MAY_CONT;\r
+ data->ready_for_tnc = 1;\r
+ if (eap_ttls_process_tnc_start(sm, data, ret, parse,\r
+ &resp) == 0)\r
+ break;\r
+ }\r
+#endif /* EAP_TNC */\r
+ return res;\r
+/* case EAP_TTLS_PHASE2_EAP:\r
+ if (eap_ttls_process_phase2_eap(sm, data, ret, parse, &resp) <\r
+ 0)\r
+ return -1;\r
+ break;\r
+ case EAP_TTLS_PHASE2_MSCHAP:\r
+ case EAP_TTLS_PHASE2_PAP:\r
+ case EAP_TTLS_PHASE2_CHAP:\r
+#ifdef EAP_TNC\r
+ if (eap_ttls_process_tnc_start(sm, data, ret, parse, &resp) <\r
+ 0)\r
+ return -1;\r
+ break;\r
+#else // EAP_TNC\r
+ // EAP-TTLS/{MSCHAP,PAP,CHAP} should not send any TLS tunneled\r
+ // requests to the supplicant\r
+ wpa_printf(MSG_ERROR, "EAP-TTLS: Phase 2 received unexpected "\r
+ "tunneled data\n");\r
+ return -1;\r
+#endif // EAP_TNC\r
+*/\r
+ default:\r
+ return -1;\r
+ }\r
+\r
+ if (resp) {\r
+ if (eap_ttls_encrypt_response(sm, data, resp, identifier,\r
+ out_data) < 0)\r
+ return -1;\r
+ } else {\r
+ wpabuf_free(data->pending_phase2_req);\r
+ data->pending_phase2_req = wpabuf_dup(in_decrypted);\r
+ }/* else if (config->pending_req_identity ||\r
+ config->pending_req_password ||\r
+ config->pending_req_otp ||\r
+ config->pending_req_new_password) {\r
+ wpabuf_free(data->pending_phase2_req);\r
+ data->pending_phase2_req = wpabuf_dup(in_decrypted);\r
+ }*/\r
+\r
+ return 0;\r
+}\r
+\r
+\r
+static int eap_ttls_implicit_identity_request(struct eap_sm *sm,\r
+ struct eap_ttls_data *data,\r
+ struct eap_method_ret *ret,\r
+ u8 identifier,\r
+ struct wpabuf **out_data)\r
+{\r
+ int retval = 0;\r
+ struct eap_hdr *hdr;\r
+ struct wpabuf *resp;\r
+\r
+ hdr = (struct eap_hdr *) eap_ttls_fake_identity_request();\r
+ if (hdr == NULL) {\r
+ ret->methodState = METHOD_DONE;\r
+ ret->decision = DECISION_FAIL;\r
+ return -1;\r
+ }\r
+\r
+ resp = NULL;\r
+ if (eap_ttls_phase2_request(sm, data, ret, hdr, &resp)) {\r
+ wpa_printf(MSG_ERROR, "EAP-TTLS: Phase2 Request "\r
+ "processing failed\n");\r
+ retval = -1;\r
+ } else {\r
+ //struct eap_peer_config *config = eap_get_config(sm);\r
+ if (resp == NULL) {/* &&\r
+ (config->pending_req_identity ||\r
+ config->pending_req_password ||\r
+ config->pending_req_otp ||\r
+ config->pending_req_new_password)) {*/\r
+ /*\r
+ * Use empty buffer to force implicit request\r
+ * processing when EAP request is re-processed after\r
+ * user input.\r
+ */\r
+ wpabuf_free(data->pending_phase2_req);\r
+ data->pending_phase2_req = wpabuf_alloc(0);\r
+ }\r
+\r
+ retval = eap_ttls_encrypt_response(sm, data, resp, identifier,\r
+ out_data);\r
+ }\r
+\r
+ os_free(hdr);\r
+\r
+ if (retval < 0) {\r
+ ret->methodState = METHOD_DONE;\r
+ ret->decision = DECISION_FAIL;\r
+ }\r
+\r
+ return retval;\r
+}\r
+\r
+\r
+static int eap_ttls_phase2_start(struct eap_sm *sm, struct eap_ttls_data *data,\r
+ struct eap_method_ret *ret, u8 identifier,\r
+ struct wpabuf **out_data)\r
+{\r
+ data->phase2_start = 0;\r
+\r
+ /*\r
+ * EAP-TTLS does not use Phase2 on fast re-auth; this must be done only\r
+ * if TLS part was indeed resuming a previous session. Most\r
+ * Authentication Servers terminate EAP-TTLS before reaching this\r
+ * point, but some do not. Make wpa_supplicant stop phase 2 here, if\r
+ * needed.\r
+ */\r
+ if (data->reauth &&\r
+ tls_connection_resumed(sm->ssl_ctx, data->ssl.conn)) {\r
+ wpa_printf(MSG_DEBUG, "EAP-TTLS: Session resumption - "\r
+ "skip phase 2\n");\r
+ *out_data = eap_peer_tls_build_ack(identifier, EAP_TYPE_TTLS,\r
+ data->ttls_version);\r
+ ret->methodState = METHOD_DONE;\r
+ ret->decision = DECISION_UNCOND_SUCC;\r
+ data->phase2_success = 1;\r
+ return 0;\r
+ }\r
+\r
+ return eap_ttls_implicit_identity_request(sm, data, ret, identifier,\r
+ out_data);\r
+}\r
+\r
+\r
+static int eap_ttls_decrypt(struct eap_sm *sm, struct eap_ttls_data *data,\r
+ struct eap_method_ret *ret, u8 identifier,\r
+ const struct wpabuf *in_data,\r
+ struct wpabuf **out_data)\r
+{\r
+ struct wpabuf *in_decrypted = NULL;\r
+ int retval = 0;\r
+ struct ttls_parse_avp parse;\r
+\r
+ os_memset(&parse, 0, sizeof(parse));\r
+\r
+ wpa_printf(MSG_DEBUG, "EAP-TTLS: received %lu bytes encrypted data for"\r
+ " Phase 2\n",\r
+ in_data ? (unsigned long) wpabuf_len(in_data) : 0);\r
+\r
+ if (data->pending_phase2_req) {\r
+ wpa_printf(MSG_DEBUG, "EAP-TTLS: Pending Phase 2 request - "\r
+ "skip decryption and use old data\n");\r
+ /* Clear TLS reassembly state. */\r
+ eap_peer_tls_reset_input(&data->ssl);\r
+\r
+ in_decrypted = data->pending_phase2_req;\r
+ data->pending_phase2_req = NULL;\r
+ if (wpabuf_len(in_decrypted) == 0) {\r
+ wpabuf_free(in_decrypted);\r
+ return eap_ttls_implicit_identity_request(\r
+ sm, data, ret, identifier, out_data);\r
+ }\r
+ goto continue_req;\r
+ }\r
+\r
+ if ((in_data == NULL || wpabuf_len(in_data) == 0) &&\r
+ data->phase2_start) {\r
+ return eap_ttls_phase2_start(sm, data, ret, identifier,\r
+ out_data);\r
+ }\r
+\r
+ if (in_data == NULL || wpabuf_len(in_data) == 0) {\r
+ /* Received TLS ACK - requesting more fragments */\r
+ return eap_peer_tls_encrypt(sm, &data->ssl, EAP_TYPE_TTLS,\r
+ data->ttls_version,\r
+ identifier, NULL, out_data);\r
+ }\r
+\r
+ retval = eap_peer_tls_decrypt(sm, &data->ssl, in_data, &in_decrypted);\r
+ if (retval)\r
+ goto done;\r
+\r
+continue_req:\r
+ data->phase2_start = 0;\r
+\r
+ if (eap_ttls_parse_avps(in_decrypted, &parse) < 0) {\r
+ retval = -1;\r
+ goto done;\r
+ }\r
+\r
+ retval = eap_ttls_process_decrypted(sm, data, ret, identifier,\r
+ &parse, in_decrypted, out_data);\r
+\r
+done:\r
+ wpabuf_free(in_decrypted);\r
+ os_free(parse.eapdata);\r
+\r
+ if (retval < 0) {\r
+ ret->methodState = METHOD_DONE;\r
+ ret->decision = DECISION_FAIL;\r
+ }\r
+\r
+ return retval;\r
+}\r
+\r
+\r
+static int eap_ttls_process_handshake(struct eap_sm *sm,\r
+ struct eap_ttls_data *data,\r
+ struct eap_method_ret *ret,\r
+ u8 identifier,\r
+ const u8 *in_data, size_t in_len,\r
+ struct wpabuf **out_data)\r
+{\r
+ int res;\r
+\r
+ res = eap_peer_tls_process_helper(sm, &data->ssl, EAP_TYPE_TTLS,\r
+ data->ttls_version, identifier,\r
+ in_data, in_len, out_data);\r
+\r
+ if (tls_connection_established(sm->ssl_ctx, data->ssl.conn)) {\r
+ wpa_printf(MSG_DEBUG, "EAP-TTLS: TLS done, proceed to "\r
+ "Phase 2\n");\r
+ if (data->resuming) {\r
+ wpa_printf(MSG_DEBUG, "EAP-TTLS: fast reauth - may "\r
+ "skip Phase 2\n");\r
+ ret->decision = DECISION_COND_SUCC;\r
+ ret->methodState = METHOD_MAY_CONT;\r
+ }\r
+ data->phase2_start = 1;\r
+ eap_ttls_v0_derive_key(sm, data);\r
+\r
+ if (*out_data == NULL || wpabuf_len(*out_data) == 0) {\r
+ if (eap_ttls_decrypt(sm, data, ret, identifier,\r
+ NULL, out_data)) {\r
+ wpa_printf(MSG_ERROR, "EAP-TTLS: "\r
+ "failed to process early "\r
+ "start for Phase 2\n");\r
+ }\r
+ res = 0;\r
+ }\r
+ data->resuming = 0;\r
+ }\r
+\r
+ if (res == 2) {\r
+ struct wpabuf msg;\r
+ /*\r
+ * Application data included in the handshake message.\r
+ */\r
+ wpabuf_free(data->pending_phase2_req);\r
+ data->pending_phase2_req = *out_data;\r
+ *out_data = NULL;\r
+ wpabuf_set(&msg, in_data, in_len);\r
+ res = eap_ttls_decrypt(sm, data, ret, identifier, &msg,\r
+ out_data);\r
+ }\r
+\r
+ return res;\r
+}\r
+\r
+\r
+static void eap_ttls_check_auth_status(struct eap_sm *sm, \r
+ struct eap_ttls_data *data,\r
+ struct eap_method_ret *ret)\r
+{\r
+ if (ret->methodState == METHOD_DONE) {\r
+ ret->allowNotifications = FALSE;\r
+ if (ret->decision == DECISION_UNCOND_SUCC ||\r
+ ret->decision == DECISION_COND_SUCC) {\r
+ wpa_printf(MSG_DEBUG, "EAP-TTLS: Authentication "\r
+ "completed successfully");\r
+ data->phase2_success = 1;\r
+#ifdef EAP_TNC\r
+ if (!data->ready_for_tnc && !data->tnc_started) {\r
+ /*\r
+ * TNC may be required as the next\r
+ * authentication method within the tunnel.\r
+ */\r
+ ret->methodState = METHOD_MAY_CONT;\r
+ data->ready_for_tnc = 1;\r
+ }\r
+#endif /* EAP_TNC */\r
+ }\r
+ } else if (ret->methodState == METHOD_MAY_CONT &&\r
+ (ret->decision == DECISION_UNCOND_SUCC ||\r
+ ret->decision == DECISION_COND_SUCC)) {\r
+ wpa_printf(MSG_DEBUG, "EAP-TTLS: Authentication "\r
+ "completed successfully (MAY_CONT)\n");\r
+ data->phase2_success = 1;\r
+ }\r
+}\r
+\r
+\r
+static struct wpabuf * eap_ttls_process(struct eap_sm *sm, void *priv,\r
+ struct eap_method_ret *ret,\r
+ const struct wpabuf *reqData)\r
+{\r
+ size_t left;\r
+ int res;\r
+ u8 flags, id;\r
+ struct wpabuf *resp;\r
+ const u8 *pos;\r
+ struct eap_ttls_data *data = priv;\r
+\r
+ pos = eap_peer_tls_process_init(sm, &data->ssl, EAP_TYPE_TTLS, ret,\r
+ reqData, &left, &flags);\r
+ if (pos == NULL)\r
+ return NULL;\r
+ id = eap_get_id(reqData);\r
+\r
+ if (flags & EAP_TLS_FLAGS_START) {\r
+ wpa_printf(MSG_DEBUG, "EAP-TTLS: Start (server ver=%d, own "\r
+ "ver=%d)\n", flags & EAP_TLS_VERSION_MASK,\r
+ data->ttls_version);\r
+\r
+ /* RFC 5281, Ch. 9.2:\r
+ * "This packet MAY contain additional information in the form\r
+ * of AVPs, which may provide useful hints to the client"\r
+ * For now, ignore any potential extra data.\r
+ */\r
+ left = 0;\r
+ }\r
+\r
+ resp = NULL;\r
+ if (tls_connection_established(sm->ssl_ctx, data->ssl.conn) &&\r
+ !data->resuming) {\r
+ struct wpabuf msg;\r
+ wpabuf_set(&msg, pos, left);\r
+ res = eap_ttls_decrypt(sm, data, ret, id, &msg, &resp);\r
+ } else {\r
+ res = eap_ttls_process_handshake(sm, data, ret, id,\r
+ pos, left, &resp);\r
+ }\r
+\r
+ eap_ttls_check_auth_status(sm, data, ret);\r
+ /* FIX: what about res == -1? Could just move all error processing into\r
+ * the other functions and get rid of this res==1 case here. */\r
+ if (res == 1) {\r
+ wpabuf_free(resp);\r
+ return eap_peer_tls_build_ack(id, EAP_TYPE_TTLS,\r
+ data->ttls_version);\r
+ }\r
+\r
+ return resp;\r
+}\r
+\r
+\r
+static bool eap_ttls_has_reauth_data(struct eap_sm *sm, void *priv)\r
+{\r
+ struct eap_ttls_data *data = priv;\r
+ return tls_connection_established(sm->ssl_ctx, data->ssl.conn) &&\r
+ data->phase2_success;\r
+}\r
+\r
+\r
+static void eap_ttls_deinit_for_reauth(struct eap_sm *sm, void *priv)\r
+{\r
+ struct eap_ttls_data *data = priv;\r
+ wpabuf_free(data->pending_phase2_req);\r
+ data->pending_phase2_req = NULL;\r
+#ifdef EAP_TNC\r
+ data->ready_for_tnc = 0;\r
+ data->tnc_started = 0;\r
+#endif /* EAP_TNC */\r
+}\r
+\r
+\r
+static void * eap_ttls_init_for_reauth(struct eap_sm *sm, void *priv)\r
+{\r
+ struct eap_ttls_data *data = priv;\r
+ os_free(data->key_data);\r
+ data->key_data = NULL;\r
+ os_free(data->session_id);\r
+ data->session_id = NULL;\r
+ if (eap_peer_tls_reauth_init(sm, &data->ssl)) {\r
+ os_free(data);\r
+ return NULL;\r
+ }\r
+ if (data->phase2_priv && data->phase2_method &&\r
+ data->phase2_method->init_for_reauth)\r
+ data->phase2_method->init_for_reauth(sm, data->phase2_priv);\r
+ data->phase2_start = 0;\r
+ data->phase2_success = 0;\r
+ data->resuming = 1;\r
+ data->reauth = 1;\r
+ return priv;\r
+}\r
+\r
+\r
+static int eap_ttls_get_status(struct eap_sm *sm, void *priv, char *buf,\r
+ size_t buflen, int verbose)\r
+{\r
+ struct eap_ttls_data *data = priv;\r
+ int len, ret;\r
+\r
+ len = eap_peer_tls_status(sm, &data->ssl, buf, buflen, verbose);\r
+ ret = snprintf(buf + len, buflen - len,\r
+ "EAP-TTLSv%d Phase2 method=",\r
+ data->ttls_version);\r
+ if (ret < 0 || (size_t) ret >= buflen - len)\r
+ return len;\r
+ len += ret;\r
+ switch (data->phase2_type) {\r
+ /*case EAP_TTLS_PHASE2_EAP:\r
+ ret = os_snprintf(buf + len, buflen - len, "EAP-%s\n",\r
+ data->phase2_method ?\r
+ data->phase2_method->name : "?");\r
+ break;*/\r
+ case EAP_TTLS_PHASE2_MSCHAPV2:\r
+ ret = snprintf(buf + len, buflen - len, "MSCHAPV2\n");\r
+ break;\r
+ /*case EAP_TTLS_PHASE2_MSCHAP:\r
+ ret = os_snprintf(buf + len, buflen - len, "MSCHAP\n");\r
+ break;\r
+ case EAP_TTLS_PHASE2_PAP:\r
+ ret = os_snprintf(buf + len, buflen - len, "PAP\n");\r
+ break;\r
+ case EAP_TTLS_PHASE2_CHAP:\r
+ ret = os_snprintf(buf + len, buflen - len, "CHAP\n");\r
+ break;*/\r
+ default:\r
+ ret = 0;\r
+ break;\r
+ }\r
+ if (ret < 0 || (size_t) ret >= buflen - len)\r
+ return len;\r
+ len += ret;\r
+\r
+ return len;\r
+}\r
+\r
+\r
+static bool eap_ttls_isKeyAvailable(struct eap_sm *sm, void *priv)\r
+{\r
+ struct eap_ttls_data *data = priv;\r
+ return data->key_data != NULL && data->phase2_success;\r
+}\r
+\r
+\r
+static u8 * eap_ttls_getKey(struct eap_sm *sm, void *priv, size_t *len)\r
+{\r
+ struct eap_ttls_data *data = priv;\r
+ u8 *key;\r
+\r
+ if (data->key_data == NULL || !data->phase2_success)\r
+ return NULL;\r
+\r
+ key = os_malloc(EAP_TLS_KEY_LEN);\r
+ if (key == NULL)\r
+ return NULL;\r
+\r
+ *len = EAP_TLS_KEY_LEN;\r
+ os_memcpy(key, data->key_data, EAP_TLS_KEY_LEN);\r
+\r
+ return key;\r
+}\r
+\r
+\r
+static u8 * eap_ttls_get_session_id(struct eap_sm *sm, void *priv, size_t *len)\r
+{\r
+ struct eap_ttls_data *data = priv;\r
+ u8 *id;\r
+\r
+ if (data->session_id == NULL || !data->phase2_success)\r
+ return NULL;\r
+\r
+ id = os_malloc(data->id_len);\r
+ if (id == NULL)\r
+ return NULL;\r
+\r
+ *len = data->id_len;\r
+ os_memcpy(id, data->session_id, data->id_len);\r
+\r
+ return id;\r
+}\r
+\r
+int eap_peer_ttls_register(void)\r
+{\r
+ struct eap_method *eap;\r
+ int ret;\r
+\r
+ eap = eap_peer_method_alloc(EAP_VENDOR_IETF, EAP_TYPE_TTLS,\r
+ "TTLS");\r
+ if (eap == NULL)\r
+ return -1;\r
+\r
+ eap->init = eap_ttls_init;\r
+ eap->deinit = eap_ttls_deinit;\r
+ eap->process = eap_ttls_process;\r
+ eap->isKeyAvailable = eap_ttls_isKeyAvailable;\r
+ eap->getKey = eap_ttls_getKey;\r
+ eap->getSessionId = eap_ttls_get_session_id;\r
+ eap->get_status = eap_ttls_get_status;\r
+ eap->has_reauth_data = eap_ttls_has_reauth_data;\r
+ eap->deinit_for_reauth = eap_ttls_deinit_for_reauth;\r
+ eap->init_for_reauth = eap_ttls_init_for_reauth;\r
+\r
+ ret = eap_peer_method_register(eap);\r
+ if (ret)\r
+ eap_peer_method_free(eap);\r
+ return ret;\r
+}\r
+\r
+#endif /* EAP_TTLS */\r
--- /dev/null
+/*
+ * MSCHAPV2
+ */
+
+#ifdef EAP_MSCHAPv2
+
+#include "wpa/includes.h"
+#include "wpa/common.h"
+#include "crypto/ms_funcs.h"
+#include "wpa2/eap_peer/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];
+
+ username_len = identity_len;
+ username = mschapv2_remove_domain(identity, &username_len);
+
+ if (pwhash) {
+ if (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))
+ return -1;
+ } else {
+ if (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))
+ return -1;
+ }
+
+ 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;
+ }
+ if (get_master_key(password_hash_hash, nt_response, master_key))
+ return -1;
+
+ 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;
+}
+
+#endif /* EAP_MSCHAPv2 */
--- /dev/null
+/*
+ * ASN.1 DER parsing
+ * Copyright (c) 2006, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "wpa/includes.h"
+
+#include "wpa/common.h"
+#include "wpa2/tls/asn1.h"
+
+int asn1_get_next(const u8 *buf, size_t len, struct asn1_hdr *hdr)
+{
+ const u8 *pos, *end;
+ u8 tmp;
+
+ os_memset(hdr, 0, sizeof(*hdr));
+ pos = buf;
+ end = buf + len;
+
+ hdr->identifier = *pos++;
+ hdr->class = hdr->identifier >> 6;
+ hdr->constructed = !!(hdr->identifier & (1 << 5));
+
+ if ((hdr->identifier & 0x1f) == 0x1f) {
+ hdr->tag = 0;
+ do {
+ if (pos >= end) {
+ wpa_printf(MSG_DEBUG, "ASN.1: Identifier "
+ "underflow");
+ return -1;
+ }
+ tmp = *pos++;
+ wpa_printf(MSG_DEBUG, "ASN.1: Extended tag data: "
+ "0x%02x", tmp);
+ hdr->tag = (hdr->tag << 7) | (tmp & 0x7f);
+ } while (tmp & 0x80);
+ } else
+ hdr->tag = hdr->identifier & 0x1f;
+
+ tmp = *pos++;
+ if (tmp & 0x80) {
+ if (tmp == 0xff) {
+ wpa_printf(MSG_DEBUG, "ASN.1: Reserved length "
+ "value 0xff used");
+ return -1;
+ }
+ tmp &= 0x7f; /* number of subsequent octets */
+ hdr->length = 0;
+ if (tmp > 4) {
+ wpa_printf(MSG_DEBUG, "ASN.1: Too long length field");
+ return -1;
+ }
+ while (tmp--) {
+ if (pos >= end) {
+ wpa_printf(MSG_DEBUG, "ASN.1: Length "
+ "underflow");
+ return -1;
+ }
+ hdr->length = (hdr->length << 8) | *pos++;
+ }
+ } else {
+ /* Short form - length 0..127 in one octet */
+ hdr->length = tmp;
+ }
+
+ if (end < pos || hdr->length > (unsigned int) (end - pos)) {
+ wpa_printf(MSG_DEBUG, "ASN.1: Contents underflow");
+ return -1;
+ }
+
+ hdr->payload = pos;
+ return 0;
+}
+
+
+int asn1_parse_oid(const u8 *buf, size_t len, struct asn1_oid *oid)
+{
+ const u8 *pos, *end;
+ unsigned long val;
+ u8 tmp;
+
+ os_memset(oid, 0, sizeof(*oid));
+
+ pos = buf;
+ end = buf + len;
+
+ while (pos < end) {
+ val = 0;
+
+ do {
+ if (pos >= end)
+ return -1;
+ tmp = *pos++;
+ val = (val << 7) | (tmp & 0x7f);
+ } while (tmp & 0x80);
+
+ if (oid->len >= ASN1_MAX_OID_LEN) {
+ wpa_printf(MSG_DEBUG, "ASN.1: Too long OID value");
+ return -1;
+ }
+ if (oid->len == 0) {
+ /*
+ * The first octet encodes the first two object
+ * identifier components in (X*40) + Y formula.
+ * X = 0..2.
+ */
+ oid->oid[0] = val / 40;
+ if (oid->oid[0] > 2)
+ oid->oid[0] = 2;
+ oid->oid[1] = val - oid->oid[0] * 40;
+ oid->len = 2;
+ } else
+ oid->oid[oid->len++] = val;
+ }
+
+ return 0;
+}
+
+
+int asn1_get_oid(const u8 *buf, size_t len, struct asn1_oid *oid,
+ const u8 **next)
+{
+ struct asn1_hdr hdr;
+
+ if (asn1_get_next(buf, len, &hdr) < 0 || hdr.length == 0)
+ return -1;
+
+ if (hdr.class != ASN1_CLASS_UNIVERSAL || hdr.tag != ASN1_TAG_OID) {
+ wpa_printf(MSG_DEBUG, "ASN.1: Expected OID - found class %d "
+ "tag 0x%x", hdr.class, hdr.tag);
+ return -1;
+ }
+
+ *next = hdr.payload + hdr.length;
+
+ return asn1_parse_oid(hdr.payload, hdr.length, oid);
+}
+
+
+void asn1_oid_to_str(struct asn1_oid *oid, char *buf, size_t len)
+{
+ char *pos = buf;
+ size_t i;
+ int ret;
+
+ if (len == 0)
+ return;
+
+ buf[0] = '\0';
+
+ for (i = 0; i < oid->len; i++) {
+ //ret = os_snprintf(pos, buf + len - pos,
+ ret = sprintf(pos,
+ "%s%lu",
+ i == 0 ? "" : ".", oid->oid[i]);
+ if (ret < 0 || ret >= buf + len - pos)
+ break;
+ pos += ret;
+ }
+ buf[len - 1] = '\0';
+}
+
+
+static u8 rotate_bits(u8 octet)
+{
+ int i;
+ u8 res;
+
+ res = 0;
+ for (i = 0; i < 8; i++) {
+ res <<= 1;
+ if (octet & 1)
+ res |= 1;
+ octet >>= 1;
+ }
+
+ return res;
+}
+
+
+unsigned long asn1_bit_string_to_long(const u8 *buf, size_t len)
+{
+ unsigned long val = 0;
+ const u8 *pos = buf;
+
+ /* BER requires that unused bits are zero, so we can ignore the number
+ * of unused bits */
+ pos++;
+
+ if (len >= 2)
+ val |= rotate_bits(*pos++);
+ if (len >= 3)
+ val |= ((unsigned long) rotate_bits(*pos++)) << 8;
+ if (len >= 4)
+ val |= ((unsigned long) rotate_bits(*pos++)) << 16;
+ if (len >= 5)
+ val |= ((unsigned long) rotate_bits(*pos++)) << 24;
+ if (len >= 6)
+ wpa_printf(MSG_DEBUG, "X509: %s - some bits ignored "
+ "(BIT STRING length %lu)",
+ __func__, (unsigned long) len);
+
+ return val;
+}
--- /dev/null
+/*
+ * Big number math
+ * Copyright (c) 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 "crypto/includes.h"
+#include "crypto/common.h"
+#include "wpa/wpabuf.h"
+#include "wpa/wpa_debug.h"
+#include "wpa2/tls/bignum.h"
+
+#define CONFIG_INTERNAL_LIBTOMMATH
+#ifdef CONFIG_INTERNAL_LIBTOMMATH
+#include "wpa2/tls/libtommath.h"
+#else /* CONFIG_INTERNAL_LIBTOMMATH */
+#include <tommath.h>
+#endif /* CONFIG_INTERNAL_LIBTOMMATH */
+
+
+/*
+ * The current version is just a wrapper for LibTomMath library, so
+ * struct bignum is just typecast to mp_int.
+ */
+
+/**
+ * bignum_init - Allocate memory for bignum
+ * Returns: Pointer to allocated bignum or %NULL on failure
+ */
+struct bignum *
+bignum_init(void)
+{
+ struct bignum *n = (struct bignum *)os_zalloc(sizeof(mp_int));
+ if (n == NULL)
+ return NULL;
+ if (mp_init((mp_int *) n) != MP_OKAY) {
+ os_free(n);
+ n = NULL;
+ }
+ return n;
+}
+
+
+/**
+ * bignum_deinit - Free bignum
+ * @n: Bignum from bignum_init()
+ */
+void
+bignum_deinit(struct bignum *n)
+{
+ if (n) {
+ mp_clear((mp_int *) n);
+ os_free(n);
+ }
+}
+
+
+/**
+ * bignum_get_unsigned_bin - Get length of bignum as an unsigned binary buffer
+ * @n: Bignum from bignum_init()
+ * Returns: Length of n if written to a binary buffer
+ */
+size_t
+bignum_get_unsigned_bin_len(struct bignum *n)
+{
+ return mp_unsigned_bin_size((mp_int *) n);
+}
+
+
+/**
+ * bignum_get_unsigned_bin - Set binary buffer to unsigned bignum
+ * @n: Bignum from bignum_init()
+ * @buf: Buffer for the binary number
+ * @len: Length of the buffer, can be %NULL if buffer is known to be long
+ * enough. Set to used buffer length on success if not %NULL.
+ * Returns: 0 on success, -1 on failure
+ */
+int
+bignum_get_unsigned_bin(const struct bignum *n, u8 *buf, size_t *len)
+{
+ size_t need = mp_unsigned_bin_size((mp_int *) n);
+ if (len && need > *len) {
+ *len = need;
+ return -1;
+ }
+ if (mp_to_unsigned_bin((mp_int *) n, buf) != MP_OKAY) {
+ wpa_printf(MSG_DEBUG, "BIGNUM: %s failed", __func__);
+ return -1;
+ }
+ if (len)
+ *len = need;
+ return 0;
+}
+
+
+/**
+ * bignum_set_unsigned_bin - Set bignum based on unsigned binary buffer
+ * @n: Bignum from bignum_init(); to be set to the given value
+ * @buf: Buffer with unsigned binary value
+ * @len: Length of buf in octets
+ * Returns: 0 on success, -1 on failure
+ */
+int
+bignum_set_unsigned_bin(struct bignum *n, const u8 *buf, size_t len)
+{
+ if (mp_read_unsigned_bin((mp_int *) n, (u8 *) buf, len) != MP_OKAY) {
+ wpa_printf(MSG_DEBUG, "BIGNUM: %s failed", __func__);
+ return -1;
+ }
+ return 0;
+}
+
+
+/**
+ * bignum_cmp - Signed comparison
+ * @a: Bignum from bignum_init()
+ * @b: Bignum from bignum_init()
+ * Returns: 0 on success, -1 on failure
+ */
+int
+bignum_cmp(const struct bignum *a, const struct bignum *b)
+{
+ return mp_cmp((mp_int *) a, (mp_int *) b);
+}
+
+
+/**
+ * bignum_cmd_d - Compare bignum to standard integer
+ * @a: Bignum from bignum_init()
+ * @b: Small integer
+ * Returns: 0 on success, -1 on failure
+ */
+int
+bignum_cmp_d(const struct bignum *a, unsigned long b)
+{
+ return mp_cmp_d((mp_int *) a, b);
+}
+
+
+/**
+ * bignum_add - c = a + b
+ * @a: Bignum from bignum_init()
+ * @b: Bignum from bignum_init()
+ * @c: Bignum from bignum_init(); used to store the result of a + b
+ * Returns: 0 on success, -1 on failure
+ */
+int
+bignum_add(const struct bignum *a, const struct bignum *b,
+ struct bignum *c)
+{
+ if (mp_add((mp_int *) a, (mp_int *) b, (mp_int *) c) != MP_OKAY) {
+ wpa_printf(MSG_DEBUG, "BIGNUM: %s failed", __func__);
+ return -1;
+ }
+ return 0;
+}
+
+
+/**
+ * bignum_sub - c = a - b
+ * @a: Bignum from bignum_init()
+ * @b: Bignum from bignum_init()
+ * @c: Bignum from bignum_init(); used to store the result of a - b
+ * Returns: 0 on success, -1 on failure
+ */
+int
+bignum_sub(const struct bignum *a, const struct bignum *b,
+ struct bignum *c)
+{
+ if (mp_sub((mp_int *) a, (mp_int *) b, (mp_int *) c) != MP_OKAY) {
+ wpa_printf(MSG_DEBUG, "BIGNUM: %s failed", __func__);
+ return -1;
+ }
+ return 0;
+}
+
+
+/**
+ * bignum_mul - c = a * b
+ * @a: Bignum from bignum_init()
+ * @b: Bignum from bignum_init()
+ * @c: Bignum from bignum_init(); used to store the result of a * b
+ * Returns: 0 on success, -1 on failure
+ */
+int
+bignum_mul(const struct bignum *a, const struct bignum *b,
+ struct bignum *c)
+{
+ if (mp_mul((mp_int *) a, (mp_int *) b, (mp_int *) c) != MP_OKAY) {
+ wpa_printf(MSG_DEBUG, "BIGNUM: %s failed", __func__);
+ return -1;
+ }
+ return 0;
+}
+
+
+/**
+ * bignum_mulmod - d = a * b (mod c)
+ * @a: Bignum from bignum_init()
+ * @b: Bignum from bignum_init()
+ * @c: Bignum from bignum_init(); modulus
+ * @d: Bignum from bignum_init(); used to store the result of a * b (mod c)
+ * Returns: 0 on success, -1 on failure
+ */
+int
+bignum_mulmod(const struct bignum *a, const struct bignum *b,
+ const struct bignum *c, struct bignum *d)
+{
+ if (mp_mulmod((mp_int *) a, (mp_int *) b, (mp_int *) c, (mp_int *) d)
+ != MP_OKAY) {
+ wpa_printf(MSG_DEBUG, "BIGNUM: %s failed", __func__);
+ return -1;
+ }
+ return 0;
+}
+
+
+/**
+ * bignum_exptmod - Modular exponentiation: d = a^b (mod c)
+ * @a: Bignum from bignum_init(); base
+ * @b: Bignum from bignum_init(); exponent
+ * @c: Bignum from bignum_init(); modulus
+ * @d: Bignum from bignum_init(); used to store the result of a^b (mod c)
+ * Returns: 0 on success, -1 on failure
+ */
+int
+bignum_exptmod(const struct bignum *a, const struct bignum *b,
+ const struct bignum *c, struct bignum *d)
+{
+ if (mp_exptmod((mp_int *) a, (mp_int *) b, (mp_int *) c, (mp_int *) d)
+ != MP_OKAY) {
+ wpa_printf(MSG_DEBUG, "BIGNUM: %s failed", __func__);
+ return -1;
+ }
+ return 0;
+}
--- /dev/null
+/*
+ * PKCS #1 (RSA Encryption)
+ * Copyright (c) 2006-2009, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "wpa/includes.h"
+
+#include "wpa/common.h"
+#include "wpa2/tls/rsa.h"
+#include "wpa2/tls/pkcs1.h"
+
+
+static int pkcs1_generate_encryption_block(u8 block_type, size_t modlen,
+ const u8 *in, size_t inlen,
+ u8 *out, size_t *outlen)
+{
+ size_t ps_len;
+ u8 *pos;
+
+ /*
+ * PKCS #1 v1.5, 8.1:
+ *
+ * EB = 00 || BT || PS || 00 || D
+ * BT = 00 or 01 for private-key operation; 02 for public-key operation
+ * PS = k-3-||D||; at least eight octets
+ * (BT=0: PS=0x00, BT=1: PS=0xff, BT=2: PS=pseudorandom non-zero)
+ * k = length of modulus in octets (modlen)
+ */
+
+ if (modlen < 12 || modlen > *outlen || inlen > modlen - 11) {
+ wpa_printf(MSG_DEBUG, "PKCS #1: %s - Invalid buffer "
+ "lengths (modlen=%lu outlen=%lu inlen=%lu)",
+ __func__, (unsigned long) modlen,
+ (unsigned long) *outlen,
+ (unsigned long) inlen);
+ return -1;
+ }
+
+ pos = out;
+ *pos++ = 0x00;
+ *pos++ = block_type; /* BT */
+ ps_len = modlen - inlen - 3;
+ switch (block_type) {
+ case 0:
+ os_memset(pos, 0x00, ps_len);
+ pos += ps_len;
+ break;
+ case 1:
+ os_memset(pos, 0xff, ps_len);
+ pos += ps_len;
+ break;
+ case 2:
+ if (os_get_random(pos, ps_len) < 0) {
+ wpa_printf(MSG_DEBUG, "PKCS #1: %s - Failed to get "
+ "random data for PS", __func__);
+ return -1;
+ }
+ while (ps_len--) {
+ if (*pos == 0x00)
+ *pos = 0x01;
+ pos++;
+ }
+ break;
+ default:
+ wpa_printf(MSG_DEBUG, "PKCS #1: %s - Unsupported block type "
+ "%d", __func__, block_type);
+ return -1;
+ }
+ *pos++ = 0x00;
+ os_memcpy(pos, in, inlen); /* D */
+
+ return 0;
+}
+
+
+int pkcs1_encrypt(int block_type, struct crypto_rsa_key *key,
+ int use_private, const u8 *in, size_t inlen,
+ u8 *out, size_t *outlen)
+{
+ size_t modlen;
+
+ modlen = crypto_rsa_get_modulus_len(key);
+
+ if (pkcs1_generate_encryption_block(block_type, modlen, in, inlen,
+ out, outlen) < 0)
+ return -1;
+
+ return crypto_rsa_exptmod(out, modlen, out, outlen, key, use_private);
+}
+
+
+int pkcs1_v15_private_key_decrypt(struct crypto_rsa_key *key,
+ const u8 *in, size_t inlen,
+ u8 *out, size_t *outlen)
+{
+ int res;
+ u8 *pos, *end;
+
+ res = crypto_rsa_exptmod(in, inlen, out, outlen, key, 1);
+ if (res)
+ return res;
+
+ if (*outlen < 2 || out[0] != 0 || out[1] != 2)
+ return -1;
+
+ /* Skip PS (pseudorandom non-zero octets) */
+ pos = out + 2;
+ end = out + *outlen;
+ while (*pos && pos < end)
+ pos++;
+ if (pos == end)
+ return -1;
+ pos++;
+
+ *outlen -= pos - out;
+
+ /* Strip PKCS #1 header */
+ os_memmove(out, pos, *outlen);
+
+ return 0;
+}
+
+
+int pkcs1_decrypt_public_key(struct crypto_rsa_key *key,
+ const u8 *crypt, size_t crypt_len,
+ u8 *plain, size_t *plain_len)
+{
+ size_t len;
+ u8 *pos;
+
+ len = *plain_len;
+ if (crypto_rsa_exptmod(crypt, crypt_len, plain, &len, key, 0) < 0)
+ return -1;
+
+ /*
+ * PKCS #1 v1.5, 8.1:
+ *
+ * EB = 00 || BT || PS || 00 || D
+ * BT = 00 or 01
+ * PS = k-3-||D|| times (00 if BT=00) or (FF if BT=01)
+ * k = length of modulus in octets
+ */
+
+ if (len < 3 + 8 + 16 /* min hash len */ ||
+ plain[0] != 0x00 || (plain[1] != 0x00 && plain[1] != 0x01)) {
+ wpa_printf(MSG_INFO, "LibTomCrypt: Invalid signature EB "
+ "structure");
+ return -1;
+ }
+
+ pos = plain + 3;
+ if (plain[1] == 0x00) {
+ /* BT = 00 */
+ if (plain[2] != 0x00) {
+ wpa_printf(MSG_INFO, "LibTomCrypt: Invalid signature "
+ "PS (BT=00)");
+ return -1;
+ }
+ while (pos + 1 < plain + len && *pos == 0x00 && pos[1] == 0x00)
+ pos++;
+ } else {
+ /* BT = 01 */
+ if (plain[2] != 0xff) {
+ wpa_printf(MSG_INFO, "LibTomCrypt: Invalid signature "
+ "PS (BT=01)");
+ return -1;
+ }
+ while (pos < plain + len && *pos == 0xff)
+ pos++;
+ }
+
+ if (pos - plain - 2 < 8) {
+ /* PKCS #1 v1.5, 8.1: At least eight octets long PS */
+ wpa_printf(MSG_INFO, "LibTomCrypt: Too short signature "
+ "padding");
+ return -1;
+ }
+
+ if (pos + 16 /* min hash len */ >= plain + len || *pos != 0x00) {
+ wpa_printf(MSG_INFO, "LibTomCrypt: Invalid signature EB "
+ "structure (2)");
+ return -1;
+ }
+ pos++;
+ len -= pos - plain;
+
+ /* Strip PKCS #1 header */
+ os_memmove(plain, pos, len);
+ *plain_len = len;
+
+ return 0;
+}
--- /dev/null
+/*
+ * PKCS #5 (Password-based Encryption)
+ * Copyright (c) 2009, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "wpa/includes.h"
+
+#include "wpa/common.h"
+#include "crypto/crypto.h"
+#include "crypto/md5.h"
+#include "wpa2/tls/asn1.h"
+#include "wpa2/tls/pkcs5.h"
+
+#include "wpa2/eap_peer/eap_i.h"
+
+struct pkcs5_params {
+ enum pkcs5_alg {
+ PKCS5_ALG_UNKNOWN,
+ PKCS5_ALG_MD5_DES_CBC
+ } alg;
+ u8 salt[8];
+ size_t salt_len;
+ unsigned int iter_count;
+};
+
+
+static enum pkcs5_alg pkcs5_get_alg(struct asn1_oid *oid)
+{
+ if (oid->len == 7 &&
+ oid->oid[0] == 1 /* iso */ &&
+ oid->oid[1] == 2 /* member-body */ &&
+ oid->oid[2] == 840 /* us */ &&
+ oid->oid[3] == 113549 /* rsadsi */ &&
+ oid->oid[4] == 1 /* pkcs */ &&
+ oid->oid[5] == 5 /* pkcs-5 */ &&
+ oid->oid[6] == 3 /* pbeWithMD5AndDES-CBC */)
+ return PKCS5_ALG_MD5_DES_CBC;
+
+ return PKCS5_ALG_UNKNOWN;
+}
+
+
+static int pkcs5_get_params(const u8 *enc_alg, size_t enc_alg_len,
+ struct pkcs5_params *params)
+{
+ struct asn1_hdr hdr;
+ const u8 *enc_alg_end, *pos, *end;
+ struct asn1_oid oid;
+ char obuf[80];
+
+ /* AlgorithmIdentifier */
+
+ enc_alg_end = enc_alg + enc_alg_len;
+
+ os_memset(params, 0, sizeof(*params));
+
+ if (asn1_get_oid(enc_alg, enc_alg_end - enc_alg, &oid, &pos)) {
+ wpa_printf(MSG_DEBUG, "PKCS #5: Failed to parse OID "
+ "(algorithm)");
+ return -1;
+ }
+
+ asn1_oid_to_str(&oid, obuf, sizeof(obuf));
+ wpa_printf(MSG_DEBUG, "PKCS #5: encryption algorithm %s", obuf);
+ params->alg = pkcs5_get_alg(&oid);
+ if (params->alg == PKCS5_ALG_UNKNOWN) {
+ wpa_printf(MSG_INFO, "PKCS #5: unsupported encryption "
+ "algorithm %s", obuf);
+ return -1;
+ }
+
+ /*
+ * PKCS#5, Section 8
+ * PBEParameter ::= SEQUENCE {
+ * salt OCTET STRING SIZE(8),
+ * iterationCount INTEGER }
+ */
+
+ if (asn1_get_next(pos, enc_alg_end - pos, &hdr) < 0 ||
+ hdr.class != ASN1_CLASS_UNIVERSAL ||
+ hdr.tag != ASN1_TAG_SEQUENCE) {
+ wpa_printf(MSG_DEBUG, "PKCS #5: Expected SEQUENCE "
+ "(PBEParameter) - found class %d tag 0x%x",
+ hdr.class, hdr.tag);
+ return -1;
+ }
+ pos = hdr.payload;
+ end = hdr.payload + hdr.length;
+
+ /* salt OCTET STRING SIZE(8) */
+ if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
+ hdr.class != ASN1_CLASS_UNIVERSAL ||
+ hdr.tag != ASN1_TAG_OCTETSTRING ||
+ hdr.length != 8) {
+ wpa_printf(MSG_DEBUG, "PKCS #5: Expected OCTETSTRING SIZE(8) "
+ "(salt) - found class %d tag 0x%x size %d",
+ hdr.class, hdr.tag, hdr.length);
+ return -1;
+ }
+ pos = hdr.payload + hdr.length;
+ os_memcpy(params->salt, hdr.payload, hdr.length);
+ params->salt_len = hdr.length;
+ wpa_hexdump(MSG_DEBUG, "PKCS #5: salt",
+ params->salt, params->salt_len);
+
+ /* iterationCount INTEGER */
+ if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
+ hdr.class != ASN1_CLASS_UNIVERSAL || hdr.tag != ASN1_TAG_INTEGER) {
+ wpa_printf(MSG_DEBUG, "PKCS #5: Expected INTEGER - found "
+ "class %d tag 0x%x", hdr.class, hdr.tag);
+ return -1;
+ }
+ if (hdr.length == 1)
+ params->iter_count = *hdr.payload;
+ else if (hdr.length == 2)
+ params->iter_count = WPA_GET_BE16(hdr.payload);
+ else if (hdr.length == 4)
+ params->iter_count = WPA_GET_BE32(hdr.payload);
+ else {
+ wpa_hexdump(MSG_DEBUG, "PKCS #5: Unsupported INTEGER value "
+ " (iterationCount)",
+ hdr.payload, hdr.length);
+ return -1;
+ }
+ wpa_printf(MSG_DEBUG, "PKCS #5: iterationCount=0x%x",
+ params->iter_count);
+ if (params->iter_count == 0 || params->iter_count > 0xffff) {
+ wpa_printf(MSG_INFO, "PKCS #5: Unsupported "
+ "iterationCount=0x%x", params->iter_count);
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static struct crypto_cipher * pkcs5_crypto_init(struct pkcs5_params *params,
+ const char *passwd)
+{
+ unsigned int i;
+ u8 hash[MD5_MAC_LEN];
+ const u8 *addr[2];
+ size_t len[2];
+
+ if (params->alg != PKCS5_ALG_MD5_DES_CBC) {
+ return NULL;
+ }
+
+ addr[0] = (const u8 *) passwd;
+ len[0] = os_strlen(passwd);
+ addr[1] = params->salt;
+ len[1] = params->salt_len;
+ if (md5_vector(2, addr, len, hash) < 0)
+ return NULL;
+ addr[0] = hash;
+ len[0] = MD5_MAC_LEN;
+ for (i = 1; i < params->iter_count; i++) {
+ if (md5_vector(1, addr, len, hash) < 0)
+ return NULL;
+ }
+ /* TODO: DES key parity bits(?) */
+ wpa_hexdump_key(MSG_DEBUG, "PKCS #5: DES key", hash, 8);
+ wpa_hexdump_key(MSG_DEBUG, "PKCS #5: DES IV", hash + 8, 8);
+
+ if (wpa2_crypto_funcs.crypto_cipher_init) {
+ return wpa2_crypto_funcs.crypto_cipher_init(CRYPTO_CIPHER_ALG_DES, hash + 8, hash, 8);
+ } else {
+ wpa_printf(MSG_ERROR, "In function %s, fail to register crypto_cipher_init function! \r\n", __FUNCTION__);
+ return NULL;
+ }
+}
+
+u8 * pkcs5_decrypt(const u8 *enc_alg, size_t enc_alg_len,
+ const u8 *enc_data, size_t enc_data_len,
+ const char *passwd, size_t *data_len)
+{
+ struct crypto_cipher *ctx = NULL;
+ u8 *eb, pad;
+ struct pkcs5_params params;
+ unsigned int i;
+
+ if (pkcs5_get_params(enc_alg, enc_alg_len, ¶ms) < 0) {
+ wpa_printf(MSG_DEBUG, "PKCS #5: Unsupported parameters");
+ return NULL;
+ }
+
+ ctx = pkcs5_crypto_init(¶ms, passwd);
+
+ if (ctx == NULL) {
+ wpa_printf(MSG_DEBUG, "PKCS #5: Failed to initialize crypto");
+ return NULL;
+ }
+
+ /* PKCS #5, Section 7 - Decryption process */
+ if (enc_data_len < 16 || enc_data_len % 8) {
+ wpa_printf(MSG_INFO, "PKCS #5: invalid length of ciphertext "
+ "%d", (int) enc_data_len);
+ if (wpa2_crypto_funcs.crypto_cipher_deinit) {
+ wpa2_crypto_funcs.crypto_cipher_deinit(ctx);
+ } else {
+ wpa_printf(MSG_ERROR, "Fail to register crypto cipher deinit function!\r\n");
+ return NULL;
+ }
+ return NULL;
+ }
+
+ eb = os_malloc(enc_data_len);
+ if (eb == NULL) {
+ if (wpa2_crypto_funcs.crypto_cipher_deinit) {
+ wpa2_crypto_funcs.crypto_cipher_deinit(ctx);
+ } else {
+ wpa_printf(MSG_ERROR, "Fail to register crypto cipher deinit function!\r\n");
+ return NULL;
+ }
+ return NULL;
+ }
+
+ if (wpa2_crypto_funcs.crypto_cipher_decrypt) {
+ if ((int)wpa2_crypto_funcs.crypto_cipher_decrypt(ctx, enc_data, eb, enc_data_len) < 0) {
+ wpa_printf(MSG_DEBUG, "PKCS #5: Failed to decrypt EB");
+ wpa2_crypto_funcs.crypto_cipher_deinit(ctx);
+ os_free(eb);
+ return NULL;
+ }
+ } else {
+ wpa_printf(MSG_ERROR, "Fail to register crypto cipher decrypt function.\r\n");
+ wpa2_crypto_funcs.crypto_cipher_deinit(ctx);
+ os_free(eb);
+ return NULL;
+ }
+
+ if (wpa2_crypto_funcs.crypto_cipher_deinit) {
+ wpa2_crypto_funcs.crypto_cipher_deinit(ctx);
+ } else {
+ wpa_printf(MSG_ERROR, "Fail to register crypto cipher deinit function!\r\n");
+ return NULL;
+ }
+
+ pad = eb[enc_data_len - 1];
+ if (pad > 8) {
+ wpa_printf(MSG_INFO, "PKCS #5: Invalid PS octet 0x%x", pad);
+ os_free(eb);
+ return NULL;
+ }
+ for (i = enc_data_len - pad; i < enc_data_len; i++) {
+ if (eb[i] != pad) {
+ wpa_hexdump(MSG_INFO, "PKCS #5: Invalid PS",
+ eb + enc_data_len - pad, pad);
+ os_free(eb);
+ return NULL;
+ }
+ }
+
+ wpa_hexdump_key(MSG_MSGDUMP, "PKCS #5: message M (encrypted key)",
+ eb, enc_data_len - pad);
+
+ *data_len = enc_data_len - pad;
+ return eb;
+}
--- /dev/null
+/*
+ * PKCS #8 (Private-key information syntax)
+ * Copyright (c) 2006-2009, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "wpa/includes.h"
+
+#include "wpa/common.h"
+#include "wpa2/tls/asn1.h"
+#include "wpa2/tls/bignum.h"
+#include "wpa2/tls/rsa.h"
+#include "wpa2/tls/pkcs5.h"
+#include "wpa2/tls/pkcs8.h"
+
+struct crypto_private_key * pkcs8_key_import(const u8 *buf, size_t len)
+{
+ struct asn1_hdr hdr;
+ const u8 *pos, *end;
+ struct bignum *zero;
+ struct asn1_oid oid;
+ char obuf[80];
+
+ /* PKCS #8, Chapter 6 */
+
+ /* PrivateKeyInfo ::= SEQUENCE */
+ if (asn1_get_next(buf, len, &hdr) < 0 ||
+ hdr.class != ASN1_CLASS_UNIVERSAL ||
+ hdr.tag != ASN1_TAG_SEQUENCE) {
+ wpa_printf(MSG_DEBUG, "PKCS #8: Does not start with PKCS #8 "
+ "header (SEQUENCE); assume PKCS #8 not used");
+ return NULL;
+ }
+ pos = hdr.payload;
+ end = pos + hdr.length;
+
+ /* version Version (Version ::= INTEGER) */
+ if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
+ hdr.class != ASN1_CLASS_UNIVERSAL || hdr.tag != ASN1_TAG_INTEGER) {
+ wpa_printf(MSG_DEBUG, "PKCS #8: Expected INTEGER - found "
+ "class %d tag 0x%x; assume PKCS #8 not used",
+ hdr.class, hdr.tag);
+ return NULL;
+ }
+
+ zero = bignum_init();
+ if (zero == NULL)
+ return NULL;
+
+ if (bignum_set_unsigned_bin(zero, hdr.payload, hdr.length) < 0) {
+ wpa_printf(MSG_DEBUG, "PKCS #8: Failed to parse INTEGER");
+ bignum_deinit(zero);
+ return NULL;
+ }
+ pos = hdr.payload + hdr.length;
+
+ if (bignum_cmp_d(zero, 0) != 0) {
+ wpa_printf(MSG_DEBUG, "PKCS #8: Expected zero INTEGER in the "
+ "beginning of private key; not found; assume "
+ "PKCS #8 not used");
+ bignum_deinit(zero);
+ return NULL;
+ }
+ bignum_deinit(zero);
+
+ /* privateKeyAlgorithm PrivateKeyAlgorithmIdentifier
+ * (PrivateKeyAlgorithmIdentifier ::= AlgorithmIdentifier) */
+ if (asn1_get_next(pos, len, &hdr) < 0 ||
+ hdr.class != ASN1_CLASS_UNIVERSAL ||
+ hdr.tag != ASN1_TAG_SEQUENCE) {
+ wpa_printf(MSG_DEBUG, "PKCS #8: Expected SEQUENCE "
+ "(AlgorithmIdentifier) - found class %d tag 0x%x; "
+ "assume PKCS #8 not used",
+ hdr.class, hdr.tag);
+ return NULL;
+ }
+
+ if (asn1_get_oid(hdr.payload, hdr.length, &oid, &pos)) {
+ wpa_printf(MSG_DEBUG, "PKCS #8: Failed to parse OID "
+ "(algorithm); assume PKCS #8 not used");
+ return NULL;
+ }
+
+ asn1_oid_to_str(&oid, obuf, sizeof(obuf));
+ wpa_printf(MSG_DEBUG, "PKCS #8: algorithm=%s", obuf);
+
+ if (oid.len != 7 ||
+ oid.oid[0] != 1 /* iso */ ||
+ oid.oid[1] != 2 /* member-body */ ||
+ oid.oid[2] != 840 /* us */ ||
+ oid.oid[3] != 113549 /* rsadsi */ ||
+ oid.oid[4] != 1 /* pkcs */ ||
+ oid.oid[5] != 1 /* pkcs-1 */ ||
+ oid.oid[6] != 1 /* rsaEncryption */) {
+ wpa_printf(MSG_DEBUG, "PKCS #8: Unsupported private key "
+ "algorithm %s", obuf);
+ return NULL;
+ }
+
+ pos = hdr.payload + hdr.length;
+
+ /* privateKey PrivateKey (PrivateKey ::= OCTET STRING) */
+ if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
+ hdr.class != ASN1_CLASS_UNIVERSAL ||
+ hdr.tag != ASN1_TAG_OCTETSTRING) {
+ wpa_printf(MSG_DEBUG, "PKCS #8: Expected OCTETSTRING "
+ "(privateKey) - found class %d tag 0x%x",
+ hdr.class, hdr.tag);
+ return NULL;
+ }
+ wpa_printf(MSG_DEBUG, "PKCS #8: Try to parse RSAPrivateKey");
+
+ return (struct crypto_private_key *)
+ crypto_rsa_import_private_key(hdr.payload, hdr.length);
+}
+
+
+struct crypto_private_key *
+pkcs8_enc_key_import(const u8 *buf, size_t len, const char *passwd)
+{
+ struct asn1_hdr hdr;
+ const u8 *pos, *end, *enc_alg;
+ size_t enc_alg_len;
+ u8 *data;
+ size_t data_len;
+
+ if (passwd == NULL)
+ return NULL;
+
+ /*
+ * PKCS #8, Chapter 7
+ * EncryptedPrivateKeyInfo ::= SEQUENCE {
+ * encryptionAlgorithm EncryptionAlgorithmIdentifier,
+ * encryptedData EncryptedData }
+ * EncryptionAlgorithmIdentifier ::= AlgorithmIdentifier
+ * EncryptedData ::= OCTET STRING
+ */
+
+ if (asn1_get_next(buf, len, &hdr) < 0 ||
+ hdr.class != ASN1_CLASS_UNIVERSAL ||
+ hdr.tag != ASN1_TAG_SEQUENCE) {
+ wpa_printf(MSG_DEBUG, "PKCS #8: Does not start with PKCS #8 "
+ "header (SEQUENCE); assume encrypted PKCS #8 not "
+ "used");
+ return NULL;
+ }
+ pos = hdr.payload;
+ end = pos + hdr.length;
+
+ /* encryptionAlgorithm EncryptionAlgorithmIdentifier */
+ if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
+ hdr.class != ASN1_CLASS_UNIVERSAL ||
+ hdr.tag != ASN1_TAG_SEQUENCE) {
+ wpa_printf(MSG_DEBUG, "PKCS #8: Expected SEQUENCE "
+ "(AlgorithmIdentifier) - found class %d tag 0x%x; "
+ "assume encrypted PKCS #8 not used",
+ hdr.class, hdr.tag);
+ return NULL;
+ }
+ enc_alg = hdr.payload;
+ enc_alg_len = hdr.length;
+ pos = hdr.payload + hdr.length;
+
+ /* encryptedData EncryptedData */
+ if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
+ hdr.class != ASN1_CLASS_UNIVERSAL ||
+ hdr.tag != ASN1_TAG_OCTETSTRING) {
+ wpa_printf(MSG_DEBUG, "PKCS #8: Expected OCTETSTRING "
+ "(encryptedData) - found class %d tag 0x%x",
+ hdr.class, hdr.tag);
+ return NULL;
+ }
+
+ data = pkcs5_decrypt(enc_alg, enc_alg_len, hdr.payload, hdr.length,
+ passwd, &data_len);
+ if (data) {
+ struct crypto_private_key *key;
+ key = pkcs8_key_import(data, data_len);
+ os_free(data);
+ return key;
+ }
+
+ return NULL;
+}
--- /dev/null
+/*
+ * RSA
+ * Copyright (c) 2006, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "wpa/includes.h"
+
+#include "wpa/common.h"
+#include "wpa2/tls/asn1.h"
+#include "wpa2/tls/bignum.h"
+#include "wpa2/tls/rsa.h"
+#include "soc/dport_reg.h"
+
+struct crypto_rsa_key {
+ int private_key; /* whether private key is set */
+ struct bignum *n; /* modulus (p * q) */
+ struct bignum *e; /* public exponent */
+ /* The following parameters are available only if private_key is set */
+ struct bignum *d; /* private exponent */
+ struct bignum *p; /* prime p (factor of n) */
+ struct bignum *q; /* prime q (factor of n) */
+ struct bignum *dmp1; /* d mod (p - 1); CRT exponent */
+ struct bignum *dmq1; /* d mod (q - 1); CRT exponent */
+ struct bignum *iqmp; /* 1 / q mod p; CRT coefficient */
+};
+
+
+static const u8 * crypto_rsa_parse_integer(const u8 *pos, const u8 *end,
+ struct bignum *num)
+{
+ struct asn1_hdr hdr;
+
+ if (pos == NULL)
+ return NULL;
+
+ if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
+ hdr.class != ASN1_CLASS_UNIVERSAL || hdr.tag != ASN1_TAG_INTEGER) {
+ wpa_printf(MSG_DEBUG, "RSA: Expected INTEGER - found class %d "
+ "tag 0x%x", hdr.class, hdr.tag);
+ return NULL;
+ }
+
+ if (bignum_set_unsigned_bin(num, hdr.payload, hdr.length) < 0) {
+ wpa_printf(MSG_DEBUG, "RSA: Failed to parse INTEGER");
+ return NULL;
+ }
+
+ return hdr.payload + hdr.length;
+}
+
+
+/**
+ * crypto_rsa_import_public_key - Import an RSA public key
+ * @buf: Key buffer (DER encoded RSA public key)
+ * @len: Key buffer length in bytes
+ * Returns: Pointer to the public key or %NULL on failure
+ */
+struct crypto_rsa_key *
+crypto_rsa_import_public_key(const u8 *buf, size_t len)
+{
+ struct crypto_rsa_key *key;
+ struct asn1_hdr hdr;
+ const u8 *pos, *end;
+
+ key = (struct crypto_rsa_key *)os_zalloc(sizeof(*key));
+ if (key == NULL)
+ return NULL;
+
+ key->n = bignum_init();
+ key->e = bignum_init();
+ if (key->n == NULL || key->e == NULL) {
+ crypto_rsa_free(key);
+ return NULL;
+ }
+
+ /*
+ * PKCS #1, 7.1:
+ * RSAPublicKey ::= SEQUENCE {
+ * modulus INTEGER, -- n
+ * publicExponent INTEGER -- e
+ * }
+ */
+
+ if (asn1_get_next(buf, len, &hdr) < 0 ||
+ hdr.class != ASN1_CLASS_UNIVERSAL ||
+ hdr.tag != ASN1_TAG_SEQUENCE) {
+ wpa_printf(MSG_DEBUG, "RSA: Expected SEQUENCE "
+ "(public key) - found class %d tag 0x%x",
+ hdr.class, hdr.tag);
+ goto error;
+ }
+ pos = hdr.payload;
+ end = pos + hdr.length;
+
+ pos = crypto_rsa_parse_integer(pos, end, key->n);
+ pos = crypto_rsa_parse_integer(pos, end, key->e);
+
+ if (pos == NULL)
+ goto error;
+
+ if (pos != end) {
+ wpa_hexdump(MSG_DEBUG,
+ "RSA: Extra data in public key SEQUENCE",
+ pos, end - pos);
+ goto error;
+ }
+
+ return key;
+
+error:
+ crypto_rsa_free(key);
+ return NULL;
+}
+
+
+/**
+ * crypto_rsa_import_private_key - Import an RSA private key
+ * @buf: Key buffer (DER encoded RSA private key)
+ * @len: Key buffer length in bytes
+ * Returns: Pointer to the private key or %NULL on failure
+ */
+struct crypto_rsa_key *
+crypto_rsa_import_private_key(const u8 *buf, size_t len)
+{
+ struct crypto_rsa_key *key;
+ struct bignum *zero;
+ struct asn1_hdr hdr;
+ const u8 *pos, *end;
+
+ key = (struct crypto_rsa_key *)os_zalloc(sizeof(*key));
+ if (key == NULL)
+ return NULL;
+
+ key->private_key = 1;
+
+ key->n = bignum_init();
+ key->e = bignum_init();
+ key->d = bignum_init();
+ key->p = bignum_init();
+ key->q = bignum_init();
+ key->dmp1 = bignum_init();
+ key->dmq1 = bignum_init();
+ key->iqmp = bignum_init();
+
+ if (key->n == NULL || key->e == NULL || key->d == NULL ||
+ key->p == NULL || key->q == NULL || key->dmp1 == NULL ||
+ key->dmq1 == NULL || key->iqmp == NULL) {
+ crypto_rsa_free(key);
+ return NULL;
+ }
+
+ /*
+ * PKCS #1, 7.2:
+ * RSAPrivateKey ::= SEQUENCE {
+ * version Version,
+ * modulus INTEGER, -- n
+ * publicExponent INTEGER, -- e
+ * privateExponent INTEGER, -- d
+ * prime1 INTEGER, -- p
+ * prime2 INTEGER, -- q
+ * exponent1 INTEGER, -- d mod (p-1)
+ * exponent2 INTEGER, -- d mod (q-1)
+ * coefficient INTEGER -- (inverse of q) mod p
+ * }
+ *
+ * Version ::= INTEGER -- shall be 0 for this version of the standard
+ */
+ if (asn1_get_next(buf, len, &hdr) < 0 ||
+ hdr.class != ASN1_CLASS_UNIVERSAL ||
+ hdr.tag != ASN1_TAG_SEQUENCE) {
+ wpa_printf(MSG_DEBUG, "RSA: Expected SEQUENCE "
+ "(public key) - found class %d tag 0x%x",
+ hdr.class, hdr.tag);
+ goto error;
+ }
+ pos = hdr.payload;
+ end = pos + hdr.length;
+
+ zero = bignum_init();
+ if (zero == NULL)
+ goto error;
+ pos = crypto_rsa_parse_integer(pos, end, zero);
+ if (pos == NULL || bignum_cmp_d(zero, 0) != 0) {
+ wpa_printf(MSG_DEBUG, "RSA: Expected zero INTEGER in the "
+ "beginning of private key; not found");
+ bignum_deinit(zero);
+ goto error;
+ }
+ bignum_deinit(zero);
+
+ pos = crypto_rsa_parse_integer(pos, end, key->n);
+ pos = crypto_rsa_parse_integer(pos, end, key->e);
+ pos = crypto_rsa_parse_integer(pos, end, key->d);
+ pos = crypto_rsa_parse_integer(pos, end, key->p);
+ pos = crypto_rsa_parse_integer(pos, end, key->q);
+ pos = crypto_rsa_parse_integer(pos, end, key->dmp1);
+ pos = crypto_rsa_parse_integer(pos, end, key->dmq1);
+ pos = crypto_rsa_parse_integer(pos, end, key->iqmp);
+
+ if (pos == NULL)
+ goto error;
+
+ if (pos != end) {
+ wpa_hexdump(MSG_DEBUG,
+ "RSA: Extra data in public key SEQUENCE",
+ pos, end - pos);
+ goto error;
+ }
+
+ return key;
+
+error:
+ crypto_rsa_free(key);
+ return NULL;
+}
+
+
+/**
+ * crypto_rsa_get_modulus_len - Get the modulus length of the RSA key
+ * @key: RSA key
+ * Returns: Modulus length of the key
+ */
+size_t crypto_rsa_get_modulus_len(struct crypto_rsa_key *key)
+{
+ return bignum_get_unsigned_bin_len(key->n);
+}
+
+
+/**
+ * crypto_rsa_exptmod - RSA modular exponentiation
+ * @in: Input data
+ * @inlen: Input data length
+ * @out: Buffer for output data
+ * @outlen: Maximum size of the output buffer and used size on success
+ * @key: RSA key
+ * @use_private: 1 = Use RSA private key, 0 = Use RSA public key
+ * Returns: 0 on success, -1 on failure
+ */
+int crypto_rsa_exptmod(const u8 *in, size_t inlen, u8 *out, size_t *outlen,
+ struct crypto_rsa_key *key, int use_private)
+{
+ struct bignum *tmp, *a = NULL, *b = NULL;
+ int ret = -1;
+ size_t modlen;
+
+ if (use_private && !key->private_key)
+ return -1;
+
+ tmp = bignum_init();
+ if (tmp == NULL)
+ return -1;
+
+ if (bignum_set_unsigned_bin(tmp, in, inlen) < 0)
+ goto error;
+ if (bignum_cmp(key->n, tmp) < 0) {
+ /* Too large input value for the RSA key modulus */
+ goto error;
+ }
+
+ if (use_private) {
+ /*
+ * Decrypt (or sign) using Chinese remainer theorem to speed
+ * up calculation. This is equivalent to tmp = tmp^d mod n
+ * (which would require more CPU to calculate directly).
+ *
+ * dmp1 = (1/e) mod (p-1)
+ * dmq1 = (1/e) mod (q-1)
+ * iqmp = (1/q) mod p, where p > q
+ * m1 = c^dmp1 mod p
+ * m2 = c^dmq1 mod q
+ * h = q^-1 (m1 - m2) mod p
+ * m = m2 + hq
+ */
+ a = bignum_init();
+ b = bignum_init();
+ if (a == NULL || b == NULL)
+ goto error;
+
+ /* a = tmp^dmp1 mod p */
+ if (bignum_exptmod(tmp, key->dmp1, key->p, a) < 0)
+ goto error;
+
+ /* b = tmp^dmq1 mod q */
+ if (bignum_exptmod(tmp, key->dmq1, key->q, b) < 0)
+ goto error;
+
+ /* tmp = (a - b) * (1/q mod p) (mod p) */
+ if (bignum_sub(a, b, tmp) < 0 ||
+ bignum_mulmod(tmp, key->iqmp, key->p, tmp) < 0)
+ goto error;
+
+ /* tmp = b + q * tmp */
+ if (bignum_mul(tmp, key->q, tmp) < 0 ||
+ bignum_add(tmp, b, tmp) < 0)
+ goto error;
+ } else {
+ /* Encrypt (or verify signature) */
+ /* tmp = tmp^e mod N */
+ if (bignum_exptmod(tmp, key->e, key->n, tmp) < 0)
+ goto error;
+ }
+
+ modlen = crypto_rsa_get_modulus_len(key);
+ if (modlen > *outlen) {
+ *outlen = modlen;
+ goto error;
+ }
+
+ if (bignum_get_unsigned_bin_len(tmp) > modlen)
+ goto error; /* should never happen */
+
+ *outlen = modlen;
+ os_memset(out, 0, modlen);
+ if (bignum_get_unsigned_bin(
+ tmp, out +
+ (modlen - bignum_get_unsigned_bin_len(tmp)), NULL) < 0)
+ goto error;
+
+ ret = 0;
+
+error:
+
+ bignum_deinit(tmp);
+ bignum_deinit(a);
+ bignum_deinit(b);
+ return ret;
+}
+
+
+/**
+ * crypto_rsa_free - Free RSA key
+ * @key: RSA key to be freed
+ *
+ * This function frees an RSA key imported with either
+ * crypto_rsa_import_public_key() or crypto_rsa_import_private_key().
+ */
+void crypto_rsa_free(struct crypto_rsa_key *key)
+{
+ if (key) {
+ bignum_deinit(key->n);
+ bignum_deinit(key->e);
+ bignum_deinit(key->d);
+ bignum_deinit(key->p);
+ bignum_deinit(key->q);
+ bignum_deinit(key->dmp1);
+ bignum_deinit(key->dmq1);
+ bignum_deinit(key->iqmp);
+ os_free(key);
+ }
+}
--- /dev/null
+/*
+ * TLS interface functions and an internal TLS implementation
+ * Copyright (c) 2004-2011, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ *
+ * This file interface functions for hostapd/wpa_supplicant to use the
+ * integrated TLSv1 implementation.
+ */
+
+#include "wpa/includes.h"
+
+#include "wpa/common.h"
+#include "crypto/sha1.h"
+#include "crypto/md5.h"
+#include "wpa2/tls/tls.h"
+#include "wpa2/tls/tlsv1_client.h"
+#include "wpa2/tls/tlsv1_server.h"
+
+#ifndef CONFIG_TLS_INTERNAL_CLIENT
+#define CONFIG_TLS_INTERNAL_CLIENT
+#endif
+
+static int tls_ref_count = 0;
+
+struct tls_global {
+ int server;
+ struct tlsv1_credentials *server_cred;
+ int check_crl;
+};
+
+struct tls_connection {
+ struct tlsv1_client *client;
+ struct tlsv1_server *server;
+};
+
+
+void * tls_init(void)
+{
+ struct tls_global *global;
+
+ if (tls_ref_count == 0) {
+#ifdef CONFIG_TLS_INTERNAL_CLIENT
+ if (tlsv1_client_global_init())
+ return NULL;
+#endif /* CONFIG_TLS_INTERNAL_CLIENT */
+#ifdef CONFIG_TLS_INTERNAL_SERVER
+ if (tlsv1_server_global_init())
+ return NULL;
+#endif /* CONFIG_TLS_INTERNAL_SERVER */
+ }
+ tls_ref_count++;
+
+ global = (struct tls_global *)os_zalloc(sizeof(*global));
+ if (global == NULL)
+ return NULL;
+
+ return global;
+}
+
+void tls_deinit(void *ssl_ctx)
+{
+ struct tls_global *global = ssl_ctx;
+ tls_ref_count--;
+ if (tls_ref_count == 0) {
+#ifdef CONFIG_TLS_INTERNAL_CLIENT
+ tlsv1_client_global_deinit();
+#endif /* CONFIG_TLS_INTERNAL_CLIENT */
+#ifdef CONFIG_TLS_INTERNAL_SERVER
+ tlsv1_cred_free(global->server_cred);
+ tlsv1_server_global_deinit();
+#endif /* CONFIG_TLS_INTERNAL_SERVER */
+ }
+ os_free(global);
+}
+
+
+int tls_get_errors(void *tls_ctx)
+{
+ return 0;
+}
+
+
+struct tls_connection * tls_connection_init(void *tls_ctx)
+{
+ struct tls_connection *conn;
+ struct tls_global *global = tls_ctx;
+
+ conn = (struct tls_connection *)os_zalloc(sizeof(*conn));
+ if (conn == NULL)
+ return NULL;
+#ifdef CONFIG_TLS_INTERNAL_CLIENT
+ if (!global->server) {
+ conn->client = tlsv1_client_init();
+ if (conn->client == NULL) {
+ os_free(conn);
+ return NULL;
+ }
+ }
+#endif /* CONFIG_TLS_INTERNAL_CLIENT */
+#ifdef CONFIG_TLS_INTERNAL_SERVER
+ if (global->server) {
+ conn->server = tlsv1_server_init(global->server_cred);
+ if (conn->server == NULL) {
+ os_free(conn);
+ return NULL;
+ }
+ }
+#endif /* CONFIG_TLS_INTERNAL_SERVER */
+
+ return conn;
+}
+
+
+void tls_connection_deinit(void *tls_ctx, struct tls_connection *conn)
+{
+ if (conn == NULL)
+ return;
+#ifdef CONFIG_TLS_INTERNAL_CLIENT
+ if (conn->client)
+ tlsv1_client_deinit(conn->client);
+#endif /* CONFIG_TLS_INTERNAL_CLIENT */
+#ifdef CONFIG_TLS_INTERNAL_SERVER
+ if (conn->server)
+ tlsv1_server_deinit(conn->server);
+#endif /* CONFIG_TLS_INTERNAL_SERVER */
+ os_free(conn);
+}
+
+
+int tls_connection_established(void *tls_ctx, struct tls_connection *conn)
+{
+#ifdef CONFIG_TLS_INTERNAL_CLIENT
+ if (conn->client)
+ return tlsv1_client_established(conn->client);
+#endif /* CONFIG_TLS_INTERNAL_CLIENT */
+#ifdef CONFIG_TLS_INTERNAL_SERVER
+ if (conn->server)
+ return tlsv1_server_established(conn->server);
+#endif /* CONFIG_TLS_INTERNAL_SERVER */
+ return 0;
+}
+
+
+int tls_connection_shutdown(void *tls_ctx, struct tls_connection *conn)
+{
+#ifdef CONFIG_TLS_INTERNAL_CLIENT
+ if (conn->client)
+ return tlsv1_client_shutdown(conn->client);
+#endif /* CONFIG_TLS_INTERNAL_CLIENT */
+#ifdef CONFIG_TLS_INTERNAL_SERVER
+ if (conn->server)
+ return tlsv1_server_shutdown(conn->server);
+#endif /* CONFIG_TLS_INTERNAL_SERVER */
+ return -1;
+}
+
+
+int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn,
+ const struct tls_connection_params *params)
+{
+#ifdef CONFIG_TLS_INTERNAL_CLIENT
+ struct tlsv1_credentials *cred;
+
+ if (conn->client == NULL)
+ return -1;
+
+ cred = tlsv1_cred_alloc();
+ if (cred == NULL)
+ return -1;
+
+ if (tlsv1_set_ca_cert(cred, params->ca_cert,
+ params->ca_cert_blob, params->ca_cert_blob_len,
+ params->ca_path)) {
+ wpa_printf(MSG_INFO, "TLS: Failed to configure trusted CA "
+ "certificates");
+ tlsv1_cred_free(cred);
+ return -1;
+ }
+
+ if (tlsv1_set_cert(cred, params->client_cert,
+ params->client_cert_blob,
+ params->client_cert_blob_len)) {
+ wpa_printf(MSG_INFO, "TLS: Failed to configure client "
+ "certificate");
+ tlsv1_cred_free(cred);
+ return -1;
+ }
+
+ if (tlsv1_set_private_key(cred, params->private_key,
+ params->private_key_passwd,
+ params->private_key_blob,
+ params->private_key_blob_len)) {
+ wpa_printf(MSG_INFO, "TLS: Failed to load private key");
+ tlsv1_cred_free(cred);
+ return -1;
+ }
+
+ if (tlsv1_client_set_cred(conn->client, cred) < 0) {
+ tlsv1_cred_free(cred);
+ return -1;
+ }
+
+ tlsv1_client_set_time_checks(
+ conn->client, !(params->flags & TLS_CONN_DISABLE_TIME_CHECKS));
+ //conn->client, !(TLS_CONN_DISABLE_TIME_CHECKS)); //snake
+
+ return 0;
+#else /* CONFIG_TLS_INTERNAL_CLIENT */
+ return -1;
+#endif /* CONFIG_TLS_INTERNAL_CLIENT */
+}
+
+
+int tls_global_set_params(void *tls_ctx,
+ const struct tls_connection_params *params)
+{
+#ifdef CONFIG_TLS_INTERNAL_SERVER
+ struct tls_global *global = tls_ctx;
+ struct tlsv1_credentials *cred;
+
+ /* Currently, global parameters are only set when running in server
+ * mode. */
+ global->server = 1;
+ tlsv1_cred_free(global->server_cred);
+ global->server_cred = cred = tlsv1_cred_alloc();
+ if (cred == NULL)
+ return -1;
+
+ if (tlsv1_set_ca_cert(cred, params->ca_cert, params->ca_cert_blob,
+ params->ca_cert_blob_len, params->ca_path)) {
+ wpa_printf(MSG_INFO, "TLS: Failed to configure trusted CA "
+ "certificates");
+ return -1;
+ }
+
+ if (tlsv1_set_cert(cred, params->client_cert, params->client_cert_blob,
+ params->client_cert_blob_len)) {
+ wpa_printf(MSG_INFO, "TLS: Failed to configure server "
+ "certificate");
+ return -1;
+ }
+
+ if (tlsv1_set_private_key(cred, params->private_key,
+ params->private_key_passwd,
+ params->private_key_blob,
+ params->private_key_blob_len)) {
+ wpa_printf(MSG_INFO, "TLS: Failed to load private key");
+ return -1;
+ }
+
+ if (tlsv1_set_dhparams(cred, params->dh_file, params->dh_blob,
+ params->dh_blob_len)) {
+ wpa_printf(MSG_INFO, "TLS: Failed to load DH parameters");
+ return -1;
+ }
+
+ return 0;
+#else /* CONFIG_TLS_INTERNAL_SERVER */
+ return -1;
+#endif /* CONFIG_TLS_INTERNAL_SERVER */
+}
+
+
+int tls_global_set_verify(void *tls_ctx, int check_crl)
+{
+ struct tls_global *global = tls_ctx;
+ global->check_crl = check_crl;
+ return 0;
+}
+
+
+int tls_connection_set_verify(void *tls_ctx, struct tls_connection *conn,
+ int verify_peer)
+{
+#ifdef CONFIG_TLS_INTERNAL_SERVER
+ if (conn->server)
+ return tlsv1_server_set_verify(conn->server, verify_peer);
+#endif /* CONFIG_TLS_INTERNAL_SERVER */
+ return -1;
+}
+
+
+int tls_connection_get_keys(void *tls_ctx, struct tls_connection *conn,
+ struct tls_keys *keys)
+{
+#ifdef CONFIG_TLS_INTERNAL_CLIENT
+ if (conn->client)
+ return tlsv1_client_get_keys(conn->client, keys);
+#endif /* CONFIG_TLS_INTERNAL_CLIENT */
+#ifdef CONFIG_TLS_INTERNAL_SERVER
+ if (conn->server)
+ return tlsv1_server_get_keys(conn->server, keys);
+#endif /* CONFIG_TLS_INTERNAL_SERVER */
+ return -1;
+}
+
+
+int tls_connection_prf(void *tls_ctx, struct tls_connection *conn,
+ const char *label, int server_random_first,
+ u8 *out, size_t out_len)
+{
+#ifdef CONFIG_TLS_INTERNAL_CLIENT
+ if (conn->client) {
+ return tlsv1_client_prf(conn->client, label,
+ server_random_first,
+ out, out_len);
+ }
+#endif /* CONFIG_TLS_INTERNAL_CLIENT */
+#ifdef CONFIG_TLS_INTERNAL_SERVER
+ if (conn->server) {
+ return tlsv1_server_prf(conn->server, label,
+ server_random_first,
+ out, out_len);
+ }
+#endif /* CONFIG_TLS_INTERNAL_SERVER */
+ return -1;
+}
+
+
+struct wpabuf * tls_connection_handshake(void *tls_ctx,
+ struct tls_connection *conn,
+ const struct wpabuf *in_data,
+ struct wpabuf **appl_data)
+{
+ return tls_connection_handshake2(tls_ctx, conn, in_data, appl_data,
+ NULL);
+}
+
+
+struct wpabuf * tls_connection_handshake2(void *tls_ctx,
+ struct tls_connection *conn,
+ const struct wpabuf *in_data,
+ struct wpabuf **appl_data,
+ int *need_more_data)
+{
+#ifdef CONFIG_TLS_INTERNAL_CLIENT
+ u8 *res, *ad;
+ size_t res_len, ad_len;
+ struct wpabuf *out;
+
+ if (conn->client == NULL)
+ return NULL;
+
+ ad = NULL;
+ res = tlsv1_client_handshake(conn->client,
+ in_data ? wpabuf_head(in_data) : NULL,
+ in_data ? wpabuf_len(in_data) : 0,
+ &res_len, &ad, &ad_len, need_more_data);
+ if (res == NULL) {
+ return NULL;
+ }
+ out = wpabuf_alloc_ext_data(res, res_len);
+ if (out == NULL) {
+ os_free(res);
+ os_free(ad);
+ return NULL;
+ }
+ if (appl_data) {
+ if (ad) {
+ *appl_data = wpabuf_alloc_ext_data(ad, ad_len);
+ if (*appl_data == NULL)
+ os_free(ad);
+ } else
+ *appl_data = NULL;
+ } else
+ os_free(ad);
+
+ return out;
+#else /* CONFIG_TLS_INTERNAL_CLIENT */
+ return NULL;
+#endif /* CONFIG_TLS_INTERNAL_CLIENT */
+}
+
+
+struct wpabuf * tls_connection_server_handshake(void *tls_ctx,
+ struct tls_connection *conn,
+ const struct wpabuf *in_data,
+ struct wpabuf **appl_data)
+{
+#ifdef CONFIG_TLS_INTERNAL_SERVER
+ u8 *res;
+ size_t res_len;
+ struct wpabuf *out;
+
+ if (conn->server == NULL)
+ return NULL;
+
+ if (appl_data)
+ *appl_data = NULL;
+
+ res = tlsv1_server_handshake(conn->server, wpabuf_head(in_data),
+ wpabuf_len(in_data), &res_len);
+ if (res == NULL && tlsv1_server_established(conn->server))
+ return wpabuf_alloc(0);
+ if (res == NULL)
+ return NULL;
+ out = wpabuf_alloc_ext_data(res, res_len);
+ if (out == NULL) {
+ os_free(res);
+ return NULL;
+ }
+
+ return out;
+#else /* CONFIG_TLS_INTERNAL_SERVER */
+ return NULL;
+#endif /* CONFIG_TLS_INTERNAL_SERVER */
+}
+
+
+struct wpabuf * tls_connection_encrypt(void *tls_ctx,
+ struct tls_connection *conn,
+ const struct wpabuf *in_data)
+{
+#ifdef CONFIG_TLS_INTERNAL_CLIENT
+ if (conn->client) {
+ struct wpabuf *buf;
+ int res;
+ buf = wpabuf_alloc(wpabuf_len(in_data) + 300);
+ if (buf == NULL)
+ return NULL;
+ res = tlsv1_client_encrypt(conn->client, wpabuf_head(in_data),
+ wpabuf_len(in_data),
+ wpabuf_mhead(buf),
+ wpabuf_size(buf));
+ if (res < 0) {
+ wpabuf_free(buf);
+ return NULL;
+ }
+ wpabuf_put(buf, res);
+ return buf;
+ }
+#endif /* CONFIG_TLS_INTERNAL_CLIENT */
+#ifdef CONFIG_TLS_INTERNAL_SERVER
+ if (conn->server) {
+ struct wpabuf *buf;
+ int res;
+ buf = wpabuf_alloc(wpabuf_len(in_data) + 300);
+ if (buf == NULL)
+ return NULL;
+ res = tlsv1_server_encrypt(conn->server, wpabuf_head(in_data),
+ wpabuf_len(in_data),
+ wpabuf_mhead(buf),
+ wpabuf_size(buf));
+ if (res < 0) {
+ wpabuf_free(buf);
+ return NULL;
+ }
+ wpabuf_put(buf, res);
+ return buf;
+ }
+#endif /* CONFIG_TLS_INTERNAL_SERVER */
+ return NULL;
+}
+
+
+struct wpabuf * tls_connection_decrypt(void *tls_ctx,
+ struct tls_connection *conn,
+ const struct wpabuf *in_data)
+{
+ return tls_connection_decrypt2(tls_ctx, conn, in_data, NULL);
+}
+
+
+struct wpabuf * tls_connection_decrypt2(void *tls_ctx,
+ struct tls_connection *conn,
+ const struct wpabuf *in_data,
+ int *need_more_data)
+{
+ if (need_more_data)
+ *need_more_data = 0;
+
+#ifdef CONFIG_TLS_INTERNAL_CLIENT
+ if (conn->client) {
+ return tlsv1_client_decrypt(conn->client, wpabuf_head(in_data),
+ wpabuf_len(in_data),
+ need_more_data);
+ }
+#endif /* CONFIG_TLS_INTERNAL_CLIENT */
+#ifdef CONFIG_TLS_INTERNAL_SERVER
+ if (conn->server) {
+ struct wpabuf *buf;
+ int res;
+ buf = wpabuf_alloc((wpabuf_len(in_data) + 500) * 3);
+ if (buf == NULL)
+ return NULL;
+ res = tlsv1_server_decrypt(conn->server, wpabuf_head(in_data),
+ wpabuf_len(in_data),
+ wpabuf_mhead(buf),
+ wpabuf_size(buf));
+ if (res < 0) {
+ wpabuf_free(buf);
+ return NULL;
+ }
+ wpabuf_put(buf, res);
+ return buf;
+ }
+#endif /* CONFIG_TLS_INTERNAL_SERVER */
+ return NULL;
+}
+
+
+int tls_connection_resumed(void *tls_ctx, struct tls_connection *conn)
+{
+#ifdef CONFIG_TLS_INTERNAL_CLIENT
+ if (conn->client)
+ return tlsv1_client_resumed(conn->client);
+#endif /* CONFIG_TLS_INTERNAL_CLIENT */
+#ifdef CONFIG_TLS_INTERNAL_SERVER
+ if (conn->server)
+ return tlsv1_server_resumed(conn->server);
+#endif /* CONFIG_TLS_INTERNAL_SERVER */
+ return -1;
+}
+
+
+int tls_connection_set_cipher_list(void *tls_ctx, struct tls_connection *conn,
+ u8 *ciphers)
+{
+#ifdef CONFIG_TLS_INTERNAL_CLIENT
+ if (conn->client)
+ return tlsv1_client_set_cipher_list(conn->client, ciphers);
+#endif /* CONFIG_TLS_INTERNAL_CLIENT */
+#ifdef CONFIG_TLS_INTERNAL_SERVER
+ if (conn->server)
+ return tlsv1_server_set_cipher_list(conn->server, ciphers);
+#endif /* CONFIG_TLS_INTERNAL_SERVER */
+ return -1;
+}
+
+
+int tls_get_cipher(void *tls_ctx, struct tls_connection *conn,
+ char *buf, size_t buflen)
+{
+ if (conn == NULL)
+ return -1;
+#ifdef CONFIG_TLS_INTERNAL_CLIENT
+ if (conn->client)
+ return tlsv1_client_get_cipher(conn->client, buf, buflen);
+#endif /* CONFIG_TLS_INTERNAL_CLIENT */
+#ifdef CONFIG_TLS_INTERNAL_SERVER
+ if (conn->server)
+ return tlsv1_server_get_cipher(conn->server, buf, buflen);
+#endif /* CONFIG_TLS_INTERNAL_SERVER */
+ return -1;
+}
+
+
+int tls_connection_enable_workaround(void *tls_ctx,
+ struct tls_connection *conn)
+{
+ return -1;
+}
+
+
+int tls_connection_client_hello_ext(void *tls_ctx, struct tls_connection *conn,
+ int ext_type, const u8 *data,
+ size_t data_len)
+{
+#ifdef CONFIG_TLS_INTERNAL_CLIENT
+ if (conn->client) {
+ return tlsv1_client_hello_ext(conn->client, ext_type,
+ data, data_len);
+ }
+#endif /* CONFIG_TLS_INTERNAL_CLIENT */
+ return -1;
+}
+
+
+int tls_connection_get_failed(void *tls_ctx, struct tls_connection *conn)
+{
+ return 0;
+}
+
+
+int tls_connection_get_read_alerts(void *tls_ctx, struct tls_connection *conn)
+{
+ return 0;
+}
+
+
+int tls_connection_get_write_alerts(void *tls_ctx,
+ struct tls_connection *conn)
+{
+ return 0;
+}
+
+
+int tls_connection_get_keyblock_size(void *tls_ctx,
+ struct tls_connection *conn)
+{
+#ifdef CONFIG_TLS_INTERNAL_CLIENT
+ if (conn->client)
+ return tlsv1_client_get_keyblock_size(conn->client);
+#endif /* CONFIG_TLS_INTERNAL_CLIENT */
+#ifdef CONFIG_TLS_INTERNAL_SERVER
+ if (conn->server)
+ return tlsv1_server_get_keyblock_size(conn->server);
+#endif /* CONFIG_TLS_INTERNAL_SERVER */
+ return -1;
+}
+
+
+unsigned int tls_capabilities(void *tls_ctx)
+{
+ return 0;
+}
+
+
+int tls_connection_set_session_ticket_cb(void *tls_ctx,
+ struct tls_connection *conn,
+ tls_session_ticket_cb cb,
+ void *ctx)
+{
+#ifdef CONFIG_TLS_INTERNAL_CLIENT
+ if (conn->client) {
+ tlsv1_client_set_session_ticket_cb(conn->client, cb, ctx);
+ return 0;
+ }
+#endif /* CONFIG_TLS_INTERNAL_CLIENT */
+#ifdef CONFIG_TLS_INTERNAL_SERVER
+ if (conn->server) {
+ tlsv1_server_set_session_ticket_cb(conn->server, cb, ctx);
+ return 0;
+ }
+#endif /* CONFIG_TLS_INTERNAL_SERVER */
+ return -1;
+}
+
+
+
+/**
+ * tls_prf_sha1_md5 - Pseudo-Random Function for TLS (TLS-PRF, RFC 2246)
+ * @secret: Key for PRF
+ * @secret_len: Length of the key in bytes
+ * @label: A unique label for each purpose of the PRF
+ * @seed: Seed value to bind into the key
+ * @seed_len: Length of the seed
+ * @out: Buffer for the generated pseudo-random key
+ * @outlen: Number of bytes of key to generate
+ * Returns: 0 on success, -1 on failure.
+ *
+ * This function is used to derive new, cryptographically separate keys from a
+ * given key in TLS. This PRF is defined in RFC 2246, Chapter 5.
+ */
+int tls_prf_sha1_md5(const u8 *secret, size_t secret_len, const char *label,
+ const u8 *seed, size_t seed_len, u8 *out, size_t outlen)
+{
+ size_t L_S1, L_S2, i;
+ const u8 *S1, *S2;
+ u8 A_MD5[MD5_MAC_LEN], A_SHA1[SHA1_MAC_LEN];
+ u8 P_MD5[MD5_MAC_LEN], P_SHA1[SHA1_MAC_LEN];
+ int MD5_pos, SHA1_pos;
+ const u8 *MD5_addr[3];
+ size_t MD5_len[3];
+ const unsigned char *SHA1_addr[3];
+ size_t SHA1_len[3];
+
+ if (secret_len & 1)
+ return -1;
+
+ MD5_addr[0] = A_MD5;
+ MD5_len[0] = MD5_MAC_LEN;
+ MD5_addr[1] = (unsigned char *) label;
+ MD5_len[1] = os_strlen(label);
+ MD5_addr[2] = seed;
+ MD5_len[2] = seed_len;
+
+ SHA1_addr[0] = A_SHA1;
+ SHA1_len[0] = SHA1_MAC_LEN;
+ SHA1_addr[1] = (unsigned char *) label;
+ SHA1_len[1] = os_strlen(label);
+ SHA1_addr[2] = seed;
+ SHA1_len[2] = seed_len;
+
+ /* RFC 2246, Chapter 5
+ * A(0) = seed, A(i) = HMAC(secret, A(i-1))
+ * P_hash = HMAC(secret, A(1) + seed) + HMAC(secret, A(2) + seed) + ..
+ * PRF = P_MD5(S1, label + seed) XOR P_SHA-1(S2, label + seed)
+ */
+
+ L_S1 = L_S2 = (secret_len + 1) / 2;
+ S1 = secret;
+ S2 = secret + L_S1;
+ if (secret_len & 1) {
+ /* The last byte of S1 will be shared with S2 */
+ S2--;
+ }
+
+ hmac_md5_vector(S1, L_S1, 2, &MD5_addr[1], &MD5_len[1], A_MD5);
+ hmac_sha1_vector(S2, L_S2, 2, &SHA1_addr[1], &SHA1_len[1], A_SHA1);
+
+ MD5_pos = MD5_MAC_LEN;
+ SHA1_pos = SHA1_MAC_LEN;
+ for (i = 0; i < outlen; i++) {
+ if (MD5_pos == MD5_MAC_LEN) {
+ hmac_md5_vector(S1, L_S1, 3, MD5_addr, MD5_len, P_MD5);
+ MD5_pos = 0;
+ hmac_md5(S1, L_S1, A_MD5, MD5_MAC_LEN, A_MD5);
+ }
+ if (SHA1_pos == SHA1_MAC_LEN) {
+ hmac_sha1_vector(S2, L_S2, 3, SHA1_addr, SHA1_len,
+ P_SHA1);
+ SHA1_pos = 0;
+ hmac_sha1(S2, L_S2, A_SHA1, SHA1_MAC_LEN, A_SHA1);
+ }
+
+ out[i] = P_MD5[MD5_pos] ^ P_SHA1[SHA1_pos];
+
+ MD5_pos++;
+ SHA1_pos++;
+ }
+
+ return 0;
+}
--- /dev/null
+/*
+ * TLS v1.0/v1.1/v1.2 client (RFC 2246, RFC 4346, RFC 5246)
+ * Copyright (c) 2006-2011, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "wpa/includes.h"
+
+#include "wpa/common.h"
+#include "crypto/sha1.h"
+#include "wpa2/tls/tls.h"
+#include "wpa2/tls/tlsv1_common.h"
+#include "wpa2/tls/tlsv1_record.h"
+#include "wpa2/tls/tlsv1_client.h"
+#include "wpa2/tls/tlsv1_client_i.h"
+
+/* TODO:
+ * Support for a message fragmented across several records (RFC 2246, 6.2.1)
+ */
+
+
+void tls_alert(struct tlsv1_client *conn, u8 level, u8 description)
+{
+ conn->alert_level = level;
+ conn->alert_description = description;
+}
+
+
+void tlsv1_client_free_dh(struct tlsv1_client *conn)
+{
+ os_free(conn->dh_p);
+ os_free(conn->dh_g);
+ os_free(conn->dh_ys);
+ conn->dh_p = conn->dh_g = conn->dh_ys = NULL;
+}
+
+
+int tls_derive_pre_master_secret(u8 *pre_master_secret)
+{
+ WPA_PUT_BE16(pre_master_secret, TLS_VERSION);
+ if (os_get_random(pre_master_secret + 2,
+ TLS_PRE_MASTER_SECRET_LEN - 2))
+ return -1;
+ return 0;
+}
+
+
+int tls_derive_keys(struct tlsv1_client *conn,
+ const u8 *pre_master_secret, size_t pre_master_secret_len)
+{
+ u8 seed[2 * TLS_RANDOM_LEN];
+ u8 key_block[TLS_MAX_KEY_BLOCK_LEN];
+ u8 *pos;
+ size_t key_block_len;
+
+ if (pre_master_secret) {
+ wpa_hexdump_key(MSG_MSGDUMP, "TLSv1: pre_master_secret",
+ pre_master_secret, pre_master_secret_len);
+ os_memcpy(seed, conn->client_random, TLS_RANDOM_LEN);
+ os_memcpy(seed + TLS_RANDOM_LEN, conn->server_random,
+ TLS_RANDOM_LEN);
+ if (tls_prf(conn->rl.tls_version,
+ pre_master_secret, pre_master_secret_len,
+ "master secret", seed, 2 * TLS_RANDOM_LEN,
+ conn->master_secret, TLS_MASTER_SECRET_LEN)) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Failed to derive "
+ "master_secret");
+ return -1;
+ }
+ wpa_hexdump_key(MSG_MSGDUMP, "TLSv1: master_secret",
+ conn->master_secret, TLS_MASTER_SECRET_LEN);
+ }
+
+ os_memcpy(seed, conn->server_random, TLS_RANDOM_LEN);
+ os_memcpy(seed + TLS_RANDOM_LEN, conn->client_random, TLS_RANDOM_LEN);
+ key_block_len = 2 * (conn->rl.hash_size + conn->rl.key_material_len);
+ if (conn->rl.tls_version == TLS_VERSION_1)
+ key_block_len += 2 * conn->rl.iv_size;
+ if (tls_prf(conn->rl.tls_version,
+ conn->master_secret, TLS_MASTER_SECRET_LEN,
+ "key expansion", seed, 2 * TLS_RANDOM_LEN,
+ key_block, key_block_len)) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Failed to derive key_block");
+ return -1;
+ }
+ wpa_hexdump_key(MSG_MSGDUMP, "TLSv1: key_block",
+ key_block, key_block_len);
+
+ pos = key_block;
+
+ /* client_write_MAC_secret */
+ os_memcpy(conn->rl.write_mac_secret, pos, conn->rl.hash_size);
+ pos += conn->rl.hash_size;
+ /* server_write_MAC_secret */
+ os_memcpy(conn->rl.read_mac_secret, pos, conn->rl.hash_size);
+ pos += conn->rl.hash_size;
+
+ /* client_write_key */
+ os_memcpy(conn->rl.write_key, pos, conn->rl.key_material_len);
+ pos += conn->rl.key_material_len;
+ /* server_write_key */
+ os_memcpy(conn->rl.read_key, pos, conn->rl.key_material_len);
+ pos += conn->rl.key_material_len;
+
+ if (conn->rl.tls_version == TLS_VERSION_1) {
+ /* client_write_IV */
+ os_memcpy(conn->rl.write_iv, pos, conn->rl.iv_size);
+ pos += conn->rl.iv_size;
+ /* server_write_IV */
+ os_memcpy(conn->rl.read_iv, pos, conn->rl.iv_size);
+ pos += conn->rl.iv_size;
+ } else {
+ /*
+ * Use IV field to set the mask value for TLS v1.1. A fixed
+ * mask of zero is used per the RFC 4346, 6.2.3.2 CBC Block
+ * Cipher option 2a.
+ */
+ os_memset(conn->rl.write_iv, 0, conn->rl.iv_size);
+ }
+
+ return 0;
+}
+
+
+/**
+ * tlsv1_client_handshake - Process TLS handshake
+ * @conn: TLSv1 client connection data from tlsv1_client_init()
+ * @in_data: Input data from TLS peer
+ * @in_len: Input data length
+ * @out_len: Length of the output buffer.
+ * @appl_data: Pointer to application data pointer, or %NULL if dropped
+ * @appl_data_len: Pointer to variable that is set to appl_data length
+ * @need_more_data: Set to 1 if more data would be needed to complete
+ * processing
+ * Returns: Pointer to output data, %NULL on failure
+ */
+u8 * tlsv1_client_handshake(struct tlsv1_client *conn,
+ const u8 *in_data, size_t in_len,
+ size_t *out_len, u8 **appl_data,
+ size_t *appl_data_len, int *need_more_data)
+{
+ const u8 *pos, *end;
+ u8 *msg = NULL, *in_msg = NULL, *in_pos, *in_end, alert, ct;
+ size_t in_msg_len;
+ int no_appl_data;
+ int used;
+
+ if (need_more_data)
+ *need_more_data = 0;
+
+ if (conn->state == CLIENT_HELLO) {
+ if (in_len)
+ return NULL;
+ return tls_send_client_hello(conn, out_len);
+ }
+
+ if (conn->partial_input) {
+ if (wpabuf_resize(&conn->partial_input, in_len) < 0) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Failed to allocate "
+ "memory for pending record");
+ tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_INTERNAL_ERROR);
+ goto failed;
+ }
+ wpabuf_put_data(conn->partial_input, in_data, in_len);
+ in_data = wpabuf_head(conn->partial_input);
+ in_len = wpabuf_len(conn->partial_input);
+ }
+
+ if (in_data == NULL || in_len == 0)
+ return NULL;
+
+ pos = in_data;
+ end = in_data + in_len;
+ in_msg = os_malloc(in_len);
+ if (in_msg == NULL)
+ return NULL;
+
+ /* Each received packet may include multiple records */
+ while (pos < end) {
+ in_msg_len = in_len;
+ used = tlsv1_record_receive(&conn->rl, pos, end - pos,
+ in_msg, &in_msg_len, &alert);
+ if (used < 0) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Processing received "
+ "record failed");
+ tls_alert(conn, TLS_ALERT_LEVEL_FATAL, alert);
+ goto failed;
+ }
+ if (used == 0) {
+ struct wpabuf *partial;
+ wpa_printf(MSG_DEBUG, "TLSv1: Need more data");
+ partial = wpabuf_alloc_copy(pos, end - pos);
+ wpabuf_free(conn->partial_input);
+ conn->partial_input = partial;
+ if (conn->partial_input == NULL) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Failed to "
+ "allocate memory for pending "
+ "record");
+ tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_INTERNAL_ERROR);
+ goto failed;
+ }
+ os_free(in_msg);
+ if (need_more_data)
+ *need_more_data = 1;
+ return NULL;
+ }
+ ct = pos[0];
+
+ in_pos = in_msg;
+ in_end = in_msg + in_msg_len;
+
+ /* Each received record may include multiple messages of the
+ * same ContentType. */
+ while (in_pos < in_end) {
+ in_msg_len = in_end - in_pos;
+ if (tlsv1_client_process_handshake(conn, ct, in_pos,
+ &in_msg_len,
+ appl_data,
+ appl_data_len) < 0)
+ goto failed;
+ in_pos += in_msg_len;
+ }
+
+ pos += used;
+ }
+
+ os_free(in_msg);
+ in_msg = NULL;
+
+ no_appl_data = appl_data == NULL || *appl_data == NULL;
+ msg = tlsv1_client_handshake_write(conn, out_len, no_appl_data);
+
+failed:
+ os_free(in_msg);
+ if (conn->alert_level) {
+ wpabuf_free(conn->partial_input);
+ conn->partial_input = NULL;
+ conn->state = FAILED;
+ os_free(msg);
+ msg = tlsv1_client_send_alert(conn, conn->alert_level,
+ conn->alert_description,
+ out_len);
+ } else if (msg == NULL) {
+ msg = (u8 *)os_zalloc(1);
+ *out_len = 0;
+ }
+
+ if (need_more_data == NULL || !(*need_more_data)) {
+ wpabuf_free(conn->partial_input);
+ conn->partial_input = NULL;
+ }
+
+ return msg;
+}
+
+
+/**
+ * tlsv1_client_encrypt - Encrypt data into TLS tunnel
+ * @conn: TLSv1 client connection data from tlsv1_client_init()
+ * @in_data: Pointer to plaintext data to be encrypted
+ * @in_len: Input buffer length
+ * @out_data: Pointer to output buffer (encrypted TLS data)
+ * @out_len: Maximum out_data length
+ * Returns: Number of bytes written to out_data, -1 on failure
+ *
+ * This function is used after TLS handshake has been completed successfully to
+ * send data in the encrypted tunnel.
+ */
+int tlsv1_client_encrypt(struct tlsv1_client *conn,
+ const u8 *in_data, size_t in_len,
+ u8 *out_data, size_t out_len)
+{
+ size_t rlen;
+
+ wpa_hexdump_key(MSG_MSGDUMP, "TLSv1: Plaintext AppData",
+ in_data, in_len);
+
+ if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_APPLICATION_DATA,
+ out_data, out_len, in_data, in_len, &rlen) < 0) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Failed to create a record");
+ tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_INTERNAL_ERROR);
+ return -1;
+ }
+
+ return rlen;
+}
+
+
+/**
+ * tlsv1_client_decrypt - Decrypt data from TLS tunnel
+ * @conn: TLSv1 client connection data from tlsv1_client_init()
+ * @in_data: Pointer to input buffer (encrypted TLS data)
+ * @in_len: Input buffer length
+ * @need_more_data: Set to 1 if more data would be needed to complete
+ * processing
+ * Returns: Decrypted data or %NULL on failure
+ *
+ * This function is used after TLS handshake has been completed successfully to
+ * receive data from the encrypted tunnel.
+ */
+struct wpabuf * tlsv1_client_decrypt(struct tlsv1_client *conn,
+ const u8 *in_data, size_t in_len,
+ int *need_more_data)
+{
+ const u8 *in_end, *pos;
+ int used;
+ u8 alert, *out_pos, ct;
+ size_t olen;
+ struct wpabuf *buf = NULL;
+
+ if (need_more_data)
+ *need_more_data = 0;
+
+ if (conn->partial_input) {
+ if (wpabuf_resize(&conn->partial_input, in_len) < 0) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Failed to allocate "
+ "memory for pending record");
+ alert = TLS_ALERT_INTERNAL_ERROR;
+ goto fail;
+ }
+ wpabuf_put_data(conn->partial_input, in_data, in_len);
+ in_data = wpabuf_head(conn->partial_input);
+ in_len = wpabuf_len(conn->partial_input);
+ }
+
+ pos = in_data;
+ in_end = in_data + in_len;
+
+ while (pos < in_end) {
+ ct = pos[0];
+ if (wpabuf_resize(&buf, in_end - pos) < 0) {
+ alert = TLS_ALERT_INTERNAL_ERROR;
+ goto fail;
+ }
+ out_pos = wpabuf_put(buf, 0);
+ olen = wpabuf_tailroom(buf);
+ used = tlsv1_record_receive(&conn->rl, pos, in_end - pos,
+ out_pos, &olen, &alert);
+ if (used < 0) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Record layer processing "
+ "failed");
+ goto fail;
+ }
+ if (used == 0) {
+ struct wpabuf *partial;
+ wpa_printf(MSG_DEBUG, "TLSv1: Need more data");
+ partial = wpabuf_alloc_copy(pos, in_end - pos);
+ wpabuf_free(conn->partial_input);
+ conn->partial_input = partial;
+ if (conn->partial_input == NULL) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Failed to "
+ "allocate memory for pending "
+ "record");
+ alert = TLS_ALERT_INTERNAL_ERROR;
+ goto fail;
+ }
+ if (need_more_data)
+ *need_more_data = 1;
+ return buf;
+ }
+
+ if (ct == TLS_CONTENT_TYPE_ALERT) {
+ if (olen < 2) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Alert "
+ "underflow");
+ alert = TLS_ALERT_DECODE_ERROR;
+ goto fail;
+ }
+ wpa_printf(MSG_DEBUG, "TLSv1: Received alert %d:%d",
+ out_pos[0], out_pos[1]);
+ if (out_pos[0] == TLS_ALERT_LEVEL_WARNING) {
+ /* Continue processing */
+ pos += used;
+ continue;
+ }
+
+ alert = out_pos[1];
+ goto fail;
+ }
+
+ if (ct != TLS_CONTENT_TYPE_APPLICATION_DATA) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Unexpected content type "
+ "0x%x when decrypting application data",
+ pos[0]);
+ alert = TLS_ALERT_UNEXPECTED_MESSAGE;
+ goto fail;
+ }
+
+ wpabuf_put(buf, olen);
+
+ pos += used;
+ }
+
+ wpabuf_free(conn->partial_input);
+ conn->partial_input = NULL;
+ return buf;
+
+fail:
+ wpabuf_free(buf);
+ wpabuf_free(conn->partial_input);
+ conn->partial_input = NULL;
+ tls_alert(conn, TLS_ALERT_LEVEL_FATAL, alert);
+ return NULL;
+}
+
+
+/**
+ * tlsv1_client_global_init - Initialize TLSv1 client
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function must be called before using any other TLSv1 client functions.
+ */
+int tlsv1_client_global_init(void)
+{
+ return crypto_global_init();
+}
+
+
+/**
+ * tlsv1_client_global_deinit - Deinitialize TLSv1 client
+ *
+ * This function can be used to deinitialize the TLSv1 client that was
+ * initialized by calling tlsv1_client_global_init(). No TLSv1 client functions
+ * can be called after this before calling tlsv1_client_global_init() again.
+ */
+void tlsv1_client_global_deinit(void)
+{
+ crypto_global_deinit();
+}
+
+
+/**
+ * tlsv1_client_init - Initialize TLSv1 client connection
+ * Returns: Pointer to TLSv1 client connection data or %NULL on failure
+ */
+struct tlsv1_client * tlsv1_client_init(void)
+{
+ struct tlsv1_client *conn;
+ size_t count;
+ u16 *suites;
+
+ conn = (struct tlsv1_client *)os_zalloc(sizeof(*conn));
+ if (conn == NULL)
+ return NULL;
+
+ conn->state = CLIENT_HELLO;
+
+ if (tls_verify_hash_init(&conn->verify) < 0) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Failed to initialize verify "
+ "hash");
+ os_free(conn);
+ return NULL;
+ }
+
+ count = 0;
+ suites = conn->cipher_suites;
+ suites[count++] = TLS_RSA_WITH_AES_256_CBC_SHA256;
+ suites[count++] = TLS_RSA_WITH_AES_256_CBC_SHA;
+ suites[count++] = TLS_RSA_WITH_AES_128_CBC_SHA256;
+ suites[count++] = TLS_RSA_WITH_AES_128_CBC_SHA;
+ suites[count++] = TLS_RSA_WITH_3DES_EDE_CBC_SHA;
+ suites[count++] = TLS_RSA_WITH_RC4_128_SHA;
+ suites[count++] = TLS_RSA_WITH_RC4_128_MD5;
+ conn->num_cipher_suites = count;
+
+ conn->rl.tls_version = TLS_VERSION;
+
+ return conn;
+}
+
+
+/**
+ * tlsv1_client_deinit - Deinitialize TLSv1 client connection
+ * @conn: TLSv1 client connection data from tlsv1_client_init()
+ */
+void tlsv1_client_deinit(struct tlsv1_client *conn)
+{
+ crypto_public_key_free(conn->server_rsa_key);
+ tlsv1_record_set_cipher_suite(&conn->rl, TLS_NULL_WITH_NULL_NULL);
+ tlsv1_record_change_write_cipher(&conn->rl);
+ tlsv1_record_change_read_cipher(&conn->rl);
+ tls_verify_hash_free(&conn->verify);
+ os_free(conn->client_hello_ext);
+ tlsv1_client_free_dh(conn);
+ tlsv1_cred_free(conn->cred);
+ wpabuf_free(conn->partial_input);
+ os_free(conn);
+}
+
+
+/**
+ * tlsv1_client_established - Check whether connection has been established
+ * @conn: TLSv1 client connection data from tlsv1_client_init()
+ * Returns: 1 if connection is established, 0 if not
+ */
+int tlsv1_client_established(struct tlsv1_client *conn)
+{
+ return conn->state == ESTABLISHED;
+}
+
+
+/**
+ * tlsv1_client_prf - Use TLS-PRF to derive keying material
+ * @conn: TLSv1 client connection data from tlsv1_client_init()
+ * @label: Label (e.g., description of the key) for PRF
+ * @server_random_first: seed is 0 = client_random|server_random,
+ * 1 = server_random|client_random
+ * @out: Buffer for output data from TLS-PRF
+ * @out_len: Length of the output buffer
+ * Returns: 0 on success, -1 on failure
+ */
+int tlsv1_client_prf(struct tlsv1_client *conn, const char *label,
+ int server_random_first, u8 *out, size_t out_len)
+{
+ u8 seed[2 * TLS_RANDOM_LEN];
+
+ if (conn->state != ESTABLISHED)
+ return -1;
+
+ if (server_random_first) {
+ os_memcpy(seed, conn->server_random, TLS_RANDOM_LEN);
+ os_memcpy(seed + TLS_RANDOM_LEN, conn->client_random,
+ TLS_RANDOM_LEN);
+ } else {
+ os_memcpy(seed, conn->client_random, TLS_RANDOM_LEN);
+ os_memcpy(seed + TLS_RANDOM_LEN, conn->server_random,
+ TLS_RANDOM_LEN);
+ }
+
+ return tls_prf(conn->rl.tls_version,
+ conn->master_secret, TLS_MASTER_SECRET_LEN,
+ label, seed, 2 * TLS_RANDOM_LEN, out, out_len);
+}
+
+
+/**
+ * tlsv1_client_get_cipher - Get current cipher name
+ * @conn: TLSv1 client connection data from tlsv1_client_init()
+ * @buf: Buffer for the cipher name
+ * @buflen: buf size
+ * Returns: 0 on success, -1 on failure
+ *
+ * Get the name of the currently used cipher.
+ */
+int tlsv1_client_get_cipher(struct tlsv1_client *conn, char *buf,
+ size_t buflen)
+{
+#ifndef ESPRESSIF_USE
+ char *cipher;
+
+ switch (conn->rl.cipher_suite) {
+ case TLS_RSA_WITH_RC4_128_MD5:
+ cipher = "RC4-MD5";
+ break;
+ case TLS_RSA_WITH_RC4_128_SHA:
+ cipher = "RC4-SHA";
+ break;
+ case TLS_RSA_WITH_DES_CBC_SHA:
+ cipher = "DES-CBC-SHA";
+ break;
+ case TLS_RSA_WITH_3DES_EDE_CBC_SHA:
+ cipher = "DES-CBC3-SHA";
+ break;
+ case TLS_DH_anon_WITH_AES_128_CBC_SHA256:
+ cipher = "ADH-AES-128-SHA256";
+ break;
+ case TLS_DH_anon_WITH_AES_128_CBC_SHA:
+ cipher = "ADH-AES-128-SHA";
+ break;
+ case TLS_RSA_WITH_AES_256_CBC_SHA:
+ cipher = "AES-256-SHA";
+ break;
+ case TLS_RSA_WITH_AES_256_CBC_SHA256:
+ cipher = "AES-256-SHA256";
+ break;
+ case TLS_RSA_WITH_AES_128_CBC_SHA:
+ cipher = "AES-128-SHA";
+ break;
+ case TLS_RSA_WITH_AES_128_CBC_SHA256:
+ cipher = "AES-128-SHA256";
+ break;
+ default:
+ return -1;
+ }
+
+ os_memcpy((u8 *)buf, (u8 *)cipher, buflen);
+
+ return 0;
+#else
+ char cipher[20];
+
+ switch (conn->rl.cipher_suite) {
+ case TLS_RSA_WITH_RC4_128_MD5:
+ strcpy(cipher, "RC4-MD5");
+ break;
+ case TLS_RSA_WITH_RC4_128_SHA:
+ strcpy(cipher, "RC4-SHA");
+ break;
+ case TLS_RSA_WITH_DES_CBC_SHA:
+ strcpy(cipher, "DES-CBC-SHA");
+ break;
+ case TLS_RSA_WITH_3DES_EDE_CBC_SHA:
+ strcpy(cipher, "DES-CBC3-SHA");
+ break;
+ case TLS_DH_anon_WITH_AES_128_CBC_SHA256:
+ strcpy(cipher, "ADH-AES-128-SHA256");
+ break;
+ case TLS_DH_anon_WITH_AES_128_CBC_SHA:
+ strcpy(cipher, "ADH-AES-128-SHA");
+ break;
+ case TLS_RSA_WITH_AES_256_CBC_SHA:
+ strcpy(cipher, "AES-256-SHA");
+ break;
+ case TLS_RSA_WITH_AES_256_CBC_SHA256:
+ strcpy(cipher, "AES-256-SHA256");
+ break;
+ case TLS_RSA_WITH_AES_128_CBC_SHA:
+ strcpy(cipher, "AES-128-SHA");
+ break;
+ case TLS_RSA_WITH_AES_128_CBC_SHA256:
+ strcpy(cipher, "AES-128-SHA256");
+ break;
+ default:
+ return -1;
+ }
+
+ os_memcpy((u8 *)buf, (u8 *)cipher, buflen);
+
+ return 0;
+#endif
+}
+
+
+/**
+ * tlsv1_client_shutdown - Shutdown TLS connection
+ * @conn: TLSv1 client connection data from tlsv1_client_init()
+ * Returns: 0 on success, -1 on failure
+ */
+int tlsv1_client_shutdown(struct tlsv1_client *conn)
+{
+ conn->state = CLIENT_HELLO;
+
+ if (tls_verify_hash_init(&conn->verify) < 0) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Failed to re-initialize verify "
+ "hash");
+ return -1;
+ }
+
+ tlsv1_record_set_cipher_suite(&conn->rl, TLS_NULL_WITH_NULL_NULL);
+ tlsv1_record_change_write_cipher(&conn->rl);
+ tlsv1_record_change_read_cipher(&conn->rl);
+
+ conn->certificate_requested = 0;
+ crypto_public_key_free(conn->server_rsa_key);
+ conn->server_rsa_key = NULL;
+ conn->session_resumed = 0;
+
+ return 0;
+}
+
+
+/**
+ * tlsv1_client_resumed - Was session resumption used
+ * @conn: TLSv1 client connection data from tlsv1_client_init()
+ * Returns: 1 if current session used session resumption, 0 if not
+ */
+int tlsv1_client_resumed(struct tlsv1_client *conn)
+{
+ return !!conn->session_resumed;
+}
+
+
+/**
+ * tlsv1_client_hello_ext - Set TLS extension for ClientHello
+ * @conn: TLSv1 client connection data from tlsv1_client_init()
+ * @ext_type: Extension type
+ * @data: Extension payload (%NULL to remove extension)
+ * @data_len: Extension payload length
+ * Returns: 0 on success, -1 on failure
+ */
+int tlsv1_client_hello_ext(struct tlsv1_client *conn, int ext_type,
+ const u8 *data, size_t data_len)
+{
+ u8 *pos;
+
+ conn->session_ticket_included = 0;
+ os_free(conn->client_hello_ext);
+ conn->client_hello_ext = NULL;
+ conn->client_hello_ext_len = 0;
+
+ if (data == NULL || data_len == 0)
+ return 0;
+
+ pos = conn->client_hello_ext = os_malloc(6 + data_len);
+ if (pos == NULL)
+ return -1;
+
+ WPA_PUT_BE16(pos, 4 + data_len);
+ pos += 2;
+ WPA_PUT_BE16(pos, ext_type);
+ pos += 2;
+ WPA_PUT_BE16(pos, data_len);
+ pos += 2;
+ os_memcpy(pos, data, data_len);
+ conn->client_hello_ext_len = 6 + data_len;
+
+ if (ext_type == TLS_EXT_PAC_OPAQUE) {
+ conn->session_ticket_included = 1;
+ wpa_printf(MSG_DEBUG, "TLSv1: Using session ticket");
+ }
+
+ return 0;
+}
+
+
+/**
+ * tlsv1_client_get_keys - Get master key and random data from TLS connection
+ * @conn: TLSv1 client connection data from tlsv1_client_init()
+ * @keys: Structure of key/random data (filled on success)
+ * Returns: 0 on success, -1 on failure
+ */
+int tlsv1_client_get_keys(struct tlsv1_client *conn, struct tls_keys *keys)
+{
+ os_memset(keys, 0, sizeof(*keys));
+ if (conn->state == CLIENT_HELLO)
+ return -1;
+
+ keys->client_random = conn->client_random;
+ keys->client_random_len = TLS_RANDOM_LEN;
+
+ if (conn->state != SERVER_HELLO) {
+ keys->server_random = conn->server_random;
+ keys->server_random_len = TLS_RANDOM_LEN;
+ keys->master_key = conn->master_secret;
+ keys->master_key_len = TLS_MASTER_SECRET_LEN;
+ }
+
+ return 0;
+}
+
+
+/**
+ * tlsv1_client_get_keyblock_size - Get TLS key_block size
+ * @conn: TLSv1 client connection data from tlsv1_client_init()
+ * Returns: Size of the key_block for the negotiated cipher suite or -1 on
+ * failure
+ */
+int tlsv1_client_get_keyblock_size(struct tlsv1_client *conn)
+{
+ if (conn->state == CLIENT_HELLO || conn->state == SERVER_HELLO)
+ return -1;
+
+ return 2 * (conn->rl.hash_size + conn->rl.key_material_len +
+ conn->rl.iv_size);
+}
+
+
+/**
+ * tlsv1_client_set_cipher_list - Configure acceptable cipher suites
+ * @conn: TLSv1 client connection data from tlsv1_client_init()
+ * @ciphers: Zero (TLS_CIPHER_NONE) terminated list of allowed ciphers
+ * (TLS_CIPHER_*).
+ * Returns: 0 on success, -1 on failure
+ */
+int tlsv1_client_set_cipher_list(struct tlsv1_client *conn, u8 *ciphers)
+{
+ size_t count;
+ u16 *suites;
+
+ /* TODO: implement proper configuration of cipher suites */
+ if (ciphers[0] == TLS_CIPHER_ANON_DH_AES128_SHA) {
+ count = 0;
+ suites = conn->cipher_suites;
+ suites[count++] = TLS_DH_anon_WITH_AES_256_CBC_SHA256;
+ suites[count++] = TLS_DH_anon_WITH_AES_256_CBC_SHA;
+ suites[count++] = TLS_DH_anon_WITH_AES_128_CBC_SHA256;
+ suites[count++] = TLS_DH_anon_WITH_AES_128_CBC_SHA;
+ suites[count++] = TLS_DH_anon_WITH_3DES_EDE_CBC_SHA;
+ suites[count++] = TLS_DH_anon_WITH_RC4_128_MD5;
+ suites[count++] = TLS_DH_anon_WITH_DES_CBC_SHA;
+
+ /*
+ * Cisco AP (at least 350 and 1200 series) local authentication
+ * server does not know how to search cipher suites from the
+ * list and seem to require that the last entry in the list is
+ * the one that it wants to use. However, TLS specification
+ * requires the list to be in the client preference order. As a
+ * workaround, add anon-DH AES-128-SHA1 again at the end of the
+ * list to allow the Cisco code to find it.
+ */
+ suites[count++] = TLS_DH_anon_WITH_AES_128_CBC_SHA;
+ conn->num_cipher_suites = count;
+ }
+
+ return 0;
+}
+
+
+/**
+ * tlsv1_client_set_cred - Set client credentials
+ * @conn: TLSv1 client connection data from tlsv1_client_init()
+ * @cred: Credentials from tlsv1_cred_alloc()
+ * Returns: 0 on success, -1 on failure
+ *
+ * On success, the client takes ownership of the credentials block and caller
+ * must not free it. On failure, caller is responsible for freeing the
+ * credential block.
+ */
+int tlsv1_client_set_cred(struct tlsv1_client *conn,
+ struct tlsv1_credentials *cred)
+{
+ tlsv1_cred_free(conn->cred);
+ conn->cred = cred;
+ return 0;
+}
+
+
+void tlsv1_client_set_time_checks(struct tlsv1_client *conn, int enabled)
+{
+ conn->disable_time_checks = !enabled;
+}
+
+
+void tlsv1_client_set_session_ticket_cb(struct tlsv1_client *conn,
+ tlsv1_client_session_ticket_cb cb,
+ void *ctx)
+{
+ wpa_printf(MSG_DEBUG, "TLSv1: SessionTicket callback set %p (ctx %p)",
+ cb, ctx);
+ conn->session_ticket_cb = cb;
+ conn->session_ticket_cb_ctx = ctx;
+}
--- /dev/null
+/*
+ * TLSv1 client - read handshake message
+ * Copyright (c) 2006-2011, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "wpa/includes.h"
+
+#include "wpa/common.h"
+#include "crypto/md5.h"
+#include "crypto/sha1.h"
+#include "crypto/sha256.h"
+#include "wpa2/tls/tls.h"
+#include "wpa2/tls/x509v3.h"
+#include "wpa2/tls/tlsv1_common.h"
+#include "wpa2/tls/tlsv1_record.h"
+#include "wpa2/tls/tlsv1_client.h"
+#include "wpa2/tls/tlsv1_client_i.h"
+#include "wpa2/eap_peer/eap_i.h"
+
+static int tls_process_server_key_exchange(struct tlsv1_client *conn, u8 ct,
+ const u8 *in_data, size_t *in_len);
+static int tls_process_certificate_request(struct tlsv1_client *conn, u8 ct,
+ const u8 *in_data, size_t *in_len);
+static int tls_process_server_hello_done(struct tlsv1_client *conn, u8 ct,
+ const u8 *in_data, size_t *in_len);
+
+
+static int tls_process_server_hello(struct tlsv1_client *conn, u8 ct,
+ const u8 *in_data, size_t *in_len)
+{
+ const u8 *pos, *end;
+ size_t left, len, i;
+ u16 cipher_suite;
+ u16 tls_version;
+
+ if (ct != TLS_CONTENT_TYPE_HANDSHAKE) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Expected Handshake; "
+ "received content type 0x%x", ct);
+ tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_UNEXPECTED_MESSAGE);
+ return -1;
+ }
+
+ pos = in_data;
+ left = *in_len;
+
+ if (left < 4)
+ goto decode_error;
+
+ /* HandshakeType msg_type */
+ if (*pos != TLS_HANDSHAKE_TYPE_SERVER_HELLO) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Received unexpected handshake "
+ "message %d (expected ServerHello)", *pos);
+ tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_UNEXPECTED_MESSAGE);
+ return -1;
+ }
+ wpa_printf(MSG_DEBUG, "TLSv1: Received ServerHello");
+ pos++;
+ /* uint24 length */
+ len = WPA_GET_BE24(pos);
+ pos += 3;
+ left -= 4;
+
+ if (len > left)
+ goto decode_error;
+
+ /* body - ServerHello */
+
+ wpa_hexdump(MSG_MSGDUMP, "TLSv1: ServerHello", pos, len);
+ end = pos + len;
+
+ /* ProtocolVersion server_version */
+ if (end - pos < 2)
+ goto decode_error;
+ tls_version = WPA_GET_BE16(pos);
+ if (!tls_version_ok(tls_version)) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Unexpected protocol version in "
+ "ServerHello %u.%u", pos[0], pos[1]);
+ tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_PROTOCOL_VERSION);
+ return -1;
+ }
+ pos += 2;
+
+ wpa_printf(MSG_DEBUG, "TLSv1: Using TLS v%s",
+ tls_version_str(tls_version));
+ conn->rl.tls_version = tls_version;
+
+ /* Random random */
+ if (end - pos < TLS_RANDOM_LEN)
+ goto decode_error;
+
+ os_memcpy(conn->server_random, pos, TLS_RANDOM_LEN);
+ pos += TLS_RANDOM_LEN;
+ wpa_hexdump(MSG_MSGDUMP, "TLSv1: server_random",
+ conn->server_random, TLS_RANDOM_LEN);
+
+ /* SessionID session_id */
+ if (end - pos < 1)
+ goto decode_error;
+ if (end - pos < 1 + *pos || *pos > TLS_SESSION_ID_MAX_LEN)
+ goto decode_error;
+ if (conn->session_id_len && conn->session_id_len == *pos &&
+ os_memcmp(conn->session_id, pos + 1, conn->session_id_len) == 0) {
+ pos += 1 + conn->session_id_len;
+ wpa_printf(MSG_DEBUG, "TLSv1: Resuming old session");
+ conn->session_resumed = 1;
+ } else {
+ conn->session_id_len = *pos;
+ pos++;
+ os_memcpy(conn->session_id, pos, conn->session_id_len);
+ pos += conn->session_id_len;
+ }
+ wpa_hexdump(MSG_MSGDUMP, "TLSv1: session_id",
+ conn->session_id, conn->session_id_len);
+
+ /* CipherSuite cipher_suite */
+ if (end - pos < 2)
+ goto decode_error;
+ cipher_suite = WPA_GET_BE16(pos);
+ pos += 2;
+ for (i = 0; i < conn->num_cipher_suites; i++) {
+ if (cipher_suite == conn->cipher_suites[i])
+ break;
+ }
+ if (i == conn->num_cipher_suites) {
+ wpa_printf(MSG_INFO, "TLSv1: Server selected unexpected "
+ "cipher suite 0x%04x", cipher_suite);
+ tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_ILLEGAL_PARAMETER);
+ return -1;
+ }
+
+ if (conn->session_resumed && cipher_suite != conn->prev_cipher_suite) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Server selected a different "
+ "cipher suite for a resumed connection (0x%04x != "
+ "0x%04x)", cipher_suite, conn->prev_cipher_suite);
+ tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_ILLEGAL_PARAMETER);
+ return -1;
+ }
+
+ if (tlsv1_record_set_cipher_suite(&conn->rl, cipher_suite) < 0) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Failed to set CipherSuite for "
+ "record layer");
+ tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_INTERNAL_ERROR);
+ return -1;
+ }
+
+ conn->prev_cipher_suite = cipher_suite;
+
+ /* CompressionMethod compression_method */
+ if (end - pos < 1)
+ goto decode_error;
+ if (*pos != TLS_COMPRESSION_NULL) {
+ wpa_printf(MSG_INFO, "TLSv1: Server selected unexpected "
+ "compression 0x%02x", *pos);
+ tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_ILLEGAL_PARAMETER);
+ return -1;
+ }
+ pos++;
+
+ if (end != pos) {
+ /* TODO: ServerHello extensions */
+ wpa_hexdump(MSG_DEBUG, "TLSv1: Unexpected extra data in the "
+ "end of ServerHello", pos, end - pos);
+ goto decode_error;
+ }
+
+ if (conn->session_ticket_included && conn->session_ticket_cb) {
+ /* TODO: include SessionTicket extension if one was included in
+ * ServerHello */
+ int res = conn->session_ticket_cb(
+ conn->session_ticket_cb_ctx, NULL, 0,
+ conn->client_random, conn->server_random,
+ conn->master_secret);
+ if (res < 0) {
+ wpa_printf(MSG_DEBUG, "TLSv1: SessionTicket callback "
+ "indicated failure");
+ tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_HANDSHAKE_FAILURE);
+ return -1;
+ }
+ conn->use_session_ticket = !!res;
+ }
+
+ if ((conn->session_resumed || conn->use_session_ticket) &&
+ tls_derive_keys(conn, NULL, 0)) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Failed to derive keys");
+ tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_INTERNAL_ERROR);
+ return -1;
+ }
+
+ *in_len = end - in_data;
+
+ conn->state = (conn->session_resumed || conn->use_session_ticket) ?
+ SERVER_CHANGE_CIPHER_SPEC : SERVER_CERTIFICATE;
+
+ return 0;
+
+decode_error:
+ wpa_printf(MSG_DEBUG, "TLSv1: Failed to decode ServerHello");
+ tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR);
+ return -1;
+}
+
+
+static int tls_process_certificate(struct tlsv1_client *conn, u8 ct,
+ const u8 *in_data, size_t *in_len)
+{
+ const u8 *pos, *end;
+ size_t left, len, list_len, cert_len, idx;
+ u8 type;
+ struct x509_certificate *chain = NULL, *last = NULL, *cert;
+ int reason;
+
+ if (ct != TLS_CONTENT_TYPE_HANDSHAKE) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Expected Handshake; "
+ "received content type 0x%x", ct);
+ tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_UNEXPECTED_MESSAGE);
+ return -1;
+ }
+
+ pos = in_data;
+ left = *in_len;
+
+ if (left < 4) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Too short Certificate message "
+ "(len=%lu)", (unsigned long) left);
+ tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR);
+ return -1;
+ }
+
+ type = *pos++;
+ len = WPA_GET_BE24(pos);
+ pos += 3;
+ left -= 4;
+
+ if (len > left) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Unexpected Certificate message "
+ "length (len=%lu != left=%lu)",
+ (unsigned long) len, (unsigned long) left);
+ tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR);
+ return -1;
+ }
+
+ if (type == TLS_HANDSHAKE_TYPE_SERVER_KEY_EXCHANGE)
+ return tls_process_server_key_exchange(conn, ct, in_data,
+ in_len);
+ if (type == TLS_HANDSHAKE_TYPE_CERTIFICATE_REQUEST)
+ return tls_process_certificate_request(conn, ct, in_data,
+ in_len);
+ if (type == TLS_HANDSHAKE_TYPE_SERVER_HELLO_DONE)
+ return tls_process_server_hello_done(conn, ct, in_data,
+ in_len);
+ if (type != TLS_HANDSHAKE_TYPE_CERTIFICATE) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Received unexpected handshake "
+ "message %d (expected Certificate/"
+ "ServerKeyExchange/CertificateRequest/"
+ "ServerHelloDone)", type);
+ tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_UNEXPECTED_MESSAGE);
+ return -1;
+ }
+
+ wpa_printf(MSG_DEBUG, "TLSv1: Received Certificate (certificate_list len %lu)",
+ (unsigned long) len);
+
+ /*
+ * opaque ASN.1Cert<2^24-1>;
+ *
+ * struct {
+ * ASN.1Cert certificate_list<1..2^24-1>;
+ * } Certificate;
+ */
+
+ end = pos + len;
+
+ if (end - pos < 3) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Too short Certificate "
+ "(left=%lu)", (unsigned long) left);
+ tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR);
+ return -1;
+ }
+
+ list_len = WPA_GET_BE24(pos);
+ pos += 3;
+
+ if ((size_t) (end - pos) != list_len) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Unexpected certificate_list "
+ "length (len=%lu left=%lu)",
+ (unsigned long) list_len,
+ (unsigned long) (end - pos));
+ tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR);
+ return -1;
+ }
+
+ idx = 0;
+ while (pos < end) {
+ if (end - pos < 3) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Failed to parse "
+ "certificate_list");
+ tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_DECODE_ERROR);
+ x509_certificate_chain_free(chain);
+ return -1;
+ }
+
+ cert_len = WPA_GET_BE24(pos);
+ pos += 3;
+
+ if ((size_t) (end - pos) < cert_len) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Unexpected certificate "
+ "length (len=%lu left=%lu)",
+ (unsigned long) cert_len,
+ (unsigned long) (end - pos));
+ tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_DECODE_ERROR);
+ x509_certificate_chain_free(chain);
+ return -1;
+ }
+
+ wpa_printf(MSG_DEBUG, "TLSv1: Certificate %lu (len %lu)",
+ (unsigned long) idx, (unsigned long) cert_len);
+
+ if (idx == 0) {
+ crypto_public_key_free(conn->server_rsa_key);
+ if (tls_parse_cert(pos, cert_len,
+ &conn->server_rsa_key)) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Failed to parse "
+ "the certificate");
+ tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_BAD_CERTIFICATE);
+ x509_certificate_chain_free(chain);
+ return -1;
+ }
+ }
+
+ cert = x509_certificate_parse(pos, cert_len);
+ if (cert == NULL) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Failed to parse "
+ "the certificate");
+ tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_BAD_CERTIFICATE);
+ x509_certificate_chain_free(chain);
+ return -1;
+ }
+
+ if (last == NULL)
+ chain = cert;
+ else
+ last->next = cert;
+ last = cert;
+
+ idx++;
+ pos += cert_len;
+ }
+
+ if (conn->cred &&
+ x509_certificate_chain_validate(conn->cred->trusted_certs, chain,
+ &reason, conn->disable_time_checks)
+ < 0) {
+ int tls_reason;
+ wpa_printf(MSG_DEBUG, "TLSv1: Server certificate chain "
+ "validation failed (reason=%d)", reason);
+ switch (reason) {
+ case X509_VALIDATE_BAD_CERTIFICATE:
+ tls_reason = TLS_ALERT_BAD_CERTIFICATE;
+ break;
+ case X509_VALIDATE_UNSUPPORTED_CERTIFICATE:
+ tls_reason = TLS_ALERT_UNSUPPORTED_CERTIFICATE;
+ break;
+ case X509_VALIDATE_CERTIFICATE_REVOKED:
+ tls_reason = TLS_ALERT_CERTIFICATE_REVOKED;
+ break;
+ case X509_VALIDATE_CERTIFICATE_EXPIRED:
+ tls_reason = TLS_ALERT_CERTIFICATE_EXPIRED;
+ break;
+ case X509_VALIDATE_CERTIFICATE_UNKNOWN:
+ tls_reason = TLS_ALERT_CERTIFICATE_UNKNOWN;
+ break;
+ case X509_VALIDATE_UNKNOWN_CA:
+ tls_reason = TLS_ALERT_UNKNOWN_CA;
+ break;
+ default:
+ tls_reason = TLS_ALERT_BAD_CERTIFICATE;
+ break;
+ }
+ tls_alert(conn, TLS_ALERT_LEVEL_FATAL, tls_reason);
+ x509_certificate_chain_free(chain);
+ return -1;
+ }
+
+ x509_certificate_chain_free(chain);
+
+ *in_len = end - in_data;
+ conn->state = SERVER_KEY_EXCHANGE;
+
+ return 0;
+}
+
+
+static int tlsv1_process_diffie_hellman(struct tlsv1_client *conn,
+ const u8 *buf, size_t len)
+{
+ const u8 *pos, *end;
+
+ tlsv1_client_free_dh(conn);
+
+ pos = buf;
+ end = buf + len;
+
+ if (end - pos < 3)
+ goto fail;
+ conn->dh_p_len = WPA_GET_BE16(pos);
+ pos += 2;
+ if (conn->dh_p_len == 0 || end - pos < (int) conn->dh_p_len) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Invalid dh_p length %lu",
+ (unsigned long) conn->dh_p_len);
+ goto fail;
+ }
+ conn->dh_p = os_malloc(conn->dh_p_len);
+ if (conn->dh_p == NULL)
+ goto fail;
+ os_memcpy(conn->dh_p, pos, conn->dh_p_len);
+ pos += conn->dh_p_len;
+ wpa_hexdump(MSG_DEBUG, "TLSv1: DH p (prime)",
+ conn->dh_p, conn->dh_p_len);
+
+ if (end - pos < 3)
+ goto fail;
+ conn->dh_g_len = WPA_GET_BE16(pos);
+ pos += 2;
+ if (conn->dh_g_len == 0 || end - pos < (int) conn->dh_g_len)
+ goto fail;
+ conn->dh_g = os_malloc(conn->dh_g_len);
+ if (conn->dh_g == NULL)
+ goto fail;
+ os_memcpy(conn->dh_g, pos, conn->dh_g_len);
+ pos += conn->dh_g_len;
+ wpa_hexdump(MSG_DEBUG, "TLSv1: DH g (generator)",
+ conn->dh_g, conn->dh_g_len);
+ if (conn->dh_g_len == 1 && conn->dh_g[0] < 2)
+ goto fail;
+
+ if (end - pos < 3)
+ goto fail;
+ conn->dh_ys_len = WPA_GET_BE16(pos);
+ pos += 2;
+ if (conn->dh_ys_len == 0 || end - pos < (int) conn->dh_ys_len)
+ goto fail;
+ conn->dh_ys = os_malloc(conn->dh_ys_len);
+ if (conn->dh_ys == NULL)
+ goto fail;
+ os_memcpy(conn->dh_ys, pos, conn->dh_ys_len);
+ pos += conn->dh_ys_len;
+ wpa_hexdump(MSG_DEBUG, "TLSv1: DH Ys (server's public value)",
+ conn->dh_ys, conn->dh_ys_len);
+
+ return 0;
+
+fail:
+ wpa_printf(MSG_DEBUG, "TLSv1: Processing DH params failed");
+ tlsv1_client_free_dh(conn);
+ return -1;
+}
+
+
+static int tls_process_server_key_exchange(struct tlsv1_client *conn, u8 ct,
+ const u8 *in_data, size_t *in_len)
+{
+ const u8 *pos, *end;
+ size_t left, len;
+ u8 type;
+ const struct tls_cipher_suite *suite;
+
+ if (ct != TLS_CONTENT_TYPE_HANDSHAKE) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Expected Handshake; "
+ "received content type 0x%x", ct);
+ tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_UNEXPECTED_MESSAGE);
+ return -1;
+ }
+
+ pos = in_data;
+ left = *in_len;
+
+ if (left < 4) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Too short ServerKeyExchange "
+ "(Left=%lu)", (unsigned long) left);
+ tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR);
+ return -1;
+ }
+
+ type = *pos++;
+ len = WPA_GET_BE24(pos);
+ pos += 3;
+ left -= 4;
+
+ if (len > left) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Mismatch in ServerKeyExchange "
+ "length (len=%lu != left=%lu)",
+ (unsigned long) len, (unsigned long) left);
+ tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR);
+ return -1;
+ }
+
+ end = pos + len;
+
+ if (type == TLS_HANDSHAKE_TYPE_CERTIFICATE_REQUEST)
+ return tls_process_certificate_request(conn, ct, in_data,
+ in_len);
+ if (type == TLS_HANDSHAKE_TYPE_SERVER_HELLO_DONE)
+ return tls_process_server_hello_done(conn, ct, in_data,
+ in_len);
+ if (type != TLS_HANDSHAKE_TYPE_SERVER_KEY_EXCHANGE) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Received unexpected handshake "
+ "message %d (expected ServerKeyExchange/"
+ "CertificateRequest/ServerHelloDone)", type);
+ tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_UNEXPECTED_MESSAGE);
+ return -1;
+ }
+
+ wpa_printf(MSG_DEBUG, "TLSv1: Received ServerKeyExchange");
+
+ if (!tls_server_key_exchange_allowed(conn->rl.cipher_suite)) {
+ wpa_printf(MSG_DEBUG, "TLSv1: ServerKeyExchange not allowed "
+ "with the selected cipher suite");
+ tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_UNEXPECTED_MESSAGE);
+ return -1;
+ }
+
+ wpa_hexdump(MSG_DEBUG, "TLSv1: ServerKeyExchange", pos, len);
+ suite = tls_get_cipher_suite(conn->rl.cipher_suite);
+ if (suite && suite->key_exchange == TLS_KEY_X_DH_anon) {
+ if (tlsv1_process_diffie_hellman(conn, pos, len) < 0) {
+ tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_DECODE_ERROR);
+ return -1;
+ }
+ } else {
+ wpa_printf(MSG_DEBUG, "TLSv1: UnexpectedServerKeyExchange");
+ tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_UNEXPECTED_MESSAGE);
+ return -1;
+ }
+
+ *in_len = end - in_data;
+ conn->state = SERVER_CERTIFICATE_REQUEST;
+
+ return 0;
+}
+
+
+static int tls_process_certificate_request(struct tlsv1_client *conn, u8 ct,
+ const u8 *in_data, size_t *in_len)
+{
+ const u8 *pos, *end;
+ size_t left, len;
+ u8 type;
+
+ if (ct != TLS_CONTENT_TYPE_HANDSHAKE) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Expected Handshake; "
+ "received content type 0x%x", ct);
+ tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_UNEXPECTED_MESSAGE);
+ return -1;
+ }
+
+ pos = in_data;
+ left = *in_len;
+
+ if (left < 4) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Too short CertificateRequest "
+ "(left=%lu)", (unsigned long) left);
+ tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR);
+ return -1;
+ }
+
+ type = *pos++;
+ len = WPA_GET_BE24(pos);
+ pos += 3;
+ left -= 4;
+
+ if (len > left) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Mismatch in CertificateRequest "
+ "length (len=%lu != left=%lu)",
+ (unsigned long) len, (unsigned long) left);
+ tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR);
+ return -1;
+ }
+
+ end = pos + len;
+
+ if (type == TLS_HANDSHAKE_TYPE_SERVER_HELLO_DONE)
+ return tls_process_server_hello_done(conn, ct, in_data,
+ in_len);
+ if (type != TLS_HANDSHAKE_TYPE_CERTIFICATE_REQUEST) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Received unexpected handshake "
+ "message %d (expected CertificateRequest/"
+ "ServerHelloDone)", type);
+ tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_UNEXPECTED_MESSAGE);
+ return -1;
+ }
+
+ wpa_printf(MSG_DEBUG, "TLSv1: Received CertificateRequest");
+
+ conn->certificate_requested = 1;
+
+ *in_len = end - in_data;
+ conn->state = SERVER_HELLO_DONE;
+
+ return 0;
+}
+
+
+static int tls_process_server_hello_done(struct tlsv1_client *conn, u8 ct,
+ const u8 *in_data, size_t *in_len)
+{
+ const u8 *pos, *end;
+ size_t left, len;
+ u8 type;
+
+ if (ct != TLS_CONTENT_TYPE_HANDSHAKE) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Expected Handshake; "
+ "received content type 0x%x", ct);
+ tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_UNEXPECTED_MESSAGE);
+ return -1;
+ }
+
+ pos = in_data;
+ left = *in_len;
+
+ if (left < 4) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Too short ServerHelloDone "
+ "(left=%lu)", (unsigned long) left);
+ tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR);
+ return -1;
+ }
+
+ type = *pos++;
+ len = WPA_GET_BE24(pos);
+ pos += 3;
+ left -= 4;
+
+ if (len > left) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Mismatch in ServerHelloDone "
+ "length (len=%lu != left=%lu)",
+ (unsigned long) len, (unsigned long) left);
+ tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR);
+ return -1;
+ }
+ end = pos + len;
+
+ if (type != TLS_HANDSHAKE_TYPE_SERVER_HELLO_DONE) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Received unexpected handshake "
+ "message %d (expected ServerHelloDone)", type);
+ tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_UNEXPECTED_MESSAGE);
+ return -1;
+ }
+
+ wpa_printf(MSG_DEBUG, "TLSv1: Received ServerHelloDone");
+
+ *in_len = end - in_data;
+ conn->state = CLIENT_KEY_EXCHANGE;
+
+ return 0;
+}
+
+
+static int tls_process_server_change_cipher_spec(struct tlsv1_client *conn,
+ u8 ct, const u8 *in_data,
+ size_t *in_len)
+{
+ const u8 *pos;
+ size_t left;
+
+ if (ct != TLS_CONTENT_TYPE_CHANGE_CIPHER_SPEC) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Expected ChangeCipherSpec; "
+ "received content type 0x%x", ct);
+ if (conn->use_session_ticket) {
+ int res;
+ wpa_printf(MSG_DEBUG, "TLSv1: Server may have "
+ "rejected SessionTicket");
+ conn->use_session_ticket = 0;
+
+ /* Notify upper layers that SessionTicket failed */
+ res = conn->session_ticket_cb(
+ conn->session_ticket_cb_ctx, NULL, 0, NULL,
+ NULL, NULL);
+ if (res < 0) {
+ wpa_printf(MSG_DEBUG, "TLSv1: SessionTicket "
+ "callback indicated failure");
+ tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_HANDSHAKE_FAILURE);
+ return -1;
+ }
+ printf("[Debug] set the state to server certificate \n");
+ conn->state = SERVER_CERTIFICATE;
+ return tls_process_certificate(conn, ct, in_data,
+ in_len);
+ }
+ tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_UNEXPECTED_MESSAGE);
+ return -1;
+ }
+
+ pos = in_data;
+ left = *in_len;
+
+ if (left < 1) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Too short ChangeCipherSpec");
+ tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR);
+ return -1;
+ }
+
+ if (*pos != TLS_CHANGE_CIPHER_SPEC) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Expected ChangeCipherSpec; "
+ "received data 0x%x", *pos);
+ tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_UNEXPECTED_MESSAGE);
+ return -1;
+ }
+
+ wpa_printf(MSG_DEBUG, "TLSv1: Received ChangeCipherSpec");
+ if (tlsv1_record_change_read_cipher(&conn->rl) < 0) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Failed to change read cipher "
+ "for record layer");
+ tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_INTERNAL_ERROR);
+ return -1;
+ }
+
+ *in_len = pos + 1 - in_data;
+ conn->state = SERVER_FINISHED;
+
+ return 0;
+}
+
+
+static int tls_process_server_finished(struct tlsv1_client *conn, u8 ct,
+ const u8 *in_data, size_t *in_len)
+{
+ const u8 *pos, *end;
+ size_t left, len, hlen;
+ u8 verify_data[TLS_VERIFY_DATA_LEN];
+ u8 hash[MD5_MAC_LEN + SHA1_MAC_LEN];
+
+ if (ct != TLS_CONTENT_TYPE_HANDSHAKE) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Expected Finished; "
+ "received content type 0x%x", ct);
+ tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_UNEXPECTED_MESSAGE);
+ return -1;
+ }
+
+ pos = in_data;
+ left = *in_len;
+
+ if (left < 4) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Too short record (left=%lu) for "
+ "Finished",
+ (unsigned long) left);
+ tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_DECODE_ERROR);
+ return -1;
+ }
+
+ if (pos[0] != TLS_HANDSHAKE_TYPE_FINISHED) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Expected Finished; received "
+ "type 0x%x", pos[0]);
+ tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_UNEXPECTED_MESSAGE);
+ return -1;
+ }
+
+ len = WPA_GET_BE24(pos + 1);
+
+ pos += 4;
+ left -= 4;
+
+ if (len > left) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Too short buffer for Finished "
+ "(len=%lu > left=%lu)",
+ (unsigned long) len, (unsigned long) left);
+ tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_DECODE_ERROR);
+ return -1;
+ }
+ end = pos + len;
+ if (len != TLS_VERIFY_DATA_LEN) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Unexpected verify_data length "
+ "in Finished: %lu (expected %d)",
+ (unsigned long) len, TLS_VERIFY_DATA_LEN);
+ tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_DECODE_ERROR);
+ return -1;
+ }
+ wpa_hexdump(MSG_MSGDUMP, "TLSv1: verify_data in Finished",
+ pos, TLS_VERIFY_DATA_LEN);
+
+#ifdef CONFIG_TLSV12
+ if (conn->rl.tls_version >= TLS_VERSION_1_2) {
+ hlen = SHA256_MAC_LEN;
+ if (wpa2_crypto_funcs.crypto_hash_finish) {
+ if (conn->verify.sha256_server == NULL ||
+ wpa_crypto_funcs.crypto_hash_finish(conn->verify.sha256_server, hash, &hlen) < 0) {
+ tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_INTERNAL_ERROR);
+ conn->verify.sha256_server = NULL;
+ return -1;
+ }
+ } else {
+ wpa_printf(MSG_ERROR, "In function %s, fail to register crypto_hash_finish function!\r\n", __FUNCTION__);
+ tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_INTERNAL_ERROR);
+ conn->verify.sha256_server = NULL;
+ return -1;
+ }
+ conn->verify.sha256_server = NULL;
+ } else {
+#endif /* CONFIG_TLSV12 */
+
+ hlen = MD5_MAC_LEN;
+ if (conn->verify.md5_server == NULL ||
+ crypto_hash_finish(conn->verify.md5_server, hash, &hlen) < 0) {
+ tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_INTERNAL_ERROR);
+ conn->verify.md5_server = NULL;
+ crypto_hash_finish(conn->verify.sha1_server, NULL, NULL);
+ conn->verify.sha1_server = NULL;
+ return -1;
+ }
+ conn->verify.md5_server = NULL;
+ hlen = SHA1_MAC_LEN;
+ if (conn->verify.sha1_server == NULL ||
+ crypto_hash_finish(conn->verify.sha1_server, hash + MD5_MAC_LEN,
+ &hlen) < 0) {
+ conn->verify.sha1_server = NULL;
+ tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_INTERNAL_ERROR);
+ return -1;
+ }
+ conn->verify.sha1_server = NULL;
+ hlen = MD5_MAC_LEN + SHA1_MAC_LEN;
+
+#ifdef CONFIG_TLSV12
+ }
+#endif /* CONFIG_TLSV12 */
+
+ if (tls_prf(conn->rl.tls_version,
+ conn->master_secret, TLS_MASTER_SECRET_LEN,
+ "server finished", hash, hlen,
+ verify_data, TLS_VERIFY_DATA_LEN)) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Failed to derive verify_data");
+ tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_DECRYPT_ERROR);
+ return -1;
+ }
+ wpa_hexdump_key(MSG_DEBUG, "TLSv1: verify_data (server)",
+ verify_data, TLS_VERIFY_DATA_LEN);
+
+ if (os_memcmp(pos, verify_data, TLS_VERIFY_DATA_LEN) != 0) {
+ wpa_printf(MSG_INFO, "TLSv1: Mismatch in verify_data");
+ return -1;
+ }
+
+ wpa_printf(MSG_DEBUG, "TLSv1: Received Finished");
+
+ *in_len = end - in_data;
+
+ conn->state = (conn->session_resumed || conn->use_session_ticket) ?
+ CHANGE_CIPHER_SPEC : ACK_FINISHED;
+ return 0;
+}
+
+
+static int tls_process_application_data(struct tlsv1_client *conn, u8 ct,
+ const u8 *in_data, size_t *in_len,
+ u8 **out_data, size_t *out_len)
+{
+ const u8 *pos;
+ size_t left;
+
+ if (ct != TLS_CONTENT_TYPE_APPLICATION_DATA) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Expected Application Data; "
+ "received content type 0x%x", ct);
+ tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_UNEXPECTED_MESSAGE);
+ return -1;
+ }
+
+ pos = in_data;
+ left = *in_len;
+
+ wpa_hexdump(MSG_DEBUG, "TLSv1: Application Data included in Handshake",
+ pos, left);
+
+ *out_data = os_malloc(left);
+ if (*out_data) {
+ os_memcpy(*out_data, pos, left);
+ *out_len = left;
+ }
+
+ return 0;
+}
+
+
+int tlsv1_client_process_handshake(struct tlsv1_client *conn, u8 ct,
+ const u8 *buf, size_t *len,
+ u8 **out_data, size_t *out_len)
+{
+ if (ct == TLS_CONTENT_TYPE_ALERT) {
+ if (*len < 2) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Alert underflow");
+ tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_DECODE_ERROR);
+ return -1;
+ }
+ wpa_printf(MSG_DEBUG, "TLSv1: Received alert %d:%d",
+ buf[0], buf[1]);
+ *len = 2;
+ conn->state = FAILED;
+ return -1;
+ }
+
+ if (ct == TLS_CONTENT_TYPE_HANDSHAKE && *len >= 4 &&
+ buf[0] == TLS_HANDSHAKE_TYPE_HELLO_REQUEST) {
+ size_t hr_len = WPA_GET_BE24(buf + 1);
+ if (hr_len > *len - 4) {
+ wpa_printf(MSG_DEBUG, "TLSv1: HelloRequest underflow");
+ tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_DECODE_ERROR);
+ return -1;
+ }
+ wpa_printf(MSG_DEBUG, "TLSv1: Ignored HelloRequest");
+ *len = 4 + hr_len;
+ return 0;
+ }
+
+ switch (conn->state) {
+ case SERVER_HELLO:
+ if (tls_process_server_hello(conn, ct, buf, len))
+ return -1;
+ break;
+ case SERVER_CERTIFICATE:
+ if (tls_process_certificate(conn, ct, buf, len))
+ return -1;
+ break;
+ case SERVER_KEY_EXCHANGE:
+ if (tls_process_server_key_exchange(conn, ct, buf, len))
+ return -1;
+ break;
+ case SERVER_CERTIFICATE_REQUEST:
+ if (tls_process_certificate_request(conn, ct, buf, len))
+ return -1;
+ break;
+ case SERVER_HELLO_DONE:
+ if (tls_process_server_hello_done(conn, ct, buf, len))
+ return -1;
+ break;
+ case SERVER_CHANGE_CIPHER_SPEC:
+ if (tls_process_server_change_cipher_spec(conn, ct, buf, len))
+ return -1;
+ break;
+ case SERVER_FINISHED:
+ if (tls_process_server_finished(conn, ct, buf, len)) {
+ printf("[debug] server finish process fall \n");
+ return -1;
+ }
+ break;
+ case ACK_FINISHED:
+ if (out_data &&
+ tls_process_application_data(conn, ct, buf, len, out_data,
+ out_len))
+ return -1;
+ break;
+ default:
+ wpa_printf(MSG_DEBUG, "TLSv1: Unexpected state %d "
+ "while processing received message",
+ conn->state);
+ return -1;
+ }
+
+ if (ct == TLS_CONTENT_TYPE_HANDSHAKE) {
+ tls_verify_hash_add(&conn->verify, buf, *len);
+ }
+
+ return 0;
+}
--- /dev/null
+/*
+ * TLSv1 client - write handshake message
+ * Copyright (c) 2006-2011, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "wpa/includes.h"
+
+#include "wpa/common.h"
+#include "crypto/md5.h"
+#include "crypto/sha1.h"
+#include "crypto/sha256.h"
+#include "crypto/random.h"
+#include "wpa2/tls/tls.h"
+#include "wpa2/tls/x509v3.h"
+#include "wpa2/tls/tlsv1_common.h"
+#include "wpa2/tls/tlsv1_record.h"
+#include "wpa2/tls/tlsv1_client.h"
+#include "wpa2/tls/tlsv1_client_i.h"
+
+#include "wpa2/eap_peer/eap_i.h"
+
+static size_t tls_client_cert_chain_der_len(struct tlsv1_client *conn)
+{
+ size_t len = 0;
+ struct x509_certificate *cert;
+
+ if (conn->cred == NULL)
+ return 0;
+
+ cert = conn->cred->cert;
+ while (cert) {
+ len += 3 + cert->cert_len;
+ if (x509_certificate_self_signed(cert))
+ break;
+ cert = x509_certificate_get_subject(conn->cred->trusted_certs,
+ &cert->issuer);
+ }
+
+ return len;
+}
+
+
+u8 * tls_send_client_hello(struct tlsv1_client *conn, size_t *out_len)
+{
+ u8 *hello, *end, *pos, *hs_length, *hs_start, *rhdr;
+ struct os_time now;
+ size_t len, i;
+
+ wpa_printf(MSG_DEBUG, "TLSv1: Send ClientHello");
+ *out_len = 0;
+
+ os_get_time(&now);
+ WPA_PUT_BE32(conn->client_random, now.sec);
+ if (random_get_bytes(conn->client_random + 4, TLS_RANDOM_LEN - 4)) {
+ wpa_printf(MSG_ERROR, "TLSv1: Could not generate "
+ "client_random");
+ return NULL;
+ }
+ wpa_hexdump(MSG_MSGDUMP, "TLSv1: client_random",
+ conn->client_random, TLS_RANDOM_LEN);
+
+ len = 100 + conn->num_cipher_suites * 2 + conn->client_hello_ext_len;
+ hello = os_malloc(len);
+ if (hello == NULL)
+ return NULL;
+ end = hello + len;
+
+ rhdr = hello;
+ pos = rhdr + TLS_RECORD_HEADER_LEN;
+
+ /* opaque fragment[TLSPlaintext.length] */
+
+ /* Handshake */
+ hs_start = pos;
+ /* HandshakeType msg_type */
+ *pos++ = TLS_HANDSHAKE_TYPE_CLIENT_HELLO;
+ /* uint24 length (to be filled) */
+ hs_length = pos;
+ pos += 3;
+ /* body - ClientHello */
+ /* ProtocolVersion client_version */
+ WPA_PUT_BE16(pos, TLS_VERSION);
+ pos += 2;
+ /* Random random: uint32 gmt_unix_time, opaque random_bytes */
+ os_memcpy(pos, conn->client_random, TLS_RANDOM_LEN);
+ pos += TLS_RANDOM_LEN;
+ /* SessionID session_id */
+ *pos++ = conn->session_id_len;
+ os_memcpy(pos, conn->session_id, conn->session_id_len);
+ pos += conn->session_id_len;
+ /* CipherSuite cipher_suites<2..2^16-1> */
+ WPA_PUT_BE16(pos, 2 * conn->num_cipher_suites);
+ pos += 2;
+ for (i = 0; i < conn->num_cipher_suites; i++) {
+ WPA_PUT_BE16(pos, conn->cipher_suites[i]);
+ pos += 2;
+ }
+ /* CompressionMethod compression_methods<1..2^8-1> */
+ *pos++ = 1;
+ *pos++ = TLS_COMPRESSION_NULL;
+
+ if (conn->client_hello_ext) {
+ os_memcpy(pos, conn->client_hello_ext,
+ conn->client_hello_ext_len);
+ pos += conn->client_hello_ext_len;
+ }
+
+ WPA_PUT_BE24(hs_length, pos - hs_length - 3);
+ tls_verify_hash_add(&conn->verify, hs_start, pos - hs_start);
+
+ if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_HANDSHAKE,
+ rhdr, end - rhdr, hs_start, pos - hs_start,
+ out_len) < 0) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Failed to create TLS record");
+ tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_INTERNAL_ERROR);
+ os_free(hello);
+ return NULL;
+ }
+
+ conn->state = SERVER_HELLO;
+
+ return hello;
+}
+
+
+static int tls_write_client_certificate(struct tlsv1_client *conn,
+ u8 **msgpos, u8 *end)
+{
+ u8 *pos, *rhdr, *hs_start, *hs_length, *cert_start;
+ size_t rlen;
+ struct x509_certificate *cert;
+
+ pos = *msgpos;
+
+ wpa_printf(MSG_DEBUG, "TLSv1: Send Certificate");
+ rhdr = pos;
+ pos += TLS_RECORD_HEADER_LEN;
+
+ /* opaque fragment[TLSPlaintext.length] */
+
+ /* Handshake */
+ hs_start = pos;
+ /* HandshakeType msg_type */
+ *pos++ = TLS_HANDSHAKE_TYPE_CERTIFICATE;
+ /* uint24 length (to be filled) */
+ hs_length = pos;
+ pos += 3;
+ /* body - Certificate */
+ /* uint24 length (to be filled) */
+ cert_start = pos;
+ pos += 3;
+ cert = conn->cred ? conn->cred->cert : NULL;
+ while (cert) {
+ if (pos + 3 + cert->cert_len > end) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Not enough buffer space "
+ "for Certificate (cert_len=%lu left=%lu)",
+ (unsigned long) cert->cert_len,
+ (unsigned long) (end - pos));
+ tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_INTERNAL_ERROR);
+ return -1;
+ }
+ WPA_PUT_BE24(pos, cert->cert_len);
+ pos += 3;
+ os_memcpy(pos, cert->cert_start, cert->cert_len);
+ pos += cert->cert_len;
+
+ if (x509_certificate_self_signed(cert))
+ break;
+ cert = x509_certificate_get_subject(conn->cred->trusted_certs,
+ &cert->issuer);
+ }
+ if (conn->cred == NULL || cert == conn->cred->cert || cert == NULL) {
+ /*
+ * Client was not configured with all the needed certificates
+ * to form a full certificate chain. The server may fail to
+ * validate the chain unless it is configured with all the
+ * missing CA certificates.
+ */
+ wpa_printf(MSG_DEBUG, "TLSv1: Full client certificate chain "
+ "not configured - validation may fail");
+ }
+ WPA_PUT_BE24(cert_start, pos - cert_start - 3);
+
+ WPA_PUT_BE24(hs_length, pos - hs_length - 3);
+
+ if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_HANDSHAKE,
+ rhdr, end - rhdr, hs_start, pos - hs_start,
+ &rlen) < 0) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Failed to generate a record");
+ tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_INTERNAL_ERROR);
+ return -1;
+ }
+ pos = rhdr + rlen;
+
+ tls_verify_hash_add(&conn->verify, hs_start, pos - hs_start);
+
+ *msgpos = pos;
+
+ return 0;
+}
+
+
+static int tlsv1_key_x_anon_dh(struct tlsv1_client *conn, u8 **pos, u8 *end)
+{
+ /* ClientDiffieHellmanPublic */
+ u8 *csecret, *csecret_start, *dh_yc, *shared;
+ size_t csecret_len, dh_yc_len, shared_len;
+
+ csecret_len = conn->dh_p_len;
+ csecret = os_malloc(csecret_len);
+ if (csecret == NULL) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Failed to allocate "
+ "memory for Yc (Diffie-Hellman)");
+ tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_INTERNAL_ERROR);
+ return -1;
+ }
+ if (random_get_bytes(csecret, csecret_len)) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Failed to get random "
+ "data for Diffie-Hellman");
+ tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_INTERNAL_ERROR);
+ os_free(csecret);
+ return -1;
+ }
+
+ if (os_memcmp(csecret, conn->dh_p, csecret_len) > 0)
+ csecret[0] = 0; /* make sure Yc < p */
+
+ csecret_start = csecret;
+ while (csecret_len > 1 && *csecret_start == 0) {
+ csecret_start++;
+ csecret_len--;
+ }
+ wpa_hexdump_key(MSG_DEBUG, "TLSv1: DH client's secret value",
+ csecret_start, csecret_len);
+
+ /* Yc = g^csecret mod p */
+ dh_yc_len = conn->dh_p_len;
+ dh_yc = os_malloc(dh_yc_len);
+ if (dh_yc == NULL) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Failed to allocate "
+ "memory for Diffie-Hellman");
+ tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_INTERNAL_ERROR);
+ os_free(csecret);
+ return -1;
+ }
+ if (wpa2_crypto_funcs.crypto_mod_exp) {
+ if(wpa2_crypto_funcs.crypto_mod_exp(conn->dh_g, conn->dh_g_len,
+ csecret_start, csecret_len,
+ conn->dh_p, conn->dh_p_len,
+ dh_yc, &dh_yc_len)) {
+ tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_INTERNAL_ERROR);
+ os_free(csecret);
+ os_free(dh_yc);
+ return -1;
+ }
+ } else {
+ tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_INTERNAL_ERROR);
+ os_free(csecret);
+ os_free(dh_yc);
+ wpa_printf(MSG_ERROR, "Fail to register crypto_mod_exp function!\r\n");
+ return -1;
+ }
+
+ wpa_hexdump(MSG_DEBUG, "TLSv1: DH Yc (client's public value)",
+ dh_yc, dh_yc_len);
+
+ WPA_PUT_BE16(*pos, dh_yc_len);
+ *pos += 2;
+ if (*pos + dh_yc_len > end) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Not enough room in the "
+ "message buffer for Yc");
+ tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_INTERNAL_ERROR);
+ os_free(csecret);
+ os_free(dh_yc);
+ return -1;
+ }
+ os_memcpy(*pos, dh_yc, dh_yc_len);
+ *pos += dh_yc_len;
+ os_free(dh_yc);
+
+ shared_len = conn->dh_p_len;
+ shared = os_malloc(shared_len);
+ if (shared == NULL) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Could not allocate memory for "
+ "DH");
+ tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_INTERNAL_ERROR);
+ os_free(csecret);
+ return -1;
+ }
+
+ /* shared = Ys^csecret mod p */
+ if (wpa2_crypto_funcs.crypto_mod_exp) {
+ if(wpa2_crypto_funcs.crypto_mod_exp(conn->dh_ys, conn->dh_ys_len,
+ csecret_start, csecret_len,
+ conn->dh_p, conn->dh_p_len,
+ shared, &shared_len)) {
+ tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_INTERNAL_ERROR);
+ os_free(csecret);
+ os_free(shared);
+ return -1;
+ }
+ } else {
+ tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_INTERNAL_ERROR);
+ os_free(csecret);
+ os_free(shared);
+ wpa_printf(MSG_ERROR, "Fail to register crypto_mod_exp function!\r\n");
+ return -1;
+ }
+ wpa_hexdump_key(MSG_DEBUG, "TLSv1: Shared secret from DH key exchange",
+ shared, shared_len);
+
+ os_memset(csecret_start, 0, csecret_len);
+ os_free(csecret);
+ if (tls_derive_keys(conn, shared, shared_len)) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Failed to derive keys");
+ tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_INTERNAL_ERROR);
+ os_free(shared);
+ return -1;
+ }
+ os_memset(shared, 0, shared_len);
+ os_free(shared);
+ tlsv1_client_free_dh(conn);
+ return 0;
+}
+
+
+static int tlsv1_key_x_rsa(struct tlsv1_client *conn, u8 **pos, u8 *end)
+{
+ u8 pre_master_secret[TLS_PRE_MASTER_SECRET_LEN];
+ size_t clen;
+ int res;
+
+ if (tls_derive_pre_master_secret(pre_master_secret) < 0 ||
+ tls_derive_keys(conn, pre_master_secret,
+ TLS_PRE_MASTER_SECRET_LEN)) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Failed to derive keys");
+ tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_INTERNAL_ERROR);
+ return -1;
+ }
+
+ /* EncryptedPreMasterSecret */
+ if (conn->server_rsa_key == NULL) {
+ wpa_printf(MSG_DEBUG, "TLSv1: No server RSA key to "
+ "use for encrypting pre-master secret");
+ tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_INTERNAL_ERROR);
+ return -1;
+ }
+
+ /* RSA encrypted value is encoded with PKCS #1 v1.5 block type 2. */
+ *pos += 2;
+ clen = end - *pos;
+ res = crypto_public_key_encrypt_pkcs1_v15(
+ conn->server_rsa_key,
+ pre_master_secret, TLS_PRE_MASTER_SECRET_LEN,
+ *pos, &clen);
+ os_memset(pre_master_secret, 0, TLS_PRE_MASTER_SECRET_LEN);
+ if (res < 0) {
+ wpa_printf(MSG_DEBUG, "TLSv1: RSA encryption failed");
+ tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_INTERNAL_ERROR);
+ return -1;
+ }
+ WPA_PUT_BE16(*pos - 2, clen);
+ wpa_hexdump(MSG_MSGDUMP, "TLSv1: Encrypted pre_master_secret",
+ *pos, clen);
+ *pos += clen;
+
+ return 0;
+}
+
+
+static int tls_write_client_key_exchange(struct tlsv1_client *conn,
+ u8 **msgpos, u8 *end)
+{
+ u8 *pos, *rhdr, *hs_start, *hs_length;
+ size_t rlen;
+ tls_key_exchange keyx;
+ const struct tls_cipher_suite *suite;
+
+ suite = tls_get_cipher_suite(conn->rl.cipher_suite);
+ if (suite == NULL)
+ keyx = TLS_KEY_X_NULL;
+ else
+ keyx = suite->key_exchange;
+
+ pos = *msgpos;
+
+ wpa_printf(MSG_DEBUG, "TLSv1: Send ClientKeyExchange");
+
+ rhdr = pos;
+ pos += TLS_RECORD_HEADER_LEN;
+
+ /* opaque fragment[TLSPlaintext.length] */
+
+ /* Handshake */
+ hs_start = pos;
+ /* HandshakeType msg_type */
+ *pos++ = TLS_HANDSHAKE_TYPE_CLIENT_KEY_EXCHANGE;
+ /* uint24 length (to be filled) */
+ hs_length = pos;
+ pos += 3;
+ /* body - ClientKeyExchange */
+ if (keyx == TLS_KEY_X_DH_anon) {
+ if (tlsv1_key_x_anon_dh(conn, &pos, end) < 0)
+ return -1;
+ } else {
+ if (tlsv1_key_x_rsa(conn, &pos, end) < 0)
+ return -1;
+ }
+
+ WPA_PUT_BE24(hs_length, pos - hs_length - 3);
+
+ if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_HANDSHAKE,
+ rhdr, end - rhdr, hs_start, pos - hs_start,
+ &rlen) < 0) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Failed to create a record");
+ tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_INTERNAL_ERROR);
+ return -1;
+ }
+ pos = rhdr + rlen;
+ tls_verify_hash_add(&conn->verify, hs_start, pos - hs_start);
+
+ *msgpos = pos;
+
+ return 0;
+}
+
+
+static int tls_write_client_certificate_verify(struct tlsv1_client *conn,
+ u8 **msgpos, u8 *end)
+{
+ u8 *pos, *rhdr, *hs_start, *hs_length, *signed_start;
+ size_t rlen, hlen, clen;
+ u8 hash[100], *hpos;
+ enum { SIGN_ALG_RSA, SIGN_ALG_DSA } alg = SIGN_ALG_RSA;
+
+ pos = *msgpos;
+
+ wpa_printf(MSG_DEBUG, "TLSv1: Send CertificateVerify");
+ rhdr = pos;
+ pos += TLS_RECORD_HEADER_LEN;
+
+ /* Handshake */
+ hs_start = pos;
+ /* HandshakeType msg_type */
+ *pos++ = TLS_HANDSHAKE_TYPE_CERTIFICATE_VERIFY;
+ /* uint24 length (to be filled) */
+ hs_length = pos;
+ pos += 3;
+
+ /*
+ * RFC 2246: 7.4.3 and 7.4.8:
+ * Signature signature
+ *
+ * RSA:
+ * digitally-signed struct {
+ * opaque md5_hash[16];
+ * opaque sha_hash[20];
+ * };
+ *
+ * DSA:
+ * digitally-signed struct {
+ * opaque sha_hash[20];
+ * };
+ *
+ * The hash values are calculated over all handshake messages sent or
+ * received starting at ClientHello up to, but not including, this
+ * CertificateVerify message, including the type and length fields of
+ * the handshake messages.
+ */
+
+ hpos = hash;
+
+#ifdef CONFIG_TLSV12
+ if (conn->rl.tls_version == TLS_VERSION_1_2) {
+ hlen = SHA256_MAC_LEN;
+ if (wpa2_crypto_funcs.crypto_hash_finish) {
+ if (conn->verify.sha256_cert == NULL ||
+ wpa2_crypto_funcs.crypto_hash_finish(conn->verify.sha256_cert, hpos, &hlen) <
+ 0) {
+ conn->verify.sha256_cert = NULL;
+ tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_INTERNAL_ERROR);
+ return -1;
+ }
+ } else {
+ conn->verify.sha256_cert = NULL;
+ tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_INTERNAL_ERROR);
+ wpa_printf(MSG_ERROR, "In function %s, fail to register crypto_hash_finish function!\r\n", __FUNCTION__);
+ return -1;
+ }
+ conn->verify.sha256_cert = NULL;
+
+ /*
+ * RFC 3447, A.2.4 RSASSA-PKCS1-v1_5
+ *
+ * DigestInfo ::= SEQUENCE {
+ * digestAlgorithm DigestAlgorithm,
+ * digest OCTET STRING
+ * }
+ *
+ * SHA-256 OID: sha256WithRSAEncryption ::= {pkcs-1 11}
+ *
+ * DER encoded DigestInfo for SHA256 per RFC 3447:
+ * 30 31 30 0d 06 09 60 86 48 01 65 03 04 02 01 05 00 04 20 ||
+ * H
+ */
+ os_memmove(hash + 19, hash, hlen);
+ hlen += 19;
+ os_memcpy(hash, "\x30\x31\x30\x0d\x06\x09\x60\x86\x48\x01\x65"
+ "\x03\x04\x02\x01\x05\x00\x04\x20", 19);
+ } else {
+#endif /* CONFIG_TLSV12 */
+
+ if (alg == SIGN_ALG_RSA) {
+ hlen = MD5_MAC_LEN;
+ if (conn->verify.md5_cert == NULL ||
+ crypto_hash_finish(conn->verify.md5_cert, hpos, &hlen) < 0)
+ {
+ tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_INTERNAL_ERROR);
+ conn->verify.md5_cert = NULL;
+ crypto_hash_finish(conn->verify.sha1_cert, NULL, NULL);
+ conn->verify.sha1_cert = NULL;
+ return -1;
+ }
+ hpos += MD5_MAC_LEN;
+ } else
+ crypto_hash_finish(conn->verify.md5_cert, NULL, NULL);
+
+ conn->verify.md5_cert = NULL;
+ hlen = SHA1_MAC_LEN;
+ if (conn->verify.sha1_cert == NULL ||
+ crypto_hash_finish(conn->verify.sha1_cert, hpos, &hlen) < 0) {
+ conn->verify.sha1_cert = NULL;
+ tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_INTERNAL_ERROR);
+ return -1;
+ }
+ conn->verify.sha1_cert = NULL;
+
+ if (alg == SIGN_ALG_RSA)
+ hlen += MD5_MAC_LEN;
+
+#ifdef CONFIG_TLSV12
+ }
+#endif /* CONFIG_TLSV12 */
+
+ wpa_hexdump(MSG_MSGDUMP, "TLSv1: CertificateVerify hash", hash, hlen);
+
+#ifdef CONFIG_TLSV12
+ if (conn->rl.tls_version >= TLS_VERSION_1_2) {
+ /*
+ * RFC 5246, 4.7:
+ * TLS v1.2 adds explicit indication of the used signature and
+ * hash algorithms.
+ *
+ * struct {
+ * HashAlgorithm hash;
+ * SignatureAlgorithm signature;
+ * } SignatureAndHashAlgorithm;
+ */
+ *pos++ = TLS_HASH_ALG_SHA256;
+ *pos++ = TLS_SIGN_ALG_RSA;
+ }
+#endif /* CONFIG_TLSV12 */
+
+ /*
+ * RFC 2246, 4.7:
+ * In digital signing, one-way hash functions are used as input for a
+ * signing algorithm. A digitally-signed element is encoded as an
+ * opaque vector <0..2^16-1>, where the length is specified by the
+ * signing algorithm and key.
+ *
+ * In RSA signing, a 36-byte structure of two hashes (one SHA and one
+ * MD5) is signed (encrypted with the private key). It is encoded with
+ * PKCS #1 block type 0 or type 1 as described in [PKCS1].
+ */
+ signed_start = pos; /* length to be filled */
+ pos += 2;
+ clen = end - pos;
+ if (conn->cred == NULL ||
+ crypto_private_key_sign_pkcs1(conn->cred->key, hash, hlen,
+ pos, &clen) < 0) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Failed to sign hash (PKCS #1)");
+ tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_INTERNAL_ERROR);
+ return -1;
+ }
+ WPA_PUT_BE16(signed_start, clen);
+
+ pos += clen;
+
+ WPA_PUT_BE24(hs_length, pos - hs_length - 3);
+
+ if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_HANDSHAKE,
+ rhdr, end - rhdr, hs_start, pos - hs_start,
+ &rlen) < 0) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Failed to generate a record");
+ tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_INTERNAL_ERROR);
+ return -1;
+ }
+ pos = rhdr + rlen;
+
+ tls_verify_hash_add(&conn->verify, hs_start, pos - hs_start);
+
+ *msgpos = pos;
+
+ return 0;
+}
+
+
+static int tls_write_client_change_cipher_spec(struct tlsv1_client *conn,
+ u8 **msgpos, u8 *end)
+{
+ size_t rlen;
+ u8 payload[1];
+
+ wpa_printf(MSG_DEBUG, "TLSv1: Send ChangeCipherSpec");
+
+ payload[0] = TLS_CHANGE_CIPHER_SPEC;
+
+ if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_CHANGE_CIPHER_SPEC,
+ *msgpos, end - *msgpos, payload, sizeof(payload),
+ &rlen) < 0) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Failed to create a record");
+ tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_INTERNAL_ERROR);
+ return -1;
+ }
+
+ if (tlsv1_record_change_write_cipher(&conn->rl) < 0) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Failed to set write cipher for "
+ "record layer");
+ tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_INTERNAL_ERROR);
+ return -1;
+ }
+
+ *msgpos += rlen;
+
+ return 0;
+}
+
+
+static int tls_write_client_finished(struct tlsv1_client *conn,
+ u8 **msgpos, u8 *end)
+{
+ u8 *pos, *hs_start;
+ size_t rlen, hlen;
+ u8 verify_data[1 + 3 + TLS_VERIFY_DATA_LEN];
+ u8 hash[MD5_MAC_LEN + SHA1_MAC_LEN];
+
+ wpa_printf(MSG_DEBUG, "TLSv1: Send Finished");
+
+ /* Encrypted Handshake Message: Finished */
+
+#ifdef CONFIG_TLSV12
+ if (conn->rl.tls_version >= TLS_VERSION_1_2) {
+ hlen = SHA256_MAC_LEN;
+ if (wpa2_crypto_funcs.crypto_hash_finish) {
+ if (conn->verify.sha256_client == NULL ||
+ wpa2_crypto_funcs.crypto_hash_finish(conn->verify.sha256_client, hash, &hlen)
+ < 0) {
+ tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_INTERNAL_ERROR);
+ conn->verify.sha256_client = NULL;
+ return -1;
+ }
+ } else {
+ tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_INTERNAL_ERROR);
+ conn->verify.sha256_client = NULL;
+ wpa_printf(MSG_ERROR, "In function %s, fail to register crypto_hash_finish function!\r\n", __FUNCTION__);
+ return -1;
+ }
+ conn->verify.sha256_client = NULL;
+ } else {
+#endif /* CONFIG_TLSV12 */
+
+ hlen = MD5_MAC_LEN;
+ if (conn->verify.md5_client == NULL ||
+ crypto_hash_finish(conn->verify.md5_client, hash, &hlen) < 0) {
+ tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_INTERNAL_ERROR);
+ conn->verify.md5_client = NULL;
+ crypto_hash_finish(conn->verify.sha1_client, NULL, NULL);
+ conn->verify.sha1_client = NULL;
+ return -1;
+ }
+ conn->verify.md5_client = NULL;
+ hlen = SHA1_MAC_LEN;
+ if (conn->verify.sha1_client == NULL ||
+ crypto_hash_finish(conn->verify.sha1_client, hash + MD5_MAC_LEN,
+ &hlen) < 0) {
+ conn->verify.sha1_client = NULL;
+ tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_INTERNAL_ERROR);
+ return -1;
+ }
+ conn->verify.sha1_client = NULL;
+ hlen = MD5_MAC_LEN + SHA1_MAC_LEN;
+
+#ifdef CONFIG_TLSV12
+ }
+#endif /* CONFIG_TLSV12 */
+
+ if (tls_prf(conn->rl.tls_version,
+ conn->master_secret, TLS_MASTER_SECRET_LEN,
+ "client finished", hash, hlen,
+ verify_data + 1 + 3, TLS_VERIFY_DATA_LEN)) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Failed to generate verify_data");
+ tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_INTERNAL_ERROR);
+ return -1;
+ }
+ wpa_hexdump_key(MSG_DEBUG, "TLSv1: verify_data (client)",
+ verify_data + 1 + 3, TLS_VERIFY_DATA_LEN);
+
+ /* Handshake */
+ pos = hs_start = verify_data;
+ /* HandshakeType msg_type */
+ *pos++ = TLS_HANDSHAKE_TYPE_FINISHED;
+ /* uint24 length */
+ WPA_PUT_BE24(pos, TLS_VERIFY_DATA_LEN);
+ pos += 3;
+ pos += TLS_VERIFY_DATA_LEN; /* verify_data already in place */
+ tls_verify_hash_add(&conn->verify, hs_start, pos - hs_start);
+
+ if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_HANDSHAKE,
+ *msgpos, end - *msgpos, hs_start, pos - hs_start,
+ &rlen) < 0) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Failed to create a record");
+ tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_INTERNAL_ERROR);
+ return -1;
+ }
+
+ *msgpos += rlen;
+
+ return 0;
+}
+
+
+static u8 * tls_send_client_key_exchange(struct tlsv1_client *conn,
+ size_t *out_len)
+{
+ u8 *msg, *end, *pos;
+ size_t msglen;
+
+ *out_len = 0;
+
+ msglen = 2000;
+ if (conn->certificate_requested)
+ msglen += tls_client_cert_chain_der_len(conn);
+
+ msg = os_malloc(msglen);
+ if (msg == NULL)
+ return NULL;
+
+ pos = msg;
+ end = msg + msglen;
+
+ if (conn->certificate_requested) {
+ if (tls_write_client_certificate(conn, &pos, end) < 0) {
+ os_free(msg);
+ return NULL;
+ }
+ }
+
+ if (tls_write_client_key_exchange(conn, &pos, end) < 0 ||
+ (conn->certificate_requested && conn->cred && conn->cred->key &&
+ tls_write_client_certificate_verify(conn, &pos, end) < 0) ||
+ tls_write_client_change_cipher_spec(conn, &pos, end) < 0 ||
+ tls_write_client_finished(conn, &pos, end) < 0) {
+ os_free(msg);
+ return NULL;
+ }
+
+ *out_len = pos - msg;
+ conn->state = SERVER_CHANGE_CIPHER_SPEC;
+
+ return msg;
+}
+
+
+static u8 * tls_send_change_cipher_spec(struct tlsv1_client *conn,
+ size_t *out_len)
+{
+ u8 *msg, *end, *pos;
+
+ *out_len = 0;
+
+ msg = os_malloc(1000);
+ if (msg == NULL)
+ return NULL;
+
+ pos = msg;
+ end = msg + 1000;
+
+ if (tls_write_client_change_cipher_spec(conn, &pos, end) < 0 ||
+ tls_write_client_finished(conn, &pos, end) < 0) {
+ os_free(msg);
+ return NULL;
+ }
+
+ *out_len = pos - msg;
+
+ wpa_printf(MSG_DEBUG, "TLSv1: Session resumption completed "
+ "successfully");
+
+ conn->state = ESTABLISHED;
+
+ return msg;
+}
+
+
+u8 * tlsv1_client_handshake_write(struct tlsv1_client *conn, size_t *out_len,
+ int no_appl_data)
+{
+ switch (conn->state) {
+ case CLIENT_KEY_EXCHANGE:
+ return tls_send_client_key_exchange(conn, out_len);
+ case CHANGE_CIPHER_SPEC:
+ return tls_send_change_cipher_spec(conn, out_len);
+ case ACK_FINISHED:
+ wpa_printf(MSG_DEBUG, "TLSv1: Handshake completed "
+ "successfully");
+ conn->state = ESTABLISHED;
+ *out_len = 0;
+ if (no_appl_data) {
+ /* Need to return something to get final TLS ACK. */
+ return os_malloc(1);
+ }
+ return NULL;
+ default:
+ wpa_printf(MSG_DEBUG, "TLSv1: Unexpected state %d while "
+ "generating reply", conn->state);
+ return NULL;
+ }
+}
+
+
+u8 * tlsv1_client_send_alert(struct tlsv1_client *conn, u8 level,
+ u8 description, size_t *out_len)
+{
+ u8 *alert, *pos, *length;
+
+ wpa_printf(MSG_DEBUG, "TLSv1: Send Alert(%d:%d)", level, description);
+ *out_len = 0;
+
+ alert = os_malloc(10);
+ if (alert == NULL)
+ return NULL;
+
+ pos = alert;
+
+ /* TLSPlaintext */
+ /* ContentType type */
+ *pos++ = TLS_CONTENT_TYPE_ALERT;
+ /* ProtocolVersion version */
+ WPA_PUT_BE16(pos, conn->rl.tls_version ? conn->rl.tls_version :
+ TLS_VERSION);
+ pos += 2;
+ /* uint16 length (to be filled) */
+ length = pos;
+ pos += 2;
+ /* opaque fragment[TLSPlaintext.length] */
+
+ /* Alert */
+ /* AlertLevel level */
+ *pos++ = level;
+ /* AlertDescription description */
+ *pos++ = description;
+
+ WPA_PUT_BE16(length, pos - length - 2);
+ *out_len = pos - alert;
+
+ return alert;
+}
--- /dev/null
+/*
+ * TLSv1 common routines
+ * Copyright (c) 2006-2011, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "wpa/includes.h"
+
+#include "wpa/common.h"
+#include "crypto/sha1.h"
+#include "crypto/sha256.h"
+#include "wpa2/tls/tls.h"
+#include "wpa2/tls/x509v3.h"
+#include "wpa2/tls/tlsv1_common.h"
+#include "wpa2/eap_peer/eap_i.h"
+
+
+/*
+ * TODO:
+ * RFC 2246 Section 9: Mandatory to implement TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA
+ * Add support for commonly used cipher suites; don't bother with exportable
+ * suites.
+ */
+
+static const struct tls_cipher_suite tls_cipher_suites[] = {
+ { TLS_NULL_WITH_NULL_NULL, TLS_KEY_X_NULL, TLS_CIPHER_NULL,
+ TLS_HASH_NULL },
+ { TLS_RSA_WITH_RC4_128_MD5, TLS_KEY_X_RSA, TLS_CIPHER_RC4_128,
+ TLS_HASH_MD5 },
+ { TLS_RSA_WITH_RC4_128_SHA, TLS_KEY_X_RSA, TLS_CIPHER_RC4_128,
+ TLS_HASH_SHA },
+ { TLS_RSA_WITH_DES_CBC_SHA, TLS_KEY_X_RSA, TLS_CIPHER_DES_CBC,
+ TLS_HASH_SHA },
+ { TLS_RSA_WITH_3DES_EDE_CBC_SHA, TLS_KEY_X_RSA,
+ TLS_CIPHER_3DES_EDE_CBC, TLS_HASH_SHA },
+ { TLS_DH_anon_WITH_RC4_128_MD5, TLS_KEY_X_DH_anon,
+ TLS_CIPHER_RC4_128, TLS_HASH_MD5 },
+ { TLS_DH_anon_WITH_DES_CBC_SHA, TLS_KEY_X_DH_anon,
+ TLS_CIPHER_DES_CBC, TLS_HASH_SHA },
+ { TLS_DH_anon_WITH_3DES_EDE_CBC_SHA, TLS_KEY_X_DH_anon,
+ TLS_CIPHER_3DES_EDE_CBC, TLS_HASH_SHA },
+ { TLS_RSA_WITH_AES_128_CBC_SHA, TLS_KEY_X_RSA, TLS_CIPHER_AES_128_CBC,
+ TLS_HASH_SHA },
+ { TLS_DH_anon_WITH_AES_128_CBC_SHA, TLS_KEY_X_DH_anon,
+ TLS_CIPHER_AES_128_CBC, TLS_HASH_SHA },
+ { TLS_RSA_WITH_AES_256_CBC_SHA, TLS_KEY_X_RSA, TLS_CIPHER_AES_256_CBC,
+ TLS_HASH_SHA },
+ { TLS_DH_anon_WITH_AES_256_CBC_SHA, TLS_KEY_X_DH_anon,
+ TLS_CIPHER_AES_256_CBC, TLS_HASH_SHA },
+ { TLS_RSA_WITH_AES_128_CBC_SHA256, TLS_KEY_X_RSA,
+ TLS_CIPHER_AES_128_CBC, TLS_HASH_SHA256 },
+ { TLS_RSA_WITH_AES_256_CBC_SHA256, TLS_KEY_X_RSA,
+ TLS_CIPHER_AES_256_CBC, TLS_HASH_SHA256 },
+ { TLS_DH_anon_WITH_AES_128_CBC_SHA256, TLS_KEY_X_DH_anon,
+ TLS_CIPHER_AES_128_CBC, TLS_HASH_SHA256 },
+ { TLS_DH_anon_WITH_AES_256_CBC_SHA256, TLS_KEY_X_DH_anon,
+ TLS_CIPHER_AES_256_CBC, TLS_HASH_SHA256 }
+};
+
+#define NUM_ELEMS(a) (sizeof(a) / sizeof((a)[0]))
+#define NUM_TLS_CIPHER_SUITES NUM_ELEMS(tls_cipher_suites)
+
+
+static const struct tls_cipher_data tls_ciphers[] = {
+ { TLS_CIPHER_NULL, TLS_CIPHER_STREAM, 0, 0, 0,
+ CRYPTO_CIPHER_NULL },
+ { TLS_CIPHER_IDEA_CBC, TLS_CIPHER_BLOCK, 16, 16, 8,
+ CRYPTO_CIPHER_NULL },
+ { TLS_CIPHER_RC2_CBC_40, TLS_CIPHER_BLOCK, 5, 16, 0,
+ CRYPTO_CIPHER_ALG_RC2 },
+ { TLS_CIPHER_RC4_40, TLS_CIPHER_STREAM, 5, 16, 0,
+ CRYPTO_CIPHER_ALG_RC4 },
+ { TLS_CIPHER_RC4_128, TLS_CIPHER_STREAM, 16, 16, 0,
+ CRYPTO_CIPHER_ALG_RC4 },
+ { TLS_CIPHER_DES40_CBC, TLS_CIPHER_BLOCK, 5, 8, 8,
+ CRYPTO_CIPHER_ALG_DES },
+ { TLS_CIPHER_DES_CBC, TLS_CIPHER_BLOCK, 8, 8, 8,
+ CRYPTO_CIPHER_ALG_DES },
+ { TLS_CIPHER_3DES_EDE_CBC, TLS_CIPHER_BLOCK, 24, 24, 8,
+ CRYPTO_CIPHER_ALG_3DES },
+ { TLS_CIPHER_AES_128_CBC, TLS_CIPHER_BLOCK, 16, 16, 16,
+ CRYPTO_CIPHER_ALG_AES },
+ { TLS_CIPHER_AES_256_CBC, TLS_CIPHER_BLOCK, 32, 32, 16,
+ CRYPTO_CIPHER_ALG_AES }
+};
+
+#define NUM_TLS_CIPHER_DATA NUM_ELEMS(tls_ciphers)
+
+
+/**
+ * tls_get_cipher_suite - Get TLS cipher suite
+ * @suite: Cipher suite identifier
+ * Returns: Pointer to the cipher data or %NULL if not found
+ */
+const struct tls_cipher_suite * tls_get_cipher_suite(u16 suite)
+{
+ size_t i;
+ for (i = 0; i < NUM_TLS_CIPHER_SUITES; i++)
+ if (tls_cipher_suites[i].suite == suite)
+ return &tls_cipher_suites[i];
+ return NULL;
+}
+
+
+const struct tls_cipher_data * tls_get_cipher_data(tls_cipher cipher)
+{
+ size_t i;
+ for (i = 0; i < NUM_TLS_CIPHER_DATA; i++)
+ if (tls_ciphers[i].cipher == cipher)
+ return &tls_ciphers[i];
+ return NULL;
+}
+
+
+int tls_server_key_exchange_allowed(tls_cipher cipher)
+{
+ const struct tls_cipher_suite *suite;
+
+ /* RFC 2246, Section 7.4.3 */
+ suite = tls_get_cipher_suite(cipher);
+ if (suite == NULL)
+ return 0;
+
+ switch (suite->key_exchange) {
+ case TLS_KEY_X_DHE_DSS:
+ case TLS_KEY_X_DHE_DSS_EXPORT:
+ case TLS_KEY_X_DHE_RSA:
+ case TLS_KEY_X_DHE_RSA_EXPORT:
+ case TLS_KEY_X_DH_anon_EXPORT:
+ case TLS_KEY_X_DH_anon:
+ return 1;
+ case TLS_KEY_X_RSA_EXPORT:
+ return 1 /* FIX: public key len > 512 bits */;
+ default:
+ return 0;
+ }
+}
+
+
+/**
+ * tls_parse_cert - Parse DER encoded X.509 certificate and get public key
+ * @buf: ASN.1 DER encoded certificate
+ * @len: Length of the buffer
+ * @pk: Buffer for returning the allocated public key
+ * Returns: 0 on success, -1 on failure
+ *
+ * This functions parses an ASN.1 DER encoded X.509 certificate and retrieves
+ * the public key from it. The caller is responsible for freeing the public key
+ * by calling crypto_public_key_free().
+ */
+int tls_parse_cert(const u8 *buf, size_t len, struct crypto_public_key **pk)
+{
+ struct x509_certificate *cert;
+
+ wpa_hexdump(MSG_MSGDUMP, "TLSv1: Parse ASN.1 DER certificate",
+ buf, len);
+
+ *pk = crypto_public_key_from_cert(buf, len);
+ if (*pk)
+ return 0;
+
+ cert = x509_certificate_parse(buf, len);
+ if (cert == NULL) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Failed to parse X.509 "
+ "certificate");
+ return -1;
+ }
+
+ /* TODO
+ * verify key usage (must allow encryption)
+ *
+ * All certificate profiles, key and cryptographic formats are
+ * defined by the IETF PKIX working group [PKIX]. When a key
+ * usage extension is present, the digitalSignature bit must be
+ * set for the key to be eligible for signing, as described
+ * above, and the keyEncipherment bit must be present to allow
+ * encryption, as described above. The keyAgreement bit must be
+ * set on Diffie-Hellman certificates. (PKIX: RFC 3280)
+ */
+
+ *pk = crypto_public_key_import(cert->public_key, cert->public_key_len);
+ x509_certificate_free(cert);
+
+ if (*pk == NULL) {
+ wpa_printf(MSG_ERROR, "TLSv1: Failed to import "
+ "server public key");
+ return -1;
+ }
+
+ return 0;
+}
+
+
+int tls_verify_hash_init(struct tls_verify_hash *verify)
+{
+ tls_verify_hash_free(verify);
+ verify->md5_client = crypto_hash_init(CRYPTO_HASH_ALG_MD5, NULL, 0);
+ verify->md5_server = crypto_hash_init(CRYPTO_HASH_ALG_MD5, NULL, 0);
+ verify->md5_cert = crypto_hash_init(CRYPTO_HASH_ALG_MD5, NULL, 0);
+ verify->sha1_client = crypto_hash_init(CRYPTO_HASH_ALG_SHA1, NULL, 0);
+ verify->sha1_server = crypto_hash_init(CRYPTO_HASH_ALG_SHA1, NULL, 0);
+ verify->sha1_cert = crypto_hash_init(CRYPTO_HASH_ALG_SHA1, NULL, 0);
+ if (verify->md5_client == NULL || verify->md5_server == NULL ||
+ verify->md5_cert == NULL || verify->sha1_client == NULL ||
+ verify->sha1_server == NULL || verify->sha1_cert == NULL) {
+ tls_verify_hash_free(verify);
+ return -1;
+ }
+#ifdef CONFIG_TLSV12
+ if (wpa2_crypto_funcs.crypto_hash_init) {
+ verify->sha256_client = wpa2_crypto_funcs.crypto_hash_init(CRYPTO_HASH_ALG_SHA256, NULL, 0);
+ verify->sha256_server = wpa2_crypto_funcs.crypto_hash_init(CRYPTO_HASH_ALG_SHA256, NULL, 0);
+ verify->sha256_cert = wpa2_crypto_funcs.crypto_hash_init(CRYPTO_HASH_ALG_SHA256, NULL, 0);
+ } else {
+ wpa_printf(MSG_ERROR, "In function %s, fail to register crypto hash init function!\r\n", __FUNCTION__);
+ return -1;
+ }
+ if (verify->sha256_client == NULL ||
+ verify->sha256_server == NULL ||
+ verify->sha256_cert == NULL) {
+ tls_verify_hash_free(verify);
+ return -1;
+ }
+#endif /* CONFIG_TLSV12 */
+ return 0;
+}
+
+
+void tls_verify_hash_add(struct tls_verify_hash *verify, const u8 *buf,
+ size_t len)
+{
+ if (verify->md5_client && verify->sha1_client) {
+ crypto_hash_update(verify->md5_client, buf, len);
+ crypto_hash_update(verify->sha1_client, buf, len);
+ }
+ if (verify->md5_server && verify->sha1_server) {
+ crypto_hash_update(verify->md5_server, buf, len);
+ crypto_hash_update(verify->sha1_server, buf, len);
+ }
+ if (verify->md5_cert && verify->sha1_cert) {
+ crypto_hash_update(verify->md5_cert, buf, len);
+ crypto_hash_update(verify->sha1_cert, buf, len);
+ }
+#ifdef CONFIG_TLSV12
+ if (wpa2_crypto_funcs.crypto_hash_update) {
+ if (verify->sha256_client)
+ wpa2_crypto_funcs.crypto_hash_update(verify->sha256_client, buf, len);
+ if (verify->sha256_server)
+ wpa2_crypto_funcs.crypto_hash_update(verify->sha256_server, buf, len);
+ if (verify->sha256_cert)
+ wpa2_crypto_funcs.crypto_hash_update(verify->sha256_cert, buf, len);
+ } else {
+ wpa_printf(MSG_ERROR, "In function %s, fail to register crypto hash update function!\r\n", __FUNCTION__);
+ return;
+ }
+#endif /* CONFIG_TLSV12 */
+}
+
+
+void tls_verify_hash_free(struct tls_verify_hash *verify)
+{
+ crypto_hash_finish(verify->md5_client, NULL, NULL);
+ crypto_hash_finish(verify->md5_server, NULL, NULL);
+ crypto_hash_finish(verify->md5_cert, NULL, NULL);
+ crypto_hash_finish(verify->sha1_client, NULL, NULL);
+ crypto_hash_finish(verify->sha1_server, NULL, NULL);
+ crypto_hash_finish(verify->sha1_cert, NULL, NULL);
+ verify->md5_client = NULL;
+ verify->md5_server = NULL;
+ verify->md5_cert = NULL;
+ verify->sha1_client = NULL;
+ verify->sha1_server = NULL;
+ verify->sha1_cert = NULL;
+#ifdef CONFIG_TLSV12
+ if (wpa2_crypto_funcs.crypto_hash_finish) {
+ wpa2_crypto_funcs.crypto_hash_finish(verify->sha256_client, NULL, NULL);
+ wpa2_crypto_funcs.crypto_hash_finish(verify->sha256_server, NULL, NULL);
+ wpa2_crypto_funcs.crypto_hash_finish(verify->sha256_cert, NULL, NULL);
+ verify->sha256_client = NULL;
+ verify->sha256_server = NULL;
+ verify->sha256_cert = NULL;
+ } else {
+ wpa_printf(MSG_ERROR, "In function %s, fail to register crypto hash finish function!\r\n", __FUNCTION__);
+ return;
+ }
+#endif /* CONFIG_TLSV12 */
+}
+
+
+int tls_version_ok(u16 ver)
+{
+ if (ver == TLS_VERSION_1)
+ return 1;
+#ifdef CONFIG_TLSV11
+ if (ver == TLS_VERSION_1_1)
+ return 1;
+#endif /* CONFIG_TLSV11 */
+#ifdef CONFIG_TLSV12
+ if (ver == TLS_VERSION_1_2)
+ return 1;
+#endif /* CONFIG_TLSV12 */
+
+ return 0;
+}
+
+
+const char * tls_version_str(u16 ver)
+{
+ switch (ver) {
+ case TLS_VERSION_1:
+ return "1.0";
+ case TLS_VERSION_1_1:
+ return "1.1";
+ case TLS_VERSION_1_2:
+ return "1.2";
+ }
+
+ return "?";
+}
+
+
+int tls_prf(u16 ver, const u8 *secret, size_t secret_len, const char *label,
+ const u8 *seed, size_t seed_len, u8 *out, size_t outlen)
+{
+#ifdef CONFIG_TLSV12
+ if (ver >= TLS_VERSION_1_2) {
+ tls_prf_sha256(secret, secret_len, label, seed, seed_len,
+ out, outlen);
+ return 0;
+ }
+#endif /* CONFIG_TLSV12 */
+
+ return tls_prf_sha1_md5(secret, secret_len, label, seed, seed_len, out,
+ outlen);
+}
--- /dev/null
+/*
+ * TLSv1 credentials
+ * Copyright (c) 2006-2009, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "wpa/includes.h"
+
+#include "wpa/common.h"
+#include "wpa2/utils/base64.h"
+#include "crypto/crypto.h"
+#include "wpa2/tls/x509v3.h"
+#include "wpa2/tls/tlsv1_cred.h"
+
+struct tlsv1_credentials * tlsv1_cred_alloc(void)
+{
+ struct tlsv1_credentials *cred;
+ cred = (struct tlsv1_credentials *)os_zalloc(sizeof(*cred));
+ return cred;
+}
+
+
+void tlsv1_cred_free(struct tlsv1_credentials *cred)
+{
+ if (cred == NULL)
+ return;
+
+ x509_certificate_chain_free(cred->trusted_certs);
+ x509_certificate_chain_free(cred->cert);
+ crypto_private_key_free(cred->key);
+ os_free(cred->dh_p);
+ os_free(cred->dh_g);
+ os_free(cred);
+}
+
+
+static int tlsv1_add_cert_der(struct x509_certificate **chain,
+ const u8 *buf, size_t len)
+{
+ struct x509_certificate *cert, *p;
+ char name[128];
+
+ cert = x509_certificate_parse(buf, len);
+ if (cert == NULL) {
+ wpa_printf(MSG_INFO, "TLSv1: %s - failed to parse certificate",
+ __func__);
+ return -1;
+ }
+
+ p = *chain;
+ while (p && p->next)
+ p = p->next;
+ if (p && x509_name_compare(&cert->subject, &p->issuer) == 0) {
+ /*
+ * The new certificate is the issuer of the last certificate in
+ * the chain - add the new certificate to the end.
+ */
+ p->next = cert;
+ } else {
+ /* Add to the beginning of the chain */
+ cert->next = *chain;
+ *chain = cert;
+ }
+
+ x509_name_string(&cert->subject, name, sizeof(name));
+ wpa_printf(MSG_DEBUG, "TLSv1: Added certificate: %s", name);
+
+ return 0;
+}
+
+
+static const char *pem_cert_begin = "-----BEGIN CERTIFICATE-----";
+static const char *pem_cert_end = "-----END CERTIFICATE-----";
+static const char *pem_key_begin = "-----BEGIN RSA PRIVATE KEY-----";
+static const char *pem_key_end = "-----END RSA PRIVATE KEY-----";
+static const char *pem_key2_begin = "-----BEGIN PRIVATE KEY-----";
+static const char *pem_key2_end = "-----END PRIVATE KEY-----";
+static const char *pem_key_enc_begin = "-----BEGIN ENCRYPTED PRIVATE KEY-----";
+static const char *pem_key_enc_end = "-----END ENCRYPTED PRIVATE KEY-----";
+
+
+static const u8 * search_tag(const char *tag, const u8 *buf, size_t len)
+{
+ size_t i, plen;
+
+ plen = os_strlen(tag);
+ if (len < plen)
+ return NULL;
+
+ for (i = 0; i < len - plen; i++) {
+ if (os_memcmp(buf + i, tag, plen) == 0)
+ return buf + i;
+ }
+
+ return NULL;
+}
+
+
+static int tlsv1_add_cert(struct x509_certificate **chain,
+ const u8 *buf, size_t len)
+{
+ const u8 *pos, *end;
+ unsigned char *der;
+ size_t der_len;
+
+ pos = search_tag(pem_cert_begin, buf, len);
+ if (!pos) {
+ wpa_printf(MSG_DEBUG, "TLSv1: No PEM certificate tag found - "
+ "assume DER format");
+ return tlsv1_add_cert_der(chain, buf, len);
+ }
+
+ wpa_printf(MSG_DEBUG, "TLSv1: Converting PEM format certificate into "
+ "DER format");
+
+ while (pos) {
+ pos += os_strlen(pem_cert_begin);
+ end = search_tag(pem_cert_end, pos, buf + len - pos);
+ if (end == NULL) {
+ wpa_printf(MSG_INFO, "TLSv1: Could not find PEM "
+ "certificate end tag (%s)", pem_cert_end);
+ return -1;
+ }
+
+ der = base64_decode(pos, end - pos, &der_len);
+ if (der == NULL) {
+ wpa_printf(MSG_INFO, "TLSv1: Could not decode PEM "
+ "certificate");
+ return -1;
+ }
+
+ if (tlsv1_add_cert_der(chain, der, der_len) < 0) {
+ wpa_printf(MSG_INFO, "TLSv1: Failed to parse PEM "
+ "certificate after DER conversion");
+ os_free(der);
+ return -1;
+ }
+
+ os_free(der);
+
+ end += os_strlen(pem_cert_end);
+ pos = search_tag(pem_cert_begin, end, buf + len - end);
+ }
+
+ return 0;
+}
+
+
+static int tlsv1_set_cert_chain(struct x509_certificate **chain,
+ const char *cert, const u8 *cert_blob,
+ size_t cert_blob_len)
+{
+ if (cert_blob)
+ return tlsv1_add_cert(chain, cert_blob, cert_blob_len);
+
+ if (cert) {
+ u8 *buf = NULL;
+ size_t len;
+ int ret;
+
+ //buf = (u8 *) os_readfile(cert, &len);
+ if (buf == NULL) {
+ wpa_printf(MSG_INFO, "TLSv1: Failed to read '%s'",
+ cert);
+ return -1;
+ }
+
+ ret = tlsv1_add_cert(chain, buf, len);
+ os_free(buf);
+ return ret;
+ }
+
+ return 0;
+}
+
+
+/**
+ * tlsv1_set_ca_cert - Set trusted CA certificate(s)
+ * @cred: TLSv1 credentials from tlsv1_cred_alloc()
+ * @cert: File or reference name for X.509 certificate in PEM or DER format
+ * @cert_blob: cert as inlined data or %NULL if not used
+ * @cert_blob_len: ca_cert_blob length
+ * @path: Path to CA certificates (not yet supported)
+ * Returns: 0 on success, -1 on failure
+ */
+int tlsv1_set_ca_cert(struct tlsv1_credentials *cred, const char *cert,
+ const u8 *cert_blob, size_t cert_blob_len,
+ const char *path)
+{
+ if (tlsv1_set_cert_chain(&cred->trusted_certs, cert,
+ cert_blob, cert_blob_len) < 0)
+ return -1;
+
+ if (path) {
+ /* TODO: add support for reading number of certificate files */
+ wpa_printf(MSG_INFO, "TLSv1: Use of CA certificate directory "
+ "not yet supported");
+ return -1;
+ }
+
+ return 0;
+}
+
+
+/**
+ * tlsv1_set_cert - Set certificate
+ * @cred: TLSv1 credentials from tlsv1_cred_alloc()
+ * @cert: File or reference name for X.509 certificate in PEM or DER format
+ * @cert_blob: cert as inlined data or %NULL if not used
+ * @cert_blob_len: cert_blob length
+ * Returns: 0 on success, -1 on failure
+ */
+int tlsv1_set_cert(struct tlsv1_credentials *cred, const char *cert,
+ const u8 *cert_blob, size_t cert_blob_len)
+{
+ return tlsv1_set_cert_chain(&cred->cert, cert,
+ cert_blob, cert_blob_len);
+}
+
+
+static struct crypto_private_key * tlsv1_set_key_pem(const u8 *key, size_t len)
+{
+ const u8 *pos, *end;
+ unsigned char *der;
+ size_t der_len;
+ struct crypto_private_key *pkey;
+
+ pos = search_tag(pem_key_begin, key, len);
+ if (!pos) {
+ pos = search_tag(pem_key2_begin, key, len);
+ if (!pos)
+ return NULL;
+ pos += os_strlen(pem_key2_begin);
+ end = search_tag(pem_key2_end, pos, key + len - pos);
+ if (!end)
+ return NULL;
+ } else {
+ const u8 *pos2;
+ pos += os_strlen(pem_key_begin);
+ end = search_tag(pem_key_end, pos, key + len - pos);
+ if (!end)
+ return NULL;
+ pos2 = search_tag("Proc-Type: 4,ENCRYPTED", pos, end - pos);
+ if (pos2) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Unsupported private key "
+ "format (Proc-Type/DEK-Info)");
+ return NULL;
+ }
+ }
+
+ der = base64_decode(pos, end - pos, &der_len);
+ if (!der)
+ return NULL;
+ pkey = crypto_private_key_import(der, der_len, NULL);
+ os_free(der);
+ return pkey;
+}
+
+
+static struct crypto_private_key * tlsv1_set_key_enc_pem(const u8 *key,
+ size_t len,
+ const char *passwd)
+{
+ const u8 *pos, *end;
+ unsigned char *der;
+ size_t der_len;
+ struct crypto_private_key *pkey;
+
+ if (passwd == NULL)
+ return NULL;
+ pos = search_tag(pem_key_enc_begin, key, len);
+ if (!pos)
+ return NULL;
+ pos += os_strlen(pem_key_enc_begin);
+ end = search_tag(pem_key_enc_end, pos, key + len - pos);
+ if (!end)
+ return NULL;
+
+ der = base64_decode(pos, end - pos, &der_len);
+ if (!der)
+ return NULL;
+ pkey = crypto_private_key_import(der, der_len, passwd);
+ os_free(der);
+ return pkey;
+}
+
+
+static int tlsv1_set_key(struct tlsv1_credentials *cred,
+ const u8 *key, size_t len, const char *passwd)
+{
+ cred->key = crypto_private_key_import(key, len, passwd);
+ if (cred->key == NULL)
+ cred->key = tlsv1_set_key_pem(key, len);
+ if (cred->key == NULL)
+ cred->key = tlsv1_set_key_enc_pem(key, len, passwd);
+ if (cred->key == NULL) {
+ wpa_printf(MSG_INFO, "TLSv1: Failed to parse private key");
+ return -1;
+ }
+ return 0;
+}
+
+
+/**
+ * tlsv1_set_private_key - Set private key
+ * @cred: TLSv1 credentials from tlsv1_cred_alloc()
+ * @private_key: File or reference name for the key in PEM or DER format
+ * @private_key_passwd: Passphrase for decrypted private key, %NULL if no
+ * passphrase is used.
+ * @private_key_blob: private_key as inlined data or %NULL if not used
+ * @private_key_blob_len: private_key_blob length
+ * Returns: 0 on success, -1 on failure
+ */
+int tlsv1_set_private_key(struct tlsv1_credentials *cred,
+ const char *private_key,
+ const char *private_key_passwd,
+ const u8 *private_key_blob,
+ size_t private_key_blob_len)
+{
+ crypto_private_key_free(cred->key);
+ cred->key = NULL;
+
+ if (private_key_blob)
+ return tlsv1_set_key(cred, private_key_blob,
+ private_key_blob_len,
+ private_key_passwd);
+
+ if (private_key) {
+ u8 *buf = NULL;
+ size_t len;
+ int ret;
+
+ //buf = (u8 *) os_readfile(private_key, &len);
+ if (buf == NULL) {
+ wpa_printf(MSG_INFO, "TLSv1: Failed to read '%s'",
+ private_key);
+ return -1;
+ }
+
+ ret = tlsv1_set_key(cred, buf, len, private_key_passwd);
+ os_free(buf);
+ return ret;
+ }
+
+ return 0;
+}
+
+
+static int tlsv1_set_dhparams_der(struct tlsv1_credentials *cred,
+ const u8 *dh, size_t len)
+{
+ struct asn1_hdr hdr;
+ const u8 *pos, *end;
+
+ pos = dh;
+ end = dh + len;
+
+ /*
+ * DHParameter ::= SEQUENCE {
+ * prime INTEGER, -- p
+ * base INTEGER, -- g
+ * privateValueLength INTEGER OPTIONAL }
+ */
+
+ /* DHParamer ::= SEQUENCE */
+ if (asn1_get_next(pos, len, &hdr) < 0 ||
+ hdr.class != ASN1_CLASS_UNIVERSAL ||
+ hdr.tag != ASN1_TAG_SEQUENCE) {
+ wpa_printf(MSG_DEBUG, "DH: DH parameters did not start with a "
+ "valid SEQUENCE - found class %d tag 0x%x",
+ hdr.class, hdr.tag);
+ return -1;
+ }
+ pos = hdr.payload;
+
+ /* prime INTEGER */
+ if (asn1_get_next(pos, end - pos, &hdr) < 0)
+ return -1;
+
+ if (hdr.class != ASN1_CLASS_UNIVERSAL ||
+ hdr.tag != ASN1_TAG_INTEGER) {
+ wpa_printf(MSG_DEBUG, "DH: No INTEGER tag found for p; "
+ "class=%d tag=0x%x", hdr.class, hdr.tag);
+ return -1;
+ }
+
+ wpa_hexdump(MSG_MSGDUMP, "DH: prime (p)", hdr.payload, hdr.length);
+ if (hdr.length == 0)
+ return -1;
+ os_free(cred->dh_p);
+ cred->dh_p = os_malloc(hdr.length);
+ if (cred->dh_p == NULL)
+ return -1;
+ os_memcpy(cred->dh_p, hdr.payload, hdr.length);
+ cred->dh_p_len = hdr.length;
+ pos = hdr.payload + hdr.length;
+
+ /* base INTEGER */
+ if (asn1_get_next(pos, end - pos, &hdr) < 0)
+ return -1;
+
+ if (hdr.class != ASN1_CLASS_UNIVERSAL ||
+ hdr.tag != ASN1_TAG_INTEGER) {
+ wpa_printf(MSG_DEBUG, "DH: No INTEGER tag found for g; "
+ "class=%d tag=0x%x", hdr.class, hdr.tag);
+ return -1;
+ }
+
+ wpa_hexdump(MSG_MSGDUMP, "DH: base (g)", hdr.payload, hdr.length);
+ if (hdr.length == 0)
+ return -1;
+ os_free(cred->dh_g);
+ cred->dh_g = os_malloc(hdr.length);
+ if (cred->dh_g == NULL)
+ return -1;
+ os_memcpy(cred->dh_g, hdr.payload, hdr.length);
+ cred->dh_g_len = hdr.length;
+
+ return 0;
+}
+
+
+static const char *pem_dhparams_begin = "-----BEGIN DH PARAMETERS-----";
+static const char *pem_dhparams_end = "-----END DH PARAMETERS-----";
+
+
+static int tlsv1_set_dhparams_blob(struct tlsv1_credentials *cred,
+ const u8 *buf, size_t len)
+{
+ const u8 *pos, *end;
+ unsigned char *der;
+ size_t der_len;
+
+ pos = search_tag(pem_dhparams_begin, buf, len);
+ if (!pos) {
+ wpa_printf(MSG_DEBUG, "TLSv1: No PEM dhparams tag found - "
+ "assume DER format");
+ return tlsv1_set_dhparams_der(cred, buf, len);
+ }
+
+ wpa_printf(MSG_DEBUG, "TLSv1: Converting PEM format dhparams into DER "
+ "format");
+
+ pos += os_strlen(pem_dhparams_begin);
+ end = search_tag(pem_dhparams_end, pos, buf + len - pos);
+ if (end == NULL) {
+ wpa_printf(MSG_INFO, "TLSv1: Could not find PEM dhparams end "
+ "tag (%s)", pem_dhparams_end);
+ return -1;
+ }
+
+ der = base64_decode(pos, end - pos, &der_len);
+ if (der == NULL) {
+ wpa_printf(MSG_INFO, "TLSv1: Could not decode PEM dhparams");
+ return -1;
+ }
+
+ if (tlsv1_set_dhparams_der(cred, der, der_len) < 0) {
+ wpa_printf(MSG_INFO, "TLSv1: Failed to parse PEM dhparams "
+ "DER conversion");
+ os_free(der);
+ return -1;
+ }
+
+ os_free(der);
+
+ return 0;
+}
+
+
+/**
+ * tlsv1_set_dhparams - Set Diffie-Hellman parameters
+ * @cred: TLSv1 credentials from tlsv1_cred_alloc()
+ * @dh_file: File or reference name for the DH params in PEM or DER format
+ * @dh_blob: DH params as inlined data or %NULL if not used
+ * @dh_blob_len: dh_blob length
+ * Returns: 0 on success, -1 on failure
+ */
+int tlsv1_set_dhparams(struct tlsv1_credentials *cred, const char *dh_file,
+ const u8 *dh_blob, size_t dh_blob_len)
+{
+ if (dh_blob)
+ return tlsv1_set_dhparams_blob(cred, dh_blob, dh_blob_len);
+
+ if (dh_file) {
+ u8 *buf = NULL;
+ size_t len;
+ int ret;
+
+ //buf = (u8 *) os_readfile(dh_file, &len);
+ if (buf == NULL) {
+ wpa_printf(MSG_INFO, "TLSv1: Failed to read '%s'",
+ dh_file);
+ return -1;
+ }
+
+ ret = tlsv1_set_dhparams_blob(cred, buf, len);
+ os_free(buf);
+ return ret;
+ }
+
+ return 0;
+}
--- /dev/null
+/*
+ * TLSv1 Record Protocol
+ * Copyright (c) 2006-2011, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "wpa/includes.h"
+
+#include "wpa/common.h"
+#include "crypto/md5.h"
+#include "crypto/sha1.h"
+#include "crypto/sha256.h"
+#include "wpa2/tls/tlsv1_common.h"
+#include "wpa2/tls/tlsv1_record.h"
+
+#include "wpa2/eap_peer/eap_i.h"
+
+/**
+ * tlsv1_record_set_cipher_suite - TLS record layer: Set cipher suite
+ * @rl: Pointer to TLS record layer data
+ * @cipher_suite: New cipher suite
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function is used to prepare TLS record layer for cipher suite change.
+ * tlsv1_record_change_write_cipher() and
+ * tlsv1_record_change_read_cipher() functions can then be used to change the
+ * currently used ciphers.
+ */
+int tlsv1_record_set_cipher_suite(struct tlsv1_record_layer *rl,
+ u16 cipher_suite)
+{
+ const struct tls_cipher_suite *suite;
+ const struct tls_cipher_data *data;
+
+ wpa_printf(MSG_DEBUG, "TLSv1: Selected cipher suite: 0x%04x",
+ cipher_suite);
+ rl->cipher_suite = cipher_suite;
+
+ suite = tls_get_cipher_suite(cipher_suite);
+ if (suite == NULL)
+ return -1;
+
+ if (suite->hash == TLS_HASH_MD5) {
+ rl->hash_alg = CRYPTO_HASH_ALG_HMAC_MD5;
+ rl->hash_size = MD5_MAC_LEN;
+ } else if (suite->hash == TLS_HASH_SHA) {
+ rl->hash_alg = CRYPTO_HASH_ALG_HMAC_SHA1;
+ rl->hash_size = SHA1_MAC_LEN;
+ } else if (suite->hash == TLS_HASH_SHA256) {
+ rl->hash_alg = CRYPTO_HASH_ALG_HMAC_SHA256;
+ rl->hash_size = SHA256_MAC_LEN;
+ }
+
+ data = tls_get_cipher_data(suite->cipher);
+ if (data == NULL)
+ return -1;
+
+ rl->key_material_len = data->key_material;
+ rl->iv_size = data->block_size;
+ rl->cipher_alg = data->alg;
+
+ return 0;
+}
+
+
+/**
+ * tlsv1_record_change_write_cipher - TLS record layer: Change write cipher
+ * @rl: Pointer to TLS record layer data
+ * Returns: 0 on success (cipher changed), -1 on failure
+ *
+ * This function changes TLS record layer to use the new cipher suite
+ * configured with tlsv1_record_set_cipher_suite() for writing.
+ */
+int tlsv1_record_change_write_cipher(struct tlsv1_record_layer *rl)
+{
+ wpa_printf(MSG_DEBUG, "TLSv1: Record Layer - New write cipher suite "
+ "0x%04x", rl->cipher_suite);
+ rl->write_cipher_suite = rl->cipher_suite;
+ os_memset(rl->write_seq_num, 0, TLS_SEQ_NUM_LEN);
+
+ if (rl->write_cbc) {
+ if (wpa2_crypto_funcs.crypto_cipher_deinit) {
+ wpa2_crypto_funcs.crypto_cipher_deinit(rl->write_cbc);
+ rl->write_cbc = NULL;
+ } else {
+ wpa_printf(MSG_ERROR, "Fail to register crypto cipher deinit function!\r\n");
+ return -1;
+ }
+ }
+ if (rl->cipher_alg != CRYPTO_CIPHER_NULL) {
+ if (wpa2_crypto_funcs.crypto_cipher_init) {
+ rl->write_cbc = wpa2_crypto_funcs.crypto_cipher_init(rl->cipher_alg,
+ rl->write_iv, rl->write_key,
+ rl->key_material_len);
+ } else {
+ wpa_printf(MSG_ERROR, "Fail to register crypto_cipher_init function!\r\n");
+ return -1;
+ }
+
+ if (rl->write_cbc == NULL) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Failed to initialize "
+ "cipher");
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+
+/**
+ * tlsv1_record_change_read_cipher - TLS record layer: Change read cipher
+ * @rl: Pointer to TLS record layer data
+ * Returns: 0 on success (cipher changed), -1 on failure
+ *
+ * This function changes TLS record layer to use the new cipher suite
+ * configured with tlsv1_record_set_cipher_suite() for reading.
+ */
+int tlsv1_record_change_read_cipher(struct tlsv1_record_layer *rl)
+{
+ wpa_printf(MSG_DEBUG, "TLSv1: Record Layer - New read cipher suite "
+ "0x%04x \n", rl->cipher_suite);
+ rl->read_cipher_suite = rl->cipher_suite;
+ os_memset(rl->read_seq_num, 0, TLS_SEQ_NUM_LEN);
+
+ if (rl->read_cbc) {
+ if (wpa2_crypto_funcs.crypto_cipher_deinit) {
+ wpa2_crypto_funcs.crypto_cipher_deinit(rl->read_cbc);
+ rl->read_cbc = NULL;
+ } else {
+ wpa_printf(MSG_ERROR, "Fail to register crypto cipher deinit function!\r\n");
+ return -1;
+ }
+ }
+
+ if (rl->cipher_alg != CRYPTO_CIPHER_NULL) {
+ if(wpa2_crypto_funcs.crypto_cipher_init) {
+ rl->read_cbc = wpa2_crypto_funcs.crypto_cipher_init(rl->cipher_alg,
+ rl->read_iv, rl->read_key,
+ rl->key_material_len);
+ } else {
+ wpa_printf(MSG_ERROR, "Fail to register crypto_cipher_init function!\r\n");
+ return -1;
+ }
+ if (rl->read_cbc == NULL) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Failed to initialize "
+ "cipher");
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+
+/**
+ * tlsv1_record_send - TLS record layer: Send a message
+ * @rl: Pointer to TLS record layer data
+ * @content_type: Content type (TLS_CONTENT_TYPE_*)
+ * @buf: Buffer for the generated TLS message (needs to have extra space for
+ * header, IV (TLS v1.1), and HMAC)
+ * @buf_size: Maximum buf size
+ * @payload: Payload to be sent
+ * @payload_len: Length of the payload
+ * @out_len: Buffer for returning the used buf length
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function fills in the TLS record layer header, adds HMAC, and encrypts
+ * the data using the current write cipher.
+ */
+int tlsv1_record_send(struct tlsv1_record_layer *rl, u8 content_type, u8 *buf,
+ size_t buf_size, const u8 *payload, size_t payload_len,
+ size_t *out_len)
+{
+ u8 *pos, *ct_start, *length, *cpayload;
+ struct crypto_hash *hmac = NULL;
+ size_t clen;
+ int explicit_iv;
+
+ pos = buf;
+ if (pos + TLS_RECORD_HEADER_LEN > buf + buf_size)
+ return -1;
+
+ /* ContentType type */
+ ct_start = pos;
+ *pos++ = content_type;
+ /* ProtocolVersion version */
+ WPA_PUT_BE16(pos, rl->tls_version);
+ pos += 2;
+ /* uint16 length */
+ length = pos;
+ WPA_PUT_BE16(length, payload_len);
+ pos += 2;
+
+ cpayload = pos;
+ explicit_iv = rl->write_cipher_suite != TLS_NULL_WITH_NULL_NULL &&
+ rl->iv_size && rl->tls_version >= TLS_VERSION_1_1;
+ if (explicit_iv) {
+ /* opaque IV[Cipherspec.block_length] */
+ if (pos + rl->iv_size > buf + buf_size)
+ return -1;
+
+ /*
+ * Use random number R per the RFC 4346, 6.2.3.2 CBC Block
+ * Cipher option 2a.
+ */
+
+ if (os_get_random(pos, rl->iv_size))
+ return -1;
+ pos += rl->iv_size;
+ }
+
+ /*
+ * opaque fragment[TLSPlaintext.length]
+ * (opaque content[TLSCompressed.length] in GenericBlockCipher)
+ */
+ if (pos + payload_len > buf + buf_size)
+ return -1;
+ os_memmove(pos, payload, payload_len);
+ pos += payload_len;
+
+ if (rl->write_cipher_suite != TLS_NULL_WITH_NULL_NULL) {
+ /*
+ * MAC calculated over seq_num + TLSCompressed.type +
+ * TLSCompressed.version + TLSCompressed.length +
+ * TLSCompressed.fragment
+ */
+ if (wpa2_crypto_funcs.crypto_hash_init) {
+ hmac = wpa2_crypto_funcs.crypto_hash_init(rl->hash_alg, rl->write_mac_secret, rl->hash_size);
+ } else {
+ wpa_printf(MSG_ERROR, "In function %s, fail to register crypto hash init!\r\n", __FUNCTION__);
+ return -1;
+ }
+ if (hmac == NULL) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Record Layer - Failed "
+ "to initialize HMAC");
+ return -1;
+ }
+ if (wpa2_crypto_funcs.crypto_hash_update) {
+ wpa2_crypto_funcs.crypto_hash_update(hmac, rl->write_seq_num, TLS_SEQ_NUM_LEN);
+ /* type + version + length + fragment */
+ wpa2_crypto_funcs.crypto_hash_update(hmac, ct_start, TLS_RECORD_HEADER_LEN);
+ wpa2_crypto_funcs.crypto_hash_update(hmac, payload, payload_len);
+ } else {
+ wpa_printf(MSG_ERROR, "In function %s, fail to register crypto hash update!\r\n", __FUNCTION__);
+ return -1;
+ }
+ clen = buf + buf_size - pos;
+ if (clen < rl->hash_size) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Record Layer - Not "
+ "enough room for MAC");
+ if (wpa2_crypto_funcs.crypto_hash_finish) {
+ wpa2_crypto_funcs.crypto_hash_finish(hmac, NULL, NULL);
+ } else {
+ wpa_printf(MSG_ERROR, "In function %s, fail to register crypto hash finish function!\r\n", __FUNCTION__);
+ return -1;
+ }
+
+ return -1;
+ }
+
+ if (wpa2_crypto_funcs.crypto_hash_finish) {
+ if ((int)wpa2_crypto_funcs.crypto_hash_finish(hmac, pos, (int *)&clen) < 0) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Record Layer - Failed to calculate HMAC");
+ return -1;
+ }
+ } else {
+ wpa_printf(MSG_ERROR, "In function %s, fail to register crypto_hash_finish function!\r\n",__FUNCTION__);
+ return -1;
+ }
+ wpa_hexdump(MSG_MSGDUMP, "TLSv1: Record Layer - Write HMAC",
+ pos, clen);
+ pos += clen;
+ if (rl->iv_size) {
+ size_t len = pos - cpayload;
+ size_t pad;
+ pad = (len + 1) % rl->iv_size;
+ if (pad)
+ pad = rl->iv_size - pad;
+ if (pos + pad + 1 > buf + buf_size) {
+ wpa_printf(MSG_DEBUG, "TLSv1: No room for "
+ "block cipher padding");
+ return -1;
+ }
+ os_memset(pos, pad, pad + 1);
+ pos += pad + 1;
+ }
+
+ if (wpa2_crypto_funcs.crypto_cipher_encrypt) {
+ if ((int)wpa2_crypto_funcs.crypto_cipher_encrypt(rl->write_cbc, cpayload,
+ cpayload, pos - cpayload) < 0)
+ return -1;
+ } else {
+ wpa_printf(MSG_ERROR, "Fail to register crypto_cipher_encrypt function!\r\n");
+ return -1;
+ }
+ }
+
+ WPA_PUT_BE16(length, pos - length - 2);
+ inc_byte_array(rl->write_seq_num, TLS_SEQ_NUM_LEN);
+
+ *out_len = pos - buf;
+
+ return 0;
+}
+
+
+/**
+ * tlsv1_record_receive - TLS record layer: Process a received message
+ * @rl: Pointer to TLS record layer data
+ * @in_data: Received data
+ * @in_len: Length of the received data
+ * @out_data: Buffer for output data (must be at least as long as in_data)
+ * @out_len: Set to maximum out_data length by caller; used to return the
+ * length of the used data
+ * @alert: Buffer for returning an alert value on failure
+ * Returns: Number of bytes used from in_data on success, 0 if record was not
+ * complete (more data needed), or -1 on failure
+ *
+ * This function decrypts the received message, verifies HMAC and TLS record
+ * layer header.
+ */
+int tlsv1_record_receive(struct tlsv1_record_layer *rl,
+ const u8 *in_data, size_t in_len,
+ u8 *out_data, size_t *out_len, u8 *alert)
+{
+ size_t i, rlen, hlen;
+ u8 padlen;
+ struct crypto_hash *hmac = NULL;
+ u8 len[2], hash[100];
+ int force_mac_error = 0;
+ u8 ct;
+
+ if (in_len < TLS_RECORD_HEADER_LEN) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Too short record (in_len=%lu) - "
+ "need more data",
+ (unsigned long) in_len);
+ wpa_hexdump(MSG_MSGDUMP, "TLSv1: Record Layer - Received",
+ in_data, in_len);
+ return 0;
+ }
+
+ ct = in_data[0];
+ rlen = WPA_GET_BE16(in_data + 3);
+ wpa_printf(MSG_DEBUG, "TLSv1: Received content type %d version %d.%d "
+ "length %d", ct, in_data[1], in_data[2], (int) rlen);
+
+ /*
+ * TLS v1.0 and v1.1 RFCs were not exactly clear on the use of the
+ * protocol version in record layer. As such, accept any {03,xx} value
+ * to remain compatible with existing implementations.
+ */
+ if (in_data[1] != 0x03) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Unexpected protocol version "
+ "%u.%u", in_data[1], in_data[2]);
+ *alert = TLS_ALERT_PROTOCOL_VERSION;
+ return -1;
+ }
+
+ /* TLSCiphertext must not be more than 2^14+2048 bytes */
+ if (TLS_RECORD_HEADER_LEN + rlen > 18432) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Record overflow (len=%lu)",
+ (unsigned long) (TLS_RECORD_HEADER_LEN + rlen));
+ *alert = TLS_ALERT_RECORD_OVERFLOW;
+ return -1;
+ }
+
+ in_data += TLS_RECORD_HEADER_LEN;
+ in_len -= TLS_RECORD_HEADER_LEN;
+
+ if (rlen > in_len) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Not all record data included "
+ "(rlen=%lu > in_len=%lu)",
+ (unsigned long) rlen, (unsigned long) in_len);
+ return 0;
+ }
+
+ wpa_hexdump(MSG_MSGDUMP, "TLSv1: Record Layer - Received",
+ in_data, rlen);
+
+ if (ct != TLS_CONTENT_TYPE_HANDSHAKE &&
+ ct != TLS_CONTENT_TYPE_CHANGE_CIPHER_SPEC &&
+ ct != TLS_CONTENT_TYPE_ALERT &&
+ ct != TLS_CONTENT_TYPE_APPLICATION_DATA) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Ignore record with unknown "
+ "content type 0x%x", ct);
+ *alert = TLS_ALERT_UNEXPECTED_MESSAGE;
+ return -1;
+ }
+
+ in_len = rlen;
+
+ if (*out_len < in_len) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Not enough output buffer for "
+ "processing received record");
+ *alert = TLS_ALERT_INTERNAL_ERROR;
+ return -1;
+ }
+
+ if (rl->read_cipher_suite != TLS_NULL_WITH_NULL_NULL) {
+ size_t plen;
+ if (wpa2_crypto_funcs.crypto_cipher_decrypt) {
+ if ((int)wpa2_crypto_funcs.crypto_cipher_decrypt(rl->read_cbc, in_data,
+ out_data, in_len) < 0) {
+ *alert = TLS_ALERT_DECRYPTION_FAILED;
+ return -1;
+ }
+ } else {
+ wpa_printf(MSG_ERROR, "Fail to register crypto cipher decrypt function. \r\n");
+ *alert = TLS_ALERT_DECRYPTION_FAILED;
+ return -1;
+ }
+ plen = in_len;
+ wpa_hexdump_key(MSG_MSGDUMP, "TLSv1: Record Layer - Decrypted "
+ "data", out_data, plen);
+
+ if (rl->iv_size) {
+ /*
+ * TLS v1.0 defines different alert values for various
+ * failures. That may information to aid in attacks, so
+ * use the same bad_record_mac alert regardless of the
+ * issues.
+ *
+ * In addition, instead of returning immediately on
+ * error, run through the MAC check to make timing
+ * attacks more difficult.
+ */
+
+ if (rl->tls_version >= TLS_VERSION_1_1) {
+ /* Remove opaque IV[Cipherspec.block_length] */
+ if (plen < rl->iv_size) {
+ wpa_printf(MSG_DEBUG, "TLSv1.1: Not "
+ "enough room for IV");
+ force_mac_error = 1;
+ goto check_mac;
+ }
+ os_memmove(out_data, out_data + rl->iv_size,
+ plen - rl->iv_size);
+ plen -= rl->iv_size;
+ }
+
+ /* Verify and remove padding */
+ if (plen == 0) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Too short record"
+ " (no pad)");
+ force_mac_error = 1;
+ goto check_mac;
+ }
+ padlen = out_data[plen - 1];
+ if (padlen >= plen) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Incorrect pad "
+ "length (%u, plen=%lu) in "
+ "received record",
+ padlen, (unsigned long) plen);
+ force_mac_error = 1;
+ goto check_mac;
+ }
+ for (i = plen - padlen - 1; i < plen - 1; i++) {
+ if (out_data[i] != padlen) {
+ wpa_hexdump(MSG_DEBUG,
+ "TLSv1: Invalid pad in "
+ "received record",
+ out_data + plen - padlen -
+ 1, padlen + 1);
+ force_mac_error = 1;
+ goto check_mac;
+ }
+ }
+
+ plen -= padlen + 1;
+
+ wpa_hexdump_key(MSG_MSGDUMP, "TLSv1: Record Layer - "
+ "Decrypted data with IV and padding "
+ "removed", out_data, plen);
+ }
+
+ check_mac:
+ if (plen < rl->hash_size) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Too short record; no "
+ "hash value");
+ *alert = TLS_ALERT_BAD_RECORD_MAC;
+ return -1;
+ }
+
+ plen -= rl->hash_size;
+
+ if (wpa2_crypto_funcs.crypto_hash_init) {
+ hmac = wpa2_crypto_funcs.crypto_hash_init(rl->hash_alg, rl->read_mac_secret, rl->hash_size);
+ } else {
+ wpa_printf(MSG_ERROR, "In function %s, fail to register crypto_hash_init function!\r\n", __FUNCTION__);
+ return -1;
+ }
+
+ if (hmac == NULL) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Record Layer - Failed "
+ "to initialize HMAC");
+ *alert = TLS_ALERT_INTERNAL_ERROR;
+ return -1;
+ }
+
+ if (wpa2_crypto_funcs.crypto_hash_update) {
+ wpa2_crypto_funcs.crypto_hash_update(hmac, rl->read_seq_num, TLS_SEQ_NUM_LEN);
+ /* type + version + length + fragment */
+ wpa2_crypto_funcs.crypto_hash_update(hmac, in_data - TLS_RECORD_HEADER_LEN, 3);
+ WPA_PUT_BE16(len, plen);
+ wpa2_crypto_funcs.crypto_hash_update(hmac, len, 2);
+ wpa2_crypto_funcs.crypto_hash_update(hmac, out_data, plen);
+ } else {
+ wpa_printf(MSG_ERROR, "In function %s, fail to register crypto hash update function!\r\n", __FUNCTION__);
+ return -1;
+ }
+ hlen = sizeof(hash);
+ if (wpa2_crypto_funcs.crypto_hash_finish) {
+ if ((int)wpa2_crypto_funcs.crypto_hash_finish(hmac, hash, (int *)&hlen) < 0) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Record Layer - Failed to calculate HMAC");
+ *alert = TLS_ALERT_INTERNAL_ERROR;
+ return -1;
+ }
+ } else {
+ wpa_printf(MSG_ERROR, "In function %s, fail to register crypto_hash_finish function!\r\n", __FUNCTION__);
+ *alert = TLS_ALERT_INTERNAL_ERROR;
+ return -1;
+ }
+ if (hlen != rl->hash_size ||
+ os_memcmp(hash, out_data + plen, hlen) != 0 ||
+ force_mac_error) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Invalid HMAC value in "
+ "received message (force_mac_error=%d)",
+ force_mac_error);
+ *alert = TLS_ALERT_BAD_RECORD_MAC;
+ return -1;
+ }
+
+ *out_len = plen;
+ } else {
+ os_memcpy(out_data, in_data, in_len);
+ *out_len = in_len;
+ }
+
+ /* TLSCompressed must not be more than 2^14+1024 bytes */
+ if (TLS_RECORD_HEADER_LEN + *out_len > 17408) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Record overflow (len=%lu)",
+ (unsigned long) (TLS_RECORD_HEADER_LEN + *out_len));
+ *alert = TLS_ALERT_RECORD_OVERFLOW;
+ return -1;
+ }
+
+ inc_byte_array(rl->read_seq_num, TLS_SEQ_NUM_LEN);
+
+ return TLS_RECORD_HEADER_LEN + rlen;
+}
--- /dev/null
+/*
+ * TLS v1.0/v1.1/v1.2 server (RFC 2246, RFC 4346, RFC 5246)
+ * Copyright (c) 2006-2011, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "wpa/includes.h"
+
+#include "wpa/common.h"
+#include "crypto/sha1.h"
+#include "wpa2/tls/tls.h"
+#include "wpa2/tls/tlsv1_common.h"
+#include "wpa2/tls/tlsv1_record.h"
+#include "wpa2/tls/tlsv1_server.h"
+#include "wpa2/tls/tlsv1_server_i.h"
+
+/* TODO:
+ * Support for a message fragmented across several records (RFC 2246, 6.2.1)
+ */
+
+
+void tlsv1_server_alert(struct tlsv1_server *conn, u8 level, u8 description)
+{
+ conn->alert_level = level;
+ conn->alert_description = description;
+}
+
+
+int tlsv1_server_derive_keys(struct tlsv1_server *conn,
+ const u8 *pre_master_secret,
+ size_t pre_master_secret_len)
+{
+ u8 seed[2 * TLS_RANDOM_LEN];
+ u8 key_block[TLS_MAX_KEY_BLOCK_LEN];
+ u8 *pos;
+ size_t key_block_len;
+
+ if (pre_master_secret) {
+ wpa_hexdump_key(MSG_MSGDUMP, "TLSv1: pre_master_secret",
+ pre_master_secret, pre_master_secret_len);
+ os_memcpy(seed, conn->client_random, TLS_RANDOM_LEN);
+ os_memcpy(seed + TLS_RANDOM_LEN, conn->server_random,
+ TLS_RANDOM_LEN);
+ if (tls_prf(conn->rl.tls_version,
+ pre_master_secret, pre_master_secret_len,
+ "master secret", seed, 2 * TLS_RANDOM_LEN,
+ conn->master_secret, TLS_MASTER_SECRET_LEN)) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Failed to derive "
+ "master_secret");
+ return -1;
+ }
+ wpa_hexdump_key(MSG_MSGDUMP, "TLSv1: master_secret",
+ conn->master_secret, TLS_MASTER_SECRET_LEN);
+ }
+
+ os_memcpy(seed, conn->server_random, TLS_RANDOM_LEN);
+ os_memcpy(seed + TLS_RANDOM_LEN, conn->client_random, TLS_RANDOM_LEN);
+ key_block_len = 2 * (conn->rl.hash_size + conn->rl.key_material_len +
+ conn->rl.iv_size);
+ if (tls_prf(conn->rl.tls_version,
+ conn->master_secret, TLS_MASTER_SECRET_LEN,
+ "key expansion", seed, 2 * TLS_RANDOM_LEN,
+ key_block, key_block_len)) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Failed to derive key_block");
+ return -1;
+ }
+ wpa_hexdump_key(MSG_MSGDUMP, "TLSv1: key_block",
+ key_block, key_block_len);
+
+ pos = key_block;
+
+ /* client_write_MAC_secret */
+ os_memcpy(conn->rl.read_mac_secret, pos, conn->rl.hash_size);
+ pos += conn->rl.hash_size;
+ /* server_write_MAC_secret */
+ os_memcpy(conn->rl.write_mac_secret, pos, conn->rl.hash_size);
+ pos += conn->rl.hash_size;
+
+ /* client_write_key */
+ os_memcpy(conn->rl.read_key, pos, conn->rl.key_material_len);
+ pos += conn->rl.key_material_len;
+ /* server_write_key */
+ os_memcpy(conn->rl.write_key, pos, conn->rl.key_material_len);
+ pos += conn->rl.key_material_len;
+
+ /* client_write_IV */
+ os_memcpy(conn->rl.read_iv, pos, conn->rl.iv_size);
+ pos += conn->rl.iv_size;
+ /* server_write_IV */
+ os_memcpy(conn->rl.write_iv, pos, conn->rl.iv_size);
+ pos += conn->rl.iv_size;
+
+ return 0;
+}
+
+
+/**
+ * tlsv1_server_handshake - Process TLS handshake
+ * @conn: TLSv1 server connection data from tlsv1_server_init()
+ * @in_data: Input data from TLS peer
+ * @in_len: Input data length
+ * @out_len: Length of the output buffer.
+ * Returns: Pointer to output data, %NULL on failure
+ */
+u8 * tlsv1_server_handshake(struct tlsv1_server *conn,
+ const u8 *in_data, size_t in_len,
+ size_t *out_len)
+{
+ const u8 *pos, *end;
+ u8 *msg = NULL, *in_msg, *in_pos, *in_end, alert, ct;
+ size_t in_msg_len;
+ int used;
+
+ if (in_data == NULL || in_len == 0) {
+ wpa_printf(MSG_DEBUG, "TLSv1: No input data to server");
+ return NULL;
+ }
+
+ pos = in_data;
+ end = in_data + in_len;
+ in_msg = os_malloc(in_len);
+ if (in_msg == NULL)
+ return NULL;
+
+ /* Each received packet may include multiple records */
+ while (pos < end) {
+ in_msg_len = in_len;
+ used = tlsv1_record_receive(&conn->rl, pos, end - pos,
+ in_msg, &in_msg_len, &alert);
+ if (used < 0) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Processing received "
+ "record failed");
+ tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, alert);
+ goto failed;
+ }
+ if (used == 0) {
+ /* need more data */
+ wpa_printf(MSG_DEBUG, "TLSv1: Partial processing not "
+ "yet supported");
+ tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, alert);
+ goto failed;
+ }
+ ct = pos[0];
+
+ in_pos = in_msg;
+ in_end = in_msg + in_msg_len;
+
+ /* Each received record may include multiple messages of the
+ * same ContentType. */
+ while (in_pos < in_end) {
+ in_msg_len = in_end - in_pos;
+ if (tlsv1_server_process_handshake(conn, ct, in_pos,
+ &in_msg_len) < 0)
+ goto failed;
+ in_pos += in_msg_len;
+ }
+
+ pos += used;
+ }
+
+ os_free(in_msg);
+ in_msg = NULL;
+
+ msg = tlsv1_server_handshake_write(conn, out_len);
+
+failed:
+ os_free(in_msg);
+ if (conn->alert_level) {
+ if (conn->state == FAILED) {
+ /* Avoid alert loops */
+ wpa_printf(MSG_DEBUG, "TLSv1: Drop alert loop");
+ os_free(msg);
+ return NULL;
+ }
+ conn->state = FAILED;
+ os_free(msg);
+ msg = tlsv1_server_send_alert(conn, conn->alert_level,
+ conn->alert_description,
+ out_len);
+ }
+
+ return msg;
+}
+
+
+/**
+ * tlsv1_server_encrypt - Encrypt data into TLS tunnel
+ * @conn: TLSv1 server connection data from tlsv1_server_init()
+ * @in_data: Pointer to plaintext data to be encrypted
+ * @in_len: Input buffer length
+ * @out_data: Pointer to output buffer (encrypted TLS data)
+ * @out_len: Maximum out_data length
+ * Returns: Number of bytes written to out_data, -1 on failure
+ *
+ * This function is used after TLS handshake has been completed successfully to
+ * send data in the encrypted tunnel.
+ */
+int tlsv1_server_encrypt(struct tlsv1_server *conn,
+ const u8 *in_data, size_t in_len,
+ u8 *out_data, size_t out_len)
+{
+ size_t rlen;
+
+ wpa_hexdump_key(MSG_MSGDUMP, "TLSv1: Plaintext AppData",
+ in_data, in_len);
+
+ if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_APPLICATION_DATA,
+ out_data, out_len, in_data, in_len, &rlen) < 0) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Failed to create a record");
+ tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_INTERNAL_ERROR);
+ return -1;
+ }
+
+ return rlen;
+}
+
+
+/**
+ * tlsv1_server_decrypt - Decrypt data from TLS tunnel
+ * @conn: TLSv1 server connection data from tlsv1_server_init()
+ * @in_data: Pointer to input buffer (encrypted TLS data)
+ * @in_len: Input buffer length
+ * @out_data: Pointer to output buffer (decrypted data from TLS tunnel)
+ * @out_len: Maximum out_data length
+ * Returns: Number of bytes written to out_data, -1 on failure
+ *
+ * This function is used after TLS handshake has been completed successfully to
+ * receive data from the encrypted tunnel.
+ */
+int tlsv1_server_decrypt(struct tlsv1_server *conn,
+ const u8 *in_data, size_t in_len,
+ u8 *out_data, size_t out_len)
+{
+ const u8 *in_end, *pos;
+ int used;
+ u8 alert, *out_end, *out_pos, ct;
+ size_t olen;
+
+ pos = in_data;
+ in_end = in_data + in_len;
+ out_pos = out_data;
+ out_end = out_data + out_len;
+
+ while (pos < in_end) {
+ ct = pos[0];
+ olen = out_end - out_pos;
+ used = tlsv1_record_receive(&conn->rl, pos, in_end - pos,
+ out_pos, &olen, &alert);
+ if (used < 0) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Record layer processing "
+ "failed");
+ tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, alert);
+ return -1;
+ }
+ if (used == 0) {
+ /* need more data */
+ wpa_printf(MSG_DEBUG, "TLSv1: Partial processing not "
+ "yet supported");
+ tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, alert);
+ return -1;
+ }
+
+ if (ct == TLS_CONTENT_TYPE_ALERT) {
+ if (olen < 2) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Alert "
+ "underflow");
+ tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_DECODE_ERROR);
+ return -1;
+ }
+ wpa_printf(MSG_DEBUG, "TLSv1: Received alert %d:%d",
+ out_pos[0], out_pos[1]);
+ if (out_pos[0] == TLS_ALERT_LEVEL_WARNING) {
+ /* Continue processing */
+ pos += used;
+ continue;
+ }
+
+ tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ out_pos[1]);
+ return -1;
+ }
+
+ if (ct != TLS_CONTENT_TYPE_APPLICATION_DATA) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Unexpected content type "
+ "0x%x", pos[0]);
+ tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_UNEXPECTED_MESSAGE);
+ return -1;
+ }
+
+ out_pos += olen;
+ if (out_pos > out_end) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Buffer not large enough "
+ "for processing the received record");
+ tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_INTERNAL_ERROR);
+ return -1;
+ }
+
+ pos += used;
+ }
+
+ return out_pos - out_data;
+}
+
+
+/**
+ * tlsv1_server_global_init - Initialize TLSv1 server
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function must be called before using any other TLSv1 server functions.
+ */
+int tlsv1_server_global_init(void)
+{
+ return crypto_global_init();
+}
+
+
+/**
+ * tlsv1_server_global_deinit - Deinitialize TLSv1 server
+ *
+ * This function can be used to deinitialize the TLSv1 server that was
+ * initialized by calling tlsv1_server_global_init(). No TLSv1 server functions
+ * can be called after this before calling tlsv1_server_global_init() again.
+ */
+void tlsv1_server_global_deinit(void)
+{
+ crypto_global_deinit();
+}
+
+
+/**
+ * tlsv1_server_init - Initialize TLSv1 server connection
+ * @cred: Pointer to server credentials from tlsv1_server_cred_alloc()
+ * Returns: Pointer to TLSv1 server connection data or %NULL on failure
+ */
+struct tlsv1_server * tlsv1_server_init(struct tlsv1_credentials *cred)
+{
+ struct tlsv1_server *conn;
+ size_t count;
+ u16 *suites;
+
+ conn = (struct tlsv1_server *)os_zalloc(sizeof(*conn));
+ if (conn == NULL)
+ return NULL;
+
+ conn->cred = cred;
+
+ conn->state = CLIENT_HELLO;
+
+ if (tls_verify_hash_init(&conn->verify) < 0) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Failed to initialize verify "
+ "hash");
+ os_free(conn);
+ return NULL;
+ }
+
+ count = 0;
+ suites = conn->cipher_suites;
+ suites[count++] = TLS_RSA_WITH_AES_256_CBC_SHA;
+ suites[count++] = TLS_RSA_WITH_AES_128_CBC_SHA;
+ suites[count++] = TLS_RSA_WITH_3DES_EDE_CBC_SHA;
+ suites[count++] = TLS_RSA_WITH_RC4_128_SHA;
+ suites[count++] = TLS_RSA_WITH_RC4_128_MD5;
+ conn->num_cipher_suites = count;
+
+ return conn;
+}
+
+
+static void tlsv1_server_clear_data(struct tlsv1_server *conn)
+{
+ tlsv1_record_set_cipher_suite(&conn->rl, TLS_NULL_WITH_NULL_NULL);
+ tlsv1_record_change_write_cipher(&conn->rl);
+ tlsv1_record_change_read_cipher(&conn->rl);
+ tls_verify_hash_free(&conn->verify);
+
+ crypto_public_key_free(conn->client_rsa_key);
+ conn->client_rsa_key = NULL;
+
+ os_free(conn->session_ticket);
+ conn->session_ticket = NULL;
+ conn->session_ticket_len = 0;
+ conn->use_session_ticket = 0;
+
+ os_free(conn->dh_secret);
+ conn->dh_secret = NULL;
+ conn->dh_secret_len = 0;
+}
+
+
+/**
+ * tlsv1_server_deinit - Deinitialize TLSv1 server connection
+ * @conn: TLSv1 server connection data from tlsv1_server_init()
+ */
+void tlsv1_server_deinit(struct tlsv1_server *conn)
+{
+ tlsv1_server_clear_data(conn);
+ os_free(conn);
+}
+
+
+/**
+ * tlsv1_server_established - Check whether connection has been established
+ * @conn: TLSv1 server connection data from tlsv1_server_init()
+ * Returns: 1 if connection is established, 0 if not
+ */
+int tlsv1_server_established(struct tlsv1_server *conn)
+{
+ return conn->state == ESTABLISHED;
+}
+
+
+/**
+ * tlsv1_server_prf - Use TLS-PRF to derive keying material
+ * @conn: TLSv1 server connection data from tlsv1_server_init()
+ * @label: Label (e.g., description of the key) for PRF
+ * @server_random_first: seed is 0 = client_random|server_random,
+ * 1 = server_random|client_random
+ * @out: Buffer for output data from TLS-PRF
+ * @out_len: Length of the output buffer
+ * Returns: 0 on success, -1 on failure
+ */
+int tlsv1_server_prf(struct tlsv1_server *conn, const char *label,
+ int server_random_first, u8 *out, size_t out_len)
+{
+ u8 seed[2 * TLS_RANDOM_LEN];
+
+ if (conn->state != ESTABLISHED)
+ return -1;
+
+ if (server_random_first) {
+ os_memcpy(seed, conn->server_random, TLS_RANDOM_LEN);
+ os_memcpy(seed + TLS_RANDOM_LEN, conn->client_random,
+ TLS_RANDOM_LEN);
+ } else {
+ os_memcpy(seed, conn->client_random, TLS_RANDOM_LEN);
+ os_memcpy(seed + TLS_RANDOM_LEN, conn->server_random,
+ TLS_RANDOM_LEN);
+ }
+
+ return tls_prf(conn->rl.tls_version,
+ conn->master_secret, TLS_MASTER_SECRET_LEN,
+ label, seed, 2 * TLS_RANDOM_LEN, out, out_len);
+}
+
+
+/**
+ * tlsv1_server_get_cipher - Get current cipher name
+ * @conn: TLSv1 server connection data from tlsv1_server_init()
+ * @buf: Buffer for the cipher name
+ * @buflen: buf size
+ * Returns: 0 on success, -1 on failure
+ *
+ * Get the name of the currently used cipher.
+ */
+int tlsv1_server_get_cipher(struct tlsv1_server *conn, char *buf,
+ size_t buflen)
+{
+#ifndef ESPRESSIF_USE
+ char *cipher;
+
+ switch (conn->rl.cipher_suite) {
+ case TLS_RSA_WITH_RC4_128_MD5:
+ cipher = "RC4-MD5";
+ break;
+ case TLS_RSA_WITH_RC4_128_SHA:
+ cipher = "RC4-SHA";
+ break;
+ case TLS_RSA_WITH_DES_CBC_SHA:
+ cipher = "DES-CBC-SHA";
+ break;
+ case TLS_RSA_WITH_3DES_EDE_CBC_SHA:
+ cipher = "DES-CBC3-SHA";
+ break;
+ case TLS_DH_anon_WITH_AES_128_CBC_SHA:
+ cipher = "ADH-AES-128-SHA";
+ break;
+ case TLS_RSA_WITH_AES_256_CBC_SHA:
+ cipher = "AES-256-SHA";
+ break;
+ case TLS_RSA_WITH_AES_128_CBC_SHA:
+ cipher = "AES-128-SHA";
+ break;
+ default:
+ return -1;
+ }
+
+ //if (os_strlcpy(buf, cipher, buflen) >= buflen)
+ // return -1;
+
+ os_memcpy((u8 *)buf, (u8 *)cipher, buflen);
+
+ return 0;
+#else
+ char cipher[20];
+
+ switch (conn->rl.cipher_suite) {
+ case TLS_RSA_WITH_RC4_128_MD5:
+ strcpy(cipher, "RC4-MD5");
+ break;
+ case TLS_RSA_WITH_RC4_128_SHA:
+ strcpy(cipher, "RC4-SHA");
+ break;
+ case TLS_RSA_WITH_DES_CBC_SHA:
+ strcpy(cipher, "DES-CBC-SHA");
+ break;
+ case TLS_RSA_WITH_3DES_EDE_CBC_SHA:
+ strcpy(cipher, "DES-CBC3-SHA");
+ break;
+ case TLS_DH_anon_WITH_AES_128_CBC_SHA:
+ strcpy(cipher, "ADH-AES-128-SHA");
+ break;
+ case TLS_RSA_WITH_AES_256_CBC_SHA:
+ strcpy(cipher, "AES-256-SHA");
+ break;
+ case TLS_RSA_WITH_AES_128_CBC_SHA:
+ strcpy(cipher, "AES-128-SHA");
+ break;
+ default:
+ return -1;
+ }
+ os_memcpy((u8 *)buf, (u8 *)cipher, buflen);
+
+ return 0;
+#endif
+}
+
+
+/**
+ * tlsv1_server_shutdown - Shutdown TLS connection
+ * @conn: TLSv1 server connection data from tlsv1_server_init()
+ * Returns: 0 on success, -1 on failure
+ */
+int tlsv1_server_shutdown(struct tlsv1_server *conn)
+{
+ conn->state = CLIENT_HELLO;
+
+ if (tls_verify_hash_init(&conn->verify) < 0) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Failed to re-initialize verify "
+ "hash");
+ return -1;
+ }
+
+ tlsv1_server_clear_data(conn);
+
+ return 0;
+}
+
+
+/**
+ * tlsv1_server_resumed - Was session resumption used
+ * @conn: TLSv1 server connection data from tlsv1_server_init()
+ * Returns: 1 if current session used session resumption, 0 if not
+ */
+int tlsv1_server_resumed(struct tlsv1_server *conn)
+{
+ return 0;
+}
+
+
+/**
+ * tlsv1_server_get_keys - Get master key and random data from TLS connection
+ * @conn: TLSv1 server connection data from tlsv1_server_init()
+ * @keys: Structure of key/random data (filled on success)
+ * Returns: 0 on success, -1 on failure
+ */
+int tlsv1_server_get_keys(struct tlsv1_server *conn, struct tls_keys *keys)
+{
+ os_memset(keys, 0, sizeof(*keys));
+ if (conn->state == CLIENT_HELLO)
+ return -1;
+
+ keys->client_random = conn->client_random;
+ keys->client_random_len = TLS_RANDOM_LEN;
+
+ if (conn->state != SERVER_HELLO) {
+ keys->server_random = conn->server_random;
+ keys->server_random_len = TLS_RANDOM_LEN;
+ keys->master_key = conn->master_secret;
+ keys->master_key_len = TLS_MASTER_SECRET_LEN;
+ }
+
+ return 0;
+}
+
+
+/**
+ * tlsv1_server_get_keyblock_size - Get TLS key_block size
+ * @conn: TLSv1 server connection data from tlsv1_server_init()
+ * Returns: Size of the key_block for the negotiated cipher suite or -1 on
+ * failure
+ */
+int tlsv1_server_get_keyblock_size(struct tlsv1_server *conn)
+{
+ if (conn->state == CLIENT_HELLO || conn->state == SERVER_HELLO)
+ return -1;
+
+ return 2 * (conn->rl.hash_size + conn->rl.key_material_len +
+ conn->rl.iv_size);
+}
+
+
+/**
+ * tlsv1_server_set_cipher_list - Configure acceptable cipher suites
+ * @conn: TLSv1 server connection data from tlsv1_server_init()
+ * @ciphers: Zero (TLS_CIPHER_NONE) terminated list of allowed ciphers
+ * (TLS_CIPHER_*).
+ * Returns: 0 on success, -1 on failure
+ */
+int tlsv1_server_set_cipher_list(struct tlsv1_server *conn, u8 *ciphers)
+{
+ size_t count;
+ u16 *suites;
+
+ /* TODO: implement proper configuration of cipher suites */
+ if (ciphers[0] == TLS_CIPHER_ANON_DH_AES128_SHA) {
+ count = 0;
+ suites = conn->cipher_suites;
+ suites[count++] = TLS_RSA_WITH_AES_256_CBC_SHA;
+ suites[count++] = TLS_RSA_WITH_AES_128_CBC_SHA;
+ suites[count++] = TLS_RSA_WITH_3DES_EDE_CBC_SHA;
+ suites[count++] = TLS_RSA_WITH_RC4_128_SHA;
+ suites[count++] = TLS_RSA_WITH_RC4_128_MD5;
+ suites[count++] = TLS_DH_anon_WITH_AES_256_CBC_SHA;
+ suites[count++] = TLS_DH_anon_WITH_AES_128_CBC_SHA;
+ suites[count++] = TLS_DH_anon_WITH_3DES_EDE_CBC_SHA;
+ suites[count++] = TLS_DH_anon_WITH_RC4_128_MD5;
+ suites[count++] = TLS_DH_anon_WITH_DES_CBC_SHA;
+ conn->num_cipher_suites = count;
+ }
+
+ return 0;
+}
+
+
+int tlsv1_server_set_verify(struct tlsv1_server *conn, int verify_peer)
+{
+ conn->verify_peer = verify_peer;
+ return 0;
+}
+
+
+void tlsv1_server_set_session_ticket_cb(struct tlsv1_server *conn,
+ tlsv1_server_session_ticket_cb cb,
+ void *ctx)
+{
+ wpa_printf(MSG_DEBUG, "TLSv1: SessionTicket callback set %p (ctx %p)",
+ cb, ctx);
+ conn->session_ticket_cb = cb;
+ conn->session_ticket_cb_ctx = ctx;
+}
--- /dev/null
+/*
+ * TLSv1 server - read handshake message
+ * Copyright (c) 2006-2011, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "wpa/includes.h"
+
+#include "wpa/common.h"
+#include "crypto/md5.h"
+#include "crypto/sha1.h"
+#include "crypto/sha256.h"
+#include "wpa2/tls/tls.h"
+#include "wpa2/tls/x509v3.h"
+#include "wpa2/tls/tlsv1_common.h"
+#include "wpa2/tls/tlsv1_record.h"
+#include "wpa2/tls/tlsv1_server.h"
+#include "wpa2/tls/tlsv1_server_i.h"
+
+#include "wpa2/eap_peer/eap_i.h"
+
+static int tls_process_client_key_exchange(struct tlsv1_server *conn, u8 ct,
+ const u8 *in_data, size_t *in_len);
+static int tls_process_change_cipher_spec(struct tlsv1_server *conn,
+ u8 ct, const u8 *in_data,
+ size_t *in_len);
+
+
+static int tls_process_client_hello(struct tlsv1_server *conn, u8 ct,
+ const u8 *in_data, size_t *in_len)
+{
+ const u8 *pos, *end, *c;
+ size_t left, len, i, j;
+ u16 cipher_suite;
+ u16 num_suites;
+ int compr_null_found;
+ u16 ext_type, ext_len;
+
+ if (ct != TLS_CONTENT_TYPE_HANDSHAKE) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Expected Handshake; "
+ "received content type 0x%x", ct);
+ tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_UNEXPECTED_MESSAGE);
+ return -1;
+ }
+
+ pos = in_data;
+ left = *in_len;
+
+ if (left < 4)
+ goto decode_error;
+
+ /* HandshakeType msg_type */
+ if (*pos != TLS_HANDSHAKE_TYPE_CLIENT_HELLO) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Received unexpected handshake "
+ "message %d (expected ClientHello)", *pos);
+ tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_UNEXPECTED_MESSAGE);
+ return -1;
+ }
+ wpa_printf(MSG_DEBUG, "TLSv1: Received ClientHello");
+ pos++;
+ /* uint24 length */
+ len = WPA_GET_BE24(pos);
+ pos += 3;
+ left -= 4;
+
+ if (len > left)
+ goto decode_error;
+
+ /* body - ClientHello */
+
+ wpa_hexdump(MSG_MSGDUMP, "TLSv1: ClientHello", pos, len);
+ end = pos + len;
+
+ /* ProtocolVersion client_version */
+ if (end - pos < 2)
+ goto decode_error;
+ conn->client_version = WPA_GET_BE16(pos);
+ wpa_printf(MSG_DEBUG, "TLSv1: Client version %d.%d",
+ conn->client_version >> 8, conn->client_version & 0xff);
+ if (conn->client_version < TLS_VERSION_1) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Unexpected protocol version in "
+ "ClientHello %u.%u",
+ conn->client_version >> 8,
+ conn->client_version & 0xff);
+ tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_PROTOCOL_VERSION);
+ return -1;
+ }
+ pos += 2;
+
+ if (TLS_VERSION == TLS_VERSION_1)
+ conn->rl.tls_version = TLS_VERSION_1;
+#ifdef CONFIG_TLSV12
+ else if (conn->client_version >= TLS_VERSION_1_2)
+ conn->rl.tls_version = TLS_VERSION_1_2;
+#endif /* CONFIG_TLSV12 */
+ else if (conn->client_version > TLS_VERSION_1_1)
+ conn->rl.tls_version = TLS_VERSION_1_1;
+ else
+ conn->rl.tls_version = conn->client_version;
+ wpa_printf(MSG_DEBUG, "TLSv1: Using TLS v%s",
+ tls_version_str(conn->rl.tls_version));
+
+ /* Random random */
+ if (end - pos < TLS_RANDOM_LEN)
+ goto decode_error;
+
+ os_memcpy(conn->client_random, pos, TLS_RANDOM_LEN);
+ pos += TLS_RANDOM_LEN;
+ wpa_hexdump(MSG_MSGDUMP, "TLSv1: client_random",
+ conn->client_random, TLS_RANDOM_LEN);
+
+ /* SessionID session_id */
+ if (end - pos < 1)
+ goto decode_error;
+ if (end - pos < 1 + *pos || *pos > TLS_SESSION_ID_MAX_LEN)
+ goto decode_error;
+ wpa_hexdump(MSG_MSGDUMP, "TLSv1: client session_id", pos + 1, *pos);
+ pos += 1 + *pos;
+ /* TODO: add support for session resumption */
+
+ /* CipherSuite cipher_suites<2..2^16-1> */
+ if (end - pos < 2)
+ goto decode_error;
+ num_suites = WPA_GET_BE16(pos);
+ pos += 2;
+ if (end - pos < num_suites)
+ goto decode_error;
+ wpa_hexdump(MSG_MSGDUMP, "TLSv1: client cipher suites",
+ pos, num_suites);
+ if (num_suites & 1)
+ goto decode_error;
+ num_suites /= 2;
+
+ cipher_suite = 0;
+ for (i = 0; !cipher_suite && i < conn->num_cipher_suites; i++) {
+ c = pos;
+ for (j = 0; j < num_suites; j++) {
+ u16 tmp = WPA_GET_BE16(c);
+ c += 2;
+ if (!cipher_suite && tmp == conn->cipher_suites[i]) {
+ cipher_suite = tmp;
+ break;
+ }
+ }
+ }
+ pos += num_suites * 2;
+ if (!cipher_suite) {
+ wpa_printf(MSG_INFO, "TLSv1: No supported cipher suite "
+ "available");
+ tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_ILLEGAL_PARAMETER);
+ return -1;
+ }
+
+ if (tlsv1_record_set_cipher_suite(&conn->rl, cipher_suite) < 0) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Failed to set CipherSuite for "
+ "record layer");
+ tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_INTERNAL_ERROR);
+ return -1;
+ }
+
+ conn->cipher_suite = cipher_suite;
+
+ /* CompressionMethod compression_methods<1..2^8-1> */
+ if (end - pos < 1)
+ goto decode_error;
+ num_suites = *pos++;
+ if (end - pos < num_suites)
+ goto decode_error;
+ wpa_hexdump(MSG_MSGDUMP, "TLSv1: client compression_methods",
+ pos, num_suites);
+ compr_null_found = 0;
+ for (i = 0; i < num_suites; i++) {
+ if (*pos++ == TLS_COMPRESSION_NULL)
+ compr_null_found = 1;
+ }
+ if (!compr_null_found) {
+ wpa_printf(MSG_INFO, "TLSv1: Client does not accept NULL "
+ "compression");
+ tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_ILLEGAL_PARAMETER);
+ return -1;
+ }
+
+ if (end - pos == 1) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Unexpected extra octet in the "
+ "end of ClientHello: 0x%02x", *pos);
+ goto decode_error;
+ }
+
+ if (end - pos >= 2) {
+ /* Extension client_hello_extension_list<0..2^16-1> */
+ ext_len = WPA_GET_BE16(pos);
+ pos += 2;
+
+ wpa_printf(MSG_DEBUG, "TLSv1: %u bytes of ClientHello "
+ "extensions", ext_len);
+ if (end - pos != ext_len) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Invalid ClientHello "
+ "extension list length %u (expected %u)",
+ ext_len, (unsigned int) (end - pos));
+ goto decode_error;
+ }
+
+ /*
+ * struct {
+ * ExtensionType extension_type (0..65535)
+ * opaque extension_data<0..2^16-1>
+ * } Extension;
+ */
+
+ while (pos < end) {
+ if (end - pos < 2) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Invalid "
+ "extension_type field");
+ goto decode_error;
+ }
+
+ ext_type = WPA_GET_BE16(pos);
+ pos += 2;
+
+ if (end - pos < 2) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Invalid "
+ "extension_data length field");
+ goto decode_error;
+ }
+
+ ext_len = WPA_GET_BE16(pos);
+ pos += 2;
+
+ if (end - pos < ext_len) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Invalid "
+ "extension_data field");
+ goto decode_error;
+ }
+
+ wpa_printf(MSG_DEBUG, "TLSv1: ClientHello Extension "
+ "type %u", ext_type);
+ wpa_hexdump(MSG_MSGDUMP, "TLSv1: ClientHello "
+ "Extension data", pos, ext_len);
+
+ if (ext_type == TLS_EXT_SESSION_TICKET) {
+ os_free(conn->session_ticket);
+ conn->session_ticket = os_malloc(ext_len);
+ if (conn->session_ticket) {
+ os_memcpy(conn->session_ticket, pos,
+ ext_len);
+ conn->session_ticket_len = ext_len;
+ }
+ }
+
+ pos += ext_len;
+ }
+ }
+
+ *in_len = end - in_data;
+
+ wpa_printf(MSG_DEBUG, "TLSv1: ClientHello OK - proceed to "
+ "ServerHello");
+ conn->state = SERVER_HELLO;
+
+ return 0;
+
+decode_error:
+ wpa_printf(MSG_DEBUG, "TLSv1: Failed to decode ClientHello");
+ tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_DECODE_ERROR);
+ return -1;
+}
+
+
+static int tls_process_certificate(struct tlsv1_server *conn, u8 ct,
+ const u8 *in_data, size_t *in_len)
+{
+ const u8 *pos, *end;
+ size_t left, len, list_len, cert_len, idx;
+ u8 type;
+ struct x509_certificate *chain = NULL, *last = NULL, *cert;
+ int reason;
+
+ if (ct != TLS_CONTENT_TYPE_HANDSHAKE) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Expected Handshake; "
+ "received content type 0x%x", ct);
+ tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_UNEXPECTED_MESSAGE);
+ return -1;
+ }
+
+ pos = in_data;
+ left = *in_len;
+
+ if (left < 4) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Too short Certificate message "
+ "(len=%lu)", (unsigned long) left);
+ tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_DECODE_ERROR);
+ return -1;
+ }
+
+ type = *pos++;
+ len = WPA_GET_BE24(pos);
+ pos += 3;
+ left -= 4;
+
+ if (len > left) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Unexpected Certificate message "
+ "length (len=%lu != left=%lu)",
+ (unsigned long) len, (unsigned long) left);
+ tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_DECODE_ERROR);
+ return -1;
+ }
+
+ if (type == TLS_HANDSHAKE_TYPE_CLIENT_KEY_EXCHANGE) {
+ if (conn->verify_peer) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Client did not include "
+ "Certificate");
+ tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_UNEXPECTED_MESSAGE);
+ return -1;
+ }
+
+ return tls_process_client_key_exchange(conn, ct, in_data,
+ in_len);
+ }
+ if (type != TLS_HANDSHAKE_TYPE_CERTIFICATE) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Received unexpected handshake "
+ "message %d (expected Certificate/"
+ "ClientKeyExchange)", type);
+ tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_UNEXPECTED_MESSAGE);
+ return -1;
+ }
+
+ wpa_printf(MSG_DEBUG, "TLSv1: Received Certificate (certificate_list len %lu)",
+ (unsigned long) len);
+
+ /*
+ * opaque ASN.1Cert<2^24-1>;
+ *
+ * struct {
+ * ASN.1Cert certificate_list<1..2^24-1>;
+ * } Certificate;
+ */
+
+ end = pos + len;
+
+ if (end - pos < 3) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Too short Certificate "
+ "(left=%lu)", (unsigned long) left);
+ tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_DECODE_ERROR);
+ return -1;
+ }
+
+ list_len = WPA_GET_BE24(pos);
+ pos += 3;
+
+ if ((size_t) (end - pos) != list_len) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Unexpected certificate_list "
+ "length (len=%lu left=%lu)",
+ (unsigned long) list_len,
+ (unsigned long) (end - pos));
+ tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_DECODE_ERROR);
+ return -1;
+ }
+
+ idx = 0;
+ while (pos < end) {
+ if (end - pos < 3) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Failed to parse "
+ "certificate_list");
+ tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_DECODE_ERROR);
+ x509_certificate_chain_free(chain);
+ return -1;
+ }
+
+ cert_len = WPA_GET_BE24(pos);
+ pos += 3;
+
+ if ((size_t) (end - pos) < cert_len) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Unexpected certificate "
+ "length (len=%lu left=%lu)",
+ (unsigned long) cert_len,
+ (unsigned long) (end - pos));
+ tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_DECODE_ERROR);
+ x509_certificate_chain_free(chain);
+ return -1;
+ }
+
+ wpa_printf(MSG_DEBUG, "TLSv1: Certificate %lu (len %lu)",
+ (unsigned long) idx, (unsigned long) cert_len);
+
+ if (idx == 0) {
+ crypto_public_key_free(conn->client_rsa_key);
+ if (tls_parse_cert(pos, cert_len,
+ &conn->client_rsa_key)) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Failed to parse "
+ "the certificate");
+ tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_BAD_CERTIFICATE);
+ x509_certificate_chain_free(chain);
+ return -1;
+ }
+ }
+
+ cert = x509_certificate_parse(pos, cert_len);
+ if (cert == NULL) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Failed to parse "
+ "the certificate");
+ tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_BAD_CERTIFICATE);
+ x509_certificate_chain_free(chain);
+ return -1;
+ }
+
+ if (last == NULL)
+ chain = cert;
+ else
+ last->next = cert;
+ last = cert;
+
+ idx++;
+ pos += cert_len;
+ }
+
+ if (x509_certificate_chain_validate(conn->cred->trusted_certs, chain,
+ &reason, 0) < 0) {
+ int tls_reason;
+ wpa_printf(MSG_DEBUG, "TLSv1: Server certificate chain "
+ "validation failed (reason=%d)", reason);
+ switch (reason) {
+ case X509_VALIDATE_BAD_CERTIFICATE:
+ tls_reason = TLS_ALERT_BAD_CERTIFICATE;
+ break;
+ case X509_VALIDATE_UNSUPPORTED_CERTIFICATE:
+ tls_reason = TLS_ALERT_UNSUPPORTED_CERTIFICATE;
+ break;
+ case X509_VALIDATE_CERTIFICATE_REVOKED:
+ tls_reason = TLS_ALERT_CERTIFICATE_REVOKED;
+ break;
+ case X509_VALIDATE_CERTIFICATE_EXPIRED:
+ tls_reason = TLS_ALERT_CERTIFICATE_EXPIRED;
+ break;
+ case X509_VALIDATE_CERTIFICATE_UNKNOWN:
+ tls_reason = TLS_ALERT_CERTIFICATE_UNKNOWN;
+ break;
+ case X509_VALIDATE_UNKNOWN_CA:
+ tls_reason = TLS_ALERT_UNKNOWN_CA;
+ break;
+ default:
+ tls_reason = TLS_ALERT_BAD_CERTIFICATE;
+ break;
+ }
+ tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, tls_reason);
+ x509_certificate_chain_free(chain);
+ return -1;
+ }
+
+ x509_certificate_chain_free(chain);
+
+ *in_len = end - in_data;
+
+ conn->state = CLIENT_KEY_EXCHANGE;
+
+ return 0;
+}
+
+
+static int tls_process_client_key_exchange_rsa(
+ struct tlsv1_server *conn, const u8 *pos, const u8 *end)
+{
+ u8 *out;
+ size_t outlen, outbuflen;
+ u16 encr_len;
+ int res;
+ int use_random = 0;
+
+ if (end - pos < 2) {
+ tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_DECODE_ERROR);
+ return -1;
+ }
+
+ encr_len = WPA_GET_BE16(pos);
+ pos += 2;
+ if (pos + encr_len > end) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Invalid ClientKeyExchange "
+ "format: encr_len=%u left=%u",
+ encr_len, (unsigned int) (end - pos));
+ tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_DECODE_ERROR);
+ return -1;
+ }
+
+ outbuflen = outlen = end - pos;
+ out = os_malloc(outlen >= TLS_PRE_MASTER_SECRET_LEN ?
+ outlen : TLS_PRE_MASTER_SECRET_LEN);
+ if (out == NULL) {
+ tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_INTERNAL_ERROR);
+ return -1;
+ }
+
+ /*
+ * struct {
+ * ProtocolVersion client_version;
+ * opaque random[46];
+ * } PreMasterSecret;
+ *
+ * struct {
+ * public-key-encrypted PreMasterSecret pre_master_secret;
+ * } EncryptedPreMasterSecret;
+ */
+
+ /*
+ * Note: To avoid Bleichenbacher attack, we do not report decryption or
+ * parsing errors from EncryptedPreMasterSecret processing to the
+ * client. Instead, a random pre-master secret is used to force the
+ * handshake to fail.
+ */
+
+ if (crypto_private_key_decrypt_pkcs1_v15(conn->cred->key,
+ pos, encr_len,
+ out, &outlen) < 0) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Failed to decrypt "
+ "PreMasterSecret (encr_len=%u outlen=%lu)",
+ encr_len, (unsigned long) outlen);
+ use_random = 1;
+ }
+
+ if (!use_random && outlen != TLS_PRE_MASTER_SECRET_LEN) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Unexpected PreMasterSecret "
+ "length %lu", (unsigned long) outlen);
+ use_random = 1;
+ }
+
+ if (!use_random && WPA_GET_BE16(out) != conn->client_version) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Client version in "
+ "ClientKeyExchange does not match with version in "
+ "ClientHello");
+ use_random = 1;
+ }
+
+ if (use_random) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Using random premaster secret "
+ "to avoid revealing information about private key");
+ outlen = TLS_PRE_MASTER_SECRET_LEN;
+ if (os_get_random(out, outlen)) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Failed to get random "
+ "data");
+ tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_INTERNAL_ERROR);
+ os_free(out);
+ return -1;
+ }
+ }
+
+ res = tlsv1_server_derive_keys(conn, out, outlen);
+
+ /* Clear the pre-master secret since it is not needed anymore */
+ os_memset(out, 0, outbuflen);
+ os_free(out);
+
+ if (res) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Failed to derive keys");
+ tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_INTERNAL_ERROR);
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static int tls_process_client_key_exchange_dh_anon(
+ struct tlsv1_server *conn, const u8 *pos, const u8 *end)
+{
+ const u8 *dh_yc;
+ u16 dh_yc_len;
+ u8 *shared;
+ size_t shared_len;
+ int res;
+
+ /*
+ * struct {
+ * select (PublicValueEncoding) {
+ * case implicit: struct { };
+ * case explicit: opaque dh_Yc<1..2^16-1>;
+ * } dh_public;
+ * } ClientDiffieHellmanPublic;
+ */
+
+ wpa_hexdump(MSG_MSGDUMP, "TLSv1: ClientDiffieHellmanPublic",
+ pos, end - pos);
+
+ if (end == pos) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Implicit public value encoding "
+ "not supported");
+ tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_INTERNAL_ERROR);
+ return -1;
+ }
+
+ if (end - pos < 3) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Invalid client public value "
+ "length");
+ tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_DECODE_ERROR);
+ return -1;
+ }
+
+ dh_yc_len = WPA_GET_BE16(pos);
+ dh_yc = pos + 2;
+
+ if (dh_yc + dh_yc_len > end) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Client public value overflow "
+ "(length %d)", dh_yc_len);
+ tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_DECODE_ERROR);
+ return -1;
+ }
+
+ wpa_hexdump(MSG_DEBUG, "TLSv1: DH Yc (client's public value)",
+ dh_yc, dh_yc_len);
+
+ if (conn->cred == NULL || conn->cred->dh_p == NULL ||
+ conn->dh_secret == NULL) {
+ wpa_printf(MSG_DEBUG, "TLSv1: No DH parameters available");
+ tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_INTERNAL_ERROR);
+ return -1;
+ }
+
+ shared_len = conn->cred->dh_p_len;
+ shared = os_malloc(shared_len);
+ if (shared == NULL) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Could not allocate memory for "
+ "DH");
+ tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_INTERNAL_ERROR);
+ return -1;
+ }
+
+ /* shared = Yc^secret mod p */
+ if (wpa2_crypto_funcs.crypto_mod_exp) {
+ if (wpa2_crypto_funcs.crypto_mod_exp(dh_yc, dh_yc_len, conn->dh_secret,
+ conn->dh_secret_len,
+ conn->cred->dh_p, conn->cred->dh_p_len,
+ shared, &shared_len)) {
+ os_free(shared);
+ tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_INTERNAL_ERROR);
+ return -1;
+ }
+ } else {
+ wpa_printf(MSG_ERROR, "Fail to register crypto_mod_exp function!\r\n");
+ os_free(shared);
+ tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_INTERNAL_ERROR);
+ return -1;
+ }
+
+ wpa_hexdump_key(MSG_DEBUG, "TLSv1: Shared secret from DH key exchange",
+ shared, shared_len);
+
+ os_memset(conn->dh_secret, 0, conn->dh_secret_len);
+ os_free(conn->dh_secret);
+ conn->dh_secret = NULL;
+
+ res = tlsv1_server_derive_keys(conn, shared, shared_len);
+
+ /* Clear the pre-master secret since it is not needed anymore */
+ os_memset(shared, 0, shared_len);
+ os_free(shared);
+
+ if (res) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Failed to derive keys");
+ tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_INTERNAL_ERROR);
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static int tls_process_client_key_exchange(struct tlsv1_server *conn, u8 ct,
+ const u8 *in_data, size_t *in_len)
+{
+ const u8 *pos, *end;
+ size_t left, len;
+ u8 type;
+ tls_key_exchange keyx;
+ const struct tls_cipher_suite *suite;
+
+ if (ct != TLS_CONTENT_TYPE_HANDSHAKE) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Expected Handshake; "
+ "received content type 0x%x", ct);
+ tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_UNEXPECTED_MESSAGE);
+ return -1;
+ }
+
+ pos = in_data;
+ left = *in_len;
+
+ if (left < 4) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Too short ClientKeyExchange "
+ "(Left=%lu)", (unsigned long) left);
+ tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_DECODE_ERROR);
+ return -1;
+ }
+
+ type = *pos++;
+ len = WPA_GET_BE24(pos);
+ pos += 3;
+ left -= 4;
+
+ if (len > left) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Mismatch in ClientKeyExchange "
+ "length (len=%lu != left=%lu)",
+ (unsigned long) len, (unsigned long) left);
+ tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_DECODE_ERROR);
+ return -1;
+ }
+
+ end = pos + len;
+
+ if (type != TLS_HANDSHAKE_TYPE_CLIENT_KEY_EXCHANGE) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Received unexpected handshake "
+ "message %d (expected ClientKeyExchange)", type);
+ tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_UNEXPECTED_MESSAGE);
+ return -1;
+ }
+
+ wpa_printf(MSG_DEBUG, "TLSv1: Received ClientKeyExchange");
+
+ wpa_hexdump(MSG_DEBUG, "TLSv1: ClientKeyExchange", pos, len);
+
+ suite = tls_get_cipher_suite(conn->rl.cipher_suite);
+ if (suite == NULL)
+ keyx = TLS_KEY_X_NULL;
+ else
+ keyx = suite->key_exchange;
+
+ if (keyx == TLS_KEY_X_DH_anon &&
+ tls_process_client_key_exchange_dh_anon(conn, pos, end) < 0)
+ return -1;
+
+ if (keyx != TLS_KEY_X_DH_anon &&
+ tls_process_client_key_exchange_rsa(conn, pos, end) < 0)
+ return -1;
+
+ *in_len = end - in_data;
+
+ conn->state = CERTIFICATE_VERIFY;
+
+ return 0;
+}
+
+
+static int tls_process_certificate_verify(struct tlsv1_server *conn, u8 ct,
+ const u8 *in_data, size_t *in_len)
+{
+ const u8 *pos, *end;
+ size_t left, len;
+ u8 type;
+ size_t hlen, buflen;
+ u8 hash[MD5_MAC_LEN + SHA1_MAC_LEN], *hpos, *buf;
+ enum { SIGN_ALG_RSA, SIGN_ALG_DSA } alg = SIGN_ALG_RSA;
+ u16 slen;
+
+ if (ct == TLS_CONTENT_TYPE_CHANGE_CIPHER_SPEC) {
+ if (conn->verify_peer) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Client did not include "
+ "CertificateVerify");
+ tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_UNEXPECTED_MESSAGE);
+ return -1;
+ }
+
+ return tls_process_change_cipher_spec(conn, ct, in_data,
+ in_len);
+ }
+
+ if (ct != TLS_CONTENT_TYPE_HANDSHAKE) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Expected Handshake; "
+ "received content type 0x%x", ct);
+ tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_UNEXPECTED_MESSAGE);
+ return -1;
+ }
+
+ pos = in_data;
+ left = *in_len;
+
+ if (left < 4) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Too short CertificateVerify "
+ "message (len=%lu)", (unsigned long) left);
+ tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_DECODE_ERROR);
+ return -1;
+ }
+
+ type = *pos++;
+ len = WPA_GET_BE24(pos);
+ pos += 3;
+ left -= 4;
+
+ if (len > left) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Unexpected CertificateVerify "
+ "message length (len=%lu != left=%lu)",
+ (unsigned long) len, (unsigned long) left);
+ tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_DECODE_ERROR);
+ return -1;
+ }
+
+ end = pos + len;
+
+ if (type != TLS_HANDSHAKE_TYPE_CERTIFICATE_VERIFY) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Received unexpected handshake "
+ "message %d (expected CertificateVerify)", type);
+ tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_UNEXPECTED_MESSAGE);
+ return -1;
+ }
+
+ wpa_printf(MSG_DEBUG, "TLSv1: Received CertificateVerify");
+
+ /*
+ * struct {
+ * Signature signature;
+ * } CertificateVerify;
+ */
+
+ hpos = hash;
+
+#ifdef CONFIG_TLSV12
+ if (conn->rl.tls_version == TLS_VERSION_1_2) {
+ /*
+ * RFC 5246, 4.7:
+ * TLS v1.2 adds explicit indication of the used signature and
+ * hash algorithms.
+ *
+ * struct {
+ * HashAlgorithm hash;
+ * SignatureAlgorithm signature;
+ * } SignatureAndHashAlgorithm;
+ */
+ if (end - pos < 2) {
+ tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_DECODE_ERROR);
+ return -1;
+ }
+ if (pos[0] != TLS_HASH_ALG_SHA256 ||
+ pos[1] != TLS_SIGN_ALG_RSA) {
+ wpa_printf(MSG_DEBUG, "TLSv1.2: Unsupported hash(%u)/"
+ "signature(%u) algorithm",
+ pos[0], pos[1]);
+ tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_INTERNAL_ERROR);
+ return -1;
+ }
+ pos += 2;
+
+ hlen = SHA256_MAC_LEN;
+ if (wpa2_crypto_funcs.crypto_hash_finish) {
+ if (conn->verify.sha256_cert == NULL ||
+ wpa2_crypto_funcs.crypto_hash_finish(conn->verify.sha256_cert, hpos, &hlen) <
+ 0) {
+ conn->verify.sha256_cert = NULL;
+ tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_INTERNAL_ERROR);
+ return -1;
+ }
+ } else {
+ conn->verify.sha256_cert = NULL;
+ tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_INTERNAL_ERROR);
+ wpa_printf(MSG_ERROR, "In function %s, fail to register crypto_hash_finish function!\r\n", __FUNCTION__);
+ return -1;
+ }
+ conn->verify.sha256_cert = NULL;
+ } else {
+#endif /* CONFIG_TLSV12 */
+
+ if (alg == SIGN_ALG_RSA) {
+ hlen = MD5_MAC_LEN;
+ if (conn->verify.md5_cert == NULL ||
+ crypto_hash_finish(conn->verify.md5_cert, hpos, &hlen) < 0)
+ {
+ tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_INTERNAL_ERROR);
+ conn->verify.md5_cert = NULL;
+ crypto_hash_finish(conn->verify.sha1_cert, NULL, NULL);
+ conn->verify.sha1_cert = NULL;
+ return -1;
+ }
+ hpos += MD5_MAC_LEN;
+ } else
+ crypto_hash_finish(conn->verify.md5_cert, NULL, NULL);
+
+ conn->verify.md5_cert = NULL;
+ hlen = SHA1_MAC_LEN;
+ if (conn->verify.sha1_cert == NULL ||
+ crypto_hash_finish(conn->verify.sha1_cert, hpos, &hlen) < 0) {
+ conn->verify.sha1_cert = NULL;
+ tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_INTERNAL_ERROR);
+ return -1;
+ }
+ conn->verify.sha1_cert = NULL;
+
+ if (alg == SIGN_ALG_RSA)
+ hlen += MD5_MAC_LEN;
+
+#ifdef CONFIG_TLSV12
+ }
+#endif /* CONFIG_TLSV12 */
+
+ wpa_hexdump(MSG_MSGDUMP, "TLSv1: CertificateVerify hash", hash, hlen);
+
+ if (end - pos < 2) {
+ tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_DECODE_ERROR);
+ return -1;
+ }
+ slen = WPA_GET_BE16(pos);
+ pos += 2;
+ if (end - pos < slen) {
+ tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_DECODE_ERROR);
+ return -1;
+ }
+
+ wpa_hexdump(MSG_MSGDUMP, "TLSv1: Signature", pos, end - pos);
+ if (conn->client_rsa_key == NULL) {
+ wpa_printf(MSG_DEBUG, "TLSv1: No client public key to verify "
+ "signature");
+ tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_INTERNAL_ERROR);
+ return -1;
+ }
+
+ buflen = end - pos;
+ buf = os_malloc(end - pos);
+ if (crypto_public_key_decrypt_pkcs1(conn->client_rsa_key,
+ pos, end - pos, buf, &buflen) < 0)
+ {
+ wpa_printf(MSG_DEBUG, "TLSv1: Failed to decrypt signature");
+ os_free(buf);
+ tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_DECRYPT_ERROR);
+ return -1;
+ }
+
+ wpa_hexdump_key(MSG_MSGDUMP, "TLSv1: Decrypted Signature",
+ buf, buflen);
+
+#ifdef CONFIG_TLSV12
+ if (conn->rl.tls_version >= TLS_VERSION_1_2) {
+ /*
+ * RFC 3447, A.2.4 RSASSA-PKCS1-v1_5
+ *
+ * DigestInfo ::= SEQUENCE {
+ * digestAlgorithm DigestAlgorithm,
+ * digest OCTET STRING
+ * }
+ *
+ * SHA-256 OID: sha256WithRSAEncryption ::= {pkcs-1 11}
+ *
+ * DER encoded DigestInfo for SHA256 per RFC 3447:
+ * 30 31 30 0d 06 09 60 86 48 01 65 03 04 02 01 05 00 04 20 ||
+ * H
+ */
+ if (buflen >= 19 + 32 &&
+ os_memcmp(buf, "\x30\x31\x30\x0d\x06\x09\x60\x86\x48\x01"
+ "\x65\x03\x04\x02\x01\x05\x00\x04\x20", 19) == 0)
+ {
+ wpa_printf(MSG_DEBUG, "TLSv1.2: DigestAlgorithn = "
+ "SHA-256");
+ os_memmove(buf, buf + 19, buflen - 19);
+ buflen -= 19;
+ } else {
+ wpa_printf(MSG_DEBUG, "TLSv1.2: Unrecognized "
+ "DigestInfo");
+ os_free(buf);
+ tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_DECRYPT_ERROR);
+ return -1;
+ }
+ }
+#endif /* CONFIG_TLSV12 */
+
+ if (buflen != hlen || os_memcmp(buf, hash, buflen) != 0) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Invalid Signature in "
+ "CertificateVerify - did not match with calculated "
+ "hash");
+ os_free(buf);
+ tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_DECRYPT_ERROR);
+ return -1;
+ }
+
+ os_free(buf);
+
+ *in_len = end - in_data;
+
+ conn->state = CHANGE_CIPHER_SPEC;
+
+ return 0;
+}
+
+
+static int tls_process_change_cipher_spec(struct tlsv1_server *conn,
+ u8 ct, const u8 *in_data,
+ size_t *in_len)
+{
+ const u8 *pos;
+ size_t left;
+
+ if (ct != TLS_CONTENT_TYPE_CHANGE_CIPHER_SPEC) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Expected ChangeCipherSpec; "
+ "received content type 0x%x", ct);
+ tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_UNEXPECTED_MESSAGE);
+ return -1;
+ }
+
+ pos = in_data;
+ left = *in_len;
+
+ if (left < 1) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Too short ChangeCipherSpec");
+ tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_DECODE_ERROR);
+ return -1;
+ }
+
+ if (*pos != TLS_CHANGE_CIPHER_SPEC) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Expected ChangeCipherSpec; "
+ "received data 0x%x", *pos);
+ tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_UNEXPECTED_MESSAGE);
+ return -1;
+ }
+
+ wpa_printf(MSG_DEBUG, "TLSv1: Received ChangeCipherSpec");
+ if (tlsv1_record_change_read_cipher(&conn->rl) < 0) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Failed to change read cipher "
+ "for record layer");
+ tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_INTERNAL_ERROR);
+ return -1;
+ }
+
+ *in_len = pos + 1 - in_data;
+
+ conn->state = CLIENT_FINISHED;
+
+ return 0;
+}
+
+
+static int tls_process_client_finished(struct tlsv1_server *conn, u8 ct,
+ const u8 *in_data, size_t *in_len)
+{
+ const u8 *pos, *end;
+ size_t left, len, hlen;
+ u8 verify_data[TLS_VERIFY_DATA_LEN];
+ u8 hash[MD5_MAC_LEN + SHA1_MAC_LEN];
+
+ if (ct != TLS_CONTENT_TYPE_HANDSHAKE) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Expected Finished; "
+ "received content type 0x%x", ct);
+ tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_UNEXPECTED_MESSAGE);
+ return -1;
+ }
+
+ pos = in_data;
+ left = *in_len;
+
+ if (left < 4) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Too short record (left=%lu) for "
+ "Finished",
+ (unsigned long) left);
+ tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_DECODE_ERROR);
+ return -1;
+ }
+
+ if (pos[0] != TLS_HANDSHAKE_TYPE_FINISHED) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Expected Finished; received "
+ "type 0x%x", pos[0]);
+ tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_UNEXPECTED_MESSAGE);
+ return -1;
+ }
+
+ len = WPA_GET_BE24(pos + 1);
+
+ pos += 4;
+ left -= 4;
+
+ if (len > left) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Too short buffer for Finished "
+ "(len=%lu > left=%lu)",
+ (unsigned long) len, (unsigned long) left);
+ tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_DECODE_ERROR);
+ return -1;
+ }
+ end = pos + len;
+ if (len != TLS_VERIFY_DATA_LEN) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Unexpected verify_data length "
+ "in Finished: %lu (expected %d)",
+ (unsigned long) len, TLS_VERIFY_DATA_LEN);
+ tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_DECODE_ERROR);
+ return -1;
+ }
+ wpa_hexdump(MSG_MSGDUMP, "TLSv1: verify_data in Finished",
+ pos, TLS_VERIFY_DATA_LEN);
+
+#ifdef CONFIG_TLSV12
+ if (conn->rl.tls_version >= TLS_VERSION_1_2) {
+ hlen = SHA256_MAC_LEN;
+ if (wpa2_crypto_funcs.crypto_hash_finish) {
+ if (conn->verify.sha256_client == NULL ||
+ crypto_hash_finish(conn->verify.sha256_client, hash, &hlen)
+ < 0) {
+ tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_INTERNAL_ERROR);
+ conn->verify.sha256_client = NULL;
+ return -1;
+ }
+ } else {
+ tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_INTERNAL_ERROR);
+ conn->verify.sha256_client = NULL;
+ wpa_printf(MSG_ERROR, "In function %s, fail to register crypto_hash_finish function!\r\n", __FUNCTION__);
+ return -1;
+ }
+ conn->verify.sha256_client = NULL;
+ } else {
+#endif /* CONFIG_TLSV12 */
+
+ hlen = MD5_MAC_LEN;
+ if (conn->verify.md5_client == NULL ||
+ crypto_hash_finish(conn->verify.md5_client, hash, &hlen) < 0) {
+ tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_INTERNAL_ERROR);
+ conn->verify.md5_client = NULL;
+ crypto_hash_finish(conn->verify.sha1_client, NULL, NULL);
+ conn->verify.sha1_client = NULL;
+ return -1;
+ }
+ conn->verify.md5_client = NULL;
+ hlen = SHA1_MAC_LEN;
+ if (conn->verify.sha1_client == NULL ||
+ crypto_hash_finish(conn->verify.sha1_client, hash + MD5_MAC_LEN,
+ &hlen) < 0) {
+ conn->verify.sha1_client = NULL;
+ tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_INTERNAL_ERROR);
+ return -1;
+ }
+ conn->verify.sha1_client = NULL;
+ hlen = MD5_MAC_LEN + SHA1_MAC_LEN;
+
+#ifdef CONFIG_TLSV12
+ }
+#endif /* CONFIG_TLSV12 */
+
+ if (tls_prf(conn->rl.tls_version,
+ conn->master_secret, TLS_MASTER_SECRET_LEN,
+ "client finished", hash, hlen,
+ verify_data, TLS_VERIFY_DATA_LEN)) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Failed to derive verify_data");
+ tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_DECRYPT_ERROR);
+ return -1;
+ }
+ wpa_hexdump_key(MSG_DEBUG, "TLSv1: verify_data (client)",
+ verify_data, TLS_VERIFY_DATA_LEN);
+
+ if (os_memcmp(pos, verify_data, TLS_VERIFY_DATA_LEN) != 0) {
+ wpa_printf(MSG_INFO, "TLSv1: Mismatch in verify_data");
+ return -1;
+ }
+
+ wpa_printf(MSG_DEBUG, "TLSv1: Received Finished");
+
+ *in_len = end - in_data;
+
+ if (conn->use_session_ticket) {
+ /* Abbreviated handshake using session ticket; RFC 4507 */
+ wpa_printf(MSG_DEBUG, "TLSv1: Abbreviated handshake completed "
+ "successfully");
+ conn->state = ESTABLISHED;
+ } else {
+ /* Full handshake */
+ conn->state = SERVER_CHANGE_CIPHER_SPEC;
+ }
+
+ return 0;
+}
+
+
+int tlsv1_server_process_handshake(struct tlsv1_server *conn, u8 ct,
+ const u8 *buf, size_t *len)
+{
+ if (ct == TLS_CONTENT_TYPE_ALERT) {
+ if (*len < 2) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Alert underflow");
+ tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_DECODE_ERROR);
+ return -1;
+ }
+ wpa_printf(MSG_DEBUG, "TLSv1: Received alert %d:%d",
+ buf[0], buf[1]);
+ *len = 2;
+ conn->state = FAILED;
+ return -1;
+ }
+
+ switch (conn->state) {
+ case CLIENT_HELLO:
+ if (tls_process_client_hello(conn, ct, buf, len))
+ return -1;
+ break;
+ case CLIENT_CERTIFICATE:
+ if (tls_process_certificate(conn, ct, buf, len))
+ return -1;
+ break;
+ case CLIENT_KEY_EXCHANGE:
+ if (tls_process_client_key_exchange(conn, ct, buf, len))
+ return -1;
+ break;
+ case CERTIFICATE_VERIFY:
+ if (tls_process_certificate_verify(conn, ct, buf, len))
+ return -1;
+ break;
+ case CHANGE_CIPHER_SPEC:
+ if (tls_process_change_cipher_spec(conn, ct, buf, len))
+ return -1;
+ break;
+ case CLIENT_FINISHED:
+ if (tls_process_client_finished(conn, ct, buf, len))
+ return -1;
+ break;
+ default:
+ wpa_printf(MSG_DEBUG, "TLSv1: Unexpected state %d "
+ "while processing received message",
+ conn->state);
+ return -1;
+ }
+
+ if (ct == TLS_CONTENT_TYPE_HANDSHAKE)
+ tls_verify_hash_add(&conn->verify, buf, *len);
+
+ return 0;
+}
--- /dev/null
+/*
+ * TLSv1 server - write handshake message
+ * Copyright (c) 2006-2011, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "wpa/includes.h"
+
+#include "wpa/common.h"
+#include "crypto/md5.h"
+#include "crypto/sha1.h"
+#include "crypto/sha256.h"
+#include "crypto/random.h"
+#include "wpa2/tls/tls.h"
+#include "wpa2/tls/x509v3.h"
+#include "wpa2/tls/tlsv1_common.h"
+#include "wpa2/tls/tlsv1_record.h"
+#include "wpa2/tls/tlsv1_server.h"
+#include "wpa2/tls/tlsv1_server_i.h"
+
+#include "wpa2/eap_peer/eap_i.h"
+
+static size_t tls_server_cert_chain_der_len(struct tlsv1_server *conn)
+{
+ size_t len = 0;
+ struct x509_certificate *cert;
+
+ cert = conn->cred->cert;
+ while (cert) {
+ len += 3 + cert->cert_len;
+ if (x509_certificate_self_signed(cert))
+ break;
+ cert = x509_certificate_get_subject(conn->cred->trusted_certs,
+ &cert->issuer);
+ }
+
+ return len;
+}
+
+
+static int tls_write_server_hello(struct tlsv1_server *conn,
+ u8 **msgpos, u8 *end)
+{
+ u8 *pos, *rhdr, *hs_start, *hs_length;
+ struct os_time now;
+ size_t rlen;
+
+ pos = *msgpos;
+
+ wpa_printf(MSG_DEBUG, "TLSv1: Send ServerHello");
+ rhdr = pos;
+ pos += TLS_RECORD_HEADER_LEN;
+
+ os_get_time(&now);
+ WPA_PUT_BE32(conn->server_random, now.sec);
+ if (random_get_bytes(conn->server_random + 4, TLS_RANDOM_LEN - 4)) {
+ wpa_printf(MSG_ERROR, "TLSv1: Could not generate "
+ "server_random");
+ return -1;
+ }
+ wpa_hexdump(MSG_MSGDUMP, "TLSv1: server_random",
+ conn->server_random, TLS_RANDOM_LEN);
+
+ conn->session_id_len = TLS_SESSION_ID_MAX_LEN;
+ if (random_get_bytes(conn->session_id, conn->session_id_len)) {
+ wpa_printf(MSG_ERROR, "TLSv1: Could not generate "
+ "session_id");
+ return -1;
+ }
+ wpa_hexdump(MSG_MSGDUMP, "TLSv1: session_id",
+ conn->session_id, conn->session_id_len);
+
+ /* opaque fragment[TLSPlaintext.length] */
+
+ /* Handshake */
+ hs_start = pos;
+ /* HandshakeType msg_type */
+ *pos++ = TLS_HANDSHAKE_TYPE_SERVER_HELLO;
+ /* uint24 length (to be filled) */
+ hs_length = pos;
+ pos += 3;
+ /* body - ServerHello */
+ /* ProtocolVersion server_version */
+ WPA_PUT_BE16(pos, conn->rl.tls_version);
+ pos += 2;
+ /* Random random: uint32 gmt_unix_time, opaque random_bytes */
+ os_memcpy(pos, conn->server_random, TLS_RANDOM_LEN);
+ pos += TLS_RANDOM_LEN;
+ /* SessionID session_id */
+ *pos++ = conn->session_id_len;
+ os_memcpy(pos, conn->session_id, conn->session_id_len);
+ pos += conn->session_id_len;
+ /* CipherSuite cipher_suite */
+ WPA_PUT_BE16(pos, conn->cipher_suite);
+ pos += 2;
+ /* CompressionMethod compression_method */
+ *pos++ = TLS_COMPRESSION_NULL;
+
+ if (conn->session_ticket && conn->session_ticket_cb) {
+ int res = conn->session_ticket_cb(
+ conn->session_ticket_cb_ctx,
+ conn->session_ticket, conn->session_ticket_len,
+ conn->client_random, conn->server_random,
+ conn->master_secret);
+ if (res < 0) {
+ wpa_printf(MSG_DEBUG, "TLSv1: SessionTicket callback "
+ "indicated failure");
+ tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_HANDSHAKE_FAILURE);
+ return -1;
+ }
+ conn->use_session_ticket = res;
+
+ if (conn->use_session_ticket) {
+ if (tlsv1_server_derive_keys(conn, NULL, 0) < 0) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Failed to "
+ "derive keys");
+ tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_INTERNAL_ERROR);
+ return -1;
+ }
+ }
+
+ /*
+ * RFC 4507 specifies that server would include an empty
+ * SessionTicket extension in ServerHello and a
+ * NewSessionTicket message after the ServerHello. However,
+ * EAP-FAST (RFC 4851), i.e., the only user of SessionTicket
+ * extension at the moment, does not use such extensions.
+ *
+ * TODO: Add support for configuring RFC 4507 behavior and make
+ * EAP-FAST disable it.
+ */
+ }
+
+ WPA_PUT_BE24(hs_length, pos - hs_length - 3);
+ tls_verify_hash_add(&conn->verify, hs_start, pos - hs_start);
+
+ if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_HANDSHAKE,
+ rhdr, end - rhdr, hs_start, pos - hs_start,
+ &rlen) < 0) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Failed to create TLS record");
+ tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_INTERNAL_ERROR);
+ return -1;
+ }
+ pos = rhdr + rlen;
+
+ *msgpos = pos;
+
+ return 0;
+}
+
+
+static int tls_write_server_certificate(struct tlsv1_server *conn,
+ u8 **msgpos, u8 *end)
+{
+ u8 *pos, *rhdr, *hs_start, *hs_length, *cert_start;
+ size_t rlen;
+ struct x509_certificate *cert;
+ const struct tls_cipher_suite *suite;
+
+ suite = tls_get_cipher_suite(conn->rl.cipher_suite);
+ if (suite && suite->key_exchange == TLS_KEY_X_DH_anon) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Do not send Certificate when "
+ "using anonymous DH");
+ return 0;
+ }
+
+ pos = *msgpos;
+
+ wpa_printf(MSG_DEBUG, "TLSv1: Send Certificate");
+ rhdr = pos;
+ pos += TLS_RECORD_HEADER_LEN;
+
+ /* opaque fragment[TLSPlaintext.length] */
+
+ /* Handshake */
+ hs_start = pos;
+ /* HandshakeType msg_type */
+ *pos++ = TLS_HANDSHAKE_TYPE_CERTIFICATE;
+ /* uint24 length (to be filled) */
+ hs_length = pos;
+ pos += 3;
+ /* body - Certificate */
+ /* uint24 length (to be filled) */
+ cert_start = pos;
+ pos += 3;
+ cert = conn->cred->cert;
+ while (cert) {
+ if (pos + 3 + cert->cert_len > end) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Not enough buffer space "
+ "for Certificate (cert_len=%lu left=%lu)",
+ (unsigned long) cert->cert_len,
+ (unsigned long) (end - pos));
+ tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_INTERNAL_ERROR);
+ return -1;
+ }
+ WPA_PUT_BE24(pos, cert->cert_len);
+ pos += 3;
+ os_memcpy(pos, cert->cert_start, cert->cert_len);
+ pos += cert->cert_len;
+
+ if (x509_certificate_self_signed(cert))
+ break;
+ cert = x509_certificate_get_subject(conn->cred->trusted_certs,
+ &cert->issuer);
+ }
+ if (cert == conn->cred->cert || cert == NULL) {
+ /*
+ * Server was not configured with all the needed certificates
+ * to form a full certificate chain. The client may fail to
+ * validate the chain unless it is configured with all the
+ * missing CA certificates.
+ */
+ wpa_printf(MSG_DEBUG, "TLSv1: Full server certificate chain "
+ "not configured - validation may fail");
+ }
+ WPA_PUT_BE24(cert_start, pos - cert_start - 3);
+
+ WPA_PUT_BE24(hs_length, pos - hs_length - 3);
+
+ if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_HANDSHAKE,
+ rhdr, end - rhdr, hs_start, pos - hs_start,
+ &rlen) < 0) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Failed to generate a record");
+ tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_INTERNAL_ERROR);
+ return -1;
+ }
+ pos = rhdr + rlen;
+
+ tls_verify_hash_add(&conn->verify, hs_start, pos - hs_start);
+
+ *msgpos = pos;
+
+ return 0;
+}
+
+
+static int tls_write_server_key_exchange(struct tlsv1_server *conn,
+ u8 **msgpos, u8 *end)
+{
+ tls_key_exchange keyx;
+ const struct tls_cipher_suite *suite;
+ u8 *pos, *rhdr, *hs_start, *hs_length;
+ size_t rlen;
+ u8 *dh_ys;
+ size_t dh_ys_len;
+
+ suite = tls_get_cipher_suite(conn->rl.cipher_suite);
+ if (suite == NULL)
+ keyx = TLS_KEY_X_NULL;
+ else
+ keyx = suite->key_exchange;
+
+ if (!tls_server_key_exchange_allowed(conn->rl.cipher_suite)) {
+ wpa_printf(MSG_DEBUG, "TLSv1: No ServerKeyExchange needed");
+ return 0;
+ }
+
+ if (keyx != TLS_KEY_X_DH_anon) {
+ /* TODO? */
+ wpa_printf(MSG_DEBUG, "TLSv1: ServerKeyExchange not yet "
+ "supported with key exchange type %d", keyx);
+ return -1;
+ }
+
+ if (conn->cred == NULL || conn->cred->dh_p == NULL ||
+ conn->cred->dh_g == NULL) {
+ wpa_printf(MSG_DEBUG, "TLSv1: No DH parameters available for "
+ "ServerKeyExhcange");
+ return -1;
+ }
+
+ os_free(conn->dh_secret);
+ conn->dh_secret_len = conn->cred->dh_p_len;
+ conn->dh_secret = os_malloc(conn->dh_secret_len);
+ if (conn->dh_secret == NULL) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Failed to allocate "
+ "memory for secret (Diffie-Hellman)");
+ tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_INTERNAL_ERROR);
+ return -1;
+ }
+ if (random_get_bytes(conn->dh_secret, conn->dh_secret_len)) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Failed to get random "
+ "data for Diffie-Hellman");
+ tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_INTERNAL_ERROR);
+ os_free(conn->dh_secret);
+ conn->dh_secret = NULL;
+ return -1;
+ }
+
+ if (os_memcmp(conn->dh_secret, conn->cred->dh_p, conn->dh_secret_len) >
+ 0)
+ conn->dh_secret[0] = 0; /* make sure secret < p */
+
+ pos = conn->dh_secret;
+ while (pos + 1 < conn->dh_secret + conn->dh_secret_len && *pos == 0)
+ pos++;
+ if (pos != conn->dh_secret) {
+ os_memmove(conn->dh_secret, pos,
+ conn->dh_secret_len - (pos - conn->dh_secret));
+ conn->dh_secret_len -= pos - conn->dh_secret;
+ }
+ wpa_hexdump_key(MSG_DEBUG, "TLSv1: DH server's secret value",
+ conn->dh_secret, conn->dh_secret_len);
+
+ /* Ys = g^secret mod p */
+ dh_ys_len = conn->cred->dh_p_len;
+ dh_ys = os_malloc(dh_ys_len);
+ if (dh_ys == NULL) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Failed to allocate memory for "
+ "Diffie-Hellman");
+ tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_INTERNAL_ERROR);
+ return -1;
+ }
+ if(wpa2_crypto_funcs.crypto_mod_exp) {
+ if (wpa2_crypto_funcs.crypto_mod_exp(conn->cred->dh_g, conn->cred->dh_g_len,
+ conn->dh_secret, conn->dh_secret_len,
+ conn->cred->dh_p, conn->cred->dh_p_len,
+ dh_ys, &dh_ys_len)) {
+ tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_INTERNAL_ERROR);
+ os_free(dh_ys);
+ return -1;
+ }
+ } else {
+ tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_INTERNAL_ERROR);
+ os_free(dh_ys);
+ wpa_printf(MSG_ERROR, "Fail to register crypto_mod_exp function!\r\n");
+ return -1;
+ }
+
+
+ wpa_hexdump(MSG_DEBUG, "TLSv1: DH Ys (server's public value)",
+ dh_ys, dh_ys_len);
+
+ /*
+ * struct {
+ * select (KeyExchangeAlgorithm) {
+ * case diffie_hellman:
+ * ServerDHParams params;
+ * Signature signed_params;
+ * case rsa:
+ * ServerRSAParams params;
+ * Signature signed_params;
+ * };
+ * } ServerKeyExchange;
+ *
+ * struct {
+ * opaque dh_p<1..2^16-1>;
+ * opaque dh_g<1..2^16-1>;
+ * opaque dh_Ys<1..2^16-1>;
+ * } ServerDHParams;
+ */
+
+ pos = *msgpos;
+
+ wpa_printf(MSG_DEBUG, "TLSv1: Send ServerKeyExchange");
+ rhdr = pos;
+ pos += TLS_RECORD_HEADER_LEN;
+
+ /* opaque fragment[TLSPlaintext.length] */
+
+ /* Handshake */
+ hs_start = pos;
+ /* HandshakeType msg_type */
+ *pos++ = TLS_HANDSHAKE_TYPE_SERVER_KEY_EXCHANGE;
+ /* uint24 length (to be filled) */
+ hs_length = pos;
+ pos += 3;
+
+ /* body - ServerDHParams */
+ /* dh_p */
+ if (pos + 2 + conn->cred->dh_p_len > end) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Not enough buffer space for "
+ "dh_p");
+ tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_INTERNAL_ERROR);
+ os_free(dh_ys);
+ return -1;
+ }
+ WPA_PUT_BE16(pos, conn->cred->dh_p_len);
+ pos += 2;
+ os_memcpy(pos, conn->cred->dh_p, conn->cred->dh_p_len);
+ pos += conn->cred->dh_p_len;
+
+ /* dh_g */
+ if (pos + 2 + conn->cred->dh_g_len > end) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Not enough buffer space for "
+ "dh_g");
+ tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_INTERNAL_ERROR);
+ os_free(dh_ys);
+ return -1;
+ }
+ WPA_PUT_BE16(pos, conn->cred->dh_g_len);
+ pos += 2;
+ os_memcpy(pos, conn->cred->dh_g, conn->cred->dh_g_len);
+ pos += conn->cred->dh_g_len;
+
+ /* dh_Ys */
+ if (pos + 2 + dh_ys_len > end) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Not enough buffer space for "
+ "dh_Ys");
+ tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_INTERNAL_ERROR);
+ os_free(dh_ys);
+ return -1;
+ }
+ WPA_PUT_BE16(pos, dh_ys_len);
+ pos += 2;
+ os_memcpy(pos, dh_ys, dh_ys_len);
+ pos += dh_ys_len;
+ os_free(dh_ys);
+
+ WPA_PUT_BE24(hs_length, pos - hs_length - 3);
+
+ if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_HANDSHAKE,
+ rhdr, end - rhdr, hs_start, pos - hs_start,
+ &rlen) < 0) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Failed to generate a record");
+ tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_INTERNAL_ERROR);
+ return -1;
+ }
+ pos = rhdr + rlen;
+
+ tls_verify_hash_add(&conn->verify, hs_start, pos - hs_start);
+
+ *msgpos = pos;
+
+ return 0;
+}
+
+
+static int tls_write_server_certificate_request(struct tlsv1_server *conn,
+ u8 **msgpos, u8 *end)
+{
+ u8 *pos, *rhdr, *hs_start, *hs_length;
+ size_t rlen;
+
+ if (!conn->verify_peer) {
+ wpa_printf(MSG_DEBUG, "TLSv1: No CertificateRequest needed");
+ return 0;
+ }
+
+ pos = *msgpos;
+
+ wpa_printf(MSG_DEBUG, "TLSv1: Send CertificateRequest");
+ rhdr = pos;
+ pos += TLS_RECORD_HEADER_LEN;
+
+ /* opaque fragment[TLSPlaintext.length] */
+
+ /* Handshake */
+ hs_start = pos;
+ /* HandshakeType msg_type */
+ *pos++ = TLS_HANDSHAKE_TYPE_CERTIFICATE_REQUEST;
+ /* uint24 length (to be filled) */
+ hs_length = pos;
+ pos += 3;
+ /* body - CertificateRequest */
+
+ /*
+ * enum {
+ * rsa_sign(1), dss_sign(2), rsa_fixed_dh(3), dss_fixed_dh(4),
+ * (255)
+ * } ClientCertificateType;
+ * ClientCertificateType certificate_types<1..2^8-1>
+ */
+ *pos++ = 1;
+ *pos++ = 1; /* rsa_sign */
+
+ /*
+ * opaque DistinguishedName<1..2^16-1>
+ * DistinguishedName certificate_authorities<3..2^16-1>
+ */
+ /* TODO: add support for listing DNs for trusted CAs */
+ WPA_PUT_BE16(pos, 0);
+ pos += 2;
+
+ WPA_PUT_BE24(hs_length, pos - hs_length - 3);
+
+ if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_HANDSHAKE,
+ rhdr, end - rhdr, hs_start, pos - hs_start,
+ &rlen) < 0) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Failed to generate a record");
+ tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_INTERNAL_ERROR);
+ return -1;
+ }
+ pos = rhdr + rlen;
+
+ tls_verify_hash_add(&conn->verify, hs_start, pos - hs_start);
+
+ *msgpos = pos;
+
+ return 0;
+}
+
+
+static int tls_write_server_hello_done(struct tlsv1_server *conn,
+ u8 **msgpos, u8 *end)
+{
+ u8 *pos;
+ size_t rlen;
+ u8 payload[4];
+
+ wpa_printf(MSG_DEBUG, "TLSv1: Send ServerHelloDone");
+
+ /* opaque fragment[TLSPlaintext.length] */
+
+ /* Handshake */
+ pos = payload;
+ /* HandshakeType msg_type */
+ *pos++ = TLS_HANDSHAKE_TYPE_SERVER_HELLO_DONE;
+ /* uint24 length */
+ WPA_PUT_BE24(pos, 0);
+ pos += 3;
+ /* body - ServerHelloDone (empty) */
+
+ if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_HANDSHAKE,
+ *msgpos, end - *msgpos, payload, pos - payload,
+ &rlen) < 0) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Failed to generate a record");
+ tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_INTERNAL_ERROR);
+ return -1;
+ }
+
+ tls_verify_hash_add(&conn->verify, payload, pos - payload);
+
+ *msgpos += rlen;
+
+ return 0;
+}
+
+
+static int tls_write_server_change_cipher_spec(struct tlsv1_server *conn,
+ u8 **msgpos, u8 *end)
+{
+ size_t rlen;
+ u8 payload[1];
+
+ wpa_printf(MSG_DEBUG, "TLSv1: Send ChangeCipherSpec");
+
+ payload[0] = TLS_CHANGE_CIPHER_SPEC;
+
+ if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_CHANGE_CIPHER_SPEC,
+ *msgpos, end - *msgpos, payload, sizeof(payload),
+ &rlen) < 0) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Failed to create a record");
+ tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_INTERNAL_ERROR);
+ return -1;
+ }
+
+ if (tlsv1_record_change_write_cipher(&conn->rl) < 0) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Failed to set write cipher for "
+ "record layer");
+ tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_INTERNAL_ERROR);
+ return -1;
+ }
+
+ *msgpos += rlen;
+
+ return 0;
+}
+
+
+static int tls_write_server_finished(struct tlsv1_server *conn,
+ u8 **msgpos, u8 *end)
+{
+ u8 *pos, *hs_start;
+ size_t rlen, hlen;
+ u8 verify_data[1 + 3 + TLS_VERIFY_DATA_LEN];
+ u8 hash[MD5_MAC_LEN + SHA1_MAC_LEN];
+
+ pos = *msgpos;
+
+ wpa_printf(MSG_DEBUG, "TLSv1: Send Finished");
+
+ /* Encrypted Handshake Message: Finished */
+
+#ifdef CONFIG_TLSV12
+ if (conn->rl.tls_version >= TLS_VERSION_1_2) {
+ hlen = SHA256_MAC_LEN;
+ if (wpa2_crypto_funcs.crypto_hash_finish) {
+ if (conn->verify.sha256_server == NULL ||
+ wpa2_crypto_funcs.crypto_hash_finish(conn->verify.sha256_server, hash, &hlen)
+ < 0) {
+ conn->verify.sha256_server = NULL;
+ tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_INTERNAL_ERROR);
+ return -1;
+ }
+ } else {
+ conn->verify.sha256_server = NULL;
+ tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_INTERNAL_ERROR);
+ wpa_printf(MSG_ERROR, "In function %s, fail to register crypto_hash_finish function!\r\n", __FUNCTION__);
+ return -1;
+ }
+ conn->verify.sha256_server = NULL;
+ } else {
+#endif /* CONFIG_TLSV12 */
+
+ hlen = MD5_MAC_LEN;
+ if (conn->verify.md5_server == NULL ||
+ crypto_hash_finish(conn->verify.md5_server, hash, &hlen) < 0) {
+ tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_INTERNAL_ERROR);
+ conn->verify.md5_server = NULL;
+ crypto_hash_finish(conn->verify.sha1_server, NULL, NULL);
+ conn->verify.sha1_server = NULL;
+ return -1;
+ }
+ conn->verify.md5_server = NULL;
+ hlen = SHA1_MAC_LEN;
+ if (conn->verify.sha1_server == NULL ||
+ crypto_hash_finish(conn->verify.sha1_server, hash + MD5_MAC_LEN,
+ &hlen) < 0) {
+ conn->verify.sha1_server = NULL;
+ tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_INTERNAL_ERROR);
+ return -1;
+ }
+ conn->verify.sha1_server = NULL;
+ hlen = MD5_MAC_LEN + SHA1_MAC_LEN;
+
+#ifdef CONFIG_TLSV12
+ }
+#endif /* CONFIG_TLSV12 */
+
+ if (tls_prf(conn->rl.tls_version,
+ conn->master_secret, TLS_MASTER_SECRET_LEN,
+ "server finished", hash, hlen,
+ verify_data + 1 + 3, TLS_VERIFY_DATA_LEN)) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Failed to generate verify_data");
+ tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_INTERNAL_ERROR);
+ return -1;
+ }
+ wpa_hexdump_key(MSG_DEBUG, "TLSv1: verify_data (server)",
+ verify_data + 1 + 3, TLS_VERIFY_DATA_LEN);
+
+ /* Handshake */
+ pos = hs_start = verify_data;
+ /* HandshakeType msg_type */
+ *pos++ = TLS_HANDSHAKE_TYPE_FINISHED;
+ /* uint24 length */
+ WPA_PUT_BE24(pos, TLS_VERIFY_DATA_LEN);
+ pos += 3;
+ pos += TLS_VERIFY_DATA_LEN;
+ tls_verify_hash_add(&conn->verify, hs_start, pos - hs_start);
+
+ if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_HANDSHAKE,
+ *msgpos, end - *msgpos, hs_start, pos - hs_start,
+ &rlen) < 0) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Failed to create a record");
+ tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_INTERNAL_ERROR);
+ return -1;
+ }
+
+ *msgpos += rlen;
+
+ return 0;
+}
+
+
+static u8 * tls_send_server_hello(struct tlsv1_server *conn, size_t *out_len)
+{
+ u8 *msg, *end, *pos;
+ size_t msglen;
+
+ *out_len = 0;
+
+ msglen = 1000 + tls_server_cert_chain_der_len(conn);
+
+ msg = os_malloc(msglen);
+ if (msg == NULL)
+ return NULL;
+
+ pos = msg;
+ end = msg + msglen;
+
+ if (tls_write_server_hello(conn, &pos, end) < 0) {
+ os_free(msg);
+ return NULL;
+ }
+
+ if (conn->use_session_ticket) {
+ /* Abbreviated handshake using session ticket; RFC 4507 */
+ if (tls_write_server_change_cipher_spec(conn, &pos, end) < 0 ||
+ tls_write_server_finished(conn, &pos, end) < 0) {
+ os_free(msg);
+ return NULL;
+ }
+
+ *out_len = pos - msg;
+
+ conn->state = CHANGE_CIPHER_SPEC;
+
+ return msg;
+ }
+
+ /* Full handshake */
+ if (tls_write_server_certificate(conn, &pos, end) < 0 ||
+ tls_write_server_key_exchange(conn, &pos, end) < 0 ||
+ tls_write_server_certificate_request(conn, &pos, end) < 0 ||
+ tls_write_server_hello_done(conn, &pos, end) < 0) {
+ os_free(msg);
+ return NULL;
+ }
+
+ *out_len = pos - msg;
+
+ conn->state = CLIENT_CERTIFICATE;
+
+ return msg;
+}
+
+
+static u8 * tls_send_change_cipher_spec(struct tlsv1_server *conn,
+ size_t *out_len)
+{
+ u8 *msg, *end, *pos;
+
+ *out_len = 0;
+
+ msg = os_malloc(1000);
+ if (msg == NULL)
+ return NULL;
+
+ pos = msg;
+ end = msg + 1000;
+
+ if (tls_write_server_change_cipher_spec(conn, &pos, end) < 0 ||
+ tls_write_server_finished(conn, &pos, end) < 0) {
+ os_free(msg);
+ return NULL;
+ }
+
+ *out_len = pos - msg;
+
+ wpa_printf(MSG_DEBUG, "TLSv1: Handshake completed successfully");
+ conn->state = ESTABLISHED;
+
+ return msg;
+}
+
+
+u8 * tlsv1_server_handshake_write(struct tlsv1_server *conn, size_t *out_len)
+{
+ switch (conn->state) {
+ case SERVER_HELLO:
+ return tls_send_server_hello(conn, out_len);
+ case SERVER_CHANGE_CIPHER_SPEC:
+ return tls_send_change_cipher_spec(conn, out_len);
+ default:
+ if (conn->state == ESTABLISHED && conn->use_session_ticket) {
+ /* Abbreviated handshake was already completed. */
+ return NULL;
+ }
+ wpa_printf(MSG_DEBUG, "TLSv1: Unexpected state %d while "
+ "generating reply", conn->state);
+ return NULL;
+ }
+}
+
+
+u8 * tlsv1_server_send_alert(struct tlsv1_server *conn, u8 level,
+ u8 description, size_t *out_len)
+{
+ u8 *alert, *pos, *length;
+
+ wpa_printf(MSG_DEBUG, "TLSv1: Send Alert(%d:%d)", level, description);
+ *out_len = 0;
+
+ alert = os_malloc(10);
+ if (alert == NULL)
+ return NULL;
+
+ pos = alert;
+
+ /* TLSPlaintext */
+ /* ContentType type */
+ *pos++ = TLS_CONTENT_TYPE_ALERT;
+ /* ProtocolVersion version */
+ WPA_PUT_BE16(pos, conn->rl.tls_version ? conn->rl.tls_version :
+ TLS_VERSION);
+ pos += 2;
+ /* uint16 length (to be filled) */
+ length = pos;
+ pos += 2;
+ /* opaque fragment[TLSPlaintext.length] */
+
+ /* Alert */
+ /* AlertLevel level */
+ *pos++ = level;
+ /* AlertDescription description */
+ *pos++ = description;
+
+ WPA_PUT_BE16(length, pos - length - 2);
+ *out_len = pos - alert;
+
+ return alert;
+}
--- /dev/null
+/*
+ * X.509v3 certificate parsing and processing (RFC 3280 profile)
+ * Copyright (c) 2006-2011, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "wpa/includes.h"
+#include "wpa/wpa.h"
+#include "wpa/common.h"
+#include "crypto/crypto.h"
+#include "wpa2/tls/asn1.h"
+#include "wpa2/tls/x509v3.h"
+
+#include "wpa2/eap_peer/eap_i.h"
+
+static void x509_free_name(struct x509_name *name)
+{
+ size_t i;
+
+ for (i = 0; i < name->num_attr; i++) {
+ os_free(name->attr[i].value);
+ name->attr[i].value = NULL;
+ name->attr[i].type = X509_NAME_ATTR_NOT_USED;
+ }
+ name->num_attr = 0;
+ os_free(name->email);
+ name->email = NULL;
+
+ os_free(name->alt_email);
+ os_free(name->dns);
+ os_free(name->uri);
+ os_free(name->ip);
+ name->alt_email = name->dns = name->uri = NULL;
+ name->ip = NULL;
+ name->ip_len = 0;
+ os_memset(&name->rid, 0, sizeof(name->rid));
+}
+
+
+/**
+ * x509_certificate_free - Free an X.509 certificate
+ * @cert: Certificate to be freed
+ */
+void x509_certificate_free(struct x509_certificate *cert)
+{
+ if (cert == NULL)
+ return;
+ if (cert->next) {
+ wpa_printf(MSG_DEBUG, "X509: x509_certificate_free: cer=%p "
+ "was still on a list (next=%p)\n",
+ cert, cert->next);
+ }
+ x509_free_name(&cert->issuer);
+ x509_free_name(&cert->subject);
+ os_free(cert->public_key);
+ os_free(cert->sign_value);
+ os_free(cert);
+}
+
+
+/**
+ * x509_certificate_free - Free an X.509 certificate chain
+ * @cert: Pointer to the first certificate in the chain
+ */
+void x509_certificate_chain_free(struct x509_certificate *cert)
+{
+ struct x509_certificate *next;
+
+ while (cert) {
+ next = cert->next;
+ cert->next = NULL;
+ x509_certificate_free(cert);
+ cert = next;
+ }
+}
+
+
+static int x509_whitespace(char c)
+{
+ return c == ' ' || c == '\t';
+}
+
+
+static void x509_str_strip_whitespace(char *a)
+{
+ char *ipos, *opos;
+ int remove_whitespace = 1;
+
+ ipos = opos = a;
+
+ while (*ipos) {
+ if (remove_whitespace && x509_whitespace(*ipos))
+ ipos++;
+ else {
+ remove_whitespace = x509_whitespace(*ipos);
+ *opos++ = *ipos++;
+ }
+ }
+
+ *opos-- = '\0';
+ if (opos > a && x509_whitespace(*opos))
+ *opos = '\0';
+}
+
+
+static int x509_str_compare(const char *a, const char *b)
+{
+ char *aa, *bb;
+ int ret;
+
+ if (!a && b)
+ return -1;
+ if (a && !b)
+ return 1;
+ if (!a && !b)
+ return 0;
+
+ aa = os_strdup(a);
+ bb = os_strdup(b);
+
+ if (aa == NULL || bb == NULL) {
+ os_free(aa);
+ os_free(bb);
+ return strcasecmp(a, b);
+ }
+
+ x509_str_strip_whitespace(aa);
+ x509_str_strip_whitespace(bb);
+
+ ret = strcasecmp(aa, bb);
+
+ os_free(aa);
+ os_free(bb);
+
+ return ret;
+}
+
+
+/**
+ * x509_name_compare - Compare X.509 certificate names
+ * @a: Certificate name
+ * @b: Certificate name
+ * Returns: <0, 0, or >0 based on whether a is less than, equal to, or
+ * greater than b
+ */
+int x509_name_compare(struct x509_name *a, struct x509_name *b)
+{
+ int res;
+ size_t i;
+
+ if (!a && b)
+ return -1;
+ if (a && !b)
+ return 1;
+ if (!a && !b)
+ return 0;
+ if (a->num_attr < b->num_attr)
+ return -1;
+ if (a->num_attr > b->num_attr)
+ return 1;
+
+ for (i = 0; i < a->num_attr; i++) {
+ if (a->attr[i].type < b->attr[i].type)
+ return -1;
+ if (a->attr[i].type > b->attr[i].type)
+ return -1;
+ res = x509_str_compare(a->attr[i].value, b->attr[i].value);
+ if (res)
+ return res;
+ }
+ res = x509_str_compare(a->email, b->email);
+ if (res)
+ return res;
+
+ return 0;
+}
+
+
+static int x509_parse_algorithm_identifier(
+ const u8 *buf, size_t len,
+ struct x509_algorithm_identifier *id, const u8 **next)
+{
+ struct asn1_hdr hdr;
+ const u8 *pos, *end;
+
+ /*
+ * AlgorithmIdentifier ::= SEQUENCE {
+ * algorithm OBJECT IDENTIFIER,
+ * parameters ANY DEFINED BY algorithm OPTIONAL
+ * }
+ */
+
+ if (asn1_get_next(buf, len, &hdr) < 0 ||
+ hdr.class != ASN1_CLASS_UNIVERSAL ||
+ hdr.tag != ASN1_TAG_SEQUENCE) {
+ wpa_printf(MSG_DEBUG, "X509: Expected SEQUENCE "
+ "(AlgorithmIdentifier) - found class %d tag 0x%x",
+ hdr.class, hdr.tag);
+ return -1;
+ }
+ pos = hdr.payload;
+ end = pos + hdr.length;
+
+ if (end > buf + len)
+ return -1;
+
+ *next = end;
+
+ if (asn1_get_oid(pos, end - pos, &id->oid, &pos))
+ return -1;
+
+ /* TODO: optional parameters */
+
+ return 0;
+}
+
+
+static int x509_parse_public_key(const u8 *buf, size_t len,
+ struct x509_certificate *cert,
+ const u8 **next)
+{
+ struct asn1_hdr hdr;
+ const u8 *pos, *end;
+
+ /*
+ * SubjectPublicKeyInfo ::= SEQUENCE {
+ * algorithm AlgorithmIdentifier,
+ * subjectPublicKey BIT STRING
+ * }
+ */
+
+ pos = buf;
+ end = buf + len;
+
+ if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
+ hdr.class != ASN1_CLASS_UNIVERSAL ||
+ hdr.tag != ASN1_TAG_SEQUENCE) {
+ wpa_printf(MSG_DEBUG, "X509: Expected SEQUENCE "
+ "(SubjectPublicKeyInfo) - found class %d tag 0x%x",
+ hdr.class, hdr.tag);
+ return -1;
+ }
+ pos = hdr.payload;
+
+ if (pos + hdr.length > end)
+ return -1;
+ end = pos + hdr.length;
+ *next = end;
+
+ if (x509_parse_algorithm_identifier(pos, end - pos,
+ &cert->public_key_alg, &pos))
+ return -1;
+
+ if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
+ hdr.class != ASN1_CLASS_UNIVERSAL ||
+ hdr.tag != ASN1_TAG_BITSTRING) {
+ wpa_printf(MSG_DEBUG, "X509: Expected BITSTRING "
+ "(subjectPublicKey) - found class %d tag 0x%x",
+ hdr.class, hdr.tag);
+ return -1;
+ }
+ if (hdr.length < 1)
+ return -1;
+ pos = hdr.payload;
+ if (*pos) {
+ wpa_printf(MSG_DEBUG, "X509: BITSTRING - %d unused bits",
+ *pos);
+ /*
+ * TODO: should this be rejected? X.509 certificates are
+ * unlikely to use such a construction. Now we would end up
+ * including the extra bits in the buffer which may also be
+ * ok.
+ */
+ }
+ os_free(cert->public_key);
+ cert->public_key = os_malloc(hdr.length - 1);
+ if (cert->public_key == NULL) {
+ wpa_printf(MSG_DEBUG, "X509: Failed to allocate memory for "
+ "public key");
+ return -1;
+ }
+ os_memcpy(cert->public_key, pos + 1, hdr.length - 1);
+ cert->public_key_len = hdr.length - 1;
+ wpa_hexdump(MSG_MSGDUMP, "X509: subjectPublicKey",
+ cert->public_key, cert->public_key_len);
+
+ return 0;
+}
+
+
+static int x509_parse_name(const u8 *buf, size_t len, struct x509_name *name,
+ const u8 **next)
+{
+ struct asn1_hdr hdr;
+ const u8 *pos, *end, *set_pos, *set_end, *seq_pos, *seq_end;
+ struct asn1_oid oid;
+ char *val;
+
+ /*
+ * Name ::= CHOICE { RDNSequence }
+ * RDNSequence ::= SEQUENCE OF RelativeDistinguishedName
+ * RelativeDistinguishedName ::= SET OF AttributeTypeAndValue
+ * AttributeTypeAndValue ::= SEQUENCE {
+ * type AttributeType,
+ * value AttributeValue
+ * }
+ * AttributeType ::= OBJECT IDENTIFIER
+ * AttributeValue ::= ANY DEFINED BY AttributeType
+ */
+
+ if (asn1_get_next(buf, len, &hdr) < 0 ||
+ hdr.class != ASN1_CLASS_UNIVERSAL ||
+ hdr.tag != ASN1_TAG_SEQUENCE) {
+ wpa_printf(MSG_DEBUG, "X509: Expected SEQUENCE "
+ "(Name / RDNSequencer) - found class %d tag 0x%x",
+ hdr.class, hdr.tag);
+ return -1;
+ }
+ pos = hdr.payload;
+
+ if (pos + hdr.length > buf + len)
+ return -1;
+
+ end = *next = pos + hdr.length;
+
+ while (pos < end) {
+ enum x509_name_attr_type type;
+
+ if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
+ hdr.class != ASN1_CLASS_UNIVERSAL ||
+ hdr.tag != ASN1_TAG_SET) {
+ wpa_printf(MSG_DEBUG, "X509: Expected SET "
+ "(RelativeDistinguishedName) - found class "
+ "%d tag 0x%x", hdr.class, hdr.tag);
+ x509_free_name(name);
+ return -1;
+ }
+
+ set_pos = hdr.payload;
+ pos = set_end = hdr.payload + hdr.length;
+
+ if (asn1_get_next(set_pos, set_end - set_pos, &hdr) < 0 ||
+ hdr.class != ASN1_CLASS_UNIVERSAL ||
+ hdr.tag != ASN1_TAG_SEQUENCE) {
+ wpa_printf(MSG_DEBUG, "X509: Expected SEQUENCE "
+ "(AttributeTypeAndValue) - found class %d "
+ "tag 0x%x", hdr.class, hdr.tag);
+ x509_free_name(name);
+ return -1;
+ }
+
+ seq_pos = hdr.payload;
+ seq_end = hdr.payload + hdr.length;
+
+ if (asn1_get_oid(seq_pos, seq_end - seq_pos, &oid, &seq_pos)) {
+ x509_free_name(name);
+ return -1;
+ }
+
+ if (asn1_get_next(seq_pos, seq_end - seq_pos, &hdr) < 0 ||
+ hdr.class != ASN1_CLASS_UNIVERSAL) {
+ wpa_printf(MSG_DEBUG, "X509: Failed to parse "
+ "AttributeValue");
+ x509_free_name(name);
+ return -1;
+ }
+
+ /* RFC 3280:
+ * MUST: country, organization, organizational-unit,
+ * distinguished name qualifier, state or province name,
+ * common name, serial number.
+ * SHOULD: locality, title, surname, given name, initials,
+ * pseudonym, generation qualifier.
+ * MUST: domainComponent (RFC 2247).
+ */
+ type = X509_NAME_ATTR_NOT_USED;
+ if (oid.len == 4 &&
+ oid.oid[0] == 2 && oid.oid[1] == 5 && oid.oid[2] == 4) {
+ /* id-at ::= 2.5.4 */
+ switch (oid.oid[3]) {
+ case 3:
+ /* commonName */
+ type = X509_NAME_ATTR_CN;
+ break;
+ case 6:
+ /* countryName */
+ type = X509_NAME_ATTR_C;
+ break;
+ case 7:
+ /* localityName */
+ type = X509_NAME_ATTR_L;
+ break;
+ case 8:
+ /* stateOrProvinceName */
+ type = X509_NAME_ATTR_ST;
+ break;
+ case 10:
+ /* organizationName */
+ type = X509_NAME_ATTR_O;
+ break;
+ case 11:
+ /* organizationalUnitName */
+ type = X509_NAME_ATTR_OU;
+ break;
+ }
+ } else if (oid.len == 7 &&
+ oid.oid[0] == 1 && oid.oid[1] == 2 &&
+ oid.oid[2] == 840 && oid.oid[3] == 113549 &&
+ oid.oid[4] == 1 && oid.oid[5] == 9 &&
+ oid.oid[6] == 1) {
+ /* 1.2.840.113549.1.9.1 - e-mailAddress */
+ os_free(name->email);
+ name->email = os_malloc(hdr.length + 1);
+ if (name->email == NULL) {
+ x509_free_name(name);
+ return -1;
+ }
+ os_memcpy(name->email, hdr.payload, hdr.length);
+ name->email[hdr.length] = '\0';
+ continue;
+ } else if (oid.len == 7 &&
+ oid.oid[0] == 0 && oid.oid[1] == 9 &&
+ oid.oid[2] == 2342 && oid.oid[3] == 19200300 &&
+ oid.oid[4] == 100 && oid.oid[5] == 1 &&
+ oid.oid[6] == 25) {
+ /* 0.9.2342.19200300.100.1.25 - domainComponent */
+ type = X509_NAME_ATTR_DC;
+ }
+
+ if (type == X509_NAME_ATTR_NOT_USED) {
+ wpa_hexdump(MSG_DEBUG, "X509: Unrecognized OID",
+ (u8 *) oid.oid,
+ oid.len * sizeof(oid.oid[0]));
+ wpa_hexdump_ascii(MSG_MSGDUMP, "X509: Attribute Data",
+ hdr.payload, hdr.length);
+ continue;
+ }
+
+ if (name->num_attr == X509_MAX_NAME_ATTRIBUTES) {
+ wpa_printf(MSG_INFO, "X509: Too many Name attributes");
+ x509_free_name(name);
+ return -1;
+ }
+
+ val = (char *)dup_binstr(hdr.payload, hdr.length);
+ if (val == NULL) {
+ x509_free_name(name);
+ return -1;
+ }
+ if (os_strlen(val) != hdr.length) {
+ wpa_printf(MSG_INFO, "X509: Reject certificate with "
+ "embedded NUL byte in a string (%s[NUL])",
+ val);
+ os_free(val);
+ x509_free_name(name);
+ return -1;
+ }
+
+ name->attr[name->num_attr].type = type;
+ name->attr[name->num_attr].value = val;
+ name->num_attr++;
+ }
+
+ return 0;
+}
+
+
+static char * x509_name_attr_str(enum x509_name_attr_type type)
+{
+#ifndef ESPRESSIF_USE
+ switch (type) {
+ case X509_NAME_ATTR_NOT_USED:
+ return "[N/A]";
+ case X509_NAME_ATTR_DC:
+ return "DC";
+ case X509_NAME_ATTR_CN:
+ return "CN";
+ case X509_NAME_ATTR_C:
+ return "C";
+ case X509_NAME_ATTR_L:
+ return "L";
+ case X509_NAME_ATTR_ST:
+ return "ST";
+ case X509_NAME_ATTR_O:
+ return "O";
+ case X509_NAME_ATTR_OU:
+ return "OU";
+ }
+ return "?";
+#else
+ static char name_attr[6];
+ switch (type) {
+ case X509_NAME_ATTR_NOT_USED:
+ strcpy(name_attr, "[N/A]");
+ case X509_NAME_ATTR_DC:
+ strcpy(name_attr, "DC");
+ case X509_NAME_ATTR_CN:
+ strcpy(name_attr, "CN");
+ case X509_NAME_ATTR_C:
+ strcpy(name_attr, "C");
+ case X509_NAME_ATTR_L:
+ strcpy(name_attr, "L");
+ case X509_NAME_ATTR_ST:
+ strcpy(name_attr, "ST");
+ case X509_NAME_ATTR_O:
+ strcpy(name_attr, "O");
+ case X509_NAME_ATTR_OU:
+ strcpy(name_attr, "OU");
+ default :
+ strcpy(name_attr, "?");
+ }
+ return name_attr;
+#endif
+}
+
+
+/**
+ * x509_name_string - Convert an X.509 certificate name into a string
+ * @name: Name to convert
+ * @buf: Buffer for the string
+ * @len: Maximum buffer length
+ */
+void x509_name_string(struct x509_name *name, char *buf, size_t len)
+{
+ char *pos, *end;
+ int ret;
+ size_t i;
+
+ if (len == 0)
+ return;
+
+ pos = buf;
+ end = buf + len;
+
+ for (i = 0; i < name->num_attr; i++) {
+ //ret = os_snprintf(pos, end - pos, "%s=%s, ",
+ ret = sprintf(pos, "%s=%s, ",
+ x509_name_attr_str(name->attr[i].type),
+ name->attr[i].value);
+ if (ret < 0 || ret >= end - pos)
+ goto done;
+ pos += ret;
+ }
+
+ if (pos > buf + 1 && pos[-1] == ' ' && pos[-2] == ',') {
+ pos--;
+ *pos = '\0';
+ pos--;
+ *pos = '\0';
+ }
+
+ if (name->email) {
+ //ret = os_snprintf(pos, end - pos, "/emailAddress=%s",
+ ret = sprintf(pos, "/emailAddress=%s",
+ name->email);
+ if (ret < 0 || ret >= end - pos)
+ goto done;
+ pos += ret;
+ }
+
+done:
+ end[-1] = '\0';
+}
+
+
+static int x509_parse_time(const u8 *buf, size_t len, u8 asn1_tag,
+ os_time_t *val)
+{
+#if 0
+ const char *pos;
+ int year, month, day, hour, min, sec;
+
+ /*
+ * Time ::= CHOICE {
+ * utcTime UTCTime,
+ * generalTime GeneralizedTime
+ * }
+ *
+ * UTCTime: YYMMDDHHMMSSZ
+ * GeneralizedTime: YYYYMMDDHHMMSSZ
+ */
+
+ pos = (const char *) buf;
+
+ switch (asn1_tag) {
+ case ASN1_TAG_UTCTIME:
+ if (len != 13 || buf[12] != 'Z') {
+ wpa_hexdump_ascii(MSG_DEBUG, "X509: Unrecognized "
+ "UTCTime format", buf, len);
+ return -1;
+ }
+ if (sscanf(pos, "%02d", &year) != 1) {
+ wpa_hexdump_ascii(MSG_DEBUG, "X509: Failed to parse "
+ "UTCTime year", buf, len);
+ return -1;
+ }
+ if (year < 50)
+ year += 2000;
+ else
+ year += 1900;
+ pos += 2;
+ break;
+ case ASN1_TAG_GENERALIZEDTIME:
+ if (len != 15 || buf[14] != 'Z') {
+ wpa_hexdump_ascii(MSG_DEBUG, "X509: Unrecognized "
+ "GeneralizedTime format", buf, len);
+ return -1;
+ }
+ if (sscanf(pos, "%04d", &year) != 1) {
+ wpa_hexdump_ascii(MSG_DEBUG, "X509: Failed to parse "
+ "GeneralizedTime year", buf, len);
+ return -1;
+ }
+ pos += 4;
+ break;
+ default:
+ wpa_printf(MSG_DEBUG, "X509: Expected UTCTime or "
+ "GeneralizedTime - found tag 0x%x", asn1_tag);
+ return -1;
+ }
+
+ if (sscanf(pos, "%02d", &month) != 1) {
+ wpa_hexdump_ascii(MSG_DEBUG, "X509: Failed to parse Time "
+ "(month)", buf, len);
+ return -1;
+ }
+ pos += 2;
+
+ if (sscanf(pos, "%02d", &day) != 1) {
+ wpa_hexdump_ascii(MSG_DEBUG, "X509: Failed to parse Time "
+ "(day)", buf, len);
+ return -1;
+ }
+ pos += 2;
+
+ if (sscanf(pos, "%02d", &hour) != 1) {
+ wpa_hexdump_ascii(MSG_DEBUG, "X509: Failed to parse Time "
+ "(hour)", buf, len);
+ return -1;
+ }
+ pos += 2;
+
+ if (sscanf(pos, "%02d", &min) != 1) {
+ wpa_hexdump_ascii(MSG_DEBUG, "X509: Failed to parse Time "
+ "(min)", buf, len);
+ return -1;
+ }
+ pos += 2;
+
+ if (sscanf(pos, "%02d", &sec) != 1) {
+ wpa_hexdump_ascii(MSG_DEBUG, "X509: Failed to parse Time "
+ "(sec)", buf, len);
+ return -1;
+ }
+
+ if (os_mktime(year, month, day, hour, min, sec, val) < 0) {
+ wpa_hexdump_ascii(MSG_DEBUG, "X509: Failed to convert Time",
+ buf, len);
+ if (year < 1970) {
+ /*
+ * At least some test certificates have been configured
+ * to use dates prior to 1970. Set the date to
+ * beginning of 1970 to handle these case.
+ */
+ wpa_printf(MSG_DEBUG, "X509: Year=%d before epoch - "
+ "assume epoch as the time", year);
+ *val = 0;
+ return 0;
+ }
+ return -1;
+ }
+#endif
+ return 0;
+}
+
+
+static int x509_parse_validity(const u8 *buf, size_t len,
+ struct x509_certificate *cert, const u8 **next)
+{
+ struct asn1_hdr hdr;
+ const u8 *pos;
+ size_t plen;
+
+ /*
+ * Validity ::= SEQUENCE {
+ * notBefore Time,
+ * notAfter Time
+ * }
+ *
+ * RFC 3280, 4.1.2.5:
+ * CAs conforming to this profile MUST always encode certificate
+ * validity dates through the year 2049 as UTCTime; certificate
+ * validity dates in 2050 or later MUST be encoded as GeneralizedTime.
+ */
+
+ if (asn1_get_next(buf, len, &hdr) < 0 ||
+ hdr.class != ASN1_CLASS_UNIVERSAL ||
+ hdr.tag != ASN1_TAG_SEQUENCE) {
+ wpa_printf(MSG_DEBUG, "X509: Expected SEQUENCE "
+ "(Validity) - found class %d tag 0x%x",
+ hdr.class, hdr.tag);
+ return -1;
+ }
+ pos = hdr.payload;
+ plen = hdr.length;
+
+ if (pos + plen > buf + len)
+ return -1;
+
+ *next = pos + plen;
+
+ if (asn1_get_next(pos, plen, &hdr) < 0 ||
+ hdr.class != ASN1_CLASS_UNIVERSAL ||
+ x509_parse_time(hdr.payload, hdr.length, hdr.tag,
+ &cert->not_before) < 0) {
+ wpa_hexdump_ascii(MSG_DEBUG, "X509: Failed to parse notBefore "
+ "Time", hdr.payload, hdr.length);
+ return -1;
+ }
+
+ pos = hdr.payload + hdr.length;
+ plen = *next - pos;
+
+ if (asn1_get_next(pos, plen, &hdr) < 0 ||
+ hdr.class != ASN1_CLASS_UNIVERSAL ||
+ x509_parse_time(hdr.payload, hdr.length, hdr.tag,
+ &cert->not_after) < 0) {
+ wpa_hexdump_ascii(MSG_DEBUG, "X509: Failed to parse notAfter "
+ "Time", hdr.payload, hdr.length);
+ return -1;
+ }
+
+ wpa_printf(MSG_DEBUG, "X509: Validity: notBefore: %lu notAfter: %lu",
+ (unsigned long) cert->not_before,
+ (unsigned long) cert->not_after);
+
+ return 0;
+}
+
+
+static int x509_id_ce_oid(struct asn1_oid *oid)
+{
+ /* id-ce arc from X.509 for standard X.509v3 extensions */
+ return oid->len >= 4 &&
+ oid->oid[0] == 2 /* joint-iso-ccitt */ &&
+ oid->oid[1] == 5 /* ds */ &&
+ oid->oid[2] == 29 /* id-ce */;
+}
+
+
+static int x509_parse_ext_key_usage(struct x509_certificate *cert,
+ const u8 *pos, size_t len)
+{
+ struct asn1_hdr hdr;
+
+ /*
+ * KeyUsage ::= BIT STRING {
+ * digitalSignature (0),
+ * nonRepudiation (1),
+ * keyEncipherment (2),
+ * dataEncipherment (3),
+ * keyAgreement (4),
+ * keyCertSign (5),
+ * cRLSign (6),
+ * encipherOnly (7),
+ * decipherOnly (8) }
+ */
+
+ if (asn1_get_next(pos, len, &hdr) < 0 ||
+ hdr.class != ASN1_CLASS_UNIVERSAL ||
+ hdr.tag != ASN1_TAG_BITSTRING ||
+ hdr.length < 1) {
+ wpa_printf(MSG_DEBUG, "X509: Expected BIT STRING in "
+ "KeyUsage; found %d tag 0x%x len %d",
+ hdr.class, hdr.tag, hdr.length);
+ return -1;
+ }
+
+ cert->extensions_present |= X509_EXT_KEY_USAGE;
+ cert->key_usage = asn1_bit_string_to_long(hdr.payload, hdr.length);
+
+ wpa_printf(MSG_DEBUG, "X509: KeyUsage 0x%lx", cert->key_usage);
+
+ return 0;
+}
+
+
+static int x509_parse_ext_basic_constraints(struct x509_certificate *cert,
+ const u8 *pos, size_t len)
+{
+ struct asn1_hdr hdr;
+ unsigned long value;
+ size_t left;
+
+ /*
+ * BasicConstraints ::= SEQUENCE {
+ * cA BOOLEAN DEFAULT FALSE,
+ * pathLenConstraint INTEGER (0..MAX) OPTIONAL }
+ */
+
+ if (asn1_get_next(pos, len, &hdr) < 0 ||
+ hdr.class != ASN1_CLASS_UNIVERSAL ||
+ hdr.tag != ASN1_TAG_SEQUENCE) {
+ wpa_printf(MSG_DEBUG, "X509: Expected SEQUENCE in "
+ "BasicConstraints; found %d tag 0x%x",
+ hdr.class, hdr.tag);
+ return -1;
+ }
+
+ cert->extensions_present |= X509_EXT_BASIC_CONSTRAINTS;
+
+ if (hdr.length == 0)
+ return 0;
+
+ if (asn1_get_next(hdr.payload, hdr.length, &hdr) < 0 ||
+ hdr.class != ASN1_CLASS_UNIVERSAL) {
+ wpa_printf(MSG_DEBUG, "X509: Failed to parse "
+ "BasicConstraints");
+ return -1;
+ }
+
+ if (hdr.tag == ASN1_TAG_BOOLEAN) {
+ if (hdr.length != 1) {
+ wpa_printf(MSG_DEBUG, "X509: Unexpected "
+ "Boolean length (%u) in BasicConstraints",
+ hdr.length);
+ return -1;
+ }
+ cert->ca = hdr.payload[0];
+
+ if (hdr.payload + hdr.length == pos + len) {
+ wpa_printf(MSG_DEBUG, "X509: BasicConstraints - cA=%d",
+ cert->ca);
+ return 0;
+ }
+
+ if (asn1_get_next(hdr.payload + hdr.length, len - hdr.length,
+ &hdr) < 0 ||
+ hdr.class != ASN1_CLASS_UNIVERSAL) {
+ wpa_printf(MSG_DEBUG, "X509: Failed to parse "
+ "BasicConstraints");
+ return -1;
+ }
+ }
+
+ if (hdr.tag != ASN1_TAG_INTEGER) {
+ wpa_printf(MSG_DEBUG, "X509: Expected INTEGER in "
+ "BasicConstraints; found class %d tag 0x%x",
+ hdr.class, hdr.tag);
+ return -1;
+ }
+
+ pos = hdr.payload;
+ left = hdr.length;
+ value = 0;
+ while (left) {
+ value <<= 8;
+ value |= *pos++;
+ left--;
+ }
+
+ cert->path_len_constraint = value;
+ cert->extensions_present |= X509_EXT_PATH_LEN_CONSTRAINT;
+
+ wpa_printf(MSG_DEBUG, "X509: BasicConstraints - cA=%d "
+ "pathLenConstraint=%lu",
+ cert->ca, cert->path_len_constraint);
+
+ return 0;
+}
+
+
+static int x509_parse_alt_name_rfc8222(struct x509_name *name,
+ const u8 *pos, size_t len)
+{
+ /* rfc822Name IA5String */
+ wpa_hexdump_ascii(MSG_MSGDUMP, "X509: altName - rfc822Name", pos, len);
+ os_free(name->alt_email);
+ name->alt_email = (char *)os_zalloc(len + 1);
+ if (name->alt_email == NULL)
+ return -1;
+ os_memcpy(name->alt_email, pos, len);
+ if (os_strlen(name->alt_email) != len) {
+ wpa_printf(MSG_INFO, "X509: Reject certificate with "
+ "embedded NUL byte in rfc822Name (%s[NUL])",
+ name->alt_email);
+ os_free(name->alt_email);
+ name->alt_email = NULL;
+ return -1;
+ }
+ return 0;
+}
+
+
+static int x509_parse_alt_name_dns(struct x509_name *name,
+ const u8 *pos, size_t len)
+{
+ /* dNSName IA5String */
+ wpa_hexdump_ascii(MSG_MSGDUMP, "X509: altName - dNSName", pos, len);
+ os_free(name->dns);
+ name->dns = (char *)os_zalloc(len + 1);
+ if (name->dns == NULL)
+ return -1;
+ os_memcpy(name->dns, pos, len);
+ if (os_strlen(name->dns) != len) {
+ wpa_printf(MSG_INFO, "X509: Reject certificate with "
+ "embedded NUL byte in dNSName (%s[NUL])",
+ name->dns);
+ os_free(name->dns);
+ name->dns = NULL;
+ return -1;
+ }
+ return 0;
+}
+
+
+static int x509_parse_alt_name_uri(struct x509_name *name,
+ const u8 *pos, size_t len)
+{
+ /* uniformResourceIdentifier IA5String */
+ wpa_hexdump_ascii(MSG_MSGDUMP,
+ "X509: altName - uniformResourceIdentifier",
+ pos, len);
+ os_free(name->uri);
+ name->uri = (char *)os_zalloc(len + 1);
+ if (name->uri == NULL)
+ return -1;
+ os_memcpy(name->uri, pos, len);
+ if (os_strlen(name->uri) != len) {
+ wpa_printf(MSG_INFO, "X509: Reject certificate with "
+ "embedded NUL byte in uniformResourceIdentifier "
+ "(%s[NUL])", name->uri);
+ os_free(name->uri);
+ name->uri = NULL;
+ return -1;
+ }
+ return 0;
+}
+
+
+static int x509_parse_alt_name_ip(struct x509_name *name,
+ const u8 *pos, size_t len)
+{
+ /* iPAddress OCTET STRING */
+ wpa_hexdump(MSG_MSGDUMP, "X509: altName - iPAddress", pos, len);
+ os_free(name->ip);
+ name->ip = os_malloc(len);
+ if (name->ip == NULL)
+ return -1;
+ os_memcpy(name->ip, pos, len);
+ name->ip_len = len;
+ return 0;
+}
+
+
+static int x509_parse_alt_name_rid(struct x509_name *name,
+ const u8 *pos, size_t len)
+{
+ char buf[80];
+
+ /* registeredID OBJECT IDENTIFIER */
+ if (asn1_parse_oid(pos, len, &name->rid) < 0)
+ return -1;
+
+ asn1_oid_to_str(&name->rid, buf, sizeof(buf));
+ wpa_printf(MSG_DEBUG, "X509: altName - registeredID: %s", buf);
+
+ return 0;
+}
+
+
+static int x509_parse_ext_alt_name(struct x509_name *name,
+ const u8 *pos, size_t len)
+{
+ struct asn1_hdr hdr;
+ const u8 *p, *end;
+
+ /*
+ * GeneralNames ::= SEQUENCE SIZE (1..MAX) OF GeneralName
+ *
+ * GeneralName ::= CHOICE {
+ * otherName [0] OtherName,
+ * rfc822Name [1] IA5String,
+ * dNSName [2] IA5String,
+ * x400Address [3] ORAddress,
+ * directoryName [4] Name,
+ * ediPartyName [5] EDIPartyName,
+ * uniformResourceIdentifier [6] IA5String,
+ * iPAddress [7] OCTET STRING,
+ * registeredID [8] OBJECT IDENTIFIER }
+ *
+ * OtherName ::= SEQUENCE {
+ * type-id OBJECT IDENTIFIER,
+ * value [0] EXPLICIT ANY DEFINED BY type-id }
+ *
+ * EDIPartyName ::= SEQUENCE {
+ * nameAssigner [0] DirectoryString OPTIONAL,
+ * partyName [1] DirectoryString }
+ */
+
+ for (p = pos, end = pos + len; p < end; p = hdr.payload + hdr.length) {
+ int res;
+
+ if (asn1_get_next(p, end - p, &hdr) < 0) {
+ wpa_printf(MSG_DEBUG, "X509: Failed to parse "
+ "SubjectAltName item");
+ return -1;
+ }
+
+ if (hdr.class != ASN1_CLASS_CONTEXT_SPECIFIC)
+ continue;
+
+ switch (hdr.tag) {
+ case 1:
+ res = x509_parse_alt_name_rfc8222(name, hdr.payload,
+ hdr.length);
+ break;
+ case 2:
+ res = x509_parse_alt_name_dns(name, hdr.payload,
+ hdr.length);
+ break;
+ case 6:
+ res = x509_parse_alt_name_uri(name, hdr.payload,
+ hdr.length);
+ break;
+ case 7:
+ res = x509_parse_alt_name_ip(name, hdr.payload,
+ hdr.length);
+ break;
+ case 8:
+ res = x509_parse_alt_name_rid(name, hdr.payload,
+ hdr.length);
+ break;
+ case 0: /* TODO: otherName */
+ case 3: /* TODO: x500Address */
+ case 4: /* TODO: directoryName */
+ case 5: /* TODO: ediPartyName */
+ default:
+ res = 0;
+ break;
+ }
+ if (res < 0)
+ return res;
+ }
+
+ return 0;
+}
+
+
+static int x509_parse_ext_subject_alt_name(struct x509_certificate *cert,
+ const u8 *pos, size_t len)
+{
+ struct asn1_hdr hdr;
+
+ /* SubjectAltName ::= GeneralNames */
+
+ if (asn1_get_next(pos, len, &hdr) < 0 ||
+ hdr.class != ASN1_CLASS_UNIVERSAL ||
+ hdr.tag != ASN1_TAG_SEQUENCE) {
+ wpa_printf(MSG_DEBUG, "X509: Expected SEQUENCE in "
+ "SubjectAltName; found %d tag 0x%x",
+ hdr.class, hdr.tag);
+ return -1;
+ }
+
+ wpa_printf(MSG_DEBUG, "X509: SubjectAltName");
+ cert->extensions_present |= X509_EXT_SUBJECT_ALT_NAME;
+
+ if (hdr.length == 0)
+ return 0;
+
+ return x509_parse_ext_alt_name(&cert->subject, hdr.payload,
+ hdr.length);
+}
+
+
+static int x509_parse_ext_issuer_alt_name(struct x509_certificate *cert,
+ const u8 *pos, size_t len)
+{
+ struct asn1_hdr hdr;
+
+ /* IssuerAltName ::= GeneralNames */
+
+ if (asn1_get_next(pos, len, &hdr) < 0 ||
+ hdr.class != ASN1_CLASS_UNIVERSAL ||
+ hdr.tag != ASN1_TAG_SEQUENCE) {
+ wpa_printf(MSG_DEBUG, "X509: Expected SEQUENCE in "
+ "IssuerAltName; found %d tag 0x%x",
+ hdr.class, hdr.tag);
+ return -1;
+ }
+
+ wpa_printf(MSG_DEBUG, "X509: IssuerAltName");
+ cert->extensions_present |= X509_EXT_ISSUER_ALT_NAME;
+
+ if (hdr.length == 0)
+ return 0;
+
+ return x509_parse_ext_alt_name(&cert->issuer, hdr.payload,
+ hdr.length);
+}
+
+
+static int x509_parse_extension_data(struct x509_certificate *cert,
+ struct asn1_oid *oid,
+ const u8 *pos, size_t len)
+{
+ if (!x509_id_ce_oid(oid))
+ return 1;
+
+ /* TODO: add other extensions required by RFC 3280, Ch 4.2:
+ * certificate policies (section 4.2.1.5)
+ * name constraints (section 4.2.1.11)
+ * policy constraints (section 4.2.1.12)
+ * extended key usage (section 4.2.1.13)
+ * inhibit any-policy (section 4.2.1.15)
+ */
+ switch (oid->oid[3]) {
+ case 15: /* id-ce-keyUsage */
+ return x509_parse_ext_key_usage(cert, pos, len);
+ case 17: /* id-ce-subjectAltName */
+ return x509_parse_ext_subject_alt_name(cert, pos, len);
+ case 18: /* id-ce-issuerAltName */
+ return x509_parse_ext_issuer_alt_name(cert, pos, len);
+ case 19: /* id-ce-basicConstraints */
+ return x509_parse_ext_basic_constraints(cert, pos, len);
+ default:
+ return 1;
+ }
+}
+
+
+static int x509_parse_extension(struct x509_certificate *cert,
+ const u8 *pos, size_t len, const u8 **next)
+{
+ const u8 *end;
+ struct asn1_hdr hdr;
+ struct asn1_oid oid;
+ int critical_ext = 0, res;
+ char buf[80];
+
+ /*
+ * Extension ::= SEQUENCE {
+ * extnID OBJECT IDENTIFIER,
+ * critical BOOLEAN DEFAULT FALSE,
+ * extnValue OCTET STRING
+ * }
+ */
+
+ if (asn1_get_next(pos, len, &hdr) < 0 ||
+ hdr.class != ASN1_CLASS_UNIVERSAL ||
+ hdr.tag != ASN1_TAG_SEQUENCE) {
+ wpa_printf(MSG_DEBUG, "X509: Unexpected ASN.1 header in "
+ "Extensions: class %d tag 0x%x; expected SEQUENCE",
+ hdr.class, hdr.tag);
+ return -1;
+ }
+ pos = hdr.payload;
+ *next = end = pos + hdr.length;
+
+ if (asn1_get_oid(pos, end - pos, &oid, &pos) < 0) {
+ wpa_printf(MSG_DEBUG, "X509: Unexpected ASN.1 data for "
+ "Extension (expected OID)");
+ return -1;
+ }
+
+ if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
+ hdr.class != ASN1_CLASS_UNIVERSAL ||
+ (hdr.tag != ASN1_TAG_BOOLEAN &&
+ hdr.tag != ASN1_TAG_OCTETSTRING)) {
+ wpa_printf(MSG_DEBUG, "X509: Unexpected ASN.1 header in "
+ "Extensions: class %d tag 0x%x; expected BOOLEAN "
+ "or OCTET STRING", hdr.class, hdr.tag);
+ return -1;
+ }
+
+ if (hdr.tag == ASN1_TAG_BOOLEAN) {
+ if (hdr.length != 1) {
+ wpa_printf(MSG_DEBUG, "X509: Unexpected "
+ "Boolean length (%u)", hdr.length);
+ return -1;
+ }
+ critical_ext = hdr.payload[0];
+ pos = hdr.payload;
+ if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
+ (hdr.class != ASN1_CLASS_UNIVERSAL &&
+ hdr.class != ASN1_CLASS_PRIVATE) ||
+ hdr.tag != ASN1_TAG_OCTETSTRING) {
+ wpa_printf(MSG_DEBUG, "X509: Unexpected ASN.1 header "
+ "in Extensions: class %d tag 0x%x; "
+ "expected OCTET STRING",
+ hdr.class, hdr.tag);
+ return -1;
+ }
+ }
+
+ asn1_oid_to_str(&oid, buf, sizeof(buf));
+ wpa_printf(MSG_DEBUG, "X509: Extension: extnID=%s critical=%d",
+ buf, critical_ext);
+ wpa_hexdump(MSG_MSGDUMP, "X509: extnValue", hdr.payload, hdr.length);
+
+ res = x509_parse_extension_data(cert, &oid, hdr.payload, hdr.length);
+ if (res < 0)
+ return res;
+ if (res == 1 && critical_ext) {
+ wpa_printf(MSG_INFO, "X509: Unknown critical extension %s",
+ buf);
+ //return -1; //for wpa2 certification , commenout , ignore the error
+ }
+
+ return 0;
+}
+
+
+static int x509_parse_extensions(struct x509_certificate *cert,
+ const u8 *pos, size_t len)
+{
+ const u8 *end;
+ struct asn1_hdr hdr;
+
+ /* Extensions ::= SEQUENCE SIZE (1..MAX) OF Extension */
+
+ if (asn1_get_next(pos, len, &hdr) < 0 ||
+ hdr.class != ASN1_CLASS_UNIVERSAL ||
+ hdr.tag != ASN1_TAG_SEQUENCE) {
+ wpa_printf(MSG_DEBUG, "X509: Unexpected ASN.1 data "
+ "for Extensions: class %d tag 0x%x; "
+ "expected SEQUENCE", hdr.class, hdr.tag);
+ return -1;
+ }
+
+ pos = hdr.payload;
+ end = pos + hdr.length;
+
+ while (pos < end) {
+ if (x509_parse_extension(cert, pos, end - pos, &pos)
+ < 0)
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static int x509_parse_tbs_certificate(const u8 *buf, size_t len,
+ struct x509_certificate *cert,
+ const u8 **next)
+{
+ struct asn1_hdr hdr;
+ const u8 *pos, *end;
+ size_t left;
+ char sbuf[128];
+ unsigned long value;
+
+ /* tbsCertificate TBSCertificate ::= SEQUENCE */
+ if (asn1_get_next(buf, len, &hdr) < 0 ||
+ hdr.class != ASN1_CLASS_UNIVERSAL ||
+ hdr.tag != ASN1_TAG_SEQUENCE) {
+ wpa_printf(MSG_DEBUG, "X509: tbsCertificate did not start "
+ "with a valid SEQUENCE - found class %d tag 0x%x",
+ hdr.class, hdr.tag);
+ return -1;
+ }
+ pos = hdr.payload;
+ end = *next = pos + hdr.length;
+
+ /*
+ * version [0] EXPLICIT Version DEFAULT v1
+ * Version ::= INTEGER { v1(0), v2(1), v3(2) }
+ */
+ if (asn1_get_next(pos, end - pos, &hdr) < 0)
+ return -1;
+ pos = hdr.payload;
+
+ if (hdr.class == ASN1_CLASS_CONTEXT_SPECIFIC) {
+ if (asn1_get_next(pos, end - pos, &hdr) < 0)
+ return -1;
+
+ if (hdr.class != ASN1_CLASS_UNIVERSAL ||
+ hdr.tag != ASN1_TAG_INTEGER) {
+ wpa_printf(MSG_DEBUG, "X509: No INTEGER tag found for "
+ "version field - found class %d tag 0x%x",
+ hdr.class, hdr.tag);
+ return -1;
+ }
+ if (hdr.length != 1) {
+ wpa_printf(MSG_DEBUG, "X509: Unexpected version field "
+ "length %u (expected 1)", hdr.length);
+ return -1;
+ }
+ pos = hdr.payload;
+ left = hdr.length;
+ value = 0;
+ while (left) {
+ value <<= 8;
+ value |= *pos++;
+ left--;
+ }
+
+ cert->version = value;
+ if (cert->version != X509_CERT_V1 &&
+ cert->version != X509_CERT_V2 &&
+ cert->version != X509_CERT_V3) {
+ wpa_printf(MSG_DEBUG, "X509: Unsupported version %d",
+ cert->version + 1);
+ return -1;
+ }
+
+ if (asn1_get_next(pos, end - pos, &hdr) < 0)
+ return -1;
+ } else
+ cert->version = X509_CERT_V1;
+ wpa_printf(MSG_DEBUG, "X509: Version X.509v%d", cert->version + 1);
+
+ /* serialNumber CertificateSerialNumber ::= INTEGER */
+ if (hdr.class != ASN1_CLASS_UNIVERSAL ||
+ hdr.tag != ASN1_TAG_INTEGER) {
+ wpa_printf(MSG_DEBUG, "X509: No INTEGER tag found for "
+ "serialNumber; class=%d tag=0x%x",
+ hdr.class, hdr.tag);
+ return -1;
+ }
+
+ pos = hdr.payload;
+ left = hdr.length;
+ while (left) {
+ cert->serial_number <<= 8;
+ cert->serial_number |= *pos++;
+ left--;
+ }
+ wpa_printf(MSG_DEBUG, "X509: serialNumber %lu", cert->serial_number);
+
+ /* signature AlgorithmIdentifier */
+ if (x509_parse_algorithm_identifier(pos, end - pos, &cert->signature,
+ &pos))
+ return -1;
+
+ /* issuer Name */
+ if (x509_parse_name(pos, end - pos, &cert->issuer, &pos))
+ return -1;
+ x509_name_string(&cert->issuer, sbuf, sizeof(sbuf));
+ wpa_printf(MSG_DEBUG, "X509: issuer %s", sbuf);
+
+ /* validity Validity */
+ if (x509_parse_validity(pos, end - pos, cert, &pos))
+ return -1;
+
+ /* subject Name */
+ if (x509_parse_name(pos, end - pos, &cert->subject, &pos))
+ return -1;
+ x509_name_string(&cert->subject, sbuf, sizeof(sbuf));
+ wpa_printf(MSG_DEBUG, "X509: subject %s", sbuf);
+
+ /* subjectPublicKeyInfo SubjectPublicKeyInfo */
+ if (x509_parse_public_key(pos, end - pos, cert, &pos))
+ return -1;
+
+ if (pos == end)
+ return 0;
+
+ if (cert->version == X509_CERT_V1)
+ return 0;
+
+ if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
+ hdr.class != ASN1_CLASS_CONTEXT_SPECIFIC) {
+ wpa_printf(MSG_DEBUG, "X509: Expected Context-Specific"
+ " tag to parse optional tbsCertificate "
+ "field(s); parsed class %d tag 0x%x",
+ hdr.class, hdr.tag);
+ return -1;
+ }
+
+ if (hdr.tag == 1) {
+ /* issuerUniqueID [1] IMPLICIT UniqueIdentifier OPTIONAL */
+ wpa_printf(MSG_DEBUG, "X509: issuerUniqueID");
+ /* TODO: parse UniqueIdentifier ::= BIT STRING */
+
+ if (hdr.payload + hdr.length == end)
+ return 0;
+
+ if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
+ hdr.class != ASN1_CLASS_CONTEXT_SPECIFIC) {
+ wpa_printf(MSG_DEBUG, "X509: Expected Context-Specific"
+ " tag to parse optional tbsCertificate "
+ "field(s); parsed class %d tag 0x%x",
+ hdr.class, hdr.tag);
+ return -1;
+ }
+ }
+
+ if (hdr.tag == 2) {
+ /* subjectUniqueID [2] IMPLICIT UniqueIdentifier OPTIONAL */
+ wpa_printf(MSG_DEBUG, "X509: subjectUniqueID");
+ /* TODO: parse UniqueIdentifier ::= BIT STRING */
+
+ if (hdr.payload + hdr.length == end)
+ return 0;
+
+ if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
+ hdr.class != ASN1_CLASS_CONTEXT_SPECIFIC) {
+ wpa_printf(MSG_DEBUG, "X509: Expected Context-Specific"
+ " tag to parse optional tbsCertificate "
+ "field(s); parsed class %d tag 0x%x",
+ hdr.class, hdr.tag);
+ return -1;
+ }
+ }
+
+ if (hdr.tag != 3) {
+ wpa_printf(MSG_DEBUG, "X509: Ignored unexpected "
+ "Context-Specific tag %d in optional "
+ "tbsCertificate fields", hdr.tag);
+ return 0;
+ }
+
+ /* extensions [3] EXPLICIT Extensions OPTIONAL */
+
+ if (cert->version != X509_CERT_V3) {
+ wpa_printf(MSG_DEBUG, "X509: X.509%d certificate and "
+ "Extensions data which are only allowed for "
+ "version 3", cert->version + 1);
+ return -1;
+ }
+
+ if (x509_parse_extensions(cert, hdr.payload, hdr.length) < 0)
+ return -1;
+
+ pos = hdr.payload + hdr.length;
+ if (pos < end) {
+ wpa_hexdump(MSG_DEBUG,
+ "X509: Ignored extra tbsCertificate data",
+ pos, end - pos);
+ }
+
+ return 0;
+}
+
+
+static int x509_rsadsi_oid(struct asn1_oid *oid)
+{
+ return oid->len >= 4 &&
+ oid->oid[0] == 1 /* iso */ &&
+ oid->oid[1] == 2 /* member-body */ &&
+ oid->oid[2] == 840 /* us */ &&
+ oid->oid[3] == 113549 /* rsadsi */;
+}
+
+
+static int x509_pkcs_oid(struct asn1_oid *oid)
+{
+ return oid->len >= 5 &&
+ x509_rsadsi_oid(oid) &&
+ oid->oid[4] == 1 /* pkcs */;
+}
+
+
+static int x509_digest_oid(struct asn1_oid *oid)
+{
+ return oid->len >= 5 &&
+ x509_rsadsi_oid(oid) &&
+ oid->oid[4] == 2 /* digestAlgorithm */;
+}
+
+
+static int x509_sha1_oid(struct asn1_oid *oid)
+{
+ return oid->len == 6 &&
+ oid->oid[0] == 1 /* iso */ &&
+ oid->oid[1] == 3 /* identified-organization */ &&
+ oid->oid[2] == 14 /* oiw */ &&
+ oid->oid[3] == 3 /* secsig */ &&
+ oid->oid[4] == 2 /* algorithms */ &&
+ oid->oid[5] == 26 /* id-sha1 */;
+}
+
+
+static int x509_sha256_oid(struct asn1_oid *oid)
+{
+ return oid->len == 9 &&
+ oid->oid[0] == 2 /* joint-iso-itu-t */ &&
+ oid->oid[1] == 16 /* country */ &&
+ oid->oid[2] == 840 /* us */ &&
+ oid->oid[3] == 1 /* organization */ &&
+ oid->oid[4] == 101 /* gov */ &&
+ oid->oid[5] == 3 /* csor */ &&
+ oid->oid[6] == 4 /* nistAlgorithm */ &&
+ oid->oid[7] == 2 /* hashAlgs */ &&
+ oid->oid[8] == 1 /* sha256 */;
+}
+
+
+/**
+ * x509_certificate_parse - Parse a X.509 certificate in DER format
+ * @buf: Pointer to the X.509 certificate in DER format
+ * @len: Buffer length
+ * Returns: Pointer to the parsed certificate or %NULL on failure
+ *
+ * Caller is responsible for freeing the returned certificate by calling
+ * x509_certificate_free().
+ */
+struct x509_certificate * x509_certificate_parse(const u8 *buf, size_t len)
+{
+ struct asn1_hdr hdr;
+ const u8 *pos, *end, *hash_start;
+ struct x509_certificate *cert;
+
+ cert = (struct x509_certificate *)os_zalloc(sizeof(*cert) + len);
+ if (cert == NULL)
+ return NULL;
+ os_memcpy(cert + 1, buf, len);
+ cert->cert_start = (u8 *) (cert + 1);
+ cert->cert_len = len;
+
+ pos = buf;
+ end = buf + len;
+
+ /* RFC 3280 - X.509 v3 certificate / ASN.1 DER */
+
+ /* Certificate ::= SEQUENCE */
+ if (asn1_get_next(pos, len, &hdr) < 0 ||
+ hdr.class != ASN1_CLASS_UNIVERSAL ||
+ hdr.tag != ASN1_TAG_SEQUENCE) {
+ wpa_printf(MSG_DEBUG, "X509: Certificate did not start with "
+ "a valid SEQUENCE - found class %d tag 0x%x",
+ hdr.class, hdr.tag);
+ x509_certificate_free(cert);
+ return NULL;
+ }
+ pos = hdr.payload;
+
+ if (pos + hdr.length > end) {
+ x509_certificate_free(cert);
+ return NULL;
+ }
+
+ if (pos + hdr.length < end) {
+ wpa_hexdump(MSG_MSGDUMP, "X509: Ignoring extra data after DER "
+ "encoded certificate",
+ pos + hdr.length, end - pos + hdr.length);
+ end = pos + hdr.length;
+ }
+
+ hash_start = pos;
+ cert->tbs_cert_start = cert->cert_start + (hash_start - buf);
+ if (x509_parse_tbs_certificate(pos, end - pos, cert, &pos)) {
+ x509_certificate_free(cert);
+ return NULL;
+ }
+ cert->tbs_cert_len = pos - hash_start;
+
+ /* signatureAlgorithm AlgorithmIdentifier */
+ if (x509_parse_algorithm_identifier(pos, end - pos,
+ &cert->signature_alg, &pos)) {
+ x509_certificate_free(cert);
+ return NULL;
+ }
+
+ /* signatureValue BIT STRING */
+ if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
+ hdr.class != ASN1_CLASS_UNIVERSAL ||
+ hdr.tag != ASN1_TAG_BITSTRING) {
+ wpa_printf(MSG_DEBUG, "X509: Expected BITSTRING "
+ "(signatureValue) - found class %d tag 0x%x",
+ hdr.class, hdr.tag);
+ x509_certificate_free(cert);
+ return NULL;
+ }
+ if (hdr.length < 1) {
+ x509_certificate_free(cert);
+ return NULL;
+ }
+ pos = hdr.payload;
+ if (*pos) {
+ wpa_printf(MSG_DEBUG, "X509: BITSTRING - %d unused bits",
+ *pos);
+ /* PKCS #1 v1.5 10.2.1:
+ * It is an error if the length in bits of the signature S is
+ * not a multiple of eight.
+ */
+ x509_certificate_free(cert);
+ return NULL;
+ }
+ os_free(cert->sign_value);
+ cert->sign_value = os_malloc(hdr.length - 1);
+ if (cert->sign_value == NULL) {
+ wpa_printf(MSG_DEBUG, "X509: Failed to allocate memory for "
+ "signatureValue");
+ x509_certificate_free(cert);
+ return NULL;
+ }
+ os_memcpy(cert->sign_value, pos + 1, hdr.length - 1);
+ cert->sign_value_len = hdr.length - 1;
+ wpa_hexdump(MSG_MSGDUMP, "X509: signature",
+ cert->sign_value, cert->sign_value_len);
+
+ return cert;
+}
+
+
+/**
+ * x509_certificate_check_signature - Verify certificate signature
+ * @issuer: Issuer certificate
+ * @cert: Certificate to be verified
+ * Returns: 0 if cert has a valid signature that was signed by the issuer,
+ * -1 if not
+ */
+int x509_certificate_check_signature(struct x509_certificate *issuer,
+ struct x509_certificate *cert)
+{
+ struct crypto_public_key *pk;
+ u8 *data;
+ const u8 *pos, *end, *next, *da_end;
+ size_t data_len;
+ struct asn1_hdr hdr;
+ struct asn1_oid oid;
+ u8 hash[32];
+ size_t hash_len;
+
+ if (!x509_pkcs_oid(&cert->signature.oid) ||
+ cert->signature.oid.len != 7 ||
+ cert->signature.oid.oid[5] != 1 /* pkcs-1 */) {
+ wpa_printf(MSG_DEBUG, "X509: Unrecognized signature "
+ "algorithm");
+ return -1;
+ }
+
+ pk = crypto_public_key_import(issuer->public_key,
+ issuer->public_key_len);
+ if (pk == NULL)
+ return -1;
+
+ data_len = cert->sign_value_len;
+ data = os_malloc(data_len);
+ if (data == NULL) {
+ crypto_public_key_free(pk);
+ return -1;
+ }
+
+ if (crypto_public_key_decrypt_pkcs1(pk, cert->sign_value,
+ cert->sign_value_len, data,
+ &data_len) < 0) {
+ wpa_printf(MSG_DEBUG, "X509: Failed to decrypt signature");
+ crypto_public_key_free(pk);
+ os_free(data);
+ return -1;
+ }
+ crypto_public_key_free(pk);
+
+ wpa_hexdump(MSG_MSGDUMP, "X509: Signature data D", data, data_len);
+
+ /*
+ * PKCS #1 v1.5, 10.1.2:
+ *
+ * DigestInfo ::= SEQUENCE {
+ * digestAlgorithm DigestAlgorithmIdentifier,
+ * digest Digest
+ * }
+ *
+ * DigestAlgorithmIdentifier ::= AlgorithmIdentifier
+ *
+ * Digest ::= OCTET STRING
+ *
+ */
+ if (asn1_get_next(data, data_len, &hdr) < 0 ||
+ hdr.class != ASN1_CLASS_UNIVERSAL ||
+ hdr.tag != ASN1_TAG_SEQUENCE) {
+ wpa_printf(MSG_DEBUG, "X509: Expected SEQUENCE "
+ "(DigestInfo) - found class %d tag 0x%x",
+ hdr.class, hdr.tag);
+ os_free(data);
+ return -1;
+ }
+
+ pos = hdr.payload;
+ end = pos + hdr.length;
+
+ /*
+ * X.509:
+ * AlgorithmIdentifier ::= SEQUENCE {
+ * algorithm OBJECT IDENTIFIER,
+ * parameters ANY DEFINED BY algorithm OPTIONAL
+ * }
+ */
+
+ if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
+ hdr.class != ASN1_CLASS_UNIVERSAL ||
+ hdr.tag != ASN1_TAG_SEQUENCE) {
+ wpa_printf(MSG_DEBUG, "X509: Expected SEQUENCE "
+ "(AlgorithmIdentifier) - found class %d tag 0x%x",
+ hdr.class, hdr.tag);
+ os_free(data);
+ return -1;
+ }
+ da_end = hdr.payload + hdr.length;
+
+ if (asn1_get_oid(hdr.payload, hdr.length, &oid, &next)) {
+ wpa_printf(MSG_DEBUG, "X509: Failed to parse digestAlgorithm");
+ os_free(data);
+ return -1;
+ }
+
+ if (x509_sha1_oid(&oid)) {
+ if (cert->signature.oid.oid[6] !=
+ 5 /* sha-1WithRSAEncryption */) {
+ wpa_printf(MSG_DEBUG, "X509: digestAlgorithm SHA1 "
+ "does not match with certificate "
+ "signatureAlgorithm (%lu)",
+ cert->signature.oid.oid[6]);
+ os_free(data);
+ return -1;
+ }
+ goto skip_digest_oid;
+ }
+
+ if (x509_sha256_oid(&oid)) {
+ if (cert->signature.oid.oid[6] !=
+ 11 /* sha2561WithRSAEncryption */) {
+ wpa_printf(MSG_DEBUG, "X509: digestAlgorithm SHA256 "
+ "does not match with certificate "
+ "signatureAlgorithm (%lu)",
+ cert->signature.oid.oid[6]);
+ os_free(data);
+ return -1;
+ }
+ goto skip_digest_oid;
+ }
+
+ if (!x509_digest_oid(&oid)) {
+ wpa_printf(MSG_DEBUG, "X509: Unrecognized digestAlgorithm");
+ os_free(data);
+ return -1;
+ }
+ switch (oid.oid[5]) {
+ case 5: /* md5 */
+ if (cert->signature.oid.oid[6] != 4 /* md5WithRSAEncryption */)
+ {
+ wpa_printf(MSG_DEBUG, "X509: digestAlgorithm MD5 does "
+ "not match with certificate "
+ "signatureAlgorithm (%lu)",
+ cert->signature.oid.oid[6]);
+ os_free(data);
+ return -1;
+ }
+ break;
+ case 2: /* md2 */
+ case 4: /* md4 */
+ default:
+ wpa_printf(MSG_DEBUG, "X509: Unsupported digestAlgorithm "
+ "(%lu)", oid.oid[5]);
+ os_free(data);
+ return -1;
+ }
+
+skip_digest_oid:
+ /* Digest ::= OCTET STRING */
+ pos = da_end;
+ end = data + data_len;
+
+ if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
+ hdr.class != ASN1_CLASS_UNIVERSAL ||
+ hdr.tag != ASN1_TAG_OCTETSTRING) {
+ wpa_printf(MSG_DEBUG, "X509: Expected OCTETSTRING "
+ "(Digest) - found class %d tag 0x%x",
+ hdr.class, hdr.tag);
+ os_free(data);
+ return -1;
+ }
+ wpa_hexdump(MSG_MSGDUMP, "X509: Decrypted Digest",
+ hdr.payload, hdr.length);
+
+ switch (cert->signature.oid.oid[6]) {
+ case 4: /* md5WithRSAEncryption */
+ md5_vector(1, &cert->tbs_cert_start, &cert->tbs_cert_len,
+ hash);
+ hash_len = 16;
+ wpa_hexdump(MSG_MSGDUMP, "X509: Certificate hash (MD5)",
+ hash, hash_len);
+ break;
+ case 5: /* sha-1WithRSAEncryption */
+ sha1_vector(1, &cert->tbs_cert_start, &cert->tbs_cert_len,
+ hash);
+ hash_len = 20;
+ wpa_hexdump(MSG_MSGDUMP, "X509: Certificate hash (SHA1)",
+ hash, hash_len);
+ break;
+ case 11: /* sha256WithRSAEncryption */
+ if (wpa2_crypto_funcs.sha256_vector) {
+ wpa2_crypto_funcs.sha256_vector(1, &cert->tbs_cert_start, (int *)&cert->tbs_cert_len,
+ hash);
+ } else {
+ wpa_printf(MSG_ERROR, "Fail to register sha256 vector function!\r\n");
+ return -1;
+ }
+ hash_len = 32;
+ wpa_hexdump(MSG_MSGDUMP, "X509: Certificate hash (SHA256)",
+ hash, hash_len);
+ break;
+ case 2: /* md2WithRSAEncryption */
+ case 12: /* sha384WithRSAEncryption */
+ case 13: /* sha512WithRSAEncryption */
+ default:
+ wpa_printf(MSG_INFO, "X509: Unsupported certificate signature "
+ "algorithm (%lu)", cert->signature.oid.oid[6]);
+ os_free(data);
+ return -1;
+ }
+
+ if (hdr.length != hash_len ||
+ os_memcmp(hdr.payload, hash, hdr.length) != 0) {
+ wpa_printf(MSG_INFO, "X509: Certificate Digest does not match "
+ "with calculated tbsCertificate hash");
+ os_free(data);
+ return -1;
+ }
+
+ os_free(data);
+
+ wpa_printf(MSG_DEBUG, "X509: Certificate Digest matches with "
+ "calculated tbsCertificate hash");
+
+ return 0;
+}
+
+
+static int x509_valid_issuer(const struct x509_certificate *cert)
+{
+ if ((cert->extensions_present & X509_EXT_BASIC_CONSTRAINTS) &&
+ !cert->ca) {
+ wpa_printf(MSG_DEBUG, "X509: Non-CA certificate used as an "
+ "issuer");
+ return -1;
+ }
+
+ if (cert->version == X509_CERT_V3 &&
+ !(cert->extensions_present & X509_EXT_BASIC_CONSTRAINTS)) {
+ wpa_printf(MSG_DEBUG, "X509: v3 CA certificate did not "
+ "include BasicConstraints extension");
+ return -1;
+ }
+
+ if ((cert->extensions_present & X509_EXT_KEY_USAGE) &&
+ !(cert->key_usage & X509_KEY_USAGE_KEY_CERT_SIGN)) {
+ wpa_printf(MSG_DEBUG, "X509: Issuer certificate did not have "
+ "keyCertSign bit in Key Usage");
+ return -1;
+ }
+
+ return 0;
+}
+
+
+/**
+ * x509_certificate_chain_validate - Validate X.509 certificate chain
+ * @trusted: List of trusted certificates
+ * @chain: Certificate chain to be validated (first chain must be issued by
+ * signed by the second certificate in the chain and so on)
+ * @reason: Buffer for returning failure reason (X509_VALIDATE_*)
+ * Returns: 0 if chain is valid, -1 if not
+ */
+int x509_certificate_chain_validate(struct x509_certificate *trusted,
+ struct x509_certificate *chain,
+ int *reason, int disable_time_checks)
+{
+ long unsigned idx;
+ int chain_trusted = 0;
+ struct x509_certificate *cert, *trust;
+ char buf[128];
+ struct os_time now;
+
+ *reason = X509_VALIDATE_OK;
+
+ wpa_printf(MSG_DEBUG, "X509: Validate certificate chain");
+ os_get_time(&now);
+
+ for (cert = chain, idx = 0; cert; cert = cert->next, idx++) {
+ x509_name_string(&cert->subject, buf, sizeof(buf));
+ wpa_printf(MSG_DEBUG, "X509: %lu: %s", idx, buf);
+
+ if (chain_trusted)
+ continue;
+
+ if (!disable_time_checks &&
+ ((unsigned long) now.sec <
+ (unsigned long) cert->not_before ||
+ (unsigned long) now.sec >
+ (unsigned long) cert->not_after)) {
+ wpa_printf(MSG_INFO, "X509: Certificate not valid "
+ "(now=%lu not_before=%lu not_after=%lu)",
+ now.sec, cert->not_before, cert->not_after);
+ *reason = X509_VALIDATE_CERTIFICATE_EXPIRED;
+ return -1;
+ }
+
+ if (cert->next) {
+ if (x509_name_compare(&cert->issuer,
+ &cert->next->subject) != 0) {
+ wpa_printf(MSG_DEBUG, "X509: Certificate "
+ "chain issuer name mismatch");
+ x509_name_string(&cert->issuer, buf,
+ sizeof(buf));
+ wpa_printf(MSG_DEBUG, "X509: cert issuer: %s",
+ buf);
+ x509_name_string(&cert->next->subject, buf,
+ sizeof(buf));
+ wpa_printf(MSG_DEBUG, "X509: next cert "
+ "subject: %s", buf);
+ *reason = X509_VALIDATE_CERTIFICATE_UNKNOWN;
+ return -1;
+ }
+
+ if (x509_valid_issuer(cert->next) < 0) {
+ *reason = X509_VALIDATE_BAD_CERTIFICATE;
+ return -1;
+ }
+
+ if ((cert->next->extensions_present &
+ X509_EXT_PATH_LEN_CONSTRAINT) &&
+ idx > cert->next->path_len_constraint) {
+ wpa_printf(MSG_DEBUG, "X509: pathLenConstraint"
+ " not met (idx=%lu issuer "
+ "pathLenConstraint=%lu)", idx,
+ cert->next->path_len_constraint);
+ *reason = X509_VALIDATE_BAD_CERTIFICATE;
+ return -1;
+ }
+
+ if (x509_certificate_check_signature(cert->next, cert)
+ < 0) {
+ wpa_printf(MSG_DEBUG, "X509: Invalid "
+ "certificate signature within "
+ "chain");
+ *reason = X509_VALIDATE_BAD_CERTIFICATE;
+ return -1;
+ }
+ }
+
+ for (trust = trusted; trust; trust = trust->next) {
+ if (x509_name_compare(&cert->issuer, &trust->subject)
+ == 0)
+ break;
+ }
+
+ if (trust) {
+ wpa_printf(MSG_DEBUG, "X509: Found issuer from the "
+ "list of trusted certificates");
+ if (x509_valid_issuer(trust) < 0) {
+ *reason = X509_VALIDATE_BAD_CERTIFICATE;
+ return -1;
+ }
+
+ if (x509_certificate_check_signature(trust, cert) < 0)
+ {
+ wpa_printf(MSG_DEBUG, "X509: Invalid "
+ "certificate signature");
+ *reason = X509_VALIDATE_BAD_CERTIFICATE;
+ return -1;
+ }
+
+ wpa_printf(MSG_DEBUG, "X509: Trusted certificate "
+ "found to complete the chain");
+ chain_trusted = 1;
+ }
+ }
+
+ if (!chain_trusted) {
+ wpa_printf(MSG_DEBUG, "X509: Did not find any of the issuers "
+ "from the list of trusted certificates");
+ if (trusted) {
+ *reason = X509_VALIDATE_UNKNOWN_CA;
+ return -1;
+ }
+ wpa_printf(MSG_DEBUG, "X509: Certificate chain validation "
+ "disabled - ignore unknown CA issue");
+ }
+
+ wpa_printf(MSG_DEBUG, "X509: Certificate chain valid");
+
+ return 0;
+}
+
+
+/**
+ * x509_certificate_get_subject - Get a certificate based on Subject name
+ * @chain: Certificate chain to search through
+ * @name: Subject name to search for
+ * Returns: Pointer to the certificate with the given Subject name or
+ * %NULL on failure
+ */
+struct x509_certificate *
+x509_certificate_get_subject(struct x509_certificate *chain,
+ struct x509_name *name)
+{
+ struct x509_certificate *cert;
+
+ for (cert = chain; cert; cert = cert->next) {
+ if (x509_name_compare(&cert->subject, name) == 0)
+ return cert;
+ }
+ return NULL;
+}
+
+
+/**
+ * x509_certificate_self_signed - Is the certificate self-signed?
+ * @cert: Certificate
+ * Returns: 1 if certificate is self-signed, 0 if not
+ */
+int x509_certificate_self_signed(struct x509_certificate *cert)
+{
+ return x509_name_compare(&cert->issuer, &cert->subject) == 0;
+}
--- /dev/null
+/*
+ * Base64 encoding/decoding (RFC1341)
+ * Copyright (c) 2005-2011, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "wpa/includes.h"
+
+#include "os.h"
+#include "wpa2/utils/base64.h"
+
+static const unsigned char base64_table[65] =
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+
+/**
+ * base64_encode - Base64 encode
+ * @src: Data to be encoded
+ * @len: Length of the data to be encoded
+ * @out_len: Pointer to output length variable, or %NULL if not used
+ * Returns: Allocated buffer of out_len bytes of encoded data,
+ * or %NULL on failure
+ *
+ * Caller is responsible for freeing the returned buffer. Returned buffer is
+ * nul terminated to make it easier to use as a C string. The nul terminator is
+ * not included in out_len.
+ */
+unsigned char * base64_encode(const unsigned char *src, size_t len,
+ size_t *out_len)
+{
+ unsigned char *out, *pos;
+ const unsigned char *end, *in;
+ size_t olen;
+ int line_len;
+
+ olen = len * 4 / 3 + 4; /* 3-byte blocks to 4-byte */
+ olen += olen / 72; /* line feeds */
+ olen++; /* nul termination */
+ if (olen < len)
+ return NULL; /* integer overflow */
+ out = os_malloc(olen);
+ if (out == NULL)
+ return NULL;
+
+ end = src + len;
+ in = src;
+ pos = out;
+ line_len = 0;
+ while (end - in >= 3) {
+ *pos++ = base64_table[in[0] >> 2];
+ *pos++ = base64_table[((in[0] & 0x03) << 4) | (in[1] >> 4)];
+ *pos++ = base64_table[((in[1] & 0x0f) << 2) | (in[2] >> 6)];
+ *pos++ = base64_table[in[2] & 0x3f];
+ in += 3;
+ line_len += 4;
+ if (line_len >= 72) {
+ *pos++ = '\n';
+ line_len = 0;
+ }
+ }
+
+ if (end - in) {
+ *pos++ = base64_table[in[0] >> 2];
+ if (end - in == 1) {
+ *pos++ = base64_table[(in[0] & 0x03) << 4];
+ *pos++ = '=';
+ } else {
+ *pos++ = base64_table[((in[0] & 0x03) << 4) |
+ (in[1] >> 4)];
+ *pos++ = base64_table[(in[1] & 0x0f) << 2];
+ }
+ *pos++ = '=';
+ line_len += 4;
+ }
+
+ if (line_len)
+ *pos++ = '\n';
+
+ *pos = '\0';
+ if (out_len)
+ *out_len = pos - out;
+ return out;
+}
+
+
+/**
+ * base64_decode - Base64 decode
+ * @src: Data to be decoded
+ * @len: Length of the data to be decoded
+ * @out_len: Pointer to output length variable
+ * Returns: Allocated buffer of out_len bytes of decoded data,
+ * or %NULL on failure
+ *
+ * Caller is responsible for freeing the returned buffer.
+ */
+unsigned char * base64_decode(const unsigned char *src, size_t len,
+ size_t *out_len)
+{
+ unsigned char dtable[256], *out, *pos, block[4], tmp;
+ size_t i, count, olen;
+ int pad = 0;
+
+ os_memset(dtable, 0x80, 256);
+ for (i = 0; i < sizeof(base64_table) - 1; i++)
+ dtable[base64_table[i]] = (unsigned char) i;
+ dtable['='] = 0;
+
+ count = 0;
+ for (i = 0; i < len; i++) {
+ if (dtable[src[i]] != 0x80)
+ count++;
+ }
+
+ if (count == 0 || count % 4)
+ return NULL;
+
+ olen = count / 4 * 3;
+ pos = out = os_malloc(olen);
+ if (out == NULL)
+ return NULL;
+
+ count = 0;
+ for (i = 0; i < len; i++) {
+ tmp = dtable[src[i]];
+ if (tmp == 0x80)
+ continue;
+
+ if (src[i] == '=')
+ pad++;
+ block[count] = tmp;
+ count++;
+ if (count == 4) {
+ *pos++ = (block[0] << 2) | (block[1] >> 4);
+ *pos++ = (block[1] << 4) | (block[2] >> 2);
+ *pos++ = (block[2] << 6) | block[3];
+ count = 0;
+ if (pad) {
+ if (pad == 1)
+ pos--;
+ else if (pad == 2)
+ pos -= 2;
+ else {
+ /* Invalid padding */
+ os_free(out);
+ return NULL;
+ }
+ break;
+ }
+ }
+ }
+
+ *out_len = pos - out;
+ return out;
+}
--- /dev/null
+/*
+ * External password backend
+ * Copyright (c) 2012, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "wpa/includes.h"
+
+#include "wpa/common.h"
+#include "wpa2/utils/ext_password_i.h"
+
+#ifdef CONFIG_EXT_PASSWORD_TEST
+extern struct ext_password_backend ext_password_test;
+#endif /* CONFIG_EXT_PASSWORD_TEST */
+
+#ifdef CONFIG_EXT_PASSWORD
+static const struct ext_password_backend *backends[] = {
+#ifdef CONFIG_EXT_PASSWORD_TEST
+ &ext_password_test,
+#endif /* CONFIG_EXT_PASSWORD_TEST */
+ NULL
+};
+#endif
+
+struct ext_password_data {
+ const struct ext_password_backend *backend;
+ void *priv;
+};
+
+#ifdef CONFIG_EXT_PASSWORD
+struct ext_password_data * ext_password_init(const char *backend,
+ const char *params)
+{
+ struct ext_password_data *data;
+ int i;
+
+ data = (struct ext_password_data *)os_zalloc(sizeof(*data));
+ if (data == NULL)
+ return NULL;
+
+ for (i = 0; backends[i]; i++) {
+ if (os_strcmp(backends[i]->name, backend) == 0) {
+ data->backend = backends[i];
+ break;
+ }
+ }
+
+ if (!data->backend) {
+ os_free(data);
+ return NULL;
+ }
+
+ data->priv = data->backend->init(params);
+ if (data->priv == NULL) {
+ os_free(data);
+ return NULL;
+ }
+
+ return data;
+}
+
+
+void ext_password_deinit(struct ext_password_data *data)
+{
+ if (data && data->backend && data->priv)
+ data->backend->deinit(data->priv);
+ os_free(data);
+}
+
+
+struct wpabuf * ext_password_get(struct ext_password_data *data,
+ const char *name)
+{
+ if (data == NULL)
+ return NULL;
+ return data->backend->get(data->priv, name);
+}
+#endif /* CONFIG_EXT_PASSWORD */
+
+struct wpabuf * ext_password_alloc(size_t len)
+{
+ struct wpabuf *buf;
+
+ buf = wpabuf_alloc(len);
+ if (buf == NULL)
+ return NULL;
+
+#ifdef __linux__
+ if (mlock(wpabuf_head(buf), wpabuf_len(buf)) < 0) {
+ wpa_printf(MSG_ERROR, "EXT PW: mlock failed: %s",
+ strerror(errno));
+ }
+#endif /* __linux__ */
+
+ return buf;
+}
+
+#ifdef CONFIG_EXT_PASSWORD
+void ext_password_free(struct wpabuf *pw)
+{
+ if (pw == NULL)
+ return;
+ os_memset(wpabuf_mhead(pw), 0, wpabuf_len(pw));
+#ifdef __linux__
+ if (munlock(wpabuf_head(pw), wpabuf_len(pw)) < 0) {
+ wpa_printf(MSG_ERROR, "EXT PW: munlock failed: %s",
+ strerror(errno));
+ }
+#endif /* __linux__ */
+ wpabuf_free(pw);
+}
+#endif /* CONFIG_EXT_PASSWORD */
--- /dev/null
+/*
+ * EAP common peer/server definitions
+ * Copyright (c) 2004-2012, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "wpa/includes.h"
+
+#include "wpa/common.h"
+#include "wpa2/eap_peer/eap_defs.h"
+#include "wpa2/eap_peer/eap_common.h"
+
+/**
+ * eap_hdr_len_valid - Validate EAP header length field
+ * @msg: EAP frame (starting with EAP header)
+ * @min_payload: Minimum payload length needed
+ * Returns: 1 for valid header, 0 for invalid
+ *
+ * This is a helper function that does minimal validation of EAP messages. The
+ * length field is verified to be large enough to include the header and not
+ * too large to go beyond the end of the buffer.
+ */
+int eap_hdr_len_valid(const struct wpabuf *msg, size_t min_payload)
+{
+ const struct eap_hdr *hdr;
+ size_t len;
+
+ if (msg == NULL)
+ return 0;
+
+ hdr = wpabuf_head(msg);
+
+ if (wpabuf_len(msg) < sizeof(*hdr)) {
+ wpa_printf(MSG_INFO, "EAP: Too short EAP frame");
+ return 0;
+ }
+
+ len = be_to_host16(hdr->length);
+ if (len < sizeof(*hdr) + min_payload || len > wpabuf_len(msg)) {
+ wpa_printf(MSG_INFO, "EAP: Invalid EAP length");
+ return 0;
+ }
+
+ return 1;
+}
+
+
+/**
+ * eap_hdr_validate - Validate EAP header
+ * @vendor: Expected EAP Vendor-Id (0 = IETF)
+ * @eap_type: Expected EAP type number
+ * @msg: EAP frame (starting with EAP header)
+ * @plen: Pointer to variable to contain the returned payload length
+ * Returns: Pointer to EAP payload (after type field), or %NULL on failure
+ *
+ * This is a helper function for EAP method implementations. This is usually
+ * called in the beginning of struct eap_method::process() function to verify
+ * that the received EAP request packet has a valid header. This function is
+ * able to process both legacy and expanded EAP headers and in most cases, the
+ * caller can just use the returned payload pointer (into *plen) for processing
+ * the payload regardless of whether the packet used the expanded EAP header or
+ * not.
+ */
+const u8 * eap_hdr_validate(int vendor, EapType eap_type,
+ const struct wpabuf *msg, size_t *plen)
+{
+ const struct eap_hdr *hdr;
+ const u8 *pos;
+ size_t len;
+
+ if (!eap_hdr_len_valid(msg, 1))
+ return NULL;
+
+ hdr = wpabuf_head(msg);
+ len = be_to_host16(hdr->length);
+ pos = (const u8 *) (hdr + 1);
+
+ if (*pos == EAP_TYPE_EXPANDED) {
+ int exp_vendor;
+ u32 exp_type;
+ if (len < sizeof(*hdr) + 8) {
+ wpa_printf(MSG_INFO, "EAP: Invalid expanded EAP "
+ "length");
+ return NULL;
+ }
+ pos++;
+ exp_vendor = WPA_GET_BE24(pos);
+ pos += 3;
+ exp_type = WPA_GET_BE32(pos);
+ pos += 4;
+ if (exp_vendor != vendor || exp_type != (u32) eap_type) {
+ wpa_printf(MSG_INFO, "EAP: Invalid expanded frame "
+ "type");
+ return NULL;
+ }
+
+ *plen = len - sizeof(*hdr) - 8;
+ return pos;
+ } else {
+ if (vendor != EAP_VENDOR_IETF || *pos != eap_type) {
+ wpa_printf(MSG_INFO, "EAP: Invalid frame type");
+ return NULL;
+ }
+ *plen = len - sizeof(*hdr) - 1;
+ return pos + 1;
+ }
+}
+
+
+/**
+ * eap_msg_alloc - Allocate a buffer for an EAP message
+ * @vendor: Vendor-Id (0 = IETF)
+ * @type: EAP type
+ * @payload_len: Payload length in bytes (data after Type)
+ * @code: Message Code (EAP_CODE_*)
+ * @identifier: Identifier
+ * Returns: Pointer to the allocated message buffer or %NULL on error
+ *
+ * This function can be used to allocate a buffer for an EAP message and fill
+ * in the EAP header. This function is automatically using expanded EAP header
+ * if the selected Vendor-Id is not IETF. In other words, most EAP methods do
+ * not need to separately select which header type to use when using this
+ * function to allocate the message buffers. The returned buffer has room for
+ * payload_len bytes and has the EAP header and Type field already filled in.
+ */
+struct wpabuf * eap_msg_alloc(int vendor, EapType type, size_t payload_len,
+ u8 code, u8 identifier)
+{
+ struct wpabuf *buf;
+ struct eap_hdr *hdr;
+ size_t len;
+
+ len = sizeof(struct eap_hdr) + (vendor == EAP_VENDOR_IETF ? 1 : 8) +
+ payload_len;
+ buf = wpabuf_alloc(len);
+ if (buf == NULL)
+ return NULL;
+
+ hdr = wpabuf_put(buf, sizeof(*hdr));
+ hdr->code = code;
+ hdr->identifier = identifier;
+ hdr->length = host_to_be16(len);
+
+ if (vendor == EAP_VENDOR_IETF) {
+ wpabuf_put_u8(buf, type);
+ } else {
+ wpabuf_put_u8(buf, EAP_TYPE_EXPANDED);
+ wpabuf_put_be24(buf, vendor);
+ wpabuf_put_be32(buf, type);
+ }
+
+ return buf;
+}
+
+
+/**
+ * eap_update_len - Update EAP header length
+ * @msg: EAP message from eap_msg_alloc
+ *
+ * This function updates the length field in the EAP header to match with the
+ * current length for the buffer. This allows eap_msg_alloc() to be used to
+ * allocate a larger buffer than the exact message length (e.g., if exact
+ * message length is not yet known).
+ */
+void eap_update_len(struct wpabuf *msg)
+{
+ struct eap_hdr *hdr;
+ hdr = wpabuf_mhead(msg);
+ if (wpabuf_len(msg) < sizeof(*hdr))
+ return;
+ hdr->length = host_to_be16(wpabuf_len(msg));
+}
+
+
+/**
+ * eap_get_id - Get EAP Identifier from wpabuf
+ * @msg: Buffer starting with an EAP header
+ * Returns: The Identifier field from the EAP header
+ */
+u8 eap_get_id(const struct wpabuf *msg)
+{
+ const struct eap_hdr *eap;
+
+ if (wpabuf_len(msg) < sizeof(*eap))
+ return 0;
+
+ eap = wpabuf_head(msg);
+ return eap->identifier;
+}
+
+
+/**
+ * eap_get_id - Get EAP Type from wpabuf
+ * @msg: Buffer starting with an EAP header
+ * Returns: The EAP Type after the EAP header
+ */
+EapType eap_get_type(const struct wpabuf *msg)
+{
+ if (wpabuf_len(msg) < sizeof(struct eap_hdr) + 1)
+ return EAP_TYPE_NONE;
+
+ return ((const u8 *) wpabuf_head(msg))[sizeof(struct eap_hdr)];
+}
\ No newline at end of file
--- /dev/null
+/*
+ * Universally Unique IDentifier (UUID)
+ * Copyright (c) 2008, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "wpa/includes.h"
+
+#include "wpa/common.h"
+#include "wps/utils/uuid.h"
+
+int uuid_str2bin(const char *str, u8 *bin)
+{
+ const char *pos;
+ u8 *opos;
+
+ pos = str;
+ opos = bin;
+
+ if (hexstr2bin(pos, opos, 4))
+ return -1;
+ pos += 8;
+ opos += 4;
+
+ if (*pos++ != '-' || hexstr2bin(pos, opos, 2))
+ return -1;
+ pos += 4;
+ opos += 2;
+
+ if (*pos++ != '-' || hexstr2bin(pos, opos, 2))
+ return -1;
+ pos += 4;
+ opos += 2;
+
+ if (*pos++ != '-' || hexstr2bin(pos, opos, 2))
+ return -1;
+ pos += 4;
+ opos += 2;
+
+ if (*pos++ != '-' || hexstr2bin(pos, opos, 6))
+ return -1;
+
+ return 0;
+}
+
+
+int uuid_bin2str(const u8 *bin, char *str, size_t max_len)
+{
+ int len;
+ len = snprintf(str, max_len, "%02x%02x%02x%02x-%02x%02x-%02x%02x-"
+ "%02x%02x-%02x%02x%02x%02x%02x%02x",
+ bin[0], bin[1], bin[2], bin[3],
+ bin[4], bin[5], bin[6], bin[7],
+ bin[8], bin[9], bin[10], bin[11],
+ bin[12], bin[13], bin[14], bin[15]);
+ if (len < 0 || (size_t) len >= max_len)
+ return -1;
+ return 0;
+}
+
+
+int is_nil_uuid(const u8 *uuid)
+{
+ int i;
+ for (i = 0; i < UUID_LEN; i++)
+ if (uuid[i])
+ return 0;
+ return 1;
+}
\ No newline at end of file
--- /dev/null
+/*
+ * Wi-Fi Protected Setup
+ * Copyright (c) 2007-2009, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+#include <string.h>
+
+#include "wpa/includes.h"
+#include "wpa/wpa.h"
+#include "wpa/common.h"
+#include "wpa/eapol_common.h"
+#include "wpa/wpa_debug.h"
+#include "wpa/ieee802_11_defs.h"
+
+#include "crypto/dh_group5.h"
+
+#include "wps/wps_i.h"
+#include "wps/wps_dev_attr.h"
+
+#include "wpa2/eap_peer/eap_defs.h"
+#include "wpa2/eap_peer/eap_common.h"
+
+
+/**
+ * wps_process_msg - Process a WPS message
+ * @wps: WPS Registration protocol data from wps_init()
+ * @op_code: Message OP Code
+ * @msg: Message data
+ * Returns: Processing result
+ *
+ * This function is used to process WPS messages with OP Codes WSC_ACK,
+ * WSC_NACK, WSC_MSG, and WSC_Done. The caller (e.g., EAP server/peer) is
+ * responsible for reassembling the messages before calling this function.
+ * Response to this message is built by calling wps_get_msg().
+ */
+enum wps_process_res wps_process_msg(struct wps_data *wps,
+ enum wsc_op_code op_code,
+ const struct wpabuf *msg)
+{
+ if (wps->registrar)
+ return wps_registrar_process_msg(wps, op_code, msg);
+ else
+ return wps_enrollee_process_msg(wps, op_code, msg);
+}
+
+
+/**
+ * wps_get_msg - Build a WPS message
+ * @wps: WPS Registration protocol data from wps_init()
+ * @op_code: Buffer for returning message OP Code
+ * Returns: The generated WPS message or %NULL on failure
+ *
+ * This function is used to build a response to a message processed by calling
+ * wps_process_msg(). The caller is responsible for freeing the buffer.
+ */
+struct wpabuf * wps_get_msg(struct wps_data *wps, enum wsc_op_code *op_code)
+{
+ if (wps->registrar)
+ return wps_registrar_get_msg(wps, op_code);
+ else
+ return wps_enrollee_get_msg(wps, op_code);
+}
+
+
+/**
+ * wps_is_selected_pbc_registrar - Check whether WPS IE indicates active PBC
+ * @msg: WPS IE contents from Beacon or Probe Response frame
+ * Returns: 1 if PBC Registrar is active, 0 if not
+ */
+int wps_is_selected_pbc_registrar(const struct wpabuf *msg, u8 *bssid)
+{
+ struct wps_sm *sm = wps_sm_get();
+ struct wps_parse_attr *attr = (struct wps_parse_attr *)os_zalloc(sizeof(struct wps_parse_attr));
+ int i = 0;
+
+ /*
+ * In theory, this could also verify that attr.sel_reg_config_methods
+ * includes WPS_CONFIG_PUSHBUTTON, but some deployed AP implementations
+ * do not set Selected Registrar Config Methods attribute properly, so
+ * it is safer to just use Device Password ID here.
+ */
+
+ if (wps_parse_msg(msg, attr) < 0) {
+ os_free(attr);
+ return 0;
+ }
+
+ if(!attr->selected_registrar || *attr->selected_registrar == 0) {
+ if (sm->ignore_sel_reg == false) {
+ os_free(attr);
+ return 0;
+ }
+ else {
+ for (i = 0; i < WPS_MAX_DIS_AP_NUM; i++) {
+ if (0 == os_memcmp(sm->dis_ap_list[i].bssid, bssid, 6)) {
+ wpa_printf(MSG_DEBUG, "discard ap bssid[%02x:%02x:%02x:%02x:%02x:%02x]\n", \
+ bssid[0], bssid[1], bssid[2], bssid[3], bssid[4], bssid[5]);
+ os_free(attr);
+ return 0;
+ }
+ }
+ }
+ }
+
+ if (!attr->dev_password_id ||
+ WPA_GET_BE16(attr->dev_password_id) != DEV_PW_PUSHBUTTON) {
+ os_free(attr);
+ return 0;
+ }
+#if 0
+#ifdef CONFIG_WPS_STRICT
+ if (!attr->sel_reg_config_methods ||
+ !(WPA_GET_BE16(attr->sel_reg_config_methods) &
+ WPS_CONFIG_PUSHBUTTON)) {
+ os_free(attr);
+ return 0;
+ }
+#endif /* CONFIG_WPS_STRICT */
+#endif
+ os_free(attr);
+ return 1;
+}
+
+#ifdef CONFIG_WPS_PIN
+
+static int is_selected_pin_registrar(struct wps_parse_attr *attr, u8 *bssid)
+{
+ struct wps_sm *sm = wps_sm_get();
+ int i = 0;
+
+ if (!sm || !bssid){
+ return 0;
+ }
+ /*
+ * In theory, this could also verify that attr.sel_reg_config_methods
+ * includes WPS_CONFIG_LABEL, WPS_CONFIG_DISPLAY, or WPS_CONFIG_KEYPAD,
+ * but some deployed AP implementations do not set Selected Registrar
+ * Config Methods attribute properly, so it is safer to just use
+ * Device Password ID here.
+ */
+
+ if (!attr->selected_registrar || *attr->selected_registrar == 0) {
+ if (sm->ignore_sel_reg == false) {
+ return 0;
+ }
+ else {
+ for (i = 0; i < WPS_MAX_DIS_AP_NUM; i++) {
+ if (0 == os_memcmp(sm->dis_ap_list[i].bssid, bssid, 6)) {
+ wpa_printf(MSG_DEBUG, "discard ap bssid[%02x:%02x:%02x:%02x:%02x:%02x]\n", \
+ bssid[0], bssid[1], bssid[2], bssid[3], bssid[4], bssid[5]);
+ return 0;
+ }
+ }
+ }
+ }
+ if (attr->dev_password_id != NULL &&
+ WPA_GET_BE16(attr->dev_password_id) == DEV_PW_PUSHBUTTON) {
+ return 0;
+ }
+#ifdef CONFIG_WPS_STRICT
+ if (!attr->sel_reg_config_methods)// ||
+ //!(WPA_GET_BE16(attr->sel_reg_config_methods) &
+ //(WPS_CONFIG_LABEL | WPS_CONFIG_DISPLAY | WPS_CONFIG_KEYPAD)))
+ return 0;
+#endif /* CONFIG_WPS_STRICT */
+ return 1;
+}
+
+
+/**
+ * wps_is_selected_pin_registrar - Check whether WPS IE indicates active PIN
+ * @msg: WPS IE contents from Beacon or Probe Response frame
+ * Returns: 1 if PIN Registrar is active, 0 if not
+ */
+int wps_is_selected_pin_registrar(const struct wpabuf *msg, u8 *bssid)
+{
+ struct wps_parse_attr *attr;
+ int ret;
+
+ attr = (struct wps_parse_attr *)os_zalloc(sizeof(struct wps_parse_attr));
+ if (attr == NULL)
+ return -99;
+
+ if (wps_parse_msg(msg, attr) < 0) {
+ os_free(attr);
+ return 0;
+ }
+
+ ret = is_selected_pin_registrar(attr, bssid);
+ os_free(attr);
+
+ return ret;
+}
+#endif
+
+/**
+ * wps_is_addr_authorized - Check whether WPS IE authorizes MAC address
+ * @msg: WPS IE contents from Beacon or Probe Response frame
+ * @addr: MAC address to search for
+ * @ver1_compat: Whether to use version 1 compatibility mode
+ * Returns: 2 if the specified address is explicit authorized, 1 if address is
+ * authorized (broadcast), 0 if not
+ */
+int wps_is_addr_authorized(const struct wpabuf *msg, const u8 *addr,
+ int ver1_compat)
+{
+ struct wps_sm *sm = wps_sm_get();
+ struct wps_parse_attr *attr;
+ int ret = 0;
+ unsigned int i;
+ const u8 *pos;
+ const u8 bcast[ETH_ALEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
+
+ if (!sm){
+ return -10;
+ }
+
+ attr = (struct wps_parse_attr *)os_zalloc(sizeof(struct wps_parse_attr));
+ if (attr == NULL) {
+ ret = -99;
+ goto _out;
+ }
+
+ if (wps_parse_msg(msg, attr) < 0) {
+ ret = 0;
+ goto _out;
+ }
+
+ if (!attr->version2 && ver1_compat) {
+ /*
+ * Version 1.0 AP - AuthorizedMACs not used, so revert back to
+ * old mechanism of using SelectedRegistrar.
+ */
+#ifdef CONFIG_WPS_PIN
+
+ ret = is_selected_pin_registrar(attr, sm->config.bssid);
+ goto _out;
+#endif
+ }
+
+ if (!attr->authorized_macs) {
+ ret = 0;
+ goto _out;
+ }
+
+ pos = attr->authorized_macs;
+ for (i = 0; i < attr->authorized_macs_len / ETH_ALEN; i++) {
+ if (os_memcmp(pos, addr, ETH_ALEN) == 0) {
+ ret = 2;
+ goto _out;
+ }
+ if (os_memcmp(pos, bcast, ETH_ALEN) == 0) {
+ ret = 1;
+ goto _out;
+ }
+ pos += ETH_ALEN;
+ }
+_out:
+ if (attr)
+ os_free(attr);
+
+ return ret;
+}
+
+
+/**
+ * wps_ap_priority_compar - Prioritize WPS IE from two APs
+ * @wps_a: WPS IE contents from Beacon or Probe Response frame
+ * @wps_b: WPS IE contents from Beacon or Probe Response frame
+ * Returns: 1 if wps_b is considered more likely selection for WPS
+ * provisioning, -1 if wps_a is considered more like, or 0 if no preference
+ */
+int wps_ap_priority_compar(const struct wpabuf *wps_a,
+ const struct wpabuf *wps_b)
+{
+ struct wps_parse_attr *attr_a, *attr_b;
+ int sel_a, sel_b;
+ int ret = 0;
+
+ attr_a = (struct wps_parse_attr *)os_zalloc(sizeof(struct wps_parse_attr));
+ attr_b = (struct wps_parse_attr *)os_zalloc(sizeof(struct wps_parse_attr));
+
+ if (attr_a == NULL || attr_b == NULL) {
+ ret = 0;
+ goto _out;
+ }
+
+ if (wps_a == NULL || wps_parse_msg(wps_a, attr_a) < 0)
+ return 1;
+ if (wps_b == NULL || wps_parse_msg(wps_b, attr_b) < 0)
+ return -1;
+
+ sel_a = attr_a->selected_registrar && *attr_a->selected_registrar != 0;
+ sel_b = attr_b->selected_registrar && *attr_b->selected_registrar != 0;
+
+ if (sel_a && !sel_b) {
+ ret = -1;
+ goto _out;
+ }
+ if (!sel_a && sel_b) {
+ ret = 1;
+ goto _out;
+ }
+
+_out:
+ if (attr_a)
+ os_free(attr_a);
+ if (attr_b)
+ os_free(attr_b);
+ return ret;
+}
+
+
+/**
+ * wps_get_uuid_e - Get UUID-E from WPS IE
+ * @msg: WPS IE contents from Beacon or Probe Response frame
+ * Returns: Pointer to UUID-E or %NULL if not included
+ *
+ * The returned pointer is to the msg contents and it remains valid only as
+ * long as the msg buffer is valid.
+ */
+const u8 * wps_get_uuid_e(const struct wpabuf *msg)
+{
+ struct wps_parse_attr *attr;
+ const u8 *uuid_e;
+
+ attr = (struct wps_parse_attr *)os_zalloc(sizeof(struct wps_parse_attr));
+ if (attr == NULL)
+ return NULL;
+
+ if (wps_parse_msg(msg, attr) < 0) {
+ uuid_e = NULL;
+ } else {
+ uuid_e = attr->uuid_e;
+ }
+ os_free(attr);
+ return uuid_e;
+}
+
+
+/**
+ * wps_is_20 - Check whether WPS attributes claim support for WPS 2.0
+ */
+int wps_is_20(const struct wpabuf *msg)
+{
+ struct wps_parse_attr *attr;
+ int ret;
+
+ attr = (struct wps_parse_attr *)os_zalloc(sizeof(struct wps_parse_attr));
+ if (attr == NULL)
+ return 0;
+
+ if (msg == NULL || wps_parse_msg(msg, attr) < 0) {
+ ret = 0;
+ } else {
+ ret = (attr->version2 != NULL);
+ }
+ os_free(attr);
+ return ret;
+}
+
+
+/**
+ * wps_build_assoc_req_ie - Build WPS IE for (Re)Association Request
+ * @req_type: Value for Request Type attribute
+ * Returns: WPS IE or %NULL on failure
+ *
+ * The caller is responsible for freeing the buffer.
+ */
+struct wpabuf * wps_build_assoc_req_ie(enum wps_request_type req_type)
+{
+ struct wpabuf *ie;
+ u8 *len;
+
+ wpa_printf(MSG_DEBUG, "WPS: Building WPS IE for (Re)Association "
+ "Request");
+ ie = wpabuf_alloc(100);
+ if (ie == NULL)
+ return NULL;
+
+ wpabuf_put_u8(ie, WLAN_EID_VENDOR_SPECIFIC);
+ len = wpabuf_put(ie, 1);
+ wpabuf_put_be32(ie, WPS_DEV_OUI_WFA);
+
+ if (wps_build_version(ie) ||
+ wps_build_req_type(ie, req_type) ||
+ wps_build_wfa_ext(ie, 0, NULL, 0)) {
+ wpabuf_free(ie);
+ return NULL;
+ }
+
+ *len = wpabuf_len(ie) - 2;
+
+ return ie;
+}
+
+
+/**
+ * wps_build_assoc_resp_ie - Build WPS IE for (Re)Association Response
+ * Returns: WPS IE or %NULL on failure
+ *
+ * The caller is responsible for freeing the buffer.
+ */
+struct wpabuf * wps_build_assoc_resp_ie(void)
+{
+ struct wpabuf *ie;
+ u8 *len;
+
+ wpa_printf(MSG_DEBUG, "WPS: Building WPS IE for (Re)Association "
+ "Response");
+ ie = wpabuf_alloc(100);
+ if (ie == NULL)
+ return NULL;
+
+ wpabuf_put_u8(ie, WLAN_EID_VENDOR_SPECIFIC);
+ len = wpabuf_put(ie, 1);
+ wpabuf_put_be32(ie, WPS_DEV_OUI_WFA);
+
+ if (wps_build_version(ie) ||
+ wps_build_resp_type(ie, WPS_RESP_AP) ||
+ wps_build_wfa_ext(ie, 0, NULL, 0)) {
+ wpabuf_free(ie);
+ return NULL;
+ }
+
+ *len = wpabuf_len(ie) - 2;
+
+ return ie;
+}
+
+
+/**
+ * wps_build_probe_req_ie - Build WPS IE for Probe Request
+ * @pw_id: Password ID (DEV_PW_PUSHBUTTON for active PBC and DEV_PW_DEFAULT for
+ * most other use cases)
+ * @dev: Device attributes
+ * @uuid: Own UUID
+ * @req_type: Value for Request Type attribute
+ * @num_req_dev_types: Number of requested device types
+ * @req_dev_types: Requested device types (8 * num_req_dev_types octets) or
+ * %NULL if none
+ * Returns: WPS IE or %NULL on failure
+ *
+ * The caller is responsible for freeing the buffer.
+ */
+struct wpabuf * wps_build_probe_req_ie(u16 pw_id, struct wps_device_data *dev,
+ const u8 *uuid,
+ enum wps_request_type req_type,
+ unsigned int num_req_dev_types,
+ const u8 *req_dev_types)
+{
+ struct wpabuf *ie;
+
+ wpa_printf(MSG_DEBUG, "WPS: Building WPS IE for Probe Request\n");
+
+ ie = wpabuf_alloc(400);
+ if (ie == NULL) {
+ wpa_printf(MSG_ERROR, "WPS: ie alloc failed.");
+ return NULL;
+ }
+
+ if (wps_build_version(ie) ||
+ wps_build_req_type(ie, req_type) ||
+ wps_build_config_methods(ie, dev->config_methods) ||
+ wps_build_uuid_e(ie, uuid) ||
+ wps_build_primary_dev_type(dev, ie) ||
+ wps_build_rf_bands(dev, ie) ||
+ wps_build_assoc_state(NULL, ie) ||
+ wps_build_config_error(ie, WPS_CFG_NO_ERROR) ||
+ wps_build_dev_password_id(ie, pw_id) ||
+#ifdef CONFIG_WPS2
+ wps_build_manufacturer(dev, ie) ||
+ wps_build_model_name(dev, ie) ||
+ wps_build_model_number(dev, ie) ||
+ wps_build_dev_name(dev, ie) ||
+ wps_build_wfa_ext(ie, req_type == WPS_REQ_ENROLLEE, NULL, 0) ||
+#endif /* CONFIG_WPS2 */
+ wps_build_req_dev_type(dev, ie, num_req_dev_types, req_dev_types)
+ ||
+ wps_build_secondary_dev_type(dev, ie)
+ ) {
+ wpabuf_free(ie);
+ return NULL;
+ }
+
+#ifndef CONFIG_WPS2
+ if (dev->p2p && wps_build_dev_name(dev, ie)) {
+ wpabuf_free(ie);
+ return NULL;
+ }
+#endif /* CONFIG_WPS2 */
+
+ return wps_ie_encapsulate(ie);
+}
+
+#ifdef CONFIG_WPS_UPNP
+
+void wps_free_pending_msgs(struct upnp_pending_message *msgs)
+{
+ struct upnp_pending_message *p, *prev;
+ p = msgs;
+ while (p) {
+ prev = p;
+ p = p->next;
+ wpabuf_free(prev->msg);
+ os_free(prev);
+ }
+}
+
+#endif
+
+int wps_attr_text(struct wpabuf *data, char *buf, char *end)
+{
+ struct wps_parse_attr *attr;
+ char *pos = buf;
+ int ret;
+
+ attr = (struct wps_parse_attr *)os_zalloc(sizeof(struct wps_parse_attr));
+ if (attr == NULL)
+ return -99;
+
+ if (wps_parse_msg(data, attr) < 0) {
+ ret = -1;
+ goto _out;
+ }
+
+ if (attr->wps_state) {
+ if (*attr->wps_state == WPS_STATE_NOT_CONFIGURED)
+ ret = snprintf(pos, end - pos,
+ "wps_state=unconfigured\n");
+ else if (*attr->wps_state == WPS_STATE_CONFIGURED)
+ ret = snprintf(pos, end - pos,
+ "wps_state=configured\n");
+ else
+ ret = 0;
+ if (ret < 0 || ret >= end - pos) {
+ ret = pos - buf;
+ goto _out;
+ }
+ pos += ret;
+ }
+
+ if (attr->ap_setup_locked && *attr->ap_setup_locked) {
+ ret = snprintf(pos, end - pos,
+ "wps_ap_setup_locked=1\n");
+ if (ret < 0 || ret >= end - pos) {
+ ret = pos - buf;
+ goto _out;
+ }
+ pos += ret;
+ }
+
+ if (attr->selected_registrar && *attr->selected_registrar) {
+ ret = snprintf(pos, end - pos,
+ "wps_selected_registrar=1\n");
+ if (ret < 0 || ret >= end - pos) {
+ ret = pos - buf;
+ goto _out;
+ }
+ pos += ret;
+ }
+
+ if (attr->dev_password_id) {
+ ret = snprintf(pos, end - pos,
+ "wps_device_password_id=%u\n",
+ WPA_GET_BE16(attr->dev_password_id));
+ if (ret < 0 || ret >= end - pos) {
+ ret = pos - buf;
+ goto _out;
+ }
+ pos += ret;
+ }
+
+ if (attr->sel_reg_config_methods) {
+ ret = snprintf(pos, end - pos,
+ "wps_selected_registrar_config_methods="
+ "0x%04x\n",
+ WPA_GET_BE16(attr->sel_reg_config_methods));
+ if (ret < 0 || ret >= end - pos) {
+ ret = pos - buf;
+ goto _out;
+ }
+ pos += ret;
+ }
+
+ if (attr->primary_dev_type) {
+ char devtype[WPS_DEV_TYPE_BUFSIZE];
+ ret = snprintf(pos, end - pos,
+ "wps_primary_device_type=%s\n",
+ wps_dev_type_bin2str(attr->primary_dev_type,
+ devtype,
+ sizeof(devtype)));
+ if (ret < 0 || ret >= end - pos) {
+ ret = pos - buf;
+ goto _out;
+ }
+ pos += ret;
+ }
+
+ if (attr->dev_name) {
+ char *str = (char *)os_malloc(attr->dev_name_len + 1);
+ size_t i;
+ if (str == NULL) {
+ ret = pos - buf;
+ goto _out;
+ }
+ for (i = 0; i < attr->dev_name_len; i++) {
+ if (attr->dev_name[i] < 32)
+ str[i] = '_';
+ else
+ str[i] = attr->dev_name[i];
+ }
+ str[i] = '\0';
+ ret = snprintf(pos, end - pos, "wps_device_name=%s\n", str);
+ os_free(str);
+ if (ret < 0 || ret >= end - pos) {
+ ret = pos - buf;
+ goto _out;
+ }
+ pos += ret;
+ }
+
+ if (attr->config_methods) {
+ ret = snprintf(pos, end - pos,
+ "wps_config_methods=0x%04x\n",
+ WPA_GET_BE16(attr->config_methods));
+ if (ret < 0 || ret >= end - pos) {
+ ret = pos - buf;
+ goto _out;
+ }
+ pos += ret;
+ }
+
+ ret = pos - buf;
+_out:
+ if (attr)
+ os_free(attr);
+ return ret;
+}
--- /dev/null
+/*
+ * Wi-Fi Protected Setup - attribute building
+ * Copyright (c) 2008, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+#include "wpa/includes.h"
+#include "wpa/common.h"
+#include "wpa/wpa_debug.h"
+
+#include "crypto/aes_wrap.h"
+#include "crypto/crypto.h"
+#include "crypto/dh_group5.h"
+#include "crypto/sha256.h"
+#include "crypto/random.h"
+
+#include "wpa/ieee802_11_defs.h"
+#include "wps/wps_i.h"
+#include "soc/dport_reg.h"
+
+int wps_build_public_key(struct wps_data *wps, struct wpabuf *msg, wps_key_mode_t mode)
+{
+ struct wpabuf *pubkey;
+
+ if (mode != WPS_CALC_KEY_NO_CALC) {
+
+ wpa_printf(MSG_DEBUG, "WPS: * Public Key");
+ wpabuf_free(wps->dh_privkey);
+ if (wps->dev_pw_id != DEV_PW_DEFAULT && wps->wps->dh_privkey) {
+ wpa_printf(MSG_DEBUG, "WPS: Using pre-configured DH keys");
+ wps->dh_privkey = wpabuf_dup(wps->wps->dh_privkey);
+ wps->dh_ctx = wps->wps->dh_ctx;
+ wps->wps->dh_ctx = NULL;
+ pubkey = wpabuf_dup(wps->wps->dh_pubkey);
+#ifdef CONFIG_WPS_NFC
+ } else if (wps->dev_pw_id >= 0x10 && wps->wps->ap &&
+ wps->dev_pw_id == wps->wps->ap_nfc_dev_pw_id) {
+ wpa_printf(MSG_DEBUG, "WPS: Using NFC password token DH keys");
+ wps->dh_privkey = wpabuf_dup(wps->wps->ap_nfc_dh_privkey);
+ pubkey = wpabuf_dup(wps->wps->ap_nfc_dh_pubkey);
+ wps->dh_ctx = dh5_init_fixed(wps->dh_privkey, pubkey);
+#endif /* CONFIG_WPS_NFC */
+ } else {
+ wpa_printf(MSG_DEBUG, "WPS: Generate new DH keys");
+ wps->dh_privkey = NULL;
+ dh5_free(wps->dh_ctx);
+
+ wpa_printf(MSG_DEBUG, "build public key start\n");
+
+ wps->dh_ctx = dh5_init(&wps->dh_privkey, &pubkey);
+
+ wpa_printf(MSG_DEBUG, "build public key finish\n");
+
+ pubkey = wpabuf_zeropad(pubkey, 192);
+ }
+ if (wps->dh_ctx == NULL || wps->dh_privkey == NULL || pubkey == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS: Failed to initialize "
+ "Diffie-Hellman handshake");
+ wpabuf_free(pubkey);
+ return -1;
+ }
+ wpa_hexdump_buf_key(MSG_DEBUG, "WPS: DH Private Key", wps->dh_privkey);
+ wpa_hexdump_buf(MSG_DEBUG, "WPS: DH own Public Key", pubkey);
+
+ if (wps->registrar) {
+ wpabuf_free(wps->dh_pubkey_r);
+ wps->dh_pubkey_r = pubkey;
+ } else {
+ wpabuf_free(wps->dh_pubkey_e);
+ wps->dh_pubkey_e = pubkey;
+ }
+
+ }
+
+ if (mode != WPS_CALC_KEY_PRE_CALC) {
+ if (wps->registrar)
+ pubkey = wps->dh_pubkey_r;
+ else
+ pubkey = wps->dh_pubkey_e;
+
+ wpabuf_put_be16(msg, ATTR_PUBLIC_KEY);
+ wpabuf_put_be16(msg, wpabuf_len(pubkey));
+ wpabuf_put_buf(msg, pubkey);
+ }
+
+ return 0;
+}
+
+
+int wps_build_req_type(struct wpabuf *msg, enum wps_request_type type)
+{
+ wpa_printf(MSG_DEBUG, "WPS: * Request Type");
+ wpabuf_put_be16(msg, ATTR_REQUEST_TYPE);
+ wpabuf_put_be16(msg, 1);
+ wpabuf_put_u8(msg, type);
+ return 0;
+}
+
+
+int wps_build_resp_type(struct wpabuf *msg, enum wps_response_type type)
+{
+ wpa_printf(MSG_DEBUG, "WPS: * Response Type (%d)", type);
+ wpabuf_put_be16(msg, ATTR_RESPONSE_TYPE);
+ wpabuf_put_be16(msg, 1);
+ wpabuf_put_u8(msg, type);
+ return 0;
+}
+
+
+int wps_build_config_methods(struct wpabuf *msg, u16 methods)
+{
+ wpa_printf(MSG_DEBUG, "WPS: * Config Methods (%x)", methods);
+ wpabuf_put_be16(msg, ATTR_CONFIG_METHODS);
+ wpabuf_put_be16(msg, 2);
+ wpabuf_put_be16(msg, methods);
+ return 0;
+}
+
+
+int wps_build_uuid_e(struct wpabuf *msg, const u8 *uuid)
+{
+ wpa_printf(MSG_DEBUG, "WPS: * UUID-E");
+ wpabuf_put_be16(msg, ATTR_UUID_E);
+ wpabuf_put_be16(msg, WPS_UUID_LEN);
+ wpabuf_put_data(msg, uuid, WPS_UUID_LEN);
+ return 0;
+}
+
+
+int wps_build_dev_password_id(struct wpabuf *msg, u16 id)
+{
+ wpa_printf(MSG_DEBUG, "WPS: * Device Password ID (%d)", id);
+ wpabuf_put_be16(msg, ATTR_DEV_PASSWORD_ID);
+ wpabuf_put_be16(msg, 2);
+ wpabuf_put_be16(msg, id);
+ return 0;
+}
+
+
+int wps_build_config_error(struct wpabuf *msg, u16 err)
+{
+ wpa_printf(MSG_DEBUG, "WPS: * Configuration Error (%d)", err);
+ wpabuf_put_be16(msg, ATTR_CONFIG_ERROR);
+ wpabuf_put_be16(msg, 2);
+ wpabuf_put_be16(msg, err);
+ return 0;
+}
+
+
+int wps_build_authenticator(struct wps_data *wps, struct wpabuf *msg)
+{
+ u8 hash[SHA256_MAC_LEN];
+ const u8 *addr[2];
+ size_t len[2];
+
+ if (wps->last_msg == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS: Last message not available for "
+ "building authenticator");
+ return -1;
+ }
+
+ /* Authenticator = HMAC-SHA256_AuthKey(M_prev || M_curr*)
+ * (M_curr* is M_curr without the Authenticator attribute)
+ */
+ addr[0] = wpabuf_head(wps->last_msg);
+ len[0] = wpabuf_len(wps->last_msg);
+ addr[1] = wpabuf_head(msg);
+ len[1] = wpabuf_len(msg);
+ if (wps_crypto_funcs.hmac_sha256_vector) {
+ wps_crypto_funcs.hmac_sha256_vector(wps->authkey, WPS_AUTHKEY_LEN, 2, addr, (int *)len, hash);
+ } else {
+ wpa_printf(MSG_ERROR, "Fail to register hmac sha256 vector!\r\n");
+ return -1;
+ }
+ wpa_printf(MSG_DEBUG, "WPS: * Authenticator");
+ wpabuf_put_be16(msg, ATTR_AUTHENTICATOR);
+ wpabuf_put_be16(msg, WPS_AUTHENTICATOR_LEN);
+ wpabuf_put_data(msg, hash, WPS_AUTHENTICATOR_LEN);
+
+ return 0;
+}
+
+
+int wps_build_version(struct wpabuf *msg)
+{
+ /*
+ * Note: This attribute is deprecated and set to hardcoded 0x10 for
+ * backwards compatibility reasons. The real version negotiation is
+ * done with Version2.
+ */
+ wpa_printf(MSG_DEBUG, "WPS: * Version (hardcoded 0x10)");
+ wpabuf_put_be16(msg, ATTR_VERSION);
+ wpabuf_put_be16(msg, 1);
+ wpabuf_put_u8(msg, 0x10);
+ return 0;
+}
+
+
+int wps_build_wfa_ext(struct wpabuf *msg, int req_to_enroll,
+ const u8 *auth_macs, size_t auth_macs_count)
+{
+#ifdef CONFIG_WPS2
+ u8 *len;
+
+ wpabuf_put_be16(msg, ATTR_VENDOR_EXT);
+ len = wpabuf_put(msg, 2); /* to be filled */
+ wpabuf_put_be24(msg, WPS_VENDOR_ID_WFA);
+
+ wpa_printf(MSG_DEBUG, "WPS: * Version2 (0x%x)", WPS_VERSION);
+ wpabuf_put_u8(msg, WFA_ELEM_VERSION2);
+ wpabuf_put_u8(msg, 1);
+ wpabuf_put_u8(msg, WPS_VERSION);
+
+ if (req_to_enroll) {
+ wpa_printf(MSG_DEBUG, "WPS: * Request to Enroll (1)");
+ wpabuf_put_u8(msg, WFA_ELEM_REQUEST_TO_ENROLL);
+ wpabuf_put_u8(msg, 1);
+ wpabuf_put_u8(msg, 1);
+ }
+
+ if (auth_macs && auth_macs_count) {
+ size_t i;
+ wpa_printf(MSG_DEBUG, "WPS: * AuthorizedMACs (count=%d)",
+ (int) auth_macs_count);
+ wpabuf_put_u8(msg, WFA_ELEM_AUTHORIZEDMACS);
+ wpabuf_put_u8(msg, auth_macs_count * ETH_ALEN);
+ wpabuf_put_data(msg, auth_macs, auth_macs_count * ETH_ALEN);
+ for (i = 0; i < auth_macs_count; i++)
+ wpa_printf(MSG_DEBUG, "WPS: AuthorizedMAC: " MACSTR,
+ MAC2STR(&auth_macs[i * ETH_ALEN]));
+ }
+
+ WPA_PUT_BE16(len, (u8 *) wpabuf_put(msg, 0) - len - 2);
+#endif /* CONFIG_WPS2 */
+
+#ifdef CONFIG_WPS_TESTING
+ if (WPS_VERSION > 0x20) {
+ wpa_printf(MSG_DEBUG, "WPS: * Extensibility Testing - extra "
+ "attribute");
+ wpabuf_put_be16(msg, ATTR_EXTENSIBILITY_TEST);
+ wpabuf_put_be16(msg, 1);
+ wpabuf_put_u8(msg, 42);
+ }
+#endif /* CONFIG_WPS_TESTING */
+ return 0;
+}
+
+
+int wps_build_msg_type(struct wpabuf *msg, enum wps_msg_type msg_type)
+{
+ wpa_printf(MSG_DEBUG, "WPS: * Message Type (%d)", msg_type);
+ wpabuf_put_be16(msg, ATTR_MSG_TYPE);
+ wpabuf_put_be16(msg, 1);
+ wpabuf_put_u8(msg, msg_type);
+ return 0;
+}
+
+
+int wps_build_enrollee_nonce(struct wps_data *wps, struct wpabuf *msg)
+{
+ wpa_printf(MSG_DEBUG, "WPS: * Enrollee Nonce");
+ wpabuf_put_be16(msg, ATTR_ENROLLEE_NONCE);
+ wpabuf_put_be16(msg, WPS_NONCE_LEN);
+ wpabuf_put_data(msg, wps->nonce_e, WPS_NONCE_LEN);
+ return 0;
+}
+
+
+int wps_build_registrar_nonce(struct wps_data *wps, struct wpabuf *msg)
+{
+ wpa_printf(MSG_DEBUG, "WPS: * Registrar Nonce");
+ wpabuf_put_be16(msg, ATTR_REGISTRAR_NONCE);
+ wpabuf_put_be16(msg, WPS_NONCE_LEN);
+ wpabuf_put_data(msg, wps->nonce_r, WPS_NONCE_LEN);
+ return 0;
+}
+
+
+int wps_build_auth_type_flags(struct wps_data *wps, struct wpabuf *msg)
+{
+ u16 auth_types = WPS_AUTH_TYPES;
+#ifdef CONFIG_WPS2
+ auth_types &= ~WPS_AUTH_SHARED;
+#endif /* CONFIG_WPS2 */
+ wpa_printf(MSG_DEBUG, "WPS: * Authentication Type Flags");
+ wpabuf_put_be16(msg, ATTR_AUTH_TYPE_FLAGS);
+ wpabuf_put_be16(msg, 2);
+ wpabuf_put_be16(msg, auth_types);
+ return 0;
+}
+
+
+int wps_build_encr_type_flags(struct wps_data *wps, struct wpabuf *msg)
+{
+ u16 encr_types = WPS_ENCR_TYPES;
+#ifdef CONFIG_WPS2
+ encr_types &= ~WPS_ENCR_WEP;
+#endif /* CONFIG_WPS2 */
+ wpa_printf(MSG_DEBUG, "WPS: * Encryption Type Flags");
+ wpabuf_put_be16(msg, ATTR_ENCR_TYPE_FLAGS);
+ wpabuf_put_be16(msg, 2);
+ wpabuf_put_be16(msg, encr_types);
+ return 0;
+}
+
+
+int wps_build_conn_type_flags(struct wps_data *wps, struct wpabuf *msg)
+{
+ wpa_printf(MSG_DEBUG, "WPS: * Connection Type Flags");
+ wpabuf_put_be16(msg, ATTR_CONN_TYPE_FLAGS);
+ wpabuf_put_be16(msg, 1);
+ wpabuf_put_u8(msg, WPS_CONN_ESS);
+ return 0;
+}
+
+
+int wps_build_assoc_state(struct wps_data *wps, struct wpabuf *msg)
+{
+ wpa_printf(MSG_DEBUG, "WPS: * Association State");
+ wpabuf_put_be16(msg, ATTR_ASSOC_STATE);
+ wpabuf_put_be16(msg, 2);
+ wpabuf_put_be16(msg, WPS_ASSOC_NOT_ASSOC);
+ return 0;
+}
+
+
+int wps_build_key_wrap_auth(struct wps_data *wps, struct wpabuf *msg)
+{
+ u8 hash[SHA256_MAC_LEN];
+
+ wpa_printf(MSG_DEBUG, "WPS: * Key Wrap Authenticator");
+ if (wps_crypto_funcs.hmac_sha256) {
+ wps_crypto_funcs.hmac_sha256(wps->authkey, WPS_AUTHKEY_LEN, wpabuf_head(msg),
+ wpabuf_len(msg), hash);
+ } else {
+ wpa_printf(MSG_ERROR, "Fail to register hmac sha256 function!\r\n");
+ return -1;
+ }
+ wpabuf_put_be16(msg, ATTR_KEY_WRAP_AUTH);
+ wpabuf_put_be16(msg, WPS_KWA_LEN);
+ wpabuf_put_data(msg, hash, WPS_KWA_LEN);
+ return 0;
+}
+
+
+int wps_build_encr_settings(struct wps_data *wps, struct wpabuf *msg,
+ struct wpabuf *plain)
+{
+ size_t pad_len;
+ const size_t block_size = 16;
+ u8 *iv, *data;
+
+ wpa_printf(MSG_DEBUG, "WPS: * Encrypted Settings");
+
+ /* PKCS#5 v2.0 pad */
+ pad_len = block_size - wpabuf_len(plain) % block_size;
+ os_memset(wpabuf_put(plain, pad_len), pad_len, pad_len);
+
+ wpabuf_put_be16(msg, ATTR_ENCR_SETTINGS);
+ wpabuf_put_be16(msg, block_size + wpabuf_len(plain));
+
+ iv = wpabuf_put(msg, block_size);
+ if (random_get_bytes(iv, block_size) < 0)
+ return -1;
+
+ data = wpabuf_put(msg, 0);
+ wpabuf_put_buf(msg, plain);
+ wpa_printf(MSG_DEBUG, "WPS: * AES 128 Encrypted Settings");
+ if (wps_crypto_funcs.aes_128_encrypt) {
+ if (wps_crypto_funcs.aes_128_encrypt(wps->keywrapkey, iv, data, wpabuf_len(plain)))
+ return -1;
+ } else {
+ wpa_printf(MSG_ERROR, "Fail to register aes_128_encrypt function!\r\n");
+ return -1;
+ }
+ return 0;
+}
+
+
+#ifdef CONFIG_WPS_OOB
+int wps_build_oob_dev_pw(struct wpabuf *msg, u16 dev_pw_id,
+ const struct wpabuf *pubkey, const u8 *dev_pw,
+ size_t dev_pw_len)
+{
+ size_t hash_len;
+ const u8 *addr[1];
+ u8 pubkey_hash[WPS_HASH_LEN];
+
+ addr[0] = wpabuf_head(pubkey);
+ hash_len = wpabuf_len(pubkey);
+ if (wps_crypto_funcs.sha256_vector) {
+ wps_crypto_funcs.sha256_vector(1, addr, &hash_len, pubkey_hash);
+ } else {
+ wpa_printf(MSG_ERROR, "Fail to register sha256 vector function!\r\n");
+ return -1;
+ }
+ wpabuf_put_be16(msg, ATTR_OOB_DEVICE_PASSWORD);
+ wpabuf_put_be16(msg, WPS_OOB_PUBKEY_HASH_LEN + 2 + dev_pw_len);
+ wpabuf_put_data(msg, pubkey_hash, WPS_OOB_PUBKEY_HASH_LEN);
+ wpabuf_put_be16(msg, dev_pw_id);
+ wpabuf_put_data(msg, dev_pw, dev_pw_len);
+
+ return 0;
+}
+#endif /* CONFIG_WPS_OOB */
+
+
+/* Encapsulate WPS IE data with one (or more, if needed) IE headers */
+struct wpabuf * wps_ie_encapsulate(struct wpabuf *data)
+{
+ struct wpabuf *ie;
+ const u8 *pos, *end;
+
+ ie = wpabuf_alloc(wpabuf_len(data) + 100);
+ if (ie == NULL) {
+ wpabuf_free(data);
+ return NULL;
+ }
+
+ pos = wpabuf_head(data);
+ end = pos + wpabuf_len(data);
+
+ while (end > pos) {
+ size_t frag_len = end - pos;
+ if (frag_len > 251)
+ frag_len = 251;
+ wpabuf_put_u8(ie, WLAN_EID_VENDOR_SPECIFIC);
+ wpabuf_put_u8(ie, 4 + frag_len);
+ wpabuf_put_be32(ie, WPS_DEV_OUI_WFA);
+ wpabuf_put_data(ie, pos, frag_len);
+ pos += frag_len;
+ }
+
+ wpabuf_free(data);
+
+ return ie;
+}
\ No newline at end of file
--- /dev/null
+/*
+ * Wi-Fi Protected Setup - attribute parsing
+ * Copyright (c) 2008, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+#include "wpa/includes.h"
+
+#include "wpa/common.h"
+#include "wps/wps_defs.h"
+#include "wps/wps_attr_parse.h"
+
+#ifndef CONFIG_WPS_STRICT
+#define WPS_WORKAROUNDS
+#endif /* CONFIG_WPS_STRICT */
+
+
+static int wps_set_vendor_ext_wfa_subelem(struct wps_parse_attr *attr,
+ u8 id, u8 len, const u8 *pos)
+{
+ wpa_printf(MSG_DEBUG, "WPS: WFA subelement id=%u len=%u",
+ id, len);
+ switch (id) {
+ case WFA_ELEM_VERSION2:
+ if (len != 1) {
+ wpa_printf(MSG_DEBUG, "WPS: Invalid Version2 length "
+ "%u", len);
+ return -1;
+ }
+ attr->version2 = pos;
+ break;
+ case WFA_ELEM_AUTHORIZEDMACS:
+ attr->authorized_macs = pos;
+ attr->authorized_macs_len = len;
+ break;
+ case WFA_ELEM_NETWORK_KEY_SHAREABLE:
+ if (len != 1) {
+ wpa_printf(MSG_DEBUG, "WPS: Invalid Network Key "
+ "Shareable length %u", len);
+ return -1;
+ }
+ attr->network_key_shareable = pos;
+ break;
+ case WFA_ELEM_REQUEST_TO_ENROLL:
+ if (len != 1) {
+ wpa_printf(MSG_DEBUG, "WPS: Invalid Request to Enroll "
+ "length %u", len);
+ return -1;
+ }
+ attr->request_to_enroll = pos;
+ break;
+ case WFA_ELEM_SETTINGS_DELAY_TIME:
+ if (len != 1) {
+ wpa_printf(MSG_DEBUG, "WPS: Invalid Settings Delay "
+ "Time length %u", len);
+ return -1;
+ }
+ attr->settings_delay_time = pos;
+ break;
+ default:
+ wpa_printf(MSG_DEBUG, "WPS: Skipped unknown WFA Vendor "
+ "Extension subelement %u", id);
+ break;
+ }
+
+ return 0;
+}
+
+
+static int wps_parse_vendor_ext_wfa(struct wps_parse_attr *attr, const u8 *pos,
+ u16 len)
+{
+ const u8 *end = pos + len;
+ u8 id, elen;
+
+ while (pos + 2 < end) {
+ id = *pos++;
+ elen = *pos++;
+ if (pos + elen > end)
+ break;
+ if (wps_set_vendor_ext_wfa_subelem(attr, id, elen, pos) < 0)
+ return -1;
+ pos += elen;
+ }
+
+ return 0;
+}
+
+
+static int wps_parse_vendor_ext(struct wps_parse_attr *attr, const u8 *pos,
+ u16 len)
+{
+ u32 vendor_id;
+
+ if (len < 3) {
+ wpa_printf(MSG_DEBUG, "WPS: Skip invalid Vendor Extension");
+ return 0;
+ }
+
+ vendor_id = WPA_GET_BE24(pos);
+ switch (vendor_id) {
+ case WPS_VENDOR_ID_WFA:
+ return wps_parse_vendor_ext_wfa(attr, pos + 3, len - 3);
+ }
+
+ /* Handle unknown vendor extensions */
+
+ wpa_printf(MSG_DEBUG, "WPS: Unknown Vendor Extension (Vendor ID %u)",
+ vendor_id);
+
+ if (len > WPS_MAX_VENDOR_EXT_LEN) {
+ wpa_printf(MSG_DEBUG, "WPS: Too long Vendor Extension (%u)",
+ len);
+ return -1;
+ }
+
+ if (attr->num_vendor_ext >= MAX_WPS_PARSE_VENDOR_EXT) {
+ wpa_printf(MSG_DEBUG, "WPS: Skipped Vendor Extension "
+ "attribute (max %d vendor extensions)",
+ MAX_WPS_PARSE_VENDOR_EXT);
+ return -1;
+ }
+ attr->vendor_ext[attr->num_vendor_ext] = pos;
+ attr->vendor_ext_len[attr->num_vendor_ext] = len;
+ attr->num_vendor_ext++;
+
+ return 0;
+}
+
+
+static int wps_set_attr(struct wps_parse_attr *attr, u16 type,
+ const u8 *pos, u16 len)
+{
+ switch (type) {
+ case ATTR_VERSION:
+ if (len != 1) {
+ wpa_printf(MSG_DEBUG, "WPS: Invalid Version length %u",
+ len);
+ return -1;
+ }
+ attr->version = pos;
+ break;
+ case ATTR_MSG_TYPE:
+ if (len != 1) {
+ wpa_printf(MSG_DEBUG, "WPS: Invalid Message Type "
+ "length %u", len);
+ return -1;
+ }
+ attr->msg_type = pos;
+ break;
+ case ATTR_ENROLLEE_NONCE:
+ if (len != WPS_NONCE_LEN) {
+ wpa_printf(MSG_DEBUG, "WPS: Invalid Enrollee Nonce "
+ "length %u", len);
+ return -1;
+ }
+ attr->enrollee_nonce = pos;
+ break;
+ case ATTR_REGISTRAR_NONCE:
+ if (len != WPS_NONCE_LEN) {
+ wpa_printf(MSG_DEBUG, "WPS: Invalid Registrar Nonce "
+ "length %u", len);
+ return -1;
+ }
+ attr->registrar_nonce = pos;
+ break;
+ case ATTR_UUID_E:
+ if (len != WPS_UUID_LEN) {
+ wpa_printf(MSG_DEBUG, "WPS: Invalid UUID-E length %u",
+ len);
+ return -1;
+ }
+ attr->uuid_e = pos;
+ break;
+ case ATTR_UUID_R:
+ if (len != WPS_UUID_LEN) {
+ wpa_printf(MSG_DEBUG, "WPS: Invalid UUID-R length %u",
+ len);
+ return -1;
+ }
+ attr->uuid_r = pos;
+ break;
+ case ATTR_AUTH_TYPE_FLAGS:
+ if (len != 2) {
+ wpa_printf(MSG_DEBUG, "WPS: Invalid Authentication "
+ "Type Flags length %u", len);
+ return -1;
+ }
+ attr->auth_type_flags = pos;
+ break;
+ case ATTR_ENCR_TYPE_FLAGS:
+ if (len != 2) {
+ wpa_printf(MSG_DEBUG, "WPS: Invalid Encryption Type "
+ "Flags length %u", len);
+ return -1;
+ }
+ attr->encr_type_flags = pos;
+ break;
+ case ATTR_CONN_TYPE_FLAGS:
+ if (len != 1) {
+ wpa_printf(MSG_DEBUG, "WPS: Invalid Connection Type "
+ "Flags length %u", len);
+ return -1;
+ }
+ attr->conn_type_flags = pos;
+ break;
+ case ATTR_CONFIG_METHODS:
+ if (len != 2) {
+ wpa_printf(MSG_DEBUG, "WPS: Invalid Config Methods "
+ "length %u", len);
+ return -1;
+ }
+ attr->config_methods = pos;
+ break;
+ case ATTR_SELECTED_REGISTRAR_CONFIG_METHODS:
+ if (len != 2) {
+ wpa_printf(MSG_DEBUG, "WPS: Invalid Selected "
+ "Registrar Config Methods length %u", len);
+ return -1;
+ }
+ attr->sel_reg_config_methods = pos;
+ break;
+ case ATTR_PRIMARY_DEV_TYPE:
+ if (len != WPS_DEV_TYPE_LEN) {
+ wpa_printf(MSG_DEBUG, "WPS: Invalid Primary Device "
+ "Type length %u", len);
+ return -1;
+ }
+ attr->primary_dev_type = pos;
+ break;
+ case ATTR_RF_BANDS:
+ if (len != 1) {
+ wpa_printf(MSG_DEBUG, "WPS: Invalid RF Bands length "
+ "%u", len);
+ return -1;
+ }
+ attr->rf_bands = pos;
+ break;
+ case ATTR_ASSOC_STATE:
+ if (len != 2) {
+ wpa_printf(MSG_DEBUG, "WPS: Invalid Association State "
+ "length %u", len);
+ return -1;
+ }
+ attr->assoc_state = pos;
+ break;
+ case ATTR_CONFIG_ERROR:
+ if (len != 2) {
+ wpa_printf(MSG_DEBUG, "WPS: Invalid Configuration "
+ "Error length %u", len);
+ return -1;
+ }
+ attr->config_error = pos;
+ break;
+ case ATTR_DEV_PASSWORD_ID:
+ if (len != 2) {
+ wpa_printf(MSG_DEBUG, "WPS: Invalid Device Password "
+ "ID length %u", len);
+ return -1;
+ }
+ attr->dev_password_id = pos;
+ break;
+ case ATTR_OOB_DEVICE_PASSWORD:
+ if (len < WPS_OOB_PUBKEY_HASH_LEN + 2 +
+ WPS_OOB_DEVICE_PASSWORD_MIN_LEN ||
+ len > WPS_OOB_PUBKEY_HASH_LEN + 2 +
+ WPS_OOB_DEVICE_PASSWORD_LEN) {
+ wpa_printf(MSG_DEBUG, "WPS: Invalid OOB Device "
+ "Password length %u", len);
+ return -1;
+ }
+ attr->oob_dev_password = pos;
+ attr->oob_dev_password_len = len;
+ break;
+ case ATTR_OS_VERSION:
+ if (len != 4) {
+ wpa_printf(MSG_DEBUG, "WPS: Invalid OS Version length "
+ "%u", len);
+ return -1;
+ }
+ attr->os_version = pos;
+ break;
+ case ATTR_WPS_STATE:
+ if (len != 1) {
+ wpa_printf(MSG_DEBUG, "WPS: Invalid Wi-Fi Protected "
+ "Setup State length %u", len);
+ return -1;
+ }
+ attr->wps_state = pos;
+ break;
+ case ATTR_AUTHENTICATOR:
+ if (len != WPS_AUTHENTICATOR_LEN) {
+ wpa_printf(MSG_DEBUG, "WPS: Invalid Authenticator "
+ "length %u", len);
+ return -1;
+ }
+ attr->authenticator = pos;
+ break;
+ case ATTR_R_HASH1:
+ if (len != WPS_HASH_LEN) {
+ wpa_printf(MSG_DEBUG, "WPS: Invalid R-Hash1 length %u",
+ len);
+ return -1;
+ }
+ attr->r_hash1 = pos;
+ break;
+ case ATTR_R_HASH2:
+ if (len != WPS_HASH_LEN) {
+ wpa_printf(MSG_DEBUG, "WPS: Invalid R-Hash2 length %u",
+ len);
+ return -1;
+ }
+ attr->r_hash2 = pos;
+ break;
+ case ATTR_E_HASH1:
+ if (len != WPS_HASH_LEN) {
+ wpa_printf(MSG_DEBUG, "WPS: Invalid E-Hash1 length %u",
+ len);
+ return -1;
+ }
+ attr->e_hash1 = pos;
+ break;
+ case ATTR_E_HASH2:
+ if (len != WPS_HASH_LEN) {
+ wpa_printf(MSG_DEBUG, "WPS: Invalid E-Hash2 length %u",
+ len);
+ return -1;
+ }
+ attr->e_hash2 = pos;
+ break;
+ case ATTR_R_SNONCE1:
+ if (len != WPS_SECRET_NONCE_LEN) {
+ wpa_printf(MSG_DEBUG, "WPS: Invalid R-SNonce1 length "
+ "%u", len);
+ return -1;
+ }
+ attr->r_snonce1 = pos;
+ break;
+ case ATTR_R_SNONCE2:
+ if (len != WPS_SECRET_NONCE_LEN) {
+ wpa_printf(MSG_DEBUG, "WPS: Invalid R-SNonce2 length "
+ "%u", len);
+ return -1;
+ }
+ attr->r_snonce2 = pos;
+ break;
+ case ATTR_E_SNONCE1:
+ if (len != WPS_SECRET_NONCE_LEN) {
+ wpa_printf(MSG_DEBUG, "WPS: Invalid E-SNonce1 length "
+ "%u", len);
+ return -1;
+ }
+ attr->e_snonce1 = pos;
+ break;
+ case ATTR_E_SNONCE2:
+ if (len != WPS_SECRET_NONCE_LEN) {
+ wpa_printf(MSG_DEBUG, "WPS: Invalid E-SNonce2 length "
+ "%u", len);
+ return -1;
+ }
+ attr->e_snonce2 = pos;
+ break;
+ case ATTR_KEY_WRAP_AUTH:
+ if (len != WPS_KWA_LEN) {
+ wpa_printf(MSG_DEBUG, "WPS: Invalid Key Wrap "
+ "Authenticator length %u", len);
+ return -1;
+ }
+ attr->key_wrap_auth = pos;
+ break;
+ case ATTR_AUTH_TYPE:
+ if (len != 2) {
+ wpa_printf(MSG_DEBUG, "WPS: Invalid Authentication "
+ "Type length %u", len);
+ return -1;
+ }
+ attr->auth_type = pos;
+ break;
+ case ATTR_ENCR_TYPE:
+ if (len != 2) {
+ wpa_printf(MSG_DEBUG, "WPS: Invalid Encryption "
+ "Type length %u", len);
+ return -1;
+ }
+ attr->encr_type = pos;
+ break;
+ case ATTR_NETWORK_INDEX:
+ if (len != 1) {
+ wpa_printf(MSG_DEBUG, "WPS: Invalid Network Index "
+ "length %u", len);
+ return -1;
+ }
+ attr->network_idx = pos;
+ break;
+ case ATTR_NETWORK_KEY_INDEX:
+ if (len != 1) {
+ wpa_printf(MSG_DEBUG, "WPS: Invalid Network Key Index "
+ "length %u", len);
+ return -1;
+ }
+ attr->network_key_idx = pos;
+ break;
+ case ATTR_MAC_ADDR:
+ if (len != ETH_ALEN) {
+ wpa_printf(MSG_DEBUG, "WPS: Invalid MAC Address "
+ "length %u", len);
+ return -1;
+ }
+ attr->mac_addr = pos;
+ break;
+ case ATTR_KEY_PROVIDED_AUTO:
+ if (len != 1) {
+ wpa_printf(MSG_DEBUG, "WPS: Invalid Key Provided "
+ "Automatically length %u", len);
+ return -1;
+ }
+ attr->key_prov_auto = pos;
+ break;
+ case ATTR_802_1X_ENABLED:
+ if (len != 1) {
+ wpa_printf(MSG_DEBUG, "WPS: Invalid 802.1X Enabled "
+ "length %u", len);
+ return -1;
+ }
+ attr->dot1x_enabled = pos;
+ break;
+ case ATTR_SELECTED_REGISTRAR:
+ if (len != 1) {
+ wpa_printf(MSG_DEBUG, "WPS: Invalid Selected Registrar"
+ " length %u", len);
+ return -1;
+ }
+ attr->selected_registrar = pos;
+ break;
+ case ATTR_REQUEST_TYPE:
+ if (len != 1) {
+ wpa_printf(MSG_DEBUG, "WPS: Invalid Request Type "
+ "length %u", len);
+ return -1;
+ }
+ attr->request_type = pos;
+ break;
+ case ATTR_RESPONSE_TYPE:
+ if (len != 1) {
+ wpa_printf(MSG_DEBUG, "WPS: Invalid Response Type "
+ "length %u", len);
+ return -1;
+ }
+ attr->response_type = pos;
+ break;
+ case ATTR_MANUFACTURER:
+ attr->manufacturer = pos;
+ attr->manufacturer_len = len;
+ break;
+ case ATTR_MODEL_NAME:
+ attr->model_name = pos;
+ attr->model_name_len = len;
+ break;
+ case ATTR_MODEL_NUMBER:
+ attr->model_number = pos;
+ attr->model_number_len = len;
+ break;
+ case ATTR_SERIAL_NUMBER:
+ attr->serial_number = pos;
+ attr->serial_number_len = len;
+ break;
+ case ATTR_DEV_NAME:
+ attr->dev_name = pos;
+ attr->dev_name_len = len;
+ break;
+ case ATTR_PUBLIC_KEY:
+ attr->public_key = pos;
+ attr->public_key_len = len;
+ break;
+ case ATTR_ENCR_SETTINGS:
+ attr->encr_settings = pos;
+ attr->encr_settings_len = len;
+ break;
+ case ATTR_CRED:
+ if (attr->num_cred >= MAX_CRED_COUNT) {
+ wpa_printf(MSG_DEBUG, "WPS: Skipped Credential "
+ "attribute (max %d credentials)",
+ MAX_CRED_COUNT);
+ break;
+ }
+ attr->cred[attr->num_cred] = pos;
+ attr->cred_len[attr->num_cred] = len;
+ attr->num_cred++;
+ break;
+ case ATTR_SSID:
+ attr->ssid = pos;
+ attr->ssid_len = len;
+ break;
+ case ATTR_NETWORK_KEY:
+ attr->network_key = pos;
+ attr->network_key_len = len;
+ break;
+ case ATTR_EAP_TYPE:
+ attr->eap_type = pos;
+ attr->eap_type_len = len;
+ break;
+ case ATTR_EAP_IDENTITY:
+ attr->eap_identity = pos;
+ attr->eap_identity_len = len;
+ break;
+ case ATTR_AP_SETUP_LOCKED:
+ if (len != 1) {
+ wpa_printf(MSG_DEBUG, "WPS: Invalid AP Setup Locked "
+ "length %u", len);
+ return -1;
+ }
+ attr->ap_setup_locked = pos;
+ break;
+ case ATTR_REQUESTED_DEV_TYPE:
+ if (len != WPS_DEV_TYPE_LEN) {
+ wpa_printf(MSG_DEBUG, "WPS: Invalid Requested Device "
+ "Type length %u", len);
+ return -1;
+ }
+ if (attr->num_req_dev_type >= MAX_REQ_DEV_TYPE_COUNT) {
+ wpa_printf(MSG_DEBUG, "WPS: Skipped Requested Device "
+ "Type attribute (max %u types)",
+ MAX_REQ_DEV_TYPE_COUNT);
+ break;
+ }
+ attr->req_dev_type[attr->num_req_dev_type] = pos;
+ attr->num_req_dev_type++;
+ break;
+ case ATTR_SECONDARY_DEV_TYPE_LIST:
+ if (len > WPS_SEC_DEV_TYPE_MAX_LEN ||
+ (len % WPS_DEV_TYPE_LEN) > 0) {
+ wpa_printf(MSG_DEBUG, "WPS: Invalid Secondary Device "
+ "Type length %u", len);
+ return -1;
+ }
+ attr->sec_dev_type_list = pos;
+ attr->sec_dev_type_list_len = len;
+ break;
+ case ATTR_VENDOR_EXT:
+ if (wps_parse_vendor_ext(attr, pos, len) < 0)
+ return -1;
+ break;
+ case ATTR_AP_CHANNEL:
+ if (len != 2) {
+ wpa_printf(MSG_DEBUG, "WPS: Invalid AP Channel "
+ "length %u", len);
+ return -1;
+ }
+ attr->ap_channel = pos;
+ break;
+ default:
+ wpa_printf(MSG_DEBUG, "WPS: Unsupported attribute type 0x%x "
+ "len=%u", type, len);
+ break;
+ }
+
+ return 0;
+}
+
+
+int wps_parse_msg(const struct wpabuf *msg, struct wps_parse_attr *attr)
+{
+ const u8 *pos, *end;
+ u16 type, len;
+#ifdef WPS_WORKAROUNDS
+ u16 prev_type = 0;
+#endif /* WPS_WORKAROUNDS */
+
+ os_memset(attr, 0, sizeof(*attr));
+ pos = wpabuf_head(msg);
+ end = pos + wpabuf_len(msg);
+
+ while (pos < end) {
+ if (end - pos < 4) {
+ wpa_printf(MSG_DEBUG, "WPS: Invalid message - "
+ "%lu bytes remaining",
+ (unsigned long) (end - pos));
+ return -1;
+ }
+
+ type = WPA_GET_BE16(pos);
+ pos += 2;
+ len = WPA_GET_BE16(pos);
+ pos += 2;
+ wpa_printf(MSG_DEBUG, "WPS: attr type=0x%x len=%u",
+ type, len);
+ if (len > end - pos) {
+ wpa_printf(MSG_DEBUG, "WPS: Attribute overflow");
+ wpa_hexdump_buf(MSG_MSGDUMP, "WPS: Message data", msg);
+#ifdef WPS_WORKAROUNDS
+ /*
+ * Some deployed APs seem to have a bug in encoding of
+ * Network Key attribute in the Credential attribute
+ * where they add an extra octet after the Network Key
+ * attribute at least when open network is being
+ * provisioned.
+ */
+ if ((type & 0xff00) != 0x1000 &&
+ prev_type == ATTR_NETWORK_KEY) {
+ wpa_printf(MSG_DEBUG, "WPS: Workaround - try "
+ "to skip unexpected octet after "
+ "Network Key");
+ pos -= 3;
+ continue;
+ }
+#endif /* WPS_WORKAROUNDS */
+ return -1;
+ }
+
+#ifdef WPS_WORKAROUNDS
+ if (type == 0 && len == 0) {
+ /*
+ * Mac OS X 10.6 seems to be adding 0x00 padding to the
+ * end of M1. Skip those to avoid interop issues.
+ */
+ int i;
+ for (i = 0; i < end - pos; i++) {
+ if (pos[i])
+ break;
+ }
+ if (i == end - pos) {
+ wpa_printf(MSG_DEBUG, "WPS: Workaround - skip "
+ "unexpected message padding");
+ break;
+ }
+ }
+#endif /* WPS_WORKAROUNDS */
+
+ if (wps_set_attr(attr, type, pos, len) < 0)
+ return -1;
+
+#ifdef WPS_WORKAROUNDS
+ prev_type = type;
+#endif /* WPS_WORKAROUNDS */
+ pos += len;
+ }
+
+ return 0;
+}
\ No newline at end of file
--- /dev/null
+/*
+ * Wi-Fi Protected Setup - attribute processing
+ * Copyright (c) 2008, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+#include "wpa/includes.h"
+
+#include "wpa/common.h"
+#include "crypto/sha256.h"
+#include "wps/wps_i.h"
+
+
+int wps_process_authenticator(struct wps_data *wps, const u8 *authenticator,
+ const struct wpabuf *msg)
+{
+ u8 hash[SHA256_MAC_LEN];
+ const u8 *addr[2];
+ size_t len[2];
+
+ if (authenticator == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS: No Authenticator attribute "
+ "included");
+ return -1;
+ }
+
+ if (wps->last_msg == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS: Last message not available for "
+ "validating authenticator");
+ return -1;
+ }
+
+ /* Authenticator = HMAC-SHA256_AuthKey(M_prev || M_curr*)
+ * (M_curr* is M_curr without the Authenticator attribute)
+ */
+ addr[0] = wpabuf_head(wps->last_msg);
+ len[0] = wpabuf_len(wps->last_msg);
+ addr[1] = wpabuf_head(msg);
+ len[1] = wpabuf_len(msg) - 4 - WPS_AUTHENTICATOR_LEN;
+ if (wps_crypto_funcs.hmac_sha256_vector) {
+ wps_crypto_funcs.hmac_sha256_vector(wps->authkey, WPS_AUTHKEY_LEN, 2, addr, (int *)len, hash);
+ } else {
+ wpa_printf(MSG_ERROR, "Fail to register hmac_sha256_vector function!\r\n");
+ return -1;
+ }
+ if (os_memcmp(hash, authenticator, WPS_AUTHENTICATOR_LEN) != 0) {
+ wpa_printf(MSG_DEBUG, "WPS: Incorrect Authenticator");
+ return -1;
+ }
+
+ return 0;
+}
+
+
+int wps_process_key_wrap_auth(struct wps_data *wps, struct wpabuf *msg,
+ const u8 *key_wrap_auth)
+{
+ u8 hash[SHA256_MAC_LEN];
+ const u8 *head;
+ size_t len;
+
+ if (key_wrap_auth == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS: No KWA in decrypted attribute");
+ return -1;
+ }
+
+ head = wpabuf_head(msg);
+ len = wpabuf_len(msg) - 4 - WPS_KWA_LEN;
+ if (head + len != key_wrap_auth - 4) {
+ wpa_printf(MSG_DEBUG, "WPS: KWA not in the end of the "
+ "decrypted attribute");
+ return -1;
+ }
+
+ if (wps_crypto_funcs.hmac_sha256) {
+ wps_crypto_funcs.hmac_sha256(wps->authkey, WPS_AUTHKEY_LEN, head, len, hash);
+ } else {
+ wpa_printf(MSG_ERROR, "Fail to register hmac sha256 function!\r\n");
+ return -1;
+ }
+ if (os_memcmp(hash, key_wrap_auth, WPS_KWA_LEN) != 0) {
+ wpa_printf(MSG_DEBUG, "WPS: Invalid KWA");
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static int wps_process_cred_network_idx(struct wps_credential *cred,
+ const u8 *idx)
+{
+ if (idx == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS: Credential did not include "
+ "Network Index");
+ return -1;
+ }
+
+ wpa_printf(MSG_DEBUG, "WPS: Network Index: %d", *idx);
+
+ return 0;
+}
+
+
+static int wps_process_cred_ssid(struct wps_credential *cred, const u8 *ssid,
+ size_t ssid_len)
+{
+ if (ssid == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS: Credential did not include SSID");
+ return -1;
+ }
+
+ /* Remove zero-padding since some Registrar implementations seem to use
+ * hardcoded 32-octet length for this attribute */
+ while (ssid_len > 0 && ssid[ssid_len - 1] == 0)
+ ssid_len--;
+
+ wpa_hexdump_ascii(MSG_DEBUG, "WPS: SSID", ssid, ssid_len);
+ if (ssid_len <= sizeof(cred->ssid)) {
+ os_memcpy(cred->ssid, ssid, ssid_len);
+ cred->ssid_len = ssid_len;
+ }
+
+ return 0;
+}
+
+
+static int wps_process_cred_auth_type(struct wps_credential *cred,
+ const u8 *auth_type)
+{
+ if (auth_type == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS: Credential did not include "
+ "Authentication Type");
+ return -1;
+ }
+
+ cred->auth_type = WPA_GET_BE16(auth_type);
+ wpa_printf(MSG_DEBUG, "WPS: Authentication Type: 0x%x",
+ cred->auth_type);
+
+ return 0;
+}
+
+
+static int wps_process_cred_encr_type(struct wps_credential *cred,
+ const u8 *encr_type)
+{
+ if (encr_type == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS: Credential did not include "
+ "Encryption Type");
+ return -1;
+ }
+
+ cred->encr_type = WPA_GET_BE16(encr_type);
+ wpa_printf(MSG_DEBUG, "WPS: Encryption Type: 0x%x",
+ cred->encr_type);
+
+ return 0;
+}
+
+
+static int wps_process_cred_network_key_idx(struct wps_credential *cred,
+ const u8 *key_idx)
+{
+ if (key_idx == NULL)
+ return 0; /* optional attribute */
+
+ wpa_printf(MSG_DEBUG, "WPS: Network Key Index: %d", *key_idx);
+ cred->key_idx = *key_idx;
+
+ return 0;
+}
+
+
+static int wps_process_cred_network_key(struct wps_credential *cred,
+ const u8 *key, size_t key_len)
+{
+ if (key == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS: Credential did not include "
+ "Network Key");
+ if (cred->auth_type == WPS_WIFI_AUTH_OPEN &&
+ cred->encr_type == WPS_ENCR_NONE) {
+ wpa_printf(MSG_DEBUG, "WPS: Workaround - Allow "
+ "missing mandatory Network Key attribute "
+ "for open network");
+ return 0;
+ }
+ return -1;
+ }
+
+ wpa_hexdump_key(MSG_DEBUG, "WPS: Network Key", key, key_len);
+ if (key_len <= sizeof(cred->key)) {
+ os_memcpy(cred->key, key, key_len);
+ cred->key_len = key_len;
+ }
+
+ return 0;
+}
+
+
+static int wps_process_cred_mac_addr(struct wps_credential *cred,
+ const u8 *mac_addr)
+{
+ if (mac_addr == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS: Credential did not include "
+ "MAC Address");
+ return -1;
+ }
+
+ wpa_printf(MSG_DEBUG, "WPS: MAC Address " MACSTR, MAC2STR(mac_addr));
+ os_memcpy(cred->mac_addr, mac_addr, ETH_ALEN);
+
+ return 0;
+}
+
+
+static int wps_process_cred_eap_type(struct wps_credential *cred,
+ const u8 *eap_type, size_t eap_type_len)
+{
+ if (eap_type == NULL)
+ return 0; /* optional attribute */
+
+ wpa_hexdump(MSG_DEBUG, "WPS: EAP Type", eap_type, eap_type_len);
+
+ return 0;
+}
+
+
+static int wps_process_cred_eap_identity(struct wps_credential *cred,
+ const u8 *identity,
+ size_t identity_len)
+{
+ if (identity == NULL)
+ return 0; /* optional attribute */
+
+ wpa_hexdump_ascii(MSG_DEBUG, "WPS: EAP Identity",
+ identity, identity_len);
+
+ return 0;
+}
+
+
+static int wps_process_cred_key_prov_auto(struct wps_credential *cred,
+ const u8 *key_prov_auto)
+{
+ if (key_prov_auto == NULL)
+ return 0; /* optional attribute */
+
+ wpa_printf(MSG_DEBUG, "WPS: Key Provided Automatically: %d",
+ *key_prov_auto);
+
+ return 0;
+}
+
+
+static int wps_process_cred_802_1x_enabled(struct wps_credential *cred,
+ const u8 *dot1x_enabled)
+{
+ if (dot1x_enabled == NULL)
+ return 0; /* optional attribute */
+
+ wpa_printf(MSG_DEBUG, "WPS: 802.1X Enabled: %d", *dot1x_enabled);
+
+ return 0;
+}
+
+
+static int wps_process_cred_ap_channel(struct wps_credential *cred,
+ const u8 *ap_channel)
+{
+ if (ap_channel == NULL)
+ return 0; /* optional attribute */
+
+ cred->ap_channel = WPA_GET_BE16(ap_channel);
+ wpa_printf(MSG_DEBUG, "WPS: AP Channel: %u", cred->ap_channel);
+
+ return 0;
+}
+
+
+static int wps_workaround_cred_key(struct wps_credential *cred)
+{
+ if (cred->auth_type & (WPS_AUTH_WPAPSK | WPS_AUTH_WPA2PSK) &&
+ cred->key_len > 8 && cred->key_len < 64 &&
+ cred->key[cred->key_len - 1] == 0) {
+#ifdef CONFIG_WPS_STRICT
+ wpa_printf(MSG_INFO, "WPS: WPA/WPA2-Personal passphrase uses "
+ "forbidden NULL termination");
+ wpa_hexdump_ascii_key(MSG_INFO, "WPS: Network Key",
+ cred->key, cred->key_len);
+ return -1;
+#else /* CONFIG_WPS_STRICT */
+ /*
+ * A deployed external registrar is known to encode ASCII
+ * passphrases incorrectly. Remove the extra NULL termination
+ * to fix the encoding.
+ */
+ wpa_printf(MSG_DEBUG, "WPS: Workaround - remove NULL "
+ "termination from ASCII passphrase");
+ cred->key_len--;
+#endif /* CONFIG_WPS_STRICT */
+ }
+ return 0;
+}
+
+
+int wps_process_cred(struct wps_parse_attr *attr,
+ struct wps_credential *cred)
+{
+ wpa_printf(MSG_DEBUG, "WPS: Process Credential");
+
+ /* TODO: support multiple Network Keys */
+ if (wps_process_cred_network_idx(cred, attr->network_idx) ||
+ wps_process_cred_ssid(cred, attr->ssid, attr->ssid_len) ||
+ wps_process_cred_auth_type(cred, attr->auth_type) ||
+ wps_process_cred_encr_type(cred, attr->encr_type) ||
+ wps_process_cred_network_key_idx(cred, attr->network_key_idx) ||
+ wps_process_cred_network_key(cred, attr->network_key,
+ attr->network_key_len) ||
+ wps_process_cred_mac_addr(cred, attr->mac_addr) ||
+ wps_process_cred_eap_type(cred, attr->eap_type,
+ attr->eap_type_len) ||
+ wps_process_cred_eap_identity(cred, attr->eap_identity,
+ attr->eap_identity_len) ||
+ wps_process_cred_key_prov_auto(cred, attr->key_prov_auto) ||
+ wps_process_cred_802_1x_enabled(cred, attr->dot1x_enabled) ||
+ wps_process_cred_ap_channel(cred, attr->ap_channel))
+ return -1;
+
+ return wps_workaround_cred_key(cred);
+}
+
+
+int wps_process_ap_settings(struct wps_parse_attr *attr,
+ struct wps_credential *cred)
+{
+ wpa_printf(MSG_DEBUG, "WPS: Processing AP Settings");
+ os_memset(cred, 0, sizeof(*cred));
+ /* TODO: optional attributes New Password and Device Password ID */
+ if (wps_process_cred_ssid(cred, attr->ssid, attr->ssid_len) ||
+ wps_process_cred_auth_type(cred, attr->auth_type) ||
+ wps_process_cred_encr_type(cred, attr->encr_type) ||
+ wps_process_cred_network_key_idx(cred, attr->network_key_idx) ||
+ wps_process_cred_network_key(cred, attr->network_key,
+ attr->network_key_len) ||
+ wps_process_cred_mac_addr(cred, attr->mac_addr))
+ return -1;
+
+ return wps_workaround_cred_key(cred);
+}
\ No newline at end of file
--- /dev/null
+/*
+ * Wi-Fi Protected Setup - common functionality
+ * Copyright (c) 2008-2012, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+#include <string.h>
+
+#include "wpa/includes.h"
+#include "wpa/common.h"
+
+#include "crypto/aes_wrap.h"
+#include "crypto/crypto.h"
+#include "crypto/dh_group5.h"
+#include "crypto/sha1.h"
+#include "crypto/sha256.h"
+#include "crypto/random.h"
+
+#include "wps/wps_i.h"
+
+void wps_kdf(const u8 *key, const u8 *label_prefix, size_t label_prefix_len,
+ const char *label, u8 *res, size_t res_len)
+{
+ u8 i_buf[4], key_bits[4];
+ const u8 *addr[4];
+ size_t len[4];
+ int i, iter;
+ u8 hash[SHA256_MAC_LEN], *opos;
+ size_t left;
+
+ WPA_PUT_BE32(key_bits, res_len * 8);
+
+ addr[0] = i_buf;
+ len[0] = sizeof(i_buf);
+ addr[1] = label_prefix;
+ len[1] = label_prefix_len;
+ addr[2] = (const u8 *) label;
+ len[2] = os_strlen(label);
+ addr[3] = key_bits;
+ len[3] = sizeof(key_bits);
+
+ iter = (res_len + SHA256_MAC_LEN - 1) / SHA256_MAC_LEN;
+ opos = res;
+ left = res_len;
+
+ for (i = 1; i <= iter; i++) {
+ WPA_PUT_BE32(i_buf, i);
+ if (wps_crypto_funcs.hmac_sha256_vector) {
+ wps_crypto_funcs.hmac_sha256_vector(key, SHA256_MAC_LEN, 4, addr, (int *)len, hash);
+ } else {
+ wpa_printf(MSG_ERROR, "In function %s, fail to reigster hmac sha256 vector function!\r\n", __FUNCTION__);
+ return ;
+ }
+ if (i < iter) {
+ os_memcpy(opos, hash, SHA256_MAC_LEN);
+ opos += SHA256_MAC_LEN;
+ left -= SHA256_MAC_LEN;
+ } else
+ os_memcpy(opos, hash, left);
+ }
+}
+
+
+int wps_derive_keys(struct wps_data *wps)
+{
+ struct wpabuf *pubkey, *dh_shared;
+ u8 dhkey[SHA256_MAC_LEN], kdk[SHA256_MAC_LEN];
+ const u8 *addr[3];
+ size_t len[3];
+ u8 keys[WPS_AUTHKEY_LEN + WPS_KEYWRAPKEY_LEN + WPS_EMSK_LEN];
+
+ if (wps->dh_privkey == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS: Own DH private key not available");
+ return -1;
+ }
+
+ pubkey = wps->registrar ? wps->dh_pubkey_e : wps->dh_pubkey_r;
+ if (pubkey == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS: Peer DH public key not available");
+ return -1;
+ }
+
+ wpa_hexdump_buf_key(MSG_DEBUG, "WPS: DH Private Key", wps->dh_privkey);
+ wpa_hexdump_buf(MSG_DEBUG, "WPS: DH peer Public Key", pubkey);
+ dh_shared = dh5_derive_shared(wps->dh_ctx, pubkey, wps->dh_privkey);
+ dh5_free(wps->dh_ctx);
+ wps->dh_ctx = NULL;
+ dh_shared = wpabuf_zeropad(dh_shared, 192);
+ if (dh_shared == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS: Failed to derive DH shared key");
+ return -1;
+ }
+
+ /* Own DH private key is not needed anymore */
+/*
+ * due to the public key calculated when wps start, it will not calculate anymore even when we build M1 message, also calculate the key need take a long time
+ * which would cause WPS fail, so we clean the key after WPS finished .
+ */
+#ifndef ESP32_WORKAROUND
+ wpabuf_free(wps->dh_privkey);
+ wps->dh_privkey = NULL;
+#endif //ESP32_WORKAROUND
+
+ wpa_hexdump_buf_key(MSG_DEBUG, "WPS: DH shared key", dh_shared);
+
+ /* DHKey = SHA-256(g^AB mod p) */
+ addr[0] = wpabuf_head(dh_shared);
+ len[0] = wpabuf_len(dh_shared);
+
+ if (wps_crypto_funcs.sha256_vector) {
+ wps_crypto_funcs.sha256_vector(1, addr, (int *)len, dhkey);
+ } else {
+ wpa_printf(MSG_ERROR, "In function %s, Fail to register sha256 vector function!\r\n", __FUNCTION__);
+ return -1;
+ }
+ wpa_hexdump_key(MSG_DEBUG, "WPS: DHKey", dhkey, sizeof(dhkey));
+ wpabuf_free(dh_shared);
+
+ /* KDK = HMAC-SHA-256_DHKey(N1 || EnrolleeMAC || N2) */
+ addr[0] = wps->nonce_e;
+ len[0] = WPS_NONCE_LEN;
+ addr[1] = wps->mac_addr_e;
+ len[1] = ETH_ALEN;
+ addr[2] = wps->nonce_r;
+ len[2] = WPS_NONCE_LEN;
+ if (wps_crypto_funcs.hmac_sha256_vector) {
+ wps_crypto_funcs.hmac_sha256_vector(dhkey, sizeof(dhkey), 3, addr, (int *)len, kdk);
+ } else {
+ wpa_printf(MSG_ERROR, "In function %s, Fail to register hmac sha256 vector function!\r\n", __FUNCTION__);
+ return -1;
+ }
+ wpa_hexdump_key(MSG_DEBUG, "WPS: KDK", kdk, sizeof(kdk));
+
+ wps_kdf(kdk, NULL, 0, "Wi-Fi Easy and Secure Key Derivation",
+ keys, sizeof(keys));
+ os_memcpy(wps->authkey, keys, WPS_AUTHKEY_LEN);
+ os_memcpy(wps->keywrapkey, keys + WPS_AUTHKEY_LEN, WPS_KEYWRAPKEY_LEN);
+ os_memcpy(wps->emsk, keys + WPS_AUTHKEY_LEN + WPS_KEYWRAPKEY_LEN,
+ WPS_EMSK_LEN);
+
+ wpa_hexdump_key(MSG_DEBUG, "WPS: AuthKey",
+ wps->authkey, WPS_AUTHKEY_LEN);
+ wpa_hexdump_key(MSG_DEBUG, "WPS: KeyWrapKey",
+ wps->keywrapkey, WPS_KEYWRAPKEY_LEN);
+ wpa_hexdump_key(MSG_DEBUG, "WPS: EMSK", wps->emsk, WPS_EMSK_LEN);
+
+ return 0;
+}
+
+
+void wps_derive_psk(struct wps_data *wps, const u8 *dev_passwd,
+ size_t dev_passwd_len)
+{
+ u8 hash[SHA256_MAC_LEN];
+
+ if (wps_crypto_funcs.hmac_sha256) {
+ wps_crypto_funcs.hmac_sha256(wps->authkey, WPS_AUTHKEY_LEN, dev_passwd,
+ (dev_passwd_len + 1) / 2, hash);
+ } else {
+ wpa_printf(MSG_ERROR, "In function %s, fail to register hmac_sha256 function!\r\n", __FUNCTION__);
+ return ;
+ }
+ os_memcpy(wps->psk1, hash, WPS_PSK_LEN);
+ if (wps_crypto_funcs.hmac_sha256) {
+ wps_crypto_funcs.hmac_sha256(wps->authkey, WPS_AUTHKEY_LEN,
+ dev_passwd + (dev_passwd_len + 1) / 2,
+ dev_passwd_len / 2, hash);
+ } else {
+ wpa_printf(MSG_ERROR, "In function %s, fail to register hmac_sha256 function!\r\n", __FUNCTION__);
+ return ;
+ }
+ os_memcpy(wps->psk2, hash, WPS_PSK_LEN);
+
+ wpa_hexdump_ascii_key(MSG_DEBUG, "WPS: Device Password",
+ dev_passwd, dev_passwd_len);
+ wpa_hexdump_key(MSG_DEBUG, "WPS: PSK1", wps->psk1, WPS_PSK_LEN);
+ wpa_hexdump_key(MSG_DEBUG, "WPS: PSK2", wps->psk2, WPS_PSK_LEN);
+}
+
+
+struct wpabuf * wps_decrypt_encr_settings(struct wps_data *wps, const u8 *encr,
+ size_t encr_len)
+{
+ struct wpabuf *decrypted;
+ const size_t block_size = 16;
+ size_t i;
+ u8 pad;
+ const u8 *pos;
+
+ /* AES-128-CBC */
+ if (encr == NULL || encr_len < 2 * block_size || encr_len % block_size)
+ {
+ wpa_printf(MSG_DEBUG, "WPS: No Encrypted Settings received");
+ return NULL;
+ }
+
+ decrypted = wpabuf_alloc(encr_len - block_size);
+ if (decrypted == NULL)
+ return NULL;
+
+ wpa_hexdump(MSG_MSGDUMP, "WPS: Encrypted Settings", encr, encr_len);
+ wpabuf_put_data(decrypted, encr + block_size, encr_len - block_size);
+ wpa_printf(MSG_DEBUG, "WPS: AES Decrypt setting");
+ if (wps_crypto_funcs.aes_128_decrypt) {
+ if (wps_crypto_funcs.aes_128_decrypt(wps->keywrapkey, encr, wpabuf_mhead(decrypted),
+ wpabuf_len(decrypted))) {
+ wpabuf_free(decrypted);
+ return NULL;
+ }
+ } else {
+ wpa_printf(MSG_ERROR, "In function %s, fail to register aes 128 decrypt function!\r\n", __FUNCTION__);
+ return NULL;
+ }
+
+ wpa_hexdump_buf_key(MSG_MSGDUMP, "WPS: Decrypted Encrypted Settings",
+ decrypted);
+
+ pos = wpabuf_head_u8(decrypted) + wpabuf_len(decrypted) - 1;
+ pad = *pos;
+ if (pad > wpabuf_len(decrypted)) {
+ wpa_printf(MSG_DEBUG, "WPS: Invalid PKCS#5 v2.0 pad value");
+ wpabuf_free(decrypted);
+ return NULL;
+ }
+ for (i = 0; i < pad; i++) {
+ if (*pos-- != pad) {
+ wpa_printf(MSG_DEBUG, "WPS: Invalid PKCS#5 v2.0 pad "
+ "string");
+ wpabuf_free(decrypted);
+ return NULL;
+ }
+ }
+ decrypted->used -= pad;
+
+ return decrypted;
+}
+
+#ifdef CONFIG_WPS_PIN
+/**
+ * wps_pin_checksum - Compute PIN checksum
+ * @pin: Seven digit PIN (i.e., eight digit PIN without the checksum digit)
+ * Returns: Checksum digit
+ */
+unsigned int wps_pin_checksum(unsigned int pin)
+{
+ unsigned int accum = 0;
+ while (pin) {
+ accum += 3 * (pin % 10);
+ pin /= 10;
+ accum += pin % 10;
+ pin /= 10;
+ }
+
+ return (10 - accum % 10) % 10;
+}
+
+
+/**
+ * wps_pin_valid - Check whether a PIN has a valid checksum
+ * @pin: Eight digit PIN (i.e., including the checksum digit)
+ * Returns: 1 if checksum digit is valid, or 0 if not
+ */
+unsigned int wps_pin_valid(unsigned int pin)
+{
+ return wps_pin_checksum(pin / 10) == (pin % 10);
+}
+
+
+/**
+ * wps_generate_pin - Generate a random PIN
+ * Returns: Eight digit PIN (i.e., including the checksum digit)
+ */
+unsigned int wps_generate_pin(void)
+{
+ unsigned int val;
+
+ /* Generate seven random digits for the PIN */
+ if (random_get_bytes((unsigned char *) &val, sizeof(val)) < 0) {
+ return -1;
+ }
+ val %= 10000000;
+
+ /* Append checksum digit */
+ return val * 10 + wps_pin_checksum(val);
+}
+
+
+int wps_pin_str_valid(const char *pin)
+{
+ const char *p;
+ size_t len;
+
+ p = pin;
+ while (*p >= '0' && *p <= '9')
+ p++;
+ if (*p != '\0')
+ return 0;
+
+ len = p - pin;
+ return len == 4 || len == 8;
+}
+#endif
+
+void wps_fail_event(struct wps_context *wps, enum wps_msg_type msg,
+ u16 config_error, u16 error_indication)
+{
+ union wps_event_data *data;
+
+ data = (union wps_event_data *)os_zalloc(sizeof(union wps_event_data));
+ if (data == NULL)
+ return;
+
+ if (wps->event_cb == NULL) {
+ os_free(data);
+ return;
+ }
+
+ os_memset(data, 0, sizeof(union wps_event_data));
+ data->fail.msg = msg;
+ data->fail.config_error = config_error;
+ data->fail.error_indication = error_indication;
+ wps->event_cb(wps->cb_ctx, WPS_EV_FAIL, data);
+ os_free(data);
+}
+
+
+void wps_success_event(struct wps_context *wps)
+{
+ if (wps->event_cb == NULL)
+ return;
+
+ wps->event_cb(wps->cb_ctx, WPS_EV_SUCCESS, NULL);
+}
+
+
+void wps_pwd_auth_fail_event(struct wps_context *wps, int enrollee, int part)
+{
+ union wps_event_data *data;
+
+ data = (union wps_event_data *)os_zalloc(sizeof(union wps_event_data));
+ if (data == NULL)
+ return;
+
+ if (wps->event_cb == NULL) {
+ os_free(data);
+ return;
+ }
+
+ os_memset(data, 0, sizeof(union wps_event_data));
+ data->pwd_auth_fail.enrollee = enrollee;
+ data->pwd_auth_fail.part = part;
+ wps->event_cb(wps->cb_ctx, WPS_EV_PWD_AUTH_FAIL, data);
+ os_free(data);
+}
+
+
+void wps_pbc_overlap_event(struct wps_context *wps)
+{
+ if (wps->event_cb == NULL)
+ return;
+
+ wps->event_cb(wps->cb_ctx, WPS_EV_PBC_OVERLAP, NULL);
+}
+
+
+void wps_pbc_timeout_event(struct wps_context *wps)
+{
+ if (wps->event_cb == NULL)
+ return;
+
+ wps->event_cb(wps->cb_ctx, WPS_EV_PBC_TIMEOUT, NULL);
+}
+
+
+#ifdef CONFIG_WPS_OOB
+
+struct wpabuf * wps_get_oob_cred(struct wps_context *wps)
+{
+ struct wps_data *data;
+ struct wpabuf *plain;
+
+ data = (struct wps_data *)os_zalloc(sizeof(struct wps_data));
+ if (data == NULL)
+ return NULL;
+
+ plain = wpabuf_alloc(500);
+ if (plain == NULL) {
+ os_free(data);
+ wpa_printf(MSG_ERROR, "WPS: Failed to allocate memory for OOB "
+ "credential");
+ return NULL;
+ }
+
+ os_memset(data, 0, sizeof(struct wps_data));
+ data->wps = wps;
+ data->auth_type = wps->auth_types;
+ data->encr_type = wps->encr_types;
+ if (wps_build_version(plain) ||
+ wps_build_cred(data, plain) ||
+ wps_build_wfa_ext(plain, 0, NULL, 0)) {
+ wpabuf_free(plain);
+ os_free(data);
+ return NULL;
+ }
+
+ os_free(data);
+ return plain;
+}
+
+#ifdef CONFIG_WPS_NFC
+
+struct wpabuf * wps_build_nfc_pw_token(u16 dev_pw_id,
+ const struct wpabuf *pubkey,
+ const struct wpabuf *dev_pw)
+{
+ struct wpabuf *data;
+
+ data = wpabuf_alloc(200);
+ if (data == NULL)
+ return NULL;
+
+ if (wps_build_version(data) ||
+ wps_build_oob_dev_pw(data, dev_pw_id, pubkey,
+ wpabuf_head(dev_pw), wpabuf_len(dev_pw)) ||
+ wps_build_wfa_ext(data, 0, NULL, 0)) {
+ wpa_printf(MSG_ERROR, "WPS: Failed to build NFC password "
+ "token");
+ wpabuf_free(data);
+ return NULL;
+ }
+
+ return data;
+}
+
+#endif
+
+int wps_oob_use_cred(struct wps_context *wps, struct wps_parse_attr *attr)
+{
+ struct wpabuf msg;
+ size_t i;
+
+ for (i = 0; i < attr->num_cred; i++) {
+ struct wps_credential local_cred;
+ struct wps_parse_attr cattr;
+
+ os_memset(&local_cred, 0, sizeof(local_cred));
+ wpabuf_set(&msg, attr->cred[i], attr->cred_len[i]);
+ if (wps_parse_msg(&msg, &cattr) < 0 ||
+ wps_process_cred(&cattr, &local_cred)) {
+ wpa_printf(MSG_ERROR, "WPS: Failed to parse OOB "
+ "credential");
+ return -1;
+ }
+ wps->cred_cb(wps->cb_ctx, &local_cred);
+ }
+
+ return 0;
+}
+
+
+#endif /* CONFIG_WPS_OOB */
+
+
+int wps_dev_type_str2bin(const char *str, u8 dev_type[WPS_DEV_TYPE_LEN])
+{
+ const char *pos;
+
+ /* <categ>-<OUI>-<subcateg> */
+ WPA_PUT_BE16(dev_type, atoi(str));
+ pos = (char *)os_strchr(str, '-');
+ if (pos == NULL)
+ return -1;
+ pos++;
+ if (hexstr2bin(pos, &dev_type[2], 4))
+ return -1;
+ pos = (char *)os_strchr(pos, '-');
+ if (pos == NULL)
+ return -1;
+ pos++;
+ WPA_PUT_BE16(&dev_type[6], atoi(pos));
+
+
+ return 0;
+}
+
+
+char * wps_dev_type_bin2str(const u8 dev_type[WPS_DEV_TYPE_LEN], char *buf,
+ size_t buf_len)
+{
+ int ret;
+
+ ret = snprintf(buf, buf_len, "%u-%08X-%u",
+ WPA_GET_BE16(dev_type), WPA_GET_BE32(&dev_type[2]),
+ WPA_GET_BE16(&dev_type[6]));
+ if (ret < 0 || (unsigned int) ret >= buf_len)
+ return NULL;
+
+ return buf;
+}
+
+
+void uuid_gen_mac_addr(const u8 *mac_addr, u8 *uuid)
+{
+ const u8 *addr[2];
+ size_t len[2];
+ u8 hash[SHA1_MAC_LEN];
+ u8 nsid[16] = {
+ 0x52, 0x64, 0x80, 0xf8,
+ 0xc9, 0x9b,
+ 0x4b, 0xe5,
+ 0xa6, 0x55,
+ 0x58, 0xed, 0x5f, 0x5d, 0x60, 0x84
+ };
+
+ addr[0] = nsid;
+ len[0] = sizeof(nsid);
+ addr[1] = mac_addr;
+ len[1] = 6;
+ sha1_vector(2, addr, len, hash);
+ os_memcpy(uuid, hash, 16);
+
+ /* Version: 5 = named-based version using SHA-1 */
+ uuid[6] = (5 << 4) | (uuid[6] & 0x0f);
+
+ /* Variant specified in RFC 4122 */
+ uuid[8] = 0x80 | (uuid[8] & 0x3f);
+}
+
+
+u16 wps_config_methods_str2bin(const char *str)
+{
+ u16 methods = 0;
+
+ if (str == NULL) {
+ /* Default to enabling methods based on build configuration */
+ methods |= WPS_CONFIG_DISPLAY | WPS_CONFIG_KEYPAD;
+#ifdef CONFIG_WPS2
+ methods |= WPS_CONFIG_VIRT_DISPLAY;
+#endif /* CONFIG_WPS2 */
+#ifdef CONFIG_WPS_NFC
+ methods |= WPS_CONFIG_NFC_INTERFACE;
+#endif /* CONFIG_WPS_NFC */
+ } else {
+ if (os_strstr(str, "ethernet"))
+ methods |= WPS_CONFIG_ETHERNET;
+ if (os_strstr(str, "label"))
+ methods |= WPS_CONFIG_LABEL;
+ if (os_strstr(str, "display"))
+ methods |= WPS_CONFIG_DISPLAY;
+ if (os_strstr(str, "ext_nfc_token"))
+ methods |= WPS_CONFIG_EXT_NFC_TOKEN;
+ if (os_strstr(str, "int_nfc_token"))
+ methods |= WPS_CONFIG_INT_NFC_TOKEN;
+ if (os_strstr(str, "nfc_interface"))
+ methods |= WPS_CONFIG_NFC_INTERFACE;
+ if (os_strstr(str, "push_button"))
+ methods |= WPS_CONFIG_PUSHBUTTON;
+ if (os_strstr(str, "keypad"))
+ methods |= WPS_CONFIG_KEYPAD;
+#ifdef CONFIG_WPS2
+ if (os_strstr(str, "virtual_display"))
+ methods |= WPS_CONFIG_VIRT_DISPLAY;
+ if (os_strstr(str, "physical_display"))
+ methods |= WPS_CONFIG_PHY_DISPLAY;
+ if (os_strstr(str, "virtual_push_button"))
+ methods |= WPS_CONFIG_VIRT_PUSHBUTTON;
+ if (os_strstr(str, "physical_push_button"))
+ methods |= WPS_CONFIG_PHY_PUSHBUTTON;
+#endif /* CONFIG_WPS2 */
+ }
+
+ return methods;
+}
+
+
+struct wpabuf * wps_build_wsc_ack(struct wps_data *wps)
+{
+ struct wpabuf *msg;
+
+ wpa_printf(MSG_DEBUG, "WPS: Building Message WSC_ACK");
+
+ msg = wpabuf_alloc(1000);
+ if (msg == NULL)
+ return NULL;
+
+ if (wps_build_version(msg) ||
+ wps_build_msg_type(msg, WPS_WSC_ACK) ||
+ wps_build_enrollee_nonce(wps, msg) ||
+ wps_build_registrar_nonce(wps, msg) ||
+ wps_build_wfa_ext(msg, 0, NULL, 0)) {
+ wpabuf_free(msg);
+ return NULL;
+ }
+
+ return msg;
+}
+
+
+struct wpabuf * wps_build_wsc_nack(struct wps_data *wps)
+{
+ struct wpabuf *msg;
+
+ wpa_printf(MSG_DEBUG, "WPS: Building Message WSC_NACK");
+
+ msg = wpabuf_alloc(1000);
+ if (msg == NULL)
+ return NULL;
+
+ if (wps_build_version(msg) ||
+ wps_build_msg_type(msg, WPS_WSC_NACK) ||
+ wps_build_enrollee_nonce(wps, msg) ||
+ wps_build_registrar_nonce(wps, msg) ||
+ wps_build_config_error(msg, wps->config_error) ||
+ wps_build_wfa_ext(msg, 0, NULL, 0)) {
+ wpabuf_free(msg);
+ return NULL;
+ }
+
+ return msg;
+}
+
+
+#ifdef CONFIG_WPS_NFC
+struct wpabuf * wps_nfc_token_gen(int ndef, int *id, struct wpabuf **pubkey,
+ struct wpabuf **privkey,
+ struct wpabuf **dev_pw)
+{
+ struct wpabuf *priv = NULL, *pub = NULL, *pw, *ret;
+ void *dh_ctx;
+ u16 val;
+
+ pw = wpabuf_alloc(WPS_OOB_DEVICE_PASSWORD_LEN);
+ if (pw == NULL)
+ return NULL;
+
+ if (random_get_bytes(wpabuf_put(pw, WPS_OOB_DEVICE_PASSWORD_LEN),
+ WPS_OOB_DEVICE_PASSWORD_LEN) ||
+ random_get_bytes((u8 *) &val, sizeof(val))) {
+ wpabuf_free(pw);
+ return NULL;
+ }
+
+ dh_ctx = dh5_init(&priv, &pub);
+ if (dh_ctx == NULL) {
+ wpabuf_free(pw);
+ return NULL;
+ }
+ dh5_free(dh_ctx);
+
+ *id = 0x10 + val % 0xfff0;
+ wpabuf_free(*pubkey);
+ *pubkey = pub;
+ wpabuf_free(*privkey);
+ *privkey = priv;
+ wpabuf_free(*dev_pw);
+ *dev_pw = pw;
+
+ ret = wps_build_nfc_pw_token(*id, *pubkey, *dev_pw);
+ if (ndef && ret) {
+ struct wpabuf *tmp;
+ tmp = ndef_build_wifi(ret);
+ wpabuf_free(ret);
+ if (tmp == NULL)
+ return NULL;
+ ret = tmp;
+ }
+
+ return ret;
+}
+#endif /* CONFIG_WPS_NFC */
--- /dev/null
+/*
+ * Wi-Fi Protected Setup - device attributes
+ * Copyright (c) 2008, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+#include "wpa/includes.h"
+#include "wpa/common.h"
+
+#include "wps/wps_i.h"
+#include "wps/wps_dev_attr.h"
+
+int wps_build_manufacturer(struct wps_device_data *dev, struct wpabuf *msg)
+{
+ size_t len;
+ wpa_printf(MSG_DEBUG, "WPS: * Manufacturer");
+ wpabuf_put_be16(msg, ATTR_MANUFACTURER);
+ len = dev->manufacturer ? os_strlen(dev->manufacturer) : 0;
+#ifndef CONFIG_WPS_STRICT
+ if (len == 0) {
+ /*
+ * Some deployed WPS implementations fail to parse zero-length
+ * attributes. As a workaround, send a space character if the
+ * device attribute string is empty.
+ */
+ wpabuf_put_be16(msg, 1);
+ wpabuf_put_u8(msg, ' ');
+ return 0;
+ }
+#endif /* CONFIG_WPS_STRICT */
+ wpabuf_put_be16(msg, len);
+ wpabuf_put_data(msg, dev->manufacturer, len);
+ return 0;
+}
+
+
+int wps_build_model_name(struct wps_device_data *dev, struct wpabuf *msg)
+{
+ size_t len;
+ wpa_printf(MSG_DEBUG, "WPS: * Model Name");
+ wpabuf_put_be16(msg, ATTR_MODEL_NAME);
+ len = dev->model_name ? os_strlen(dev->model_name) : 0;
+#ifndef CONFIG_WPS_STRICT
+ if (len == 0) {
+ /*
+ * Some deployed WPS implementations fail to parse zero-length
+ * attributes. As a workaround, send a space character if the
+ * device attribute string is empty.
+ */
+ wpabuf_put_be16(msg, 1);
+ wpabuf_put_u8(msg, ' ');
+ return 0;
+ }
+#endif /* CONFIG_WPS_STRICT */
+ wpabuf_put_be16(msg, len);
+ wpabuf_put_data(msg, dev->model_name, len);
+ return 0;
+}
+
+
+int wps_build_model_number(struct wps_device_data *dev, struct wpabuf *msg)
+{
+ size_t len;
+ wpa_printf(MSG_DEBUG, "WPS: * Model Number");
+ wpabuf_put_be16(msg, ATTR_MODEL_NUMBER);
+ len = dev->model_number ? os_strlen(dev->model_number) : 0;
+#ifndef CONFIG_WPS_STRICT
+ if (len == 0) {
+ /*
+ * Some deployed WPS implementations fail to parse zero-length
+ * attributes. As a workaround, send a space character if the
+ * device attribute string is empty.
+ */
+ wpabuf_put_be16(msg, 1);
+ wpabuf_put_u8(msg, ' ');
+ return 0;
+ }
+#endif /* CONFIG_WPS_STRICT */
+ wpabuf_put_be16(msg, len);
+ wpabuf_put_data(msg, dev->model_number, len);
+ return 0;
+}
+
+
+static int wps_build_serial_number(struct wps_device_data *dev,
+ struct wpabuf *msg)
+{
+ size_t len;
+ wpa_printf(MSG_DEBUG, "WPS: * Serial Number");
+ wpabuf_put_be16(msg, ATTR_SERIAL_NUMBER);
+ len = dev->serial_number ? os_strlen(dev->serial_number) : 0;
+#ifndef CONFIG_WPS_STRICT
+ if (len == 0) {
+ /*
+ * Some deployed WPS implementations fail to parse zero-length
+ * attributes. As a workaround, send a space character if the
+ * device attribute string is empty.
+ */
+ wpabuf_put_be16(msg, 1);
+ wpabuf_put_u8(msg, ' ');
+ return 0;
+ }
+#endif /* CONFIG_WPS_STRICT */
+ wpabuf_put_be16(msg, len);
+ wpabuf_put_data(msg, dev->serial_number, len);
+ return 0;
+}
+
+
+int wps_build_primary_dev_type(struct wps_device_data *dev, struct wpabuf *msg)
+{
+ wpa_printf(MSG_DEBUG, "WPS: * Primary Device Type");
+ wpabuf_put_be16(msg, ATTR_PRIMARY_DEV_TYPE);
+ wpabuf_put_be16(msg, WPS_DEV_TYPE_LEN);
+ wpabuf_put_data(msg, dev->pri_dev_type, WPS_DEV_TYPE_LEN);
+ return 0;
+}
+
+
+int wps_build_secondary_dev_type(struct wps_device_data *dev,
+ struct wpabuf *msg)
+{
+ if (!dev->num_sec_dev_types)
+ return 0;
+
+ wpa_printf(MSG_DEBUG, "WPS: * Secondary Device Type");
+ wpabuf_put_be16(msg, ATTR_SECONDARY_DEV_TYPE_LIST);
+ wpabuf_put_be16(msg, WPS_DEV_TYPE_LEN * dev->num_sec_dev_types);
+ wpabuf_put_data(msg, dev->sec_dev_type,
+ WPS_DEV_TYPE_LEN * dev->num_sec_dev_types);
+
+ return 0;
+}
+
+
+int wps_build_req_dev_type(struct wps_device_data *dev, struct wpabuf *msg,
+ unsigned int num_req_dev_types,
+ const u8 *req_dev_types)
+{
+ unsigned int i;
+
+ for (i = 0; i < num_req_dev_types; i++) {
+ wpa_hexdump(MSG_DEBUG, "WPS: * Requested Device Type",
+ req_dev_types + i * WPS_DEV_TYPE_LEN,
+ WPS_DEV_TYPE_LEN);
+ wpabuf_put_be16(msg, ATTR_REQUESTED_DEV_TYPE);
+ wpabuf_put_be16(msg, WPS_DEV_TYPE_LEN);
+ wpabuf_put_data(msg, req_dev_types + i * WPS_DEV_TYPE_LEN,
+ WPS_DEV_TYPE_LEN);
+ }
+
+ return 0;
+}
+
+
+int wps_build_dev_name(struct wps_device_data *dev, struct wpabuf *msg)
+{
+ size_t len;
+ wpa_printf(MSG_DEBUG, "WPS: * Device Name");
+ wpabuf_put_be16(msg, ATTR_DEV_NAME);
+ len = dev->device_name ? os_strlen(dev->device_name) : 0;
+#ifndef CONFIG_WPS_STRICT
+ if (len == 0) {
+ /*
+ * Some deployed WPS implementations fail to parse zero-length
+ * attributes. As a workaround, send a space character if the
+ * device attribute string is empty.
+ */
+ wpabuf_put_be16(msg, 1);
+ wpabuf_put_u8(msg, ' ');
+ return 0;
+ }
+#endif /* CONFIG_WPS_STRICT */
+ wpabuf_put_be16(msg, len);
+ wpabuf_put_data(msg, dev->device_name, len);
+ return 0;
+}
+
+
+int wps_build_device_attrs(struct wps_device_data *dev, struct wpabuf *msg)
+{
+ if (wps_build_manufacturer(dev, msg) ||
+ wps_build_model_name(dev, msg) ||
+ wps_build_model_number(dev, msg) ||
+ wps_build_serial_number(dev, msg) ||
+ wps_build_primary_dev_type(dev, msg) ||
+ wps_build_dev_name(dev, msg))
+ return -1;
+ return 0;
+}
+
+
+int wps_build_os_version(struct wps_device_data *dev, struct wpabuf *msg)
+{
+ wpa_printf(MSG_DEBUG, "WPS: * OS Version");
+ wpabuf_put_be16(msg, ATTR_OS_VERSION);
+ wpabuf_put_be16(msg, 4);
+ wpabuf_put_be32(msg, 0x80000000 | dev->os_version);
+ return 0;
+}
+
+
+int wps_build_vendor_ext_m1(struct wps_device_data *dev, struct wpabuf *msg)
+{
+ if (dev->vendor_ext_m1 != NULL) {
+ wpa_hexdump(MSG_DEBUG, "WPS: * Vendor Extension M1",
+ wpabuf_head_u8(dev->vendor_ext_m1),
+ wpabuf_len(dev->vendor_ext_m1));
+ wpabuf_put_be16(msg, ATTR_VENDOR_EXT);
+ wpabuf_put_be16(msg, wpabuf_len(dev->vendor_ext_m1));
+ wpabuf_put_buf(msg, dev->vendor_ext_m1);
+ }
+ return 0;
+}
+
+
+int wps_build_rf_bands(struct wps_device_data *dev, struct wpabuf *msg)
+{
+ wpa_printf(MSG_DEBUG, "WPS: * RF Bands (%x)", dev->rf_bands);
+ wpabuf_put_be16(msg, ATTR_RF_BANDS);
+ wpabuf_put_be16(msg, 1);
+ wpabuf_put_u8(msg, dev->rf_bands);
+ return 0;
+}
+
+
+int wps_build_vendor_ext(struct wps_device_data *dev, struct wpabuf *msg)
+{
+ int i;
+
+ for (i = 0; i < MAX_WPS_VENDOR_EXTENSIONS; i++) {
+ if (dev->vendor_ext[i] == NULL)
+ continue;
+ wpa_hexdump(MSG_DEBUG, "WPS: * Vendor Extension",
+ wpabuf_head_u8(dev->vendor_ext[i]),
+ wpabuf_len(dev->vendor_ext[i]));
+ wpabuf_put_be16(msg, ATTR_VENDOR_EXT);
+ wpabuf_put_be16(msg, wpabuf_len(dev->vendor_ext[i]));
+ wpabuf_put_buf(msg, dev->vendor_ext[i]);
+ }
+
+ return 0;
+}
+
+
+static int wps_process_manufacturer(struct wps_device_data *dev, const u8 *str,
+ size_t str_len)
+{
+ if (str == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS: No Manufacturer received");
+ return -1;
+ }
+
+ wpa_hexdump_ascii(MSG_DEBUG, "WPS: Manufacturer", str, str_len);
+
+ os_free(dev->manufacturer);
+ dev->manufacturer = (char *)os_malloc(str_len + 1);
+ if (dev->manufacturer == NULL)
+ return -1;
+ os_memcpy(dev->manufacturer, str, str_len);
+ dev->manufacturer[str_len] = '\0';
+
+ return 0;
+}
+
+
+static int wps_process_model_name(struct wps_device_data *dev, const u8 *str,
+ size_t str_len)
+{
+ if (str == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS: No Model Name received");
+ return -1;
+ }
+
+ wpa_hexdump_ascii(MSG_DEBUG, "WPS: Model Name", str, str_len);
+
+ os_free(dev->model_name);
+ dev->model_name = (char *)os_malloc(str_len + 1);
+ if (dev->model_name == NULL)
+ return -1;
+ os_memcpy(dev->model_name, str, str_len);
+ dev->model_name[str_len] = '\0';
+
+ return 0;
+}
+
+
+static int wps_process_model_number(struct wps_device_data *dev, const u8 *str,
+ size_t str_len)
+{
+ if (str == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS: No Model Number received");
+ return -1;
+ }
+
+ wpa_hexdump_ascii(MSG_DEBUG, "WPS: Model Number", str, str_len);
+
+ os_free(dev->model_number);
+ dev->model_number = (char *)os_malloc(str_len + 1);
+ if (dev->model_number == NULL)
+ return -1;
+ os_memcpy(dev->model_number, str, str_len);
+ dev->model_number[str_len] = '\0';
+
+ return 0;
+}
+
+
+static int wps_process_serial_number(struct wps_device_data *dev,
+ const u8 *str, size_t str_len)
+{
+ if (str == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS: No Serial Number received");
+ return -1;
+ }
+
+ wpa_hexdump_ascii(MSG_DEBUG, "WPS: Serial Number", str, str_len);
+
+ os_free(dev->serial_number);
+ dev->serial_number = (char *)os_malloc(str_len + 1);
+ if (dev->serial_number == NULL)
+ return -1;
+ os_memcpy(dev->serial_number, str, str_len);
+ dev->serial_number[str_len] = '\0';
+
+ return 0;
+}
+
+
+static int wps_process_dev_name(struct wps_device_data *dev, const u8 *str,
+ size_t str_len)
+{
+ if (str == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS: No Device Name received");
+ return -1;
+ }
+
+ wpa_hexdump_ascii(MSG_DEBUG, "WPS: Device Name", str, str_len);
+
+ os_free(dev->device_name);
+ dev->device_name = (char *)os_malloc(str_len + 1);
+ if (dev->device_name == NULL)
+ return -1;
+ os_memcpy(dev->device_name, str, str_len);
+ dev->device_name[str_len] = '\0';
+
+ return 0;
+}
+
+
+static int wps_process_primary_dev_type(struct wps_device_data *dev,
+ const u8 *dev_type)
+{
+#if 0
+#ifndef CONFIG_NO_STDOUT_DEBUG
+ char devtype[WPS_DEV_TYPE_BUFSIZE];
+#endif /* CONFIG_NO_STDOUT_DEBUG */
+#endif
+ if (dev_type == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS: No Primary Device Type received");
+ return -1;
+ }
+
+ os_memcpy(dev->pri_dev_type, dev_type, WPS_DEV_TYPE_LEN);
+ //wpa_printf(MSG_DEBUG, "WPS: Primary Device Type: %s",
+ // wps_dev_type_bin2str(dev->pri_dev_type, devtype,
+ // sizeof(devtype)));
+
+ return 0;
+}
+
+
+int wps_process_device_attrs(struct wps_device_data *dev,
+ struct wps_parse_attr *attr)
+{
+ if (wps_process_manufacturer(dev, attr->manufacturer,
+ attr->manufacturer_len) ||
+ wps_process_model_name(dev, attr->model_name,
+ attr->model_name_len) ||
+ wps_process_model_number(dev, attr->model_number,
+ attr->model_number_len) ||
+ wps_process_serial_number(dev, attr->serial_number,
+ attr->serial_number_len) ||
+ wps_process_primary_dev_type(dev, attr->primary_dev_type) ||
+ wps_process_dev_name(dev, attr->dev_name, attr->dev_name_len))
+ return -1;
+ return 0;
+}
+
+
+int wps_process_os_version(struct wps_device_data *dev, const u8 *ver)
+{
+ if (ver == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS: No OS Version received");
+ return -1;
+ }
+
+ dev->os_version = WPA_GET_BE32(ver);
+ wpa_printf(MSG_DEBUG, "WPS: OS Version %08x", dev->os_version);
+
+ return 0;
+}
+
+
+int wps_process_rf_bands(struct wps_device_data *dev, const u8 *bands)
+{
+ if (bands == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS: No RF Bands received");
+ return -1;
+ }
+
+ dev->rf_bands = *bands;
+ wpa_printf(MSG_DEBUG, "WPS: Enrollee RF Bands 0x%x", dev->rf_bands);
+
+ return 0;
+}
+
+
+void wps_device_data_dup(struct wps_device_data *dst,
+ const struct wps_device_data *src)
+{
+ if (src->device_name)
+ dst->device_name = os_strdup(src->device_name);
+ if (src->manufacturer)
+ dst->manufacturer = os_strdup(src->manufacturer);
+ if (src->model_name)
+ dst->model_name = os_strdup(src->model_name);
+ if (src->model_number)
+ dst->model_number = os_strdup(src->model_number);
+ if (src->serial_number)
+ dst->serial_number = os_strdup(src->serial_number);
+ os_memcpy(dst->pri_dev_type, src->pri_dev_type, WPS_DEV_TYPE_LEN);
+ dst->os_version = src->os_version;
+ dst->rf_bands = src->rf_bands;
+}
+
+
+void wps_device_data_free(struct wps_device_data *dev)
+{
+ os_free(dev->device_name);
+ dev->device_name = NULL;
+ os_free(dev->manufacturer);
+ dev->manufacturer = NULL;
+ os_free(dev->model_name);
+ dev->model_name = NULL;
+ os_free(dev->model_number);
+ dev->model_number = NULL;
+ os_free(dev->serial_number);
+ dev->serial_number = NULL;
+}
--- /dev/null
+/*
+ * Wi-Fi Protected Setup - Enrollee
+ * Copyright (c) 2008, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "rom/ets_sys.h"
+#include "wpa/includes.h"
+
+#include "wpa/common.h"
+#include "crypto/crypto.h"
+#include "crypto/sha256.h"
+#include "crypto/random.h"
+#include "wps/wps_i.h"
+#include "wps/wps.h"
+#include "wps/wps_dev_attr.h"
+
+#include "soc/dport_reg.h"
+
+
+static int wps_build_mac_addr(struct wps_data *wps, struct wpabuf *msg) {
+ wpa_printf(MSG_DEBUG, "WPS: * MAC Address");
+ wpabuf_put_be16(msg, ATTR_MAC_ADDR);
+ wpabuf_put_be16(msg, ETH_ALEN);
+ wpabuf_put_data(msg, wps->mac_addr_e, ETH_ALEN);
+ return 0;
+}
+
+
+static int wps_build_wps_state(struct wps_data *wps, struct wpabuf *msg)
+{
+ u8 state;
+ if (wps->wps->ap)
+ state = wps->wps->wps_state;
+ else
+ state = WPS_STATE_NOT_CONFIGURED;
+ wpa_printf(MSG_DEBUG, "WPS: * Wi-Fi Protected Setup State (%d)",
+ state);
+ wpabuf_put_be16(msg, ATTR_WPS_STATE);
+ wpabuf_put_be16(msg, 1);
+ wpabuf_put_u8(msg, state);
+ return 0;
+}
+
+
+static int wps_build_e_hash(struct wps_data *wps, struct wpabuf *msg)
+{
+ u8 *hash;
+ const u8 *addr[4];
+ size_t len[4];
+
+ if (random_get_bytes(wps->snonce, 2 * WPS_SECRET_NONCE_LEN) < 0)
+ return -1;
+ wpa_hexdump(MSG_DEBUG, "WPS: E-S1", wps->snonce, WPS_SECRET_NONCE_LEN);
+ wpa_hexdump(MSG_DEBUG, "WPS: E-S2",
+ wps->snonce + WPS_SECRET_NONCE_LEN, WPS_SECRET_NONCE_LEN);
+
+ if (wps->dh_pubkey_e == NULL || wps->dh_pubkey_r == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS: DH public keys not available for "
+ "E-Hash derivation");
+ return -1;
+ }
+
+ wpa_printf(MSG_DEBUG, "WPS: * E-Hash1");
+ wpabuf_put_be16(msg, ATTR_E_HASH1);
+ wpabuf_put_be16(msg, SHA256_MAC_LEN);
+ hash = wpabuf_put(msg, SHA256_MAC_LEN);
+ /* E-Hash1 = HMAC_AuthKey(E-S1 || PSK1 || PK_E || PK_R) */
+ addr[0] = wps->snonce;
+ len[0] = WPS_SECRET_NONCE_LEN;
+ addr[1] = wps->psk1;
+ len[1] = WPS_PSK_LEN;
+ addr[2] = wpabuf_head(wps->dh_pubkey_e);
+ len[2] = wpabuf_len(wps->dh_pubkey_e);
+ addr[3] = wpabuf_head(wps->dh_pubkey_r);
+ len[3] = wpabuf_len(wps->dh_pubkey_r);
+ if (wps_crypto_funcs.hmac_sha256_vector) {
+ wps_crypto_funcs.hmac_sha256_vector(wps->authkey, WPS_AUTHKEY_LEN, 4, addr, (int *)len, hash);
+ } else {
+ wpa_printf(MSG_ERROR, "In function %s, fail to register hmac_sha256_vector function!\r\n", __FUNCTION__);
+ return -1;
+ }
+ wpa_hexdump(MSG_DEBUG, "WPS: E-Hash1", hash, SHA256_MAC_LEN);
+
+ wpa_printf(MSG_DEBUG, "WPS: * E-Hash2");
+ wpabuf_put_be16(msg, ATTR_E_HASH2);
+ wpabuf_put_be16(msg, SHA256_MAC_LEN);
+ hash = wpabuf_put(msg, SHA256_MAC_LEN);
+ /* E-Hash2 = HMAC_AuthKey(E-S2 || PSK2 || PK_E || PK_R) */
+ addr[0] = wps->snonce + WPS_SECRET_NONCE_LEN;
+ addr[1] = wps->psk2;
+ if (wps_crypto_funcs.hmac_sha256_vector) {
+ wps_crypto_funcs.hmac_sha256_vector(wps->authkey, WPS_AUTHKEY_LEN, 4, addr, (int *)len, hash);
+ } else {
+ wpa_printf(MSG_ERROR, "In function %s, fail to register hmac_sha256_vector function!\r\n", __FUNCTION__);
+ return -1;
+ }
+ wpa_hexdump(MSG_DEBUG, "WPS: E-Hash2", hash, SHA256_MAC_LEN);
+
+ return 0;
+}
+
+
+static int wps_build_e_snonce1(struct wps_data *wps, struct wpabuf *msg)
+{
+ wpa_printf(MSG_DEBUG, "WPS: * E-SNonce1");
+ wpabuf_put_be16(msg, ATTR_E_SNONCE1);
+ wpabuf_put_be16(msg, WPS_SECRET_NONCE_LEN);
+ wpabuf_put_data(msg, wps->snonce, WPS_SECRET_NONCE_LEN);
+ return 0;
+}
+
+
+static int wps_build_e_snonce2(struct wps_data *wps, struct wpabuf *msg)
+{
+ wpa_printf(MSG_DEBUG, "WPS: * E-SNonce2");
+ wpabuf_put_be16(msg, ATTR_E_SNONCE2);
+ wpabuf_put_be16(msg, WPS_SECRET_NONCE_LEN);
+ wpabuf_put_data(msg, wps->snonce + WPS_SECRET_NONCE_LEN,
+ WPS_SECRET_NONCE_LEN);
+ return 0;
+}
+
+
+static struct wpabuf * wps_build_m1(struct wps_data *wps)
+{
+ struct wpabuf *msg;
+ u16 config_methods;
+
+ if (random_get_bytes(wps->nonce_e, WPS_NONCE_LEN) < 0)
+ return NULL;
+ wpa_hexdump(MSG_DEBUG, "WPS: Enrollee Nonce",
+ wps->nonce_e, WPS_NONCE_LEN);
+
+ wpa_printf(MSG_DEBUG, "WPS: Building Message M1");
+ msg = wpabuf_alloc(1000);
+ if (msg == NULL)
+ return NULL;
+
+ config_methods = wps->wps->config_methods;
+ if (wps->wps->ap && !wps->pbc_in_m1 &&
+ (wps->dev_password_len != 0 ||
+ (config_methods & WPS_CONFIG_DISPLAY))) {
+ /*
+ * These are the methods that the AP supports as an Enrollee
+ * for adding external Registrars, so remove PushButton.
+ *
+ * As a workaround for Windows 7 mechanism for probing WPS
+ * capabilities from M1, leave PushButton option if no PIN
+ * method is available or if WPS configuration enables PBC
+ * workaround.
+ */
+ config_methods &= ~WPS_CONFIG_PUSHBUTTON;
+#ifdef CONFIG_WPS2
+ config_methods &= ~(WPS_CONFIG_VIRT_PUSHBUTTON |
+ WPS_CONFIG_PHY_PUSHBUTTON);
+#endif /* CONFIG_WPS2 */
+ }
+
+ if (wps_build_version(msg) ||
+ wps_build_msg_type(msg, WPS_M1) ||
+ wps_build_uuid_e(msg, wps->uuid_e) ||
+ wps_build_mac_addr(wps, msg) ||
+ wps_build_enrollee_nonce(wps, msg) ||
+ wps_build_public_key(wps, msg, WPS_CALC_KEY_NO_CALC) ||
+ wps_build_auth_type_flags(wps, msg) ||
+ wps_build_encr_type_flags(wps, msg) ||
+ wps_build_conn_type_flags(wps, msg) ||
+ wps_build_config_methods(msg, config_methods) ||
+ wps_build_wps_state(wps, msg) ||
+ wps_build_device_attrs(&wps->wps->dev, msg) ||
+ wps_build_rf_bands(&wps->wps->dev, msg) ||
+ wps_build_assoc_state(wps, msg) ||
+ wps_build_dev_password_id(msg, wps->dev_pw_id) ||
+ wps_build_config_error(msg, WPS_CFG_NO_ERROR) ||
+ wps_build_os_version(&wps->wps->dev, msg) ||
+ wps_build_wfa_ext(msg, 0, NULL, 0) ||
+ wps_build_vendor_ext_m1(&wps->wps->dev, msg)) {
+ wpabuf_free(msg);
+ return NULL;
+ }
+
+ wps->state = RECV_M2;
+ return msg;
+}
+
+
+static struct wpabuf * wps_build_m3(struct wps_data *wps)
+{
+ struct wpabuf *msg;
+
+ wpa_printf(MSG_DEBUG, "WPS: Building Message M3");
+
+ if (wps->dev_password == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS: No Device Password available");
+ return NULL;
+ }
+ wps_derive_psk(wps, wps->dev_password, wps->dev_password_len);
+
+ msg = wpabuf_alloc(1000);
+ if (msg == NULL)
+ return NULL;
+
+ if (wps_build_version(msg) ||
+ wps_build_msg_type(msg, WPS_M3) ||
+ wps_build_registrar_nonce(wps, msg) ||
+ wps_build_e_hash(wps, msg) ||
+ wps_build_wfa_ext(msg, 0, NULL, 0) ||
+ wps_build_authenticator(wps, msg)) {
+ wpabuf_free(msg);
+ return NULL;
+ }
+
+ wps->state = RECV_M4;
+ return msg;
+}
+
+
+static struct wpabuf * wps_build_m5(struct wps_data *wps)
+{
+ struct wpabuf *msg, *plain;
+
+ wpa_printf(MSG_DEBUG, "WPS: Building Message M5");
+
+ plain = wpabuf_alloc(200);
+ if (plain == NULL)
+ return NULL;
+
+ msg = wpabuf_alloc(1000);
+ if (msg == NULL) {
+ wpabuf_free(plain);
+ return NULL;
+ }
+
+ if (wps_build_version(msg) ||
+ wps_build_msg_type(msg, WPS_M5) ||
+ wps_build_registrar_nonce(wps, msg) ||
+ wps_build_e_snonce1(wps, plain) ||
+ wps_build_key_wrap_auth(wps, plain) ||
+ wps_build_encr_settings(wps, msg, plain) ||
+ wps_build_wfa_ext(msg, 0, NULL, 0) ||
+ wps_build_authenticator(wps, msg)) {
+ wpabuf_free(plain);
+ wpabuf_free(msg);
+ return NULL;
+ }
+ wpabuf_free(plain);
+
+ wps->state = RECV_M6;
+ return msg;
+}
+
+
+static int wps_build_cred_ssid(struct wps_data *wps, struct wpabuf *msg)
+{
+ wpa_printf(MSG_DEBUG, "WPS: * SSID");
+ wpabuf_put_be16(msg, ATTR_SSID);
+ wpabuf_put_be16(msg, wps->wps->ssid_len);
+ wpabuf_put_data(msg, wps->wps->ssid, wps->wps->ssid_len);
+ return 0;
+}
+
+
+static int wps_build_cred_auth_type(struct wps_data *wps, struct wpabuf *msg)
+{
+ u16 auth_type = wps->wps->auth_types;
+
+ /* Select the best authentication type */
+ if (auth_type & WPS_AUTH_WPA2PSK)
+ auth_type = WPS_AUTH_WPA2PSK;
+ else if (auth_type & WPS_AUTH_WPAPSK)
+ auth_type = WPS_AUTH_WPAPSK;
+ else if (auth_type & WPS_WIFI_AUTH_OPEN)
+ auth_type = WPS_WIFI_AUTH_OPEN;
+ else if (auth_type & WPS_AUTH_SHARED)
+ auth_type = WPS_AUTH_SHARED;
+
+ wpa_printf(MSG_DEBUG, "WPS: * Authentication Type (0x%x)", auth_type);
+ wpabuf_put_be16(msg, ATTR_AUTH_TYPE);
+ wpabuf_put_be16(msg, 2);
+ wpabuf_put_be16(msg, auth_type);
+ return 0;
+}
+
+
+static int wps_build_cred_encr_type(struct wps_data *wps, struct wpabuf *msg)
+{
+ u16 encr_type = wps->wps->encr_types;
+
+ /* Select the best encryption type */
+ if (wps->wps->auth_types & (WPS_AUTH_WPA2PSK | WPS_AUTH_WPAPSK)) {
+ if (encr_type & WPS_ENCR_AES)
+ encr_type = WPS_ENCR_AES;
+ else if (encr_type & WPS_ENCR_TKIP)
+ encr_type = WPS_ENCR_TKIP;
+ } else {
+ if (encr_type & WPS_ENCR_WEP)
+ encr_type = WPS_ENCR_WEP;
+ else if (encr_type & WPS_ENCR_NONE)
+ encr_type = WPS_ENCR_NONE;
+ }
+
+ wpa_printf(MSG_DEBUG, "WPS: * Encryption Type (0x%x)", encr_type);
+ wpabuf_put_be16(msg, ATTR_ENCR_TYPE);
+ wpabuf_put_be16(msg, 2);
+ wpabuf_put_be16(msg, encr_type);
+ return 0;
+}
+
+
+static int wps_build_cred_network_key(struct wps_data *wps, struct wpabuf *msg)
+{
+ wpa_printf(MSG_DEBUG, "WPS: * Network Key");
+ wpabuf_put_be16(msg, ATTR_NETWORK_KEY);
+ wpabuf_put_be16(msg, wps->wps->network_key_len);
+ wpabuf_put_data(msg, wps->wps->network_key, wps->wps->network_key_len);
+ return 0;
+}
+
+
+static int wps_build_cred_mac_addr(struct wps_data *wps, struct wpabuf *msg)
+{
+ wpa_printf(MSG_DEBUG, "WPS: * MAC Address (AP BSSID)");
+ wpabuf_put_be16(msg, ATTR_MAC_ADDR);
+ wpabuf_put_be16(msg, ETH_ALEN);
+ wpabuf_put_data(msg, wps->wps->dev.mac_addr, ETH_ALEN);
+ return 0;
+}
+
+
+static int wps_build_ap_settings(struct wps_data *wps, struct wpabuf *plain)
+{
+ if (wps->wps->ap_settings) {
+ wpa_printf(MSG_DEBUG, "WPS: * AP Settings (pre-configured)");
+ wpabuf_put_data(plain, wps->wps->ap_settings,
+ wps->wps->ap_settings_len);
+ return 0;
+ }
+
+ return wps_build_cred_ssid(wps, plain) ||
+ wps_build_cred_mac_addr(wps, plain) ||
+ wps_build_cred_auth_type(wps, plain) ||
+ wps_build_cred_encr_type(wps, plain) ||
+ wps_build_cred_network_key(wps, plain);
+}
+
+
+static struct wpabuf * wps_build_m7(struct wps_data *wps)
+{
+ struct wpabuf *msg, *plain;
+
+ wpa_printf(MSG_DEBUG, "WPS: Building Message M7");
+
+ plain = wpabuf_alloc(500 + wps->wps->ap_settings_len);
+ if (plain == NULL)
+ return NULL;
+
+ msg = wpabuf_alloc(1000 + wps->wps->ap_settings_len);
+ if (msg == NULL) {
+ wpabuf_free(plain);
+ return NULL;
+ }
+
+ if (wps_build_version(msg) ||
+ wps_build_msg_type(msg, WPS_M7) ||
+ wps_build_registrar_nonce(wps, msg) ||
+ wps_build_e_snonce2(wps, plain) ||
+ (wps->wps->ap && wps_build_ap_settings(wps, plain)) ||
+ wps_build_key_wrap_auth(wps, plain) ||
+ wps_build_encr_settings(wps, msg, plain) ||
+ wps_build_wfa_ext(msg, 0, NULL, 0) ||
+ wps_build_authenticator(wps, msg)) {
+ wpabuf_free(plain);
+ wpabuf_free(msg);
+ return NULL;
+ }
+ wpabuf_free(plain);
+
+ if (wps->wps->ap && wps->wps->registrar) {
+ /*
+ * If the Registrar is only learning our current configuration,
+ * it may not continue protocol run to successful completion.
+ * Store information here to make sure it remains available.
+ */
+ wps_device_store(wps->wps->registrar, &wps->peer_dev,
+ wps->uuid_r);
+ }
+
+ wps->state = RECV_M8;
+ return msg;
+}
+
+
+static struct wpabuf * wps_build_wsc_done(struct wps_data *wps)
+{
+ struct wpabuf *msg;
+
+ wpa_printf(MSG_DEBUG, "WPS: Building Message WSC_Done");
+
+ msg = wpabuf_alloc(1000);
+ if (msg == NULL)
+ return NULL;
+
+ if (wps_build_version(msg) ||
+ wps_build_msg_type(msg, WPS_WSC_DONE) ||
+ wps_build_enrollee_nonce(wps, msg) ||
+ wps_build_registrar_nonce(wps, msg) ||
+ wps_build_wfa_ext(msg, 0, NULL, 0)) {
+ wpabuf_free(msg);
+ return NULL;
+ }
+
+ if (wps->wps->ap)
+ wps->state = RECV_ACK;
+ else {
+ wps_success_event(wps->wps);
+ wps->state = WPS_FINISHED;
+ }
+ return msg;
+}
+
+
+struct wpabuf * wps_enrollee_get_msg(struct wps_data *wps,
+ enum wsc_op_code *op_code)
+{
+ struct wpabuf *msg;
+
+ switch (wps->state) {
+ case SEND_M1:
+ msg = wps_build_m1(wps);
+ *op_code = WSC_MSG;
+ break;
+ case SEND_M3:
+ msg = wps_build_m3(wps);
+ *op_code = WSC_MSG;
+ break;
+ case SEND_M5:
+ msg = wps_build_m5(wps);
+ *op_code = WSC_MSG;
+ break;
+ case SEND_M7:
+ msg = wps_build_m7(wps);
+ *op_code = WSC_MSG;
+ break;
+ case RECEIVED_M2D:
+ if (wps->wps->ap) {
+ msg = wps_build_wsc_nack(wps);
+ *op_code = WSC_NACK;
+ break;
+ }
+ msg = wps_build_wsc_ack(wps);
+ *op_code = WSC_ACK;
+ if (msg) {
+ /* Another M2/M2D may be received */
+ wps->state = RECV_M2;
+ }
+ break;
+ case SEND_WSC_NACK:
+ msg = wps_build_wsc_nack(wps);
+ *op_code = WSC_NACK;
+ break;
+ case WPS_MSG_DONE:
+ msg = wps_build_wsc_done(wps);
+ *op_code = WSC_Done;
+ break;
+ default:
+ wpa_printf(MSG_DEBUG, "WPS: Unsupported state %d for building "
+ "a message", wps->state);
+ msg = NULL;
+ break;
+ }
+
+ if (*op_code == WSC_MSG && msg) {
+ /* Save a copy of the last message for Authenticator derivation
+ */
+ wpabuf_free(wps->last_msg);
+ wps->last_msg = wpabuf_dup(msg);
+ }
+
+ return msg;
+}
+
+
+static int wps_process_registrar_nonce(struct wps_data *wps, const u8 *r_nonce)
+{
+ if (r_nonce == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS: No Registrar Nonce received");
+ return -1;
+ }
+
+ os_memcpy(wps->nonce_r, r_nonce, WPS_NONCE_LEN);
+ wpa_hexdump(MSG_DEBUG, "WPS: Registrar Nonce",
+ wps->nonce_r, WPS_NONCE_LEN);
+
+ return 0;
+}
+
+
+static int wps_process_enrollee_nonce(struct wps_data *wps, const u8 *e_nonce)
+{
+ if (e_nonce == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS: No Enrollee Nonce received");
+ return -1;
+ }
+
+ if (os_memcmp(wps->nonce_e, e_nonce, WPS_NONCE_LEN) != 0) {
+ wpa_printf(MSG_DEBUG, "WPS: Invalid Enrollee Nonce received");
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static int wps_process_uuid_r(struct wps_data *wps, const u8 *uuid_r)
+{
+ if (uuid_r == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS: No UUID-R received");
+ return -1;
+ }
+
+ os_memcpy(wps->uuid_r, uuid_r, WPS_UUID_LEN);
+ wpa_hexdump(MSG_DEBUG, "WPS: UUID-R", wps->uuid_r, WPS_UUID_LEN);
+
+ return 0;
+}
+
+
+static int wps_process_pubkey(struct wps_data *wps, const u8 *pk,
+ size_t pk_len)
+{
+ if (pk == NULL || pk_len == 0) {
+ wpa_printf(MSG_DEBUG, "WPS: No Public Key received");
+ return -1;
+ }
+
+ wpabuf_free(wps->dh_pubkey_r);
+ wps->dh_pubkey_r = wpabuf_alloc_copy(pk, pk_len);
+ if (wps->dh_pubkey_r == NULL)
+ return -1;
+
+ wpa_printf(MSG_DEBUG, "process pubkey start\n");
+
+ if (wps_derive_keys(wps) < 0) {
+ return -1;
+ }
+
+ wpa_printf(MSG_DEBUG, "process pubkey finish\n");
+
+ return 0;
+}
+
+
+static int wps_process_r_hash1(struct wps_data *wps, const u8 *r_hash1)
+{
+ if (r_hash1 == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS: No R-Hash1 received");
+ return -1;
+ }
+
+ os_memcpy(wps->peer_hash1, r_hash1, WPS_HASH_LEN);
+ wpa_hexdump(MSG_DEBUG, "WPS: R-Hash1", wps->peer_hash1, WPS_HASH_LEN);
+
+ return 0;
+}
+
+
+static int wps_process_r_hash2(struct wps_data *wps, const u8 *r_hash2)
+{
+ if (r_hash2 == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS: No R-Hash2 received");
+ return -1;
+ }
+
+ os_memcpy(wps->peer_hash2, r_hash2, WPS_HASH_LEN);
+ wpa_hexdump(MSG_DEBUG, "WPS: R-Hash2", wps->peer_hash2, WPS_HASH_LEN);
+
+ return 0;
+}
+
+
+static int wps_process_r_snonce1(struct wps_data *wps, const u8 *r_snonce1)
+{
+ u8 hash[SHA256_MAC_LEN];
+ const u8 *addr[4];
+ size_t len[4];
+
+ if (r_snonce1 == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS: No R-SNonce1 received");
+ return -1;
+ }
+
+ wpa_hexdump_key(MSG_DEBUG, "WPS: R-SNonce1", r_snonce1,
+ WPS_SECRET_NONCE_LEN);
+
+ /* R-Hash1 = HMAC_AuthKey(R-S1 || PSK1 || PK_E || PK_R) */
+ addr[0] = r_snonce1;
+ len[0] = WPS_SECRET_NONCE_LEN;
+ addr[1] = wps->psk1;
+ len[1] = WPS_PSK_LEN;
+ addr[2] = wpabuf_head(wps->dh_pubkey_e);
+ len[2] = wpabuf_len(wps->dh_pubkey_e);
+ addr[3] = wpabuf_head(wps->dh_pubkey_r);
+ len[3] = wpabuf_len(wps->dh_pubkey_r);
+
+ if (wps_crypto_funcs.hmac_sha256_vector) {
+ wps_crypto_funcs.hmac_sha256_vector(wps->authkey, WPS_AUTHKEY_LEN, 4, addr, (int *)len, hash);
+ } else {
+ wpa_printf(MSG_ERROR, "In function %s, fail to register hmac_sha256_vector function!\r\n", __FUNCTION__);
+ return -1;
+ }
+ if (os_memcmp(wps->peer_hash1, hash, WPS_HASH_LEN) != 0) {
+ wpa_printf(MSG_DEBUG, "WPS: R-Hash1 derived from R-S1 does "
+ "not match with the pre-committed value");
+ wps->config_error = WPS_CFG_DEV_PASSWORD_AUTH_FAILURE;
+ wps_pwd_auth_fail_event(wps->wps, 1, 1);
+ return -1;
+ }
+
+ wpa_printf(MSG_DEBUG, "WPS: Registrar proved knowledge of the first "
+ "half of the device password");
+
+ return 0;
+}
+
+
+static int wps_process_r_snonce2(struct wps_data *wps, const u8 *r_snonce2)
+{
+ u8 hash[SHA256_MAC_LEN];
+ const u8 *addr[4];
+ size_t len[4];
+
+ if (r_snonce2 == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS: No R-SNonce2 received");
+ return -1;
+ }
+
+ wpa_hexdump_key(MSG_DEBUG, "WPS: R-SNonce2", r_snonce2,
+ WPS_SECRET_NONCE_LEN);
+
+ /* R-Hash2 = HMAC_AuthKey(R-S2 || PSK2 || PK_E || PK_R) */
+ addr[0] = r_snonce2;
+ len[0] = WPS_SECRET_NONCE_LEN;
+ addr[1] = wps->psk2;
+ len[1] = WPS_PSK_LEN;
+ addr[2] = wpabuf_head(wps->dh_pubkey_e);
+ len[2] = wpabuf_len(wps->dh_pubkey_e);
+ addr[3] = wpabuf_head(wps->dh_pubkey_r);
+ len[3] = wpabuf_len(wps->dh_pubkey_r);
+
+ if (wps_crypto_funcs.hmac_sha256_vector) {
+ wps_crypto_funcs.hmac_sha256_vector(wps->authkey, WPS_AUTHKEY_LEN, 4, addr, (int *)len, hash);
+ } else {
+ wpa_printf(MSG_ERROR, "In function %s, fail to regiset hmac_sha256_vector function!\r\n", __FUNCTION__);
+ return -1;
+ }
+
+ if (os_memcmp(wps->peer_hash2, hash, WPS_HASH_LEN) != 0) {
+ wpa_printf(MSG_DEBUG, "WPS: R-Hash2 derived from R-S2 does "
+ "not match with the pre-committed value");
+ wps->config_error = WPS_CFG_DEV_PASSWORD_AUTH_FAILURE;
+ wps_pwd_auth_fail_event(wps->wps, 1, 2);
+ return -1;
+ }
+
+ wpa_printf(MSG_DEBUG, "WPS: Registrar proved knowledge of the second "
+ "half of the device password");
+
+ return 0;
+}
+
+
+static int wps_process_cred_e(struct wps_data *wps, const u8 *cred,
+ size_t cred_len, int wps2)
+{
+ struct wps_parse_attr *attr;
+ struct wpabuf msg;
+ int ret = 0;
+
+ wpa_printf(MSG_DEBUG, "WPS: Received Credential");
+
+ attr = (struct wps_parse_attr *)os_zalloc(sizeof(struct wps_parse_attr));
+ if (attr == NULL)
+ return -99;
+
+ os_memset(&wps->cred, 0, sizeof(wps->cred));
+ wpabuf_set(&msg, cred, cred_len);
+ if (wps_parse_msg(&msg, attr) < 0 ||
+ wps_process_cred(attr, &wps->cred)) {
+ ret = -1;
+ goto _out;
+ }
+
+ if (os_memcmp(wps->cred.mac_addr, wps->wps->dev.mac_addr, ETH_ALEN) !=
+ 0) {
+ wpa_printf(MSG_DEBUG, "WPS: MAC Address in the Credential ("
+ MACSTR ") does not match with own address (" MACSTR
+ ")", MAC2STR(wps->cred.mac_addr),
+ MAC2STR(wps->wps->dev.mac_addr));
+ /*
+ * In theory, this could be consider fatal error, but there are
+ * number of deployed implementations using other address here
+ * due to unclarity in the specification. For interoperability
+ * reasons, allow this to be processed since we do not really
+ * use the MAC Address information for anything.
+ */
+#ifdef CONFIG_WPS_STRICT
+ if (wps2) {
+ wpa_printf(MSG_INFO, "WPS: Do not accept incorrect "
+ "MAC Address in AP Settings");
+ ret = -1;
+ goto _out;
+ }
+#endif /* CONFIG_WPS_STRICT */
+ }
+
+#ifdef CONFIG_WPS2
+ if (!(wps->cred.encr_type &
+ (WPS_ENCR_NONE | WPS_ENCR_TKIP | WPS_ENCR_AES))) {
+ if (wps->cred.encr_type & WPS_ENCR_WEP) {
+ wpa_printf(MSG_INFO, "WPS: Reject Credential "
+ "due to WEP configuration");
+ wps->error_indication = WPS_EI_SECURITY_WEP_PROHIBITED;
+ ret = -2;
+ goto _out;
+ }
+
+ wpa_printf(MSG_INFO, "WPS: Reject Credential due to "
+ "invalid encr_type 0x%x", wps->cred.encr_type);
+ ret = -1;
+ goto _out;
+ }
+#endif /* CONFIG_WPS2 */
+
+ wps_ssid_save(wps->cred.ssid, wps->cred.ssid_len);
+ wps_key_save((char *)wps->cred.key, wps->cred.key_len);
+
+ if (wps->wps->cred_cb) {
+ wps->cred.cred_attr = cred - 4;
+ wps->cred.cred_attr_len = cred_len + 4;
+ ret = wps->wps->cred_cb(wps->wps->cb_ctx, &wps->cred);
+ wps->cred.cred_attr = NULL;
+ wps->cred.cred_attr_len = 0;
+ }
+
+_out:
+ if (attr)
+ os_free(attr);
+
+ return ret;
+}
+
+
+static int wps_process_creds(struct wps_data *wps, const u8 *cred[],
+ size_t cred_len[], size_t num_cred, int wps2)
+{
+ size_t i;
+ int ok = 0;
+
+ if (wps->wps->ap)
+ return 0;
+
+ if (num_cred == 0) {
+ wpa_printf(MSG_DEBUG, "WPS: No Credential attributes "
+ "received");
+ return -1;
+ }
+
+ for (i = 0; i < num_cred; i++) {
+ int res;
+ res = wps_process_cred_e(wps, cred[i], cred_len[i], wps2);
+ if (res == 0)
+ ok++;
+ else if (res == -2) {
+ wpa_printf(MSG_DEBUG, "WPS: WEP credential skipped");
+ }
+ else
+ return -1;
+ }
+
+ if (ok == 0) {
+ wpa_printf(MSG_DEBUG, "WPS: No valid Credential attribute "
+ "received");
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static int wps_process_ap_settings_e(struct wps_data *wps,
+ struct wps_parse_attr *attr,
+ struct wpabuf *attrs, int wps2)
+{
+ struct wps_credential *cred;
+ int ret = 0;
+
+ cred = (struct wps_credential *)os_zalloc(sizeof(struct wps_credential));
+ if (cred == NULL) {
+ ret = -99;
+ goto _out;
+ }
+
+ if (!wps->wps->ap) {
+ ret = 0;
+ goto _out;
+ }
+
+ if (wps_process_ap_settings(attr, cred) < 0) {
+ ret = -1;
+ goto _out;
+ }
+
+ wpa_printf(MSG_INFO, "WPS: Received new AP configuration from "
+ "Registrar");
+
+ if (os_memcmp(cred->mac_addr, wps->wps->dev.mac_addr, ETH_ALEN) !=
+ 0) {
+ wpa_printf(MSG_DEBUG, "WPS: MAC Address in the AP Settings ("
+ MACSTR ") does not match with own address (" MACSTR
+ ")", MAC2STR(cred->mac_addr),
+ MAC2STR(wps->wps->dev.mac_addr));
+ /*
+ * In theory, this could be consider fatal error, but there are
+ * number of deployed implementations using other address here
+ * due to unclarity in the specification. For interoperability
+ * reasons, allow this to be processed since we do not really
+ * use the MAC Address information for anything.
+ */
+#ifdef CONFIG_WPS_STRICT
+ if (wps2) {
+ wpa_printf(MSG_INFO, "WPS: Do not accept incorrect "
+ "MAC Address in AP Settings");
+ ret = -1;
+ goto _out;
+ }
+#endif /* CONFIG_WPS_STRICT */
+ }
+
+#ifdef CONFIG_WPS2
+ if (!(cred->encr_type & (WPS_ENCR_NONE | WPS_ENCR_TKIP | WPS_ENCR_AES)))
+ {
+ if (cred->encr_type & WPS_ENCR_WEP) {
+ wpa_printf(MSG_INFO, "WPS: Reject new AP settings "
+ "due to WEP configuration");
+ wps->error_indication = WPS_EI_SECURITY_WEP_PROHIBITED;
+ ret = -1;
+ goto _out;
+ }
+
+ wpa_printf(MSG_INFO, "WPS: Reject new AP settings due to "
+ "invalid encr_type 0x%x", cred->encr_type);
+ ret = -1;
+ goto _out;
+ }
+#endif /* CONFIG_WPS2 */
+
+#ifdef CONFIG_WPS_STRICT
+ if (wps2) {
+ if ((cred->encr_type & (WPS_ENCR_TKIP | WPS_ENCR_AES)) ==
+ WPS_ENCR_TKIP ||
+ (cred->auth_type & (WPS_AUTH_WPAPSK | WPS_AUTH_WPA2PSK)) ==
+ WPS_AUTH_WPAPSK) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Invalid WSC 2.0 "
+ "AP Settings: WPA-Personal/TKIP only");
+ wps->error_indication =
+ WPS_EI_SECURITY_TKIP_ONLY_PROHIBITED;
+ ret = -1;
+ goto _out;
+ }
+ }
+#endif /* CONFIG_WPS_STRICT */
+
+#ifdef CONFIG_WPS2
+ if ((cred->encr_type & (WPS_ENCR_TKIP | WPS_ENCR_AES)) == WPS_ENCR_TKIP)
+ {
+ wpa_printf(MSG_DEBUG, "WPS: Upgrade encr_type TKIP -> "
+ "TKIP+AES");
+ cred->encr_type |= WPS_ENCR_AES;
+ }
+
+ if ((cred->auth_type & (WPS_AUTH_WPAPSK | WPS_AUTH_WPA2PSK)) ==
+ WPS_AUTH_WPAPSK) {
+ wpa_printf(MSG_DEBUG, "WPS: Upgrade auth_type WPAPSK -> "
+ "WPAPSK+WPA2PSK");
+ cred->auth_type |= WPS_AUTH_WPA2PSK;
+ }
+#endif /* CONFIG_WPS2 */
+
+ if (wps->wps->cred_cb) {
+ cred->cred_attr = wpabuf_head(attrs);
+ cred->cred_attr_len = wpabuf_len(attrs);
+ wps->wps->cred_cb(wps->wps->cb_ctx, cred);
+ }
+
+_out:
+ if (cred)
+ os_free(cred);
+
+ return ret;
+}
+
+
+static enum wps_process_res wps_process_m2(struct wps_data *wps,
+ const struct wpabuf *msg,
+ struct wps_parse_attr *attr)
+{
+ wpa_printf(MSG_DEBUG, "WPS: Received M2");
+
+ if (wps->state != RECV_M2) {
+ wpa_printf(MSG_DEBUG, "WPS: Unexpected state (%d) for "
+ "receiving M2", wps->state);
+ wps->state = SEND_WSC_NACK;
+ return WPS_CONTINUE;
+ }
+
+ if (wps_process_registrar_nonce(wps, attr->registrar_nonce) ||
+ wps_process_enrollee_nonce(wps, attr->enrollee_nonce) ||
+ wps_process_uuid_r(wps, attr->uuid_r)) {
+ wps->state = SEND_WSC_NACK;
+ return WPS_CONTINUE;
+ }
+
+ /*
+ * Stop here on an AP as an Enrollee if AP Setup is locked unless the
+ * special locked mode is used to allow protocol run up to M7 in order
+ * to support external Registrars that only learn the current AP
+ * configuration without changing it.
+ */
+ if (wps->wps->ap &&
+ ((wps->wps->ap_setup_locked && wps->wps->ap_setup_locked != 2) ||
+ wps->dev_password == NULL)) {
+ wpa_printf(MSG_DEBUG, "WPS: AP Setup is locked - refuse "
+ "registration of a new Registrar");
+ wps->config_error = WPS_CFG_SETUP_LOCKED;
+ wps->state = SEND_WSC_NACK;
+ return WPS_CONTINUE;
+ }
+
+ if (wps_process_pubkey(wps, attr->public_key, attr->public_key_len) ||
+ wps_process_authenticator(wps, attr->authenticator, msg) ||
+ wps_process_device_attrs(&wps->peer_dev, attr)) {
+ wps->state = SEND_WSC_NACK;
+ return WPS_CONTINUE;
+ }
+
+ wps->state = SEND_M3;
+ return WPS_CONTINUE;
+}
+
+
+static enum wps_process_res wps_process_m2d(struct wps_data *wps,
+ struct wps_parse_attr *attr)
+{
+ wpa_printf(MSG_DEBUG, "WPS: Received M2D");
+
+ if (wps->state != RECV_M2) {
+ wpa_printf(MSG_DEBUG, "WPS: Unexpected state (%d) for "
+ "receiving M2D", wps->state);
+ wps->state = SEND_WSC_NACK;
+ return WPS_CONTINUE;
+ }
+
+ wpa_hexdump_ascii(MSG_DEBUG, "WPS: Manufacturer",
+ attr->manufacturer, attr->manufacturer_len);
+ wpa_hexdump_ascii(MSG_DEBUG, "WPS: Model Name",
+ attr->model_name, attr->model_name_len);
+ wpa_hexdump_ascii(MSG_DEBUG, "WPS: Model Number",
+ attr->model_number, attr->model_number_len);
+ wpa_hexdump_ascii(MSG_DEBUG, "WPS: Serial Number",
+ attr->serial_number, attr->serial_number_len);
+ wpa_hexdump_ascii(MSG_DEBUG, "WPS: Device Name",
+ attr->dev_name, attr->dev_name_len);
+
+ if (wps->wps->event_cb) {
+ union wps_event_data data;
+ struct wps_event_m2d *m2d = &data.m2d;
+ os_memset(&data, 0, sizeof(data));
+ if (attr->config_methods)
+ m2d->config_methods =
+ WPA_GET_BE16(attr->config_methods);
+ m2d->manufacturer = attr->manufacturer;
+ m2d->manufacturer_len = attr->manufacturer_len;
+ m2d->model_name = attr->model_name;
+ m2d->model_name_len = attr->model_name_len;
+ m2d->model_number = attr->model_number;
+ m2d->model_number_len = attr->model_number_len;
+ m2d->serial_number = attr->serial_number;
+ m2d->serial_number_len = attr->serial_number_len;
+ m2d->dev_name = attr->dev_name;
+ m2d->dev_name_len = attr->dev_name_len;
+ m2d->primary_dev_type = attr->primary_dev_type;
+ if (attr->config_error)
+ m2d->config_error =
+ WPA_GET_BE16(attr->config_error);
+ if (attr->dev_password_id)
+ m2d->dev_password_id =
+ WPA_GET_BE16(attr->dev_password_id);
+ wps->wps->event_cb(wps->wps->cb_ctx, WPS_EV_M2D, &data);
+ }
+
+ wps->state = RECEIVED_M2D;
+ return WPS_CONTINUE;
+}
+
+
+static enum wps_process_res wps_process_m4(struct wps_data *wps,
+ const struct wpabuf *msg,
+ struct wps_parse_attr *attr)
+{
+ struct wpabuf *decrypted;
+ struct wps_parse_attr *eattr;
+ enum wps_process_res res;
+
+ wpa_printf(MSG_DEBUG, "WPS: Received M4");
+
+ eattr = (struct wps_parse_attr *)os_zalloc(sizeof(struct wps_parse_attr));
+ if (eattr == NULL) {
+ wps->state = SEND_WSC_NACK;
+ res = WPS_CONTINUE;
+ goto _out;
+ }
+
+ if (wps->state != RECV_M4) {
+ wpa_printf(MSG_DEBUG, "WPS: Unexpected state (%d) for "
+ "receiving M4", wps->state);
+ wps->state = SEND_WSC_NACK;
+ res = WPS_CONTINUE;
+ goto _out;
+ }
+
+ if (wps_process_enrollee_nonce(wps, attr->enrollee_nonce) ||
+ wps_process_authenticator(wps, attr->authenticator, msg) ||
+ wps_process_r_hash1(wps, attr->r_hash1) ||
+ wps_process_r_hash2(wps, attr->r_hash2)) {
+ wps->state = SEND_WSC_NACK;
+ res = WPS_CONTINUE;
+ goto _out;
+ }
+
+ decrypted = wps_decrypt_encr_settings(wps, attr->encr_settings,
+ attr->encr_settings_len);
+ if (decrypted == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS: Failed to decrypted Encrypted "
+ "Settings attribute");
+ wps->state = SEND_WSC_NACK;
+ res = WPS_CONTINUE;
+ goto _out;
+ }
+
+ if (wps_validate_m4_encr(decrypted, attr->version2 != NULL) < 0) {
+ wpabuf_free(decrypted);
+ wps->state = SEND_WSC_NACK;
+ res = WPS_CONTINUE;
+ goto _out;
+ }
+
+ wpa_printf(MSG_DEBUG, "WPS: Processing decrypted Encrypted Settings "
+ "attribute");
+ if (wps_parse_msg(decrypted, eattr) < 0 ||
+ wps_process_key_wrap_auth(wps, decrypted, eattr->key_wrap_auth) ||
+ wps_process_r_snonce1(wps, eattr->r_snonce1)) {
+ wpabuf_free(decrypted);
+ wps->state = SEND_WSC_NACK;
+ res = WPS_CONTINUE;
+ goto _out;
+ }
+ wpabuf_free(decrypted);
+
+ wps->state = SEND_M5;
+ res = WPS_CONTINUE;
+_out:
+ if (eattr)
+ os_free(eattr);
+ return res;
+}
+
+
+static enum wps_process_res wps_process_m6(struct wps_data *wps,
+ const struct wpabuf *msg,
+ struct wps_parse_attr *attr)
+{
+ struct wpabuf *decrypted;
+ struct wps_parse_attr *eattr;
+ enum wps_process_res res;
+
+ wpa_printf(MSG_DEBUG, "WPS: Received M6");
+
+ eattr = (struct wps_parse_attr *)os_zalloc(sizeof(struct wps_parse_attr));
+ if (eattr == NULL) {
+ wps->state = SEND_WSC_NACK;
+ res = WPS_CONTINUE;
+ goto _out;
+ }
+
+ if (wps->state != RECV_M6) {
+ wpa_printf(MSG_DEBUG, "WPS: Unexpected state (%d) for "
+ "receiving M6", wps->state);
+ wps->state = SEND_WSC_NACK;
+ res = WPS_CONTINUE;
+ goto _out;
+ }
+
+ if (wps_process_enrollee_nonce(wps, attr->enrollee_nonce) ||
+ wps_process_authenticator(wps, attr->authenticator, msg)) {
+ wps->state = SEND_WSC_NACK;
+ res = WPS_CONTINUE;
+ goto _out;
+ }
+
+ decrypted = wps_decrypt_encr_settings(wps, attr->encr_settings,
+ attr->encr_settings_len);
+ if (decrypted == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS: Failed to decrypted Encrypted "
+ "Settings attribute");
+ wps->state = SEND_WSC_NACK;
+ res = WPS_CONTINUE;
+ goto _out;
+ }
+
+ if (wps_validate_m6_encr(decrypted, attr->version2 != NULL) < 0) {
+ wpabuf_free(decrypted);
+ wps->state = SEND_WSC_NACK;
+ res = WPS_CONTINUE;
+ goto _out;
+ }
+
+ wpa_printf(MSG_DEBUG, "WPS: Processing decrypted Encrypted Settings "
+ "attribute");
+ if (wps_parse_msg(decrypted, eattr) < 0 ||
+ wps_process_key_wrap_auth(wps, decrypted, eattr->key_wrap_auth) ||
+ wps_process_r_snonce2(wps, eattr->r_snonce2)) {
+ wpabuf_free(decrypted);
+ wps->state = SEND_WSC_NACK;
+ res = WPS_CONTINUE;
+ goto _out;
+ }
+ wpabuf_free(decrypted);
+
+ if (wps->wps->ap)
+ wps->wps->event_cb(wps->wps->cb_ctx, WPS_EV_AP_PIN_SUCCESS,
+ NULL);
+
+ wps->state = SEND_M7;
+ res = WPS_CONTINUE;
+_out:
+ if (eattr)
+ os_free(eattr);
+ return res;
+}
+
+
+static enum wps_process_res wps_process_m8(struct wps_data *wps,
+ const struct wpabuf *msg,
+ struct wps_parse_attr *attr)
+{
+ struct wpabuf *decrypted;
+ struct wps_parse_attr *eattr;
+ enum wps_process_res res;
+
+ wpa_printf(MSG_DEBUG, "WPS: Received M8");
+
+ eattr = (struct wps_parse_attr *)os_zalloc(sizeof(struct wps_parse_attr));
+ if (eattr == NULL) {
+ wps->state = SEND_WSC_NACK;
+ res = WPS_CONTINUE;
+ goto _out;
+ }
+
+ if (wps->state != RECV_M8) {
+ wpa_printf(MSG_DEBUG, "WPS: Unexpected state (%d) for "
+ "receiving M8", wps->state);
+ wps->state = SEND_WSC_NACK;
+ res = WPS_CONTINUE;
+ goto _out;
+ }
+
+ if (wps_process_enrollee_nonce(wps, attr->enrollee_nonce) ||
+ wps_process_authenticator(wps, attr->authenticator, msg)) {
+ wps->state = SEND_WSC_NACK;
+ res = WPS_CONTINUE;
+ goto _out;
+ }
+
+ if (wps->wps->ap && wps->wps->ap_setup_locked) {
+ /*
+ * Stop here if special ap_setup_locked == 2 mode allowed the
+ * protocol to continue beyond M2. This allows ER to learn the
+ * current AP settings without changing them.
+ */
+ wpa_printf(MSG_DEBUG, "WPS: AP Setup is locked - refuse "
+ "registration of a new Registrar");
+ wps->config_error = WPS_CFG_SETUP_LOCKED;
+ wps->state = SEND_WSC_NACK;
+ res = WPS_CONTINUE;
+ goto _out;
+ }
+
+ decrypted = wps_decrypt_encr_settings(wps, attr->encr_settings,
+ attr->encr_settings_len);
+ if (decrypted == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS: Failed to decrypted Encrypted "
+ "Settings attribute");
+ wps->state = SEND_WSC_NACK;
+ res = WPS_CONTINUE;
+ goto _out;
+ }
+
+ if (wps_validate_m8_encr(decrypted, wps->wps->ap,
+ attr->version2 != NULL) < 0) {
+ wpabuf_free(decrypted);
+ wps->state = SEND_WSC_NACK;
+ res = WPS_CONTINUE;
+ goto _out;
+ }
+
+ wpa_printf(MSG_DEBUG, "WPS: Processing decrypted Encrypted Settings "
+ "attribute");
+ if (wps_parse_msg(decrypted, eattr) < 0 ||
+ wps_process_key_wrap_auth(wps, decrypted, eattr->key_wrap_auth) ||
+ wps_process_creds(wps, eattr->cred, eattr->cred_len,
+ eattr->num_cred, attr->version2 != NULL) ||
+ wps_process_ap_settings_e(wps, eattr, decrypted,
+ attr->version2 != NULL)) {
+ wpabuf_free(decrypted);
+ wps->state = SEND_WSC_NACK;
+ res = WPS_CONTINUE;
+ goto _out;
+ }
+ wpabuf_free(decrypted);
+
+ wps->state = WPS_MSG_DONE;
+ res = WPS_CONTINUE;
+
+_out:
+ if (eattr)
+ os_free(eattr);
+ return res;
+}
+
+extern struct wps_sm *gWpsSm;
+
+static enum wps_process_res wps_process_wsc_start(struct wps_data *wps,
+ const struct wpabuf *msg)
+{
+ struct wps_sm *sm = gWpsSm;
+ enum wps_process_res ret = WPS_CONTINUE;
+
+ wpa_printf(MSG_DEBUG, "WPS: Received WSC_START");
+ ets_timer_disarm(&sm->wps_eapol_start_timer);
+ wps->state = SEND_M1;
+ return ret;
+}
+
+#define WPS_IGNORE_STATE(wps_st) do {\
+ if (wps->state <= RECV_M8 && ((wps_st) == wps->state - 1 || (wps_st) == wps->state - 2)) { \
+ return WPS_IGNORE;\
+ }\
+} while (0)
+
+static enum wps_process_res wps_process_wsc_msg(struct wps_data *wps,
+ const struct wpabuf *msg)
+{
+ struct wps_parse_attr *attr;
+ enum wps_process_res ret = WPS_CONTINUE;
+
+ wpa_printf(MSG_DEBUG, "WPS: Received WSC_MSG");
+
+ attr = (struct wps_parse_attr *)os_zalloc(sizeof(struct wps_parse_attr));
+ if (attr == NULL) {
+ ret = WPS_FAILURE;
+ goto _out;
+ }
+
+ if (wps_parse_msg(msg, attr) < 0) {
+ ret = WPS_FAILURE;
+ goto _out;
+ }
+
+ if (attr->enrollee_nonce == NULL ||
+ os_memcmp(wps->nonce_e, attr->enrollee_nonce, WPS_NONCE_LEN) != 0) {
+ wpa_printf(MSG_DEBUG, "WPS: Mismatch in enrollee nonce");
+ ret = WPS_FAILURE;
+ goto _out;
+ }
+
+ if (attr->msg_type == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS: No Message Type attribute");
+ wps->state = SEND_WSC_NACK;
+ ret = WPS_CONTINUE;
+ goto _out;
+ }
+
+ switch (*attr->msg_type) {
+ case WPS_M2:
+ WPS_IGNORE_STATE(RECV_M2);
+ if (wps_validate_m2(msg) < 0) {
+ ret = WPS_FAILURE;
+ goto _out;
+ }
+ ret = wps_process_m2(wps, msg, attr);
+ break;
+ case WPS_M2D:
+ if (wps_validate_m2d(msg) < 0) {
+ ret = WPS_FAILURE;
+ goto _out;
+ }
+ ret = wps_process_m2d(wps, attr);
+ break;
+ case WPS_M4:
+ WPS_IGNORE_STATE(RECV_M4);
+ if (wps_validate_m4(msg) < 0) {
+ ret = WPS_FAILURE;
+ goto _out;
+ }
+ ret = wps_process_m4(wps, msg, attr);
+ if (ret == WPS_FAILURE || wps->state == SEND_WSC_NACK)
+ wps_fail_event(wps->wps, WPS_M4, wps->config_error,
+ wps->error_indication);
+ break;
+ case WPS_M6:
+ WPS_IGNORE_STATE(RECV_M6);
+ if (wps_validate_m6(msg) < 0) {
+ ret = WPS_FAILURE;
+ goto _out;
+ }
+ ret = wps_process_m6(wps, msg, attr);
+ if (ret == WPS_FAILURE || wps->state == SEND_WSC_NACK)
+ wps_fail_event(wps->wps, WPS_M6, wps->config_error,
+ wps->error_indication);
+ break;
+ case WPS_M8:
+ WPS_IGNORE_STATE(RECV_M8);
+ if (wps_validate_m8(msg) < 0) {
+ ret = WPS_FAILURE;
+ goto _out;
+ }
+ ret = wps_process_m8(wps, msg, attr);
+ if (ret == WPS_FAILURE || wps->state == SEND_WSC_NACK)
+ wps_fail_event(wps->wps, WPS_M8, wps->config_error,
+ wps->error_indication);
+ break;
+ default:
+ wpa_printf(MSG_DEBUG, "WPS: Unsupported Message Type %d",
+ *attr->msg_type);
+ ret = WPS_FAILURE;
+ goto _out;
+ }
+
+ /*
+ * Save a copy of the last message for Authenticator derivation if we
+ * are continuing. However, skip M2D since it is not authenticated and
+ * neither is the ACK/NACK response frame. This allows the possibly
+ * following M2 to be processed correctly by using the previously sent
+ * M1 in Authenticator derivation.
+ */
+ if (ret == WPS_CONTINUE && *attr->msg_type != WPS_M2D) {
+ /* Save a copy of the last message for Authenticator derivation
+ */
+ wpabuf_free(wps->last_msg);
+ wps->last_msg = wpabuf_dup(msg);
+ }
+
+_out:
+ if (attr)
+ os_free(attr);
+
+ return ret;
+}
+
+
+static enum wps_process_res wps_process_wsc_ack(struct wps_data *wps,
+ const struct wpabuf *msg)
+{
+ struct wps_parse_attr *attr;
+ enum wps_process_res res;
+
+ wpa_printf(MSG_DEBUG, "WPS: Received WSC_ACK");
+
+ attr = (struct wps_parse_attr *)os_zalloc(sizeof(struct wps_parse_attr));
+ if (attr == NULL) {
+ res = WPS_FAILURE;
+ goto _out;
+ }
+
+ if (wps_parse_msg(msg, attr) < 0) {
+ res = WPS_FAILURE;
+ goto _out;
+ }
+
+ if (attr->msg_type == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS: No Message Type attribute");
+ res = WPS_FAILURE;
+ goto _out;
+ }
+
+ if (*attr->msg_type != WPS_WSC_ACK) {
+ wpa_printf(MSG_DEBUG, "WPS: Invalid Message Type %d",
+ *attr->msg_type);
+ res = WPS_FAILURE;
+ goto _out;
+ }
+
+ if (attr->registrar_nonce == NULL ||
+ os_memcmp(wps->nonce_r, attr->registrar_nonce, WPS_NONCE_LEN) != 0)
+ {
+ wpa_printf(MSG_DEBUG, "WPS: Mismatch in registrar nonce");
+ res = WPS_FAILURE;
+ goto _out;
+ }
+
+ if (attr->enrollee_nonce == NULL ||
+ os_memcmp(wps->nonce_e, attr->enrollee_nonce, WPS_NONCE_LEN) != 0) {
+ wpa_printf(MSG_DEBUG, "WPS: Mismatch in enrollee nonce");
+ res = WPS_FAILURE;
+ goto _out;
+ }
+
+ if (wps->state == RECV_ACK && wps->wps->ap) {
+ wpa_printf(MSG_DEBUG, "WPS: External Registrar registration "
+ "completed successfully");
+ wps_success_event(wps->wps);
+ wps->state = WPS_FINISHED;
+ res = WPS_DONE;
+ goto _out;
+ }
+
+ res = WPS_FAILURE;
+_out:
+ if (attr)
+ os_free(attr);
+
+ return res;
+}
+
+
+static enum wps_process_res wps_process_wsc_nack(struct wps_data *wps,
+ const struct wpabuf *msg)
+{
+ struct wps_parse_attr *attr;
+ enum wps_process_res res;
+ u16 config_error;
+
+ wpa_printf(MSG_DEBUG, "WPS: Received WSC_NACK");
+
+ attr = (struct wps_parse_attr *)os_zalloc(sizeof(struct wps_parse_attr));
+ if (attr == NULL) {
+ res = WPS_FAILURE;
+ goto _out;
+ }
+
+ if (wps_parse_msg(msg, attr) < 0) {
+ res = WPS_FAILURE;
+ goto _out;
+ }
+
+ if (attr->msg_type == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS: No Message Type attribute");
+ res = WPS_FAILURE;
+ goto _out;
+ }
+
+ if (*attr->msg_type != WPS_WSC_NACK) {
+ wpa_printf(MSG_DEBUG, "WPS: Invalid Message Type %d",
+ *attr->msg_type);
+ res = WPS_FAILURE;
+ goto _out;
+ }
+
+ if (attr->registrar_nonce == NULL ||
+ os_memcmp(wps->nonce_r, attr->registrar_nonce, WPS_NONCE_LEN) != 0)
+ {
+ wpa_printf(MSG_DEBUG, "WPS: Mismatch in registrar nonce");
+ wpa_hexdump(MSG_DEBUG, "WPS: Received Registrar Nonce",
+ attr->registrar_nonce, WPS_NONCE_LEN);
+ wpa_hexdump(MSG_DEBUG, "WPS: Expected Registrar Nonce",
+ wps->nonce_r, WPS_NONCE_LEN);
+ res = WPS_FAILURE;
+ goto _out;
+ }
+
+ if (attr->enrollee_nonce == NULL ||
+ os_memcmp(wps->nonce_e, attr->enrollee_nonce, WPS_NONCE_LEN) != 0) {
+ wpa_printf(MSG_DEBUG, "WPS: Mismatch in enrollee nonce");
+ wpa_hexdump(MSG_DEBUG, "WPS: Received Enrollee Nonce",
+ attr->enrollee_nonce, WPS_NONCE_LEN);
+ wpa_hexdump(MSG_DEBUG, "WPS: Expected Enrollee Nonce",
+ wps->nonce_e, WPS_NONCE_LEN);
+ res = WPS_FAILURE;
+ goto _out;
+ }
+
+ if (attr->config_error == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS: No Configuration Error attribute "
+ "in WSC_NACK");
+ res = WPS_FAILURE;
+ goto _out;
+ }
+
+ config_error = WPA_GET_BE16(attr->config_error);
+ wpa_printf(MSG_DEBUG, "WPS: Registrar terminated negotiation with "
+ "Configuration Error %d", config_error);
+
+ switch (wps->state) {
+ case RECV_M4:
+ wps_fail_event(wps->wps, WPS_M3, config_error,
+ wps->error_indication);
+ break;
+ case RECV_M6:
+ wps_fail_event(wps->wps, WPS_M5, config_error,
+ wps->error_indication);
+ break;
+ case RECV_M8:
+ wps_fail_event(wps->wps, WPS_M7, config_error,
+ wps->error_indication);
+ break;
+ default:
+ break;
+ }
+
+ /* Followed by NACK if Enrollee is Supplicant or EAP-Failure if
+ * Enrollee is Authenticator */
+ wps->state = SEND_WSC_NACK;
+
+ res = WPS_FAILURE;
+_out:
+ if (attr)
+ os_free(attr);
+
+ return res;
+}
+
+
+enum wps_process_res wps_enrollee_process_msg(struct wps_data *wps,
+ enum wsc_op_code op_code,
+ const struct wpabuf *msg)
+{
+
+ wpa_printf(MSG_DEBUG, "WPS: Processing received message (len=%lu "
+ "op_code=%d)",
+ (unsigned long) wpabuf_len(msg), op_code);
+
+ if (op_code == WSC_UPnP) {
+ /* Determine the OpCode based on message type attribute */
+ struct wps_parse_attr attr;
+ if (wps_parse_msg(msg, &attr) == 0 && attr.msg_type) {
+ if (*attr.msg_type == WPS_WSC_ACK)
+ op_code = WSC_ACK;
+ else if (*attr.msg_type == WPS_WSC_NACK)
+ op_code = WSC_NACK;
+ }
+ }
+
+ switch (op_code) {
+ case WSC_Start:
+ return wps_process_wsc_start(wps, msg);
+ case WSC_MSG:
+ case WSC_UPnP:
+ return wps_process_wsc_msg(wps, msg);
+ case WSC_ACK:
+ if (wps_validate_wsc_ack(msg) < 0)
+ return WPS_FAILURE;
+ return wps_process_wsc_ack(wps, msg);
+ case WSC_NACK:
+ if (wps_validate_wsc_nack(msg) < 0)
+ return WPS_FAILURE;
+ return wps_process_wsc_nack(wps, msg);
+ default:
+ wpa_printf(MSG_DEBUG, "WPS: Unsupported op_code %d", op_code);
+ return WPS_FAILURE;
+ }
+}
--- /dev/null
+/*
+ * Wi-Fi Protected Setup - Registrar
+ * Copyright (c) 2008-2012, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+#include "wpa/includes.h"
+#include "wpa/list.h"
+#include "wpa/common.h"
+#include "crypto/base64.h"
+//#include "utils/eloop.h"
+#include "wps/utils/uuid.h"
+#include "wpa/list.h"
+#include "crypto/crypto.h"
+#include "crypto/sha256.h"
+#include "crypto/random.h"
+#include "wpa/ieee802_11_defs.h"
+#include "wps/wps_i.h"
+#include "wps/wps_dev_attr.h"
+//#include "wps/wps_upnp.h"
+//#include "wps/wps_upnp_i.h"
+
+#ifndef CONFIG_WPS_STRICT
+#define WPS_WORKAROUNDS
+#endif /* CONFIG_WPS_STRICT */
+
+#ifdef CONFIG_WPS_NFC
+
+struct wps_nfc_pw_token {
+ struct dl_list list;
+ u8 pubkey_hash[WPS_OOB_PUBKEY_HASH_LEN];
+ u16 pw_id;
+ u8 dev_pw[WPS_OOB_DEVICE_PASSWORD_LEN];
+ size_t dev_pw_len;
+};
+
+
+static void wps_remove_nfc_pw_token(struct wps_nfc_pw_token *token)
+{
+ dl_list_del(&token->list);
+ os_free(token);
+}
+
+
+static void wps_free_nfc_pw_tokens(struct dl_list *tokens, u16 pw_id)
+{
+ struct wps_nfc_pw_token *token, *prev;
+ dl_list_for_each_safe(token, prev, tokens, struct wps_nfc_pw_token,
+ list) {
+ if (pw_id == 0 || pw_id == token->pw_id)
+ wps_remove_nfc_pw_token(token);
+ }
+}
+
+
+static struct wps_nfc_pw_token * wps_get_nfc_pw_token(struct dl_list *tokens,
+ u16 pw_id)
+{
+ struct wps_nfc_pw_token *token;
+ dl_list_for_each(token, tokens, struct wps_nfc_pw_token, list) {
+ if (pw_id == token->pw_id)
+ return token;
+ }
+ return NULL;
+}
+
+#else /* CONFIG_WPS_NFC */
+
+#define wps_free_nfc_pw_tokens(t, p) do { } while (0)
+
+#endif /* CONFIG_WPS_NFC */
+
+#ifdef CONFIG_WPS_PIN
+
+struct wps_uuid_pin {
+ struct dl_list list;
+ u8 uuid[WPS_UUID_LEN];
+ int wildcard_uuid;
+ u8 *pin;
+ size_t pin_len;
+#define PIN_LOCKED BIT(0)
+#define PIN_EXPIRES BIT(1)
+ int flags;
+ struct os_time expiration;
+ u8 enrollee_addr[ETH_ALEN];
+};
+
+
+static void wps_free_pin(struct wps_uuid_pin *pin)
+{
+ os_free(pin->pin);
+ os_free(pin);
+}
+
+
+static void wps_remove_pin(struct wps_uuid_pin *pin)
+{
+ dl_list_del(&pin->list);
+ wps_free_pin(pin);
+}
+
+
+static void wps_free_pins(struct dl_list *pins)
+{
+ struct wps_uuid_pin *pin, *prev;
+ dl_list_for_each_safe(pin, prev, pins, struct wps_uuid_pin, list);
+ wps_remove_pin(pin);
+}
+
+#endif
+
+struct wps_pbc_session {
+ struct wps_pbc_session *next;
+ u8 addr[ETH_ALEN];
+ u8 uuid_e[WPS_UUID_LEN];
+ struct os_time timestamp;
+};
+
+
+static void wps_free_pbc_sessions(struct wps_pbc_session *pbc)
+{
+ struct wps_pbc_session *prev;
+
+ while (pbc) {
+ prev = pbc;
+ pbc = pbc->next;
+ os_free(prev);
+ }
+}
+
+
+struct wps_registrar_device {
+ struct wps_registrar_device *next;
+ struct wps_device_data dev;
+ u8 uuid[WPS_UUID_LEN];
+};
+
+
+struct wps_registrar {
+ struct wps_context *wps;
+
+ int pbc;
+ int selected_registrar;
+
+ int (*new_psk_cb)(void *ctx, const u8 *mac_addr, const u8 *psk,
+ size_t psk_len);
+ int (*set_ie_cb)(void *ctx, struct wpabuf *beacon_ie,
+ struct wpabuf *probe_resp_ie);
+ void (*pin_needed_cb)(void *ctx, const u8 *uuid_e,
+ const struct wps_device_data *dev);
+ void (*reg_success_cb)(void *ctx, const u8 *mac_addr,
+ const u8 *uuid_e, const u8 *dev_pw,
+ size_t dev_pw_len);
+ void (*set_sel_reg_cb)(void *ctx, int sel_reg, u16 dev_passwd_id,
+ u16 sel_reg_config_methods);
+ void (*enrollee_seen_cb)(void *ctx, const u8 *addr, const u8 *uuid_e,
+ const u8 *pri_dev_type, u16 config_methods,
+ u16 dev_password_id, u8 request_type,
+ const char *dev_name);
+ void *cb_ctx;
+
+ struct dl_list pins;
+ struct dl_list nfc_pw_tokens;
+ struct wps_pbc_session *pbc_sessions;
+
+ int skip_cred_build;
+ struct wpabuf *extra_cred;
+ int disable_auto_conf;
+ int sel_reg_union;
+ int sel_reg_dev_password_id_override;
+ int sel_reg_config_methods_override;
+ int static_wep_only;
+ int dualband;
+
+ struct wps_registrar_device *devices;
+
+ int force_pbc_overlap;
+
+ u8 authorized_macs[WPS_MAX_AUTHORIZED_MACS][ETH_ALEN];
+ u8 authorized_macs_union[WPS_MAX_AUTHORIZED_MACS][ETH_ALEN];
+
+ u8 p2p_dev_addr[ETH_ALEN];
+
+ u8 pbc_ignore_uuid[WPS_UUID_LEN];
+ struct os_time pbc_ignore_start;
+};
+
+
+static int wps_set_ie(struct wps_registrar *reg);
+//static void wps_registrar_pbc_timeout(void *eloop_ctx, void *timeout_ctx);
+//static void wps_registrar_set_selected_timeout(void *eloop_ctx,
+// void *timeout_ctx);
+static void wps_registrar_pbc_timeout(void *eloop_ctx);
+//static void wps_registrar_set_selected_timeout(void *eloop_ctx);
+
+#ifdef CONFIG_WPS_PIN
+
+static void wps_registrar_remove_pin(struct wps_registrar *reg,
+ struct wps_uuid_pin *pin);
+#endif
+
+static void wps_registrar_add_authorized_mac(struct wps_registrar *reg,
+ const u8 *addr)
+{
+ int i;
+ wpa_printf(MSG_DEBUG, "WPS: Add authorized MAC " MACSTR,
+ MAC2STR(addr));
+ for (i = 0; i < WPS_MAX_AUTHORIZED_MACS; i++)
+ if (os_memcmp(reg->authorized_macs[i], addr, ETH_ALEN) == 0) {
+ wpa_printf(MSG_DEBUG, "WPS: Authorized MAC was "
+ "already in the list");
+ return; /* already in list */
+ }
+ for (i = WPS_MAX_AUTHORIZED_MACS - 1; i > 0; i--)
+ os_memcpy(reg->authorized_macs[i], reg->authorized_macs[i - 1],
+ ETH_ALEN);
+ os_memcpy(reg->authorized_macs[0], addr, ETH_ALEN);
+ wpa_hexdump(MSG_DEBUG, "WPS: Authorized MACs",
+ (u8 *) reg->authorized_macs, sizeof(reg->authorized_macs));
+}
+
+
+static void wps_registrar_remove_authorized_mac(struct wps_registrar *reg,
+ const u8 *addr)
+{
+ int i;
+ wpa_printf(MSG_DEBUG, "WPS: Remove authorized MAC " MACSTR,
+ MAC2STR(addr));
+ for (i = 0; i < WPS_MAX_AUTHORIZED_MACS; i++) {
+ if (os_memcmp(reg->authorized_macs, addr, ETH_ALEN) == 0)
+ break;
+ }
+ if (i == WPS_MAX_AUTHORIZED_MACS) {
+ wpa_printf(MSG_DEBUG, "WPS: Authorized MAC was not in the "
+ "list");
+ return; /* not in the list */
+ }
+ for (; i + 1 < WPS_MAX_AUTHORIZED_MACS; i++)
+ os_memcpy(reg->authorized_macs[i], reg->authorized_macs[i + 1],
+ ETH_ALEN);
+ os_memset(reg->authorized_macs[WPS_MAX_AUTHORIZED_MACS - 1], 0,
+ ETH_ALEN);
+ wpa_hexdump(MSG_DEBUG, "WPS: Authorized MACs",
+ (u8 *) reg->authorized_macs, sizeof(reg->authorized_macs));
+}
+
+
+static void wps_free_devices(struct wps_registrar_device *dev)
+{
+ struct wps_registrar_device *prev;
+
+ while (dev) {
+ prev = dev;
+ dev = dev->next;
+ wps_device_data_free(&prev->dev);
+ os_free(prev);
+ }
+}
+
+
+static struct wps_registrar_device * wps_device_get(struct wps_registrar *reg,
+ const u8 *addr)
+{
+ struct wps_registrar_device *dev;
+
+ for (dev = reg->devices; dev; dev = dev->next) {
+ if (os_memcmp(dev->dev.mac_addr, addr, ETH_ALEN) == 0)
+ return dev;
+ }
+ return NULL;
+}
+
+
+static void wps_device_clone_data(struct wps_device_data *dst,
+ struct wps_device_data *src)
+{
+ os_memcpy(dst->mac_addr, src->mac_addr, ETH_ALEN);
+ os_memcpy(dst->pri_dev_type, src->pri_dev_type, WPS_DEV_TYPE_LEN);
+
+#define WPS_STRDUP(n) \
+ os_free(dst->n); \
+ dst->n = src->n ? os_strdup(src->n) : NULL
+
+ WPS_STRDUP(device_name);
+ WPS_STRDUP(manufacturer);
+ WPS_STRDUP(model_name);
+ WPS_STRDUP(model_number);
+ WPS_STRDUP(serial_number);
+#undef WPS_STRDUP
+}
+
+
+int wps_device_store(struct wps_registrar *reg,
+ struct wps_device_data *dev, const u8 *uuid)
+{
+ struct wps_registrar_device *d;
+
+ d = wps_device_get(reg, dev->mac_addr);
+ if (d == NULL) {
+ d = (struct wps_registrar_device *)os_zalloc(sizeof(*d));
+ if (d == NULL)
+ return -1;
+ d->next = reg->devices;
+ reg->devices = d;
+ }
+
+ wps_device_clone_data(&d->dev, dev);
+ os_memcpy(d->uuid, uuid, WPS_UUID_LEN);
+
+ return 0;
+}
+
+
+static void wps_registrar_add_pbc_session(struct wps_registrar *reg,
+ const u8 *addr, const u8 *uuid_e)
+{
+ struct wps_pbc_session *pbc, *prev = NULL;
+ struct os_time now;
+
+ os_get_time(&now);
+
+ pbc = reg->pbc_sessions;
+ while (pbc) {
+ if (os_memcmp(pbc->addr, addr, ETH_ALEN) == 0 &&
+ os_memcmp(pbc->uuid_e, uuid_e, WPS_UUID_LEN) == 0) {
+ if (prev)
+ prev->next = pbc->next;
+ else
+ reg->pbc_sessions = pbc->next;
+ break;
+ }
+ prev = pbc;
+ pbc = pbc->next;
+ }
+
+ if (!pbc) {
+ pbc = (struct wps_pbc_session *)os_zalloc(sizeof(*pbc));
+ if (pbc == NULL)
+ return;
+ os_memcpy(pbc->addr, addr, ETH_ALEN);
+ if (uuid_e)
+ os_memcpy(pbc->uuid_e, uuid_e, WPS_UUID_LEN);
+ }
+
+ pbc->next = reg->pbc_sessions;
+ reg->pbc_sessions = pbc;
+ pbc->timestamp = now;
+
+ /* remove entries that have timed out */
+ prev = pbc;
+ pbc = pbc->next;
+
+ while (pbc) {
+ if (now.sec > pbc->timestamp.sec + WPS_PBC_WALK_TIME) {
+ prev->next = NULL;
+ wps_free_pbc_sessions(pbc);
+ break;
+ }
+ prev = pbc;
+ pbc = pbc->next;
+ }
+}
+
+
+static void wps_registrar_remove_pbc_session(struct wps_registrar *reg,
+ const u8 *uuid_e,
+ const u8 *p2p_dev_addr)
+{
+ struct wps_pbc_session *pbc, *prev = NULL, *tmp;
+
+ pbc = reg->pbc_sessions;
+ while (pbc) {
+ if (os_memcmp(pbc->uuid_e, uuid_e, WPS_UUID_LEN) == 0 ||
+ (p2p_dev_addr && !is_zero_ether_addr(reg->p2p_dev_addr) &&
+ os_memcmp(reg->p2p_dev_addr, p2p_dev_addr, ETH_ALEN) ==
+ 0)) {
+ if (prev)
+ prev->next = pbc->next;
+ else
+ reg->pbc_sessions = pbc->next;
+ tmp = pbc;
+ pbc = pbc->next;
+ wpa_printf(MSG_DEBUG, "WPS: Removing PBC session for "
+ "addr=" MACSTR, MAC2STR(tmp->addr));
+ wpa_hexdump(MSG_DEBUG, "WPS: Removed UUID-E",
+ tmp->uuid_e, WPS_UUID_LEN);
+ os_free(tmp);
+ continue;
+ }
+ prev = pbc;
+ pbc = pbc->next;
+ }
+}
+
+
+int wps_registrar_pbc_overlap(struct wps_registrar *reg,
+ const u8 *addr, const u8 *uuid_e)
+{
+ int count = 0;
+ struct wps_pbc_session *pbc;
+ struct wps_pbc_session *first = NULL;
+ struct os_time now;
+
+ os_get_time(&now);
+
+ wpa_printf(MSG_DEBUG, "WPS: Checking active PBC sessions for overlap");
+
+ if (uuid_e) {
+ wpa_printf(MSG_DEBUG, "WPS: Add one for the requested UUID");
+ wpa_hexdump(MSG_DEBUG, "WPS: Requested UUID",
+ uuid_e, WPS_UUID_LEN);
+ count++;
+ }
+
+ for (pbc = reg->pbc_sessions; pbc; pbc = pbc->next) {
+ wpa_printf(MSG_DEBUG, "WPS: Consider PBC session with " MACSTR,
+ MAC2STR(pbc->addr));
+ wpa_hexdump(MSG_DEBUG, "WPS: UUID-E",
+ pbc->uuid_e, WPS_UUID_LEN);
+ if (now.sec > pbc->timestamp.sec + WPS_PBC_WALK_TIME) {
+ wpa_printf(MSG_DEBUG, "WPS: PBC walk time has "
+ "expired");
+ break;
+ }
+ if (first &&
+ os_memcmp(pbc->uuid_e, first->uuid_e, WPS_UUID_LEN) == 0) {
+ wpa_printf(MSG_DEBUG, "WPS: Same Enrollee");
+ continue; /* same Enrollee */
+ }
+ if (uuid_e == NULL ||
+ os_memcmp(uuid_e, pbc->uuid_e, WPS_UUID_LEN)) {
+ wpa_printf(MSG_DEBUG, "WPS: New Enrollee");
+ count++;
+ }
+ if (first == NULL)
+ first = pbc;
+ }
+
+ wpa_printf(MSG_DEBUG, "WPS: %u active PBC session(s) found", count);
+
+ return count > 1 ? 1 : 0;
+}
+
+
+static int wps_build_wps_state(struct wps_context *wps, struct wpabuf *msg)
+{
+ wpa_printf(MSG_DEBUG, "WPS: * Wi-Fi Protected Setup State (%d)",
+ wps->wps_state);
+ wpabuf_put_be16(msg, ATTR_WPS_STATE);
+ wpabuf_put_be16(msg, 1);
+ wpabuf_put_u8(msg, wps->wps_state);
+ return 0;
+}
+
+
+#ifdef CONFIG_WPS_UPNP
+static void wps_registrar_free_pending_m2(struct wps_context *wps)
+{
+ struct upnp_pending_message *p, *p2, *prev = NULL;
+ p = wps->upnp_msgs;
+ while (p) {
+ if (p->type == WPS_M2 || p->type == WPS_M2D) {
+ if (prev == NULL)
+ wps->upnp_msgs = p->next;
+ else
+ prev->next = p->next;
+ wpa_printf(MSG_DEBUG, "WPS UPnP: Drop pending M2/M2D");
+ p2 = p;
+ p = p->next;
+ wpabuf_free(p2->msg);
+ os_free(p2);
+ continue;
+ }
+ prev = p;
+ p = p->next;
+ }
+}
+#endif /* CONFIG_WPS_UPNP */
+
+
+static int wps_build_ap_setup_locked(struct wps_context *wps,
+ struct wpabuf *msg)
+{
+ if (wps->ap_setup_locked && wps->ap_setup_locked != 2) {
+ wpa_printf(MSG_DEBUG, "WPS: * AP Setup Locked");
+ wpabuf_put_be16(msg, ATTR_AP_SETUP_LOCKED);
+ wpabuf_put_be16(msg, 1);
+ wpabuf_put_u8(msg, 1);
+ }
+ return 0;
+}
+
+
+static int wps_build_selected_registrar(struct wps_registrar *reg,
+ struct wpabuf *msg)
+{
+ if (!reg->sel_reg_union)
+ return 0;
+ wpa_printf(MSG_DEBUG, "WPS: * Selected Registrar");
+ wpabuf_put_be16(msg, ATTR_SELECTED_REGISTRAR);
+ wpabuf_put_be16(msg, 1);
+ wpabuf_put_u8(msg, 1);
+ return 0;
+}
+
+
+static int wps_build_sel_reg_dev_password_id(struct wps_registrar *reg,
+ struct wpabuf *msg)
+{
+ u16 id = reg->pbc ? DEV_PW_PUSHBUTTON : DEV_PW_DEFAULT;
+ if (!reg->sel_reg_union)
+ return 0;
+ if (reg->sel_reg_dev_password_id_override >= 0)
+ id = reg->sel_reg_dev_password_id_override;
+ wpa_printf(MSG_DEBUG, "WPS: * Device Password ID (%d)", id);
+ wpabuf_put_be16(msg, ATTR_DEV_PASSWORD_ID);
+ wpabuf_put_be16(msg, 2);
+ wpabuf_put_be16(msg, id);
+ return 0;
+}
+
+
+static int wps_build_sel_pbc_reg_uuid_e(struct wps_registrar *reg,
+ struct wpabuf *msg)
+{
+ u16 id = reg->pbc ? DEV_PW_PUSHBUTTON : DEV_PW_DEFAULT;
+ if (!reg->sel_reg_union)
+ return 0;
+ if (reg->sel_reg_dev_password_id_override >= 0)
+ id = reg->sel_reg_dev_password_id_override;
+ if (id != DEV_PW_PUSHBUTTON || !reg->dualband)
+ return 0;
+ return wps_build_uuid_e(msg, reg->wps->uuid);
+}
+
+
+static void wps_set_pushbutton(u16 *methods, u16 conf_methods)
+{
+ *methods |= WPS_CONFIG_PUSHBUTTON;
+#ifdef CONFIG_WPS2
+ if ((conf_methods & WPS_CONFIG_VIRT_PUSHBUTTON) ==
+ WPS_CONFIG_VIRT_PUSHBUTTON)
+ *methods |= WPS_CONFIG_VIRT_PUSHBUTTON;
+ if ((conf_methods & WPS_CONFIG_PHY_PUSHBUTTON) ==
+ WPS_CONFIG_PHY_PUSHBUTTON)
+ *methods |= WPS_CONFIG_PHY_PUSHBUTTON;
+ if ((*methods & WPS_CONFIG_VIRT_PUSHBUTTON) !=
+ WPS_CONFIG_VIRT_PUSHBUTTON &&
+ (*methods & WPS_CONFIG_PHY_PUSHBUTTON) !=
+ WPS_CONFIG_PHY_PUSHBUTTON) {
+ /*
+ * Required to include virtual/physical flag, but we were not
+ * configured with push button type, so have to default to one
+ * of them.
+ */
+ *methods |= WPS_CONFIG_PHY_PUSHBUTTON;
+ }
+#endif /* CONFIG_WPS2 */
+}
+
+
+static int wps_build_sel_reg_config_methods(struct wps_registrar *reg,
+ struct wpabuf *msg)
+{
+ u16 methods;
+ if (!reg->sel_reg_union)
+ return 0;
+ methods = reg->wps->config_methods;
+ methods &= ~WPS_CONFIG_PUSHBUTTON;
+#ifdef CONFIG_WPS2
+ methods &= ~(WPS_CONFIG_VIRT_PUSHBUTTON |
+ WPS_CONFIG_PHY_PUSHBUTTON);
+#endif /* CONFIG_WPS2 */
+ if (reg->pbc)
+ wps_set_pushbutton(&methods, reg->wps->config_methods);
+ if (reg->sel_reg_config_methods_override >= 0)
+ methods = reg->sel_reg_config_methods_override;
+ wpa_printf(MSG_DEBUG, "WPS: * Selected Registrar Config Methods (%x)",
+ methods);
+ wpabuf_put_be16(msg, ATTR_SELECTED_REGISTRAR_CONFIG_METHODS);
+ wpabuf_put_be16(msg, 2);
+ wpabuf_put_be16(msg, methods);
+ return 0;
+}
+
+
+static int wps_build_probe_config_methods(struct wps_registrar *reg,
+ struct wpabuf *msg)
+{
+ u16 methods;
+ /*
+ * These are the methods that the AP supports as an Enrollee for adding
+ * external Registrars.
+ */
+ methods = reg->wps->config_methods & ~WPS_CONFIG_PUSHBUTTON;
+#ifdef CONFIG_WPS2
+ methods &= ~(WPS_CONFIG_VIRT_PUSHBUTTON |
+ WPS_CONFIG_PHY_PUSHBUTTON);
+#endif /* CONFIG_WPS2 */
+ wpa_printf(MSG_DEBUG, "WPS: * Config Methods (%x)", methods);
+ wpabuf_put_be16(msg, ATTR_CONFIG_METHODS);
+ wpabuf_put_be16(msg, 2);
+ wpabuf_put_be16(msg, methods);
+ return 0;
+}
+
+
+static int wps_build_config_methods_r(struct wps_registrar *reg,
+ struct wpabuf *msg)
+{
+ return wps_build_config_methods(msg, reg->wps->config_methods);
+}
+
+
+const u8 * wps_authorized_macs(struct wps_registrar *reg, size_t *count)
+{
+ *count = 0;
+
+#ifdef CONFIG_WPS2
+ while (*count < WPS_MAX_AUTHORIZED_MACS) {
+ if (is_zero_ether_addr(reg->authorized_macs_union[*count]))
+ break;
+ (*count)++;
+ }
+#endif /* CONFIG_WPS2 */
+
+ return (const u8 *) reg->authorized_macs_union;
+}
+
+
+/**
+ * wps_registrar_init - Initialize WPS Registrar data
+ * @wps: Pointer to longterm WPS context
+ * @cfg: Registrar configuration
+ * Returns: Pointer to allocated Registrar data or %NULL on failure
+ *
+ * This function is used to initialize WPS Registrar functionality. It can be
+ * used for a single Registrar run (e.g., when run in a supplicant) or multiple
+ * runs (e.g., when run as an internal Registrar in an AP). Caller is
+ * responsible for freeing the returned data with wps_registrar_deinit() when
+ * Registrar functionality is not needed anymore.
+ */
+struct wps_registrar * wps_registrar_init(struct wps_context *wps,
+ const struct wps_registrar_config *cfg)
+{
+ struct wps_registrar *reg = (struct wps_registrar *)os_zalloc(sizeof(*reg));
+ if (reg == NULL)
+ return NULL;
+#ifdef CONFIG_WPS_PIN
+ dl_list_init(®->pins);
+#endif
+#ifdef CONFIG_WPS_NFC
+ dl_list_init(®->nfc_pw_tokens);
+#endif
+ reg->wps = wps;
+ reg->new_psk_cb = cfg->new_psk_cb;
+ reg->set_ie_cb = cfg->set_ie_cb;
+#ifdef CONFIG_WPS_PIN
+ reg->pin_needed_cb = cfg->pin_needed_cb;
+#endif
+ reg->reg_success_cb = cfg->reg_success_cb;
+ reg->set_sel_reg_cb = cfg->set_sel_reg_cb;
+ reg->enrollee_seen_cb = cfg->enrollee_seen_cb;
+ reg->cb_ctx = cfg->cb_ctx;
+ reg->skip_cred_build = cfg->skip_cred_build;
+ if (cfg->extra_cred) {
+ reg->extra_cred = wpabuf_alloc_copy(cfg->extra_cred,
+ cfg->extra_cred_len);
+ if (reg->extra_cred == NULL) {
+ os_free(reg);
+ return NULL;
+ }
+ }
+ reg->disable_auto_conf = cfg->disable_auto_conf;
+ reg->sel_reg_dev_password_id_override = -1;
+ reg->sel_reg_config_methods_override = -1;
+ reg->static_wep_only = cfg->static_wep_only;
+ reg->dualband = cfg->dualband;
+
+ if (wps_set_ie(reg)) {
+ wps_registrar_deinit(reg);
+ return NULL;
+ }
+
+ return reg;
+}
+
+
+/**
+ * wps_registrar_deinit - Deinitialize WPS Registrar data
+ * @reg: Registrar data from wps_registrar_init()
+ */
+void wps_registrar_deinit(struct wps_registrar *reg)
+{
+ if (reg == NULL)
+ return;
+ //eloop_cancel_timeout(wps_registrar_pbc_timeout, reg, NULL);
+ //eloop_cancel_timeout(wps_registrar_set_selected_timeout, reg, NULL);
+
+ // TODO: snake to check, no sys_untimeout now, by wujg
+// sys_untimeout(wps_registrar_pbc_timeout, reg);
+// sys_untimeout(wps_registrar_set_selected_timeout, reg);
+
+#ifdef CONFIG_WPS_PIN
+ wps_free_pins(®->pins);
+#endif
+#ifdef CONFIG_WPS_NFC
+ wps_free_nfc_pw_tokens(®->nfc_pw_tokens, 0);
+#endif
+ wps_free_pbc_sessions(reg->pbc_sessions);
+ wpabuf_free(reg->extra_cred);
+ wps_free_devices(reg->devices);
+ os_free(reg);
+}
+
+#ifdef CONFIG_WPS_PIN
+
+static void wps_registrar_invalidate_unused(struct wps_registrar *reg)
+{
+ struct wps_uuid_pin *pin;
+
+ dl_list_for_each(pin, ®->pins, struct wps_uuid_pin, list) {
+ if (pin->wildcard_uuid == 1 && !(pin->flags & PIN_LOCKED)) {
+ wpa_printf(MSG_DEBUG, "WPS: Invalidate previously "
+ "configured wildcard PIN");
+ wps_registrar_remove_pin(reg, pin);
+ break;
+ }
+ }
+}
+
+
+/**
+ * wps_registrar_add_pin - Configure a new PIN for Registrar
+ * @reg: Registrar data from wps_registrar_init()
+ * @addr: Enrollee MAC address or %NULL if not known
+ * @uuid: UUID-E or %NULL for wildcard (any UUID)
+ * @pin: PIN (Device Password)
+ * @pin_len: Length of pin in octets
+ * @timeout: Time (in seconds) when the PIN will be invalidated; 0 = no timeout
+ * Returns: 0 on success, -1 on failure
+ */
+int wps_registrar_add_pin(struct wps_registrar *reg, const u8 *addr,
+ const u8 *uuid, const u8 *pin, size_t pin_len,
+ int timeout)
+{
+ struct wps_uuid_pin *p;
+
+ p = (struct wps_uuid_pin *)os_zalloc(sizeof(*p));
+ if (p == NULL)
+ return -1;
+ if (addr)
+ os_memcpy(p->enrollee_addr, addr, ETH_ALEN);
+ if (uuid == NULL)
+ p->wildcard_uuid = 1;
+ else
+ os_memcpy(p->uuid, uuid, WPS_UUID_LEN);
+ p->pin = (u8 *)os_malloc(pin_len);
+ if (p->pin == NULL) {
+ os_free(p);
+ return -1;
+ }
+ os_memcpy(p->pin, pin, pin_len);
+ p->pin_len = pin_len;
+
+ if (timeout) {
+ p->flags |= PIN_EXPIRES;
+ os_get_time(&p->expiration);
+ p->expiration.sec += timeout;
+ }
+
+ if (p->wildcard_uuid)
+ wps_registrar_invalidate_unused(reg);
+
+ dl_list_add(®->pins, &p->list);
+
+ wpa_printf(MSG_DEBUG, "WPS: A new PIN configured (timeout=%d)",
+ timeout);
+ wpa_hexdump(MSG_DEBUG, "WPS: UUID", uuid, WPS_UUID_LEN);
+ wpa_hexdump_ascii_key(MSG_DEBUG, "WPS: PIN", pin, pin_len);
+ reg->selected_registrar = 1;
+ reg->pbc = 0;
+ if (addr)
+ wps_registrar_add_authorized_mac(reg, addr);
+ else
+ wps_registrar_add_authorized_mac(
+ reg, (u8 *) "\xff\xff\xff\xff\xff\xff");
+ wps_registrar_selected_registrar_changed(reg);
+ //eloop_cancel_timeout(wps_registrar_set_selected_timeout, reg, NULL);
+ //eloop_register_timeout(WPS_PBC_WALK_TIME, 0,
+ //wps_registrar_set_selected_timeout,
+ //reg, NULL);
+
+ return 0;
+}
+
+
+static void wps_registrar_remove_pin(struct wps_registrar *reg,
+ struct wps_uuid_pin *pin)
+{
+ u8 *addr;
+ u8 bcast[ETH_ALEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
+
+ if (is_zero_ether_addr(pin->enrollee_addr))
+ addr = bcast;
+ else
+ addr = pin->enrollee_addr;
+ wps_registrar_remove_authorized_mac(reg, addr);
+ wps_remove_pin(pin);
+ wps_registrar_selected_registrar_changed(reg);
+}
+
+
+static void wps_registrar_expire_pins(struct wps_registrar *reg)
+{
+ struct wps_uuid_pin *pin, *prev;
+ struct os_time now;
+
+ os_get_time(&now);
+ dl_list_for_each_safe(pin, prev, ®->pins, struct wps_uuid_pin, list)
+ {
+ if ((pin->flags & PIN_EXPIRES) &&
+ os_time_before(&pin->expiration, &now)) {
+ wpa_hexdump(MSG_DEBUG, "WPS: Expired PIN for UUID",
+ pin->uuid, WPS_UUID_LEN);
+ wps_registrar_remove_pin(reg, pin);
+ }
+ }
+}
+
+
+/**
+ * wps_registrar_invalidate_wildcard_pin - Invalidate a wildcard PIN
+ * @reg: Registrar data from wps_registrar_init()
+ * @dev_pw: PIN to search for or %NULL to match any
+ * @dev_pw_len: Length of dev_pw in octets
+ * Returns: 0 on success, -1 if not wildcard PIN is enabled
+ */
+static int wps_registrar_invalidate_wildcard_pin(struct wps_registrar *reg,
+ const u8 *dev_pw,
+ size_t dev_pw_len)
+{
+ struct wps_uuid_pin *pin, *prev;
+
+ dl_list_for_each_safe(pin, prev, ®->pins, struct wps_uuid_pin, list)
+ {
+ if (dev_pw && pin->pin &&
+ (dev_pw_len != pin->pin_len ||
+ os_memcmp(dev_pw, pin->pin, dev_pw_len) != 0))
+ continue; /* different PIN */
+ if (pin->wildcard_uuid) {
+ wpa_hexdump(MSG_DEBUG, "WPS: Invalidated PIN for UUID",
+ pin->uuid, WPS_UUID_LEN);
+ wps_registrar_remove_pin(reg, pin);
+ return 0;
+ }
+ }
+
+ return -1;
+}
+
+
+/**
+ * wps_registrar_invalidate_pin - Invalidate a PIN for a specific UUID-E
+ * @reg: Registrar data from wps_registrar_init()
+ * @uuid: UUID-E
+ * Returns: 0 on success, -1 on failure (e.g., PIN not found)
+ */
+int wps_registrar_invalidate_pin(struct wps_registrar *reg, const u8 *uuid)
+{
+ struct wps_uuid_pin *pin, *prev;
+
+ dl_list_for_each_safe(pin, prev, ®->pins, struct wps_uuid_pin, list)
+ {
+ if (os_memcmp(pin->uuid, uuid, WPS_UUID_LEN) == 0) {
+ wpa_hexdump(MSG_DEBUG, "WPS: Invalidated PIN for UUID",
+ pin->uuid, WPS_UUID_LEN);
+ wps_registrar_remove_pin(reg, pin);
+ return 0;
+ }
+ }
+
+ return -1;
+}
+
+
+static const u8 * wps_registrar_get_pin(struct wps_registrar *reg,
+ const u8 *uuid, size_t *pin_len)
+{
+ struct wps_uuid_pin *pin, *found = NULL;
+
+ wps_registrar_expire_pins(reg);
+
+ dl_list_for_each(pin, ®->pins, struct wps_uuid_pin, list) {
+ if (!pin->wildcard_uuid &&
+ os_memcmp(pin->uuid, uuid, WPS_UUID_LEN) == 0) {
+ found = pin;
+ break;
+ }
+ }
+
+ if (!found) {
+ /* Check for wildcard UUIDs since none of the UUID-specific
+ * PINs matched */
+ dl_list_for_each(pin, ®->pins, struct wps_uuid_pin, list) {
+ if (pin->wildcard_uuid == 1 ||
+ pin->wildcard_uuid == 2) {
+ wpa_printf(MSG_DEBUG, "WPS: Found a wildcard "
+ "PIN. Assigned it for this UUID-E");
+ pin->wildcard_uuid++;
+ os_memcpy(pin->uuid, uuid, WPS_UUID_LEN);
+ found = pin;
+ break;
+ }
+ }
+ }
+
+ if (!found)
+ return NULL;
+
+ /*
+ * Lock the PIN to avoid attacks based on concurrent re-use of the PIN
+ * that could otherwise avoid PIN invalidations.
+ */
+ if (found->flags & PIN_LOCKED) {
+ wpa_printf(MSG_DEBUG, "WPS: Selected PIN locked - do not "
+ "allow concurrent re-use");
+ return NULL;
+ }
+ *pin_len = found->pin_len;
+ found->flags |= PIN_LOCKED;
+ return found->pin;
+}
+
+
+/**
+ * wps_registrar_unlock_pin - Unlock a PIN for a specific UUID-E
+ * @reg: Registrar data from wps_registrar_init()
+ * @uuid: UUID-E
+ * Returns: 0 on success, -1 on failure
+ *
+ * PINs are locked to enforce only one concurrent use. This function unlocks a
+ * PIN to allow it to be used again. If the specified PIN was configured using
+ * a wildcard UUID, it will be removed instead of allowing multiple uses.
+ */
+int wps_registrar_unlock_pin(struct wps_registrar *reg, const u8 *uuid)
+{
+ struct wps_uuid_pin *pin;
+
+ dl_list_for_each(pin, ®->pins, struct wps_uuid_pin, list) {
+ if (os_memcmp(pin->uuid, uuid, WPS_UUID_LEN) == 0) {
+ if (pin->wildcard_uuid == 3) {
+ wpa_printf(MSG_DEBUG, "WPS: Invalidating used "
+ "wildcard PIN");
+ return wps_registrar_invalidate_pin(reg, uuid);
+ }
+ pin->flags &= ~PIN_LOCKED;
+ return 0;
+ }
+ }
+
+ return -1;
+}
+#endif
+
+static void wps_registrar_stop_pbc(struct wps_registrar *reg)
+{
+ reg->selected_registrar = 0;
+ reg->pbc = 0;
+ os_memset(reg->p2p_dev_addr, 0, ETH_ALEN);
+ wps_registrar_remove_authorized_mac(reg,
+ (u8 *) "\xff\xff\xff\xff\xff\xff");
+ wps_registrar_selected_registrar_changed(reg);
+}
+
+
+//static void ICACHE_FLASH_ATTR wps_registrar_pbc_timeout(void *eloop_ctx, void *timeout_ctx)
+static void wps_registrar_pbc_timeout(void *eloop_ctx)
+{
+ struct wps_registrar *reg = eloop_ctx;
+
+ wpa_printf(MSG_DEBUG, "WPS: PBC timed out - disable PBC mode");
+ wps_pbc_timeout_event(reg->wps);
+ wps_registrar_stop_pbc(reg);
+}
+
+
+/**
+ * wps_registrar_button_pushed - Notify Registrar that AP button was pushed
+ * @reg: Registrar data from wps_registrar_init()
+ * @p2p_dev_addr: Limit allowed PBC devices to the specified P2P device, %NULL
+ * indicates no such filtering
+ * Returns: 0 on success, -1 on failure, -2 on session overlap
+ *
+ * This function is called on an AP when a push button is pushed to activate
+ * PBC mode. The PBC mode will be stopped after walk time (2 minutes) timeout
+ * or when a PBC registration is completed. If more than one Enrollee in active
+ * PBC mode has been detected during the monitor time (previous 2 minutes), the
+ * PBC mode is not activated and -2 is returned to indicate session overlap.
+ * This is skipped if a specific Enrollee is selected.
+ */
+int wps_registrar_button_pushed(struct wps_registrar *reg,
+ const u8 *p2p_dev_addr)
+{
+ if (p2p_dev_addr == NULL &&
+ wps_registrar_pbc_overlap(reg, NULL, NULL)) {
+ wpa_printf(MSG_DEBUG, "WPS: PBC overlap - do not start PBC "
+ "mode");
+ wps_pbc_overlap_event(reg->wps);
+ return -2;
+ }
+ wpa_printf(MSG_DEBUG, "WPS: Button pushed - PBC mode started");
+ reg->force_pbc_overlap = 0;
+ reg->selected_registrar = 1;
+ reg->pbc = 1;
+ if (p2p_dev_addr)
+ os_memcpy(reg->p2p_dev_addr, p2p_dev_addr, ETH_ALEN);
+ else
+ os_memset(reg->p2p_dev_addr, 0, ETH_ALEN);
+ wps_registrar_add_authorized_mac(reg,
+ (u8 *) "\xff\xff\xff\xff\xff\xff");
+ wps_registrar_selected_registrar_changed(reg);
+
+ //eloop_cancel_timeout(wps_registrar_set_selected_timeout, reg, NULL);
+ //eloop_cancel_timeout(wps_registrar_pbc_timeout, reg, NULL);
+
+ // TODO: snake to check, no sys_untimeout now, by wujg
+// sys_untimeout(wps_registrar_set_selected_timeout, reg);
+// sys_untimeout(wps_registrar_pbc_timeout, reg);
+
+ //eloop_register_timeout(WPS_PBC_WALK_TIME, 0, wps_registrar_pbc_timeout,
+ // reg, NULL);
+// sys_timeout(WPS_PBC_WALK_TIME*1000, wps_registrar_pbc_timeout, reg);
+
+ return 0;
+}
+
+
+static void wps_registrar_pbc_completed(struct wps_registrar *reg)
+{
+ wpa_printf(MSG_DEBUG, "WPS: PBC completed - stopping PBC mode");
+
+ // TODO: snake to check, no sys_untimeout now, by wujg
+ //eloop_cancel_timeout(wps_registrar_pbc_timeout, reg, NULL);
+// sys_untimeout(wps_registrar_pbc_timeout, reg);
+
+ wps_registrar_stop_pbc(reg);
+}
+
+#ifdef CONFIG_WPS_PIN
+
+static void wps_registrar_pin_completed(struct wps_registrar *reg)
+{
+ wpa_printf(MSG_DEBUG, "WPS: PIN completed using internal Registrar");
+ //eloop_cancel_timeout(wps_registrar_set_selected_timeout, reg, NULL);
+ reg->selected_registrar = 0;
+ wps_registrar_selected_registrar_changed(reg);
+}
+#endif
+
+void wps_registrar_complete(struct wps_registrar *registrar, const u8 *uuid_e,
+ const u8 *dev_pw, size_t dev_pw_len)
+{
+ if (registrar->pbc) {
+ wps_registrar_remove_pbc_session(registrar,
+ uuid_e, NULL);
+ wps_registrar_pbc_completed(registrar);
+ os_get_time(®istrar->pbc_ignore_start);
+ os_memcpy(registrar->pbc_ignore_uuid, uuid_e, WPS_UUID_LEN);
+ } else {
+#ifdef CONFIG_WPS_PIN
+ wps_registrar_pin_completed(registrar);
+#endif
+ }
+#ifdef CONFIG_WPS_PIN
+ if (dev_pw &&
+ wps_registrar_invalidate_wildcard_pin(registrar, dev_pw,
+ dev_pw_len) == 0) {
+ wpa_hexdump_key(MSG_DEBUG, "WPS: Invalidated wildcard PIN",
+ dev_pw, dev_pw_len);
+ }
+#endif
+}
+
+
+int wps_registrar_wps_cancel(struct wps_registrar *reg)
+{
+ if (reg->pbc) {
+ wpa_printf(MSG_DEBUG, "WPS: PBC is set - cancelling it");
+ //wps_registrar_pbc_timeout(reg, NULL);
+ //eloop_cancel_timeout(wps_registrar_pbc_timeout, reg, NULL);
+ wps_registrar_pbc_timeout(reg);
+
+ // TODO: snake to check, no sys_untimeout now, by wujg
+// sys_untimeout(wps_registrar_pbc_timeout, reg);
+
+ return 1;
+ } else if (reg->selected_registrar) {
+#ifdef CONFIG_WPS_PIN
+
+ /* PIN Method */
+ wpa_printf(MSG_DEBUG, "WPS: PIN is set - cancelling it");
+ wps_registrar_pin_completed(reg);
+ wps_registrar_invalidate_wildcard_pin(reg, NULL, 0);
+ return 1;
+#endif
+ }
+ return 0;
+}
+
+
+/**
+ * wps_registrar_probe_req_rx - Notify Registrar of Probe Request
+ * @reg: Registrar data from wps_registrar_init()
+ * @addr: MAC address of the Probe Request sender
+ * @wps_data: WPS IE contents
+ *
+ * This function is called on an AP when a Probe Request with WPS IE is
+ * received. This is used to track PBC mode use and to detect possible overlap
+ * situation with other WPS APs.
+ */
+void wps_registrar_probe_req_rx(struct wps_registrar *reg, const u8 *addr,
+ const struct wpabuf *wps_data,
+ int p2p_wildcard)
+{
+ struct wps_parse_attr attr;
+ int skip_add = 0;
+
+ wpa_hexdump_buf(MSG_MSGDUMP,
+ "WPS: Probe Request with WPS data received",
+ wps_data);
+
+ if (wps_parse_msg(wps_data, &attr) < 0)
+ return;
+
+ if (attr.config_methods == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS: No Config Methods attribute in "
+ "Probe Request");
+ return;
+ }
+
+ if (attr.dev_password_id == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS: No Device Password Id attribute "
+ "in Probe Request");
+ return;
+ }
+
+ if (reg->enrollee_seen_cb && attr.uuid_e &&
+ attr.primary_dev_type && attr.request_type && !p2p_wildcard) {
+ char *dev_name = NULL;
+ if (attr.dev_name) {
+ dev_name = (char *)os_zalloc(attr.dev_name_len + 1);
+ if (dev_name) {
+ os_memcpy(dev_name, attr.dev_name,
+ attr.dev_name_len);
+ }
+ }
+ reg->enrollee_seen_cb(reg->cb_ctx, addr, attr.uuid_e,
+ attr.primary_dev_type,
+ WPA_GET_BE16(attr.config_methods),
+ WPA_GET_BE16(attr.dev_password_id),
+ *attr.request_type, dev_name);
+ os_free(dev_name);
+ }
+
+ if (WPA_GET_BE16(attr.dev_password_id) != DEV_PW_PUSHBUTTON)
+ return; /* Not PBC */
+
+ wpa_printf(MSG_DEBUG, "WPS: Probe Request for PBC received from "
+ MACSTR, MAC2STR(addr));
+ if (attr.uuid_e == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS: Invalid Probe Request WPS IE: No "
+ "UUID-E included");
+ return;
+ }
+ wpa_hexdump(MSG_DEBUG, "WPS: UUID-E from Probe Request", attr.uuid_e,
+ WPS_UUID_LEN);
+
+#ifdef WPS_WORKAROUNDS
+ if (reg->pbc_ignore_start.sec &&
+ os_memcmp(attr.uuid_e, reg->pbc_ignore_uuid, WPS_UUID_LEN) == 0) {
+ struct os_time now, dur;
+ os_get_time(&now);
+ os_time_sub(&now, ®->pbc_ignore_start, &dur);
+ if (dur.sec >= 0 && dur.sec < 5) {
+ wpa_printf(MSG_DEBUG, "WPS: Ignore PBC activation "
+ "based on Probe Request from the Enrollee "
+ "that just completed PBC provisioning");
+ skip_add = 1;
+ } else
+ reg->pbc_ignore_start.sec = 0;
+ }
+#endif /* WPS_WORKAROUNDS */
+
+ if (!skip_add)
+ wps_registrar_add_pbc_session(reg, addr, attr.uuid_e);
+ if (wps_registrar_pbc_overlap(reg, addr, attr.uuid_e)) {
+ wpa_printf(MSG_DEBUG, "WPS: PBC session overlap detected");
+ reg->force_pbc_overlap = 1;
+ wps_pbc_overlap_event(reg->wps);
+ }
+}
+
+
+static int wps_cb_new_psk(struct wps_registrar *reg, const u8 *mac_addr,
+ const u8 *psk, size_t psk_len)
+{
+ if (reg->new_psk_cb == NULL)
+ return 0;
+
+ return reg->new_psk_cb(reg->cb_ctx, mac_addr, psk, psk_len);
+}
+
+#ifdef CONFIG_WPS_PIN
+
+static void wps_cb_pin_needed(struct wps_registrar *reg, const u8 *uuid_e,
+ const struct wps_device_data *dev)
+{
+ if (reg->pin_needed_cb == NULL)
+ return;
+
+ reg->pin_needed_cb(reg->cb_ctx, uuid_e, dev);
+}
+#endif
+
+static void wps_cb_reg_success(struct wps_registrar *reg, const u8 *mac_addr,
+ const u8 *uuid_e, const u8 *dev_pw,
+ size_t dev_pw_len)
+{
+ if (reg->reg_success_cb == NULL)
+ return;
+
+ reg->reg_success_cb(reg->cb_ctx, mac_addr, uuid_e, dev_pw, dev_pw_len);
+}
+
+
+static int wps_cb_set_ie(struct wps_registrar *reg, struct wpabuf *beacon_ie,
+ struct wpabuf *probe_resp_ie)
+{
+ return reg->set_ie_cb(reg->cb_ctx, beacon_ie, probe_resp_ie);
+}
+
+
+static void wps_cb_set_sel_reg(struct wps_registrar *reg)
+{
+ u16 methods = 0;
+ if (reg->set_sel_reg_cb == NULL)
+ return;
+
+ if (reg->selected_registrar) {
+ methods = reg->wps->config_methods & ~WPS_CONFIG_PUSHBUTTON;
+#ifdef CONFIG_WPS2
+ methods &= ~(WPS_CONFIG_VIRT_PUSHBUTTON |
+ WPS_CONFIG_PHY_PUSHBUTTON);
+#endif /* CONFIG_WPS2 */
+ if (reg->pbc)
+ wps_set_pushbutton(&methods, reg->wps->config_methods);
+ }
+
+ wpa_printf(MSG_DEBUG, "WPS: wps_cb_set_sel_reg: sel_reg=%d "
+ "config_methods=0x%x pbc=%d methods=0x%x",
+ reg->selected_registrar, reg->wps->config_methods,
+ reg->pbc, methods);
+
+ reg->set_sel_reg_cb(reg->cb_ctx, reg->selected_registrar,
+ reg->pbc ? DEV_PW_PUSHBUTTON : DEV_PW_DEFAULT,
+ methods);
+}
+
+
+static int wps_set_ie(struct wps_registrar *reg)
+{
+ struct wpabuf *beacon;
+ struct wpabuf *probe;
+ const u8 *auth_macs;
+ size_t count;
+ size_t vendor_len = 0;
+ int i;
+
+ if (reg->set_ie_cb == NULL)
+ return 0;
+
+ for (i = 0; i < MAX_WPS_VENDOR_EXTENSIONS; i++) {
+ if (reg->wps->dev.vendor_ext[i]) {
+ vendor_len += 2 + 2;
+ vendor_len += wpabuf_len(reg->wps->dev.vendor_ext[i]);
+ }
+ }
+
+ beacon = wpabuf_alloc(400 + vendor_len);
+ if (beacon == NULL)
+ return -1;
+ probe = wpabuf_alloc(500 + vendor_len);
+ if (probe == NULL) {
+ wpabuf_free(beacon);
+ return -1;
+ }
+
+ auth_macs = wps_authorized_macs(reg, &count);
+
+ wpa_printf(MSG_DEBUG, "WPS: Build Beacon IEs");
+
+ if (wps_build_version(beacon) ||
+ wps_build_wps_state(reg->wps, beacon) ||
+ wps_build_ap_setup_locked(reg->wps, beacon) ||
+ wps_build_selected_registrar(reg, beacon) ||
+ wps_build_sel_reg_dev_password_id(reg, beacon) ||
+ wps_build_sel_reg_config_methods(reg, beacon) ||
+ wps_build_sel_pbc_reg_uuid_e(reg, beacon) ||
+ (reg->dualband && wps_build_rf_bands(®->wps->dev, beacon)) ||
+ wps_build_wfa_ext(beacon, 0, auth_macs, count) ||
+ wps_build_vendor_ext(®->wps->dev, beacon)) {
+ wpabuf_free(beacon);
+ wpabuf_free(probe);
+ return -1;
+ }
+
+#ifdef CONFIG_P2P
+ if (wps_build_dev_name(®->wps->dev, beacon) ||
+ wps_build_primary_dev_type(®->wps->dev, beacon)) {
+ wpabuf_free(beacon);
+ wpabuf_free(probe);
+ return -1;
+ }
+#endif /* CONFIG_P2P */
+
+ wpa_printf(MSG_DEBUG, "WPS: Build Probe Response IEs");
+
+ if (wps_build_version(probe) ||
+ wps_build_wps_state(reg->wps, probe) ||
+ wps_build_ap_setup_locked(reg->wps, probe) ||
+ wps_build_selected_registrar(reg, probe) ||
+ wps_build_sel_reg_dev_password_id(reg, probe) ||
+ wps_build_sel_reg_config_methods(reg, probe) ||
+ wps_build_resp_type(probe, reg->wps->ap ? WPS_RESP_AP :
+ WPS_RESP_REGISTRAR) ||
+ wps_build_uuid_e(probe, reg->wps->uuid) ||
+ wps_build_device_attrs(®->wps->dev, probe) ||
+ wps_build_probe_config_methods(reg, probe) ||
+ (reg->dualband && wps_build_rf_bands(®->wps->dev, probe)) ||
+ wps_build_wfa_ext(probe, 0, auth_macs, count) ||
+ wps_build_vendor_ext(®->wps->dev, probe)) {
+ wpabuf_free(beacon);
+ wpabuf_free(probe);
+ return -1;
+ }
+
+ beacon = wps_ie_encapsulate(beacon);
+ probe = wps_ie_encapsulate(probe);
+
+ if (!beacon || !probe) {
+ wpabuf_free(beacon);
+ wpabuf_free(probe);
+ return -1;
+ }
+
+ if (reg->static_wep_only) {
+ /*
+ * Windows XP and Vista clients can get confused about
+ * EAP-Identity/Request when they probe the network with
+ * EAPOL-Start. In such a case, they may assume the network is
+ * using IEEE 802.1X and prompt user for a certificate while
+ * the correct (non-WPS) behavior would be to ask for the
+ * static WEP key. As a workaround, use Microsoft Provisioning
+ * IE to advertise that legacy 802.1X is not supported.
+ */
+ const u8 ms_wps[7] = {
+ WLAN_EID_VENDOR_SPECIFIC, 5,
+ /* Microsoft Provisioning IE (00:50:f2:5) */
+ 0x00, 0x50, 0xf2, 5,
+ 0x00 /* no legacy 802.1X or MS WPS */
+ };
+ wpa_printf(MSG_DEBUG, "WPS: Add Microsoft Provisioning IE "
+ "into Beacon/Probe Response frames");
+ wpabuf_put_data(beacon, ms_wps, sizeof(ms_wps));
+ wpabuf_put_data(probe, ms_wps, sizeof(ms_wps));
+ }
+
+ return wps_cb_set_ie(reg, beacon, probe);
+}
+
+
+static int wps_get_dev_password(struct wps_data *wps)
+{
+ const u8 *pin;
+ size_t pin_len = 0;
+
+ os_free(wps->dev_password);
+ wps->dev_password = NULL;
+
+ if (wps->pbc) {
+ wpa_printf(MSG_DEBUG, "WPS: Use default PIN for PBC");
+ pin = (const u8 *) "00000000";
+ pin_len = 8;
+#ifdef CONFIG_WPS_NFC
+ } else if (wps->nfc_pw_token) {
+ wpa_printf(MSG_DEBUG, "WPS: Use OOB Device Password from NFC "
+ "Password Token");
+ pin = wps->nfc_pw_token->dev_pw;
+ pin_len = wps->nfc_pw_token->dev_pw_len;
+#endif /* CONFIG_WPS_NFC */
+ } else {
+#ifdef CONFIG_WPS_PIN
+
+ pin = wps_registrar_get_pin(wps->wps->registrar, wps->uuid_e,
+ &pin_len);
+ }
+ if (pin == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS: No Device Password available for "
+ "the Enrollee");
+ wps_cb_pin_needed(wps->wps->registrar, wps->uuid_e,
+ &wps->peer_dev);
+ return -1;
+#endif
+ }
+
+ wps->dev_password = (u8 *)os_malloc(pin_len);
+ if (wps->dev_password == NULL)
+ return -1;
+ os_memcpy(wps->dev_password, pin, pin_len);
+ wps->dev_password_len = pin_len;
+
+ return 0;
+}
+
+
+static int wps_build_uuid_r(struct wps_data *wps, struct wpabuf *msg)
+{
+ wpa_printf(MSG_DEBUG, "WPS: * UUID-R");
+ wpabuf_put_be16(msg, ATTR_UUID_R);
+ wpabuf_put_be16(msg, WPS_UUID_LEN);
+ wpabuf_put_data(msg, wps->uuid_r, WPS_UUID_LEN);
+ return 0;
+}
+
+
+static int wps_build_r_hash(struct wps_data *wps, struct wpabuf *msg)
+{
+ u8 *hash;
+ const u8 *addr[4];
+ size_t len[4];
+
+ if (random_get_bytes(wps->snonce, 2 * WPS_SECRET_NONCE_LEN) < 0)
+ return -1;
+ wpa_hexdump(MSG_DEBUG, "WPS: R-S1", wps->snonce, WPS_SECRET_NONCE_LEN);
+ wpa_hexdump(MSG_DEBUG, "WPS: R-S2",
+ wps->snonce + WPS_SECRET_NONCE_LEN, WPS_SECRET_NONCE_LEN);
+
+ if (wps->dh_pubkey_e == NULL || wps->dh_pubkey_r == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS: DH public keys not available for "
+ "R-Hash derivation");
+ return -1;
+ }
+
+ wpa_printf(MSG_DEBUG, "WPS: * R-Hash1");
+ wpabuf_put_be16(msg, ATTR_R_HASH1);
+ wpabuf_put_be16(msg, SHA256_MAC_LEN);
+ hash = wpabuf_put(msg, SHA256_MAC_LEN);
+ /* R-Hash1 = HMAC_AuthKey(R-S1 || PSK1 || PK_E || PK_R) */
+ addr[0] = wps->snonce;
+ len[0] = WPS_SECRET_NONCE_LEN;
+ addr[1] = wps->psk1;
+ len[1] = WPS_PSK_LEN;
+ addr[2] = wpabuf_head(wps->dh_pubkey_e);
+ len[2] = wpabuf_len(wps->dh_pubkey_e);
+ addr[3] = wpabuf_head(wps->dh_pubkey_r);
+ len[3] = wpabuf_len(wps->dh_pubkey_r);
+ if (wps_crypto_funcs.hmac_sha256_vector) {
+ wps_crypto_funcs.hmac_sha256_vector(wps->authkey, WPS_AUTHKEY_LEN, 4, addr, (int *)len, hash);
+ } else {
+ wpa_printf(MSG_ERROR, "In function %s, fail to register hmac_sha256_vector function!\r\n", __FUNCTION__);
+ return -1;
+ }
+ wpa_hexdump(MSG_DEBUG, "WPS: R-Hash1", hash, SHA256_MAC_LEN);
+
+ wpa_printf(MSG_DEBUG, "WPS: * R-Hash2");
+ wpabuf_put_be16(msg, ATTR_R_HASH2);
+ wpabuf_put_be16(msg, SHA256_MAC_LEN);
+ hash = wpabuf_put(msg, SHA256_MAC_LEN);
+ /* R-Hash2 = HMAC_AuthKey(R-S2 || PSK2 || PK_E || PK_R) */
+ addr[0] = wps->snonce + WPS_SECRET_NONCE_LEN;
+ addr[1] = wps->psk2;
+ if (wps_crypto_funcs.hmac_sha256_vector) {
+ wps_crypto_funcs.hmac_sha256_vector(wps->authkey, WPS_AUTHKEY_LEN, 4, addr, (int *)len, hash);
+ } else {
+ wpa_printf(MSG_ERROR, "In function %s, fail to register hmac_sha256_vector function!\r\n", __FUNCTION__);
+ return -1;
+ }
+ wpa_hexdump(MSG_DEBUG, "WPS: R-Hash2", hash, SHA256_MAC_LEN);
+
+ return 0;
+}
+
+
+static int wps_build_r_snonce1(struct wps_data *wps, struct wpabuf *msg)
+{
+ wpa_printf(MSG_DEBUG, "WPS: * R-SNonce1");
+ wpabuf_put_be16(msg, ATTR_R_SNONCE1);
+ wpabuf_put_be16(msg, WPS_SECRET_NONCE_LEN);
+ wpabuf_put_data(msg, wps->snonce, WPS_SECRET_NONCE_LEN);
+ return 0;
+}
+
+
+static int wps_build_r_snonce2(struct wps_data *wps, struct wpabuf *msg)
+{
+ wpa_printf(MSG_DEBUG, "WPS: * R-SNonce2");
+ wpabuf_put_be16(msg, ATTR_R_SNONCE2);
+ wpabuf_put_be16(msg, WPS_SECRET_NONCE_LEN);
+ wpabuf_put_data(msg, wps->snonce + WPS_SECRET_NONCE_LEN,
+ WPS_SECRET_NONCE_LEN);
+ return 0;
+}
+
+
+static int wps_build_cred_network_idx(struct wpabuf *msg,
+ const struct wps_credential *cred)
+{
+ wpa_printf(MSG_DEBUG, "WPS: * Network Index (1)");
+ wpabuf_put_be16(msg, ATTR_NETWORK_INDEX);
+ wpabuf_put_be16(msg, 1);
+ wpabuf_put_u8(msg, 1);
+ return 0;
+}
+
+
+static int wps_build_cred_ssid(struct wpabuf *msg,
+ const struct wps_credential *cred)
+{
+ wpa_printf(MSG_DEBUG, "WPS: * SSID");
+ wpa_hexdump_ascii(MSG_DEBUG, "WPS: SSID for Credential",
+ cred->ssid, cred->ssid_len);
+ wpabuf_put_be16(msg, ATTR_SSID);
+ wpabuf_put_be16(msg, cred->ssid_len);
+ wpabuf_put_data(msg, cred->ssid, cred->ssid_len);
+ return 0;
+}
+
+
+static int wps_build_cred_auth_type(struct wpabuf *msg,
+ const struct wps_credential *cred)
+{
+ wpa_printf(MSG_DEBUG, "WPS: * Authentication Type (0x%x)",
+ cred->auth_type);
+ wpabuf_put_be16(msg, ATTR_AUTH_TYPE);
+ wpabuf_put_be16(msg, 2);
+ wpabuf_put_be16(msg, cred->auth_type);
+ return 0;
+}
+
+
+static int wps_build_cred_encr_type(struct wpabuf *msg,
+ const struct wps_credential *cred)
+{
+ wpa_printf(MSG_DEBUG, "WPS: * Encryption Type (0x%x)",
+ cred->encr_type);
+ wpabuf_put_be16(msg, ATTR_ENCR_TYPE);
+ wpabuf_put_be16(msg, 2);
+ wpabuf_put_be16(msg, cred->encr_type);
+ return 0;
+}
+
+
+static int wps_build_cred_network_key(struct wpabuf *msg,
+ const struct wps_credential *cred)
+{
+ wpa_printf(MSG_DEBUG, "WPS: * Network Key (len=%d)",
+ (int) cred->key_len);
+ wpa_hexdump_key(MSG_DEBUG, "WPS: Network Key",
+ cred->key, cred->key_len);
+ wpabuf_put_be16(msg, ATTR_NETWORK_KEY);
+ wpabuf_put_be16(msg, cred->key_len);
+ wpabuf_put_data(msg, cred->key, cred->key_len);
+ return 0;
+}
+
+
+static int wps_build_cred_mac_addr(struct wpabuf *msg,
+ const struct wps_credential *cred)
+{
+ wpa_printf(MSG_DEBUG, "WPS: * MAC Address (" MACSTR ")",
+ MAC2STR(cred->mac_addr));
+ wpabuf_put_be16(msg, ATTR_MAC_ADDR);
+ wpabuf_put_be16(msg, ETH_ALEN);
+ wpabuf_put_data(msg, cred->mac_addr, ETH_ALEN);
+ return 0;
+}
+
+
+static int wps_build_credential(struct wpabuf *msg,
+ const struct wps_credential *cred)
+{
+ if (wps_build_cred_network_idx(msg, cred) ||
+ wps_build_cred_ssid(msg, cred) ||
+ wps_build_cred_auth_type(msg, cred) ||
+ wps_build_cred_encr_type(msg, cred) ||
+ wps_build_cred_network_key(msg, cred) ||
+ wps_build_cred_mac_addr(msg, cred))
+ return -1;
+ return 0;
+}
+
+
+int wps_build_credential_wrap(struct wpabuf *msg,
+ const struct wps_credential *cred)
+{
+ struct wpabuf *wbuf;
+ wbuf = wpabuf_alloc(200);
+ if (wbuf == NULL)
+ return -1;
+ if (wps_build_credential(wbuf, cred)) {
+ wpabuf_free(wbuf);
+ return -1;
+ }
+ wpabuf_put_be16(msg, ATTR_CRED);
+ wpabuf_put_be16(msg, wpabuf_len(wbuf));
+ wpabuf_put_buf(msg, wbuf);
+ wpabuf_free(wbuf);
+ return 0;
+}
+
+
+int wps_build_cred(struct wps_data *wps, struct wpabuf *msg)
+{
+ struct wpabuf *cred;
+
+ if (wps->wps->registrar->skip_cred_build)
+ goto skip_cred_build;
+
+ wpa_printf(MSG_DEBUG, "WPS: * Credential");
+ if (wps->use_cred) {
+ os_memcpy(&wps->cred, wps->use_cred, sizeof(wps->cred));
+ goto use_provided;
+ }
+ os_memset(&wps->cred, 0, sizeof(wps->cred));
+
+ os_memcpy(wps->cred.ssid, wps->wps->ssid, wps->wps->ssid_len);
+ wps->cred.ssid_len = wps->wps->ssid_len;
+
+ /* Select the best authentication and encryption type */
+ if (wps->auth_type & WPS_AUTH_WPA2PSK)
+ wps->auth_type = WPS_AUTH_WPA2PSK;
+ else if (wps->auth_type & WPS_AUTH_WPAPSK)
+ wps->auth_type = WPS_AUTH_WPAPSK;
+ else if (wps->auth_type & WPS_WIFI_AUTH_OPEN)
+ wps->auth_type = WPS_WIFI_AUTH_OPEN;
+ else if (wps->auth_type & WPS_AUTH_SHARED)
+ wps->auth_type = WPS_AUTH_SHARED;
+ else {
+ wpa_printf(MSG_DEBUG, "WPS: Unsupported auth_type 0x%x",
+ wps->auth_type);
+ return -1;
+ }
+ wps->cred.auth_type = wps->auth_type;
+
+ if (wps->auth_type == WPS_AUTH_WPA2PSK ||
+ wps->auth_type == WPS_AUTH_WPAPSK) {
+ if (wps->encr_type & WPS_ENCR_AES)
+ wps->encr_type = WPS_ENCR_AES;
+ else if (wps->encr_type & WPS_ENCR_TKIP)
+ wps->encr_type = WPS_ENCR_TKIP;
+ else {
+ wpa_printf(MSG_DEBUG, "WPS: No suitable encryption "
+ "type for WPA/WPA2");
+ return -1;
+ }
+ } else {
+ if (wps->encr_type & WPS_ENCR_WEP)
+ wps->encr_type = WPS_ENCR_WEP;
+ else if (wps->encr_type & WPS_ENCR_NONE)
+ wps->encr_type = WPS_ENCR_NONE;
+ else {
+ wpa_printf(MSG_DEBUG, "WPS: No suitable encryption "
+ "type for non-WPA/WPA2 mode");
+ return -1;
+ }
+ }
+ wps->cred.encr_type = wps->encr_type;
+ /*
+ * Set MAC address in the Credential to be the Enrollee's MAC address
+ */
+ os_memcpy(wps->cred.mac_addr, wps->mac_addr_e, ETH_ALEN);
+
+ if (wps->wps->wps_state == WPS_STATE_NOT_CONFIGURED && wps->wps->ap &&
+ !wps->wps->registrar->disable_auto_conf) {
+ u8 r[16];
+ /* Generate a random passphrase */
+ if (random_get_bytes(r, sizeof(r)) < 0)
+ return -1;
+ os_free(wps->new_psk);
+ //wps->new_psk = base64_encode(r, sizeof(r), &wps->new_psk_len);
+ if (wps->new_psk == NULL)
+ return -1;
+ wps->new_psk_len--; /* remove newline */
+ while (wps->new_psk_len &&
+ wps->new_psk[wps->new_psk_len - 1] == '=')
+ wps->new_psk_len--;
+ wpa_hexdump_ascii_key(MSG_DEBUG, "WPS: Generated passphrase",
+ wps->new_psk, wps->new_psk_len);
+ os_memcpy(wps->cred.key, wps->new_psk, wps->new_psk_len);
+ wps->cred.key_len = wps->new_psk_len;
+ } else if (wps->use_psk_key && wps->wps->psk_set) {
+ char hex[65];
+ wpa_printf(MSG_DEBUG, "WPS: Use PSK format for Network Key");
+ //wpa_snprintf_hex(hex, sizeof(hex), wps->wps->psk, 32);
+ os_memcpy(wps->cred.key, hex, 32 * 2);
+ wps->cred.key_len = 32 * 2;
+ } else if (wps->wps->network_key) {
+ os_memcpy(wps->cred.key, wps->wps->network_key,
+ wps->wps->network_key_len);
+ wps->cred.key_len = wps->wps->network_key_len;
+ } else if (wps->auth_type & (WPS_AUTH_WPAPSK | WPS_AUTH_WPA2PSK)) {
+ char hex[65];
+ /* Generate a random per-device PSK */
+ os_free(wps->new_psk);
+ wps->new_psk_len = 32;
+ wps->new_psk = (u8 *)os_malloc(wps->new_psk_len);
+ if (wps->new_psk == NULL)
+ return -1;
+ if (random_get_bytes(wps->new_psk, wps->new_psk_len) < 0) {
+ os_free(wps->new_psk);
+ wps->new_psk = NULL;
+ return -1;
+ }
+ wpa_hexdump_key(MSG_DEBUG, "WPS: Generated per-device PSK",
+ wps->new_psk, wps->new_psk_len);
+ //wpa_snprintf_hex(hex, sizeof(hex), wps->new_psk,
+ // wps->new_psk_len);
+ os_memcpy(wps->cred.key, hex, wps->new_psk_len * 2);
+ wps->cred.key_len = wps->new_psk_len * 2;
+ }
+
+use_provided:
+#ifdef CONFIG_WPS_TESTING
+ if (wps_testing_dummy_cred)
+ cred = wpabuf_alloc(200);
+ else
+ cred = NULL;
+ if (cred) {
+ struct wps_credential dummy;
+ wpa_printf(MSG_DEBUG, "WPS: Add dummy credential");
+ os_memset(&dummy, 0, sizeof(dummy));
+ os_memcpy(dummy.ssid, "dummy", 5);
+ dummy.ssid_len = 5;
+ dummy.auth_type = WPS_AUTH_WPA2PSK;
+ dummy.encr_type = WPS_ENCR_AES;
+ os_memcpy(dummy.key, "dummy psk", 9);
+ dummy.key_len = 9;
+ os_memcpy(dummy.mac_addr, wps->mac_addr_e, ETH_ALEN);
+ wps_build_credential(cred, &dummy);
+ wpa_hexdump_buf(MSG_DEBUG, "WPS: Dummy Credential", cred);
+
+ wpabuf_put_be16(msg, ATTR_CRED);
+ wpabuf_put_be16(msg, wpabuf_len(cred));
+ wpabuf_put_buf(msg, cred);
+
+ wpabuf_free(cred);
+ }
+#endif /* CONFIG_WPS_TESTING */
+
+ cred = wpabuf_alloc(200);
+ if (cred == NULL)
+ return -1;
+
+ if (wps_build_credential(cred, &wps->cred)) {
+ wpabuf_free(cred);
+ return -1;
+ }
+
+ wpabuf_put_be16(msg, ATTR_CRED);
+ wpabuf_put_be16(msg, wpabuf_len(cred));
+ wpabuf_put_buf(msg, cred);
+ wpabuf_free(cred);
+
+skip_cred_build:
+ if (wps->wps->registrar->extra_cred) {
+ wpa_printf(MSG_DEBUG, "WPS: * Credential (pre-configured)");
+ wpabuf_put_buf(msg, wps->wps->registrar->extra_cred);
+ }
+
+ return 0;
+}
+
+
+static int wps_build_ap_settings(struct wps_data *wps, struct wpabuf *msg)
+{
+ wpa_printf(MSG_DEBUG, "WPS: * AP Settings");
+
+ if (wps_build_credential(msg, &wps->cred))
+ return -1;
+
+ return 0;
+}
+
+
+static struct wpabuf * wps_build_ap_cred(struct wps_data *wps)
+{
+ struct wpabuf *msg, *plain;
+
+ msg = wpabuf_alloc(1000);
+ if (msg == NULL)
+ return NULL;
+
+ plain = wpabuf_alloc(200);
+ if (plain == NULL) {
+ wpabuf_free(msg);
+ return NULL;
+ }
+
+ if (wps_build_ap_settings(wps, plain)) {
+ wpabuf_free(plain);
+ wpabuf_free(msg);
+ return NULL;
+ }
+
+ wpabuf_put_be16(msg, ATTR_CRED);
+ wpabuf_put_be16(msg, wpabuf_len(plain));
+ wpabuf_put_buf(msg, plain);
+ wpabuf_free(plain);
+
+ return msg;
+}
+
+
+static struct wpabuf * wps_build_m2(struct wps_data *wps)
+{
+ struct wpabuf *msg;
+
+ if (random_get_bytes(wps->nonce_r, WPS_NONCE_LEN) < 0)
+ return NULL;
+ wpa_hexdump(MSG_DEBUG, "WPS: Registrar Nonce",
+ wps->nonce_r, WPS_NONCE_LEN);
+ wpa_hexdump(MSG_DEBUG, "WPS: UUID-R", wps->uuid_r, WPS_UUID_LEN);
+
+ wpa_printf(MSG_DEBUG, "WPS: Building Message M2");
+ msg = wpabuf_alloc(1000);
+ if (msg == NULL)
+ return NULL;
+
+ if (wps_build_version(msg) ||
+ wps_build_msg_type(msg, WPS_M2) ||
+ wps_build_enrollee_nonce(wps, msg) ||
+ wps_build_registrar_nonce(wps, msg) ||
+ wps_build_uuid_r(wps, msg) ||
+ wps_build_public_key(wps, msg, WPS_CALC_KEY_NORMAL) ||
+ wps_derive_keys(wps) ||
+ wps_build_auth_type_flags(wps, msg) ||
+ wps_build_encr_type_flags(wps, msg) ||
+ wps_build_conn_type_flags(wps, msg) ||
+ wps_build_config_methods_r(wps->wps->registrar, msg) ||
+ wps_build_device_attrs(&wps->wps->dev, msg) ||
+ wps_build_rf_bands(&wps->wps->dev, msg) ||
+ wps_build_assoc_state(wps, msg) ||
+ wps_build_config_error(msg, WPS_CFG_NO_ERROR) ||
+ wps_build_dev_password_id(msg, wps->dev_pw_id) ||
+ wps_build_os_version(&wps->wps->dev, msg) ||
+ wps_build_wfa_ext(msg, 0, NULL, 0) ||
+ wps_build_authenticator(wps, msg)) {
+ wpabuf_free(msg);
+ return NULL;
+ }
+
+ wps->int_reg = 1;
+ wps->state = RECV_M3;
+ return msg;
+}
+
+
+static struct wpabuf * wps_build_m2d(struct wps_data *wps)
+{
+ struct wpabuf *msg;
+ u16 err = wps->config_error;
+
+ wpa_printf(MSG_DEBUG, "WPS: Building Message M2D");
+ msg = wpabuf_alloc(1000);
+ if (msg == NULL)
+ return NULL;
+
+ if (wps->wps->ap && wps->wps->ap_setup_locked &&
+ err == WPS_CFG_NO_ERROR)
+ err = WPS_CFG_SETUP_LOCKED;
+
+ if (wps_build_version(msg) ||
+ wps_build_msg_type(msg, WPS_M2D) ||
+ wps_build_enrollee_nonce(wps, msg) ||
+ wps_build_registrar_nonce(wps, msg) ||
+ wps_build_uuid_r(wps, msg) ||
+ wps_build_auth_type_flags(wps, msg) ||
+ wps_build_encr_type_flags(wps, msg) ||
+ wps_build_conn_type_flags(wps, msg) ||
+ wps_build_config_methods_r(wps->wps->registrar, msg) ||
+ wps_build_device_attrs(&wps->wps->dev, msg) ||
+ wps_build_rf_bands(&wps->wps->dev, msg) ||
+ wps_build_assoc_state(wps, msg) ||
+ wps_build_config_error(msg, err) ||
+ wps_build_os_version(&wps->wps->dev, msg) ||
+ wps_build_wfa_ext(msg, 0, NULL, 0)) {
+ wpabuf_free(msg);
+ return NULL;
+ }
+
+ wps->state = RECV_M2D_ACK;
+ return msg;
+}
+
+
+static struct wpabuf * wps_build_m4(struct wps_data *wps)
+{
+ struct wpabuf *msg, *plain;
+
+ wpa_printf(MSG_DEBUG, "WPS: Building Message M4");
+
+ wps_derive_psk(wps, wps->dev_password, wps->dev_password_len);
+
+ plain = wpabuf_alloc(200);
+ if (plain == NULL)
+ return NULL;
+
+ msg = wpabuf_alloc(1000);
+ if (msg == NULL) {
+ wpabuf_free(plain);
+ return NULL;
+ }
+
+ if (wps_build_version(msg) ||
+ wps_build_msg_type(msg, WPS_M4) ||
+ wps_build_enrollee_nonce(wps, msg) ||
+ wps_build_r_hash(wps, msg) ||
+ wps_build_r_snonce1(wps, plain) ||
+ wps_build_key_wrap_auth(wps, plain) ||
+ wps_build_encr_settings(wps, msg, plain) ||
+ wps_build_wfa_ext(msg, 0, NULL, 0) ||
+ wps_build_authenticator(wps, msg)) {
+ wpabuf_free(plain);
+ wpabuf_free(msg);
+ return NULL;
+ }
+ wpabuf_free(plain);
+
+ wps->state = RECV_M5;
+ return msg;
+}
+
+
+static struct wpabuf * wps_build_m6(struct wps_data *wps)
+{
+ struct wpabuf *msg, *plain;
+
+ wpa_printf(MSG_DEBUG, "WPS: Building Message M6");
+
+ plain = wpabuf_alloc(200);
+ if (plain == NULL)
+ return NULL;
+
+ msg = wpabuf_alloc(1000);
+ if (msg == NULL) {
+ wpabuf_free(plain);
+ return NULL;
+ }
+
+ if (wps_build_version(msg) ||
+ wps_build_msg_type(msg, WPS_M6) ||
+ wps_build_enrollee_nonce(wps, msg) ||
+ wps_build_r_snonce2(wps, plain) ||
+ wps_build_key_wrap_auth(wps, plain) ||
+ wps_build_encr_settings(wps, msg, plain) ||
+ wps_build_wfa_ext(msg, 0, NULL, 0) ||
+ wps_build_authenticator(wps, msg)) {
+ wpabuf_free(plain);
+ wpabuf_free(msg);
+ return NULL;
+ }
+ wpabuf_free(plain);
+
+ wps->wps_pin_revealed = 1;
+ wps->state = RECV_M7;
+ return msg;
+}
+
+
+static struct wpabuf * wps_build_m8(struct wps_data *wps)
+{
+ struct wpabuf *msg, *plain;
+
+ wpa_printf(MSG_DEBUG, "WPS: Building Message M8");
+
+ plain = wpabuf_alloc(500);
+ if (plain == NULL)
+ return NULL;
+
+ msg = wpabuf_alloc(1000);
+ if (msg == NULL) {
+ wpabuf_free(plain);
+ return NULL;
+ }
+
+ if (wps_build_version(msg) ||
+ wps_build_msg_type(msg, WPS_M8) ||
+ wps_build_enrollee_nonce(wps, msg) ||
+ ((wps->wps->ap || wps->er) && wps_build_cred(wps, plain)) ||
+ (!wps->wps->ap && !wps->er && wps_build_ap_settings(wps, plain)) ||
+ wps_build_key_wrap_auth(wps, plain) ||
+ wps_build_encr_settings(wps, msg, plain) ||
+ wps_build_wfa_ext(msg, 0, NULL, 0) ||
+ wps_build_authenticator(wps, msg)) {
+ wpabuf_free(plain);
+ wpabuf_free(msg);
+ return NULL;
+ }
+ wpabuf_free(plain);
+
+ wps->state = RECV_DONE;
+ return msg;
+}
+
+
+struct wpabuf * wps_registrar_get_msg(struct wps_data *wps,
+ enum wsc_op_code *op_code)
+{
+ struct wpabuf *msg;
+
+#ifdef CONFIG_WPS_UPNP
+ if (!wps->int_reg && wps->wps->wps_upnp) {
+ struct upnp_pending_message *p, *prev = NULL;
+ if (wps->ext_reg > 1)
+ wps_registrar_free_pending_m2(wps->wps);
+ p = wps->wps->upnp_msgs;
+ /* TODO: check pending message MAC address */
+ while (p && p->next) {
+ prev = p;
+ p = p->next;
+ }
+ if (p) {
+ wpa_printf(MSG_DEBUG, "WPS: Use pending message from "
+ "UPnP");
+ if (prev)
+ prev->next = NULL;
+ else
+ wps->wps->upnp_msgs = NULL;
+ msg = p->msg;
+ switch (p->type) {
+ case WPS_WSC_ACK:
+ *op_code = WSC_ACK;
+ break;
+ case WPS_WSC_NACK:
+ *op_code = WSC_NACK;
+ break;
+ default:
+ *op_code = WSC_MSG;
+ break;
+ }
+ os_free(p);
+ if (wps->ext_reg == 0)
+ wps->ext_reg = 1;
+ return msg;
+ }
+ }
+ if (wps->ext_reg) {
+ wpa_printf(MSG_DEBUG, "WPS: Using external Registrar, but no "
+ "pending message available");
+ return NULL;
+ }
+#endif /* CONFIG_WPS_UPNP */
+
+ switch (wps->state) {
+ case SEND_M2:
+ if (wps_get_dev_password(wps) < 0)
+ msg = wps_build_m2d(wps);
+ else
+ msg = wps_build_m2(wps);
+ *op_code = WSC_MSG;
+ break;
+ case SEND_M2D:
+ msg = wps_build_m2d(wps);
+ *op_code = WSC_MSG;
+ break;
+ case SEND_M4:
+ msg = wps_build_m4(wps);
+ *op_code = WSC_MSG;
+ break;
+ case SEND_M6:
+ msg = wps_build_m6(wps);
+ *op_code = WSC_MSG;
+ break;
+ case SEND_M8:
+ msg = wps_build_m8(wps);
+ *op_code = WSC_MSG;
+ break;
+ case RECV_DONE:
+ msg = wps_build_wsc_ack(wps);
+ *op_code = WSC_ACK;
+ break;
+ case SEND_WSC_NACK:
+ msg = wps_build_wsc_nack(wps);
+ *op_code = WSC_NACK;
+ break;
+ default:
+ wpa_printf(MSG_DEBUG, "WPS: Unsupported state %d for building "
+ "a message", wps->state);
+ msg = NULL;
+ break;
+ }
+
+ if (*op_code == WSC_MSG && msg) {
+ /* Save a copy of the last message for Authenticator derivation
+ */
+ wpabuf_free(wps->last_msg);
+ wps->last_msg = wpabuf_dup(msg);
+ }
+
+ return msg;
+}
+
+
+static int wps_process_enrollee_nonce(struct wps_data *wps, const u8 *e_nonce)
+{
+ if (e_nonce == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS: No Enrollee Nonce received");
+ return -1;
+ }
+
+ os_memcpy(wps->nonce_e, e_nonce, WPS_NONCE_LEN);
+ wpa_hexdump(MSG_DEBUG, "WPS: Enrollee Nonce",
+ wps->nonce_e, WPS_NONCE_LEN);
+
+ return 0;
+}
+
+
+static int wps_process_registrar_nonce(struct wps_data *wps, const u8 *r_nonce)
+{
+ if (r_nonce == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS: No Registrar Nonce received");
+ return -1;
+ }
+
+ if (os_memcmp(wps->nonce_r, r_nonce, WPS_NONCE_LEN) != 0) {
+ wpa_printf(MSG_DEBUG, "WPS: Invalid Registrar Nonce received");
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static int wps_process_uuid_e(struct wps_data *wps, const u8 *uuid_e)
+{
+ if (uuid_e == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS: No UUID-E received");
+ return -1;
+ }
+
+ os_memcpy(wps->uuid_e, uuid_e, WPS_UUID_LEN);
+ wpa_hexdump(MSG_DEBUG, "WPS: UUID-E", wps->uuid_e, WPS_UUID_LEN);
+
+ return 0;
+}
+
+
+static int wps_process_dev_password_id(struct wps_data *wps, const u8 *pw_id)
+{
+ if (pw_id == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS: No Device Password ID received");
+ return -1;
+ }
+
+ wps->dev_pw_id = WPA_GET_BE16(pw_id);
+ wpa_printf(MSG_DEBUG, "WPS: Device Password ID %d", wps->dev_pw_id);
+
+ return 0;
+}
+
+
+static int wps_process_e_hash1(struct wps_data *wps, const u8 *e_hash1)
+{
+ if (e_hash1 == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS: No E-Hash1 received");
+ return -1;
+ }
+
+ os_memcpy(wps->peer_hash1, e_hash1, WPS_HASH_LEN);
+ wpa_hexdump(MSG_DEBUG, "WPS: E-Hash1", wps->peer_hash1, WPS_HASH_LEN);
+
+ return 0;
+}
+
+
+static int wps_process_e_hash2(struct wps_data *wps, const u8 *e_hash2)
+{
+ if (e_hash2 == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS: No E-Hash2 received");
+ return -1;
+ }
+
+ os_memcpy(wps->peer_hash2, e_hash2, WPS_HASH_LEN);
+ wpa_hexdump(MSG_DEBUG, "WPS: E-Hash2", wps->peer_hash2, WPS_HASH_LEN);
+
+ return 0;
+}
+
+
+static int wps_process_e_snonce1(struct wps_data *wps, const u8 *e_snonce1)
+{
+ u8 hash[SHA256_MAC_LEN];
+ const u8 *addr[4];
+ size_t len[4];
+
+ if (e_snonce1 == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS: No E-SNonce1 received");
+ return -1;
+ }
+
+ wpa_hexdump_key(MSG_DEBUG, "WPS: E-SNonce1", e_snonce1,
+ WPS_SECRET_NONCE_LEN);
+
+ /* E-Hash1 = HMAC_AuthKey(E-S1 || PSK1 || PK_E || PK_R) */
+ addr[0] = e_snonce1;
+ len[0] = WPS_SECRET_NONCE_LEN;
+ addr[1] = wps->psk1;
+ len[1] = WPS_PSK_LEN;
+ addr[2] = wpabuf_head(wps->dh_pubkey_e);
+ len[2] = wpabuf_len(wps->dh_pubkey_e);
+ addr[3] = wpabuf_head(wps->dh_pubkey_r);
+ len[3] = wpabuf_len(wps->dh_pubkey_r);
+ if (wps_crypto_funcs.hmac_sha256_vector) {
+ wps_crypto_funcs.hmac_sha256_vector(wps->authkey, WPS_AUTHKEY_LEN, 4, addr, (int *)len, hash);
+ } else {
+ wpa_printf(MSG_ERROR, "In function %s, fail to register hmac_sha256_vector function!\r\n", __FUNCTION__);
+ return -1;
+ }
+ if (os_memcmp(wps->peer_hash1, hash, WPS_HASH_LEN) != 0) {
+ wpa_printf(MSG_DEBUG, "WPS: E-Hash1 derived from E-S1 does "
+ "not match with the pre-committed value");
+ wps->config_error = WPS_CFG_DEV_PASSWORD_AUTH_FAILURE;
+ wps_pwd_auth_fail_event(wps->wps, 0, 1);
+ return -1;
+ }
+
+ wpa_printf(MSG_DEBUG, "WPS: Enrollee proved knowledge of the first "
+ "half of the device password");
+
+ return 0;
+}
+
+
+static int wps_process_e_snonce2(struct wps_data *wps, const u8 *e_snonce2)
+{
+ u8 hash[SHA256_MAC_LEN];
+ const u8 *addr[4];
+ size_t len[4];
+
+ if (e_snonce2 == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS: No E-SNonce2 received");
+ return -1;
+ }
+
+ wpa_hexdump_key(MSG_DEBUG, "WPS: E-SNonce2", e_snonce2,
+ WPS_SECRET_NONCE_LEN);
+
+ /* E-Hash2 = HMAC_AuthKey(E-S2 || PSK2 || PK_E || PK_R) */
+ addr[0] = e_snonce2;
+ len[0] = WPS_SECRET_NONCE_LEN;
+ addr[1] = wps->psk2;
+ len[1] = WPS_PSK_LEN;
+ addr[2] = wpabuf_head(wps->dh_pubkey_e);
+ len[2] = wpabuf_len(wps->dh_pubkey_e);
+ addr[3] = wpabuf_head(wps->dh_pubkey_r);
+ len[3] = wpabuf_len(wps->dh_pubkey_r);
+
+ if (wps_crypto_funcs.hmac_sha256_vector) {
+ wps_crypto_funcs.hmac_sha256_vector(wps->authkey, WPS_AUTHKEY_LEN, 4, addr, (int *)len, hash);
+ } else {
+ wpa_printf(MSG_ERROR, "In function %s, fail to register hmac_sha256_vector function!\r\n", __FUNCTION__);
+ return -1;
+ }
+ if (os_memcmp(wps->peer_hash2, hash, WPS_HASH_LEN) != 0) {
+ wpa_printf(MSG_DEBUG, "WPS: E-Hash2 derived from E-S2 does "
+ "not match with the pre-committed value");
+#ifdef CONFIG_WPS_PIN
+ wps_registrar_invalidate_pin(wps->wps->registrar, wps->uuid_e);
+#endif
+ wps->config_error = WPS_CFG_DEV_PASSWORD_AUTH_FAILURE;
+ wps_pwd_auth_fail_event(wps->wps, 0, 2);
+ return -1;
+ }
+
+ wpa_printf(MSG_DEBUG, "WPS: Enrollee proved knowledge of the second "
+ "half of the device password");
+ wps->wps_pin_revealed = 0;
+#ifdef CONFIG_WPS_PIN
+ wps_registrar_unlock_pin(wps->wps->registrar, wps->uuid_e);
+
+ /*
+ * In case wildcard PIN is used and WPS handshake succeeds in the first
+ * attempt, wps_registrar_unlock_pin() would not free the PIN, so make
+ * sure the PIN gets invalidated here.
+ */
+ wps_registrar_invalidate_pin(wps->wps->registrar, wps->uuid_e);
+#endif
+ return 0;
+}
+
+
+static int wps_process_mac_addr(struct wps_data *wps, const u8 *mac_addr)
+{
+ if (mac_addr == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS: No MAC Address received");
+ return -1;
+ }
+
+ wpa_printf(MSG_DEBUG, "WPS: Enrollee MAC Address " MACSTR,
+ MAC2STR(mac_addr));
+ os_memcpy(wps->mac_addr_e, mac_addr, ETH_ALEN);
+ os_memcpy(wps->peer_dev.mac_addr, mac_addr, ETH_ALEN);
+
+ return 0;
+}
+
+
+static int wps_process_pubkey(struct wps_data *wps, const u8 *pk,
+ size_t pk_len)
+{
+ if (pk == NULL || pk_len == 0) {
+ wpa_printf(MSG_DEBUG, "WPS: No Public Key received");
+ return -1;
+ }
+
+ wpabuf_free(wps->dh_pubkey_e);
+ wps->dh_pubkey_e = wpabuf_alloc_copy(pk, pk_len);
+ if (wps->dh_pubkey_e == NULL)
+ return -1;
+
+ return 0;
+}
+
+
+static int wps_process_auth_type_flags(struct wps_data *wps, const u8 *auth)
+{
+ u16 auth_types;
+
+ if (auth == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS: No Authentication Type flags "
+ "received");
+ return -1;
+ }
+
+ auth_types = WPA_GET_BE16(auth);
+
+ wpa_printf(MSG_DEBUG, "WPS: Enrollee Authentication Type flags 0x%x",
+ auth_types);
+ wps->auth_type = wps->wps->auth_types & auth_types;
+ if (wps->auth_type == 0) {
+ wpa_printf(MSG_DEBUG, "WPS: No match in supported "
+ "authentication types (own 0x%x Enrollee 0x%x)",
+ wps->wps->auth_types, auth_types);
+#ifdef WPS_WORKAROUNDS
+ /*
+ * Some deployed implementations seem to advertise incorrect
+ * information in this attribute. For example, Linksys WRT350N
+ * seems to have a byteorder bug that breaks this negotiation.
+ * In order to interoperate with existing implementations,
+ * assume that the Enrollee supports everything we do.
+ */
+ wpa_printf(MSG_DEBUG, "WPS: Workaround - assume Enrollee "
+ "does not advertise supported authentication types "
+ "correctly");
+ wps->auth_type = wps->wps->auth_types;
+#else /* WPS_WORKAROUNDS */
+ return -1;
+#endif /* WPS_WORKAROUNDS */
+ }
+
+ return 0;
+}
+
+
+static int wps_process_encr_type_flags(struct wps_data *wps, const u8 *encr)
+{
+ u16 encr_types;
+
+ if (encr == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS: No Encryption Type flags "
+ "received");
+ return -1;
+ }
+
+ encr_types = WPA_GET_BE16(encr);
+
+ wpa_printf(MSG_DEBUG, "WPS: Enrollee Encryption Type flags 0x%x",
+ encr_types);
+ wps->encr_type = wps->wps->encr_types & encr_types;
+ if (wps->encr_type == 0) {
+ wpa_printf(MSG_DEBUG, "WPS: No match in supported "
+ "encryption types (own 0x%x Enrollee 0x%x)",
+ wps->wps->encr_types, encr_types);
+#ifdef WPS_WORKAROUNDS
+ /*
+ * Some deployed implementations seem to advertise incorrect
+ * information in this attribute. For example, Linksys WRT350N
+ * seems to have a byteorder bug that breaks this negotiation.
+ * In order to interoperate with existing implementations,
+ * assume that the Enrollee supports everything we do.
+ */
+ wpa_printf(MSG_DEBUG, "WPS: Workaround - assume Enrollee "
+ "does not advertise supported encryption types "
+ "correctly");
+ wps->encr_type = wps->wps->encr_types;
+#else /* WPS_WORKAROUNDS */
+ return -1;
+#endif /* WPS_WORKAROUNDS */
+ }
+
+ return 0;
+}
+
+
+static int wps_process_conn_type_flags(struct wps_data *wps, const u8 *conn)
+{
+ if (conn == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS: No Connection Type flags "
+ "received");
+ return -1;
+ }
+
+ wpa_printf(MSG_DEBUG, "WPS: Enrollee Connection Type flags 0x%x",
+ *conn);
+
+ return 0;
+}
+
+
+static int wps_process_config_methods(struct wps_data *wps, const u8 *methods)
+{
+ u16 m;
+
+ if (methods == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS: No Config Methods received");
+ return -1;
+ }
+
+ m = WPA_GET_BE16(methods);
+
+ wpa_printf(MSG_DEBUG, "WPS: Enrollee Config Methods 0x%x"
+ "%s%s%s%s%s%s%s%s%s", m,
+ m & WPS_CONFIG_USBA ? " [USBA]" : "",
+ m & WPS_CONFIG_ETHERNET ? " [Ethernet]" : "",
+ m & WPS_CONFIG_LABEL ? " [Label]" : "",
+ m & WPS_CONFIG_DISPLAY ? " [Display]" : "",
+ m & WPS_CONFIG_EXT_NFC_TOKEN ? " [Ext NFC Token]" : "",
+ m & WPS_CONFIG_INT_NFC_TOKEN ? " [Int NFC Token]" : "",
+ m & WPS_CONFIG_NFC_INTERFACE ? " [NFC]" : "",
+ m & WPS_CONFIG_PUSHBUTTON ? " [PBC]" : "",
+ m & WPS_CONFIG_KEYPAD ? " [Keypad]" : "");
+
+ if (!(m & WPS_CONFIG_DISPLAY) && !wps->use_psk_key) {
+ /*
+ * The Enrollee does not have a display so it is unlikely to be
+ * able to show the passphrase to a user and as such, could
+ * benefit from receiving PSK to reduce key derivation time.
+ */
+ wpa_printf(MSG_DEBUG, "WPS: Prefer PSK format key due to "
+ "Enrollee not supporting display");
+ wps->use_psk_key = 1;
+ }
+
+ return 0;
+}
+
+
+static int wps_process_wps_state(struct wps_data *wps, const u8 *state)
+{
+ if (state == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS: No Wi-Fi Protected Setup State "
+ "received");
+ return -1;
+ }
+
+ wpa_printf(MSG_DEBUG, "WPS: Enrollee Wi-Fi Protected Setup State %d",
+ *state);
+
+ return 0;
+}
+
+
+static int wps_process_assoc_state(struct wps_data *wps, const u8 *assoc)
+{
+ u16 a;
+
+ if (assoc == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS: No Association State received");
+ return -1;
+ }
+
+ a = WPA_GET_BE16(assoc);
+ wpa_printf(MSG_DEBUG, "WPS: Enrollee Association State %d", a);
+
+ return 0;
+}
+
+
+static int wps_process_config_error(struct wps_data *wps, const u8 *err)
+{
+ u16 e;
+
+ if (err == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS: No Configuration Error received");
+ return -1;
+ }
+
+ e = WPA_GET_BE16(err);
+ wpa_printf(MSG_DEBUG, "WPS: Enrollee Configuration Error %d", e);
+
+ return 0;
+}
+
+
+static int wps_registrar_p2p_dev_addr_match(struct wps_data *wps)
+{
+#ifdef CONFIG_P2P
+ struct wps_registrar *reg = wps->wps->registrar;
+
+ if (is_zero_ether_addr(reg->p2p_dev_addr))
+ return 1; /* no filtering in use */
+
+ if (os_memcmp(reg->p2p_dev_addr, wps->p2p_dev_addr, ETH_ALEN) != 0) {
+ wpa_printf(MSG_DEBUG, "WPS: No match on P2P Device Address "
+ "filtering for PBC: expected " MACSTR " was "
+ MACSTR " - indicate PBC session overlap",
+ MAC2STR(reg->p2p_dev_addr),
+ MAC2STR(wps->p2p_dev_addr));
+ return 0;
+ }
+#endif /* CONFIG_P2P */
+ return 1;
+}
+
+
+static int wps_registrar_skip_overlap(struct wps_data *wps)
+{
+#ifdef CONFIG_P2P
+ struct wps_registrar *reg = wps->wps->registrar;
+
+ if (is_zero_ether_addr(reg->p2p_dev_addr))
+ return 0; /* no specific Enrollee selected */
+
+ if (os_memcmp(reg->p2p_dev_addr, wps->p2p_dev_addr, ETH_ALEN) == 0) {
+ wpa_printf(MSG_DEBUG, "WPS: Skip PBC overlap due to selected "
+ "Enrollee match");
+ return 1;
+ }
+#endif /* CONFIG_P2P */
+ return 0;
+}
+
+
+static enum wps_process_res wps_process_m1(struct wps_data *wps,
+ struct wps_parse_attr *attr)
+{
+ wpa_printf(MSG_DEBUG, "WPS: Received M1");
+
+ if (wps->state != RECV_M1) {
+ wpa_printf(MSG_DEBUG, "WPS: Unexpected state (%d) for "
+ "receiving M1", wps->state);
+ return WPS_FAILURE;
+ }
+
+ if (wps_process_uuid_e(wps, attr->uuid_e) ||
+ wps_process_mac_addr(wps, attr->mac_addr) ||
+ wps_process_enrollee_nonce(wps, attr->enrollee_nonce) ||
+ wps_process_pubkey(wps, attr->public_key, attr->public_key_len) ||
+ wps_process_auth_type_flags(wps, attr->auth_type_flags) ||
+ wps_process_encr_type_flags(wps, attr->encr_type_flags) ||
+ wps_process_conn_type_flags(wps, attr->conn_type_flags) ||
+ wps_process_config_methods(wps, attr->config_methods) ||
+ wps_process_wps_state(wps, attr->wps_state) ||
+ wps_process_device_attrs(&wps->peer_dev, attr) ||
+ wps_process_rf_bands(&wps->peer_dev, attr->rf_bands) ||
+ wps_process_assoc_state(wps, attr->assoc_state) ||
+ wps_process_dev_password_id(wps, attr->dev_password_id) ||
+ wps_process_config_error(wps, attr->config_error) ||
+ wps_process_os_version(&wps->peer_dev, attr->os_version))
+ return WPS_FAILURE;
+
+ if (wps->dev_pw_id < 0x10 &&
+ wps->dev_pw_id != DEV_PW_DEFAULT &&
+ wps->dev_pw_id != DEV_PW_USER_SPECIFIED &&
+ wps->dev_pw_id != DEV_PW_MACHINE_SPECIFIED &&
+ wps->dev_pw_id != DEV_PW_REGISTRAR_SPECIFIED &&
+ (wps->dev_pw_id != DEV_PW_PUSHBUTTON ||
+ !wps->wps->registrar->pbc)) {
+ wpa_printf(MSG_DEBUG, "WPS: Unsupported Device Password ID %d",
+ wps->dev_pw_id);
+ wps->state = SEND_M2D;
+ return WPS_CONTINUE;
+ }
+
+#ifdef CONFIG_WPS_NFC
+ if (wps->dev_pw_id >= 0x10) {
+ struct wps_nfc_pw_token *token;
+ const u8 *addr[1];
+ u8 hash[WPS_HASH_LEN];
+
+ token = wps_get_nfc_pw_token(
+ &wps->wps->registrar->nfc_pw_tokens, wps->dev_pw_id);
+ if (token) {
+ wpa_printf(MSG_DEBUG, "WPS: Found matching NFC "
+ "Password Token");
+ dl_list_del(&token->list);
+ wps->nfc_pw_token = token;
+
+ addr[0] = attr->public_key;
+ if (wps_crypto_funcs.sha256_vector) {
+ wps_crypto_funcs.sha256_vector(1, addr, &attr->public_key_len, hash);
+ } else {
+ wpa_printf(MSG_ERROR, "In function %s, fail to register sha256_vector function!\r\n", __FUNCTION__);
+ return WPS_FAILURE;
+ }
+ if (os_memcmp(hash, wps->nfc_pw_token->pubkey_hash,
+ WPS_OOB_PUBKEY_HASH_LEN) != 0) {
+ wpa_printf(MSG_ERROR, "WPS: Public Key hash "
+ "mismatch");
+ return WPS_FAILURE;
+ }
+ }
+ }
+#endif /* CONFIG_WPS_NFC */
+
+ if (wps->dev_pw_id == DEV_PW_PUSHBUTTON) {
+ if ((wps->wps->registrar->force_pbc_overlap ||
+ wps_registrar_pbc_overlap(wps->wps->registrar,
+ wps->mac_addr_e, wps->uuid_e) ||
+ !wps_registrar_p2p_dev_addr_match(wps)) &&
+ !wps_registrar_skip_overlap(wps)) {
+ wpa_printf(MSG_DEBUG, "WPS: PBC overlap - deny PBC "
+ "negotiation");
+ wps->state = SEND_M2D;
+ wps->config_error = WPS_CFG_MULTIPLE_PBC_DETECTED;
+ wps_pbc_overlap_event(wps->wps);
+ wps_fail_event(wps->wps, WPS_M1,
+ WPS_CFG_MULTIPLE_PBC_DETECTED,
+ WPS_EI_NO_ERROR);
+ wps->wps->registrar->force_pbc_overlap = 1;
+ return WPS_CONTINUE;
+ }
+ wps_registrar_add_pbc_session(wps->wps->registrar,
+ wps->mac_addr_e, wps->uuid_e);
+ wps->pbc = 1;
+ }
+
+#ifdef WPS_WORKAROUNDS
+ /*
+ * It looks like Mac OS X 10.6.3 and 10.6.4 do not like Network Key in
+ * passphrase format. To avoid interop issues, force PSK format to be
+ * used.
+ */
+ if (!wps->use_psk_key &&
+ wps->peer_dev.manufacturer &&
+ os_strncmp(wps->peer_dev.manufacturer, "Apple ", 6) == 0 &&
+ wps->peer_dev.model_name &&
+ os_strcmp(wps->peer_dev.model_name, "AirPort") == 0) {
+ wpa_printf(MSG_DEBUG, "WPS: Workaround - Force Network Key in "
+ "PSK format");
+ wps->use_psk_key = 1;
+ }
+#endif /* WPS_WORKAROUNDS */
+
+ wps->state = SEND_M2;
+ return WPS_CONTINUE;
+}
+
+
+static enum wps_process_res wps_process_m3(struct wps_data *wps,
+ const struct wpabuf *msg,
+ struct wps_parse_attr *attr)
+{
+ wpa_printf(MSG_DEBUG, "WPS: Received M3");
+
+ if (wps->state != RECV_M3) {
+ wpa_printf(MSG_DEBUG, "WPS: Unexpected state (%d) for "
+ "receiving M3", wps->state);
+ wps->state = SEND_WSC_NACK;
+ return WPS_CONTINUE;
+ }
+
+ if (wps->pbc && wps->wps->registrar->force_pbc_overlap &&
+ !wps_registrar_skip_overlap(wps)) {
+ wpa_printf(MSG_DEBUG, "WPS: Reject negotiation due to PBC "
+ "session overlap");
+ wps->state = SEND_WSC_NACK;
+ wps->config_error = WPS_CFG_MULTIPLE_PBC_DETECTED;
+ return WPS_CONTINUE;
+ }
+
+ if (wps_process_registrar_nonce(wps, attr->registrar_nonce) ||
+ wps_process_authenticator(wps, attr->authenticator, msg) ||
+ wps_process_e_hash1(wps, attr->e_hash1) ||
+ wps_process_e_hash2(wps, attr->e_hash2)) {
+ wps->state = SEND_WSC_NACK;
+ return WPS_CONTINUE;
+ }
+
+ wps->state = SEND_M4;
+ return WPS_CONTINUE;
+}
+
+
+static enum wps_process_res wps_process_m5(struct wps_data *wps,
+ const struct wpabuf *msg,
+ struct wps_parse_attr *attr)
+{
+ struct wpabuf *decrypted;
+ struct wps_parse_attr eattr;
+
+ wpa_printf(MSG_DEBUG, "WPS: Received M5");
+
+ if (wps->state != RECV_M5) {
+ wpa_printf(MSG_DEBUG, "WPS: Unexpected state (%d) for "
+ "receiving M5", wps->state);
+ wps->state = SEND_WSC_NACK;
+ return WPS_CONTINUE;
+ }
+
+ if (wps->pbc && wps->wps->registrar->force_pbc_overlap &&
+ !wps_registrar_skip_overlap(wps)) {
+ wpa_printf(MSG_DEBUG, "WPS: Reject negotiation due to PBC "
+ "session overlap");
+ wps->state = SEND_WSC_NACK;
+ wps->config_error = WPS_CFG_MULTIPLE_PBC_DETECTED;
+ return WPS_CONTINUE;
+ }
+
+ if (wps_process_registrar_nonce(wps, attr->registrar_nonce) ||
+ wps_process_authenticator(wps, attr->authenticator, msg)) {
+ wps->state = SEND_WSC_NACK;
+ return WPS_CONTINUE;
+ }
+
+ decrypted = wps_decrypt_encr_settings(wps, attr->encr_settings,
+ attr->encr_settings_len);
+ if (decrypted == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS: Failed to decrypted Encrypted "
+ "Settings attribute");
+ wps->state = SEND_WSC_NACK;
+ return WPS_CONTINUE;
+ }
+
+ if (wps_validate_m5_encr(decrypted, attr->version2 != NULL) < 0) {
+ wpabuf_free(decrypted);
+ wps->state = SEND_WSC_NACK;
+ return WPS_CONTINUE;
+ }
+
+ wpa_printf(MSG_DEBUG, "WPS: Processing decrypted Encrypted Settings "
+ "attribute");
+ if (wps_parse_msg(decrypted, &eattr) < 0 ||
+ wps_process_key_wrap_auth(wps, decrypted, eattr.key_wrap_auth) ||
+ wps_process_e_snonce1(wps, eattr.e_snonce1)) {
+ wpabuf_free(decrypted);
+ wps->state = SEND_WSC_NACK;
+ return WPS_CONTINUE;
+ }
+ wpabuf_free(decrypted);
+
+ wps->state = SEND_M6;
+ return WPS_CONTINUE;
+}
+
+
+static void wps_sta_cred_cb(struct wps_data *wps)
+{
+ /*
+ * Update credential to only include a single authentication and
+ * encryption type in case the AP configuration includes more than one
+ * option.
+ */
+ if (wps->cred.auth_type & WPS_AUTH_WPA2PSK)
+ wps->cred.auth_type = WPS_AUTH_WPA2PSK;
+ else if (wps->cred.auth_type & WPS_AUTH_WPAPSK)
+ wps->cred.auth_type = WPS_AUTH_WPAPSK;
+ if (wps->cred.encr_type & WPS_ENCR_AES)
+ wps->cred.encr_type = WPS_ENCR_AES;
+ else if (wps->cred.encr_type & WPS_ENCR_TKIP)
+ wps->cred.encr_type = WPS_ENCR_TKIP;
+ wpa_printf(MSG_DEBUG, "WPS: Update local configuration based on the "
+ "AP configuration");
+ if (wps->wps->cred_cb)
+ wps->wps->cred_cb(wps->wps->cb_ctx, &wps->cred);
+}
+
+
+static void wps_cred_update(struct wps_credential *dst,
+ struct wps_credential *src)
+{
+ os_memcpy(dst->ssid, src->ssid, sizeof(dst->ssid));
+ dst->ssid_len = src->ssid_len;
+ dst->auth_type = src->auth_type;
+ dst->encr_type = src->encr_type;
+ dst->key_idx = src->key_idx;
+ os_memcpy(dst->key, src->key, sizeof(dst->key));
+ dst->key_len = src->key_len;
+}
+
+
+static int wps_process_ap_settings_r(struct wps_data *wps,
+ struct wps_parse_attr *attr)
+{
+ struct wpabuf *msg;
+
+ if (wps->wps->ap || wps->er)
+ return 0;
+
+ /* AP Settings Attributes in M7 when Enrollee is an AP */
+ if (wps_process_ap_settings(attr, &wps->cred) < 0)
+ return -1;
+
+ wpa_printf(MSG_INFO, "WPS: Received old AP configuration from AP");
+
+ if (wps->new_ap_settings) {
+ wpa_printf(MSG_INFO, "WPS: Update AP configuration based on "
+ "new settings");
+ wps_cred_update(&wps->cred, wps->new_ap_settings);
+ return 0;
+ } else {
+#ifdef CONFIG_WPS_PIN
+ /*
+ * Use the AP PIN only to receive the current AP settings, not
+ * to reconfigure the AP.
+ */
+
+ /*
+ * Clear selected registrar here since we do not get to
+ * WSC_Done in this protocol run.
+ */
+ wps_registrar_pin_completed(wps->wps->registrar);
+#endif
+ msg = wps_build_ap_cred(wps);
+ if (msg == NULL)
+ return -1;
+ wps->cred.cred_attr = wpabuf_head(msg);
+ wps->cred.cred_attr_len = wpabuf_len(msg);
+
+ if (wps->ap_settings_cb) {
+ wps->ap_settings_cb(wps->ap_settings_cb_ctx,
+ &wps->cred);
+ wpabuf_free(msg);
+ return 1;
+ }
+ wps_sta_cred_cb(wps);
+
+ wps->cred.cred_attr = NULL;
+ wps->cred.cred_attr_len = 0;
+ wpabuf_free(msg);
+
+ return 1;
+ }
+}
+
+
+static enum wps_process_res wps_process_m7(struct wps_data *wps,
+ const struct wpabuf *msg,
+ struct wps_parse_attr *attr)
+{
+ struct wpabuf *decrypted;
+ struct wps_parse_attr eattr;
+
+ wpa_printf(MSG_DEBUG, "WPS: Received M7");
+
+ if (wps->state != RECV_M7) {
+ wpa_printf(MSG_DEBUG, "WPS: Unexpected state (%d) for "
+ "receiving M7", wps->state);
+ wps->state = SEND_WSC_NACK;
+ return WPS_CONTINUE;
+ }
+
+ if (wps->pbc && wps->wps->registrar->force_pbc_overlap &&
+ !wps_registrar_skip_overlap(wps)) {
+ wpa_printf(MSG_DEBUG, "WPS: Reject negotiation due to PBC "
+ "session overlap");
+ wps->state = SEND_WSC_NACK;
+ wps->config_error = WPS_CFG_MULTIPLE_PBC_DETECTED;
+ return WPS_CONTINUE;
+ }
+
+ if (wps_process_registrar_nonce(wps, attr->registrar_nonce) ||
+ wps_process_authenticator(wps, attr->authenticator, msg)) {
+ wps->state = SEND_WSC_NACK;
+ return WPS_CONTINUE;
+ }
+
+ decrypted = wps_decrypt_encr_settings(wps, attr->encr_settings,
+ attr->encr_settings_len);
+ if (decrypted == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS: Failed to decrypt Encrypted "
+ "Settings attribute");
+ wps->state = SEND_WSC_NACK;
+ return WPS_CONTINUE;
+ }
+
+ if (wps_validate_m7_encr(decrypted, wps->wps->ap || wps->er,
+ attr->version2 != NULL) < 0) {
+ wpabuf_free(decrypted);
+ wps->state = SEND_WSC_NACK;
+ return WPS_CONTINUE;
+ }
+
+ wpa_printf(MSG_DEBUG, "WPS: Processing decrypted Encrypted Settings "
+ "attribute");
+ if (wps_parse_msg(decrypted, &eattr) < 0 ||
+ wps_process_key_wrap_auth(wps, decrypted, eattr.key_wrap_auth) ||
+ wps_process_e_snonce2(wps, eattr.e_snonce2) ||
+ wps_process_ap_settings_r(wps, &eattr)) {
+ wpabuf_free(decrypted);
+ wps->state = SEND_WSC_NACK;
+ return WPS_CONTINUE;
+ }
+
+ wpabuf_free(decrypted);
+
+ wps->state = SEND_M8;
+ return WPS_CONTINUE;
+}
+
+
+static enum wps_process_res wps_process_wsc_msg(struct wps_data *wps,
+ const struct wpabuf *msg)
+{
+ struct wps_parse_attr attr;
+ enum wps_process_res ret = WPS_CONTINUE;
+
+ wpa_printf(MSG_DEBUG, "WPS: Received WSC_MSG");
+
+ if (wps_parse_msg(msg, &attr) < 0)
+ return WPS_FAILURE;
+
+ if (attr.msg_type == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS: No Message Type attribute");
+ wps->state = SEND_WSC_NACK;
+ return WPS_CONTINUE;
+ }
+
+ if (*attr.msg_type != WPS_M1 &&
+ (attr.registrar_nonce == NULL ||
+ os_memcmp(wps->nonce_r, attr.registrar_nonce,
+ WPS_NONCE_LEN) != 0)) {
+ wpa_printf(MSG_DEBUG, "WPS: Mismatch in registrar nonce");
+ return WPS_FAILURE;
+ }
+
+ switch (*attr.msg_type) {
+ case WPS_M1:
+ if (wps_validate_m1(msg) < 0)
+ return WPS_FAILURE;
+#ifdef CONFIG_WPS_UPNP
+ if (wps->wps->wps_upnp && attr.mac_addr) {
+ /* Remove old pending messages when starting new run */
+ wps_free_pending_msgs(wps->wps->upnp_msgs);
+ wps->wps->upnp_msgs = NULL;
+
+ upnp_wps_device_send_wlan_event(
+ wps->wps->wps_upnp, attr.mac_addr,
+ UPNP_WPS_WLANEVENT_TYPE_EAP, msg);
+ }
+#endif /* CONFIG_WPS_UPNP */
+ ret = wps_process_m1(wps, &attr);
+ break;
+ case WPS_M3:
+ if (wps_validate_m3(msg) < 0)
+ return WPS_FAILURE;
+ ret = wps_process_m3(wps, msg, &attr);
+ if (ret == WPS_FAILURE || wps->state == SEND_WSC_NACK)
+ wps_fail_event(wps->wps, WPS_M3, wps->config_error,
+ wps->error_indication);
+ break;
+ case WPS_M5:
+ if (wps_validate_m5(msg) < 0)
+ return WPS_FAILURE;
+ ret = wps_process_m5(wps, msg, &attr);
+ if (ret == WPS_FAILURE || wps->state == SEND_WSC_NACK)
+ wps_fail_event(wps->wps, WPS_M5, wps->config_error,
+ wps->error_indication);
+ break;
+ case WPS_M7:
+ if (wps_validate_m7(msg) < 0)
+ return WPS_FAILURE;
+ ret = wps_process_m7(wps, msg, &attr);
+ if (ret == WPS_FAILURE || wps->state == SEND_WSC_NACK)
+ wps_fail_event(wps->wps, WPS_M7, wps->config_error,
+ wps->error_indication);
+ break;
+ default:
+ wpa_printf(MSG_DEBUG, "WPS: Unsupported Message Type %d",
+ *attr.msg_type);
+ return WPS_FAILURE;
+ }
+
+ if (ret == WPS_CONTINUE) {
+ /* Save a copy of the last message for Authenticator derivation
+ */
+ wpabuf_free(wps->last_msg);
+ wps->last_msg = wpabuf_dup(msg);
+ }
+
+ return ret;
+}
+
+
+static enum wps_process_res wps_process_wsc_ack(struct wps_data *wps,
+ const struct wpabuf *msg)
+{
+ struct wps_parse_attr attr;
+
+ wpa_printf(MSG_DEBUG, "WPS: Received WSC_ACK");
+
+ if (wps_parse_msg(msg, &attr) < 0)
+ return WPS_FAILURE;
+
+ if (attr.msg_type == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS: No Message Type attribute");
+ return WPS_FAILURE;
+ }
+
+ if (*attr.msg_type != WPS_WSC_ACK) {
+ wpa_printf(MSG_DEBUG, "WPS: Invalid Message Type %d",
+ *attr.msg_type);
+ return WPS_FAILURE;
+ }
+
+#ifdef CONFIG_WPS_UPNP
+ if (wps->wps->wps_upnp && wps->ext_reg && wps->state == RECV_M2D_ACK &&
+ upnp_wps_subscribers(wps->wps->wps_upnp)) {
+ if (wps->wps->upnp_msgs)
+ return WPS_CONTINUE;
+ wpa_printf(MSG_DEBUG, "WPS: Wait for response from an "
+ "external Registrar");
+ return WPS_PENDING;
+ }
+#endif /* CONFIG_WPS_UPNP */
+
+ if (attr.registrar_nonce == NULL ||
+ os_memcmp(wps->nonce_r, attr.registrar_nonce, WPS_NONCE_LEN) != 0)
+ {
+ wpa_printf(MSG_DEBUG, "WPS: Mismatch in registrar nonce");
+ return WPS_FAILURE;
+ }
+
+ if (attr.enrollee_nonce == NULL ||
+ os_memcmp(wps->nonce_e, attr.enrollee_nonce, WPS_NONCE_LEN) != 0) {
+ wpa_printf(MSG_DEBUG, "WPS: Mismatch in enrollee nonce");
+ return WPS_FAILURE;
+ }
+
+ if (wps->state == RECV_M2D_ACK) {
+#ifdef CONFIG_WPS_UPNP
+ if (wps->wps->wps_upnp &&
+ upnp_wps_subscribers(wps->wps->wps_upnp)) {
+ if (wps->wps->upnp_msgs)
+ return WPS_CONTINUE;
+ if (wps->ext_reg == 0)
+ wps->ext_reg = 1;
+ wpa_printf(MSG_DEBUG, "WPS: Wait for response from an "
+ "external Registrar");
+ return WPS_PENDING;
+ }
+#endif /* CONFIG_WPS_UPNP */
+
+ wpa_printf(MSG_DEBUG, "WPS: No more registrars available - "
+ "terminate negotiation");
+ }
+
+ return WPS_FAILURE;
+}
+
+
+static enum wps_process_res wps_process_wsc_nack(struct wps_data *wps,
+ const struct wpabuf *msg)
+{
+ struct wps_parse_attr attr;
+ int old_state;
+ u16 config_error;
+
+ wpa_printf(MSG_DEBUG, "WPS: Received WSC_NACK");
+
+ old_state = wps->state;
+ wps->state = SEND_WSC_NACK;
+
+ if (wps_parse_msg(msg, &attr) < 0)
+ return WPS_FAILURE;
+
+ if (attr.msg_type == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS: No Message Type attribute");
+ return WPS_FAILURE;
+ }
+
+ if (*attr.msg_type != WPS_WSC_NACK) {
+ wpa_printf(MSG_DEBUG, "WPS: Invalid Message Type %d",
+ *attr.msg_type);
+ return WPS_FAILURE;
+ }
+
+#ifdef CONFIG_WPS_UPNP
+ if (wps->wps->wps_upnp && wps->ext_reg) {
+ wpa_printf(MSG_DEBUG, "WPS: Negotiation using external "
+ "Registrar terminated by the Enrollee");
+ return WPS_FAILURE;
+ }
+#endif /* CONFIG_WPS_UPNP */
+
+ if (attr.registrar_nonce == NULL ||
+ os_memcmp(wps->nonce_r, attr.registrar_nonce, WPS_NONCE_LEN) != 0)
+ {
+ wpa_printf(MSG_DEBUG, "WPS: Mismatch in registrar nonce");
+ return WPS_FAILURE;
+ }
+
+ if (attr.enrollee_nonce == NULL ||
+ os_memcmp(wps->nonce_e, attr.enrollee_nonce, WPS_NONCE_LEN) != 0) {
+ wpa_printf(MSG_DEBUG, "WPS: Mismatch in enrollee nonce");
+ return WPS_FAILURE;
+ }
+
+ if (attr.config_error == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS: No Configuration Error attribute "
+ "in WSC_NACK");
+ return WPS_FAILURE;
+ }
+
+ config_error = WPA_GET_BE16(attr.config_error);
+ wpa_printf(MSG_DEBUG, "WPS: Enrollee terminated negotiation with "
+ "Configuration Error %d", config_error);
+
+ switch (old_state) {
+ case RECV_M3:
+ wps_fail_event(wps->wps, WPS_M2, config_error,
+ wps->error_indication);
+ break;
+ case RECV_M5:
+ wps_fail_event(wps->wps, WPS_M4, config_error,
+ wps->error_indication);
+ break;
+ case RECV_M7:
+ wps_fail_event(wps->wps, WPS_M6, config_error,
+ wps->error_indication);
+ break;
+ case RECV_DONE:
+ wps_fail_event(wps->wps, WPS_M8, config_error,
+ wps->error_indication);
+ break;
+ default:
+ break;
+ }
+
+ return WPS_FAILURE;
+}
+
+
+static enum wps_process_res wps_process_wsc_done(struct wps_data *wps,
+ const struct wpabuf *msg)
+{
+ struct wps_parse_attr attr;
+
+ wpa_printf(MSG_DEBUG, "WPS: Received WSC_Done");
+
+ //if (wps->state != RECV_DONE &&
+ // (!wps->wps->wps_upnp || !wps->ext_reg))
+ if (wps->state != RECV_DONE && (!wps->ext_reg)){
+ wpa_printf(MSG_DEBUG, "WPS: Unexpected state (%d) for "
+ "receiving WSC_Done", wps->state);
+ return WPS_FAILURE;
+ }
+
+ if (wps_parse_msg(msg, &attr) < 0)
+ return WPS_FAILURE;
+
+ if (attr.msg_type == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS: No Message Type attribute");
+ return WPS_FAILURE;
+ }
+
+ if (*attr.msg_type != WPS_WSC_DONE) {
+ wpa_printf(MSG_DEBUG, "WPS: Invalid Message Type %d",
+ *attr.msg_type);
+ return WPS_FAILURE;
+ }
+
+#ifdef CONFIG_WPS_UPNP
+ if (wps->wps->wps_upnp && wps->ext_reg) {
+ wpa_printf(MSG_DEBUG, "WPS: Negotiation using external "
+ "Registrar completed successfully");
+ wps_device_store(wps->wps->registrar, &wps->peer_dev,
+ wps->uuid_e);
+ return WPS_DONE;
+ }
+#endif /* CONFIG_WPS_UPNP */
+
+ if (attr.registrar_nonce == NULL ||
+ os_memcmp(wps->nonce_r, attr.registrar_nonce, WPS_NONCE_LEN) != 0)
+ {
+ wpa_printf(MSG_DEBUG, "WPS: Mismatch in registrar nonce");
+ return WPS_FAILURE;
+ }
+
+ if (attr.enrollee_nonce == NULL ||
+ os_memcmp(wps->nonce_e, attr.enrollee_nonce, WPS_NONCE_LEN) != 0) {
+ wpa_printf(MSG_DEBUG, "WPS: Mismatch in enrollee nonce");
+ return WPS_FAILURE;
+ }
+
+ wpa_printf(MSG_DEBUG, "WPS: Negotiation completed successfully");
+ wps_device_store(wps->wps->registrar, &wps->peer_dev,
+ wps->uuid_e);
+
+ if (wps->wps->wps_state == WPS_STATE_NOT_CONFIGURED && wps->new_psk &&
+ wps->wps->ap && !wps->wps->registrar->disable_auto_conf) {
+ struct wps_credential cred;
+
+ wpa_printf(MSG_DEBUG, "WPS: Moving to Configured state based "
+ "on first Enrollee connection");
+
+ os_memset(&cred, 0, sizeof(cred));
+ os_memcpy(cred.ssid, wps->wps->ssid, wps->wps->ssid_len);
+ cred.ssid_len = wps->wps->ssid_len;
+ cred.auth_type = WPS_AUTH_WPAPSK | WPS_AUTH_WPA2PSK;
+ cred.encr_type = WPS_ENCR_TKIP | WPS_ENCR_AES;
+ os_memcpy(cred.key, wps->new_psk, wps->new_psk_len);
+ cred.key_len = wps->new_psk_len;
+
+ wps->wps->wps_state = WPS_STATE_CONFIGURED;
+ wpa_hexdump_ascii_key(MSG_DEBUG,
+ "WPS: Generated random passphrase",
+ wps->new_psk, wps->new_psk_len);
+ if (wps->wps->cred_cb)
+ wps->wps->cred_cb(wps->wps->cb_ctx, &cred);
+
+ os_free(wps->new_psk);
+ wps->new_psk = NULL;
+ }
+
+ if (!wps->wps->ap && !wps->er)
+ wps_sta_cred_cb(wps);
+
+ if (wps->new_psk) {
+ if (wps_cb_new_psk(wps->wps->registrar, wps->mac_addr_e,
+ wps->new_psk, wps->new_psk_len)) {
+ wpa_printf(MSG_DEBUG, "WPS: Failed to configure the "
+ "new PSK");
+ }
+ os_free(wps->new_psk);
+ wps->new_psk = NULL;
+ }
+
+ wps_cb_reg_success(wps->wps->registrar, wps->mac_addr_e, wps->uuid_e,
+ wps->dev_password, wps->dev_password_len);
+
+ if (wps->pbc) {
+ wps_registrar_remove_pbc_session(wps->wps->registrar,
+ wps->uuid_e,
+ wps->p2p_dev_addr);
+ wps_registrar_pbc_completed(wps->wps->registrar);
+ os_get_time(&wps->wps->registrar->pbc_ignore_start);
+ os_memcpy(wps->wps->registrar->pbc_ignore_uuid, wps->uuid_e,
+ WPS_UUID_LEN);
+ } else {
+#ifdef CONFIG_WPS_PIN
+ wps_registrar_pin_completed(wps->wps->registrar);
+#endif
+ }
+ /* TODO: maintain AuthorizedMACs somewhere separately for each ER and
+ * merge them into APs own list.. */
+
+ wps_success_event(wps->wps);
+
+ return WPS_DONE;
+}
+
+
+enum wps_process_res wps_registrar_process_msg(struct wps_data *wps,
+ enum wsc_op_code op_code,
+ const struct wpabuf *msg)
+{
+ enum wps_process_res ret;
+
+ wpa_printf(MSG_DEBUG, "WPS: Processing received message (len=%lu "
+ "op_code=%d)",
+ (unsigned long) wpabuf_len(msg), op_code);
+
+#ifdef CONFIG_WPS_UPNP
+ if (wps->wps->wps_upnp && op_code == WSC_MSG && wps->ext_reg == 1) {
+ struct wps_parse_attr attr;
+ if (wps_parse_msg(msg, &attr) == 0 && attr.msg_type &&
+ *attr.msg_type == WPS_M3)
+ wps->ext_reg = 2; /* past M2/M2D phase */
+ }
+ if (wps->ext_reg > 1)
+ wps_registrar_free_pending_m2(wps->wps);
+ if (wps->wps->wps_upnp && wps->ext_reg &&
+ wps->wps->upnp_msgs == NULL &&
+ (op_code == WSC_MSG || op_code == WSC_Done || op_code == WSC_NACK))
+ {
+ struct wps_parse_attr attr;
+ int type;
+ if (wps_parse_msg(msg, &attr) < 0 || attr.msg_type == NULL)
+ type = -1;
+ else
+ type = *attr.msg_type;
+ wpa_printf(MSG_DEBUG, "WPS: Sending received message (type %d)"
+ " to external Registrar for processing", type);
+ upnp_wps_device_send_wlan_event(wps->wps->wps_upnp,
+ wps->mac_addr_e,
+ UPNP_WPS_WLANEVENT_TYPE_EAP,
+ msg);
+ if (op_code == WSC_MSG)
+ return WPS_PENDING;
+ } else if (wps->wps->wps_upnp && wps->ext_reg && op_code == WSC_MSG) {
+ wpa_printf(MSG_DEBUG, "WPS: Skip internal processing - using "
+ "external Registrar");
+ return WPS_CONTINUE;
+ }
+#endif /* CONFIG_WPS_UPNP */
+
+ switch (op_code) {
+ case WSC_MSG:
+ return wps_process_wsc_msg(wps, msg);
+ case WSC_ACK:
+ if (wps_validate_wsc_ack(msg) < 0)
+ return WPS_FAILURE;
+ return wps_process_wsc_ack(wps, msg);
+ case WSC_NACK:
+ if (wps_validate_wsc_nack(msg) < 0)
+ return WPS_FAILURE;
+ return wps_process_wsc_nack(wps, msg);
+ case WSC_Done:
+ if (wps_validate_wsc_done(msg) < 0)
+ return WPS_FAILURE;
+ ret = wps_process_wsc_done(wps, msg);
+ if (ret == WPS_FAILURE) {
+ wps->state = SEND_WSC_NACK;
+ wps_fail_event(wps->wps, WPS_WSC_DONE,
+ wps->config_error,
+ wps->error_indication);
+ }
+ return ret;
+ default:
+ wpa_printf(MSG_DEBUG, "WPS: Unsupported op_code %d", op_code);
+ return WPS_FAILURE;
+ }
+}
+
+
+int wps_registrar_update_ie(struct wps_registrar *reg)
+{
+ return wps_set_ie(reg);
+}
+
+
+//static void ICACHE_FLASH_ATTR wps_registrar_set_selected_timeout(void *eloop_ctx,
+// void *timeout_ctx)
+#if 0
+static void wps_registrar_set_selected_timeout(void *eloop_ctx)
+{
+ struct wps_registrar *reg = eloop_ctx;
+
+ wpa_printf(MSG_DEBUG, "WPS: Selected Registrar timeout - "
+ "unselect internal Registrar");
+ reg->selected_registrar = 0;
+ reg->pbc = 0;
+ wps_registrar_selected_registrar_changed(reg);
+}
+#endif
+
+#ifdef CONFIG_WPS_UPNP
+static void wps_registrar_sel_reg_add(struct wps_registrar *reg,
+ struct subscription *s)
+{
+ int i, j;
+ wpa_printf(MSG_DEBUG, "WPS: External Registrar selected (dev_pw_id=%d "
+ "config_methods=0x%x)",
+ s->dev_password_id, s->config_methods);
+ reg->sel_reg_union = 1;
+ if (reg->sel_reg_dev_password_id_override != DEV_PW_PUSHBUTTON)
+ reg->sel_reg_dev_password_id_override = s->dev_password_id;
+ if (reg->sel_reg_config_methods_override == -1)
+ reg->sel_reg_config_methods_override = 0;
+ reg->sel_reg_config_methods_override |= s->config_methods;
+ for (i = 0; i < WPS_MAX_AUTHORIZED_MACS; i++)
+ if (is_zero_ether_addr(reg->authorized_macs_union[i]))
+ break;
+ for (j = 0; i < WPS_MAX_AUTHORIZED_MACS && j < WPS_MAX_AUTHORIZED_MACS;
+ j++) {
+ if (is_zero_ether_addr(s->authorized_macs[j]))
+ break;
+ wpa_printf(MSG_DEBUG, "WPS: Add authorized MAC into union: "
+ MACSTR, MAC2STR(s->authorized_macs[j]));
+ os_memcpy(reg->authorized_macs_union[i],
+ s->authorized_macs[j], ETH_ALEN);
+ i++;
+ }
+ wpa_hexdump(MSG_DEBUG, "WPS: Authorized MACs union",
+ (u8 *) reg->authorized_macs_union,
+ sizeof(reg->authorized_macs_union));
+}
+#endif /* CONFIG_WPS_UPNP */
+
+
+static void wps_registrar_sel_reg_union(struct wps_registrar *reg)
+{
+#ifdef CONFIG_WPS_UPNP
+ struct subscription *s;
+
+ if (reg->wps->wps_upnp == NULL)
+ return;
+
+ dl_list_for_each(s, ®->wps->wps_upnp->subscriptions,
+ struct subscription, list) {
+ struct subscr_addr *sa;
+ sa = dl_list_first(&s->addr_list, struct subscr_addr, list);
+ if (sa) {
+ wpa_printf(MSG_DEBUG, "WPS: External Registrar %s:%d",
+ inet_ntoa(sa->saddr.sin_addr),
+ ntohs(sa->saddr.sin_port));
+ }
+ if (s->selected_registrar)
+ wps_registrar_sel_reg_add(reg, s);
+ else
+ wpa_printf(MSG_DEBUG, "WPS: External Registrar not "
+ "selected");
+ }
+#endif /* CONFIG_WPS_UPNP */
+}
+
+
+/**
+ * wps_registrar_selected_registrar_changed - SetSelectedRegistrar change
+ * @reg: Registrar data from wps_registrar_init()
+ *
+ * This function is called when selected registrar state changes, e.g., when an
+ * AP receives a SetSelectedRegistrar UPnP message.
+ */
+void wps_registrar_selected_registrar_changed(struct wps_registrar *reg)
+{
+ wpa_printf(MSG_DEBUG, "WPS: Selected registrar information changed");
+
+ reg->sel_reg_union = reg->selected_registrar;
+ reg->sel_reg_dev_password_id_override = -1;
+ reg->sel_reg_config_methods_override = -1;
+ os_memcpy(reg->authorized_macs_union, reg->authorized_macs,
+ WPS_MAX_AUTHORIZED_MACS * ETH_ALEN);
+ wpa_hexdump(MSG_DEBUG, "WPS: Authorized MACs union (start with own)",
+ (u8 *) reg->authorized_macs_union,
+ sizeof(reg->authorized_macs_union));
+ if (reg->selected_registrar) {
+ u16 methods;
+
+ methods = reg->wps->config_methods & ~WPS_CONFIG_PUSHBUTTON;
+#ifdef CONFIG_WPS2
+ methods &= ~(WPS_CONFIG_VIRT_PUSHBUTTON |
+ WPS_CONFIG_PHY_PUSHBUTTON);
+#endif /* CONFIG_WPS2 */
+ if (reg->pbc) {
+ reg->sel_reg_dev_password_id_override =
+ DEV_PW_PUSHBUTTON;
+ wps_set_pushbutton(&methods, reg->wps->config_methods);
+ }
+ wpa_printf(MSG_DEBUG, "WPS: Internal Registrar selected "
+ "(pbc=%d)", reg->pbc);
+ reg->sel_reg_config_methods_override = methods;
+ } else {
+ wpa_printf(MSG_DEBUG, "WPS: Internal Registrar not selected");
+ return;
+ }
+
+ wps_registrar_sel_reg_union(reg);
+
+ wps_set_ie(reg);
+ wps_cb_set_sel_reg(reg);
+}
+
+
+int wps_registrar_get_info(struct wps_registrar *reg, const u8 *addr,
+ char *buf, size_t buflen)
+{
+ struct wps_registrar_device *d;
+ int len = 0, ret;
+ char uuid[40];
+ char devtype[WPS_DEV_TYPE_BUFSIZE];
+
+ d = wps_device_get(reg, addr);
+ if (d == NULL)
+ return 0;
+ if (uuid_bin2str(d->uuid, uuid, sizeof(uuid)))
+ return 0;
+
+ ret = snprintf(buf + len, buflen - len,
+ "wpsUuid=%s\n"
+ "wpsPrimaryDeviceType=%s\n"
+ "wpsDeviceName=%s\n"
+ "wpsManufacturer=%s\n"
+ "wpsModelName=%s\n"
+ "wpsModelNumber=%s\n"
+ "wpsSerialNumber=%s\n",
+ uuid,
+ wps_dev_type_bin2str(d->dev.pri_dev_type, devtype,
+ sizeof(devtype)),
+ d->dev.device_name ? d->dev.device_name : "",
+ d->dev.manufacturer ? d->dev.manufacturer : "",
+ d->dev.model_name ? d->dev.model_name : "",
+ d->dev.model_number ? d->dev.model_number : "",
+ d->dev.serial_number ? d->dev.serial_number : "");
+ if (ret < 0 || (size_t) ret >= buflen - len)
+ return len;
+ len += ret;
+
+ return len;
+}
+
+
+int wps_registrar_config_ap(struct wps_registrar *reg,
+ struct wps_credential *cred)
+{
+#ifdef CONFIG_WPS2
+ if (!(cred->encr_type & (WPS_ENCR_NONE | WPS_ENCR_TKIP |
+ WPS_ENCR_AES))) {
+ if (cred->encr_type & WPS_ENCR_WEP) {
+ wpa_printf(MSG_INFO, "WPS: Reject new AP settings "
+ "due to WEP configuration");
+ return -1;
+ }
+
+ wpa_printf(MSG_INFO, "WPS: Reject new AP settings due to "
+ "invalid encr_type 0x%x", cred->encr_type);
+ return -1;
+ }
+
+ if ((cred->encr_type & (WPS_ENCR_TKIP | WPS_ENCR_AES)) ==
+ WPS_ENCR_TKIP) {
+ wpa_printf(MSG_DEBUG, "WPS: Upgrade encr_type TKIP -> "
+ "TKIP+AES");
+ cred->encr_type |= WPS_ENCR_AES;
+ }
+
+ if ((cred->auth_type & (WPS_AUTH_WPAPSK | WPS_AUTH_WPA2PSK)) ==
+ WPS_AUTH_WPAPSK) {
+ wpa_printf(MSG_DEBUG, "WPS: Upgrade auth_type WPAPSK -> "
+ "WPAPSK+WPA2PSK");
+ cred->auth_type |= WPS_AUTH_WPA2PSK;
+ }
+#endif /* CONFIG_WPS2 */
+
+ if (reg->wps->cred_cb)
+ return reg->wps->cred_cb(reg->wps->cb_ctx, cred);
+
+ return -1;
+}
+
+
+#ifdef CONFIG_WPS_NFC
+
+int wps_registrar_add_nfc_pw_token(struct wps_registrar *reg,
+ const u8 *pubkey_hash, u16 pw_id,
+ const u8 *dev_pw, size_t dev_pw_len)
+{
+ struct wps_nfc_pw_token *token;
+
+ if (dev_pw_len > WPS_OOB_DEVICE_PASSWORD_LEN)
+ return -1;
+
+ wps_free_nfc_pw_tokens(®->nfc_pw_tokens, pw_id);
+
+ token = os_zalloc(sizeof(*token));
+ if (token == NULL)
+ return -1;
+
+ os_memcpy(token->pubkey_hash, pubkey_hash, WPS_OOB_PUBKEY_HASH_LEN);
+ token->pw_id = pw_id;
+ os_memcpy(token->dev_pw, dev_pw, dev_pw_len);
+ token->dev_pw_len = dev_pw_len;
+
+ dl_list_add(®->nfc_pw_tokens, &token->list);
+
+ reg->selected_registrar = 1;
+ reg->pbc = 0;
+ wps_registrar_add_authorized_mac(reg,
+ (u8 *) "\xff\xff\xff\xff\xff\xff");
+ wps_registrar_selected_registrar_changed(reg);
+ #if 0
+ eloop_cancel_timeout(wps_registrar_set_selected_timeout, reg, NULL);
+ eloop_register_timeout(WPS_PBC_WALK_TIME, 0,
+ wps_registrar_set_selected_timeout,
+ reg, NULL);
+ #endif
+ return 0;
+}
+
+
+int wps_registrar_add_nfc_password_token(struct wps_registrar *reg,
+ const u8 *oob_dev_pw,
+ size_t oob_dev_pw_len)
+{
+ const u8 *pos, *hash, *dev_pw;
+ u16 id;
+ size_t dev_pw_len;
+
+ if (oob_dev_pw_len < WPS_OOB_PUBKEY_HASH_LEN + 2 +
+ WPS_OOB_DEVICE_PASSWORD_MIN_LEN ||
+ oob_dev_pw_len > WPS_OOB_PUBKEY_HASH_LEN + 2 +
+ WPS_OOB_DEVICE_PASSWORD_LEN)
+ return -1;
+
+ hash = oob_dev_pw;
+ pos = oob_dev_pw + WPS_OOB_PUBKEY_HASH_LEN;
+ id = WPA_GET_BE16(pos);
+ dev_pw = pos + 2;
+ dev_pw_len = oob_dev_pw + oob_dev_pw_len - dev_pw;
+
+ wpa_printf(MSG_DEBUG, "WPS: Add NFC Password Token for Password ID %u",
+ id);
+
+ wpa_hexdump(MSG_DEBUG, "WPS: Public Key Hash",
+ hash, WPS_OOB_PUBKEY_HASH_LEN);
+ wpa_hexdump_key(MSG_DEBUG, "WPS: Device Password", dev_pw, dev_pw_len);
+
+ return wps_registrar_add_nfc_pw_token(reg, hash, id, dev_pw,
+ dev_pw_len);
+}
+
+
+void wps_registrar_remove_nfc_pw_token(struct wps_registrar *reg,
+ struct wps_nfc_pw_token *token)
+{
+ wps_registrar_remove_authorized_mac(reg,
+ (u8 *) "\xff\xff\xff\xff\xff\xff");
+ wps_registrar_selected_registrar_changed(reg);
+}
+
+#endif /* CONFIG_WPS_NFC */
--- /dev/null
+/*
+ * Wi-Fi Protected Setup - Strict protocol validation routines
+ * Copyright (c) 2010, Atheros Communications, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+#include "wpa/includes.h"
+
+#include "wpa/common.h"
+#include "wps/wps_i.h"
+#include "wps/wps.h"
+
+
+#ifndef WPS_STRICT_ALL
+#define WPS_STRICT_WPS2
+#endif /* WPS_STRICT_ALL */
+
+
+static int wps_validate_version(const u8 *version, int mandatory)
+{
+ if (version == NULL) {
+ if (mandatory) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Version attribute "
+ "missing");
+ return -1;
+ }
+ return 0;
+ }
+ if (*version != 0x10) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Invalid Version attribute "
+ "value 0x%x", *version);
+ return -1;
+ }
+ return 0;
+}
+
+
+static int wps_validate_version2(const u8 *version2, int mandatory)
+{
+ if (version2 == NULL) {
+ if (mandatory) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Version2 attribute "
+ "missing");
+ return -1;
+ }
+ return 0;
+ }
+ if (*version2 < 0x20) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Invalid Version2 attribute "
+ "value 0x%x", *version2);
+ return -1;
+ }
+ return 0;
+}
+
+
+static int wps_validate_request_type(const u8 *request_type, int mandatory)
+{
+ if (request_type == NULL) {
+ if (mandatory) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Request Type "
+ "attribute missing");
+ return -1;
+ }
+ return 0;
+ }
+ if (*request_type > 0x03) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Invalid Request Type "
+ "attribute value 0x%x", *request_type);
+ return -1;
+ }
+ return 0;
+}
+
+
+static int wps_validate_response_type(const u8 *response_type, int mandatory)
+{
+ if (response_type == NULL) {
+ if (mandatory) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Response Type "
+ "attribute missing");
+ return -1;
+ }
+ return 0;
+ }
+ if (*response_type > 0x03) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Invalid Response Type "
+ "attribute value 0x%x", *response_type);
+ return -1;
+ }
+ return 0;
+}
+
+
+static int valid_config_methods(u16 val, int wps2)
+{
+ if (wps2) {
+ if ((val & 0x6000) && !(val & WPS_CONFIG_DISPLAY)) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Physical/Virtual "
+ "Display flag without old Display flag "
+ "set");
+ return 0;
+ }
+ if (!(val & 0x6000) && (val & WPS_CONFIG_DISPLAY)) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Display flag "
+ "without Physical/Virtual Display flag");
+ return 0;
+ }
+ if ((val & 0x0600) && !(val & WPS_CONFIG_PUSHBUTTON)) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Physical/Virtual "
+ "PushButton flag without old PushButton "
+ "flag set");
+ return 0;
+ }
+ if (!(val & 0x0600) && (val & WPS_CONFIG_PUSHBUTTON)) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: PushButton flag "
+ "without Physical/Virtual PushButton flag");
+ return 0;
+ }
+ }
+
+ return 1;
+}
+
+
+static int wps_validate_config_methods(const u8 *config_methods, int wps2,
+ int mandatory)
+{
+ u16 val;
+
+ if (config_methods == NULL) {
+ if (mandatory) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Configuration "
+ "Methods attribute missing");
+ return -1;
+ }
+ return 0;
+ }
+
+ val = WPA_GET_BE16(config_methods);
+ if (!valid_config_methods(val, wps2)) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Invalid Configuration "
+ "Methods attribute value 0x%04x", val);
+ return -1;
+ }
+ return 0;
+}
+
+
+static int wps_validate_ap_config_methods(const u8 *config_methods, int wps2,
+ int mandatory)
+{
+ u16 val;
+
+ if (wps_validate_config_methods(config_methods, wps2, mandatory) < 0)
+ return -1;
+ if (config_methods == NULL)
+ return 0;
+ val = WPA_GET_BE16(config_methods);
+ if (val & WPS_CONFIG_PUSHBUTTON) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Invalid Configuration "
+ "Methods attribute value 0x%04x in AP info "
+ "(PushButton not allowed for registering new ER)",
+ val);
+ return -1;
+ }
+ return 0;
+}
+
+
+static int wps_validate_uuid_e(const u8 *uuid_e, int mandatory)
+{
+ if (uuid_e == NULL) {
+ if (mandatory) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: UUID-E "
+ "attribute missing");
+ return -1;
+ }
+ return 0;
+ }
+ return 0;
+}
+
+
+static int wps_validate_uuid_r(const u8 *uuid_r, int mandatory)
+{
+ if (uuid_r == NULL) {
+ if (mandatory) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: UUID-R "
+ "attribute missing");
+ return -1;
+ }
+ return 0;
+ }
+ return 0;
+}
+
+
+static int wps_validate_primary_dev_type(const u8 *primary_dev_type,
+ int mandatory)
+{
+ if (primary_dev_type == NULL) {
+ if (mandatory) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Primary Device Type "
+ "attribute missing");
+ return -1;
+ }
+ return 0;
+ }
+ return 0;
+}
+
+
+static int wps_validate_rf_bands(const u8 *rf_bands, int mandatory)
+{
+ if (rf_bands == NULL) {
+ if (mandatory) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: RF Bands "
+ "attribute missing");
+ return -1;
+ }
+ return 0;
+ }
+ if (*rf_bands != WPS_RF_24GHZ && *rf_bands != WPS_RF_50GHZ &&
+ *rf_bands != (WPS_RF_24GHZ | WPS_RF_50GHZ)) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Invalid Rf Bands "
+ "attribute value 0x%x", *rf_bands);
+ return -1;
+ }
+ return 0;
+}
+
+
+static int wps_validate_assoc_state(const u8 *assoc_state, int mandatory)
+{
+ u16 val;
+ if (assoc_state == NULL) {
+ if (mandatory) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Association State "
+ "attribute missing");
+ return -1;
+ }
+ return 0;
+ }
+ val = WPA_GET_BE16(assoc_state);
+ if (val > 4) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Invalid Association State "
+ "attribute value 0x%04x", val);
+ return -1;
+ }
+ return 0;
+}
+
+
+static int wps_validate_config_error(const u8 *config_error, int mandatory)
+{
+ u16 val;
+
+ if (config_error == NULL) {
+ if (mandatory) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Configuration Error "
+ "attribute missing");
+ return -1;
+ }
+ return 0;
+ }
+ val = WPA_GET_BE16(config_error);
+ if (val > 18) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Invalid Configuration Error "
+ "attribute value 0x%04x", val);
+ return -1;
+ }
+ return 0;
+}
+
+
+static int wps_validate_dev_password_id(const u8 *dev_password_id,
+ int mandatory)
+{
+ u16 val;
+
+ if (dev_password_id == NULL) {
+ if (mandatory) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Device Password ID "
+ "attribute missing");
+ return -1;
+ }
+ return 0;
+ }
+ val = WPA_GET_BE16(dev_password_id);
+ if (val >= 0x0006 && val <= 0x000f) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Invalid Device Password ID "
+ "attribute value 0x%04x", val);
+ return -1;
+ }
+ return 0;
+}
+
+
+static int wps_validate_manufacturer(const u8 *manufacturer, size_t len,
+ int mandatory)
+{
+ if (manufacturer == NULL) {
+ if (mandatory) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Manufacturer "
+ "attribute missing");
+ return -1;
+ }
+ return 0;
+ }
+ if (len > 0 && manufacturer[len - 1] == 0) {
+ wpa_hexdump_ascii(MSG_INFO, "WPS-STRICT: Invalid Manufacturer "
+ "attribute value", manufacturer, len);
+ return -1;
+ }
+ return 0;
+}
+
+
+static int wps_validate_model_name(const u8 *model_name, size_t len,
+ int mandatory)
+{
+ if (model_name == NULL) {
+ if (mandatory) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Model Name "
+ "attribute missing");
+ return -1;
+ }
+ return 0;
+ }
+ if (len > 0 && model_name[len - 1] == 0) {
+ wpa_hexdump_ascii(MSG_INFO, "WPS-STRICT: Invalid Model Name "
+ "attribute value", model_name, len);
+ return -1;
+ }
+ return 0;
+}
+
+
+static int wps_validate_model_number(const u8 *model_number, size_t len,
+ int mandatory)
+{
+ if (model_number == NULL) {
+ if (mandatory) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Model Number "
+ "attribute missing");
+ return -1;
+ }
+ return 0;
+ }
+ if (len > 0 && model_number[len - 1] == 0) {
+ wpa_hexdump_ascii(MSG_INFO, "WPS-STRICT: Invalid Model Number "
+ "attribute value", model_number, len);
+ return -1;
+ }
+ return 0;
+}
+
+
+static int wps_validate_serial_number(const u8 *serial_number, size_t len,
+ int mandatory)
+{
+ if (serial_number == NULL) {
+ if (mandatory) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Serial Number "
+ "attribute missing");
+ return -1;
+ }
+ return 0;
+ }
+ if (len > 0 && serial_number[len - 1] == 0) {
+ wpa_hexdump_ascii(MSG_INFO, "WPS-STRICT: Invalid Serial "
+ "Number attribute value",
+ serial_number, len);
+ return -1;
+ }
+ return 0;
+}
+
+
+static int wps_validate_dev_name(const u8 *dev_name, size_t len,
+ int mandatory)
+{
+ if (dev_name == NULL) {
+ if (mandatory) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Device Name "
+ "attribute missing");
+ return -1;
+ }
+ return 0;
+ }
+ if (len > 0 && dev_name[len - 1] == 0) {
+ wpa_hexdump_ascii(MSG_INFO, "WPS-STRICT: Invalid Device Name "
+ "attribute value", dev_name, len);
+ return -1;
+ }
+ return 0;
+}
+
+
+static int wps_validate_request_to_enroll(const u8 *request_to_enroll,
+ int mandatory)
+{
+ if (request_to_enroll == NULL) {
+ if (mandatory) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Request to Enroll "
+ "attribute missing");
+ return -1;
+ }
+ return 0;
+ }
+ if (*request_to_enroll > 0x01) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Invalid Request to Enroll "
+ "attribute value 0x%x", *request_to_enroll);
+ return -1;
+ }
+ return 0;
+}
+
+
+static int wps_validate_req_dev_type(const u8 *req_dev_type[], size_t num,
+ int mandatory)
+{
+ if (num == 0) {
+ if (mandatory) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Requested Device "
+ "Type attribute missing");
+ return -1;
+ }
+ return 0;
+ }
+ return 0;
+}
+
+
+static int wps_validate_wps_state(const u8 *wps_state, int mandatory)
+{
+ if (wps_state == NULL) {
+ if (mandatory) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Wi-Fi Protected "
+ "Setup State attribute missing");
+ return -1;
+ }
+ return 0;
+ }
+ if (*wps_state != WPS_STATE_NOT_CONFIGURED &&
+ *wps_state != WPS_STATE_CONFIGURED) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Invalid Wi-Fi Protected "
+ "Setup State attribute value 0x%x", *wps_state);
+ return -1;
+ }
+ return 0;
+}
+
+
+static int wps_validate_ap_setup_locked(const u8 *ap_setup_locked,
+ int mandatory)
+{
+ if (ap_setup_locked == NULL) {
+ if (mandatory) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: AP Setup Locked "
+ "attribute missing");
+ return -1;
+ }
+ return 0;
+ }
+ if (*ap_setup_locked > 1) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Invalid AP Setup Locked "
+ "attribute value 0x%x", *ap_setup_locked);
+ return -1;
+ }
+ return 0;
+}
+
+
+static int wps_validate_selected_registrar(const u8 *selected_registrar,
+ int mandatory)
+{
+ if (selected_registrar == NULL) {
+ if (mandatory) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Selected Registrar "
+ "attribute missing");
+ return -1;
+ }
+ return 0;
+ }
+ if (*selected_registrar > 1) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Invalid Selected Registrar "
+ "attribute value 0x%x", *selected_registrar);
+ return -1;
+ }
+ return 0;
+}
+
+
+static int wps_validate_sel_reg_config_methods(const u8 *config_methods,
+ int wps2, int mandatory)
+{
+ u16 val;
+
+ if (config_methods == NULL) {
+ if (mandatory) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Selected Registrar "
+ "Configuration Methods attribute missing");
+ return -1;
+ }
+ return 0;
+ }
+
+ val = WPA_GET_BE16(config_methods);
+ if (!valid_config_methods(val, wps2)) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Invalid Selected Registrar "
+ "Configuration Methods attribute value 0x%04x",
+ val);
+ return -1;
+ }
+ return 0;
+}
+
+
+static int wps_validate_authorized_macs(const u8 *authorized_macs, size_t len,
+ int mandatory)
+{
+ if (authorized_macs == NULL) {
+ if (mandatory) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Authorized MACs "
+ "attribute missing");
+ return -1;
+ }
+ return 0;
+ }
+ if (len > 30 && (len % ETH_ALEN) != 0) {
+ wpa_hexdump(MSG_INFO, "WPS-STRICT: Invalid Authorized "
+ "MACs attribute value", authorized_macs, len);
+ return -1;
+ }
+ return 0;
+}
+
+
+static int wps_validate_msg_type(const u8 *msg_type, int mandatory)
+{
+ if (msg_type == NULL) {
+ if (mandatory) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Message Type "
+ "attribute missing");
+ return -1;
+ }
+ return 0;
+ }
+ if (*msg_type < WPS_Beacon || *msg_type > WPS_WSC_DONE) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Invalid Message Type "
+ "attribute value 0x%x", *msg_type);
+ return -1;
+ }
+ return 0;
+}
+
+
+static int wps_validate_mac_addr(const u8 *mac_addr, int mandatory)
+{
+ if (mac_addr == NULL) {
+ if (mandatory) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: MAC Address "
+ "attribute missing");
+ return -1;
+ }
+ return 0;
+ }
+ if (mac_addr[0] & 0x01) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Invalid MAC Address "
+ "attribute value " MACSTR, MAC2STR(mac_addr));
+ return -1;
+ }
+ return 0;
+}
+
+
+static int wps_validate_enrollee_nonce(const u8 *enrollee_nonce, int mandatory)
+{
+ if (enrollee_nonce == NULL) {
+ if (mandatory) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Enrollee Nonce "
+ "attribute missing");
+ return -1;
+ }
+ return 0;
+ }
+ return 0;
+}
+
+
+static int wps_validate_registrar_nonce(const u8 *registrar_nonce,
+ int mandatory)
+{
+ if (registrar_nonce == NULL) {
+ if (mandatory) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Registrar Nonce "
+ "attribute missing");
+ return -1;
+ }
+ return 0;
+ }
+ return 0;
+}
+
+
+static int wps_validate_public_key(const u8 *public_key, size_t len,
+ int mandatory)
+{
+ if (public_key == NULL) {
+ if (mandatory) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Public Key "
+ "attribute missing");
+ return -1;
+ }
+ return 0;
+ }
+ if (len != 192) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Invalid Public Key "
+ "attribute length %d", (int) len);
+ return -1;
+ }
+ return 0;
+}
+
+
+static int num_bits_set(u16 val)
+{
+ int c;
+ for (c = 0; val; c++)
+ val &= val - 1;
+ return c;
+}
+
+
+static int wps_validate_auth_type_flags(const u8 *flags, int mandatory)
+{
+ u16 val;
+
+ if (flags == NULL) {
+ if (mandatory) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Authentication Type "
+ "Flags attribute missing");
+ return -1;
+ }
+ return 0;
+ }
+ val = WPA_GET_BE16(flags);
+ if ((val & ~WPS_AUTH_TYPES) || !(val & WPS_AUTH_WPA2PSK)) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Invalid Authentication Type "
+ "Flags attribute value 0x%04x", val);
+ return -1;
+ }
+ return 0;
+}
+
+
+static int wps_validate_auth_type(const u8 *type, int mandatory)
+{
+ u16 val;
+
+ if (type == NULL) {
+ if (mandatory) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Authentication Type "
+ "attribute missing");
+ return -1;
+ }
+ return 0;
+ }
+ val = WPA_GET_BE16(type);
+ if ((val & ~WPS_AUTH_TYPES) || val == 0 ||
+ (num_bits_set(val) > 1 &&
+ val != (WPS_AUTH_WPAPSK | WPS_AUTH_WPA2PSK))) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Invalid Authentication Type "
+ "attribute value 0x%04x", val);
+ return -1;
+ }
+ return 0;
+}
+
+
+static int wps_validate_encr_type_flags(const u8 *flags, int mandatory)
+{
+ u16 val;
+
+ if (flags == NULL) {
+ if (mandatory) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Encryption Type "
+ "Flags attribute missing");
+ return -1;
+ }
+ return 0;
+ }
+ val = WPA_GET_BE16(flags);
+ if ((val & ~WPS_ENCR_TYPES) || !(val & WPS_ENCR_AES)) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Invalid Encryption Type "
+ "Flags attribute value 0x%04x", val);
+ return -1;
+ }
+ return 0;
+}
+
+
+static int wps_validate_encr_type(const u8 *type, int mandatory)
+{
+ u16 val;
+
+ if (type == NULL) {
+ if (mandatory) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Encryption Type "
+ "attribute missing");
+ return -1;
+ }
+ return 0;
+ }
+ val = WPA_GET_BE16(type);
+ if ((val & ~WPS_ENCR_TYPES) || val == 0 ||
+ (num_bits_set(val) > 1 && val != (WPS_ENCR_TKIP | WPS_ENCR_AES))) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Invalid Encryption Type "
+ "attribute value 0x%04x", val);
+ return -1;
+ }
+ return 0;
+}
+
+
+static int wps_validate_conn_type_flags(const u8 *flags, int mandatory)
+{
+ if (flags == NULL) {
+ if (mandatory) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Connection Type "
+ "Flags attribute missing");
+ return -1;
+ }
+ return 0;
+ }
+ if ((*flags & ~(WPS_CONN_ESS | WPS_CONN_IBSS)) ||
+ !(*flags & WPS_CONN_ESS)) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Invalid Connection Type "
+ "Flags attribute value 0x%02x", *flags);
+ return -1;
+ }
+ return 0;
+}
+
+
+static int wps_validate_os_version(const u8 *os_version, int mandatory)
+{
+ if (os_version == NULL) {
+ if (mandatory) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: OS Version "
+ "attribute missing");
+ return -1;
+ }
+ return 0;
+ }
+ return 0;
+}
+
+
+static int wps_validate_authenticator(const u8 *authenticator, int mandatory)
+{
+ if (authenticator == NULL) {
+ if (mandatory) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Authenticator "
+ "attribute missing");
+ return -1;
+ }
+ return 0;
+ }
+ return 0;
+}
+
+
+static int wps_validate_e_hash1(const u8 *hash, int mandatory)
+{
+ if (hash == NULL) {
+ if (mandatory) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: E-Hash1 "
+ "attribute missing");
+ return -1;
+ }
+ return 0;
+ }
+ return 0;
+}
+
+
+static int wps_validate_e_hash2(const u8 *hash, int mandatory)
+{
+ if (hash == NULL) {
+ if (mandatory) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: E-Hash2 "
+ "attribute missing");
+ return -1;
+ }
+ return 0;
+ }
+ return 0;
+}
+
+
+static int wps_validate_r_hash1(const u8 *hash, int mandatory)
+{
+ if (hash == NULL) {
+ if (mandatory) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: R-Hash1 "
+ "attribute missing");
+ return -1;
+ }
+ return 0;
+ }
+ return 0;
+}
+
+
+static int wps_validate_r_hash2(const u8 *hash, int mandatory)
+{
+ if (hash == NULL) {
+ if (mandatory) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: R-Hash2 "
+ "attribute missing");
+ return -1;
+ }
+ return 0;
+ }
+ return 0;
+}
+
+
+static int wps_validate_encr_settings(const u8 *encr_settings, size_t len,
+ int mandatory)
+{
+ if (encr_settings == NULL) {
+ if (mandatory) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Encrypted Settings "
+ "attribute missing");
+ return -1;
+ }
+ return 0;
+ }
+ if (len < 16) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Invalid Encrypted Settings "
+ "attribute length %d", (int) len);
+ return -1;
+ }
+ return 0;
+}
+
+
+static int wps_validate_settings_delay_time(const u8 *delay, int mandatory)
+{
+ if (delay == NULL) {
+ if (mandatory) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Settings Delay Time "
+ "attribute missing");
+ return -1;
+ }
+ return 0;
+ }
+ return 0;
+}
+
+
+static int wps_validate_r_snonce1(const u8 *nonce, int mandatory)
+{
+ if (nonce == NULL) {
+ if (mandatory) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: R-SNonce1 "
+ "attribute missing");
+ return -1;
+ }
+ return 0;
+ }
+ return 0;
+}
+
+
+static int wps_validate_r_snonce2(const u8 *nonce, int mandatory)
+{
+ if (nonce == NULL) {
+ if (mandatory) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: R-SNonce2 "
+ "attribute missing");
+ return -1;
+ }
+ return 0;
+ }
+ return 0;
+}
+
+
+static int wps_validate_e_snonce1(const u8 *nonce, int mandatory)
+{
+ if (nonce == NULL) {
+ if (mandatory) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: E-SNonce1 "
+ "attribute missing");
+ return -1;
+ }
+ return 0;
+ }
+ return 0;
+}
+
+
+static int wps_validate_e_snonce2(const u8 *nonce, int mandatory)
+{
+ if (nonce == NULL) {
+ if (mandatory) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: E-SNonce2 "
+ "attribute missing");
+ return -1;
+ }
+ return 0;
+ }
+ return 0;
+}
+
+
+static int wps_validate_key_wrap_auth(const u8 *auth, int mandatory)
+{
+ if (auth == NULL) {
+ if (mandatory) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Key Wrap "
+ "Authenticator attribute missing");
+ return -1;
+ }
+ return 0;
+ }
+ return 0;
+}
+
+
+static int wps_validate_ssid(const u8 *ssid, size_t ssid_len, int mandatory)
+{
+ if (ssid == NULL) {
+ if (mandatory) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: SSID "
+ "attribute missing");
+ return -1;
+ }
+ return 0;
+ }
+ if (ssid_len == 0 || ssid[ssid_len - 1] == 0) {
+ wpa_hexdump_ascii(MSG_INFO, "WPS-STRICT: Invalid SSID "
+ "attribute value", ssid, ssid_len);
+ return -1;
+ }
+ return 0;
+}
+
+
+static int wps_validate_network_key_index(const u8 *idx, int mandatory)
+{
+ if (idx == NULL) {
+ if (mandatory) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Network Key Index "
+ "attribute missing");
+ return -1;
+ }
+ return 0;
+ }
+ return 0;
+}
+
+
+static int wps_validate_network_idx(const u8 *idx, int mandatory)
+{
+ if (idx == NULL) {
+ if (mandatory) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Network Index "
+ "attribute missing");
+ return -1;
+ }
+ return 0;
+ }
+ return 0;
+}
+
+
+static int wps_validate_network_key(const u8 *key, size_t key_len,
+ const u8 *encr_type, int mandatory)
+{
+ if (key == NULL) {
+ if (mandatory) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Network Key "
+ "attribute missing");
+ return -1;
+ }
+ return 0;
+ }
+ if (((encr_type == NULL || WPA_GET_BE16(encr_type) != WPS_ENCR_WEP) &&
+ key_len > 8 && key_len < 64 && key[key_len - 1] == 0) ||
+ key_len > 64) {
+ wpa_hexdump_ascii_key(MSG_INFO, "WPS-STRICT: Invalid Network "
+ "Key attribute value", key, key_len);
+ return -1;
+ }
+ return 0;
+}
+
+
+static int wps_validate_network_key_shareable(const u8 *val, int mandatory)
+{
+ if (val == NULL) {
+ if (mandatory) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Network Key "
+ "Shareable attribute missing");
+ return -1;
+ }
+ return 0;
+ }
+ if (*val > 1) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Invalid Network Key "
+ "Shareable attribute value 0x%x", *val);
+ return -1;
+ }
+ return 0;
+}
+
+
+static int wps_validate_cred(const u8 *cred, size_t len)
+{
+ struct wps_parse_attr *attr;
+ struct wpabuf buf;
+ int ret;
+
+ attr = (struct wps_parse_attr *)os_zalloc(sizeof(struct wps_parse_attr));
+ if (attr == NULL) {
+ ret = -99;
+ goto _out;
+ }
+
+ if (cred == NULL) {
+ ret = -1;
+ goto _out;
+ }
+ wpabuf_set(&buf, cred, len);
+ if (wps_parse_msg(&buf, attr) < 0) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Failed to parse Credential");
+ ret = -1;
+ goto _out;
+ }
+
+ if (wps_validate_network_idx(attr->network_idx, 1) ||
+ wps_validate_ssid(attr->ssid, attr->ssid_len, 1) ||
+ wps_validate_auth_type(attr->auth_type, 1) ||
+ wps_validate_encr_type(attr->encr_type, 1) ||
+ wps_validate_network_key_index(attr->network_key_idx, 0) ||
+ wps_validate_network_key(attr->network_key, attr->network_key_len,
+ attr->encr_type, 1) ||
+ wps_validate_mac_addr(attr->mac_addr, 1) ||
+ wps_validate_network_key_shareable(attr->network_key_shareable, 0))
+ {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Invalid Credential");
+ ret = -1;
+ goto _out;
+ }
+
+ ret = 0;
+_out:
+ if (attr)
+ os_free(attr);
+
+ return ret;
+}
+
+
+static int wps_validate_credential(const u8 *cred[], size_t len[], size_t num,
+ int mandatory)
+{
+ size_t i;
+
+ if (num == 0) {
+ if (mandatory) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Credential "
+ "attribute missing");
+ return -1;
+ }
+ return 0;
+ }
+
+ for (i = 0; i < num; i++) {
+ if (wps_validate_cred(cred[i], len[i]) < 0)
+ return -1;
+ }
+
+ return 0;
+}
+
+
+int wps_validate_beacon(const struct wpabuf *wps_ie)
+{
+ struct wps_parse_attr *attr;
+ int wps2, sel_reg;
+ int ret;
+
+ attr = (struct wps_parse_attr *)os_zalloc(sizeof(struct wps_parse_attr));
+ if (attr == NULL) {
+ ret = -99;
+ goto _out;
+ }
+
+ if (wps_ie == NULL) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: No WPS IE in Beacon frame");
+ ret = -1;
+ goto _out;
+ }
+ if (wps_parse_msg(wps_ie, attr) < 0) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Failed to parse WPS IE in "
+ "Beacon frame");
+ ret = -1;
+ goto _out;
+ }
+
+ wps2 = attr->version2 != NULL;
+ sel_reg = attr->selected_registrar != NULL &&
+ *attr->selected_registrar != 0;
+ if (wps_validate_version(attr->version, 1) ||
+ wps_validate_wps_state(attr->wps_state, 1) ||
+ wps_validate_ap_setup_locked(attr->ap_setup_locked, 0) ||
+ wps_validate_selected_registrar(attr->selected_registrar, 0) ||
+ wps_validate_dev_password_id(attr->dev_password_id, sel_reg) ||
+ wps_validate_sel_reg_config_methods(attr->sel_reg_config_methods,
+ wps2, sel_reg) ||
+ wps_validate_uuid_e(attr->uuid_e, 0) ||
+ wps_validate_rf_bands(attr->rf_bands, 0) ||
+ wps_validate_version2(attr->version2, wps2) ||
+ wps_validate_authorized_macs(attr->authorized_macs,
+ attr->authorized_macs_len, 0)) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Invalid Beacon frame");
+ ret = -1;
+ goto _out;
+ }
+
+ ret = 0;
+_out:
+ if (attr)
+ os_free(attr);
+
+ return ret;
+}
+
+
+int wps_validate_beacon_probe_resp(const struct wpabuf *wps_ie, int probe,
+ const u8 *addr)
+{
+ struct wps_parse_attr *attr;
+ int wps2, sel_reg;
+ int ret;
+
+ attr = (struct wps_parse_attr *)os_zalloc(sizeof(struct wps_parse_attr));
+ if (attr == NULL) {
+ ret = -99;
+ goto _out;
+ }
+
+ if (wps_ie == NULL) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: No WPS IE in "
+ "%sProbe Response frame", probe ? "" : "Beacon/");
+ ret = -1;
+ goto _out;
+ }
+ if (wps_parse_msg(wps_ie, attr) < 0) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Failed to parse WPS IE in "
+ "%sProbe Response frame", probe ? "" : "Beacon/");
+ ret = -1;
+ goto _out;
+ }
+
+ wps2 = attr->version2 != NULL;
+ sel_reg = attr->selected_registrar != NULL &&
+ *attr->selected_registrar != 0;
+ if (wps_validate_version(attr->version, 1) ||
+ wps_validate_wps_state(attr->wps_state, 1) ||
+ wps_validate_ap_setup_locked(attr->ap_setup_locked, 0) ||
+ wps_validate_selected_registrar(attr->selected_registrar, 0) ||
+ wps_validate_dev_password_id(attr->dev_password_id, sel_reg) ||
+ wps_validate_sel_reg_config_methods(attr->sel_reg_config_methods,
+ wps2, sel_reg) ||
+ wps_validate_response_type(attr->response_type, probe) ||
+ wps_validate_uuid_e(attr->uuid_e, probe) ||
+ wps_validate_manufacturer(attr->manufacturer, attr->manufacturer_len,
+ probe) ||
+ wps_validate_model_name(attr->model_name, attr->model_name_len,
+ probe) ||
+ wps_validate_model_number(attr->model_number, attr->model_number_len,
+ probe) ||
+ wps_validate_serial_number(attr->serial_number,
+ attr->serial_number_len, probe) ||
+ wps_validate_primary_dev_type(attr->primary_dev_type, probe) ||
+ wps_validate_dev_name(attr->dev_name, attr->dev_name_len, probe) ||
+ wps_validate_ap_config_methods(attr->config_methods, wps2, probe) ||
+ wps_validate_rf_bands(attr->rf_bands, 0) ||
+ wps_validate_version2(attr->version2, wps2) ||
+ wps_validate_authorized_macs(attr->authorized_macs,
+ attr->authorized_macs_len, 0)) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Invalid %sProbe Response "
+ "frame from " MACSTR, probe ? "" : "Beacon/",
+ MAC2STR(addr));
+#ifdef WPS_STRICT_WPS2
+ if (wps2) {
+ ret = -1;
+ goto _out;
+ }
+#else /* WPS_STRICT_WPS2 */
+ ret = -1;
+ goto _out;
+#endif /* WPS_STRICT_WPS2 */
+ }
+
+ ret = 0;
+_out:
+ if (attr)
+ os_free(attr);
+
+ return ret;
+}
+
+
+int wps_validate_probe_req(const struct wpabuf *wps_ie, const u8 *addr)
+{
+ struct wps_parse_attr *attr;
+ int wps2;
+ int ret;
+
+ attr = (struct wps_parse_attr *)os_zalloc(sizeof(struct wps_parse_attr));
+ if (attr == NULL) {
+ ret = -99;
+ goto _out;
+ }
+
+ if (wps_ie == NULL) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: No WPS IE in "
+ "Probe Request frame");
+ ret = -1;
+ goto _out;
+ }
+ if (wps_parse_msg(wps_ie, attr) < 0) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Failed to parse WPS IE in "
+ "Probe Request frame");
+ ret = -1;
+ goto _out;
+ }
+
+ wps2 = attr->version2 != NULL;
+ if (wps_validate_version(attr->version, 1) ||
+ wps_validate_request_type(attr->request_type, 1) ||
+ wps_validate_config_methods(attr->config_methods, wps2, 1) ||
+ wps_validate_uuid_e(attr->uuid_e, attr->uuid_r == NULL) ||
+ wps_validate_uuid_r(attr->uuid_r, attr->uuid_e == NULL) ||
+ wps_validate_primary_dev_type(attr->primary_dev_type, 1) ||
+ wps_validate_rf_bands(attr->rf_bands, 1) ||
+ wps_validate_assoc_state(attr->assoc_state, 1) ||
+ wps_validate_config_error(attr->config_error, 1) ||
+ wps_validate_dev_password_id(attr->dev_password_id, 1) ||
+ wps_validate_version2(attr->version2, wps2) ||
+ wps_validate_manufacturer(attr->manufacturer, attr->manufacturer_len,
+ wps2) ||
+ wps_validate_model_name(attr->model_name, attr->model_name_len,
+ wps2) ||
+ wps_validate_model_number(attr->model_number, attr->model_number_len,
+ wps2) ||
+ wps_validate_dev_name(attr->dev_name, attr->dev_name_len, wps2) ||
+ wps_validate_request_to_enroll(attr->request_to_enroll, 0) ||
+ wps_validate_req_dev_type(attr->req_dev_type, attr->num_req_dev_type,
+ 0)) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Invalid Probe Request "
+ "frame from " MACSTR, MAC2STR(addr));
+ ret = -1;
+ goto _out;
+ }
+
+ ret = 0;
+_out:
+ if (attr)
+ os_free(attr);
+
+ return ret;
+}
+
+
+int wps_validate_assoc_req(const struct wpabuf *wps_ie)
+{
+ struct wps_parse_attr *attr;
+ int wps2;
+ int ret;
+
+ attr = (struct wps_parse_attr *)os_zalloc(sizeof(struct wps_parse_attr));
+ if (attr == NULL) {
+ ret = -99;
+ goto _out;
+ }
+
+ if (wps_ie == NULL) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: No WPS IE in "
+ "(Re)Association Request frame");
+ ret = -1;
+ goto _out;
+ }
+ if (wps_parse_msg(wps_ie, attr) < 0) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Failed to parse WPS IE in "
+ "(Re)Association Request frame");
+ ret = -1;
+ goto _out;
+ }
+
+ wps2 = attr->version2 != NULL;
+ if (wps_validate_version(attr->version, 1) ||
+ wps_validate_request_type(attr->request_type, 1) ||
+ wps_validate_version2(attr->version2, wps2)) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Invalid (Re)Association "
+ "Request frame");
+ ret = -1;
+ goto _out;
+ }
+
+ ret = 0;
+_out:
+ if (attr)
+ os_free(attr);
+
+ return ret;
+}
+
+
+int wps_validate_assoc_resp(const struct wpabuf *wps_ie)
+{
+ struct wps_parse_attr *attr;
+ int wps2;
+ int ret;
+
+ attr = (struct wps_parse_attr *)os_zalloc(sizeof(struct wps_parse_attr));
+ if (attr == NULL) {
+ ret = -99;
+ goto _out;
+ }
+
+
+ if (wps_ie == NULL) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: No WPS IE in "
+ "(Re)Association Response frame");
+ ret = -1;
+ goto _out;
+ }
+ if (wps_parse_msg(wps_ie, attr) < 0) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Failed to parse WPS IE in "
+ "(Re)Association Response frame");
+ ret = -1;
+ goto _out;
+ }
+
+ wps2 = attr->version2 != NULL;
+ if (wps_validate_version(attr->version, 1) ||
+ wps_validate_response_type(attr->response_type, 1) ||
+ wps_validate_version2(attr->version2, wps2)) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Invalid (Re)Association "
+ "Response frame");
+ ret = -1;
+ goto _out;
+ }
+
+ ret = 0;
+_out:
+ if(attr)
+ os_free(attr);
+
+ return ret;
+}
+
+
+int wps_validate_m1(const struct wpabuf *tlvs)
+{
+ struct wps_parse_attr *attr;
+ int wps2;
+ int ret;
+
+ attr = (struct wps_parse_attr *)os_zalloc(sizeof(struct wps_parse_attr));
+ if (attr == NULL) {
+ ret = -99;
+ goto _out;
+ }
+
+
+ if (tlvs == NULL) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: No TLVs in M1");
+ ret = -1;
+ goto _out;
+ }
+ if (wps_parse_msg(tlvs, attr) < 0) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Failed to parse attributes "
+ "in M1");
+ ret = -1;
+ goto _out;
+ }
+
+ wps2 = attr->version2 != NULL;
+ if (wps_validate_version(attr->version, 1) ||
+ wps_validate_msg_type(attr->msg_type, 1) ||
+ wps_validate_uuid_e(attr->uuid_e, 1) ||
+ wps_validate_mac_addr(attr->mac_addr, 1) ||
+ wps_validate_enrollee_nonce(attr->enrollee_nonce, 1) ||
+ wps_validate_public_key(attr->public_key, attr->public_key_len, 1) ||
+ wps_validate_auth_type_flags(attr->auth_type_flags, 1) ||
+ wps_validate_encr_type_flags(attr->encr_type_flags, 1) ||
+ wps_validate_conn_type_flags(attr->conn_type_flags, 1) ||
+ wps_validate_config_methods(attr->config_methods, wps2, 1) ||
+ wps_validate_wps_state(attr->wps_state, 1) ||
+ wps_validate_manufacturer(attr->manufacturer, attr->manufacturer_len,
+ 1) ||
+ wps_validate_model_name(attr->model_name, attr->model_name_len, 1) ||
+ wps_validate_model_number(attr->model_number, attr->model_number_len,
+ 1) ||
+ wps_validate_serial_number(attr->serial_number,
+ attr->serial_number_len, 1) ||
+ wps_validate_primary_dev_type(attr->primary_dev_type, 1) ||
+ wps_validate_dev_name(attr->dev_name, attr->dev_name_len, 1) ||
+ wps_validate_rf_bands(attr->rf_bands, 1) ||
+ wps_validate_assoc_state(attr->assoc_state, 1) ||
+ wps_validate_dev_password_id(attr->dev_password_id, 1) ||
+ wps_validate_config_error(attr->config_error, 1) ||
+ wps_validate_os_version(attr->os_version, 1) ||
+ wps_validate_version2(attr->version2, wps2) ||
+ wps_validate_request_to_enroll(attr->request_to_enroll, 0)) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Invalid M1");
+#ifdef WPS_STRICT_WPS2
+ if (wps2) {
+ ret = -1;
+ goto _out;
+ }
+#else /* WPS_STRICT_WPS2 */
+ ret = -1;
+ goto _out;
+#endif /* WPS_STRICT_WPS2 */
+ }
+
+ ret = 0;
+_out:
+ if (attr)
+ os_free(attr);
+
+ return ret;
+}
+
+
+int wps_validate_m2(const struct wpabuf *tlvs)
+{
+ struct wps_parse_attr *attr;
+ int wps2;
+ int ret;
+
+ attr = (struct wps_parse_attr *)os_zalloc(sizeof(struct wps_parse_attr));
+ if (attr == NULL) {
+ ret = -99;
+ goto _out;
+ }
+
+
+ if (tlvs == NULL) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: No TLVs in M2");
+ ret = -1;
+ goto _out;
+ }
+ if (wps_parse_msg(tlvs, attr) < 0) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Failed to parse attributes "
+ "in M2");
+ ret = -1;
+ goto _out;
+ }
+
+ wps2 = attr->version2 != NULL;
+ if (wps_validate_version(attr->version, 1) ||
+ wps_validate_msg_type(attr->msg_type, 1) ||
+ wps_validate_enrollee_nonce(attr->enrollee_nonce, 1) ||
+ wps_validate_registrar_nonce(attr->registrar_nonce, 1) ||
+ wps_validate_uuid_r(attr->uuid_r, 1) ||
+ wps_validate_public_key(attr->public_key, attr->public_key_len, 1) ||
+ wps_validate_auth_type_flags(attr->auth_type_flags, 1) ||
+ wps_validate_encr_type_flags(attr->encr_type_flags, 1) ||
+ wps_validate_conn_type_flags(attr->conn_type_flags, 1) ||
+ wps_validate_config_methods(attr->config_methods, wps2, 1) ||
+ wps_validate_manufacturer(attr->manufacturer, attr->manufacturer_len,
+ 1) ||
+ wps_validate_model_name(attr->model_name, attr->model_name_len, 1) ||
+ wps_validate_model_number(attr->model_number, attr->model_number_len,
+ 1) ||
+ wps_validate_serial_number(attr->serial_number,
+ attr->serial_number_len, 1) ||
+ wps_validate_primary_dev_type(attr->primary_dev_type, 1) ||
+ wps_validate_dev_name(attr->dev_name, attr->dev_name_len, 1) ||
+ wps_validate_rf_bands(attr->rf_bands, 1) ||
+ wps_validate_assoc_state(attr->assoc_state, 1) ||
+ wps_validate_config_error(attr->config_error, 1) ||
+ wps_validate_dev_password_id(attr->dev_password_id, 1) ||
+ wps_validate_os_version(attr->os_version, 1) ||
+ wps_validate_version2(attr->version2, wps2) ||
+ wps_validate_authenticator(attr->authenticator, 1)) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Invalid M2");
+#ifdef WPS_STRICT_WPS2
+ if (wps2) {
+ ret = -1;
+ goto _out;
+ }
+#else /* WPS_STRICT_WPS2 */
+ ret = -1;
+ goto _out;
+#endif /* WPS_STRICT_WPS2 */
+ }
+
+ ret = 0;
+_out:
+ if (attr)
+ os_free(attr);
+
+ return ret;
+}
+
+
+int wps_validate_m2d(const struct wpabuf *tlvs)
+{
+ struct wps_parse_attr *attr;
+ int wps2;
+ int ret;
+
+ attr = (struct wps_parse_attr *)os_zalloc(sizeof(struct wps_parse_attr));
+ if (attr == NULL) {
+ ret = -99;
+ goto _out;
+ }
+
+ if (tlvs == NULL) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: No TLVs in M2D");
+ ret = -1;
+ goto _out;
+ }
+ if (wps_parse_msg(tlvs, attr) < 0) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Failed to parse attributes "
+ "in M2D");
+ ret = -1;
+ goto _out;
+ }
+
+ wps2 = attr->version2 != NULL;
+ if (wps_validate_version(attr->version, 1) ||
+ wps_validate_msg_type(attr->msg_type, 1) ||
+ wps_validate_enrollee_nonce(attr->enrollee_nonce, 1) ||
+ wps_validate_registrar_nonce(attr->registrar_nonce, 1) ||
+ wps_validate_uuid_r(attr->uuid_r, 1) ||
+ wps_validate_auth_type_flags(attr->auth_type_flags, 1) ||
+ wps_validate_encr_type_flags(attr->encr_type_flags, 1) ||
+ wps_validate_conn_type_flags(attr->conn_type_flags, 1) ||
+ wps_validate_config_methods(attr->config_methods, wps2, 1) ||
+ wps_validate_manufacturer(attr->manufacturer, attr->manufacturer_len,
+ 1) ||
+ wps_validate_model_name(attr->model_name, attr->model_name_len, 1) ||
+ wps_validate_model_number(attr->model_number, attr->model_number_len,
+ 1) ||
+ wps_validate_serial_number(attr->serial_number,
+ attr->serial_number_len, 1) ||
+ wps_validate_primary_dev_type(attr->primary_dev_type, 1) ||
+ wps_validate_dev_name(attr->dev_name, attr->dev_name_len, 1) ||
+ wps_validate_rf_bands(attr->rf_bands, 1) ||
+ wps_validate_assoc_state(attr->assoc_state, 1) ||
+ wps_validate_config_error(attr->config_error, 1) ||
+ wps_validate_os_version(attr->os_version, 1) ||
+ wps_validate_version2(attr->version2, wps2)) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Invalid M2D");
+#ifdef WPS_STRICT_WPS2
+ if (wps2) {
+ ret = -1;
+ goto _out;
+ }
+#else /* WPS_STRICT_WPS2 */
+ ret = -1;
+ goto _out;
+#endif /* WPS_STRICT_WPS2 */
+ }
+
+ ret = 0;
+_out:
+ if (attr)
+ os_free(attr);
+
+ return ret;
+}
+
+
+int wps_validate_m3(const struct wpabuf *tlvs)
+{
+ struct wps_parse_attr *attr;
+ int wps2;
+ int ret;
+
+ attr = (struct wps_parse_attr *)os_zalloc(sizeof(struct wps_parse_attr));
+ if (attr == NULL) {
+ ret = -99;
+ goto _out;
+ }
+
+ if (tlvs == NULL) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: No TLVs in M3");
+ ret = -1;
+ goto _out;
+ }
+ if (wps_parse_msg(tlvs, attr) < 0) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Failed to parse attributes "
+ "in M3");
+ ret = -1;
+ goto _out;
+ }
+
+ wps2 = attr->version2 != NULL;
+ if (wps_validate_version(attr->version, 1) ||
+ wps_validate_msg_type(attr->msg_type, 1) ||
+ wps_validate_registrar_nonce(attr->registrar_nonce, 1) ||
+ wps_validate_e_hash1(attr->e_hash1, 1) ||
+ wps_validate_e_hash2(attr->e_hash2, 1) ||
+ wps_validate_version2(attr->version2, wps2) ||
+ wps_validate_authenticator(attr->authenticator, 1)) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Invalid M3");
+#ifdef WPS_STRICT_WPS2
+ if (wps2) {
+ ret = -1;
+ goto _out;
+ }
+#else /* WPS_STRICT_WPS2 */
+ ret = -1;
+ goto _out;
+#endif /* WPS_STRICT_WPS2 */
+ }
+
+ ret = 0;
+_out:
+ if (attr)
+ os_free(attr);
+
+ return ret;
+}
+
+
+int wps_validate_m4(const struct wpabuf *tlvs)
+{
+ struct wps_parse_attr *attr;
+ int wps2;
+ int ret;
+
+ attr = (struct wps_parse_attr *)os_zalloc(sizeof(struct wps_parse_attr));
+ if (attr == NULL) {
+ ret = -99;
+ goto _out;
+ }
+
+ if (tlvs == NULL) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: No TLVs in M4");
+ ret = -1;
+ goto _out;
+ }
+ if (wps_parse_msg(tlvs, attr) < 0) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Failed to parse attributes "
+ "in M4");
+ ret = -1;
+ goto _out;
+ }
+
+ wps2 = attr->version2 != NULL;
+ if (wps_validate_version(attr->version, 1) ||
+ wps_validate_msg_type(attr->msg_type, 1) ||
+ wps_validate_enrollee_nonce(attr->enrollee_nonce, 1) ||
+ wps_validate_r_hash1(attr->r_hash1, 1) ||
+ wps_validate_r_hash2(attr->r_hash2, 1) ||
+ wps_validate_encr_settings(attr->encr_settings,
+ attr->encr_settings_len, 1) ||
+ wps_validate_version2(attr->version2, wps2) ||
+ wps_validate_authenticator(attr->authenticator, 1)) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Invalid M4");
+#ifdef WPS_STRICT_WPS2
+ if (wps2) {
+ ret = -1;
+ goto _out;
+ }
+#else /* WPS_STRICT_WPS2 */
+ ret = -1;
+ goto _out;
+#endif /* WPS_STRICT_WPS2 */
+ }
+
+ ret = 0;
+_out:
+ if (attr)
+ os_free(attr);
+
+ return ret;
+}
+
+
+int wps_validate_m4_encr(const struct wpabuf *tlvs, int wps2)
+{
+ struct wps_parse_attr *attr;
+ int ret;
+
+ attr = (struct wps_parse_attr *)os_zalloc(sizeof(struct wps_parse_attr));
+ if (attr == NULL) {
+ ret = -99;
+ goto _out;
+ }
+
+
+ if (tlvs == NULL) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: No TLVs in M4 encrypted "
+ "settings");
+ ret = -1;
+ goto _out;
+ }
+ if (wps_parse_msg(tlvs, attr) < 0) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Failed to parse attributes "
+ "in M4 encrypted settings");
+ ret = -1;
+ goto _out;
+ }
+
+ if (wps_validate_r_snonce1(attr->r_snonce1, 1) ||
+ wps_validate_key_wrap_auth(attr->key_wrap_auth, 1)) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Invalid M4 encrypted "
+ "settings");
+#ifdef WPS_STRICT_WPS2
+ if (wps2) {
+ ret = -1;
+ goto _out;
+ }
+#else /* WPS_STRICT_WPS2 */
+ ret = -1;
+ goto _out;
+#endif /* WPS_STRICT_WPS2 */
+ }
+
+ ret = 0;
+_out:
+ if (attr)
+ os_free(attr);
+
+ return ret;
+}
+
+
+int wps_validate_m5(const struct wpabuf *tlvs)
+{
+ struct wps_parse_attr *attr;
+ int wps2;
+ int ret;
+
+ attr = (struct wps_parse_attr *)os_zalloc(sizeof(struct wps_parse_attr));
+ if (attr == NULL) {
+ ret = -99;
+ goto _out;
+ }
+
+ if (tlvs == NULL) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: No TLVs in M5");
+ ret = -1;
+ goto _out;
+ }
+ if (wps_parse_msg(tlvs, attr) < 0) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Failed to parse attributes "
+ "in M5");
+ ret = -1;
+ goto _out;
+ }
+
+ wps2 = attr->version2 != NULL;
+ if (wps_validate_version(attr->version, 1) ||
+ wps_validate_msg_type(attr->msg_type, 1) ||
+ wps_validate_registrar_nonce(attr->registrar_nonce, 1) ||
+ wps_validate_encr_settings(attr->encr_settings,
+ attr->encr_settings_len, 1) ||
+ wps_validate_version2(attr->version2, wps2) ||
+ wps_validate_authenticator(attr->authenticator, 1)) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Invalid M5");
+#ifdef WPS_STRICT_WPS2
+ if (wps2) {
+ ret = -1;
+ goto _out;
+ }
+#else /* WPS_STRICT_WPS2 */
+ ret = -1;
+ goto _out;
+#endif /* WPS_STRICT_WPS2 */
+ }
+
+ ret = 0;
+_out:
+ if (attr)
+ os_free(attr);
+
+ return ret;
+}
+
+
+int wps_validate_m5_encr(const struct wpabuf *tlvs, int wps2)
+{
+ struct wps_parse_attr *attr;
+ int ret;
+
+ attr = (struct wps_parse_attr *)os_zalloc(sizeof(struct wps_parse_attr));
+ if (attr == NULL) {
+ ret = -99;
+ goto _out;
+ }
+
+ if (tlvs == NULL) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: No TLVs in M5 encrypted "
+ "settings");
+ ret = -1;
+ goto _out;
+ }
+ if (wps_parse_msg(tlvs, attr) < 0) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Failed to parse attributes "
+ "in M5 encrypted settings");
+ ret = -1;
+ goto _out;
+ }
+
+ if (wps_validate_e_snonce1(attr->e_snonce1, 1) ||
+ wps_validate_key_wrap_auth(attr->key_wrap_auth, 1)) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Invalid M5 encrypted "
+ "settings");
+#ifdef WPS_STRICT_WPS2
+ if (wps2) {
+ ret = -1;
+ goto _out;
+ }
+#else /* WPS_STRICT_WPS2 */
+ ret = -1;
+ goto _out;
+#endif /* WPS_STRICT_WPS2 */
+ }
+
+ ret = 0;
+_out:
+ if (attr)
+ os_free(attr);
+
+ return ret;
+}
+
+
+int wps_validate_m6(const struct wpabuf *tlvs)
+{
+ struct wps_parse_attr *attr;
+ int wps2;
+ int ret;
+
+ attr = (struct wps_parse_attr *)os_zalloc(sizeof(struct wps_parse_attr));
+ if (attr == NULL) {
+ ret = -99;
+ goto _out;
+ }
+
+ if (tlvs == NULL) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: No TLVs in M6");
+ ret = -1;
+ goto _out;
+ }
+ if (wps_parse_msg(tlvs, attr) < 0) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Failed to parse attributes "
+ "in M6");
+ ret = -1;
+ goto _out;
+ }
+
+ wps2 = attr->version2 != NULL;
+ if (wps_validate_version(attr->version, 1) ||
+ wps_validate_msg_type(attr->msg_type, 1) ||
+ wps_validate_enrollee_nonce(attr->enrollee_nonce, 1) ||
+ wps_validate_encr_settings(attr->encr_settings,
+ attr->encr_settings_len, 1) ||
+ wps_validate_version2(attr->version2, wps2) ||
+ wps_validate_authenticator(attr->authenticator, 1)) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Invalid M6");
+#ifdef WPS_STRICT_WPS2
+ if (wps2) {
+ ret = -1;
+ goto _out;
+ }
+#else /* WPS_STRICT_WPS2 */
+ ret = -1;
+ goto _out;
+#endif /* WPS_STRICT_WPS2 */
+ }
+
+ ret = 0;
+_out:
+ if (attr)
+ os_free(attr);
+
+ return ret;
+}
+
+
+int wps_validate_m6_encr(const struct wpabuf *tlvs, int wps2)
+{
+ struct wps_parse_attr *attr;
+ int ret;
+
+ attr = (struct wps_parse_attr *)os_zalloc(sizeof(struct wps_parse_attr));
+ if (attr == NULL) {
+ ret = -99;
+ goto _out;
+ }
+
+ if (tlvs == NULL) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: No TLVs in M6 encrypted "
+ "settings");
+ ret = -1;
+ goto _out;
+ }
+ if (wps_parse_msg(tlvs, attr) < 0) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Failed to parse attributes "
+ "in M6 encrypted settings");
+ ret = -1;
+ goto _out;
+ }
+
+ if (wps_validate_r_snonce2(attr->r_snonce2, 1) ||
+ wps_validate_key_wrap_auth(attr->key_wrap_auth, 1)) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Invalid M6 encrypted "
+ "settings");
+#ifdef WPS_STRICT_WPS2
+ if (wps2) {
+ ret = -1;
+ goto _out;
+ }
+#else /* WPS_STRICT_WPS2 */
+ ret = -1;
+ goto _out;
+#endif /* WPS_STRICT_WPS2 */
+ }
+
+ ret = 0;
+_out:
+ if (attr)
+ os_free(attr);
+
+ return ret;
+}
+
+
+int wps_validate_m7(const struct wpabuf *tlvs)
+{
+ struct wps_parse_attr *attr;
+ int wps2;
+ int ret;
+
+ attr = (struct wps_parse_attr *)os_zalloc(sizeof(struct wps_parse_attr));
+ if (attr == NULL) {
+ ret = -99;
+ goto _out;
+ }
+
+ if (tlvs == NULL) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: No TLVs in M7");
+ ret = -1;
+ goto _out;
+ }
+ if (wps_parse_msg(tlvs, attr) < 0) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Failed to parse attributes "
+ "in M7");
+ ret = -1;
+ goto _out;
+ }
+
+ wps2 = attr->version2 != NULL;
+ if (wps_validate_version(attr->version, 1) ||
+ wps_validate_msg_type(attr->msg_type, 1) ||
+ wps_validate_registrar_nonce(attr->registrar_nonce, 1) ||
+ wps_validate_encr_settings(attr->encr_settings,
+ attr->encr_settings_len, 1) ||
+ wps_validate_settings_delay_time(attr->settings_delay_time, 0) ||
+ wps_validate_version2(attr->version2, wps2) ||
+ wps_validate_authenticator(attr->authenticator, 1)) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Invalid M7");
+#ifdef WPS_STRICT_WPS2
+ if (wps2) {
+ ret = -1;
+ goto _out;
+ }
+#else /* WPS_STRICT_WPS2 */
+ ret = -1;
+ goto _out;
+#endif /* WPS_STRICT_WPS2 */
+ }
+
+ ret = 0;
+_out:
+ if (attr)
+ os_free(attr);
+
+ return ret;
+}
+
+
+int wps_validate_m7_encr(const struct wpabuf *tlvs, int ap, int wps2)
+{
+ struct wps_parse_attr *attr;
+ int ret;
+
+ attr = (struct wps_parse_attr *)os_zalloc(sizeof(struct wps_parse_attr));
+ if (attr == NULL) {
+ ret = -99;
+ goto _out;
+ }
+
+ if (tlvs == NULL) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: No TLVs in M7 encrypted "
+ "settings");
+ ret = -1;
+ goto _out;
+ }
+ if (wps_parse_msg(tlvs, attr) < 0) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Failed to parse attributes "
+ "in M7 encrypted settings");
+ ret = -1;
+ goto _out;
+ }
+
+ if (wps_validate_e_snonce2(attr->e_snonce2, 1) ||
+ wps_validate_ssid(attr->ssid, attr->ssid_len, !ap) ||
+ wps_validate_mac_addr(attr->mac_addr, !ap) ||
+ wps_validate_auth_type(attr->auth_type, !ap) ||
+ wps_validate_encr_type(attr->encr_type, !ap) ||
+ wps_validate_network_key_index(attr->network_key_idx, 0) ||
+ wps_validate_network_key(attr->network_key, attr->network_key_len,
+ attr->encr_type, !ap) ||
+ wps_validate_key_wrap_auth(attr->key_wrap_auth, 1)) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Invalid M7 encrypted "
+ "settings");
+#ifdef WPS_STRICT_WPS2
+ if (wps2) {
+ ret = -1;
+ goto _out;
+ }
+#else /* WPS_STRICT_WPS2 */
+ ret = -1;
+ goto _out;
+#endif /* WPS_STRICT_WPS2 */
+ }
+
+ ret = 0;
+_out:
+ if (attr)
+ os_free(attr);
+
+ return ret;
+}
+
+
+int wps_validate_m8(const struct wpabuf *tlvs)
+{
+ struct wps_parse_attr *attr;
+ int wps2;
+ int ret;
+
+ attr = (struct wps_parse_attr *)os_zalloc(sizeof(struct wps_parse_attr));
+ if (attr == NULL) {
+ ret = -99;
+ goto _out;
+ }
+
+ if (tlvs == NULL) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: No TLVs in M8");
+ ret = -1;
+ goto _out;
+ }
+ if (wps_parse_msg(tlvs, attr) < 0) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Failed to parse attributes "
+ "in M8");
+ ret = -1;
+ goto _out;
+ }
+
+ wps2 = attr->version2 != NULL;
+ if (wps_validate_version(attr->version, 1) ||
+ wps_validate_msg_type(attr->msg_type, 1) ||
+ wps_validate_enrollee_nonce(attr->enrollee_nonce, 1) ||
+ wps_validate_encr_settings(attr->encr_settings,
+ attr->encr_settings_len, 1) ||
+ wps_validate_version2(attr->version2, wps2) ||
+ wps_validate_authenticator(attr->authenticator, 1)) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Invalid M8");
+#ifdef WPS_STRICT_WPS2
+ if (wps2) {
+ ret = -1;
+ goto _out;
+ }
+#else /* WPS_STRICT_WPS2 */
+ ret = -1;
+ goto _out;
+#endif /* WPS_STRICT_WPS2 */
+ }
+
+ ret = 0;
+_out:
+ if (attr)
+ os_free(attr);
+
+ return ret;
+}
+
+
+int wps_validate_m8_encr(const struct wpabuf *tlvs, int ap, int wps2)
+{
+ struct wps_parse_attr *attr;
+ int ret;
+
+ attr = (struct wps_parse_attr *)os_zalloc(sizeof(struct wps_parse_attr));
+ if (attr == NULL) {
+ ret = -99;
+ goto _out;
+ }
+
+ if (tlvs == NULL) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: No TLVs in M8 encrypted "
+ "settings");
+ ret = -1;
+ goto _out;
+ }
+ if (wps_parse_msg(tlvs, attr) < 0) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Failed to parse attributes "
+ "in M8 encrypted settings");
+ ret = -1;
+ goto _out;
+ }
+
+ if (wps_validate_ssid(attr->ssid, attr->ssid_len, ap) ||
+ wps_validate_auth_type(attr->auth_type, ap) ||
+ wps_validate_encr_type(attr->encr_type, ap) ||
+ wps_validate_network_key_index(attr->network_key_idx, 0) ||
+ wps_validate_mac_addr(attr->mac_addr, ap) ||
+ wps_validate_credential(attr->cred, attr->cred_len, attr->num_cred,
+ !ap) ||
+ wps_validate_key_wrap_auth(attr->key_wrap_auth, 1)) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Invalid M8 encrypted "
+ "settings");
+#ifdef WPS_STRICT_WPS2
+ if (wps2) {
+ ret = -1;
+ goto _out;
+ }
+#else /* WPS_STRICT_WPS2 */
+ ret = -1;
+ goto _out;
+#endif /* WPS_STRICT_WPS2 */
+ }
+
+ ret = 0;
+_out:
+ if (attr)
+ os_free(attr);
+
+ return ret;
+}
+
+
+int wps_validate_wsc_ack(const struct wpabuf *tlvs)
+{
+ struct wps_parse_attr *attr;
+ int wps2;
+ int ret;
+
+ attr = (struct wps_parse_attr *)os_zalloc(sizeof(struct wps_parse_attr));
+ if (attr == NULL) {
+ ret = -99;
+ goto _out;
+ }
+
+ if (tlvs == NULL) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: No TLVs in WSC_ACK");
+ ret = -1;
+ goto _out;
+ }
+ if (wps_parse_msg(tlvs, attr) < 0) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Failed to parse attributes "
+ "in WSC_ACK");
+ ret = -1;
+ goto _out;
+ }
+
+ wps2 = attr->version2 != NULL;
+ if (wps_validate_version(attr->version, 1) ||
+ wps_validate_msg_type(attr->msg_type, 1) ||
+ wps_validate_enrollee_nonce(attr->enrollee_nonce, 1) ||
+ wps_validate_registrar_nonce(attr->registrar_nonce, 1) ||
+ wps_validate_version2(attr->version2, wps2)) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Invalid WSC_ACK");
+#ifdef WPS_STRICT_WPS2
+ if (wps2) {
+ ret = -1;
+ goto _out;
+ }
+#else /* WPS_STRICT_WPS2 */
+ ret = -1;
+ goto _out;
+#endif /* WPS_STRICT_WPS2 */
+ }
+
+ ret = 0;
+_out:
+ if (attr)
+ os_free(attr);
+
+ return ret;
+}
+
+
+int wps_validate_wsc_nack(const struct wpabuf *tlvs)
+{
+ struct wps_parse_attr *attr;
+ int wps2;
+ int ret;
+
+ attr = (struct wps_parse_attr *)os_zalloc(sizeof(struct wps_parse_attr));
+ if (attr == NULL) {
+ ret = -99;
+ goto _out;
+ }
+
+ if (tlvs == NULL) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: No TLVs in WSC_NACK");
+ ret = -1;
+ }
+ if (wps_parse_msg(tlvs, attr) < 0) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Failed to parse attributes "
+ "in WSC_NACK");
+ ret = -1;
+ goto _out;
+ }
+
+ wps2 = attr->version2 != NULL;
+ if (wps_validate_version(attr->version, 1) ||
+ wps_validate_msg_type(attr->msg_type, 1) ||
+ wps_validate_enrollee_nonce(attr->enrollee_nonce, 1) ||
+ wps_validate_registrar_nonce(attr->registrar_nonce, 1) ||
+ wps_validate_config_error(attr->config_error, 1) ||
+ wps_validate_version2(attr->version2, wps2)) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Invalid WSC_NACK");
+#ifdef WPS_STRICT_WPS2
+ if (wps2) {
+ ret = -1;
+ goto _out;
+ }
+#else /* WPS_STRICT_WPS2 */
+ goto _out;
+ ret = -1;
+#endif /* WPS_STRICT_WPS2 */
+ }
+
+ ret = 0;
+_out:
+ if (attr)
+ os_free(attr);
+
+ return ret;
+}
+
+
+int wps_validate_wsc_done(const struct wpabuf *tlvs)
+{
+ struct wps_parse_attr *attr;
+ int wps2;
+ int ret;
+
+ attr = (struct wps_parse_attr *)os_zalloc(sizeof(struct wps_parse_attr));
+ if (attr == NULL) {
+ ret = -99;
+ goto _out;
+ }
+
+ if (tlvs == NULL) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: No TLVs in WSC_Done");
+ ret = -1;
+ goto _out;
+ }
+ if (wps_parse_msg(tlvs, attr) < 0) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Failed to parse attributes "
+ "in WSC_Done");
+ ret = -1;
+ goto _out;
+ }
+
+ wps2 = attr->version2 != NULL;
+ if (wps_validate_version(attr->version, 1) ||
+ wps_validate_msg_type(attr->msg_type, 1) ||
+ wps_validate_enrollee_nonce(attr->enrollee_nonce, 1) ||
+ wps_validate_registrar_nonce(attr->registrar_nonce, 1) ||
+ wps_validate_version2(attr->version2, wps2)) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Invalid WSC_Done");
+#ifdef WPS_STRICT_WPS2
+ if (wps2) {
+ ret = -1;
+ goto _out;
+ }
+#else /* WPS_STRICT_WPS2 */
+ ret = -1;
+ goto _out;
+#endif /* WPS_STRICT_WPS2 */
+ }
+
+ ret = 0;
+_out:
+ if (attr)
+ os_free(attr);
+
+ return ret;
+}
+
+
+int wps_validate_upnp_set_selected_registrar(const struct wpabuf *tlvs)
+{
+ struct wps_parse_attr *attr;
+ int wps2;
+ int sel_reg;
+ int ret;
+
+ attr = (struct wps_parse_attr *)os_zalloc(sizeof(struct wps_parse_attr));
+ if (attr == NULL) {
+ ret = -99;
+ goto _out;
+ }
+
+ if (tlvs == NULL) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: No TLVs in "
+ "SetSelectedRegistrar");
+ ret = -1;
+ goto _out;
+ }
+ if (wps_parse_msg(tlvs, attr) < 0) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Failed to parse attributes "
+ "in SetSelectedRegistrar");
+ ret = -1;
+ goto _out;
+ }
+
+ wps2 = attr->version2 != NULL;
+ sel_reg = attr->selected_registrar != NULL &&
+ *attr->selected_registrar != 0;
+ if (wps_validate_version(attr->version, 1) ||
+ wps_validate_dev_password_id(attr->dev_password_id, sel_reg) ||
+ wps_validate_sel_reg_config_methods(attr->sel_reg_config_methods,
+ wps2, sel_reg) ||
+ wps_validate_version2(attr->version2, wps2) ||
+ wps_validate_authorized_macs(attr->authorized_macs,
+ attr->authorized_macs_len, wps2) ||
+ wps_validate_uuid_r(attr->uuid_r, wps2)) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Invalid "
+ "SetSelectedRegistrar");
+#ifdef WPS_STRICT_WPS2
+ if (wps2) {
+ ret = -1;
+ goto _out;
+ }
+#else /* WPS_STRICT_WPS2 */
+ ret -1;
+ goto _out;
+#endif /* WPS_STRICT_WPS2 */
+ }
+
+ ret = 0;
+_out:
+ if (attr)
+ os_free(attr);
+
+ return ret;
+}