summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSichen Zhao <1473996754@qq.com>2017-08-01 20:43:41 +0800
committerSebastian Huber <sebastian.huber@embedded-brains.de>2017-11-10 13:33:02 +0100
commit9c9d11b481ce0dcdfa9c69e6781cc15435cfb043 (patch)
tree3d0e6678afcdbb17d35606398f220fba66dcfa7b
parent847de24eb0a351a37c6f004cc506a5177599283d (diff)
Import wpa from FreeBSD
-rw-r--r--freebsd/contrib/wpa/src/ap/ap_config.h692
-rw-r--r--freebsd/contrib/wpa/src/ap/ap_drv_ops.c844
-rw-r--r--freebsd/contrib/wpa/src/ap/ap_drv_ops.h340
-rw-r--r--freebsd/contrib/wpa/src/ap/hostapd.h494
-rw-r--r--freebsd/contrib/wpa/src/ap/hs20.c179
-rw-r--r--freebsd/contrib/wpa/src/ap/hs20.h22
-rw-r--r--freebsd/contrib/wpa/src/ap/ieee802_11.h107
-rw-r--r--freebsd/contrib/wpa/src/ap/ieee802_11_auth.h29
-rw-r--r--freebsd/contrib/wpa/src/ap/ieee802_11_shared.c510
-rw-r--r--freebsd/contrib/wpa/src/ap/p2p_hostapd.h35
-rw-r--r--freebsd/contrib/wpa/src/ap/sta_info.h241
-rw-r--r--freebsd/contrib/wpa/src/common/defs.h337
-rw-r--r--freebsd/contrib/wpa/src/common/eapol_common.h92
-rw-r--r--freebsd/contrib/wpa/src/common/gas.c275
-rw-r--r--freebsd/contrib/wpa/src/common/gas.h37
-rw-r--r--freebsd/contrib/wpa/src/common/hw_features_common.c457
-rw-r--r--freebsd/contrib/wpa/src/common/hw_features_common.h39
-rw-r--r--freebsd/contrib/wpa/src/common/ieee802_11_common.c1149
-rw-r--r--freebsd/contrib/wpa/src/common/ieee802_11_common.h128
-rw-r--r--freebsd/contrib/wpa/src/common/ieee802_11_defs.h1436
-rw-r--r--freebsd/contrib/wpa/src/common/qca-vendor.h357
-rw-r--r--freebsd/contrib/wpa/src/common/sae.h70
-rw-r--r--freebsd/contrib/wpa/src/common/version.h10
-rw-r--r--freebsd/contrib/wpa/src/common/wpa_common.c1666
-rw-r--r--freebsd/contrib/wpa/src/common/wpa_common.h451
-rw-r--r--freebsd/contrib/wpa/src/common/wpa_ctrl.h472
-rw-r--r--freebsd/contrib/wpa/src/crypto/aes-ctr.c57
-rw-r--r--freebsd/contrib/wpa/src/crypto/aes-eax.c147
-rw-r--r--freebsd/contrib/wpa/src/crypto/aes-encblock.c34
-rw-r--r--freebsd/contrib/wpa/src/crypto/aes-omac1.c172
-rw-r--r--freebsd/contrib/wpa/src/crypto/aes-unwrap.c82
-rw-r--r--freebsd/contrib/wpa/src/crypto/aes.h21
-rw-r--r--freebsd/contrib/wpa/src/crypto/aes_wrap.h71
-rw-r--r--freebsd/contrib/wpa/src/crypto/crypto.h809
-rw-r--r--freebsd/contrib/wpa/src/crypto/crypto_openssl.c1444
-rw-r--r--freebsd/contrib/wpa/src/crypto/dh_group5.h18
-rw-r--r--freebsd/contrib/wpa/src/crypto/md5.h19
-rw-r--r--freebsd/contrib/wpa/src/crypto/ms_funcs.c526
-rw-r--r--freebsd/contrib/wpa/src/crypto/ms_funcs.h60
-rw-r--r--freebsd/contrib/wpa/src/crypto/random.c441
-rw-r--r--freebsd/contrib/wpa/src/crypto/random.h28
-rw-r--r--freebsd/contrib/wpa/src/crypto/rc4.c56
-rw-r--r--freebsd/contrib/wpa/src/crypto/sha1-pbkdf2.c94
-rw-r--r--freebsd/contrib/wpa/src/crypto/sha1-prf.c69
-rw-r--r--freebsd/contrib/wpa/src/crypto/sha1.c109
-rw-r--r--freebsd/contrib/wpa/src/crypto/sha1.h27
-rw-r--r--freebsd/contrib/wpa/src/crypto/sha256-internal.c228
-rw-r--r--freebsd/contrib/wpa/src/crypto/sha256-prf.c102
-rw-r--r--freebsd/contrib/wpa/src/crypto/sha256.h30
-rw-r--r--freebsd/contrib/wpa/src/crypto/sha256_i.h25
-rw-r--r--freebsd/contrib/wpa/src/crypto/sha384.h24
-rw-r--r--freebsd/contrib/wpa/src/crypto/tls.h588
-rw-r--r--freebsd/contrib/wpa/src/crypto/tls_internal.c735
-rw-r--r--freebsd/contrib/wpa/src/drivers/driver.h4704
-rw-r--r--freebsd/contrib/wpa/src/drivers/driver_bsd.c1675
-rw-r--r--freebsd/contrib/wpa/src/drivers/driver_common.c222
-rw-r--r--freebsd/contrib/wpa/src/drivers/driver_ndis.c3221
-rw-r--r--freebsd/contrib/wpa/src/drivers/driver_ndis.h59
-rw-r--r--freebsd/contrib/wpa/src/drivers/driver_nl80211.h277
-rw-r--r--freebsd/contrib/wpa/src/drivers/driver_wired.c681
-rw-r--r--freebsd/contrib/wpa/src/drivers/drivers.c88
-rw-r--r--freebsd/contrib/wpa/src/drivers/linux_defines.h46
-rw-r--r--freebsd/contrib/wpa/src/eap_common/chap.c30
-rw-r--r--freebsd/contrib/wpa/src/eap_common/chap.h17
-rw-r--r--freebsd/contrib/wpa/src/eap_common/eap_common.c290
-rw-r--r--freebsd/contrib/wpa/src/eap_common/eap_common.h33
-rw-r--r--freebsd/contrib/wpa/src/eap_common/eap_defs.h118
-rw-r--r--freebsd/contrib/wpa/src/eap_common/eap_peap_common.c87
-rw-r--r--freebsd/contrib/wpa/src/eap_common/eap_peap_common.h16
-rw-r--r--freebsd/contrib/wpa/src/eap_common/eap_psk_common.c70
-rw-r--r--freebsd/contrib/wpa/src/eap_common/eap_psk_common.h72
-rw-r--r--freebsd/contrib/wpa/src/eap_common/eap_tlv_common.h112
-rw-r--r--freebsd/contrib/wpa/src/eap_common/eap_ttls.h65
-rw-r--r--freebsd/contrib/wpa/src/eap_common/eap_wsc_common.h27
-rw-r--r--freebsd/contrib/wpa/src/eap_peer/eap.c2952
-rw-r--r--freebsd/contrib/wpa/src/eap_peer/eap.h354
-rw-r--r--freebsd/contrib/wpa/src/eap_peer/eap_config.h774
-rw-r--r--freebsd/contrib/wpa/src/eap_peer/eap_gtc.c147
-rw-r--r--freebsd/contrib/wpa/src/eap_peer/eap_i.h389
-rw-r--r--freebsd/contrib/wpa/src/eap_peer/eap_leap.c415
-rw-r--r--freebsd/contrib/wpa/src/eap_peer/eap_md5.c122
-rw-r--r--freebsd/contrib/wpa/src/eap_peer/eap_methods.c371
-rw-r--r--freebsd/contrib/wpa/src/eap_peer/eap_methods.h111
-rw-r--r--freebsd/contrib/wpa/src/eap_peer/eap_mschapv2.c903
-rw-r--r--freebsd/contrib/wpa/src/eap_peer/eap_otp.c103
-rw-r--r--freebsd/contrib/wpa/src/eap_peer/eap_peap.c1264
-rw-r--r--freebsd/contrib/wpa/src/eap_peer/eap_proxy.h49
-rw-r--r--freebsd/contrib/wpa/src/eap_peer/eap_psk.c504
-rw-r--r--freebsd/contrib/wpa/src/eap_peer/eap_tls.c441
-rw-r--r--freebsd/contrib/wpa/src/eap_peer/eap_tls_common.c1110
-rw-r--r--freebsd/contrib/wpa/src/eap_peer/eap_tls_common.h132
-rw-r--r--freebsd/contrib/wpa/src/eap_peer/eap_ttls.c1723
-rw-r--r--freebsd/contrib/wpa/src/eap_peer/mschapv2.c126
-rw-r--r--freebsd/contrib/wpa/src/eap_peer/mschapv2.h28
-rw-r--r--freebsd/contrib/wpa/src/eap_peer/tncc.h36
-rw-r--r--freebsd/contrib/wpa/src/eap_server/eap_methods.h51
-rw-r--r--freebsd/contrib/wpa/src/eapol_supp/eapol_supp_sm.c2152
-rw-r--r--freebsd/contrib/wpa/src/eapol_supp/eapol_supp_sm.h443
-rw-r--r--freebsd/contrib/wpa/src/fst/fst.h296
-rw-r--r--freebsd/contrib/wpa/src/fst/fst_ctrl_aux.h91
-rw-r--r--freebsd/contrib/wpa/src/fst/fst_ctrl_iface.h45
-rw-r--r--freebsd/contrib/wpa/src/l2_packet/l2_packet.h154
-rw-r--r--freebsd/contrib/wpa/src/l2_packet/l2_packet_freebsd.c331
-rw-r--r--freebsd/contrib/wpa/src/p2p/p2p.h2343
-rw-r--r--freebsd/contrib/wpa/src/rsn_supp/peerkey.c1157
-rw-r--r--freebsd/contrib/wpa/src/rsn_supp/peerkey.h82
-rw-r--r--freebsd/contrib/wpa/src/rsn_supp/pmksa_cache.c539
-rw-r--r--freebsd/contrib/wpa/src/rsn_supp/pmksa_cache.h134
-rw-r--r--freebsd/contrib/wpa/src/rsn_supp/preauth.c543
-rw-r--r--freebsd/contrib/wpa/src/rsn_supp/preauth.h79
-rw-r--r--freebsd/contrib/wpa/src/rsn_supp/wpa.c2979
-rw-r--r--freebsd/contrib/wpa/src/rsn_supp/wpa.h421
-rw-r--r--freebsd/contrib/wpa/src/rsn_supp/wpa_ft.c849
-rw-r--r--freebsd/contrib/wpa/src/rsn_supp/wpa_i.h371
-rw-r--r--freebsd/contrib/wpa/src/rsn_supp/wpa_ie.c607
-rw-r--r--freebsd/contrib/wpa/src/rsn_supp/wpa_ie.h72
-rw-r--r--freebsd/contrib/wpa/src/tls/tlsv1_client.h54
-rw-r--r--freebsd/contrib/wpa/src/tls/tlsv1_cred.h40
-rw-r--r--freebsd/contrib/wpa/src/tls/tlsv1_server.h53
-rw-r--r--freebsd/contrib/wpa/src/utils/base64.c159
-rw-r--r--freebsd/contrib/wpa/src/utils/base64.h17
-rw-r--r--freebsd/contrib/wpa/src/utils/build_config.h50
-rw-r--r--freebsd/contrib/wpa/src/utils/common.c1127
-rw-r--r--freebsd/contrib/wpa/src/utils/common.h559
-rw-r--r--freebsd/contrib/wpa/src/utils/eloop.c1144
-rw-r--r--freebsd/contrib/wpa/src/utils/eloop.h359
-rw-r--r--freebsd/contrib/wpa/src/utils/ext_password.h33
-rw-r--r--freebsd/contrib/wpa/src/utils/includes.h45
-rw-r--r--freebsd/contrib/wpa/src/utils/ip_addr.h27
-rw-r--r--freebsd/contrib/wpa/src/utils/list.h97
-rw-r--r--freebsd/contrib/wpa/src/utils/os.h664
-rw-r--r--freebsd/contrib/wpa/src/utils/os_unix.c851
-rw-r--r--freebsd/contrib/wpa/src/utils/pcsc_funcs.h42
-rw-r--r--freebsd/contrib/wpa/src/utils/platform.h21
-rw-r--r--freebsd/contrib/wpa/src/utils/state_machine.h138
-rw-r--r--freebsd/contrib/wpa/src/utils/trace.h69
-rw-r--r--freebsd/contrib/wpa/src/utils/uuid.c73
-rw-r--r--freebsd/contrib/wpa/src/utils/uuid.h18
-rw-r--r--freebsd/contrib/wpa/src/utils/wpa_debug.c862
-rw-r--r--freebsd/contrib/wpa/src/utils/wpa_debug.h370
-rw-r--r--freebsd/contrib/wpa/src/utils/wpabuf.c314
-rw-r--r--freebsd/contrib/wpa/src/utils/wpabuf.h163
-rw-r--r--freebsd/contrib/wpa/src/wps/http.h29
-rw-r--r--freebsd/contrib/wpa/src/wps/http_client.c364
-rw-r--r--freebsd/contrib/wpa/src/wps/http_client.h40
-rw-r--r--freebsd/contrib/wpa/src/wps/http_server.c316
-rw-r--r--freebsd/contrib/wpa/src/wps/http_server.h33
-rw-r--r--freebsd/contrib/wpa/src/wps/httpread.c850
-rw-r--r--freebsd/contrib/wpa/src/wps/httpread.h117
-rw-r--r--freebsd/contrib/wpa/src/wps/upnp_xml.c254
-rw-r--r--freebsd/contrib/wpa/src/wps/upnp_xml.h25
-rw-r--r--freebsd/contrib/wpa/src/wps/wps.c664
-rw-r--r--freebsd/contrib/wpa/src/wps/wps.h1041
-rw-r--r--freebsd/contrib/wpa/src/wps/wps_attr_build.c487
-rw-r--r--freebsd/contrib/wpa/src/wps/wps_attr_parse.c665
-rw-r--r--freebsd/contrib/wpa/src/wps/wps_attr_parse.h104
-rw-r--r--freebsd/contrib/wpa/src/wps/wps_attr_process.c274
-rw-r--r--freebsd/contrib/wpa/src/wps/wps_common.c911
-rw-r--r--freebsd/contrib/wpa/src/wps/wps_defs.h379
-rw-r--r--freebsd/contrib/wpa/src/wps/wps_dev_attr.c421
-rw-r--r--freebsd/contrib/wpa/src/wps/wps_dev_attr.h39
-rw-r--r--freebsd/contrib/wpa/src/wps/wps_enrollee.c1510
-rw-r--r--freebsd/contrib/wpa/src/wps/wps_er.h112
-rw-r--r--freebsd/contrib/wpa/src/wps/wps_i.h218
-rw-r--r--freebsd/contrib/wpa/src/wps/wps_registrar.c3672
-rw-r--r--freebsd/contrib/wpa/src/wps/wps_upnp.c1214
-rw-r--r--freebsd/contrib/wpa/src/wps/wps_upnp.h48
-rw-r--r--freebsd/contrib/wpa/src/wps/wps_upnp_ap.c85
-rw-r--r--freebsd/contrib/wpa/src/wps/wps_upnp_event.c423
-rw-r--r--freebsd/contrib/wpa/src/wps/wps_upnp_i.h193
-rw-r--r--freebsd/contrib/wpa/src/wps/wps_upnp_ssdp.c950
-rw-r--r--freebsd/contrib/wpa/src/wps/wps_upnp_web.c1352
-rw-r--r--freebsd/contrib/wpa/wpa_supplicant/ap.h98
-rw-r--r--freebsd/contrib/wpa/wpa_supplicant/autoscan.h49
-rw-r--r--freebsd/contrib/wpa/wpa_supplicant/bgscan.h73
-rw-r--r--freebsd/contrib/wpa/wpa_supplicant/blacklist.c143
-rw-r--r--freebsd/contrib/wpa/wpa_supplicant/blacklist.h24
-rw-r--r--freebsd/contrib/wpa/wpa_supplicant/bss.c1238
-rw-r--r--freebsd/contrib/wpa/wpa_supplicant/bss.h150
-rw-r--r--freebsd/contrib/wpa/wpa_supplicant/config.c4359
-rw-r--r--freebsd/contrib/wpa/wpa_supplicant/config.h1342
-rw-r--r--freebsd/contrib/wpa/wpa_supplicant/config_file.c1389
-rw-r--r--freebsd/contrib/wpa/wpa_supplicant/config_ssid.h724
-rw-r--r--freebsd/contrib/wpa/wpa_supplicant/ctrl_iface.c9480
-rw-r--r--freebsd/contrib/wpa/wpa_supplicant/ctrl_iface.h159
-rw-r--r--freebsd/contrib/wpa/wpa_supplicant/ctrl_iface_unix.c1220
-rw-r--r--freebsd/contrib/wpa/wpa_supplicant/dbus/dbus_common.h20
-rw-r--r--freebsd/contrib/wpa/wpa_supplicant/dbus/dbus_new.h557
-rw-r--r--freebsd/contrib/wpa/wpa_supplicant/dbus/dbus_old.h142
-rw-r--r--freebsd/contrib/wpa/wpa_supplicant/driver_i.h915
-rw-r--r--freebsd/contrib/wpa/wpa_supplicant/eap_register.c263
-rw-r--r--freebsd/contrib/wpa/wpa_supplicant/events.c3829
-rw-r--r--freebsd/contrib/wpa/wpa_supplicant/gas_query.c721
-rw-r--r--freebsd/contrib/wpa/wpa_supplicant/gas_query.h59
-rw-r--r--freebsd/contrib/wpa/wpa_supplicant/hs20_supplicant.c1011
-rw-r--r--freebsd/contrib/wpa/wpa_supplicant/hs20_supplicant.h41
-rw-r--r--freebsd/contrib/wpa/wpa_supplicant/ibss_rsn.h64
-rw-r--r--freebsd/contrib/wpa/wpa_supplicant/interworking.c3059
-rw-r--r--freebsd/contrib/wpa/wpa_supplicant/interworking.h36
-rw-r--r--freebsd/contrib/wpa/wpa_supplicant/main.c361
-rw-r--r--freebsd/contrib/wpa/wpa_supplicant/mesh.h44
-rw-r--r--freebsd/contrib/wpa/wpa_supplicant/mesh_mpm.h43
-rw-r--r--freebsd/contrib/wpa/wpa_supplicant/mesh_rsn.h36
-rw-r--r--freebsd/contrib/wpa/wpa_supplicant/notify.c833
-rw-r--r--freebsd/contrib/wpa/wpa_supplicant/notify.h144
-rw-r--r--freebsd/contrib/wpa/wpa_supplicant/offchannel.c449
-rw-r--r--freebsd/contrib/wpa/wpa_supplicant/offchannel.h35
-rw-r--r--freebsd/contrib/wpa/wpa_supplicant/p2p_supplicant.h335
-rw-r--r--freebsd/contrib/wpa/wpa_supplicant/scan.c2466
-rw-r--r--freebsd/contrib/wpa/wpa_supplicant/scan.h58
-rw-r--r--freebsd/contrib/wpa/wpa_supplicant/sme.h118
-rw-r--r--freebsd/contrib/wpa/wpa_supplicant/wifi_display.h24
-rw-r--r--freebsd/contrib/wpa/wpa_supplicant/wmm_ac.c997
-rw-r--r--freebsd/contrib/wpa/wpa_supplicant/wmm_ac.h176
-rw-r--r--freebsd/contrib/wpa/wpa_supplicant/wnm_sta.h77
-rw-r--r--freebsd/contrib/wpa/wpa_supplicant/wpa_supplicant.c5872
-rw-r--r--freebsd/contrib/wpa/wpa_supplicant/wpa_supplicant_i.h1170
-rw-r--r--freebsd/contrib/wpa/wpa_supplicant/wpas_glue.c1131
-rw-r--r--freebsd/contrib/wpa/wpa_supplicant/wpas_glue.h28
-rw-r--r--freebsd/contrib/wpa/wpa_supplicant/wpas_kay.h41
-rw-r--r--freebsd/contrib/wpa/wpa_supplicant/wps_supplicant.c2904
-rw-r--r--freebsd/contrib/wpa/wpa_supplicant/wps_supplicant.h152
-rw-r--r--freebsd/usr.sbin/wpa/wpa_supplicant/Packet32.c415
-rw-r--r--freebsd/usr.sbin/wpa/wpa_supplicant/Packet32.h67
-rw-r--r--freebsd/usr.sbin/wpa/wpa_supplicant/ntddndis.h31
225 files changed, 127499 insertions, 0 deletions
diff --git a/freebsd/contrib/wpa/src/ap/ap_config.h b/freebsd/contrib/wpa/src/ap/ap_config.h
new file mode 100644
index 00000000..de470a96
--- /dev/null
+++ b/freebsd/contrib/wpa/src/ap/ap_config.h
@@ -0,0 +1,692 @@
+/*
+ * hostapd / Configuration definitions and helpers functions
+ * Copyright (c) 2003-2015, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef HOSTAPD_CONFIG_H
+#define HOSTAPD_CONFIG_H
+
+#include "common/defs.h"
+#include "ip_addr.h"
+#include "common/wpa_common.h"
+#include "common/ieee802_11_defs.h"
+#include "common/ieee802_11_common.h"
+#include "wps/wps.h"
+#include "fst/fst.h"
+
+/**
+ * mesh_conf - local MBSS state and settings
+ */
+struct mesh_conf {
+ u8 meshid[32];
+ u8 meshid_len;
+ /* Active Path Selection Protocol Identifier */
+ u8 mesh_pp_id;
+ /* Active Path Selection Metric Identifier */
+ u8 mesh_pm_id;
+ /* Congestion Control Mode Identifier */
+ u8 mesh_cc_id;
+ /* Synchronization Protocol Identifier */
+ u8 mesh_sp_id;
+ /* Authentication Protocol Identifier */
+ u8 mesh_auth_id;
+ u8 *rsn_ie;
+ int rsn_ie_len;
+#define MESH_CONF_SEC_NONE BIT(0)
+#define MESH_CONF_SEC_AUTH BIT(1)
+#define MESH_CONF_SEC_AMPE BIT(2)
+ unsigned int security;
+ int dot11MeshMaxRetries;
+ int dot11MeshRetryTimeout; /* msec */
+ int dot11MeshConfirmTimeout; /* msec */
+ int dot11MeshHoldingTimeout; /* msec */
+};
+
+#define MAX_STA_COUNT 2007
+#define MAX_VLAN_ID 4094
+
+typedef u8 macaddr[ETH_ALEN];
+
+struct mac_acl_entry {
+ macaddr addr;
+ int vlan_id;
+};
+
+struct hostapd_radius_servers;
+struct ft_remote_r0kh;
+struct ft_remote_r1kh;
+
+#define NUM_WEP_KEYS 4
+struct hostapd_wep_keys {
+ u8 idx;
+ u8 *key[NUM_WEP_KEYS];
+ size_t len[NUM_WEP_KEYS];
+ int keys_set;
+ size_t default_len; /* key length used for dynamic key generation */
+};
+
+typedef enum hostap_security_policy {
+ SECURITY_PLAINTEXT = 0,
+ SECURITY_STATIC_WEP = 1,
+ SECURITY_IEEE_802_1X = 2,
+ SECURITY_WPA_PSK = 3,
+ SECURITY_WPA = 4,
+ SECURITY_OSEN = 5
+} secpolicy;
+
+struct hostapd_ssid {
+ u8 ssid[SSID_MAX_LEN];
+ size_t ssid_len;
+ unsigned int ssid_set:1;
+ unsigned int utf8_ssid:1;
+ unsigned int wpa_passphrase_set:1;
+ unsigned int wpa_psk_set:1;
+
+ char vlan[IFNAMSIZ + 1];
+ secpolicy security_policy;
+
+ struct hostapd_wpa_psk *wpa_psk;
+ char *wpa_passphrase;
+ char *wpa_psk_file;
+
+ struct hostapd_wep_keys wep;
+
+#define DYNAMIC_VLAN_DISABLED 0
+#define DYNAMIC_VLAN_OPTIONAL 1
+#define DYNAMIC_VLAN_REQUIRED 2
+ int dynamic_vlan;
+#define DYNAMIC_VLAN_NAMING_WITHOUT_DEVICE 0
+#define DYNAMIC_VLAN_NAMING_WITH_DEVICE 1
+#define DYNAMIC_VLAN_NAMING_END 2
+ int vlan_naming;
+#ifdef CONFIG_FULL_DYNAMIC_VLAN
+ char *vlan_tagged_interface;
+#endif /* CONFIG_FULL_DYNAMIC_VLAN */
+};
+
+
+#define VLAN_ID_WILDCARD -1
+
+struct hostapd_vlan {
+ struct hostapd_vlan *next;
+ int vlan_id; /* VLAN ID or -1 (VLAN_ID_WILDCARD) for wildcard entry */
+ char ifname[IFNAMSIZ + 1];
+ int configured;
+ int dynamic_vlan;
+#ifdef CONFIG_FULL_DYNAMIC_VLAN
+
+#define DVLAN_CLEAN_WLAN_PORT 0x8
+ int clean;
+#endif /* CONFIG_FULL_DYNAMIC_VLAN */
+};
+
+#define PMK_LEN 32
+struct hostapd_sta_wpa_psk_short {
+ struct hostapd_sta_wpa_psk_short *next;
+ u8 psk[PMK_LEN];
+};
+
+struct hostapd_wpa_psk {
+ struct hostapd_wpa_psk *next;
+ int group;
+ u8 psk[PMK_LEN];
+ u8 addr[ETH_ALEN];
+ u8 p2p_dev_addr[ETH_ALEN];
+};
+
+struct hostapd_eap_user {
+ struct hostapd_eap_user *next;
+ u8 *identity;
+ size_t identity_len;
+ struct {
+ int vendor;
+ u32 method;
+ } methods[EAP_MAX_METHODS];
+ u8 *password;
+ size_t password_len;
+ int phase2;
+ int force_version;
+ unsigned int wildcard_prefix:1;
+ unsigned int password_hash:1; /* whether password is hashed with
+ * nt_password_hash() */
+ unsigned int remediation:1;
+ unsigned int macacl:1;
+ int ttls_auth; /* EAP_TTLS_AUTH_* bitfield */
+ struct hostapd_radius_attr *accept_attr;
+};
+
+struct hostapd_radius_attr {
+ u8 type;
+ struct wpabuf *val;
+ struct hostapd_radius_attr *next;
+};
+
+
+#define NUM_TX_QUEUES 4
+
+struct hostapd_tx_queue_params {
+ int aifs;
+ int cwmin;
+ int cwmax;
+ int burst; /* maximum burst time in 0.1 ms, i.e., 10 = 1 ms */
+};
+
+
+#define MAX_ROAMING_CONSORTIUM_LEN 15
+
+struct hostapd_roaming_consortium {
+ u8 len;
+ u8 oi[MAX_ROAMING_CONSORTIUM_LEN];
+};
+
+struct hostapd_lang_string {
+ u8 lang[3];
+ u8 name_len;
+ u8 name[252];
+};
+
+#define MAX_NAI_REALMS 10
+#define MAX_NAI_REALMLEN 255
+#define MAX_NAI_EAP_METHODS 5
+#define MAX_NAI_AUTH_TYPES 4
+struct hostapd_nai_realm_data {
+ u8 encoding;
+ char realm_buf[MAX_NAI_REALMLEN + 1];
+ char *realm[MAX_NAI_REALMS];
+ u8 eap_method_count;
+ struct hostapd_nai_realm_eap {
+ u8 eap_method;
+ u8 num_auths;
+ u8 auth_id[MAX_NAI_AUTH_TYPES];
+ u8 auth_val[MAX_NAI_AUTH_TYPES];
+ } eap_method[MAX_NAI_EAP_METHODS];
+};
+
+/**
+ * struct hostapd_bss_config - Per-BSS configuration
+ */
+struct hostapd_bss_config {
+ char iface[IFNAMSIZ + 1];
+ char bridge[IFNAMSIZ + 1];
+ char vlan_bridge[IFNAMSIZ + 1];
+ char wds_bridge[IFNAMSIZ + 1];
+
+ enum hostapd_logger_level logger_syslog_level, logger_stdout_level;
+
+ unsigned int logger_syslog; /* module bitfield */
+ unsigned int logger_stdout; /* module bitfield */
+
+ int max_num_sta; /* maximum number of STAs in station table */
+
+ int dtim_period;
+ int bss_load_update_period;
+
+ int ieee802_1x; /* use IEEE 802.1X */
+ int eapol_version;
+ int eap_server; /* Use internal EAP server instead of external
+ * RADIUS server */
+ struct hostapd_eap_user *eap_user;
+ char *eap_user_sqlite;
+ char *eap_sim_db;
+ int eap_server_erp; /* Whether ERP is enabled on internal EAP server */
+ struct hostapd_ip_addr own_ip_addr;
+ char *nas_identifier;
+ struct hostapd_radius_servers *radius;
+ int acct_interim_interval;
+ int radius_request_cui;
+ struct hostapd_radius_attr *radius_auth_req_attr;
+ struct hostapd_radius_attr *radius_acct_req_attr;
+ int radius_das_port;
+ unsigned int radius_das_time_window;
+ int radius_das_require_event_timestamp;
+ struct hostapd_ip_addr radius_das_client_addr;
+ u8 *radius_das_shared_secret;
+ size_t radius_das_shared_secret_len;
+
+ struct hostapd_ssid ssid;
+
+ char *eap_req_id_text; /* optional displayable message sent with
+ * EAP Request-Identity */
+ size_t eap_req_id_text_len;
+ int eapol_key_index_workaround;
+
+ size_t default_wep_key_len;
+ int individual_wep_key_len;
+ int wep_rekeying_period;
+ int broadcast_key_idx_min, broadcast_key_idx_max;
+ int eap_reauth_period;
+ int erp_send_reauth_start;
+ char *erp_domain;
+
+ int ieee802_11f; /* use IEEE 802.11f (IAPP) */
+ char iapp_iface[IFNAMSIZ + 1]; /* interface used with IAPP broadcast
+ * frames */
+
+ enum {
+ ACCEPT_UNLESS_DENIED = 0,
+ DENY_UNLESS_ACCEPTED = 1,
+ USE_EXTERNAL_RADIUS_AUTH = 2
+ } macaddr_acl;
+ struct mac_acl_entry *accept_mac;
+ int num_accept_mac;
+ struct mac_acl_entry *deny_mac;
+ int num_deny_mac;
+ int wds_sta;
+ int isolate;
+ int start_disabled;
+
+ int auth_algs; /* bitfield of allowed IEEE 802.11 authentication
+ * algorithms, WPA_AUTH_ALG_{OPEN,SHARED,LEAP} */
+
+ int wpa; /* bitfield of WPA_PROTO_WPA, WPA_PROTO_RSN */
+ int wpa_key_mgmt;
+#ifdef CONFIG_IEEE80211W
+ enum mfp_options ieee80211w;
+ int group_mgmt_cipher;
+ /* dot11AssociationSAQueryMaximumTimeout (in TUs) */
+ unsigned int assoc_sa_query_max_timeout;
+ /* dot11AssociationSAQueryRetryTimeout (in TUs) */
+ int assoc_sa_query_retry_timeout;
+#endif /* CONFIG_IEEE80211W */
+ enum {
+ PSK_RADIUS_IGNORED = 0,
+ PSK_RADIUS_ACCEPTED = 1,
+ PSK_RADIUS_REQUIRED = 2
+ } wpa_psk_radius;
+ int wpa_pairwise;
+ int wpa_group;
+ int wpa_group_rekey;
+ int wpa_strict_rekey;
+ int wpa_gmk_rekey;
+ int wpa_ptk_rekey;
+ int rsn_pairwise;
+ int rsn_preauth;
+ char *rsn_preauth_interfaces;
+ int peerkey;
+
+#ifdef CONFIG_IEEE80211R
+ /* IEEE 802.11r - Fast BSS Transition */
+ u8 mobility_domain[MOBILITY_DOMAIN_ID_LEN];
+ u8 r1_key_holder[FT_R1KH_ID_LEN];
+ u32 r0_key_lifetime;
+ u32 reassociation_deadline;
+ struct ft_remote_r0kh *r0kh_list;
+ struct ft_remote_r1kh *r1kh_list;
+ int pmk_r1_push;
+ int ft_over_ds;
+#endif /* CONFIG_IEEE80211R */
+
+ char *ctrl_interface; /* directory for UNIX domain sockets */
+#ifndef CONFIG_NATIVE_WINDOWS
+ gid_t ctrl_interface_gid;
+#endif /* CONFIG_NATIVE_WINDOWS */
+ int ctrl_interface_gid_set;
+
+ char *ca_cert;
+ char *server_cert;
+ char *private_key;
+ char *private_key_passwd;
+ int check_crl;
+ unsigned int tls_session_lifetime;
+ char *ocsp_stapling_response;
+ char *dh_file;
+ char *openssl_ciphers;
+ u8 *pac_opaque_encr_key;
+ u8 *eap_fast_a_id;
+ size_t eap_fast_a_id_len;
+ char *eap_fast_a_id_info;
+ int eap_fast_prov;
+ int pac_key_lifetime;
+ int pac_key_refresh_time;
+ int eap_sim_aka_result_ind;
+ int tnc;
+ int fragment_size;
+ u16 pwd_group;
+
+ char *radius_server_clients;
+ int radius_server_auth_port;
+ int radius_server_acct_port;
+ int radius_server_ipv6;
+
+ int use_pae_group_addr; /* Whether to send EAPOL frames to PAE group
+ * address instead of individual address
+ * (for driver_wired.c).
+ */
+
+ int ap_max_inactivity;
+ int ignore_broadcast_ssid;
+
+ int wmm_enabled;
+ int wmm_uapsd;
+
+ struct hostapd_vlan *vlan;
+
+ macaddr bssid;
+
+ /*
+ * Maximum listen interval that STAs can use when associating with this
+ * BSS. If a STA tries to use larger value, the association will be
+ * denied with status code 51.
+ */
+ u16 max_listen_interval;
+
+ int disable_pmksa_caching;
+ int okc; /* Opportunistic Key Caching */
+
+ int wps_state;
+#ifdef CONFIG_WPS
+ int wps_independent;
+ int ap_setup_locked;
+ u8 uuid[16];
+ char *wps_pin_requests;
+ char *device_name;
+ char *manufacturer;
+ char *model_name;
+ char *model_number;
+ char *serial_number;
+ u8 device_type[WPS_DEV_TYPE_LEN];
+ char *config_methods;
+ u8 os_version[4];
+ char *ap_pin;
+ int skip_cred_build;
+ u8 *extra_cred;
+ size_t extra_cred_len;
+ int wps_cred_processing;
+ int force_per_enrollee_psk;
+ u8 *ap_settings;
+ size_t ap_settings_len;
+ char *upnp_iface;
+ char *friendly_name;
+ char *manufacturer_url;
+ char *model_description;
+ char *model_url;
+ char *upc;
+ struct wpabuf *wps_vendor_ext[MAX_WPS_VENDOR_EXTENSIONS];
+ int wps_nfc_pw_from_config;
+ int wps_nfc_dev_pw_id;
+ struct wpabuf *wps_nfc_dh_pubkey;
+ struct wpabuf *wps_nfc_dh_privkey;
+ struct wpabuf *wps_nfc_dev_pw;
+#endif /* CONFIG_WPS */
+ int pbc_in_m1;
+ char *server_id;
+
+#define P2P_ENABLED BIT(0)
+#define P2P_GROUP_OWNER BIT(1)
+#define P2P_GROUP_FORMATION BIT(2)
+#define P2P_MANAGE BIT(3)
+#define P2P_ALLOW_CROSS_CONNECTION BIT(4)
+ int p2p;
+#ifdef CONFIG_P2P
+ u8 ip_addr_go[4];
+ u8 ip_addr_mask[4];
+ u8 ip_addr_start[4];
+ u8 ip_addr_end[4];
+#endif /* CONFIG_P2P */
+
+ int disassoc_low_ack;
+ int skip_inactivity_poll;
+
+#define TDLS_PROHIBIT BIT(0)
+#define TDLS_PROHIBIT_CHAN_SWITCH BIT(1)
+ int tdls;
+ int disable_11n;
+ int disable_11ac;
+
+ /* IEEE 802.11v */
+ int time_advertisement;
+ char *time_zone;
+ int wnm_sleep_mode;
+ int bss_transition;
+
+ /* IEEE 802.11u - Interworking */
+ int interworking;
+ int access_network_type;
+ int internet;
+ int asra;
+ int esr;
+ int uesa;
+ int venue_info_set;
+ u8 venue_group;
+ u8 venue_type;
+ u8 hessid[ETH_ALEN];
+
+ /* IEEE 802.11u - Roaming Consortium list */
+ unsigned int roaming_consortium_count;
+ struct hostapd_roaming_consortium *roaming_consortium;
+
+ /* IEEE 802.11u - Venue Name duples */
+ unsigned int venue_name_count;
+ struct hostapd_lang_string *venue_name;
+
+ /* IEEE 802.11u - Network Authentication Type */
+ u8 *network_auth_type;
+ size_t network_auth_type_len;
+
+ /* IEEE 802.11u - IP Address Type Availability */
+ u8 ipaddr_type_availability;
+ u8 ipaddr_type_configured;
+
+ /* IEEE 802.11u - 3GPP Cellular Network */
+ u8 *anqp_3gpp_cell_net;
+ size_t anqp_3gpp_cell_net_len;
+
+ /* IEEE 802.11u - Domain Name */
+ u8 *domain_name;
+ size_t domain_name_len;
+
+ unsigned int nai_realm_count;
+ struct hostapd_nai_realm_data *nai_realm_data;
+
+ u16 gas_comeback_delay;
+ int gas_frag_limit;
+
+ u8 qos_map_set[16 + 2 * 21];
+ unsigned int qos_map_set_len;
+
+ int osen;
+ int proxy_arp;
+ int na_mcast_to_ucast;
+#ifdef CONFIG_HS20
+ int hs20;
+ int disable_dgaf;
+ u16 anqp_domain_id;
+ unsigned int hs20_oper_friendly_name_count;
+ struct hostapd_lang_string *hs20_oper_friendly_name;
+ u8 *hs20_wan_metrics;
+ u8 *hs20_connection_capability;
+ size_t hs20_connection_capability_len;
+ u8 *hs20_operating_class;
+ u8 hs20_operating_class_len;
+ struct hs20_icon {
+ u16 width;
+ u16 height;
+ char language[3];
+ char type[256];
+ char name[256];
+ char file[256];
+ } *hs20_icons;
+ size_t hs20_icons_count;
+ u8 osu_ssid[SSID_MAX_LEN];
+ size_t osu_ssid_len;
+ struct hs20_osu_provider {
+ unsigned int friendly_name_count;
+ struct hostapd_lang_string *friendly_name;
+ char *server_uri;
+ int *method_list;
+ char **icons;
+ size_t icons_count;
+ char *osu_nai;
+ unsigned int service_desc_count;
+ struct hostapd_lang_string *service_desc;
+ } *hs20_osu_providers, *last_osu;
+ size_t hs20_osu_providers_count;
+ unsigned int hs20_deauth_req_timeout;
+ char *subscr_remediation_url;
+ u8 subscr_remediation_method;
+#endif /* CONFIG_HS20 */
+
+ u8 wps_rf_bands; /* RF bands for WPS (WPS_RF_*) */
+
+#ifdef CONFIG_RADIUS_TEST
+ char *dump_msk_file;
+#endif /* CONFIG_RADIUS_TEST */
+
+ struct wpabuf *vendor_elements;
+
+ unsigned int sae_anti_clogging_threshold;
+ int *sae_groups;
+
+ char *wowlan_triggers; /* Wake-on-WLAN triggers */
+
+#ifdef CONFIG_TESTING_OPTIONS
+ u8 bss_load_test[5];
+ u8 bss_load_test_set;
+ struct wpabuf *own_ie_override;
+#endif /* CONFIG_TESTING_OPTIONS */
+
+#define MESH_ENABLED BIT(0)
+ int mesh;
+
+ int radio_measurements;
+
+ int vendor_vht;
+
+ char *no_probe_resp_if_seen_on;
+ char *no_auth_if_seen_on;
+};
+
+
+/**
+ * struct hostapd_config - Per-radio interface configuration
+ */
+struct hostapd_config {
+ struct hostapd_bss_config **bss, *last_bss;
+ size_t num_bss;
+
+ u16 beacon_int;
+ int rts_threshold;
+ int fragm_threshold;
+ u8 send_probe_response;
+ u8 channel;
+ u8 acs;
+ struct wpa_freq_range_list acs_ch_list;
+ enum hostapd_hw_mode hw_mode; /* HOSTAPD_MODE_IEEE80211A, .. */
+ enum {
+ LONG_PREAMBLE = 0,
+ SHORT_PREAMBLE = 1
+ } preamble;
+
+ int *supported_rates;
+ int *basic_rates;
+
+ const struct wpa_driver_ops *driver;
+ char *driver_params;
+
+ int ap_table_max_size;
+ int ap_table_expiration_time;
+
+ unsigned int track_sta_max_num;
+ unsigned int track_sta_max_age;
+
+ char country[3]; /* first two octets: country code as described in
+ * ISO/IEC 3166-1. Third octet:
+ * ' ' (ascii 32): all environments
+ * 'O': Outdoor environemnt only
+ * 'I': Indoor environment only
+ */
+
+ int ieee80211d;
+
+ int ieee80211h; /* DFS */
+
+ /*
+ * Local power constraint is an octet encoded as an unsigned integer in
+ * units of decibels. Invalid value -1 indicates that Power Constraint
+ * element will not be added.
+ */
+ int local_pwr_constraint;
+
+ /* Control Spectrum Management bit */
+ int spectrum_mgmt_required;
+
+ struct hostapd_tx_queue_params tx_queue[NUM_TX_QUEUES];
+
+ /*
+ * WMM AC parameters, in same order as 802.1D, i.e.
+ * 0 = BE (best effort)
+ * 1 = BK (background)
+ * 2 = VI (video)
+ * 3 = VO (voice)
+ */
+ struct hostapd_wmm_ac_params wmm_ac_params[4];
+
+ int ht_op_mode_fixed;
+ u16 ht_capab;
+ int ieee80211n;
+ int secondary_channel;
+ int no_pri_sec_switch;
+ int require_ht;
+ int obss_interval;
+ u32 vht_capab;
+ int ieee80211ac;
+ int require_vht;
+ u8 vht_oper_chwidth;
+ u8 vht_oper_centr_freq_seg0_idx;
+ u8 vht_oper_centr_freq_seg1_idx;
+
+#ifdef CONFIG_FST
+ struct fst_iface_cfg fst_cfg;
+#endif /* CONFIG_FST */
+
+#ifdef CONFIG_P2P
+ u8 p2p_go_ctwindow;
+#endif /* CONFIG_P2P */
+
+#ifdef CONFIG_TESTING_OPTIONS
+ double ignore_probe_probability;
+ double ignore_auth_probability;
+ double ignore_assoc_probability;
+ double ignore_reassoc_probability;
+ double corrupt_gtk_rekey_mic_probability;
+#endif /* CONFIG_TESTING_OPTIONS */
+
+#ifdef CONFIG_ACS
+ unsigned int acs_num_scans;
+ struct acs_bias {
+ int channel;
+ double bias;
+ } *acs_chan_bias;
+ unsigned int num_acs_chan_bias;
+#endif /* CONFIG_ACS */
+};
+
+
+int hostapd_mac_comp(const void *a, const void *b);
+int hostapd_mac_comp_empty(const void *a);
+struct hostapd_config * hostapd_config_defaults(void);
+void hostapd_config_defaults_bss(struct hostapd_bss_config *bss);
+void hostapd_config_free_eap_user(struct hostapd_eap_user *user);
+void hostapd_config_clear_wpa_psk(struct hostapd_wpa_psk **p);
+void hostapd_config_free_bss(struct hostapd_bss_config *conf);
+void hostapd_config_free(struct hostapd_config *conf);
+int hostapd_maclist_found(struct mac_acl_entry *list, int num_entries,
+ const u8 *addr, int *vlan_id);
+int hostapd_rate_found(int *list, int rate);
+const u8 * hostapd_get_psk(const struct hostapd_bss_config *conf,
+ const u8 *addr, const u8 *p2p_dev_addr,
+ const u8 *prev_psk);
+int hostapd_setup_wpa_psk(struct hostapd_bss_config *conf);
+int hostapd_vlan_id_valid(struct hostapd_vlan *vlan, int vlan_id);
+const char * hostapd_get_vlan_id_ifname(struct hostapd_vlan *vlan,
+ int vlan_id);
+struct hostapd_radius_attr *
+hostapd_config_get_radius_attr(struct hostapd_radius_attr *attr, u8 type);
+int hostapd_config_check(struct hostapd_config *conf, int full_config);
+void hostapd_set_security_params(struct hostapd_bss_config *bss,
+ int full_config);
+
+#endif /* HOSTAPD_CONFIG_H */
diff --git a/freebsd/contrib/wpa/src/ap/ap_drv_ops.c b/freebsd/contrib/wpa/src/ap/ap_drv_ops.c
new file mode 100644
index 00000000..d20ee93f
--- /dev/null
+++ b/freebsd/contrib/wpa/src/ap/ap_drv_ops.c
@@ -0,0 +1,844 @@
+#include <machine/rtems-bsd-user-space.h>
+
+/*
+ * hostapd - Driver operations
+ * Copyright (c) 2009-2010, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+
+#include "utils/common.h"
+#include "common/ieee802_11_defs.h"
+#include "common/hw_features_common.h"
+#include "wps/wps.h"
+#include "p2p/p2p.h"
+#include "hostapd.h"
+#include "ieee802_11.h"
+#include "sta_info.h"
+#include "ap_config.h"
+#include "p2p_hostapd.h"
+#include "hs20.h"
+#include "ap_drv_ops.h"
+
+
+u32 hostapd_sta_flags_to_drv(u32 flags)
+{
+ int res = 0;
+ if (flags & WLAN_STA_AUTHORIZED)
+ res |= WPA_STA_AUTHORIZED;
+ if (flags & WLAN_STA_WMM)
+ res |= WPA_STA_WMM;
+ if (flags & WLAN_STA_SHORT_PREAMBLE)
+ res |= WPA_STA_SHORT_PREAMBLE;
+ if (flags & WLAN_STA_MFP)
+ res |= WPA_STA_MFP;
+ return res;
+}
+
+
+int hostapd_build_ap_extra_ies(struct hostapd_data *hapd,
+ struct wpabuf **beacon_ret,
+ struct wpabuf **proberesp_ret,
+ struct wpabuf **assocresp_ret)
+{
+ struct wpabuf *beacon = NULL, *proberesp = NULL, *assocresp = NULL;
+ u8 buf[200], *pos;
+
+ *beacon_ret = *proberesp_ret = *assocresp_ret = NULL;
+
+ pos = buf;
+ pos = hostapd_eid_time_adv(hapd, pos);
+ if (pos != buf) {
+ if (wpabuf_resize(&beacon, pos - buf) != 0)
+ goto fail;
+ wpabuf_put_data(beacon, buf, pos - buf);
+ }
+ pos = hostapd_eid_time_zone(hapd, pos);
+ if (pos != buf) {
+ if (wpabuf_resize(&proberesp, pos - buf) != 0)
+ goto fail;
+ wpabuf_put_data(proberesp, buf, pos - buf);
+ }
+
+ pos = buf;
+ pos = hostapd_eid_ext_capab(hapd, pos);
+ if (pos != buf) {
+ if (wpabuf_resize(&assocresp, pos - buf) != 0)
+ goto fail;
+ wpabuf_put_data(assocresp, buf, pos - buf);
+ }
+ pos = hostapd_eid_interworking(hapd, pos);
+ pos = hostapd_eid_adv_proto(hapd, pos);
+ pos = hostapd_eid_roaming_consortium(hapd, pos);
+ if (pos != buf) {
+ if (wpabuf_resize(&beacon, pos - buf) != 0)
+ goto fail;
+ wpabuf_put_data(beacon, buf, pos - buf);
+
+ if (wpabuf_resize(&proberesp, pos - buf) != 0)
+ goto fail;
+ wpabuf_put_data(proberesp, buf, pos - buf);
+ }
+
+#ifdef CONFIG_FST
+ if (hapd->iface->fst_ies) {
+ size_t add = wpabuf_len(hapd->iface->fst_ies);
+
+ if (wpabuf_resize(&beacon, add) < 0)
+ goto fail;
+ wpabuf_put_buf(beacon, hapd->iface->fst_ies);
+ if (wpabuf_resize(&proberesp, add) < 0)
+ goto fail;
+ wpabuf_put_buf(proberesp, hapd->iface->fst_ies);
+ if (wpabuf_resize(&assocresp, add) < 0)
+ goto fail;
+ wpabuf_put_buf(assocresp, hapd->iface->fst_ies);
+ }
+#endif /* CONFIG_FST */
+
+ if (hapd->wps_beacon_ie) {
+ if (wpabuf_resize(&beacon, wpabuf_len(hapd->wps_beacon_ie)) <
+ 0)
+ goto fail;
+ wpabuf_put_buf(beacon, hapd->wps_beacon_ie);
+ }
+
+ if (hapd->wps_probe_resp_ie) {
+ if (wpabuf_resize(&proberesp,
+ wpabuf_len(hapd->wps_probe_resp_ie)) < 0)
+ goto fail;
+ wpabuf_put_buf(proberesp, hapd->wps_probe_resp_ie);
+ }
+
+#ifdef CONFIG_P2P
+ if (hapd->p2p_beacon_ie) {
+ if (wpabuf_resize(&beacon, wpabuf_len(hapd->p2p_beacon_ie)) <
+ 0)
+ goto fail;
+ wpabuf_put_buf(beacon, hapd->p2p_beacon_ie);
+ }
+
+ if (hapd->p2p_probe_resp_ie) {
+ if (wpabuf_resize(&proberesp,
+ wpabuf_len(hapd->p2p_probe_resp_ie)) < 0)
+ goto fail;
+ wpabuf_put_buf(proberesp, hapd->p2p_probe_resp_ie);
+ }
+#endif /* CONFIG_P2P */
+
+#ifdef CONFIG_P2P_MANAGER
+ if (hapd->conf->p2p & P2P_MANAGE) {
+ if (wpabuf_resize(&beacon, 100) == 0) {
+ u8 *start, *p;
+ start = wpabuf_put(beacon, 0);
+ p = hostapd_eid_p2p_manage(hapd, start);
+ wpabuf_put(beacon, p - start);
+ }
+
+ if (wpabuf_resize(&proberesp, 100) == 0) {
+ u8 *start, *p;
+ start = wpabuf_put(proberesp, 0);
+ p = hostapd_eid_p2p_manage(hapd, start);
+ wpabuf_put(proberesp, p - start);
+ }
+ }
+#endif /* CONFIG_P2P_MANAGER */
+
+#ifdef CONFIG_WPS
+ if (hapd->conf->wps_state) {
+ struct wpabuf *a = wps_build_assoc_resp_ie();
+ if (a && wpabuf_resize(&assocresp, wpabuf_len(a)) == 0)
+ wpabuf_put_buf(assocresp, a);
+ wpabuf_free(a);
+ }
+#endif /* CONFIG_WPS */
+
+#ifdef CONFIG_P2P_MANAGER
+ if (hapd->conf->p2p & P2P_MANAGE) {
+ if (wpabuf_resize(&assocresp, 100) == 0) {
+ u8 *start, *p;
+ start = wpabuf_put(assocresp, 0);
+ p = hostapd_eid_p2p_manage(hapd, start);
+ wpabuf_put(assocresp, p - start);
+ }
+ }
+#endif /* CONFIG_P2P_MANAGER */
+
+#ifdef CONFIG_WIFI_DISPLAY
+ if (hapd->p2p_group) {
+ struct wpabuf *a;
+ a = p2p_group_assoc_resp_ie(hapd->p2p_group, P2P_SC_SUCCESS);
+ if (a && wpabuf_resize(&assocresp, wpabuf_len(a)) == 0)
+ wpabuf_put_buf(assocresp, a);
+ wpabuf_free(a);
+ }
+#endif /* CONFIG_WIFI_DISPLAY */
+
+#ifdef CONFIG_HS20
+ pos = buf;
+ pos = hostapd_eid_hs20_indication(hapd, pos);
+ if (pos != buf) {
+ if (wpabuf_resize(&beacon, pos - buf) != 0)
+ goto fail;
+ wpabuf_put_data(beacon, buf, pos - buf);
+
+ if (wpabuf_resize(&proberesp, pos - buf) != 0)
+ goto fail;
+ wpabuf_put_data(proberesp, buf, pos - buf);
+ }
+
+ pos = hostapd_eid_osen(hapd, buf);
+ if (pos != buf) {
+ if (wpabuf_resize(&beacon, pos - buf) != 0)
+ goto fail;
+ wpabuf_put_data(beacon, buf, pos - buf);
+
+ if (wpabuf_resize(&proberesp, pos - buf) != 0)
+ goto fail;
+ wpabuf_put_data(proberesp, buf, pos - buf);
+ }
+#endif /* CONFIG_HS20 */
+
+ if (hapd->conf->vendor_elements) {
+ size_t add = wpabuf_len(hapd->conf->vendor_elements);
+ if (wpabuf_resize(&beacon, add) == 0)
+ wpabuf_put_buf(beacon, hapd->conf->vendor_elements);
+ if (wpabuf_resize(&proberesp, add) == 0)
+ wpabuf_put_buf(proberesp, hapd->conf->vendor_elements);
+ }
+
+ *beacon_ret = beacon;
+ *proberesp_ret = proberesp;
+ *assocresp_ret = assocresp;
+
+ return 0;
+
+fail:
+ wpabuf_free(beacon);
+ wpabuf_free(proberesp);
+ wpabuf_free(assocresp);
+ return -1;
+}
+
+
+void hostapd_free_ap_extra_ies(struct hostapd_data *hapd,
+ struct wpabuf *beacon,
+ struct wpabuf *proberesp,
+ struct wpabuf *assocresp)
+{
+ wpabuf_free(beacon);
+ wpabuf_free(proberesp);
+ wpabuf_free(assocresp);
+}
+
+
+int hostapd_reset_ap_wps_ie(struct hostapd_data *hapd)
+{
+ if (hapd->driver == NULL || hapd->driver->set_ap_wps_ie == NULL)
+ return 0;
+
+ return hapd->driver->set_ap_wps_ie(hapd->drv_priv, NULL, NULL, NULL);
+}
+
+
+int hostapd_set_ap_wps_ie(struct hostapd_data *hapd)
+{
+ struct wpabuf *beacon, *proberesp, *assocresp;
+ int ret;
+
+ if (hapd->driver == NULL || hapd->driver->set_ap_wps_ie == NULL)
+ return 0;
+
+ if (hostapd_build_ap_extra_ies(hapd, &beacon, &proberesp, &assocresp) <
+ 0)
+ return -1;
+
+ ret = hapd->driver->set_ap_wps_ie(hapd->drv_priv, beacon, proberesp,
+ assocresp);
+
+ hostapd_free_ap_extra_ies(hapd, beacon, proberesp, assocresp);
+
+ return ret;
+}
+
+
+int hostapd_set_authorized(struct hostapd_data *hapd,
+ struct sta_info *sta, int authorized)
+{
+ if (authorized) {
+ return hostapd_sta_set_flags(hapd, sta->addr,
+ hostapd_sta_flags_to_drv(
+ sta->flags),
+ WPA_STA_AUTHORIZED, ~0);
+ }
+
+ return hostapd_sta_set_flags(hapd, sta->addr,
+ hostapd_sta_flags_to_drv(sta->flags),
+ 0, ~WPA_STA_AUTHORIZED);
+}
+
+
+int hostapd_set_sta_flags(struct hostapd_data *hapd, struct sta_info *sta)
+{
+ int set_flags, total_flags, flags_and, flags_or;
+ total_flags = hostapd_sta_flags_to_drv(sta->flags);
+ set_flags = WPA_STA_SHORT_PREAMBLE | WPA_STA_WMM | WPA_STA_MFP;
+ if (((!hapd->conf->ieee802_1x && !hapd->conf->wpa) ||
+ sta->auth_alg == WLAN_AUTH_FT) &&
+ sta->flags & WLAN_STA_AUTHORIZED)
+ set_flags |= WPA_STA_AUTHORIZED;
+ flags_or = total_flags & set_flags;
+ flags_and = total_flags | ~set_flags;
+ return hostapd_sta_set_flags(hapd, sta->addr, total_flags,
+ flags_or, flags_and);
+}
+
+
+int hostapd_set_drv_ieee8021x(struct hostapd_data *hapd, const char *ifname,
+ int enabled)
+{
+ struct wpa_bss_params params;
+ os_memset(&params, 0, sizeof(params));
+ params.ifname = ifname;
+ params.enabled = enabled;
+ if (enabled) {
+ params.wpa = hapd->conf->wpa;
+ params.ieee802_1x = hapd->conf->ieee802_1x;
+ params.wpa_group = hapd->conf->wpa_group;
+ if ((hapd->conf->wpa & (WPA_PROTO_WPA | WPA_PROTO_RSN)) ==
+ (WPA_PROTO_WPA | WPA_PROTO_RSN))
+ params.wpa_pairwise = hapd->conf->wpa_pairwise |
+ hapd->conf->rsn_pairwise;
+ else if (hapd->conf->wpa & WPA_PROTO_RSN)
+ params.wpa_pairwise = hapd->conf->rsn_pairwise;
+ else if (hapd->conf->wpa & WPA_PROTO_WPA)
+ params.wpa_pairwise = hapd->conf->wpa_pairwise;
+ params.wpa_key_mgmt = hapd->conf->wpa_key_mgmt;
+ params.rsn_preauth = hapd->conf->rsn_preauth;
+#ifdef CONFIG_IEEE80211W
+ params.ieee80211w = hapd->conf->ieee80211w;
+#endif /* CONFIG_IEEE80211W */
+ }
+ return hostapd_set_ieee8021x(hapd, &params);
+}
+
+
+int hostapd_vlan_if_add(struct hostapd_data *hapd, const char *ifname)
+{
+ char force_ifname[IFNAMSIZ];
+ u8 if_addr[ETH_ALEN];
+ return hostapd_if_add(hapd, WPA_IF_AP_VLAN, ifname, hapd->own_addr,
+ NULL, NULL, force_ifname, if_addr, NULL, 0);
+}
+
+
+int hostapd_vlan_if_remove(struct hostapd_data *hapd, const char *ifname)
+{
+ return hostapd_if_remove(hapd, WPA_IF_AP_VLAN, ifname);
+}
+
+
+int hostapd_set_wds_sta(struct hostapd_data *hapd, char *ifname_wds,
+ const u8 *addr, int aid, int val)
+{
+ const char *bridge = NULL;
+
+ if (hapd->driver == NULL || hapd->driver->set_wds_sta == NULL)
+ return -1;
+ if (hapd->conf->wds_bridge[0])
+ bridge = hapd->conf->wds_bridge;
+ else if (hapd->conf->bridge[0])
+ bridge = hapd->conf->bridge;
+ return hapd->driver->set_wds_sta(hapd->drv_priv, addr, aid, val,
+ bridge, ifname_wds);
+}
+
+
+int hostapd_add_sta_node(struct hostapd_data *hapd, const u8 *addr,
+ u16 auth_alg)
+{
+ if (hapd->driver == NULL || hapd->driver->add_sta_node == NULL)
+ return 0;
+ return hapd->driver->add_sta_node(hapd->drv_priv, addr, auth_alg);
+}
+
+
+int hostapd_sta_auth(struct hostapd_data *hapd, const u8 *addr,
+ u16 seq, u16 status, const u8 *ie, size_t len)
+{
+ if (hapd->driver == NULL || hapd->driver->sta_auth == NULL)
+ return 0;
+ return hapd->driver->sta_auth(hapd->drv_priv, hapd->own_addr, addr,
+ seq, status, ie, len);
+}
+
+
+int hostapd_sta_assoc(struct hostapd_data *hapd, const u8 *addr,
+ int reassoc, u16 status, const u8 *ie, size_t len)
+{
+ if (hapd->driver == NULL || hapd->driver->sta_assoc == NULL)
+ return 0;
+ return hapd->driver->sta_assoc(hapd->drv_priv, hapd->own_addr, addr,
+ reassoc, status, ie, len);
+}
+
+
+int hostapd_sta_add(struct hostapd_data *hapd,
+ const u8 *addr, u16 aid, u16 capability,
+ const u8 *supp_rates, size_t supp_rates_len,
+ u16 listen_interval,
+ const struct ieee80211_ht_capabilities *ht_capab,
+ const struct ieee80211_vht_capabilities *vht_capab,
+ u32 flags, u8 qosinfo, u8 vht_opmode)
+{
+ struct hostapd_sta_add_params params;
+
+ if (hapd->driver == NULL)
+ return 0;
+ if (hapd->driver->sta_add == NULL)
+ return 0;
+
+ os_memset(&params, 0, sizeof(params));
+ params.addr = addr;
+ params.aid = aid;
+ params.capability = capability;
+ params.supp_rates = supp_rates;
+ params.supp_rates_len = supp_rates_len;
+ params.listen_interval = listen_interval;
+ params.ht_capabilities = ht_capab;
+ params.vht_capabilities = vht_capab;
+ params.vht_opmode_enabled = !!(flags & WLAN_STA_VHT_OPMODE_ENABLED);
+ params.vht_opmode = vht_opmode;
+ params.flags = hostapd_sta_flags_to_drv(flags);
+ params.qosinfo = qosinfo;
+ return hapd->driver->sta_add(hapd->drv_priv, &params);
+}
+
+
+int hostapd_add_tspec(struct hostapd_data *hapd, const u8 *addr,
+ u8 *tspec_ie, size_t tspec_ielen)
+{
+ if (hapd->driver == NULL || hapd->driver->add_tspec == NULL)
+ return 0;
+ return hapd->driver->add_tspec(hapd->drv_priv, addr, tspec_ie,
+ tspec_ielen);
+}
+
+
+int hostapd_set_privacy(struct hostapd_data *hapd, int enabled)
+{
+ if (hapd->driver == NULL || hapd->driver->set_privacy == NULL)
+ return 0;
+ return hapd->driver->set_privacy(hapd->drv_priv, enabled);
+}
+
+
+int hostapd_set_generic_elem(struct hostapd_data *hapd, const u8 *elem,
+ size_t elem_len)
+{
+ if (hapd->driver == NULL || hapd->driver->set_generic_elem == NULL)
+ return 0;
+ return hapd->driver->set_generic_elem(hapd->drv_priv, elem, elem_len);
+}
+
+
+int hostapd_get_ssid(struct hostapd_data *hapd, u8 *buf, size_t len)
+{
+ if (hapd->driver == NULL || hapd->driver->hapd_get_ssid == NULL)
+ return 0;
+ return hapd->driver->hapd_get_ssid(hapd->drv_priv, buf, len);
+}
+
+
+int hostapd_set_ssid(struct hostapd_data *hapd, const u8 *buf, size_t len)
+{
+ if (hapd->driver == NULL || hapd->driver->hapd_set_ssid == NULL)
+ return 0;
+ return hapd->driver->hapd_set_ssid(hapd->drv_priv, buf, len);
+}
+
+
+int hostapd_if_add(struct hostapd_data *hapd, enum wpa_driver_if_type type,
+ const char *ifname, const u8 *addr, void *bss_ctx,
+ void **drv_priv, char *force_ifname, u8 *if_addr,
+ const char *bridge, int use_existing)
+{
+ if (hapd->driver == NULL || hapd->driver->if_add == NULL)
+ return -1;
+ return hapd->driver->if_add(hapd->drv_priv, type, ifname, addr,
+ bss_ctx, drv_priv, force_ifname, if_addr,
+ bridge, use_existing);
+}
+
+
+int hostapd_if_remove(struct hostapd_data *hapd, enum wpa_driver_if_type type,
+ const char *ifname)
+{
+ if (hapd->driver == NULL || hapd->drv_priv == NULL ||
+ hapd->driver->if_remove == NULL)
+ return -1;
+ return hapd->driver->if_remove(hapd->drv_priv, type, ifname);
+}
+
+
+int hostapd_set_ieee8021x(struct hostapd_data *hapd,
+ struct wpa_bss_params *params)
+{
+ if (hapd->driver == NULL || hapd->driver->set_ieee8021x == NULL)
+ return 0;
+ return hapd->driver->set_ieee8021x(hapd->drv_priv, params);
+}
+
+
+int hostapd_get_seqnum(const char *ifname, struct hostapd_data *hapd,
+ const u8 *addr, int idx, u8 *seq)
+{
+ if (hapd->driver == NULL || hapd->driver->get_seqnum == NULL)
+ return 0;
+ return hapd->driver->get_seqnum(ifname, hapd->drv_priv, addr, idx,
+ seq);
+}
+
+
+int hostapd_flush(struct hostapd_data *hapd)
+{
+ if (hapd->driver == NULL || hapd->driver->flush == NULL)
+ return 0;
+ return hapd->driver->flush(hapd->drv_priv);
+}
+
+
+int hostapd_set_freq(struct hostapd_data *hapd, enum hostapd_hw_mode mode,
+ int freq, int channel, int ht_enabled, int vht_enabled,
+ int sec_channel_offset, int vht_oper_chwidth,
+ int center_segment0, int center_segment1)
+{
+ struct hostapd_freq_params data;
+
+ if (hostapd_set_freq_params(&data, mode, freq, channel, ht_enabled,
+ vht_enabled, sec_channel_offset,
+ vht_oper_chwidth,
+ center_segment0, center_segment1,
+ hapd->iface->current_mode ?
+ hapd->iface->current_mode->vht_capab : 0))
+ return -1;
+
+ if (hapd->driver == NULL)
+ return 0;
+ if (hapd->driver->set_freq == NULL)
+ return 0;
+ return hapd->driver->set_freq(hapd->drv_priv, &data);
+}
+
+int hostapd_set_rts(struct hostapd_data *hapd, int rts)
+{
+ if (hapd->driver == NULL || hapd->driver->set_rts == NULL)
+ return 0;
+ return hapd->driver->set_rts(hapd->drv_priv, rts);
+}
+
+
+int hostapd_set_frag(struct hostapd_data *hapd, int frag)
+{
+ if (hapd->driver == NULL || hapd->driver->set_frag == NULL)
+ return 0;
+ return hapd->driver->set_frag(hapd->drv_priv, frag);
+}
+
+
+int hostapd_sta_set_flags(struct hostapd_data *hapd, u8 *addr,
+ int total_flags, int flags_or, int flags_and)
+{
+ if (hapd->driver == NULL || hapd->driver->sta_set_flags == NULL)
+ return 0;
+ return hapd->driver->sta_set_flags(hapd->drv_priv, addr, total_flags,
+ flags_or, flags_and);
+}
+
+
+int hostapd_set_country(struct hostapd_data *hapd, const char *country)
+{
+ if (hapd->driver == NULL ||
+ hapd->driver->set_country == NULL)
+ return 0;
+ return hapd->driver->set_country(hapd->drv_priv, country);
+}
+
+
+int hostapd_set_tx_queue_params(struct hostapd_data *hapd, int queue, int aifs,
+ int cw_min, int cw_max, int burst_time)
+{
+ if (hapd->driver == NULL || hapd->driver->set_tx_queue_params == NULL)
+ return 0;
+ return hapd->driver->set_tx_queue_params(hapd->drv_priv, queue, aifs,
+ cw_min, cw_max, burst_time);
+}
+
+
+struct hostapd_hw_modes *
+hostapd_get_hw_feature_data(struct hostapd_data *hapd, u16 *num_modes,
+ u16 *flags)
+{
+ if (hapd->driver == NULL ||
+ hapd->driver->get_hw_feature_data == NULL)
+ return NULL;
+ return hapd->driver->get_hw_feature_data(hapd->drv_priv, num_modes,
+ flags);
+}
+
+
+int hostapd_driver_commit(struct hostapd_data *hapd)
+{
+ if (hapd->driver == NULL || hapd->driver->commit == NULL)
+ return 0;
+ return hapd->driver->commit(hapd->drv_priv);
+}
+
+
+int hostapd_drv_none(struct hostapd_data *hapd)
+{
+ return hapd->driver && os_strcmp(hapd->driver->name, "none") == 0;
+}
+
+
+int hostapd_driver_scan(struct hostapd_data *hapd,
+ struct wpa_driver_scan_params *params)
+{
+ if (hapd->driver && hapd->driver->scan2)
+ return hapd->driver->scan2(hapd->drv_priv, params);
+ return -1;
+}
+
+
+struct wpa_scan_results * hostapd_driver_get_scan_results(
+ struct hostapd_data *hapd)
+{
+ if (hapd->driver && hapd->driver->get_scan_results2)
+ return hapd->driver->get_scan_results2(hapd->drv_priv);
+ return NULL;
+}
+
+
+int hostapd_driver_set_noa(struct hostapd_data *hapd, u8 count, int start,
+ int duration)
+{
+ if (hapd->driver && hapd->driver->set_noa)
+ return hapd->driver->set_noa(hapd->drv_priv, count, start,
+ duration);
+ return -1;
+}
+
+
+int hostapd_drv_set_key(const char *ifname, struct hostapd_data *hapd,
+ enum wpa_alg alg, const u8 *addr,
+ int key_idx, int set_tx,
+ const u8 *seq, size_t seq_len,
+ const u8 *key, size_t key_len)
+{
+ if (hapd->driver == NULL || hapd->driver->set_key == NULL)
+ return 0;
+ return hapd->driver->set_key(ifname, hapd->drv_priv, alg, addr,
+ key_idx, set_tx, seq, seq_len, key,
+ key_len);
+}
+
+
+int hostapd_drv_send_mlme(struct hostapd_data *hapd,
+ const void *msg, size_t len, int noack)
+{
+ if (hapd->driver == NULL || hapd->driver->send_mlme == NULL)
+ return 0;
+ return hapd->driver->send_mlme(hapd->drv_priv, msg, len, noack, 0);
+}
+
+
+int hostapd_drv_sta_deauth(struct hostapd_data *hapd,
+ const u8 *addr, int reason)
+{
+ if (hapd->driver == NULL || hapd->driver->sta_deauth == NULL)
+ return 0;
+ return hapd->driver->sta_deauth(hapd->drv_priv, hapd->own_addr, addr,
+ reason);
+}
+
+
+int hostapd_drv_sta_disassoc(struct hostapd_data *hapd,
+ const u8 *addr, int reason)
+{
+ if (hapd->driver == NULL || hapd->driver->sta_disassoc == NULL)
+ return 0;
+ return hapd->driver->sta_disassoc(hapd->drv_priv, hapd->own_addr, addr,
+ reason);
+}
+
+
+int hostapd_drv_wnm_oper(struct hostapd_data *hapd, enum wnm_oper oper,
+ const u8 *peer, u8 *buf, u16 *buf_len)
+{
+ if (hapd->driver == NULL || hapd->driver->wnm_oper == NULL)
+ return -1;
+ return hapd->driver->wnm_oper(hapd->drv_priv, oper, peer, buf,
+ buf_len);
+}
+
+
+int hostapd_drv_send_action(struct hostapd_data *hapd, unsigned int freq,
+ unsigned int wait, const u8 *dst, const u8 *data,
+ size_t len)
+{
+ if (hapd->driver == NULL || hapd->driver->send_action == NULL)
+ return 0;
+ return hapd->driver->send_action(hapd->drv_priv, freq, wait, dst,
+ hapd->own_addr, hapd->own_addr, data,
+ len, 0);
+}
+
+
+int hostapd_start_dfs_cac(struct hostapd_iface *iface,
+ enum hostapd_hw_mode mode, int freq,
+ int channel, int ht_enabled, int vht_enabled,
+ int sec_channel_offset, int vht_oper_chwidth,
+ int center_segment0, int center_segment1)
+{
+ struct hostapd_data *hapd = iface->bss[0];
+ struct hostapd_freq_params data;
+ int res;
+
+ if (!hapd->driver || !hapd->driver->start_dfs_cac)
+ return 0;
+
+ if (!iface->conf->ieee80211h) {
+ wpa_printf(MSG_ERROR, "Can't start DFS CAC, DFS functionality "
+ "is not enabled");
+ return -1;
+ }
+
+ if (hostapd_set_freq_params(&data, mode, freq, channel, ht_enabled,
+ vht_enabled, sec_channel_offset,
+ vht_oper_chwidth, center_segment0,
+ center_segment1,
+ iface->current_mode->vht_capab)) {
+ wpa_printf(MSG_ERROR, "Can't set freq params");
+ return -1;
+ }
+
+ res = hapd->driver->start_dfs_cac(hapd->drv_priv, &data);
+ if (!res) {
+ iface->cac_started = 1;
+ os_get_reltime(&iface->dfs_cac_start);
+ }
+
+ return res;
+}
+
+
+int hostapd_drv_set_qos_map(struct hostapd_data *hapd,
+ const u8 *qos_map_set, u8 qos_map_set_len)
+{
+ if (hapd->driver == NULL || hapd->driver->set_qos_map == NULL)
+ return 0;
+ return hapd->driver->set_qos_map(hapd->drv_priv, qos_map_set,
+ qos_map_set_len);
+}
+
+
+static void hostapd_get_hw_mode_any_channels(struct hostapd_data *hapd,
+ struct hostapd_hw_modes *mode,
+ int acs_ch_list_all,
+ int **freq_list)
+{
+ int i;
+
+ for (i = 0; i < mode->num_channels; i++) {
+ struct hostapd_channel_data *chan = &mode->channels[i];
+
+ if ((acs_ch_list_all ||
+ freq_range_list_includes(&hapd->iface->conf->acs_ch_list,
+ chan->chan)) &&
+ !(chan->flag & HOSTAPD_CHAN_DISABLED))
+ int_array_add_unique(freq_list, chan->freq);
+ }
+}
+
+
+int hostapd_drv_do_acs(struct hostapd_data *hapd)
+{
+ struct drv_acs_params params;
+ int ret, i, acs_ch_list_all = 0;
+ u8 *channels = NULL;
+ unsigned int num_channels = 0;
+ struct hostapd_hw_modes *mode;
+ int *freq_list = NULL;
+
+ if (hapd->driver == NULL || hapd->driver->do_acs == NULL)
+ return 0;
+
+ os_memset(&params, 0, sizeof(params));
+ params.hw_mode = hapd->iface->conf->hw_mode;
+
+ /*
+ * If no chanlist config parameter is provided, include all enabled
+ * channels of the selected hw_mode.
+ */
+ if (!hapd->iface->conf->acs_ch_list.num)
+ acs_ch_list_all = 1;
+
+ mode = hapd->iface->current_mode;
+ if (mode) {
+ channels = os_malloc(mode->num_channels);
+ if (channels == NULL)
+ return -1;
+
+ for (i = 0; i < mode->num_channels; i++) {
+ struct hostapd_channel_data *chan = &mode->channels[i];
+ if (!acs_ch_list_all &&
+ !freq_range_list_includes(
+ &hapd->iface->conf->acs_ch_list,
+ chan->chan))
+ continue;
+ if (!(chan->flag & HOSTAPD_CHAN_DISABLED)) {
+ channels[num_channels++] = chan->chan;
+ int_array_add_unique(&freq_list, chan->freq);
+ }
+ }
+ } else {
+ for (i = 0; i < hapd->iface->num_hw_features; i++) {
+ mode = &hapd->iface->hw_features[i];
+ hostapd_get_hw_mode_any_channels(hapd, mode,
+ acs_ch_list_all,
+ &freq_list);
+ }
+ }
+
+ params.ch_list = channels;
+ params.ch_list_len = num_channels;
+ params.freq_list = freq_list;
+
+ params.ht_enabled = !!(hapd->iface->conf->ieee80211n);
+ params.ht40_enabled = !!(hapd->iface->conf->ht_capab &
+ HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET);
+ params.vht_enabled = !!(hapd->iface->conf->ieee80211ac);
+ params.ch_width = 20;
+ if (hapd->iface->conf->ieee80211n && params.ht40_enabled)
+ params.ch_width = 40;
+
+ /* Note: VHT20 is defined by combination of ht_capab & vht_oper_chwidth
+ */
+ if (hapd->iface->conf->ieee80211ac && params.ht40_enabled) {
+ if (hapd->iface->conf->vht_oper_chwidth == VHT_CHANWIDTH_80MHZ)
+ params.ch_width = 80;
+ else if (hapd->iface->conf->vht_oper_chwidth ==
+ VHT_CHANWIDTH_160MHZ ||
+ hapd->iface->conf->vht_oper_chwidth ==
+ VHT_CHANWIDTH_80P80MHZ)
+ params.ch_width = 160;
+ }
+
+ ret = hapd->driver->do_acs(hapd->drv_priv, &params);
+ os_free(channels);
+
+ return ret;
+}
diff --git a/freebsd/contrib/wpa/src/ap/ap_drv_ops.h b/freebsd/contrib/wpa/src/ap/ap_drv_ops.h
new file mode 100644
index 00000000..82eaf3f0
--- /dev/null
+++ b/freebsd/contrib/wpa/src/ap/ap_drv_ops.h
@@ -0,0 +1,340 @@
+/*
+ * hostapd - Driver operations
+ * Copyright (c) 2009-2014, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef AP_DRV_OPS
+#define AP_DRV_OPS
+
+enum wpa_driver_if_type;
+struct wpa_bss_params;
+struct wpa_driver_scan_params;
+struct ieee80211_ht_capabilities;
+struct ieee80211_vht_capabilities;
+struct hostapd_freq_params;
+
+u32 hostapd_sta_flags_to_drv(u32 flags);
+int hostapd_build_ap_extra_ies(struct hostapd_data *hapd,
+ struct wpabuf **beacon,
+ struct wpabuf **proberesp,
+ struct wpabuf **assocresp);
+void hostapd_free_ap_extra_ies(struct hostapd_data *hapd, struct wpabuf *beacon,
+ struct wpabuf *proberesp,
+ struct wpabuf *assocresp);
+int hostapd_reset_ap_wps_ie(struct hostapd_data *hapd);
+int hostapd_set_ap_wps_ie(struct hostapd_data *hapd);
+int hostapd_set_authorized(struct hostapd_data *hapd,
+ struct sta_info *sta, int authorized);
+int hostapd_set_sta_flags(struct hostapd_data *hapd, struct sta_info *sta);
+int hostapd_set_drv_ieee8021x(struct hostapd_data *hapd, const char *ifname,
+ int enabled);
+int hostapd_vlan_if_add(struct hostapd_data *hapd, const char *ifname);
+int hostapd_vlan_if_remove(struct hostapd_data *hapd, const char *ifname);
+int hostapd_set_wds_sta(struct hostapd_data *hapd, char *ifname_wds,
+ const u8 *addr, int aid, int val);
+int hostapd_sta_add(struct hostapd_data *hapd,
+ const u8 *addr, u16 aid, u16 capability,
+ const u8 *supp_rates, size_t supp_rates_len,
+ u16 listen_interval,
+ const struct ieee80211_ht_capabilities *ht_capab,
+ const struct ieee80211_vht_capabilities *vht_capab,
+ u32 flags, u8 qosinfo, u8 vht_opmode);
+int hostapd_set_privacy(struct hostapd_data *hapd, int enabled);
+int hostapd_set_generic_elem(struct hostapd_data *hapd, const u8 *elem,
+ size_t elem_len);
+int hostapd_get_ssid(struct hostapd_data *hapd, u8 *buf, size_t len);
+int hostapd_set_ssid(struct hostapd_data *hapd, const u8 *buf, size_t len);
+int hostapd_if_add(struct hostapd_data *hapd, enum wpa_driver_if_type type,
+ const char *ifname, const u8 *addr, void *bss_ctx,
+ void **drv_priv, char *force_ifname, u8 *if_addr,
+ const char *bridge, int use_existing);
+int hostapd_if_remove(struct hostapd_data *hapd, enum wpa_driver_if_type type,
+ const char *ifname);
+int hostapd_set_ieee8021x(struct hostapd_data *hapd,
+ struct wpa_bss_params *params);
+int hostapd_get_seqnum(const char *ifname, struct hostapd_data *hapd,
+ const u8 *addr, int idx, u8 *seq);
+int hostapd_flush(struct hostapd_data *hapd);
+int hostapd_set_freq(struct hostapd_data *hapd, enum hostapd_hw_mode mode,
+ int freq, int channel, int ht_enabled, int vht_enabled,
+ int sec_channel_offset, int vht_oper_chwidth,
+ int center_segment0, int center_segment1);
+int hostapd_set_rts(struct hostapd_data *hapd, int rts);
+int hostapd_set_frag(struct hostapd_data *hapd, int frag);
+int hostapd_sta_set_flags(struct hostapd_data *hapd, u8 *addr,
+ int total_flags, int flags_or, int flags_and);
+int hostapd_set_country(struct hostapd_data *hapd, const char *country);
+int hostapd_set_tx_queue_params(struct hostapd_data *hapd, int queue, int aifs,
+ int cw_min, int cw_max, int burst_time);
+struct hostapd_hw_modes *
+hostapd_get_hw_feature_data(struct hostapd_data *hapd, u16 *num_modes,
+ u16 *flags);
+int hostapd_driver_commit(struct hostapd_data *hapd);
+int hostapd_drv_none(struct hostapd_data *hapd);
+int hostapd_driver_scan(struct hostapd_data *hapd,
+ struct wpa_driver_scan_params *params);
+struct wpa_scan_results * hostapd_driver_get_scan_results(
+ struct hostapd_data *hapd);
+int hostapd_driver_set_noa(struct hostapd_data *hapd, u8 count, int start,
+ int duration);
+int hostapd_drv_set_key(const char *ifname,
+ struct hostapd_data *hapd,
+ enum wpa_alg alg, const u8 *addr,
+ int key_idx, int set_tx,
+ const u8 *seq, size_t seq_len,
+ const u8 *key, size_t key_len);
+int hostapd_drv_send_mlme(struct hostapd_data *hapd,
+ const void *msg, size_t len, int noack);
+int hostapd_drv_sta_deauth(struct hostapd_data *hapd,
+ const u8 *addr, int reason);
+int hostapd_drv_sta_disassoc(struct hostapd_data *hapd,
+ const u8 *addr, int reason);
+int hostapd_drv_send_action(struct hostapd_data *hapd, unsigned int freq,
+ unsigned int wait, const u8 *dst, const u8 *data,
+ size_t len);
+int hostapd_add_sta_node(struct hostapd_data *hapd, const u8 *addr,
+ u16 auth_alg);
+int hostapd_sta_auth(struct hostapd_data *hapd, const u8 *addr,
+ u16 seq, u16 status, const u8 *ie, size_t len);
+int hostapd_sta_assoc(struct hostapd_data *hapd, const u8 *addr,
+ int reassoc, u16 status, const u8 *ie, size_t len);
+int hostapd_add_tspec(struct hostapd_data *hapd, const u8 *addr,
+ u8 *tspec_ie, size_t tspec_ielen);
+int hostapd_start_dfs_cac(struct hostapd_iface *iface,
+ enum hostapd_hw_mode mode, int freq,
+ int channel, int ht_enabled, int vht_enabled,
+ int sec_channel_offset, int vht_oper_chwidth,
+ int center_segment0, int center_segment1);
+int hostapd_drv_do_acs(struct hostapd_data *hapd);
+
+
+#include "drivers/driver.h"
+
+int hostapd_drv_wnm_oper(struct hostapd_data *hapd,
+ enum wnm_oper oper, const u8 *peer,
+ u8 *buf, u16 *buf_len);
+
+int hostapd_drv_set_qos_map(struct hostapd_data *hapd, const u8 *qos_map_set,
+ u8 qos_map_set_len);
+
+static inline int hostapd_drv_set_countermeasures(struct hostapd_data *hapd,
+ int enabled)
+{
+ if (hapd->driver == NULL ||
+ hapd->driver->hapd_set_countermeasures == NULL)
+ return 0;
+ return hapd->driver->hapd_set_countermeasures(hapd->drv_priv, enabled);
+}
+
+static inline int hostapd_drv_set_sta_vlan(const char *ifname,
+ struct hostapd_data *hapd,
+ const u8 *addr, int vlan_id)
+{
+ if (hapd->driver == NULL || hapd->driver->set_sta_vlan == NULL)
+ return 0;
+ return hapd->driver->set_sta_vlan(hapd->drv_priv, addr, ifname,
+ vlan_id);
+}
+
+static inline int hostapd_drv_get_inact_sec(struct hostapd_data *hapd,
+ const u8 *addr)
+{
+ if (hapd->driver == NULL || hapd->driver->get_inact_sec == NULL)
+ return 0;
+ return hapd->driver->get_inact_sec(hapd->drv_priv, addr);
+}
+
+static inline int hostapd_drv_sta_remove(struct hostapd_data *hapd,
+ const u8 *addr)
+{
+ if (hapd->driver == NULL || hapd->driver->sta_remove == NULL)
+ return 0;
+ return hapd->driver->sta_remove(hapd->drv_priv, addr);
+}
+
+static inline int hostapd_drv_hapd_send_eapol(struct hostapd_data *hapd,
+ const u8 *addr, const u8 *data,
+ size_t data_len, int encrypt,
+ u32 flags)
+{
+ if (hapd->driver == NULL || hapd->driver->hapd_send_eapol == NULL)
+ return 0;
+ return hapd->driver->hapd_send_eapol(hapd->drv_priv, addr, data,
+ data_len, encrypt,
+ hapd->own_addr, flags);
+}
+
+static inline int hostapd_drv_read_sta_data(
+ struct hostapd_data *hapd, struct hostap_sta_driver_data *data,
+ const u8 *addr)
+{
+ if (hapd->driver == NULL || hapd->driver->read_sta_data == NULL)
+ return -1;
+ return hapd->driver->read_sta_data(hapd->drv_priv, data, addr);
+}
+
+static inline int hostapd_drv_sta_clear_stats(struct hostapd_data *hapd,
+ const u8 *addr)
+{
+ if (hapd->driver == NULL || hapd->driver->sta_clear_stats == NULL)
+ return 0;
+ return hapd->driver->sta_clear_stats(hapd->drv_priv, addr);
+}
+
+static inline int hostapd_drv_set_acl(struct hostapd_data *hapd,
+ struct hostapd_acl_params *params)
+{
+ if (hapd->driver == NULL || hapd->driver->set_acl == NULL)
+ return 0;
+ return hapd->driver->set_acl(hapd->drv_priv, params);
+}
+
+static inline int hostapd_drv_set_ap(struct hostapd_data *hapd,
+ struct wpa_driver_ap_params *params)
+{
+ if (hapd->driver == NULL || hapd->driver->set_ap == NULL)
+ return 0;
+ return hapd->driver->set_ap(hapd->drv_priv, params);
+}
+
+static inline int hostapd_drv_set_radius_acl_auth(struct hostapd_data *hapd,
+ const u8 *mac, int accepted,
+ u32 session_timeout)
+{
+ if (hapd->driver == NULL || hapd->driver->set_radius_acl_auth == NULL)
+ return 0;
+ return hapd->driver->set_radius_acl_auth(hapd->drv_priv, mac, accepted,
+ session_timeout);
+}
+
+static inline int hostapd_drv_set_radius_acl_expire(struct hostapd_data *hapd,
+ const u8 *mac)
+{
+ if (hapd->driver == NULL ||
+ hapd->driver->set_radius_acl_expire == NULL)
+ return 0;
+ return hapd->driver->set_radius_acl_expire(hapd->drv_priv, mac);
+}
+
+static inline int hostapd_drv_set_authmode(struct hostapd_data *hapd,
+ int auth_algs)
+{
+ if (hapd->driver == NULL || hapd->driver->set_authmode == NULL)
+ return 0;
+ return hapd->driver->set_authmode(hapd->drv_priv, auth_algs);
+}
+
+static inline void hostapd_drv_poll_client(struct hostapd_data *hapd,
+ const u8 *own_addr, const u8 *addr,
+ int qos)
+{
+ if (hapd->driver == NULL || hapd->driver->poll_client == NULL)
+ return;
+ hapd->driver->poll_client(hapd->drv_priv, own_addr, addr, qos);
+}
+
+static inline int hostapd_drv_get_survey(struct hostapd_data *hapd,
+ unsigned int freq)
+{
+ if (hapd->driver == NULL)
+ return -1;
+ if (!hapd->driver->get_survey)
+ return -1;
+ return hapd->driver->get_survey(hapd->drv_priv, freq);
+}
+
+static inline int hostapd_get_country(struct hostapd_data *hapd, char *alpha2)
+{
+ if (hapd->driver == NULL || hapd->driver->get_country == NULL)
+ return -1;
+ return hapd->driver->get_country(hapd->drv_priv, alpha2);
+}
+
+static inline const char * hostapd_drv_get_radio_name(struct hostapd_data *hapd)
+{
+ if (hapd->driver == NULL || hapd->drv_priv == NULL ||
+ hapd->driver->get_radio_name == NULL)
+ return NULL;
+ return hapd->driver->get_radio_name(hapd->drv_priv);
+}
+
+static inline int hostapd_drv_switch_channel(struct hostapd_data *hapd,
+ struct csa_settings *settings)
+{
+ if (hapd->driver == NULL || hapd->driver->switch_channel == NULL)
+ return -ENOTSUP;
+
+ return hapd->driver->switch_channel(hapd->drv_priv, settings);
+}
+
+static inline int hostapd_drv_status(struct hostapd_data *hapd, char *buf,
+ size_t buflen)
+{
+ if (hapd->driver == NULL || hapd->driver->status == NULL)
+ return -1;
+ return hapd->driver->status(hapd->drv_priv, buf, buflen);
+}
+
+static inline int hostapd_drv_br_add_ip_neigh(struct hostapd_data *hapd,
+ int version, const u8 *ipaddr,
+ int prefixlen, const u8 *addr)
+{
+ if (hapd->driver == NULL || hapd->drv_priv == NULL ||
+ hapd->driver->br_add_ip_neigh == NULL)
+ return -1;
+ return hapd->driver->br_add_ip_neigh(hapd->drv_priv, version, ipaddr,
+ prefixlen, addr);
+}
+
+static inline int hostapd_drv_br_delete_ip_neigh(struct hostapd_data *hapd,
+ u8 version, const u8 *ipaddr)
+{
+ if (hapd->driver == NULL || hapd->drv_priv == NULL ||
+ hapd->driver->br_delete_ip_neigh == NULL)
+ return -1;
+ return hapd->driver->br_delete_ip_neigh(hapd->drv_priv, version,
+ ipaddr);
+}
+
+static inline int hostapd_drv_br_port_set_attr(struct hostapd_data *hapd,
+ enum drv_br_port_attr attr,
+ unsigned int val)
+{
+ if (hapd->driver == NULL || hapd->drv_priv == NULL ||
+ hapd->driver->br_port_set_attr == NULL)
+ return -1;
+ return hapd->driver->br_port_set_attr(hapd->drv_priv, attr, val);
+}
+
+static inline int hostapd_drv_br_set_net_param(struct hostapd_data *hapd,
+ enum drv_br_net_param param,
+ unsigned int val)
+{
+ if (hapd->driver == NULL || hapd->drv_priv == NULL ||
+ hapd->driver->br_set_net_param == NULL)
+ return -1;
+ return hapd->driver->br_set_net_param(hapd->drv_priv, param, val);
+}
+
+static inline int hostapd_drv_vendor_cmd(struct hostapd_data *hapd,
+ int vendor_id, int subcmd,
+ const u8 *data, size_t data_len,
+ struct wpabuf *buf)
+{
+ if (hapd->driver == NULL || hapd->driver->vendor_cmd == NULL)
+ return -1;
+ return hapd->driver->vendor_cmd(hapd->drv_priv, vendor_id, subcmd, data,
+ data_len, buf);
+}
+
+static inline int hostapd_drv_stop_ap(struct hostapd_data *hapd)
+{
+ if (hapd->driver == NULL || hapd->driver->stop_ap == NULL)
+ return 0;
+ return hapd->driver->stop_ap(hapd->drv_priv);
+}
+
+#endif /* AP_DRV_OPS */
diff --git a/freebsd/contrib/wpa/src/ap/hostapd.h b/freebsd/contrib/wpa/src/ap/hostapd.h
new file mode 100644
index 00000000..dcf51f00
--- /dev/null
+++ b/freebsd/contrib/wpa/src/ap/hostapd.h
@@ -0,0 +1,494 @@
+/*
+ * hostapd / Initialization and configuration
+ * Copyright (c) 2002-2014, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef HOSTAPD_H
+#define HOSTAPD_H
+
+#include "common/defs.h"
+#include "utils/list.h"
+#include "ap_config.h"
+#include "drivers/driver.h"
+
+struct wpa_ctrl_dst;
+struct radius_server_data;
+struct upnp_wps_device_sm;
+struct hostapd_data;
+struct sta_info;
+struct ieee80211_ht_capabilities;
+struct full_dynamic_vlan;
+enum wps_event;
+union wps_event_data;
+#ifdef CONFIG_MESH
+struct mesh_conf;
+#endif /* CONFIG_MESH */
+
+struct hostapd_iface;
+
+struct hapd_interfaces {
+ int (*reload_config)(struct hostapd_iface *iface);
+ struct hostapd_config * (*config_read_cb)(const char *config_fname);
+ int (*ctrl_iface_init)(struct hostapd_data *hapd);
+ void (*ctrl_iface_deinit)(struct hostapd_data *hapd);
+ int (*for_each_interface)(struct hapd_interfaces *interfaces,
+ int (*cb)(struct hostapd_iface *iface,
+ void *ctx), void *ctx);
+ int (*driver_init)(struct hostapd_iface *iface);
+
+ size_t count;
+ int global_ctrl_sock;
+ struct wpa_ctrl_dst *global_ctrl_dst;
+ char *global_iface_path;
+ char *global_iface_name;
+#ifndef CONFIG_NATIVE_WINDOWS
+ gid_t ctrl_iface_group;
+#endif /* CONFIG_NATIVE_WINDOWS */
+ struct hostapd_iface **iface;
+
+ size_t terminate_on_error;
+#ifndef CONFIG_NO_VLAN
+ struct dynamic_iface *vlan_priv;
+#endif /* CONFIG_NO_VLAN */
+};
+
+enum hostapd_chan_status {
+ HOSTAPD_CHAN_VALID = 0, /* channel is ready */
+ HOSTAPD_CHAN_INVALID = 1, /* no usable channel found */
+ HOSTAPD_CHAN_ACS = 2, /* ACS work being performed */
+};
+
+struct hostapd_probereq_cb {
+ int (*cb)(void *ctx, const u8 *sa, const u8 *da, const u8 *bssid,
+ const u8 *ie, size_t ie_len, int ssi_signal);
+ void *ctx;
+};
+
+#define HOSTAPD_RATE_BASIC 0x00000001
+
+struct hostapd_rate_data {
+ int rate; /* rate in 100 kbps */
+ int flags; /* HOSTAPD_RATE_ flags */
+};
+
+struct hostapd_frame_info {
+ u32 channel;
+ u32 datarate;
+ int ssi_signal; /* dBm */
+};
+
+enum wps_status {
+ WPS_STATUS_SUCCESS = 1,
+ WPS_STATUS_FAILURE
+};
+
+enum pbc_status {
+ WPS_PBC_STATUS_DISABLE,
+ WPS_PBC_STATUS_ACTIVE,
+ WPS_PBC_STATUS_TIMEOUT,
+ WPS_PBC_STATUS_OVERLAP
+};
+
+struct wps_stat {
+ enum wps_status status;
+ enum wps_error_indication failure_reason;
+ enum pbc_status pbc_status;
+ u8 peer_addr[ETH_ALEN];
+};
+
+
+/**
+ * struct hostapd_data - hostapd per-BSS data structure
+ */
+struct hostapd_data {
+ struct hostapd_iface *iface;
+ struct hostapd_config *iconf;
+ struct hostapd_bss_config *conf;
+ int interface_added; /* virtual interface added for this BSS */
+ unsigned int started:1;
+ unsigned int disabled:1;
+ unsigned int reenable_beacon:1;
+
+ u8 own_addr[ETH_ALEN];
+
+ int num_sta; /* number of entries in sta_list */
+ struct sta_info *sta_list; /* STA info list head */
+#define STA_HASH_SIZE 256
+#define STA_HASH(sta) (sta[5])
+ struct sta_info *sta_hash[STA_HASH_SIZE];
+
+ /*
+ * Bitfield for indicating which AIDs are allocated. Only AID values
+ * 1-2007 are used and as such, the bit at index 0 corresponds to AID
+ * 1.
+ */
+#define AID_WORDS ((2008 + 31) / 32)
+ u32 sta_aid[AID_WORDS];
+
+ const struct wpa_driver_ops *driver;
+ void *drv_priv;
+
+ void (*new_assoc_sta_cb)(struct hostapd_data *hapd,
+ struct sta_info *sta, int reassoc);
+
+ void *msg_ctx; /* ctx for wpa_msg() calls */
+ void *msg_ctx_parent; /* parent interface ctx for wpa_msg() calls */
+
+ struct radius_client_data *radius;
+ u32 acct_session_id_hi, acct_session_id_lo;
+ struct radius_das_data *radius_das;
+
+ struct iapp_data *iapp;
+
+ struct hostapd_cached_radius_acl *acl_cache;
+ struct hostapd_acl_query_data *acl_queries;
+
+ struct wpa_authenticator *wpa_auth;
+ struct eapol_authenticator *eapol_auth;
+
+ struct rsn_preauth_interface *preauth_iface;
+ struct os_reltime michael_mic_failure;
+ int michael_mic_failures;
+ int tkip_countermeasures;
+
+ int ctrl_sock;
+ struct wpa_ctrl_dst *ctrl_dst;
+
+ void *ssl_ctx;
+ void *eap_sim_db_priv;
+ struct radius_server_data *radius_srv;
+ struct dl_list erp_keys; /* struct eap_server_erp_key */
+
+ int parameter_set_count;
+
+ /* Time Advertisement */
+ u8 time_update_counter;
+ struct wpabuf *time_adv;
+
+#ifdef CONFIG_FULL_DYNAMIC_VLAN
+ struct full_dynamic_vlan *full_dynamic_vlan;
+#endif /* CONFIG_FULL_DYNAMIC_VLAN */
+
+ struct l2_packet_data *l2;
+ struct wps_context *wps;
+
+ int beacon_set_done;
+ struct wpabuf *wps_beacon_ie;
+ struct wpabuf *wps_probe_resp_ie;
+#ifdef CONFIG_WPS
+ unsigned int ap_pin_failures;
+ unsigned int ap_pin_failures_consecutive;
+ struct upnp_wps_device_sm *wps_upnp;
+ unsigned int ap_pin_lockout_time;
+
+ struct wps_stat wps_stats;
+#endif /* CONFIG_WPS */
+
+ struct hostapd_probereq_cb *probereq_cb;
+ size_t num_probereq_cb;
+
+ void (*public_action_cb)(void *ctx, const u8 *buf, size_t len,
+ int freq);
+ void *public_action_cb_ctx;
+ void (*public_action_cb2)(void *ctx, const u8 *buf, size_t len,
+ int freq);
+ void *public_action_cb2_ctx;
+
+ int (*vendor_action_cb)(void *ctx, const u8 *buf, size_t len,
+ int freq);
+ void *vendor_action_cb_ctx;
+
+ void (*wps_reg_success_cb)(void *ctx, const u8 *mac_addr,
+ const u8 *uuid_e);
+ void *wps_reg_success_cb_ctx;
+
+ void (*wps_event_cb)(void *ctx, enum wps_event event,
+ union wps_event_data *data);
+ void *wps_event_cb_ctx;
+
+ void (*sta_authorized_cb)(void *ctx, const u8 *mac_addr,
+ int authorized, const u8 *p2p_dev_addr);
+ void *sta_authorized_cb_ctx;
+
+ void (*setup_complete_cb)(void *ctx);
+ void *setup_complete_cb_ctx;
+
+ void (*new_psk_cb)(void *ctx, const u8 *mac_addr,
+ const u8 *p2p_dev_addr, const u8 *psk,
+ size_t psk_len);
+ void *new_psk_cb_ctx;
+
+ /* channel switch parameters */
+ struct hostapd_freq_params cs_freq_params;
+ u8 cs_count;
+ int cs_block_tx;
+ unsigned int cs_c_off_beacon;
+ unsigned int cs_c_off_proberesp;
+ int csa_in_progress;
+
+ /* BSS Load */
+ unsigned int bss_load_update_timeout;
+
+#ifdef CONFIG_P2P
+ struct p2p_data *p2p;
+ struct p2p_group *p2p_group;
+ struct wpabuf *p2p_beacon_ie;
+ struct wpabuf *p2p_probe_resp_ie;
+
+ /* Number of non-P2P association stations */
+ int num_sta_no_p2p;
+
+ /* Periodic NoA (used only when no non-P2P clients in the group) */
+ int noa_enabled;
+ int noa_start;
+ int noa_duration;
+#endif /* CONFIG_P2P */
+#ifdef CONFIG_INTERWORKING
+ size_t gas_frag_limit;
+#endif /* CONFIG_INTERWORKING */
+#ifdef CONFIG_PROXYARP
+ struct l2_packet_data *sock_dhcp;
+ struct l2_packet_data *sock_ndisc;
+#endif /* CONFIG_PROXYARP */
+#ifdef CONFIG_MESH
+ int num_plinks;
+ int max_plinks;
+ void (*mesh_sta_free_cb)(struct sta_info *sta);
+ struct wpabuf *mesh_pending_auth;
+ struct os_reltime mesh_pending_auth_time;
+#endif /* CONFIG_MESH */
+
+#ifdef CONFIG_SQLITE
+ struct hostapd_eap_user tmp_eap_user;
+#endif /* CONFIG_SQLITE */
+
+#ifdef CONFIG_SAE
+ /** Key used for generating SAE anti-clogging tokens */
+ u8 sae_token_key[8];
+ struct os_reltime last_sae_token_key_update;
+ int dot11RSNASAERetransPeriod; /* msec */
+#endif /* CONFIG_SAE */
+
+#ifdef CONFIG_TESTING_OPTIONS
+ unsigned int ext_mgmt_frame_handling:1;
+ unsigned int ext_eapol_frame_io:1;
+
+ struct l2_packet_data *l2_test;
+#endif /* CONFIG_TESTING_OPTIONS */
+};
+
+
+struct hostapd_sta_info {
+ struct dl_list list;
+ u8 addr[ETH_ALEN];
+ struct os_reltime last_seen;
+};
+
+/**
+ * struct hostapd_iface - hostapd per-interface data structure
+ */
+struct hostapd_iface {
+ struct hapd_interfaces *interfaces;
+ void *owner;
+ char *config_fname;
+ struct hostapd_config *conf;
+ char phy[16]; /* Name of the PHY (radio) */
+
+ enum hostapd_iface_state {
+ HAPD_IFACE_UNINITIALIZED,
+ HAPD_IFACE_DISABLED,
+ HAPD_IFACE_COUNTRY_UPDATE,
+ HAPD_IFACE_ACS,
+ HAPD_IFACE_HT_SCAN,
+ HAPD_IFACE_DFS,
+ HAPD_IFACE_ENABLED
+ } state;
+
+#ifdef CONFIG_MESH
+ struct mesh_conf *mconf;
+#endif /* CONFIG_MESH */
+
+ size_t num_bss;
+ struct hostapd_data **bss;
+
+ unsigned int wait_channel_update:1;
+ unsigned int cac_started:1;
+#ifdef CONFIG_FST
+ struct fst_iface *fst;
+ const struct wpabuf *fst_ies;
+#endif /* CONFIG_FST */
+
+ /*
+ * When set, indicates that the driver will handle the AP
+ * teardown: delete global keys, station keys, and stations.
+ */
+ unsigned int driver_ap_teardown:1;
+
+ int num_ap; /* number of entries in ap_list */
+ struct ap_info *ap_list; /* AP info list head */
+ struct ap_info *ap_hash[STA_HASH_SIZE];
+
+ u64 drv_flags;
+
+ /* SMPS modes supported by the driver (WPA_DRIVER_SMPS_MODE_*) */
+ unsigned int smps_modes;
+
+ /*
+ * A bitmap of supported protocols for probe response offload. See
+ * struct wpa_driver_capa in driver.h
+ */
+ unsigned int probe_resp_offloads;
+
+ /* extended capabilities supported by the driver */
+ const u8 *extended_capa, *extended_capa_mask;
+ unsigned int extended_capa_len;
+
+ unsigned int drv_max_acl_mac_addrs;
+
+ struct hostapd_hw_modes *hw_features;
+ int num_hw_features;
+ struct hostapd_hw_modes *current_mode;
+ /* Rates that are currently used (i.e., filtered copy of
+ * current_mode->channels */
+ int num_rates;
+ struct hostapd_rate_data *current_rates;
+ int *basic_rates;
+ int freq;
+
+ u16 hw_flags;
+
+ /* Number of associated Non-ERP stations (i.e., stations using 802.11b
+ * in 802.11g BSS) */
+ int num_sta_non_erp;
+
+ /* Number of associated stations that do not support Short Slot Time */
+ int num_sta_no_short_slot_time;
+
+ /* Number of associated stations that do not support Short Preamble */
+ int num_sta_no_short_preamble;
+
+ int olbc; /* Overlapping Legacy BSS Condition */
+
+ /* Number of HT associated stations that do not support greenfield */
+ int num_sta_ht_no_gf;
+
+ /* Number of associated non-HT stations */
+ int num_sta_no_ht;
+
+ /* Number of HT associated stations 20 MHz */
+ int num_sta_ht_20mhz;
+
+ /* Number of HT40 intolerant stations */
+ int num_sta_ht40_intolerant;
+
+ /* Overlapping BSS information */
+ int olbc_ht;
+
+ u16 ht_op_mode;
+
+ /* surveying helpers */
+
+ /* number of channels surveyed */
+ unsigned int chans_surveyed;
+
+ /* lowest observed noise floor in dBm */
+ s8 lowest_nf;
+
+ /* channel utilization calculation */
+ u64 last_channel_time;
+ u64 last_channel_time_busy;
+ u8 channel_utilization;
+
+ unsigned int dfs_cac_ms;
+ struct os_reltime dfs_cac_start;
+
+ /* Latched with the actual secondary channel information and will be
+ * used while juggling between HT20 and HT40 modes. */
+ int secondary_ch;
+
+#ifdef CONFIG_ACS
+ unsigned int acs_num_completed_scans;
+#endif /* CONFIG_ACS */
+
+ void (*scan_cb)(struct hostapd_iface *iface);
+ int num_ht40_scan_tries;
+
+ struct dl_list sta_seen; /* struct hostapd_sta_info */
+ unsigned int num_sta_seen;
+};
+
+/* hostapd.c */
+int hostapd_for_each_interface(struct hapd_interfaces *interfaces,
+ int (*cb)(struct hostapd_iface *iface,
+ void *ctx), void *ctx);
+int hostapd_reload_config(struct hostapd_iface *iface);
+struct hostapd_data *
+hostapd_alloc_bss_data(struct hostapd_iface *hapd_iface,
+ struct hostapd_config *conf,
+ struct hostapd_bss_config *bss);
+int hostapd_setup_interface(struct hostapd_iface *iface);
+int hostapd_setup_interface_complete(struct hostapd_iface *iface, int err);
+void hostapd_interface_deinit(struct hostapd_iface *iface);
+void hostapd_interface_free(struct hostapd_iface *iface);
+struct hostapd_iface * hostapd_init(struct hapd_interfaces *interfaces,
+ const char *config_file);
+struct hostapd_iface *
+hostapd_interface_init_bss(struct hapd_interfaces *interfaces, const char *phy,
+ const char *config_fname, int debug);
+void hostapd_new_assoc_sta(struct hostapd_data *hapd, struct sta_info *sta,
+ int reassoc);
+void hostapd_interface_deinit_free(struct hostapd_iface *iface);
+int hostapd_enable_iface(struct hostapd_iface *hapd_iface);
+int hostapd_reload_iface(struct hostapd_iface *hapd_iface);
+int hostapd_disable_iface(struct hostapd_iface *hapd_iface);
+int hostapd_add_iface(struct hapd_interfaces *ifaces, char *buf);
+int hostapd_remove_iface(struct hapd_interfaces *ifaces, char *buf);
+void hostapd_channel_list_updated(struct hostapd_iface *iface, int initiator);
+void hostapd_set_state(struct hostapd_iface *iface, enum hostapd_iface_state s);
+const char * hostapd_state_text(enum hostapd_iface_state s);
+int hostapd_switch_channel(struct hostapd_data *hapd,
+ struct csa_settings *settings);
+void
+hostapd_switch_channel_fallback(struct hostapd_iface *iface,
+ const struct hostapd_freq_params *freq_params);
+void hostapd_cleanup_cs_params(struct hostapd_data *hapd);
+void hostapd_periodic_iface(struct hostapd_iface *iface);
+
+/* utils.c */
+int hostapd_register_probereq_cb(struct hostapd_data *hapd,
+ int (*cb)(void *ctx, const u8 *sa,
+ const u8 *da, const u8 *bssid,
+ const u8 *ie, size_t ie_len,
+ int ssi_signal),
+ void *ctx);
+void hostapd_prune_associations(struct hostapd_data *hapd, const u8 *addr);
+
+/* drv_callbacks.c (TODO: move to somewhere else?) */
+int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr,
+ const u8 *ie, size_t ielen, int reassoc);
+void hostapd_notif_disassoc(struct hostapd_data *hapd, const u8 *addr);
+void hostapd_event_sta_low_ack(struct hostapd_data *hapd, const u8 *addr);
+void hostapd_event_connect_failed_reason(struct hostapd_data *hapd,
+ const u8 *addr, int reason_code);
+int hostapd_probe_req_rx(struct hostapd_data *hapd, const u8 *sa, const u8 *da,
+ const u8 *bssid, const u8 *ie, size_t ie_len,
+ int ssi_signal);
+void hostapd_event_ch_switch(struct hostapd_data *hapd, int freq, int ht,
+ int offset, int width, int cf1, int cf2);
+
+const struct hostapd_eap_user *
+hostapd_get_eap_user(struct hostapd_data *hapd, const u8 *identity,
+ size_t identity_len, int phase2);
+
+struct hostapd_data * hostapd_get_iface(struct hapd_interfaces *interfaces,
+ const char *ifname);
+
+#ifdef CONFIG_FST
+void fst_hostapd_fill_iface_obj(struct hostapd_data *hapd,
+ struct fst_wpa_obj *iface_obj);
+#endif /* CONFIG_FST */
+
+#endif /* HOSTAPD_H */
diff --git a/freebsd/contrib/wpa/src/ap/hs20.c b/freebsd/contrib/wpa/src/ap/hs20.c
new file mode 100644
index 00000000..1a836666
--- /dev/null
+++ b/freebsd/contrib/wpa/src/ap/hs20.c
@@ -0,0 +1,179 @@
+#include <machine/rtems-bsd-user-space.h>
+
+/*
+ * Hotspot 2.0 AP ANQP processing
+ * Copyright (c) 2009, Atheros Communications, Inc.
+ * Copyright (c) 2011-2013, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "common/ieee802_11_defs.h"
+#include "hostapd.h"
+#include "ap_config.h"
+#include "ap_drv_ops.h"
+#include "hs20.h"
+
+
+u8 * hostapd_eid_hs20_indication(struct hostapd_data *hapd, u8 *eid)
+{
+ u8 conf;
+ if (!hapd->conf->hs20)
+ return eid;
+ *eid++ = WLAN_EID_VENDOR_SPECIFIC;
+ *eid++ = 7;
+ WPA_PUT_BE24(eid, OUI_WFA);
+ eid += 3;
+ *eid++ = HS20_INDICATION_OUI_TYPE;
+ conf = HS20_VERSION; /* Release Number */
+ conf |= HS20_ANQP_DOMAIN_ID_PRESENT;
+ if (hapd->conf->disable_dgaf)
+ conf |= HS20_DGAF_DISABLED;
+ *eid++ = conf;
+ WPA_PUT_LE16(eid, hapd->conf->anqp_domain_id);
+ eid += 2;
+
+ return eid;
+}
+
+
+u8 * hostapd_eid_osen(struct hostapd_data *hapd, u8 *eid)
+{
+ u8 *len;
+ u16 capab;
+
+ if (!hapd->conf->osen)
+ return eid;
+
+ *eid++ = WLAN_EID_VENDOR_SPECIFIC;
+ len = eid++; /* to be filled */
+ WPA_PUT_BE24(eid, OUI_WFA);
+ eid += 3;
+ *eid++ = HS20_OSEN_OUI_TYPE;
+
+ /* Group Data Cipher Suite */
+ RSN_SELECTOR_PUT(eid, RSN_CIPHER_SUITE_NO_GROUP_ADDRESSED);
+ eid += RSN_SELECTOR_LEN;
+
+ /* Pairwise Cipher Suite Count and List */
+ WPA_PUT_LE16(eid, 1);
+ eid += 2;
+ RSN_SELECTOR_PUT(eid, RSN_CIPHER_SUITE_CCMP);
+ eid += RSN_SELECTOR_LEN;
+
+ /* AKM Suite Count and List */
+ WPA_PUT_LE16(eid, 1);
+ eid += 2;
+ RSN_SELECTOR_PUT(eid, RSN_AUTH_KEY_MGMT_OSEN);
+ eid += RSN_SELECTOR_LEN;
+
+ /* RSN Capabilities */
+ capab = 0;
+ if (hapd->conf->wmm_enabled) {
+ /* 4 PTKSA replay counters when using WMM */
+ capab |= (RSN_NUM_REPLAY_COUNTERS_16 << 2);
+ }
+#ifdef CONFIG_IEEE80211W
+ if (hapd->conf->ieee80211w != NO_MGMT_FRAME_PROTECTION) {
+ capab |= WPA_CAPABILITY_MFPC;
+ if (hapd->conf->ieee80211w == MGMT_FRAME_PROTECTION_REQUIRED)
+ capab |= WPA_CAPABILITY_MFPR;
+ }
+#endif /* CONFIG_IEEE80211W */
+ WPA_PUT_LE16(eid, capab);
+ eid += 2;
+
+ *len = eid - len - 1;
+
+ return eid;
+}
+
+
+int hs20_send_wnm_notification(struct hostapd_data *hapd, const u8 *addr,
+ u8 osu_method, const char *url)
+{
+ struct wpabuf *buf;
+ size_t len = 0;
+ int ret;
+
+ /* TODO: should refuse to send notification if the STA is not associated
+ * or if the STA did not indicate support for WNM-Notification */
+
+ if (url) {
+ len = 1 + os_strlen(url);
+ if (5 + len > 255) {
+ wpa_printf(MSG_INFO, "HS 2.0: Too long URL for "
+ "WNM-Notification: '%s'", url);
+ return -1;
+ }
+ }
+
+ buf = wpabuf_alloc(4 + 7 + len);
+ if (buf == NULL)
+ return -1;
+
+ wpabuf_put_u8(buf, WLAN_ACTION_WNM);
+ wpabuf_put_u8(buf, WNM_NOTIFICATION_REQ);
+ wpabuf_put_u8(buf, 1); /* Dialog token */
+ wpabuf_put_u8(buf, 1); /* Type - 1 reserved for WFA */
+
+ /* Subscription Remediation subelement */
+ wpabuf_put_u8(buf, WLAN_EID_VENDOR_SPECIFIC);
+ wpabuf_put_u8(buf, 5 + len);
+ wpabuf_put_be24(buf, OUI_WFA);
+ wpabuf_put_u8(buf, HS20_WNM_SUB_REM_NEEDED);
+ if (url) {
+ wpabuf_put_u8(buf, len - 1);
+ wpabuf_put_data(buf, url, len - 1);
+ wpabuf_put_u8(buf, osu_method);
+ } else {
+ /* Server URL and Server Method fields not included */
+ wpabuf_put_u8(buf, 0);
+ }
+
+ ret = hostapd_drv_send_action(hapd, hapd->iface->freq, 0, addr,
+ wpabuf_head(buf), wpabuf_len(buf));
+
+ wpabuf_free(buf);
+
+ return ret;
+}
+
+
+int hs20_send_wnm_notification_deauth_req(struct hostapd_data *hapd,
+ const u8 *addr,
+ const struct wpabuf *payload)
+{
+ struct wpabuf *buf;
+ int ret;
+
+ /* TODO: should refuse to send notification if the STA is not associated
+ * or if the STA did not indicate support for WNM-Notification */
+
+ buf = wpabuf_alloc(4 + 6 + wpabuf_len(payload));
+ if (buf == NULL)
+ return -1;
+
+ wpabuf_put_u8(buf, WLAN_ACTION_WNM);
+ wpabuf_put_u8(buf, WNM_NOTIFICATION_REQ);
+ wpabuf_put_u8(buf, 1); /* Dialog token */
+ wpabuf_put_u8(buf, 1); /* Type - 1 reserved for WFA */
+
+ /* Deauthentication Imminent Notice subelement */
+ wpabuf_put_u8(buf, WLAN_EID_VENDOR_SPECIFIC);
+ wpabuf_put_u8(buf, 4 + wpabuf_len(payload));
+ wpabuf_put_be24(buf, OUI_WFA);
+ wpabuf_put_u8(buf, HS20_WNM_DEAUTH_IMMINENT_NOTICE);
+ wpabuf_put_buf(buf, payload);
+
+ ret = hostapd_drv_send_action(hapd, hapd->iface->freq, 0, addr,
+ wpabuf_head(buf), wpabuf_len(buf));
+
+ wpabuf_free(buf);
+
+ return ret;
+}
diff --git a/freebsd/contrib/wpa/src/ap/hs20.h b/freebsd/contrib/wpa/src/ap/hs20.h
new file mode 100644
index 00000000..152439f4
--- /dev/null
+++ b/freebsd/contrib/wpa/src/ap/hs20.h
@@ -0,0 +1,22 @@
+/*
+ * Hotspot 2.0 AP ANQP processing
+ * Copyright (c) 2011-2013, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef HS20_H
+#define HS20_H
+
+struct hostapd_data;
+
+u8 * hostapd_eid_hs20_indication(struct hostapd_data *hapd, u8 *eid);
+u8 * hostapd_eid_osen(struct hostapd_data *hapd, u8 *eid);
+int hs20_send_wnm_notification(struct hostapd_data *hapd, const u8 *addr,
+ u8 osu_method, const char *url);
+int hs20_send_wnm_notification_deauth_req(struct hostapd_data *hapd,
+ const u8 *addr,
+ const struct wpabuf *payload);
+
+#endif /* HS20_H */
diff --git a/freebsd/contrib/wpa/src/ap/ieee802_11.h b/freebsd/contrib/wpa/src/ap/ieee802_11.h
new file mode 100644
index 00000000..44c1bff3
--- /dev/null
+++ b/freebsd/contrib/wpa/src/ap/ieee802_11.h
@@ -0,0 +1,107 @@
+/*
+ * hostapd / IEEE 802.11 Management
+ * Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef IEEE802_11_H
+#define IEEE802_11_H
+
+struct hostapd_iface;
+struct hostapd_data;
+struct sta_info;
+struct hostapd_frame_info;
+struct ieee80211_ht_capabilities;
+struct ieee80211_vht_capabilities;
+struct ieee80211_mgmt;
+
+int ieee802_11_mgmt(struct hostapd_data *hapd, const u8 *buf, size_t len,
+ struct hostapd_frame_info *fi);
+void ieee802_11_mgmt_cb(struct hostapd_data *hapd, const u8 *buf, size_t len,
+ u16 stype, int ok);
+void hostapd_2040_coex_action(struct hostapd_data *hapd,
+ const struct ieee80211_mgmt *mgmt, size_t len);
+#ifdef NEED_AP_MLME
+int ieee802_11_get_mib(struct hostapd_data *hapd, char *buf, size_t buflen);
+int ieee802_11_get_mib_sta(struct hostapd_data *hapd, struct sta_info *sta,
+ char *buf, size_t buflen);
+#else /* NEED_AP_MLME */
+static inline int ieee802_11_get_mib(struct hostapd_data *hapd, char *buf,
+ size_t buflen)
+{
+ return 0;
+}
+
+static inline int ieee802_11_get_mib_sta(struct hostapd_data *hapd,
+ struct sta_info *sta,
+ char *buf, size_t buflen)
+{
+ return 0;
+}
+#endif /* NEED_AP_MLME */
+u16 hostapd_own_capab_info(struct hostapd_data *hapd);
+void ap_ht2040_timeout(void *eloop_data, void *user_data);
+u8 * hostapd_eid_ext_capab(struct hostapd_data *hapd, u8 *eid);
+u8 * hostapd_eid_qos_map_set(struct hostapd_data *hapd, u8 *eid);
+u8 * hostapd_eid_supp_rates(struct hostapd_data *hapd, u8 *eid);
+u8 * hostapd_eid_ext_supp_rates(struct hostapd_data *hapd, u8 *eid);
+u8 * hostapd_eid_ht_capabilities(struct hostapd_data *hapd, u8 *eid);
+u8 * hostapd_eid_ht_operation(struct hostapd_data *hapd, u8 *eid);
+u8 * hostapd_eid_vht_capabilities(struct hostapd_data *hapd, u8 *eid);
+u8 * hostapd_eid_vht_operation(struct hostapd_data *hapd, u8 *eid);
+u8 * hostapd_eid_vendor_vht(struct hostapd_data *hapd, u8 *eid);
+int hostapd_ht_operation_update(struct hostapd_iface *iface);
+void ieee802_11_send_sa_query_req(struct hostapd_data *hapd,
+ const u8 *addr, const u8 *trans_id);
+void hostapd_get_ht_capab(struct hostapd_data *hapd,
+ struct ieee80211_ht_capabilities *ht_cap,
+ struct ieee80211_ht_capabilities *neg_ht_cap);
+void hostapd_get_vht_capab(struct hostapd_data *hapd,
+ struct ieee80211_vht_capabilities *vht_cap,
+ struct ieee80211_vht_capabilities *neg_vht_cap);
+u16 copy_sta_ht_capab(struct hostapd_data *hapd, struct sta_info *sta,
+ const u8 *ht_capab);
+u16 copy_sta_vendor_vht(struct hostapd_data *hapd, struct sta_info *sta,
+ const u8 *ie, size_t len);
+
+void update_ht_state(struct hostapd_data *hapd, struct sta_info *sta);
+void ht40_intolerant_add(struct hostapd_iface *iface, struct sta_info *sta);
+void ht40_intolerant_remove(struct hostapd_iface *iface, struct sta_info *sta);
+u16 copy_sta_vht_capab(struct hostapd_data *hapd, struct sta_info *sta,
+ const u8 *vht_capab);
+u16 set_sta_vht_opmode(struct hostapd_data *hapd, struct sta_info *sta,
+ const u8 *vht_opmode);
+void hostapd_tx_status(struct hostapd_data *hapd, const u8 *addr,
+ const u8 *buf, size_t len, int ack);
+void hostapd_eapol_tx_status(struct hostapd_data *hapd, const u8 *dst,
+ const u8 *data, size_t len, int ack);
+void ieee802_11_rx_from_unknown(struct hostapd_data *hapd, const u8 *src,
+ int wds);
+u8 * hostapd_eid_assoc_comeback_time(struct hostapd_data *hapd,
+ struct sta_info *sta, u8 *eid);
+void ieee802_11_sa_query_action(struct hostapd_data *hapd,
+ const u8 *sa, const u8 action_type,
+ const u8 *trans_id);
+u8 * hostapd_eid_interworking(struct hostapd_data *hapd, u8 *eid);
+u8 * hostapd_eid_adv_proto(struct hostapd_data *hapd, u8 *eid);
+u8 * hostapd_eid_roaming_consortium(struct hostapd_data *hapd, u8 *eid);
+u8 * hostapd_eid_time_adv(struct hostapd_data *hapd, u8 *eid);
+u8 * hostapd_eid_time_zone(struct hostapd_data *hapd, u8 *eid);
+int hostapd_update_time_adv(struct hostapd_data *hapd);
+void hostapd_client_poll_ok(struct hostapd_data *hapd, const u8 *addr);
+u8 * hostapd_eid_bss_max_idle_period(struct hostapd_data *hapd, u8 *eid);
+
+int auth_sae_init_committed(struct hostapd_data *hapd, struct sta_info *sta);
+#ifdef CONFIG_SAE
+void sae_clear_retransmit_timer(struct hostapd_data *hapd,
+ struct sta_info *sta);
+#else /* CONFIG_SAE */
+static inline void sae_clear_retransmit_timer(struct hostapd_data *hapd,
+ struct sta_info *sta)
+{
+}
+#endif /* CONFIG_SAE */
+
+#endif /* IEEE802_11_H */
diff --git a/freebsd/contrib/wpa/src/ap/ieee802_11_auth.h b/freebsd/contrib/wpa/src/ap/ieee802_11_auth.h
new file mode 100644
index 00000000..b66f244b
--- /dev/null
+++ b/freebsd/contrib/wpa/src/ap/ieee802_11_auth.h
@@ -0,0 +1,29 @@
+/*
+ * hostapd / IEEE 802.11 authentication (ACL)
+ * Copyright (c) 2003-2005, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef IEEE802_11_AUTH_H
+#define IEEE802_11_AUTH_H
+
+enum {
+ HOSTAPD_ACL_REJECT = 0,
+ HOSTAPD_ACL_ACCEPT = 1,
+ HOSTAPD_ACL_PENDING = 2,
+ HOSTAPD_ACL_ACCEPT_TIMEOUT = 3
+};
+
+int hostapd_allowed_address(struct hostapd_data *hapd, const u8 *addr,
+ const u8 *msg, size_t len, u32 *session_timeout,
+ u32 *acct_interim_interval, int *vlan_id,
+ struct hostapd_sta_wpa_psk_short **psk,
+ char **identity, char **radius_cui);
+int hostapd_acl_init(struct hostapd_data *hapd);
+void hostapd_acl_deinit(struct hostapd_data *hapd);
+void hostapd_free_psk_list(struct hostapd_sta_wpa_psk_short *psk);
+void hostapd_acl_expire(struct hostapd_data *hapd);
+
+#endif /* IEEE802_11_AUTH_H */
diff --git a/freebsd/contrib/wpa/src/ap/ieee802_11_shared.c b/freebsd/contrib/wpa/src/ap/ieee802_11_shared.c
new file mode 100644
index 00000000..ec6deaf5
--- /dev/null
+++ b/freebsd/contrib/wpa/src/ap/ieee802_11_shared.c
@@ -0,0 +1,510 @@
+#include <machine/rtems-bsd-user-space.h>
+
+/*
+ * hostapd / IEEE 802.11 Management
+ * Copyright (c) 2002-2012, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+
+#include "utils/common.h"
+#include "common/ieee802_11_defs.h"
+#include "hostapd.h"
+#include "sta_info.h"
+#include "ap_config.h"
+#include "ap_drv_ops.h"
+#include "ieee802_11.h"
+
+
+#ifdef CONFIG_IEEE80211W
+
+u8 * hostapd_eid_assoc_comeback_time(struct hostapd_data *hapd,
+ struct sta_info *sta, u8 *eid)
+{
+ u8 *pos = eid;
+ u32 timeout, tu;
+ struct os_reltime now, passed;
+
+ *pos++ = WLAN_EID_TIMEOUT_INTERVAL;
+ *pos++ = 5;
+ *pos++ = WLAN_TIMEOUT_ASSOC_COMEBACK;
+ os_get_reltime(&now);
+ os_reltime_sub(&now, &sta->sa_query_start, &passed);
+ tu = (passed.sec * 1000000 + passed.usec) / 1024;
+ if (hapd->conf->assoc_sa_query_max_timeout > tu)
+ timeout = hapd->conf->assoc_sa_query_max_timeout - tu;
+ else
+ timeout = 0;
+ if (timeout < hapd->conf->assoc_sa_query_max_timeout)
+ timeout++; /* add some extra time for local timers */
+ WPA_PUT_LE32(pos, timeout);
+ pos += 4;
+
+ return pos;
+}
+
+
+/* MLME-SAQuery.request */
+void ieee802_11_send_sa_query_req(struct hostapd_data *hapd,
+ const u8 *addr, const u8 *trans_id)
+{
+ struct ieee80211_mgmt mgmt;
+ u8 *end;
+
+ wpa_printf(MSG_DEBUG, "IEEE 802.11: Sending SA Query Request to "
+ MACSTR, MAC2STR(addr));
+ wpa_hexdump(MSG_DEBUG, "IEEE 802.11: SA Query Transaction ID",
+ trans_id, WLAN_SA_QUERY_TR_ID_LEN);
+
+ os_memset(&mgmt, 0, sizeof(mgmt));
+ mgmt.frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
+ WLAN_FC_STYPE_ACTION);
+ os_memcpy(mgmt.da, addr, ETH_ALEN);
+ os_memcpy(mgmt.sa, hapd->own_addr, ETH_ALEN);
+ os_memcpy(mgmt.bssid, hapd->own_addr, ETH_ALEN);
+ mgmt.u.action.category = WLAN_ACTION_SA_QUERY;
+ mgmt.u.action.u.sa_query_req.action = WLAN_SA_QUERY_REQUEST;
+ os_memcpy(mgmt.u.action.u.sa_query_req.trans_id, trans_id,
+ WLAN_SA_QUERY_TR_ID_LEN);
+ end = mgmt.u.action.u.sa_query_req.trans_id + WLAN_SA_QUERY_TR_ID_LEN;
+ if (hostapd_drv_send_mlme(hapd, &mgmt, end - (u8 *) &mgmt, 0) < 0)
+ wpa_printf(MSG_INFO, "ieee802_11_send_sa_query_req: send failed");
+}
+
+
+static void ieee802_11_send_sa_query_resp(struct hostapd_data *hapd,
+ const u8 *sa, const u8 *trans_id)
+{
+ struct sta_info *sta;
+ struct ieee80211_mgmt resp;
+ u8 *end;
+
+ wpa_printf(MSG_DEBUG, "IEEE 802.11: Received SA Query Request from "
+ MACSTR, MAC2STR(sa));
+ wpa_hexdump(MSG_DEBUG, "IEEE 802.11: SA Query Transaction ID",
+ trans_id, WLAN_SA_QUERY_TR_ID_LEN);
+
+ sta = ap_get_sta(hapd, sa);
+ if (sta == NULL || !(sta->flags & WLAN_STA_ASSOC)) {
+ wpa_printf(MSG_DEBUG, "IEEE 802.11: Ignore SA Query Request "
+ "from unassociated STA " MACSTR, MAC2STR(sa));
+ return;
+ }
+
+ wpa_printf(MSG_DEBUG, "IEEE 802.11: Sending SA Query Response to "
+ MACSTR, MAC2STR(sa));
+
+ os_memset(&resp, 0, sizeof(resp));
+ resp.frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
+ WLAN_FC_STYPE_ACTION);
+ os_memcpy(resp.da, sa, ETH_ALEN);
+ os_memcpy(resp.sa, hapd->own_addr, ETH_ALEN);
+ os_memcpy(resp.bssid, hapd->own_addr, ETH_ALEN);
+ resp.u.action.category = WLAN_ACTION_SA_QUERY;
+ resp.u.action.u.sa_query_req.action = WLAN_SA_QUERY_RESPONSE;
+ os_memcpy(resp.u.action.u.sa_query_req.trans_id, trans_id,
+ WLAN_SA_QUERY_TR_ID_LEN);
+ end = resp.u.action.u.sa_query_req.trans_id + WLAN_SA_QUERY_TR_ID_LEN;
+ if (hostapd_drv_send_mlme(hapd, &resp, end - (u8 *) &resp, 0) < 0)
+ wpa_printf(MSG_INFO, "ieee80211_mgmt_sa_query_request: send failed");
+}
+
+
+void ieee802_11_sa_query_action(struct hostapd_data *hapd, const u8 *sa,
+ const u8 action_type, const u8 *trans_id)
+{
+ struct sta_info *sta;
+ int i;
+
+ if (action_type == WLAN_SA_QUERY_REQUEST) {
+ ieee802_11_send_sa_query_resp(hapd, sa, trans_id);
+ return;
+ }
+
+ if (action_type != WLAN_SA_QUERY_RESPONSE) {
+ wpa_printf(MSG_DEBUG, "IEEE 802.11: Unexpected SA Query "
+ "Action %d", action_type);
+ return;
+ }
+
+ wpa_printf(MSG_DEBUG, "IEEE 802.11: Received SA Query Response from "
+ MACSTR, MAC2STR(sa));
+ wpa_hexdump(MSG_DEBUG, "IEEE 802.11: SA Query Transaction ID",
+ trans_id, WLAN_SA_QUERY_TR_ID_LEN);
+
+ /* MLME-SAQuery.confirm */
+
+ sta = ap_get_sta(hapd, sa);
+ if (sta == NULL || sta->sa_query_trans_id == NULL) {
+ wpa_printf(MSG_DEBUG, "IEEE 802.11: No matching STA with "
+ "pending SA Query request found");
+ return;
+ }
+
+ for (i = 0; i < sta->sa_query_count; i++) {
+ if (os_memcmp(sta->sa_query_trans_id +
+ i * WLAN_SA_QUERY_TR_ID_LEN,
+ trans_id, WLAN_SA_QUERY_TR_ID_LEN) == 0)
+ break;
+ }
+
+ if (i >= sta->sa_query_count) {
+ wpa_printf(MSG_DEBUG, "IEEE 802.11: No matching SA Query "
+ "transaction identifier found");
+ return;
+ }
+
+ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_DEBUG,
+ "Reply to pending SA Query received");
+ ap_sta_stop_sa_query(hapd, sta);
+}
+
+#endif /* CONFIG_IEEE80211W */
+
+
+static void hostapd_ext_capab_byte(struct hostapd_data *hapd, u8 *pos, int idx)
+{
+ *pos = 0x00;
+
+ switch (idx) {
+ case 0: /* Bits 0-7 */
+ if (hapd->iconf->obss_interval)
+ *pos |= 0x01; /* Bit 0 - Coexistence management */
+ break;
+ case 1: /* Bits 8-15 */
+ if (hapd->conf->proxy_arp)
+ *pos |= 0x10; /* Bit 12 - Proxy ARP */
+ break;
+ case 2: /* Bits 16-23 */
+ if (hapd->conf->wnm_sleep_mode)
+ *pos |= 0x02; /* Bit 17 - WNM-Sleep Mode */
+ if (hapd->conf->bss_transition)
+ *pos |= 0x08; /* Bit 19 - BSS Transition */
+ break;
+ case 3: /* Bits 24-31 */
+#ifdef CONFIG_WNM
+ *pos |= 0x02; /* Bit 25 - SSID List */
+#endif /* CONFIG_WNM */
+ if (hapd->conf->time_advertisement == 2)
+ *pos |= 0x08; /* Bit 27 - UTC TSF Offset */
+ if (hapd->conf->interworking)
+ *pos |= 0x80; /* Bit 31 - Interworking */
+ break;
+ case 4: /* Bits 32-39 */
+ if (hapd->conf->qos_map_set_len)
+ *pos |= 0x01; /* Bit 32 - QoS Map */
+ if (hapd->conf->tdls & TDLS_PROHIBIT)
+ *pos |= 0x40; /* Bit 38 - TDLS Prohibited */
+ if (hapd->conf->tdls & TDLS_PROHIBIT_CHAN_SWITCH) {
+ /* Bit 39 - TDLS Channel Switching Prohibited */
+ *pos |= 0x80;
+ }
+ break;
+ case 5: /* Bits 40-47 */
+#ifdef CONFIG_HS20
+ if (hapd->conf->hs20)
+ *pos |= 0x40; /* Bit 46 - WNM-Notification */
+#endif /* CONFIG_HS20 */
+ break;
+ case 6: /* Bits 48-55 */
+ if (hapd->conf->ssid.utf8_ssid)
+ *pos |= 0x01; /* Bit 48 - UTF-8 SSID */
+ break;
+ }
+}
+
+
+u8 * hostapd_eid_ext_capab(struct hostapd_data *hapd, u8 *eid)
+{
+ u8 *pos = eid;
+ u8 len = 0, i;
+
+ if (hapd->conf->tdls & (TDLS_PROHIBIT | TDLS_PROHIBIT_CHAN_SWITCH))
+ len = 5;
+ if (len < 4 && hapd->conf->interworking)
+ len = 4;
+ if (len < 3 && hapd->conf->wnm_sleep_mode)
+ len = 3;
+ if (len < 1 && hapd->iconf->obss_interval)
+ len = 1;
+ if (len < 7 && hapd->conf->ssid.utf8_ssid)
+ len = 7;
+#ifdef CONFIG_WNM
+ if (len < 4)
+ len = 4;
+#endif /* CONFIG_WNM */
+#ifdef CONFIG_HS20
+ if (hapd->conf->hs20 && len < 6)
+ len = 6;
+#endif /* CONFIG_HS20 */
+ if (len < hapd->iface->extended_capa_len)
+ len = hapd->iface->extended_capa_len;
+ if (len == 0)
+ return eid;
+
+ *pos++ = WLAN_EID_EXT_CAPAB;
+ *pos++ = len;
+ for (i = 0; i < len; i++, pos++) {
+ hostapd_ext_capab_byte(hapd, pos, i);
+
+ if (i < hapd->iface->extended_capa_len) {
+ *pos &= ~hapd->iface->extended_capa_mask[i];
+ *pos |= hapd->iface->extended_capa[i];
+ }
+ }
+
+ while (len > 0 && eid[1 + len] == 0) {
+ len--;
+ eid[1] = len;
+ }
+ if (len == 0)
+ return eid;
+
+ return eid + 2 + len;
+}
+
+
+u8 * hostapd_eid_qos_map_set(struct hostapd_data *hapd, u8 *eid)
+{
+ u8 *pos = eid;
+ u8 len = hapd->conf->qos_map_set_len;
+
+ if (!len)
+ return eid;
+
+ *pos++ = WLAN_EID_QOS_MAP_SET;
+ *pos++ = len;
+ os_memcpy(pos, hapd->conf->qos_map_set, len);
+ pos += len;
+
+ return pos;
+}
+
+
+u8 * hostapd_eid_interworking(struct hostapd_data *hapd, u8 *eid)
+{
+ u8 *pos = eid;
+#ifdef CONFIG_INTERWORKING
+ u8 *len;
+
+ if (!hapd->conf->interworking)
+ return eid;
+
+ *pos++ = WLAN_EID_INTERWORKING;
+ len = pos++;
+
+ *pos = hapd->conf->access_network_type;
+ if (hapd->conf->internet)
+ *pos |= INTERWORKING_ANO_INTERNET;
+ if (hapd->conf->asra)
+ *pos |= INTERWORKING_ANO_ASRA;
+ if (hapd->conf->esr)
+ *pos |= INTERWORKING_ANO_ESR;
+ if (hapd->conf->uesa)
+ *pos |= INTERWORKING_ANO_UESA;
+ pos++;
+
+ if (hapd->conf->venue_info_set) {
+ *pos++ = hapd->conf->venue_group;
+ *pos++ = hapd->conf->venue_type;
+ }
+
+ if (!is_zero_ether_addr(hapd->conf->hessid)) {
+ os_memcpy(pos, hapd->conf->hessid, ETH_ALEN);
+ pos += ETH_ALEN;
+ }
+
+ *len = pos - len - 1;
+#endif /* CONFIG_INTERWORKING */
+
+ return pos;
+}
+
+
+u8 * hostapd_eid_adv_proto(struct hostapd_data *hapd, u8 *eid)
+{
+ u8 *pos = eid;
+#ifdef CONFIG_INTERWORKING
+
+ /* TODO: Separate configuration for ANQP? */
+ if (!hapd->conf->interworking)
+ return eid;
+
+ *pos++ = WLAN_EID_ADV_PROTO;
+ *pos++ = 2;
+ *pos++ = 0x7F; /* Query Response Length Limit | PAME-BI */
+ *pos++ = ACCESS_NETWORK_QUERY_PROTOCOL;
+#endif /* CONFIG_INTERWORKING */
+
+ return pos;
+}
+
+
+u8 * hostapd_eid_roaming_consortium(struct hostapd_data *hapd, u8 *eid)
+{
+ u8 *pos = eid;
+#ifdef CONFIG_INTERWORKING
+ u8 *len;
+ unsigned int i, count;
+
+ if (!hapd->conf->interworking ||
+ hapd->conf->roaming_consortium == NULL ||
+ hapd->conf->roaming_consortium_count == 0)
+ return eid;
+
+ *pos++ = WLAN_EID_ROAMING_CONSORTIUM;
+ len = pos++;
+
+ /* Number of ANQP OIs (in addition to the max 3 listed here) */
+ if (hapd->conf->roaming_consortium_count > 3 + 255)
+ *pos++ = 255;
+ else if (hapd->conf->roaming_consortium_count > 3)
+ *pos++ = hapd->conf->roaming_consortium_count - 3;
+ else
+ *pos++ = 0;
+
+ /* OU #1 and #2 Lengths */
+ *pos = hapd->conf->roaming_consortium[0].len;
+ if (hapd->conf->roaming_consortium_count > 1)
+ *pos |= hapd->conf->roaming_consortium[1].len << 4;
+ pos++;
+
+ if (hapd->conf->roaming_consortium_count > 3)
+ count = 3;
+ else
+ count = hapd->conf->roaming_consortium_count;
+
+ for (i = 0; i < count; i++) {
+ os_memcpy(pos, hapd->conf->roaming_consortium[i].oi,
+ hapd->conf->roaming_consortium[i].len);
+ pos += hapd->conf->roaming_consortium[i].len;
+ }
+
+ *len = pos - len - 1;
+#endif /* CONFIG_INTERWORKING */
+
+ return pos;
+}
+
+
+u8 * hostapd_eid_time_adv(struct hostapd_data *hapd, u8 *eid)
+{
+ if (hapd->conf->time_advertisement != 2)
+ return eid;
+
+ if (hapd->time_adv == NULL &&
+ hostapd_update_time_adv(hapd) < 0)
+ return eid;
+
+ if (hapd->time_adv == NULL)
+ return eid;
+
+ os_memcpy(eid, wpabuf_head(hapd->time_adv),
+ wpabuf_len(hapd->time_adv));
+ eid += wpabuf_len(hapd->time_adv);
+
+ return eid;
+}
+
+
+u8 * hostapd_eid_time_zone(struct hostapd_data *hapd, u8 *eid)
+{
+ size_t len;
+
+ if (hapd->conf->time_advertisement != 2)
+ return eid;
+
+ len = os_strlen(hapd->conf->time_zone);
+
+ *eid++ = WLAN_EID_TIME_ZONE;
+ *eid++ = len;
+ os_memcpy(eid, hapd->conf->time_zone, len);
+ eid += len;
+
+ return eid;
+}
+
+
+int hostapd_update_time_adv(struct hostapd_data *hapd)
+{
+ const int elen = 2 + 1 + 10 + 5 + 1;
+ struct os_time t;
+ struct os_tm tm;
+ u8 *pos;
+
+ if (hapd->conf->time_advertisement != 2)
+ return 0;
+
+ if (os_get_time(&t) < 0 || os_gmtime(t.sec, &tm) < 0)
+ return -1;
+
+ if (!hapd->time_adv) {
+ hapd->time_adv = wpabuf_alloc(elen);
+ if (hapd->time_adv == NULL)
+ return -1;
+ pos = wpabuf_put(hapd->time_adv, elen);
+ } else
+ pos = wpabuf_mhead_u8(hapd->time_adv);
+
+ *pos++ = WLAN_EID_TIME_ADVERTISEMENT;
+ *pos++ = 1 + 10 + 5 + 1;
+
+ *pos++ = 2; /* UTC time at which the TSF timer is 0 */
+
+ /* Time Value at TSF 0 */
+ /* FIX: need to calculate this based on the current TSF value */
+ WPA_PUT_LE16(pos, tm.year); /* Year */
+ pos += 2;
+ *pos++ = tm.month; /* Month */
+ *pos++ = tm.day; /* Day of month */
+ *pos++ = tm.hour; /* Hours */
+ *pos++ = tm.min; /* Minutes */
+ *pos++ = tm.sec; /* Seconds */
+ WPA_PUT_LE16(pos, 0); /* Milliseconds (not used) */
+ pos += 2;
+ *pos++ = 0; /* Reserved */
+
+ /* Time Error */
+ /* TODO: fill in an estimate on the error */
+ *pos++ = 0;
+ *pos++ = 0;
+ *pos++ = 0;
+ *pos++ = 0;
+ *pos++ = 0;
+
+ *pos++ = hapd->time_update_counter++;
+
+ return 0;
+}
+
+
+u8 * hostapd_eid_bss_max_idle_period(struct hostapd_data *hapd, u8 *eid)
+{
+ u8 *pos = eid;
+
+#ifdef CONFIG_WNM
+ if (hapd->conf->ap_max_inactivity > 0) {
+ unsigned int val;
+ *pos++ = WLAN_EID_BSS_MAX_IDLE_PERIOD;
+ *pos++ = 3;
+ val = hapd->conf->ap_max_inactivity;
+ if (val > 68000)
+ val = 68000;
+ val *= 1000;
+ val /= 1024;
+ if (val == 0)
+ val = 1;
+ if (val > 65535)
+ val = 65535;
+ WPA_PUT_LE16(pos, val);
+ pos += 2;
+ *pos++ = 0x00; /* TODO: Protected Keep-Alive Required */
+ }
+#endif /* CONFIG_WNM */
+
+ return pos;
+}
diff --git a/freebsd/contrib/wpa/src/ap/p2p_hostapd.h b/freebsd/contrib/wpa/src/ap/p2p_hostapd.h
new file mode 100644
index 00000000..0e3921c6
--- /dev/null
+++ b/freebsd/contrib/wpa/src/ap/p2p_hostapd.h
@@ -0,0 +1,35 @@
+/*
+ * hostapd / P2P integration
+ * Copyright (c) 2009-2010, Atheros Communications
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef P2P_HOSTAPD_H
+#define P2P_HOSTAPD_H
+
+#ifdef CONFIG_P2P
+
+int hostapd_p2p_get_mib_sta(struct hostapd_data *hapd, struct sta_info *sta,
+ char *buf, size_t buflen);
+int hostapd_p2p_set_noa(struct hostapd_data *hapd, u8 count, int start,
+ int duration);
+void hostapd_p2p_non_p2p_sta_connected(struct hostapd_data *hapd);
+void hostapd_p2p_non_p2p_sta_disconnected(struct hostapd_data *hapd);
+
+
+#else /* CONFIG_P2P */
+
+static inline int hostapd_p2p_get_mib_sta(struct hostapd_data *hapd,
+ struct sta_info *sta,
+ char *buf, size_t buflen)
+{
+ return 0;
+}
+
+#endif /* CONFIG_P2P */
+
+u8 * hostapd_eid_p2p_manage(struct hostapd_data *hapd, u8 *eid);
+
+#endif /* P2P_HOSTAPD_H */
diff --git a/freebsd/contrib/wpa/src/ap/sta_info.h b/freebsd/contrib/wpa/src/ap/sta_info.h
new file mode 100644
index 00000000..420d64e5
--- /dev/null
+++ b/freebsd/contrib/wpa/src/ap/sta_info.h
@@ -0,0 +1,241 @@
+/*
+ * hostapd / Station table
+ * Copyright (c) 2002-2011, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef STA_INFO_H
+#define STA_INFO_H
+
+#ifdef CONFIG_MESH
+/* needed for mesh_plink_state enum */
+#include "common/defs.h"
+#endif /* CONFIG_MESH */
+
+#include "list.h"
+
+/* STA flags */
+#define WLAN_STA_AUTH BIT(0)
+#define WLAN_STA_ASSOC BIT(1)
+#define WLAN_STA_AUTHORIZED BIT(5)
+#define WLAN_STA_PENDING_POLL BIT(6) /* pending activity poll not ACKed */
+#define WLAN_STA_SHORT_PREAMBLE BIT(7)
+#define WLAN_STA_PREAUTH BIT(8)
+#define WLAN_STA_WMM BIT(9)
+#define WLAN_STA_MFP BIT(10)
+#define WLAN_STA_HT BIT(11)
+#define WLAN_STA_WPS BIT(12)
+#define WLAN_STA_MAYBE_WPS BIT(13)
+#define WLAN_STA_WDS BIT(14)
+#define WLAN_STA_ASSOC_REQ_OK BIT(15)
+#define WLAN_STA_WPS2 BIT(16)
+#define WLAN_STA_GAS BIT(17)
+#define WLAN_STA_VHT BIT(18)
+#define WLAN_STA_WNM_SLEEP_MODE BIT(19)
+#define WLAN_STA_VHT_OPMODE_ENABLED BIT(20)
+#define WLAN_STA_VENDOR_VHT BIT(21)
+#define WLAN_STA_PENDING_DISASSOC_CB BIT(29)
+#define WLAN_STA_PENDING_DEAUTH_CB BIT(30)
+#define WLAN_STA_NONERP BIT(31)
+
+/* Maximum number of supported rates (from both Supported Rates and Extended
+ * Supported Rates IEs). */
+#define WLAN_SUPP_RATES_MAX 32
+
+
+struct sta_info {
+ struct sta_info *next; /* next entry in sta list */
+ struct sta_info *hnext; /* next entry in hash table list */
+ u8 addr[6];
+ be32 ipaddr;
+ struct dl_list ip6addr; /* list head for struct ip6addr */
+ u16 aid; /* STA's unique AID (1 .. 2007) or 0 if not yet assigned */
+ u32 flags; /* Bitfield of WLAN_STA_* */
+ u16 capability;
+ u16 listen_interval; /* or beacon_int for APs */
+ u8 supported_rates[WLAN_SUPP_RATES_MAX];
+ int supported_rates_len;
+ u8 qosinfo; /* Valid when WLAN_STA_WMM is set */
+
+#ifdef CONFIG_MESH
+ enum mesh_plink_state plink_state;
+ u16 peer_lid;
+ u16 my_lid;
+ u16 mpm_close_reason;
+ int mpm_retries;
+ u8 my_nonce[32];
+ u8 peer_nonce[32];
+ u8 aek[32]; /* SHA256 digest length */
+ u8 mtk[16];
+ u8 mgtk[16];
+ u8 sae_auth_retry;
+#endif /* CONFIG_MESH */
+
+ unsigned int nonerp_set:1;
+ unsigned int no_short_slot_time_set:1;
+ unsigned int no_short_preamble_set:1;
+ unsigned int no_ht_gf_set:1;
+ unsigned int no_ht_set:1;
+ unsigned int ht40_intolerant_set:1;
+ unsigned int ht_20mhz_set:1;
+ unsigned int no_p2p_set:1;
+ unsigned int qos_map_enabled:1;
+ unsigned int remediation:1;
+ unsigned int hs20_deauth_requested:1;
+ unsigned int session_timeout_set:1;
+ unsigned int radius_das_match:1;
+
+ u16 auth_alg;
+
+ enum {
+ STA_NULLFUNC = 0, STA_DISASSOC, STA_DEAUTH, STA_REMOVE,
+ STA_DISASSOC_FROM_CLI
+ } timeout_next;
+
+ u16 deauth_reason;
+ u16 disassoc_reason;
+
+ /* IEEE 802.1X related data */
+ struct eapol_state_machine *eapol_sm;
+
+ u32 acct_session_id_hi;
+ u32 acct_session_id_lo;
+ struct os_reltime acct_session_start;
+ int acct_session_started;
+ int acct_terminate_cause; /* Acct-Terminate-Cause */
+ int acct_interim_interval; /* Acct-Interim-Interval */
+
+ unsigned long last_rx_bytes;
+ unsigned long last_tx_bytes;
+ u32 acct_input_gigawords; /* Acct-Input-Gigawords */
+ u32 acct_output_gigawords; /* Acct-Output-Gigawords */
+
+ u8 *challenge; /* IEEE 802.11 Shared Key Authentication Challenge */
+
+ struct wpa_state_machine *wpa_sm;
+ struct rsn_preauth_interface *preauth_iface;
+
+ int vlan_id; /* 0: none, >0: VID */
+ int vlan_id_bound; /* updated by ap_sta_bind_vlan() */
+ /* PSKs from RADIUS authentication server */
+ struct hostapd_sta_wpa_psk_short *psk;
+
+ char *identity; /* User-Name from RADIUS */
+ char *radius_cui; /* Chargeable-User-Identity from RADIUS */
+
+ struct ieee80211_ht_capabilities *ht_capabilities;
+ struct ieee80211_vht_capabilities *vht_capabilities;
+ u8 vht_opmode;
+
+#ifdef CONFIG_IEEE80211W
+ int sa_query_count; /* number of pending SA Query requests;
+ * 0 = no SA Query in progress */
+ int sa_query_timed_out;
+ u8 *sa_query_trans_id; /* buffer of WLAN_SA_QUERY_TR_ID_LEN *
+ * sa_query_count octets of pending SA Query
+ * transaction identifiers */
+ struct os_reltime sa_query_start;
+#endif /* CONFIG_IEEE80211W */
+
+#ifdef CONFIG_INTERWORKING
+#define GAS_DIALOG_MAX 8 /* Max concurrent dialog number */
+ struct gas_dialog_info *gas_dialog;
+ u8 gas_dialog_next;
+#endif /* CONFIG_INTERWORKING */
+
+ struct wpabuf *wps_ie; /* WPS IE from (Re)Association Request */
+ struct wpabuf *p2p_ie; /* P2P IE from (Re)Association Request */
+ struct wpabuf *hs20_ie; /* HS 2.0 IE from (Re)Association Request */
+ u8 remediation_method;
+ char *remediation_url; /* HS 2.0 Subscription Remediation Server URL */
+ struct wpabuf *hs20_deauth_req;
+ char *hs20_session_info_url;
+ int hs20_disassoc_timer;
+#ifdef CONFIG_FST
+ struct wpabuf *mb_ies; /* MB IEs from (Re)Association Request */
+#endif /* CONFIG_FST */
+
+ struct os_reltime connected_time;
+
+#ifdef CONFIG_SAE
+ struct sae_data *sae;
+#endif /* CONFIG_SAE */
+
+ u32 session_timeout; /* valid only if session_timeout_set == 1 */
+
+ /* Last Authentication/(Re)Association Request/Action frame sequence
+ * control */
+ u16 last_seq_ctrl;
+ /* Last Authentication/(Re)Association Request/Action frame subtype */
+ u8 last_subtype;
+};
+
+
+/* Default value for maximum station inactivity. After AP_MAX_INACTIVITY has
+ * passed since last received frame from the station, a nullfunc data frame is
+ * sent to the station. If this frame is not acknowledged and no other frames
+ * have been received, the station will be disassociated after
+ * AP_DISASSOC_DELAY seconds. Similarly, the station will be deauthenticated
+ * after AP_DEAUTH_DELAY seconds has passed after disassociation. */
+#define AP_MAX_INACTIVITY (5 * 60)
+#define AP_DISASSOC_DELAY (1)
+#define AP_DEAUTH_DELAY (1)
+/* Number of seconds to keep STA entry with Authenticated flag after it has
+ * been disassociated. */
+#define AP_MAX_INACTIVITY_AFTER_DISASSOC (1 * 30)
+/* Number of seconds to keep STA entry after it has been deauthenticated. */
+#define AP_MAX_INACTIVITY_AFTER_DEAUTH (1 * 5)
+
+
+struct hostapd_data;
+
+int ap_for_each_sta(struct hostapd_data *hapd,
+ int (*cb)(struct hostapd_data *hapd, struct sta_info *sta,
+ void *ctx),
+ void *ctx);
+struct sta_info * ap_get_sta(struct hostapd_data *hapd, const u8 *sta);
+struct sta_info * ap_get_sta_p2p(struct hostapd_data *hapd, const u8 *addr);
+void ap_sta_hash_add(struct hostapd_data *hapd, struct sta_info *sta);
+void ap_free_sta(struct hostapd_data *hapd, struct sta_info *sta);
+void ap_sta_ip6addr_del(struct hostapd_data *hapd, struct sta_info *sta);
+void hostapd_free_stas(struct hostapd_data *hapd);
+void ap_handle_timer(void *eloop_ctx, void *timeout_ctx);
+void ap_sta_replenish_timeout(struct hostapd_data *hapd, struct sta_info *sta,
+ u32 session_timeout);
+void ap_sta_session_timeout(struct hostapd_data *hapd, struct sta_info *sta,
+ u32 session_timeout);
+void ap_sta_no_session_timeout(struct hostapd_data *hapd,
+ struct sta_info *sta);
+void ap_sta_session_warning_timeout(struct hostapd_data *hapd,
+ struct sta_info *sta, int warning_time);
+struct sta_info * ap_sta_add(struct hostapd_data *hapd, const u8 *addr);
+void ap_sta_disassociate(struct hostapd_data *hapd, struct sta_info *sta,
+ u16 reason);
+void ap_sta_deauthenticate(struct hostapd_data *hapd, struct sta_info *sta,
+ u16 reason);
+#ifdef CONFIG_WPS
+int ap_sta_wps_cancel(struct hostapd_data *hapd,
+ struct sta_info *sta, void *ctx);
+#endif /* CONFIG_WPS */
+int ap_sta_bind_vlan(struct hostapd_data *hapd, struct sta_info *sta);
+void ap_sta_start_sa_query(struct hostapd_data *hapd, struct sta_info *sta);
+void ap_sta_stop_sa_query(struct hostapd_data *hapd, struct sta_info *sta);
+int ap_check_sa_query_timeout(struct hostapd_data *hapd, struct sta_info *sta);
+void ap_sta_disconnect(struct hostapd_data *hapd, struct sta_info *sta,
+ const u8 *addr, u16 reason);
+
+void ap_sta_set_authorized(struct hostapd_data *hapd,
+ struct sta_info *sta, int authorized);
+static inline int ap_sta_is_authorized(struct sta_info *sta)
+{
+ return sta->flags & WLAN_STA_AUTHORIZED;
+}
+
+void ap_sta_deauth_cb(struct hostapd_data *hapd, struct sta_info *sta);
+void ap_sta_disassoc_cb(struct hostapd_data *hapd, struct sta_info *sta);
+
+int ap_sta_flags_txt(u32 flags, char *buf, size_t buflen);
+
+#endif /* STA_INFO_H */
diff --git a/freebsd/contrib/wpa/src/common/defs.h b/freebsd/contrib/wpa/src/common/defs.h
new file mode 100644
index 00000000..6aea3751
--- /dev/null
+++ b/freebsd/contrib/wpa/src/common/defs.h
@@ -0,0 +1,337 @@
+/*
+ * WPA Supplicant - Common definitions
+ * Copyright (c) 2004-2015, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef DEFS_H
+#define DEFS_H
+
+#ifdef FALSE
+#undef FALSE
+#endif
+#ifdef TRUE
+#undef TRUE
+#endif
+typedef enum { FALSE = 0, TRUE = 1 } Boolean;
+
+
+#define WPA_CIPHER_NONE BIT(0)
+#define WPA_CIPHER_WEP40 BIT(1)
+#define WPA_CIPHER_WEP104 BIT(2)
+#define WPA_CIPHER_TKIP BIT(3)
+#define WPA_CIPHER_CCMP BIT(4)
+#define WPA_CIPHER_AES_128_CMAC BIT(5)
+#define WPA_CIPHER_GCMP BIT(6)
+#define WPA_CIPHER_SMS4 BIT(7)
+#define WPA_CIPHER_GCMP_256 BIT(8)
+#define WPA_CIPHER_CCMP_256 BIT(9)
+#define WPA_CIPHER_BIP_GMAC_128 BIT(11)
+#define WPA_CIPHER_BIP_GMAC_256 BIT(12)
+#define WPA_CIPHER_BIP_CMAC_256 BIT(13)
+#define WPA_CIPHER_GTK_NOT_USED BIT(14)
+
+#define WPA_KEY_MGMT_IEEE8021X BIT(0)
+#define WPA_KEY_MGMT_PSK BIT(1)
+#define WPA_KEY_MGMT_NONE BIT(2)
+#define WPA_KEY_MGMT_IEEE8021X_NO_WPA BIT(3)
+#define WPA_KEY_MGMT_WPA_NONE BIT(4)
+#define WPA_KEY_MGMT_FT_IEEE8021X BIT(5)
+#define WPA_KEY_MGMT_FT_PSK BIT(6)
+#define WPA_KEY_MGMT_IEEE8021X_SHA256 BIT(7)
+#define WPA_KEY_MGMT_PSK_SHA256 BIT(8)
+#define WPA_KEY_MGMT_WPS BIT(9)
+#define WPA_KEY_MGMT_SAE BIT(10)
+#define WPA_KEY_MGMT_FT_SAE BIT(11)
+#define WPA_KEY_MGMT_WAPI_PSK BIT(12)
+#define WPA_KEY_MGMT_WAPI_CERT BIT(13)
+#define WPA_KEY_MGMT_CCKM BIT(14)
+#define WPA_KEY_MGMT_OSEN BIT(15)
+#define WPA_KEY_MGMT_IEEE8021X_SUITE_B BIT(16)
+#define WPA_KEY_MGMT_IEEE8021X_SUITE_B_192 BIT(17)
+
+static inline int wpa_key_mgmt_wpa_ieee8021x(int akm)
+{
+ return !!(akm & (WPA_KEY_MGMT_IEEE8021X |
+ WPA_KEY_MGMT_FT_IEEE8021X |
+ WPA_KEY_MGMT_CCKM |
+ WPA_KEY_MGMT_OSEN |
+ WPA_KEY_MGMT_IEEE8021X_SHA256 |
+ WPA_KEY_MGMT_IEEE8021X_SUITE_B |
+ WPA_KEY_MGMT_IEEE8021X_SUITE_B_192));
+}
+
+static inline int wpa_key_mgmt_wpa_psk(int akm)
+{
+ return !!(akm & (WPA_KEY_MGMT_PSK |
+ WPA_KEY_MGMT_FT_PSK |
+ WPA_KEY_MGMT_PSK_SHA256 |
+ WPA_KEY_MGMT_SAE |
+ WPA_KEY_MGMT_FT_SAE));
+}
+
+static inline int wpa_key_mgmt_ft(int akm)
+{
+ return !!(akm & (WPA_KEY_MGMT_FT_PSK |
+ WPA_KEY_MGMT_FT_IEEE8021X |
+ WPA_KEY_MGMT_FT_SAE));
+}
+
+static inline int wpa_key_mgmt_sae(int akm)
+{
+ return !!(akm & (WPA_KEY_MGMT_SAE |
+ WPA_KEY_MGMT_FT_SAE));
+}
+
+static inline int wpa_key_mgmt_sha256(int akm)
+{
+ return !!(akm & (WPA_KEY_MGMT_PSK_SHA256 |
+ WPA_KEY_MGMT_IEEE8021X_SHA256 |
+ WPA_KEY_MGMT_OSEN |
+ WPA_KEY_MGMT_IEEE8021X_SUITE_B));
+}
+
+static inline int wpa_key_mgmt_sha384(int akm)
+{
+ return !!(akm & WPA_KEY_MGMT_IEEE8021X_SUITE_B_192);
+}
+
+static inline int wpa_key_mgmt_suite_b(int akm)
+{
+ return !!(akm & (WPA_KEY_MGMT_IEEE8021X_SUITE_B |
+ WPA_KEY_MGMT_IEEE8021X_SUITE_B_192));
+}
+
+static inline int wpa_key_mgmt_wpa(int akm)
+{
+ return wpa_key_mgmt_wpa_ieee8021x(akm) ||
+ wpa_key_mgmt_wpa_psk(akm) ||
+ wpa_key_mgmt_sae(akm);
+}
+
+static inline int wpa_key_mgmt_wpa_any(int akm)
+{
+ return wpa_key_mgmt_wpa(akm) || (akm & WPA_KEY_MGMT_WPA_NONE);
+}
+
+static inline int wpa_key_mgmt_cckm(int akm)
+{
+ return akm == WPA_KEY_MGMT_CCKM;
+}
+
+
+#define WPA_PROTO_WPA BIT(0)
+#define WPA_PROTO_RSN BIT(1)
+#define WPA_PROTO_WAPI BIT(2)
+#define WPA_PROTO_OSEN BIT(3)
+
+#define WPA_AUTH_ALG_OPEN BIT(0)
+#define WPA_AUTH_ALG_SHARED BIT(1)
+#define WPA_AUTH_ALG_LEAP BIT(2)
+#define WPA_AUTH_ALG_FT BIT(3)
+#define WPA_AUTH_ALG_SAE BIT(4)
+
+
+enum wpa_alg {
+ WPA_ALG_NONE,
+ WPA_ALG_WEP,
+ WPA_ALG_TKIP,
+ WPA_ALG_CCMP,
+ WPA_ALG_IGTK,
+ WPA_ALG_PMK,
+ WPA_ALG_GCMP,
+ WPA_ALG_SMS4,
+ WPA_ALG_KRK,
+ WPA_ALG_GCMP_256,
+ WPA_ALG_CCMP_256,
+ WPA_ALG_BIP_GMAC_128,
+ WPA_ALG_BIP_GMAC_256,
+ WPA_ALG_BIP_CMAC_256
+};
+
+/**
+ * enum wpa_states - wpa_supplicant state
+ *
+ * These enumeration values are used to indicate the current wpa_supplicant
+ * state (wpa_s->wpa_state). The current state can be retrieved with
+ * wpa_supplicant_get_state() function and the state can be changed by calling
+ * wpa_supplicant_set_state(). In WPA state machine (wpa.c and preauth.c), the
+ * wrapper functions wpa_sm_get_state() and wpa_sm_set_state() should be used
+ * to access the state variable.
+ */
+enum wpa_states {
+ /**
+ * WPA_DISCONNECTED - Disconnected state
+ *
+ * This state indicates that client is not associated, but is likely to
+ * start looking for an access point. This state is entered when a
+ * connection is lost.
+ */
+ WPA_DISCONNECTED,
+
+ /**
+ * WPA_INTERFACE_DISABLED - Interface disabled
+ *
+ * This state is entered if the network interface is disabled, e.g.,
+ * due to rfkill. wpa_supplicant refuses any new operations that would
+ * use the radio until the interface has been enabled.
+ */
+ WPA_INTERFACE_DISABLED,
+
+ /**
+ * WPA_INACTIVE - Inactive state (wpa_supplicant disabled)
+ *
+ * This state is entered if there are no enabled networks in the
+ * configuration. wpa_supplicant is not trying to associate with a new
+ * network and external interaction (e.g., ctrl_iface call to add or
+ * enable a network) is needed to start association.
+ */
+ WPA_INACTIVE,
+
+ /**
+ * WPA_SCANNING - Scanning for a network
+ *
+ * This state is entered when wpa_supplicant starts scanning for a
+ * network.
+ */
+ WPA_SCANNING,
+
+ /**
+ * WPA_AUTHENTICATING - Trying to authenticate with a BSS/SSID
+ *
+ * This state is entered when wpa_supplicant has found a suitable BSS
+ * to authenticate with and the driver is configured to try to
+ * authenticate with this BSS. This state is used only with drivers
+ * that use wpa_supplicant as the SME.
+ */
+ WPA_AUTHENTICATING,
+
+ /**
+ * WPA_ASSOCIATING - Trying to associate with a BSS/SSID
+ *
+ * This state is entered when wpa_supplicant has found a suitable BSS
+ * to associate with and the driver is configured to try to associate
+ * with this BSS in ap_scan=1 mode. When using ap_scan=2 mode, this
+ * state is entered when the driver is configured to try to associate
+ * with a network using the configured SSID and security policy.
+ */
+ WPA_ASSOCIATING,
+
+ /**
+ * WPA_ASSOCIATED - Association completed
+ *
+ * This state is entered when the driver reports that association has
+ * been successfully completed with an AP. If IEEE 802.1X is used
+ * (with or without WPA/WPA2), wpa_supplicant remains in this state
+ * until the IEEE 802.1X/EAPOL authentication has been completed.
+ */
+ WPA_ASSOCIATED,
+
+ /**
+ * WPA_4WAY_HANDSHAKE - WPA 4-Way Key Handshake in progress
+ *
+ * This state is entered when WPA/WPA2 4-Way Handshake is started. In
+ * case of WPA-PSK, this happens when receiving the first EAPOL-Key
+ * frame after association. In case of WPA-EAP, this state is entered
+ * when the IEEE 802.1X/EAPOL authentication has been completed.
+ */
+ WPA_4WAY_HANDSHAKE,
+
+ /**
+ * WPA_GROUP_HANDSHAKE - WPA Group Key Handshake in progress
+ *
+ * This state is entered when 4-Way Key Handshake has been completed
+ * (i.e., when the supplicant sends out message 4/4) and when Group
+ * Key rekeying is started by the AP (i.e., when supplicant receives
+ * message 1/2).
+ */
+ WPA_GROUP_HANDSHAKE,
+
+ /**
+ * WPA_COMPLETED - All authentication completed
+ *
+ * This state is entered when the full authentication process is
+ * completed. In case of WPA2, this happens when the 4-Way Handshake is
+ * successfully completed. With WPA, this state is entered after the
+ * Group Key Handshake; with IEEE 802.1X (non-WPA) connection is
+ * completed after dynamic keys are received (or if not used, after
+ * the EAP authentication has been completed). With static WEP keys and
+ * plaintext connections, this state is entered when an association
+ * has been completed.
+ *
+ * This state indicates that the supplicant has completed its
+ * processing for the association phase and that data connection is
+ * fully configured.
+ */
+ WPA_COMPLETED
+};
+
+#define MLME_SETPROTECTION_PROTECT_TYPE_NONE 0
+#define MLME_SETPROTECTION_PROTECT_TYPE_RX 1
+#define MLME_SETPROTECTION_PROTECT_TYPE_TX 2
+#define MLME_SETPROTECTION_PROTECT_TYPE_RX_TX 3
+
+#define MLME_SETPROTECTION_KEY_TYPE_GROUP 0
+#define MLME_SETPROTECTION_KEY_TYPE_PAIRWISE 1
+
+
+/**
+ * enum mfp_options - Management frame protection (IEEE 802.11w) options
+ */
+enum mfp_options {
+ NO_MGMT_FRAME_PROTECTION = 0,
+ MGMT_FRAME_PROTECTION_OPTIONAL = 1,
+ MGMT_FRAME_PROTECTION_REQUIRED = 2,
+};
+#define MGMT_FRAME_PROTECTION_DEFAULT 3
+
+/**
+ * enum hostapd_hw_mode - Hardware mode
+ */
+enum hostapd_hw_mode {
+ HOSTAPD_MODE_IEEE80211B,
+ HOSTAPD_MODE_IEEE80211G,
+ HOSTAPD_MODE_IEEE80211A,
+ HOSTAPD_MODE_IEEE80211AD,
+ HOSTAPD_MODE_IEEE80211ANY,
+ NUM_HOSTAPD_MODES
+};
+
+/**
+ * enum wpa_ctrl_req_type - Control interface request types
+ */
+enum wpa_ctrl_req_type {
+ WPA_CTRL_REQ_UNKNOWN,
+ WPA_CTRL_REQ_EAP_IDENTITY,
+ WPA_CTRL_REQ_EAP_PASSWORD,
+ WPA_CTRL_REQ_EAP_NEW_PASSWORD,
+ WPA_CTRL_REQ_EAP_PIN,
+ WPA_CTRL_REQ_EAP_OTP,
+ WPA_CTRL_REQ_EAP_PASSPHRASE,
+ WPA_CTRL_REQ_SIM,
+ WPA_CTRL_REQ_PSK_PASSPHRASE,
+ NUM_WPA_CTRL_REQS
+};
+
+/* Maximum number of EAP methods to store for EAP server user information */
+#define EAP_MAX_METHODS 8
+
+enum mesh_plink_state {
+ PLINK_LISTEN = 1,
+ PLINK_OPEN_SENT,
+ PLINK_OPEN_RCVD,
+ PLINK_CNF_RCVD,
+ PLINK_ESTAB,
+ PLINK_HOLDING,
+ PLINK_BLOCKED,
+};
+
+enum set_band {
+ WPA_SETBAND_AUTO,
+ WPA_SETBAND_5G,
+ WPA_SETBAND_2G
+};
+
+#endif /* DEFS_H */
diff --git a/freebsd/contrib/wpa/src/common/eapol_common.h b/freebsd/contrib/wpa/src/common/eapol_common.h
new file mode 100644
index 00000000..6958661f
--- /dev/null
+++ b/freebsd/contrib/wpa/src/common/eapol_common.h
@@ -0,0 +1,92 @@
+/*
+ * EAPOL definitions shared between hostapd and wpa_supplicant
+ * Copyright (c) 2002-2007, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef EAPOL_COMMON_H
+#define EAPOL_COMMON_H
+
+/* IEEE Std 802.1X-2004 */
+
+#ifdef _MSC_VER
+#pragma pack(push, 1)
+#endif /* _MSC_VER */
+
+struct ieee802_1x_hdr {
+ u8 version;
+ u8 type;
+ be16 length;
+ /* followed by length octets of data */
+} STRUCT_PACKED;
+
+struct ieee8023_hdr {
+ u8 dest[ETH_ALEN];
+ u8 src[ETH_ALEN];
+ u16 ethertype;
+} STRUCT_PACKED;
+
+#ifdef _MSC_VER
+#pragma pack(pop)
+#endif /* _MSC_VER */
+
+#ifdef CONFIG_MACSEC
+#define EAPOL_VERSION 3
+#else /* CONFIG_MACSEC */
+#define EAPOL_VERSION 2
+#endif /* CONFIG_MACSEC */
+
+enum { IEEE802_1X_TYPE_EAP_PACKET = 0,
+ IEEE802_1X_TYPE_EAPOL_START = 1,
+ IEEE802_1X_TYPE_EAPOL_LOGOFF = 2,
+ IEEE802_1X_TYPE_EAPOL_KEY = 3,
+ IEEE802_1X_TYPE_EAPOL_ENCAPSULATED_ASF_ALERT = 4,
+ IEEE802_1X_TYPE_EAPOL_MKA = 5,
+};
+
+enum { EAPOL_KEY_TYPE_RC4 = 1, EAPOL_KEY_TYPE_RSN = 2,
+ EAPOL_KEY_TYPE_WPA = 254 };
+
+
+#define IEEE8021X_REPLAY_COUNTER_LEN 8
+#define IEEE8021X_KEY_SIGN_LEN 16
+#define IEEE8021X_KEY_IV_LEN 16
+
+#define IEEE8021X_KEY_INDEX_FLAG 0x80
+#define IEEE8021X_KEY_INDEX_MASK 0x03
+
+#ifdef _MSC_VER
+#pragma pack(push, 1)
+#endif /* _MSC_VER */
+
+struct ieee802_1x_eapol_key {
+ u8 type;
+ /* Note: key_length is unaligned */
+ u8 key_length[2];
+ /* does not repeat within the life of the keying material used to
+ * encrypt the Key field; 64-bit NTP timestamp MAY be used here */
+ u8 replay_counter[IEEE8021X_REPLAY_COUNTER_LEN];
+ u8 key_iv[IEEE8021X_KEY_IV_LEN]; /* cryptographically random number */
+ u8 key_index; /* key flag in the most significant bit:
+ * 0 = broadcast (default key),
+ * 1 = unicast (key mapping key); key index is in the
+ * 7 least significant bits */
+ /* HMAC-MD5 message integrity check computed with MS-MPPE-Send-Key as
+ * the key */
+ u8 key_signature[IEEE8021X_KEY_SIGN_LEN];
+
+ /* followed by key: if packet body length = 44 + key length, then the
+ * key field (of key_length bytes) contains the key in encrypted form;
+ * if packet body length = 44, key field is absent and key_length
+ * represents the number of least significant octets from
+ * MS-MPPE-Send-Key attribute to be used as the keying material;
+ * RC4 key used in encryption = Key-IV + MS-MPPE-Recv-Key */
+} STRUCT_PACKED;
+
+#ifdef _MSC_VER
+#pragma pack(pop)
+#endif /* _MSC_VER */
+
+#endif /* EAPOL_COMMON_H */
diff --git a/freebsd/contrib/wpa/src/common/gas.c b/freebsd/contrib/wpa/src/common/gas.c
new file mode 100644
index 00000000..1aa3c806
--- /dev/null
+++ b/freebsd/contrib/wpa/src/common/gas.c
@@ -0,0 +1,275 @@
+#include <machine/rtems-bsd-user-space.h>
+
+/*
+ * Generic advertisement service (GAS) (IEEE 802.11u)
+ * Copyright (c) 2009, Atheros Communications
+ * Copyright (c) 2011-2012, Qualcomm Atheros
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "ieee802_11_defs.h"
+#include "gas.h"
+
+
+static struct wpabuf *
+gas_build_req(u8 action, u8 dialog_token, size_t size)
+{
+ struct wpabuf *buf;
+
+ buf = wpabuf_alloc(100 + size);
+ if (buf == NULL)
+ return NULL;
+
+ wpabuf_put_u8(buf, WLAN_ACTION_PUBLIC);
+ wpabuf_put_u8(buf, action);
+ wpabuf_put_u8(buf, dialog_token);
+
+ return buf;
+}
+
+
+struct wpabuf * gas_build_initial_req(u8 dialog_token, size_t size)
+{
+ return gas_build_req(WLAN_PA_GAS_INITIAL_REQ, dialog_token,
+ size);
+}
+
+
+struct wpabuf * gas_build_comeback_req(u8 dialog_token)
+{
+ return gas_build_req(WLAN_PA_GAS_COMEBACK_REQ, dialog_token, 0);
+}
+
+
+static struct wpabuf *
+gas_build_resp(u8 action, u8 dialog_token, u16 status_code, u8 frag_id,
+ u8 more, u16 comeback_delay, size_t size)
+{
+ struct wpabuf *buf;
+
+ buf = wpabuf_alloc(100 + size);
+ if (buf == NULL)
+ return NULL;
+
+ wpabuf_put_u8(buf, WLAN_ACTION_PUBLIC);
+ wpabuf_put_u8(buf, action);
+ wpabuf_put_u8(buf, dialog_token);
+ wpabuf_put_le16(buf, status_code);
+ if (action == WLAN_PA_GAS_COMEBACK_RESP)
+ wpabuf_put_u8(buf, frag_id | (more ? 0x80 : 0));
+ wpabuf_put_le16(buf, comeback_delay);
+
+ return buf;
+}
+
+
+struct wpabuf *
+gas_build_initial_resp(u8 dialog_token, u16 status_code, u16 comeback_delay,
+ size_t size)
+{
+ return gas_build_resp(WLAN_PA_GAS_INITIAL_RESP, dialog_token,
+ status_code, 0, 0, comeback_delay, size);
+}
+
+
+static struct wpabuf *
+gas_build_comeback_resp(u8 dialog_token, u16 status_code, u8 frag_id, u8 more,
+ u16 comeback_delay, size_t size)
+{
+ return gas_build_resp(WLAN_PA_GAS_COMEBACK_RESP, dialog_token,
+ status_code, frag_id, more, comeback_delay,
+ size);
+}
+
+
+/**
+ * gas_add_adv_proto_anqp - Add an Advertisement Protocol element
+ * @buf: Buffer to which the element is added
+ * @query_resp_len_limit: Query Response Length Limit in units of 256 octets
+ * @pame_bi: Pre-Association Message Exchange BSSID Independent (0/1)
+ *
+ *
+ * @query_resp_len_limit is 0 for request and 1-0x7f for response. 0x7f means
+ * that the maximum limit is determined by the maximum allowable number of
+ * fragments in the GAS Query Response Fragment ID.
+ */
+static void gas_add_adv_proto_anqp(struct wpabuf *buf, u8 query_resp_len_limit,
+ u8 pame_bi)
+{
+ /* Advertisement Protocol IE */
+ wpabuf_put_u8(buf, WLAN_EID_ADV_PROTO);
+ wpabuf_put_u8(buf, 2); /* Length */
+ wpabuf_put_u8(buf, (query_resp_len_limit & 0x7f) |
+ (pame_bi ? 0x80 : 0));
+ /* Advertisement Protocol */
+ wpabuf_put_u8(buf, ACCESS_NETWORK_QUERY_PROTOCOL);
+}
+
+
+struct wpabuf * gas_anqp_build_initial_req(u8 dialog_token, size_t size)
+{
+ struct wpabuf *buf;
+
+ buf = gas_build_initial_req(dialog_token, 4 + size);
+ if (buf == NULL)
+ return NULL;
+
+ gas_add_adv_proto_anqp(buf, 0, 0);
+
+ wpabuf_put(buf, 2); /* Query Request Length to be filled */
+
+ return buf;
+}
+
+
+struct wpabuf * gas_anqp_build_initial_resp(u8 dialog_token, u16 status_code,
+ u16 comeback_delay, size_t size)
+{
+ struct wpabuf *buf;
+
+ buf = gas_build_initial_resp(dialog_token, status_code, comeback_delay,
+ 4 + size);
+ if (buf == NULL)
+ return NULL;
+
+ gas_add_adv_proto_anqp(buf, 0x7f, 0);
+
+ wpabuf_put(buf, 2); /* Query Response Length to be filled */
+
+ return buf;
+}
+
+
+struct wpabuf * gas_anqp_build_initial_resp_buf(u8 dialog_token,
+ u16 status_code,
+ u16 comeback_delay,
+ struct wpabuf *payload)
+{
+ struct wpabuf *buf;
+
+ buf = gas_anqp_build_initial_resp(dialog_token, status_code,
+ comeback_delay,
+ payload ? wpabuf_len(payload) : 0);
+ if (buf == NULL)
+ return NULL;
+
+ if (payload)
+ wpabuf_put_buf(buf, payload);
+
+ gas_anqp_set_len(buf);
+
+ return buf;
+}
+
+
+struct wpabuf * gas_anqp_build_comeback_resp(u8 dialog_token, u16 status_code,
+ u8 frag_id, u8 more,
+ u16 comeback_delay, size_t size)
+{
+ struct wpabuf *buf;
+
+ buf = gas_build_comeback_resp(dialog_token, status_code,
+ frag_id, more, comeback_delay, 4 + size);
+ if (buf == NULL)
+ return NULL;
+
+ gas_add_adv_proto_anqp(buf, 0x7f, 0);
+
+ wpabuf_put(buf, 2); /* Query Response Length to be filled */
+
+ return buf;
+}
+
+
+struct wpabuf * gas_anqp_build_comeback_resp_buf(u8 dialog_token,
+ u16 status_code,
+ u8 frag_id, u8 more,
+ u16 comeback_delay,
+ struct wpabuf *payload)
+{
+ struct wpabuf *buf;
+
+ buf = gas_anqp_build_comeback_resp(dialog_token, status_code, frag_id,
+ more, comeback_delay,
+ payload ? wpabuf_len(payload) : 0);
+ if (buf == NULL)
+ return NULL;
+
+ if (payload)
+ wpabuf_put_buf(buf, payload);
+
+ gas_anqp_set_len(buf);
+
+ return buf;
+}
+
+
+/**
+ * gas_anqp_set_len - Set Query Request/Response Length
+ * @buf: GAS message
+ *
+ * This function is used to update the Query Request/Response Length field once
+ * the payload has been filled.
+ */
+void gas_anqp_set_len(struct wpabuf *buf)
+{
+ u8 action;
+ size_t offset;
+ u8 *len;
+
+ if (buf == NULL || wpabuf_len(buf) < 2)
+ return;
+
+ action = *(wpabuf_head_u8(buf) + 1);
+ switch (action) {
+ case WLAN_PA_GAS_INITIAL_REQ:
+ offset = 3 + 4;
+ break;
+ case WLAN_PA_GAS_INITIAL_RESP:
+ offset = 7 + 4;
+ break;
+ case WLAN_PA_GAS_COMEBACK_RESP:
+ offset = 8 + 4;
+ break;
+ default:
+ return;
+ }
+
+ if (wpabuf_len(buf) < offset + 2)
+ return;
+
+ len = wpabuf_mhead_u8(buf) + offset;
+ WPA_PUT_LE16(len, (u8 *) wpabuf_put(buf, 0) - len - 2);
+}
+
+
+/**
+ * gas_anqp_add_element - Add ANQP element header
+ * @buf: GAS message
+ * @info_id: ANQP Info ID
+ * Returns: Pointer to the Length field for gas_anqp_set_element_len()
+ */
+u8 * gas_anqp_add_element(struct wpabuf *buf, u16 info_id)
+{
+ wpabuf_put_le16(buf, info_id);
+ return wpabuf_put(buf, 2); /* Length to be filled */
+}
+
+
+/**
+ * gas_anqp_set_element_len - Update ANQP element Length field
+ * @buf: GAS message
+ * @len_pos: Length field position from gas_anqp_add_element()
+ *
+ * This function is called after the ANQP element payload has been added to the
+ * buffer.
+ */
+void gas_anqp_set_element_len(struct wpabuf *buf, u8 *len_pos)
+{
+ WPA_PUT_LE16(len_pos, (u8 *) wpabuf_put(buf, 0) - len_pos - 2);
+}
diff --git a/freebsd/contrib/wpa/src/common/gas.h b/freebsd/contrib/wpa/src/common/gas.h
new file mode 100644
index 00000000..306adc58
--- /dev/null
+++ b/freebsd/contrib/wpa/src/common/gas.h
@@ -0,0 +1,37 @@
+/*
+ * Generic advertisement service (GAS) (IEEE 802.11u)
+ * Copyright (c) 2009, Atheros Communications
+ * Copyright (c) 2011-2012, Qualcomm Atheros
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef GAS_H
+#define GAS_H
+
+struct wpabuf * gas_build_initial_req(u8 dialog_token, size_t size);
+struct wpabuf * gas_build_comeback_req(u8 dialog_token);
+struct wpabuf * gas_build_initial_resp(u8 dialog_token, u16 status_code,
+ u16 comeback_delay, size_t size);
+struct wpabuf * gas_anqp_build_initial_req(u8 dialog_token, size_t size);
+struct wpabuf * gas_anqp_build_initial_resp(u8 dialog_token, u16 status_code,
+ u16 comeback_delay, size_t size);
+struct wpabuf * gas_anqp_build_initial_resp_buf(u8 dialog_token,
+ u16 status_code,
+ u16 comeback_delay,
+ struct wpabuf *payload);
+struct wpabuf * gas_anqp_build_comeback_resp(u8 dialog_token, u16 status_code,
+ u8 frag_id, u8 more,
+ u16 comeback_delay, size_t size);
+struct wpabuf * gas_anqp_build_comeback_resp_buf(u8 dialog_token,
+ u16 status_code,
+ u8 frag_id, u8 more,
+ u16 comeback_delay,
+ struct wpabuf *payload);
+void gas_anqp_set_len(struct wpabuf *buf);
+
+u8 * gas_anqp_add_element(struct wpabuf *buf, u16 info_id);
+void gas_anqp_set_element_len(struct wpabuf *buf, u8 *len_pos);
+
+#endif /* GAS_H */
diff --git a/freebsd/contrib/wpa/src/common/hw_features_common.c b/freebsd/contrib/wpa/src/common/hw_features_common.c
new file mode 100644
index 00000000..81b8e695
--- /dev/null
+++ b/freebsd/contrib/wpa/src/common/hw_features_common.c
@@ -0,0 +1,457 @@
+#include <machine/rtems-bsd-user-space.h>
+
+/*
+ * Common hostapd/wpa_supplicant HW features
+ * Copyright (c) 2002-2013, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2015, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "defs.h"
+#include "ieee802_11_defs.h"
+#include "ieee802_11_common.h"
+#include "hw_features_common.h"
+
+
+struct hostapd_channel_data * hw_get_channel_chan(struct hostapd_hw_modes *mode,
+ int chan, int *freq)
+{
+ int i;
+
+ if (freq)
+ *freq = 0;
+
+ if (!mode)
+ return NULL;
+
+ for (i = 0; i < mode->num_channels; i++) {
+ struct hostapd_channel_data *ch = &mode->channels[i];
+ if (ch->chan == chan) {
+ if (freq)
+ *freq = ch->freq;
+ return ch;
+ }
+ }
+
+ return NULL;
+}
+
+
+struct hostapd_channel_data * hw_get_channel_freq(struct hostapd_hw_modes *mode,
+ int freq, int *chan)
+{
+ int i;
+
+ if (chan)
+ *chan = 0;
+
+ if (!mode)
+ return NULL;
+
+ for (i = 0; i < mode->num_channels; i++) {
+ struct hostapd_channel_data *ch = &mode->channels[i];
+ if (ch->freq == freq) {
+ if (chan)
+ *chan = ch->chan;
+ return ch;
+ }
+ }
+
+ return NULL;
+}
+
+
+int hw_get_freq(struct hostapd_hw_modes *mode, int chan)
+{
+ int freq;
+
+ hw_get_channel_chan(mode, chan, &freq);
+
+ return freq;
+}
+
+
+int hw_get_chan(struct hostapd_hw_modes *mode, int freq)
+{
+ int chan;
+
+ hw_get_channel_freq(mode, freq, &chan);
+
+ return chan;
+}
+
+
+int allowed_ht40_channel_pair(struct hostapd_hw_modes *mode, int pri_chan,
+ int sec_chan)
+{
+ int ok, j, first;
+ int allowed[] = { 36, 44, 52, 60, 100, 108, 116, 124, 132, 140,
+ 149, 157, 184, 192 };
+ size_t k;
+
+ if (pri_chan == sec_chan || !sec_chan)
+ return 1; /* HT40 not used */
+
+ wpa_printf(MSG_DEBUG,
+ "HT40: control channel: %d secondary channel: %d",
+ pri_chan, sec_chan);
+
+ /* Verify that HT40 secondary channel is an allowed 20 MHz
+ * channel */
+ ok = 0;
+ for (j = 0; j < mode->num_channels; j++) {
+ struct hostapd_channel_data *chan = &mode->channels[j];
+ if (!(chan->flag & HOSTAPD_CHAN_DISABLED) &&
+ chan->chan == sec_chan) {
+ ok = 1;
+ break;
+ }
+ }
+ if (!ok) {
+ wpa_printf(MSG_ERROR, "HT40 secondary channel %d not allowed",
+ sec_chan);
+ return 0;
+ }
+
+ /*
+ * Verify that HT40 primary,secondary channel pair is allowed per
+ * IEEE 802.11n Annex J. This is only needed for 5 GHz band since
+ * 2.4 GHz rules allow all cases where the secondary channel fits into
+ * the list of allowed channels (already checked above).
+ */
+ if (mode->mode != HOSTAPD_MODE_IEEE80211A)
+ return 1;
+
+ first = pri_chan < sec_chan ? pri_chan : sec_chan;
+
+ ok = 0;
+ for (k = 0; k < ARRAY_SIZE(allowed); k++) {
+ if (first == allowed[k]) {
+ ok = 1;
+ break;
+ }
+ }
+ if (!ok) {
+ wpa_printf(MSG_ERROR, "HT40 channel pair (%d, %d) not allowed",
+ pri_chan, sec_chan);
+ return 0;
+ }
+
+ return 1;
+}
+
+
+void get_pri_sec_chan(struct wpa_scan_res *bss, int *pri_chan, int *sec_chan)
+{
+ struct ieee80211_ht_operation *oper;
+ struct ieee802_11_elems elems;
+
+ *pri_chan = *sec_chan = 0;
+
+ ieee802_11_parse_elems((u8 *) (bss + 1), bss->ie_len, &elems, 0);
+ if (elems.ht_operation) {
+ oper = (struct ieee80211_ht_operation *) elems.ht_operation;
+ *pri_chan = oper->primary_chan;
+ if (oper->ht_param & HT_INFO_HT_PARAM_STA_CHNL_WIDTH) {
+ int sec = oper->ht_param &
+ HT_INFO_HT_PARAM_SECONDARY_CHNL_OFF_MASK;
+ if (sec == HT_INFO_HT_PARAM_SECONDARY_CHNL_ABOVE)
+ *sec_chan = *pri_chan + 4;
+ else if (sec == HT_INFO_HT_PARAM_SECONDARY_CHNL_BELOW)
+ *sec_chan = *pri_chan - 4;
+ }
+ }
+}
+
+
+int check_40mhz_5g(struct hostapd_hw_modes *mode,
+ struct wpa_scan_results *scan_res, int pri_chan,
+ int sec_chan)
+{
+ int pri_freq, sec_freq, pri_bss, sec_bss;
+ int bss_pri_chan, bss_sec_chan;
+ size_t i;
+ int match;
+
+ if (!mode || !scan_res || !pri_chan || !sec_chan ||
+ pri_chan == sec_chan)
+ return 0;
+
+ pri_freq = hw_get_freq(mode, pri_chan);
+ sec_freq = hw_get_freq(mode, sec_chan);
+
+ /*
+ * Switch PRI/SEC channels if Beacons were detected on selected SEC
+ * channel, but not on selected PRI channel.
+ */
+ pri_bss = sec_bss = 0;
+ for (i = 0; i < scan_res->num; i++) {
+ struct wpa_scan_res *bss = scan_res->res[i];
+ if (bss->freq == pri_freq)
+ pri_bss++;
+ else if (bss->freq == sec_freq)
+ sec_bss++;
+ }
+ if (sec_bss && !pri_bss) {
+ wpa_printf(MSG_INFO,
+ "Switch own primary and secondary channel to get secondary channel with no Beacons from other BSSes");
+ return 2;
+ }
+
+ /*
+ * Match PRI/SEC channel with any existing HT40 BSS on the same
+ * channels that we are about to use (if already mixed order in
+ * existing BSSes, use own preference).
+ */
+ match = 0;
+ for (i = 0; i < scan_res->num; i++) {
+ struct wpa_scan_res *bss = scan_res->res[i];
+ get_pri_sec_chan(bss, &bss_pri_chan, &bss_sec_chan);
+ if (pri_chan == bss_pri_chan &&
+ sec_chan == bss_sec_chan) {
+ match = 1;
+ break;
+ }
+ }
+ if (!match) {
+ for (i = 0; i < scan_res->num; i++) {
+ struct wpa_scan_res *bss = scan_res->res[i];
+ get_pri_sec_chan(bss, &bss_pri_chan, &bss_sec_chan);
+ if (pri_chan == bss_sec_chan &&
+ sec_chan == bss_pri_chan) {
+ wpa_printf(MSG_INFO, "Switch own primary and "
+ "secondary channel due to BSS "
+ "overlap with " MACSTR,
+ MAC2STR(bss->bssid));
+ return 2;
+ }
+ }
+ }
+
+ return 1;
+}
+
+
+static int check_20mhz_bss(struct wpa_scan_res *bss, int pri_freq, int start,
+ int end)
+{
+ struct ieee802_11_elems elems;
+ struct ieee80211_ht_operation *oper;
+
+ if (bss->freq < start || bss->freq > end || bss->freq == pri_freq)
+ return 0;
+
+ ieee802_11_parse_elems((u8 *) (bss + 1), bss->ie_len, &elems, 0);
+ if (!elems.ht_capabilities) {
+ wpa_printf(MSG_DEBUG, "Found overlapping legacy BSS: "
+ MACSTR " freq=%d", MAC2STR(bss->bssid), bss->freq);
+ return 1;
+ }
+
+ if (elems.ht_operation) {
+ oper = (struct ieee80211_ht_operation *) elems.ht_operation;
+ if (oper->ht_param & HT_INFO_HT_PARAM_SECONDARY_CHNL_OFF_MASK)
+ return 0;
+
+ wpa_printf(MSG_DEBUG, "Found overlapping 20 MHz HT BSS: "
+ MACSTR " freq=%d", MAC2STR(bss->bssid), bss->freq);
+ return 1;
+ }
+ return 0;
+}
+
+
+int check_40mhz_2g4(struct hostapd_hw_modes *mode,
+ struct wpa_scan_results *scan_res, int pri_chan,
+ int sec_chan)
+{
+ int pri_freq, sec_freq;
+ int affected_start, affected_end;
+ size_t i;
+
+ if (!mode || !scan_res || !pri_chan || !sec_chan ||
+ pri_chan == sec_chan)
+ return 0;
+
+ pri_freq = hw_get_freq(mode, pri_chan);
+ sec_freq = hw_get_freq(mode, sec_chan);
+
+ affected_start = (pri_freq + sec_freq) / 2 - 25;
+ affected_end = (pri_freq + sec_freq) / 2 + 25;
+ wpa_printf(MSG_DEBUG, "40 MHz affected channel range: [%d,%d] MHz",
+ affected_start, affected_end);
+ for (i = 0; i < scan_res->num; i++) {
+ struct wpa_scan_res *bss = scan_res->res[i];
+ int pri = bss->freq;
+ int sec = pri;
+ struct ieee802_11_elems elems;
+
+ /* Check for overlapping 20 MHz BSS */
+ if (check_20mhz_bss(bss, pri_freq, affected_start,
+ affected_end)) {
+ wpa_printf(MSG_DEBUG,
+ "Overlapping 20 MHz BSS is found");
+ return 0;
+ }
+
+ get_pri_sec_chan(bss, &pri_chan, &sec_chan);
+
+ if (sec_chan) {
+ if (sec_chan < pri_chan)
+ sec = pri - 20;
+ else
+ sec = pri + 20;
+ }
+
+ if ((pri < affected_start || pri > affected_end) &&
+ (sec < affected_start || sec > affected_end))
+ continue; /* not within affected channel range */
+
+ wpa_printf(MSG_DEBUG, "Neighboring BSS: " MACSTR
+ " freq=%d pri=%d sec=%d",
+ MAC2STR(bss->bssid), bss->freq, pri_chan, sec_chan);
+
+ if (sec_chan) {
+ if (pri_freq != pri || sec_freq != sec) {
+ wpa_printf(MSG_DEBUG,
+ "40 MHz pri/sec mismatch with BSS "
+ MACSTR
+ " <%d,%d> (chan=%d%c) vs. <%d,%d>",
+ MAC2STR(bss->bssid),
+ pri, sec, pri_chan,
+ sec > pri ? '+' : '-',
+ pri_freq, sec_freq);
+ return 0;
+ }
+ }
+
+ ieee802_11_parse_elems((u8 *) (bss + 1), bss->ie_len, &elems,
+ 0);
+ if (elems.ht_capabilities) {
+ struct ieee80211_ht_capabilities *ht_cap =
+ (struct ieee80211_ht_capabilities *)
+ elems.ht_capabilities;
+
+ if (le_to_host16(ht_cap->ht_capabilities_info) &
+ HT_CAP_INFO_40MHZ_INTOLERANT) {
+ wpa_printf(MSG_DEBUG,
+ "40 MHz Intolerant is set on channel %d in BSS "
+ MACSTR, pri, MAC2STR(bss->bssid));
+ return 0;
+ }
+ }
+ }
+
+ return 1;
+}
+
+
+int hostapd_set_freq_params(struct hostapd_freq_params *data,
+ enum hostapd_hw_mode mode,
+ int freq, int channel, int ht_enabled,
+ int vht_enabled, int sec_channel_offset,
+ int vht_oper_chwidth, int center_segment0,
+ int center_segment1, u32 vht_caps)
+{
+ os_memset(data, 0, sizeof(*data));
+ data->mode = mode;
+ data->freq = freq;
+ data->channel = channel;
+ data->ht_enabled = ht_enabled;
+ data->vht_enabled = vht_enabled;
+ data->sec_channel_offset = sec_channel_offset;
+ data->center_freq1 = freq + sec_channel_offset * 10;
+ data->center_freq2 = 0;
+ data->bandwidth = sec_channel_offset ? 40 : 20;
+
+ if (data->vht_enabled) switch (vht_oper_chwidth) {
+ case VHT_CHANWIDTH_USE_HT:
+ if (center_segment1 ||
+ (center_segment0 != 0 &&
+ 5000 + center_segment0 * 5 != data->center_freq1 &&
+ 2407 + center_segment0 * 5 != data->center_freq1))
+ return -1;
+ break;
+ case VHT_CHANWIDTH_80P80MHZ:
+ if (!(vht_caps & VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ)) {
+ wpa_printf(MSG_ERROR,
+ "80+80 channel width is not supported!");
+ return -1;
+ }
+ if (center_segment1 == center_segment0 + 4 ||
+ center_segment1 == center_segment0 - 4)
+ return -1;
+ data->center_freq2 = 5000 + center_segment1 * 5;
+ /* fall through */
+ case VHT_CHANWIDTH_80MHZ:
+ data->bandwidth = 80;
+ if ((vht_oper_chwidth == 1 && center_segment1) ||
+ (vht_oper_chwidth == 3 && !center_segment1) ||
+ !sec_channel_offset)
+ return -1;
+ if (!center_segment0) {
+ if (channel <= 48)
+ center_segment0 = 42;
+ else if (channel <= 64)
+ center_segment0 = 58;
+ else if (channel <= 112)
+ center_segment0 = 106;
+ else if (channel <= 128)
+ center_segment0 = 122;
+ else if (channel <= 144)
+ center_segment0 = 138;
+ else if (channel <= 161)
+ center_segment0 = 155;
+ data->center_freq1 = 5000 + center_segment0 * 5;
+ } else {
+ /*
+ * Note: HT/VHT config and params are coupled. Check if
+ * HT40 channel band is in VHT80 Pri channel band
+ * configuration.
+ */
+ if (center_segment0 == channel + 6 ||
+ center_segment0 == channel + 2 ||
+ center_segment0 == channel - 2 ||
+ center_segment0 == channel - 6)
+ data->center_freq1 = 5000 + center_segment0 * 5;
+ else
+ return -1;
+ }
+ break;
+ case VHT_CHANWIDTH_160MHZ:
+ data->bandwidth = 160;
+ if (!(vht_caps & (VHT_CAP_SUPP_CHAN_WIDTH_160MHZ |
+ VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ))) {
+ wpa_printf(MSG_ERROR,
+ "160MHZ channel width is not supported!");
+ return -1;
+ }
+ if (center_segment1)
+ return -1;
+ if (!sec_channel_offset)
+ return -1;
+ /*
+ * Note: HT/VHT config and params are coupled. Check if
+ * HT40 channel band is in VHT160 channel band configuration.
+ */
+ if (center_segment0 == channel + 14 ||
+ center_segment0 == channel + 10 ||
+ center_segment0 == channel + 6 ||
+ center_segment0 == channel + 2 ||
+ center_segment0 == channel - 2 ||
+ center_segment0 == channel - 6 ||
+ center_segment0 == channel - 10 ||
+ center_segment0 == channel - 14)
+ data->center_freq1 = 5000 + center_segment0 * 5;
+ else
+ return -1;
+ break;
+ }
+
+ return 0;
+}
diff --git a/freebsd/contrib/wpa/src/common/hw_features_common.h b/freebsd/contrib/wpa/src/common/hw_features_common.h
new file mode 100644
index 00000000..7360b4e3
--- /dev/null
+++ b/freebsd/contrib/wpa/src/common/hw_features_common.h
@@ -0,0 +1,39 @@
+/*
+ * Common hostapd/wpa_supplicant HW features
+ * Copyright (c) 2002-2013, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2015, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef HW_FEATURES_COMMON_H
+#define HW_FEATURES_COMMON_H
+
+#include "drivers/driver.h"
+
+struct hostapd_channel_data * hw_get_channel_chan(struct hostapd_hw_modes *mode,
+ int chan, int *freq);
+struct hostapd_channel_data * hw_get_channel_freq(struct hostapd_hw_modes *mode,
+ int freq, int *chan);
+
+int hw_get_freq(struct hostapd_hw_modes *mode, int chan);
+int hw_get_chan(struct hostapd_hw_modes *mode, int freq);
+
+int allowed_ht40_channel_pair(struct hostapd_hw_modes *mode, int pri_chan,
+ int sec_chan);
+void get_pri_sec_chan(struct wpa_scan_res *bss, int *pri_chan, int *sec_chan);
+int check_40mhz_5g(struct hostapd_hw_modes *mode,
+ struct wpa_scan_results *scan_res, int pri_chan,
+ int sec_chan);
+int check_40mhz_2g4(struct hostapd_hw_modes *mode,
+ struct wpa_scan_results *scan_res, int pri_chan,
+ int sec_chan);
+int hostapd_set_freq_params(struct hostapd_freq_params *data,
+ enum hostapd_hw_mode mode,
+ int freq, int channel, int ht_enabled,
+ int vht_enabled, int sec_channel_offset,
+ int vht_oper_chwidth, int center_segment0,
+ int center_segment1, u32 vht_caps);
+
+#endif /* HW_FEATURES_COMMON_H */
diff --git a/freebsd/contrib/wpa/src/common/ieee802_11_common.c b/freebsd/contrib/wpa/src/common/ieee802_11_common.c
new file mode 100644
index 00000000..ba7ef0a8
--- /dev/null
+++ b/freebsd/contrib/wpa/src/common/ieee802_11_common.c
@@ -0,0 +1,1149 @@
+#include <machine/rtems-bsd-user-space.h>
+
+/*
+ * IEEE 802.11 Common routines
+ * Copyright (c) 2002-2015, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "defs.h"
+#include "wpa_common.h"
+#include "qca-vendor.h"
+#include "ieee802_11_defs.h"
+#include "ieee802_11_common.h"
+
+
+static int ieee802_11_parse_vendor_specific(const u8 *pos, size_t elen,
+ struct ieee802_11_elems *elems,
+ int show_errors)
+{
+ unsigned int oui;
+
+ /* first 3 bytes in vendor specific information element are the IEEE
+ * OUI of the vendor. The following byte is used a vendor specific
+ * sub-type. */
+ if (elen < 4) {
+ if (show_errors) {
+ wpa_printf(MSG_MSGDUMP, "short vendor specific "
+ "information element ignored (len=%lu)",
+ (unsigned long) elen);
+ }
+ return -1;
+ }
+
+ oui = WPA_GET_BE24(pos);
+ switch (oui) {
+ case OUI_MICROSOFT:
+ /* Microsoft/Wi-Fi information elements are further typed and
+ * subtyped */
+ switch (pos[3]) {
+ case 1:
+ /* Microsoft OUI (00:50:F2) with OUI Type 1:
+ * real WPA information element */
+ elems->wpa_ie = pos;
+ elems->wpa_ie_len = elen;
+ break;
+ case WMM_OUI_TYPE:
+ /* WMM information element */
+ if (elen < 5) {
+ wpa_printf(MSG_MSGDUMP, "short WMM "
+ "information element ignored "
+ "(len=%lu)",
+ (unsigned long) elen);
+ return -1;
+ }
+ switch (pos[4]) {
+ case WMM_OUI_SUBTYPE_INFORMATION_ELEMENT:
+ case WMM_OUI_SUBTYPE_PARAMETER_ELEMENT:
+ /*
+ * Share same pointer since only one of these
+ * is used and they start with same data.
+ * Length field can be used to distinguish the
+ * IEs.
+ */
+ elems->wmm = pos;
+ elems->wmm_len = elen;
+ break;
+ case WMM_OUI_SUBTYPE_TSPEC_ELEMENT:
+ elems->wmm_tspec = pos;
+ elems->wmm_tspec_len = elen;
+ break;
+ default:
+ wpa_printf(MSG_EXCESSIVE, "unknown WMM "
+ "information element ignored "
+ "(subtype=%d len=%lu)",
+ pos[4], (unsigned long) elen);
+ return -1;
+ }
+ break;
+ case 4:
+ /* Wi-Fi Protected Setup (WPS) IE */
+ elems->wps_ie = pos;
+ elems->wps_ie_len = elen;
+ break;
+ default:
+ wpa_printf(MSG_EXCESSIVE, "Unknown Microsoft "
+ "information element ignored "
+ "(type=%d len=%lu)",
+ pos[3], (unsigned long) elen);
+ return -1;
+ }
+ break;
+
+ case OUI_WFA:
+ switch (pos[3]) {
+ case P2P_OUI_TYPE:
+ /* Wi-Fi Alliance - P2P IE */
+ elems->p2p = pos;
+ elems->p2p_len = elen;
+ break;
+ case WFD_OUI_TYPE:
+ /* Wi-Fi Alliance - WFD IE */
+ elems->wfd = pos;
+ elems->wfd_len = elen;
+ break;
+ case HS20_INDICATION_OUI_TYPE:
+ /* Hotspot 2.0 */
+ elems->hs20 = pos;
+ elems->hs20_len = elen;
+ break;
+ case HS20_OSEN_OUI_TYPE:
+ /* Hotspot 2.0 OSEN */
+ elems->osen = pos;
+ elems->osen_len = elen;
+ break;
+ default:
+ wpa_printf(MSG_MSGDUMP, "Unknown WFA "
+ "information element ignored "
+ "(type=%d len=%lu)",
+ pos[3], (unsigned long) elen);
+ return -1;
+ }
+ break;
+
+ case OUI_BROADCOM:
+ switch (pos[3]) {
+ case VENDOR_HT_CAPAB_OUI_TYPE:
+ elems->vendor_ht_cap = pos;
+ elems->vendor_ht_cap_len = elen;
+ break;
+ case VENDOR_VHT_TYPE:
+ if (elen > 4 &&
+ (pos[4] == VENDOR_VHT_SUBTYPE ||
+ pos[4] == VENDOR_VHT_SUBTYPE2)) {
+ elems->vendor_vht = pos;
+ elems->vendor_vht_len = elen;
+ } else
+ return -1;
+ break;
+ default:
+ wpa_printf(MSG_EXCESSIVE, "Unknown Broadcom "
+ "information element ignored "
+ "(type=%d len=%lu)",
+ pos[3], (unsigned long) elen);
+ return -1;
+ }
+ break;
+
+ case OUI_QCA:
+ switch (pos[3]) {
+ case QCA_VENDOR_ELEM_P2P_PREF_CHAN_LIST:
+ elems->pref_freq_list = pos;
+ elems->pref_freq_list_len = elen;
+ break;
+ default:
+ wpa_printf(MSG_EXCESSIVE,
+ "Unknown QCA information element ignored (type=%d len=%lu)",
+ pos[3], (unsigned long) elen);
+ return -1;
+ }
+ break;
+
+ default:
+ wpa_printf(MSG_EXCESSIVE, "unknown vendor specific "
+ "information element ignored (vendor OUI "
+ "%02x:%02x:%02x len=%lu)",
+ pos[0], pos[1], pos[2], (unsigned long) elen);
+ return -1;
+ }
+
+ return 0;
+}
+
+
+/**
+ * ieee802_11_parse_elems - Parse information elements in management frames
+ * @start: Pointer to the start of IEs
+ * @len: Length of IE buffer in octets
+ * @elems: Data structure for parsed elements
+ * @show_errors: Whether to show parsing errors in debug log
+ * Returns: Parsing result
+ */
+ParseRes ieee802_11_parse_elems(const u8 *start, size_t len,
+ struct ieee802_11_elems *elems,
+ int show_errors)
+{
+ size_t left = len;
+ const u8 *pos = start;
+ int unknown = 0;
+
+ os_memset(elems, 0, sizeof(*elems));
+
+ while (left >= 2) {
+ u8 id, elen;
+
+ id = *pos++;
+ elen = *pos++;
+ left -= 2;
+
+ if (elen > left) {
+ if (show_errors) {
+ wpa_printf(MSG_DEBUG, "IEEE 802.11 element "
+ "parse failed (id=%d elen=%d "
+ "left=%lu)",
+ id, elen, (unsigned long) left);
+ wpa_hexdump(MSG_MSGDUMP, "IEs", start, len);
+ }
+ return ParseFailed;
+ }
+
+ switch (id) {
+ case WLAN_EID_SSID:
+ if (elen > SSID_MAX_LEN) {
+ wpa_printf(MSG_DEBUG,
+ "Ignored too long SSID element (elen=%u)",
+ elen);
+ break;
+ }
+ elems->ssid = pos;
+ elems->ssid_len = elen;
+ break;
+ case WLAN_EID_SUPP_RATES:
+ elems->supp_rates = pos;
+ elems->supp_rates_len = elen;
+ break;
+ case WLAN_EID_DS_PARAMS:
+ if (elen < 1)
+ break;
+ elems->ds_params = pos;
+ break;
+ case WLAN_EID_CF_PARAMS:
+ case WLAN_EID_TIM:
+ break;
+ case WLAN_EID_CHALLENGE:
+ elems->challenge = pos;
+ elems->challenge_len = elen;
+ break;
+ case WLAN_EID_ERP_INFO:
+ if (elen < 1)
+ break;
+ elems->erp_info = pos;
+ break;
+ case WLAN_EID_EXT_SUPP_RATES:
+ elems->ext_supp_rates = pos;
+ elems->ext_supp_rates_len = elen;
+ break;
+ case WLAN_EID_VENDOR_SPECIFIC:
+ if (ieee802_11_parse_vendor_specific(pos, elen,
+ elems,
+ show_errors))
+ unknown++;
+ break;
+ case WLAN_EID_RSN:
+ elems->rsn_ie = pos;
+ elems->rsn_ie_len = elen;
+ break;
+ case WLAN_EID_PWR_CAPABILITY:
+ break;
+ case WLAN_EID_SUPPORTED_CHANNELS:
+ elems->supp_channels = pos;
+ elems->supp_channels_len = elen;
+ break;
+ case WLAN_EID_MOBILITY_DOMAIN:
+ if (elen < sizeof(struct rsn_mdie))
+ break;
+ elems->mdie = pos;
+ elems->mdie_len = elen;
+ break;
+ case WLAN_EID_FAST_BSS_TRANSITION:
+ if (elen < sizeof(struct rsn_ftie))
+ break;
+ elems->ftie = pos;
+ elems->ftie_len = elen;
+ break;
+ case WLAN_EID_TIMEOUT_INTERVAL:
+ if (elen != 5)
+ break;
+ elems->timeout_int = pos;
+ break;
+ case WLAN_EID_HT_CAP:
+ if (elen < sizeof(struct ieee80211_ht_capabilities))
+ break;
+ elems->ht_capabilities = pos;
+ break;
+ case WLAN_EID_HT_OPERATION:
+ if (elen < sizeof(struct ieee80211_ht_operation))
+ break;
+ elems->ht_operation = pos;
+ break;
+ case WLAN_EID_MESH_CONFIG:
+ elems->mesh_config = pos;
+ elems->mesh_config_len = elen;
+ break;
+ case WLAN_EID_MESH_ID:
+ elems->mesh_id = pos;
+ elems->mesh_id_len = elen;
+ break;
+ case WLAN_EID_PEER_MGMT:
+ elems->peer_mgmt = pos;
+ elems->peer_mgmt_len = elen;
+ break;
+ case WLAN_EID_VHT_CAP:
+ if (elen < sizeof(struct ieee80211_vht_capabilities))
+ break;
+ elems->vht_capabilities = pos;
+ break;
+ case WLAN_EID_VHT_OPERATION:
+ if (elen < sizeof(struct ieee80211_vht_operation))
+ break;
+ elems->vht_operation = pos;
+ break;
+ case WLAN_EID_VHT_OPERATING_MODE_NOTIFICATION:
+ if (elen != 1)
+ break;
+ elems->vht_opmode_notif = pos;
+ break;
+ case WLAN_EID_LINK_ID:
+ if (elen < 18)
+ break;
+ elems->link_id = pos;
+ break;
+ case WLAN_EID_INTERWORKING:
+ elems->interworking = pos;
+ elems->interworking_len = elen;
+ break;
+ case WLAN_EID_QOS_MAP_SET:
+ if (elen < 16)
+ break;
+ elems->qos_map_set = pos;
+ elems->qos_map_set_len = elen;
+ break;
+ case WLAN_EID_EXT_CAPAB:
+ elems->ext_capab = pos;
+ elems->ext_capab_len = elen;
+ break;
+ case WLAN_EID_BSS_MAX_IDLE_PERIOD:
+ if (elen < 3)
+ break;
+ elems->bss_max_idle_period = pos;
+ break;
+ case WLAN_EID_SSID_LIST:
+ elems->ssid_list = pos;
+ elems->ssid_list_len = elen;
+ break;
+ case WLAN_EID_AMPE:
+ elems->ampe = pos;
+ elems->ampe_len = elen;
+ break;
+ case WLAN_EID_MIC:
+ elems->mic = pos;
+ elems->mic_len = elen;
+ /* after mic everything is encrypted, so stop. */
+ left = elen;
+ break;
+ case WLAN_EID_MULTI_BAND:
+ if (elems->mb_ies.nof_ies >= MAX_NOF_MB_IES_SUPPORTED) {
+ wpa_printf(MSG_MSGDUMP,
+ "IEEE 802.11 element parse ignored MB IE (id=%d elen=%d)",
+ id, elen);
+ break;
+ }
+
+ elems->mb_ies.ies[elems->mb_ies.nof_ies].ie = pos;
+ elems->mb_ies.ies[elems->mb_ies.nof_ies].ie_len = elen;
+ elems->mb_ies.nof_ies++;
+ break;
+ default:
+ unknown++;
+ if (!show_errors)
+ break;
+ wpa_printf(MSG_MSGDUMP, "IEEE 802.11 element parse "
+ "ignored unknown element (id=%d elen=%d)",
+ id, elen);
+ break;
+ }
+
+ left -= elen;
+ pos += elen;
+ }
+
+ if (left)
+ return ParseFailed;
+
+ return unknown ? ParseUnknown : ParseOK;
+}
+
+
+int ieee802_11_ie_count(const u8 *ies, size_t ies_len)
+{
+ int count = 0;
+ const u8 *pos, *end;
+
+ if (ies == NULL)
+ return 0;
+
+ pos = ies;
+ end = ies + ies_len;
+
+ while (pos + 2 <= end) {
+ if (pos + 2 + pos[1] > end)
+ break;
+ count++;
+ pos += 2 + pos[1];
+ }
+
+ return count;
+}
+
+
+struct wpabuf * ieee802_11_vendor_ie_concat(const u8 *ies, size_t ies_len,
+ u32 oui_type)
+{
+ struct wpabuf *buf;
+ const u8 *end, *pos, *ie;
+
+ pos = ies;
+ end = ies + ies_len;
+ ie = NULL;
+
+ while (pos + 1 < end) {
+ if (pos + 2 + pos[1] > end)
+ return NULL;
+ if (pos[0] == WLAN_EID_VENDOR_SPECIFIC && pos[1] >= 4 &&
+ WPA_GET_BE32(&pos[2]) == oui_type) {
+ ie = pos;
+ break;
+ }
+ pos += 2 + pos[1];
+ }
+
+ if (ie == NULL)
+ return NULL; /* No specified vendor IE found */
+
+ buf = wpabuf_alloc(ies_len);
+ if (buf == NULL)
+ return NULL;
+
+ /*
+ * There may be multiple vendor IEs in the message, so need to
+ * concatenate their data fields.
+ */
+ while (pos + 1 < end) {
+ if (pos + 2 + pos[1] > end)
+ break;
+ if (pos[0] == WLAN_EID_VENDOR_SPECIFIC && pos[1] >= 4 &&
+ WPA_GET_BE32(&pos[2]) == oui_type)
+ wpabuf_put_data(buf, pos + 6, pos[1] - 4);
+ pos += 2 + pos[1];
+ }
+
+ return buf;
+}
+
+
+const u8 * get_hdr_bssid(const struct ieee80211_hdr *hdr, size_t len)
+{
+ u16 fc, type, stype;
+
+ /*
+ * PS-Poll frames are 16 bytes. All other frames are
+ * 24 bytes or longer.
+ */
+ if (len < 16)
+ return NULL;
+
+ fc = le_to_host16(hdr->frame_control);
+ type = WLAN_FC_GET_TYPE(fc);
+ stype = WLAN_FC_GET_STYPE(fc);
+
+ switch (type) {
+ case WLAN_FC_TYPE_DATA:
+ if (len < 24)
+ return NULL;
+ switch (fc & (WLAN_FC_FROMDS | WLAN_FC_TODS)) {
+ case WLAN_FC_FROMDS | WLAN_FC_TODS:
+ case WLAN_FC_TODS:
+ return hdr->addr1;
+ case WLAN_FC_FROMDS:
+ return hdr->addr2;
+ default:
+ return NULL;
+ }
+ case WLAN_FC_TYPE_CTRL:
+ if (stype != WLAN_FC_STYPE_PSPOLL)
+ return NULL;
+ return hdr->addr1;
+ case WLAN_FC_TYPE_MGMT:
+ return hdr->addr3;
+ default:
+ return NULL;
+ }
+}
+
+
+int hostapd_config_wmm_ac(struct hostapd_wmm_ac_params wmm_ac_params[],
+ const char *name, const char *val)
+{
+ int num, v;
+ const char *pos;
+ struct hostapd_wmm_ac_params *ac;
+
+ /* skip 'wme_ac_' or 'wmm_ac_' prefix */
+ pos = name + 7;
+ if (os_strncmp(pos, "be_", 3) == 0) {
+ num = 0;
+ pos += 3;
+ } else if (os_strncmp(pos, "bk_", 3) == 0) {
+ num = 1;
+ pos += 3;
+ } else if (os_strncmp(pos, "vi_", 3) == 0) {
+ num = 2;
+ pos += 3;
+ } else if (os_strncmp(pos, "vo_", 3) == 0) {
+ num = 3;
+ pos += 3;
+ } else {
+ wpa_printf(MSG_ERROR, "Unknown WMM name '%s'", pos);
+ return -1;
+ }
+
+ ac = &wmm_ac_params[num];
+
+ if (os_strcmp(pos, "aifs") == 0) {
+ v = atoi(val);
+ if (v < 1 || v > 255) {
+ wpa_printf(MSG_ERROR, "Invalid AIFS value %d", v);
+ return -1;
+ }
+ ac->aifs = v;
+ } else if (os_strcmp(pos, "cwmin") == 0) {
+ v = atoi(val);
+ if (v < 0 || v > 15) {
+ wpa_printf(MSG_ERROR, "Invalid cwMin value %d", v);
+ return -1;
+ }
+ ac->cwmin = v;
+ } else if (os_strcmp(pos, "cwmax") == 0) {
+ v = atoi(val);
+ if (v < 0 || v > 15) {
+ wpa_printf(MSG_ERROR, "Invalid cwMax value %d", v);
+ return -1;
+ }
+ ac->cwmax = v;
+ } else if (os_strcmp(pos, "txop_limit") == 0) {
+ v = atoi(val);
+ if (v < 0 || v > 0xffff) {
+ wpa_printf(MSG_ERROR, "Invalid txop value %d", v);
+ return -1;
+ }
+ ac->txop_limit = v;
+ } else if (os_strcmp(pos, "acm") == 0) {
+ v = atoi(val);
+ if (v < 0 || v > 1) {
+ wpa_printf(MSG_ERROR, "Invalid acm value %d", v);
+ return -1;
+ }
+ ac->admission_control_mandatory = v;
+ } else {
+ wpa_printf(MSG_ERROR, "Unknown wmm_ac_ field '%s'", pos);
+ return -1;
+ }
+
+ return 0;
+}
+
+
+enum hostapd_hw_mode ieee80211_freq_to_chan(int freq, u8 *channel)
+{
+ u8 op_class;
+
+ return ieee80211_freq_to_channel_ext(freq, 0, 0, &op_class, channel);
+}
+
+
+/**
+ * ieee80211_freq_to_channel_ext - Convert frequency into channel info
+ * for HT40 and VHT. DFS channels are not covered.
+ * @freq: Frequency (MHz) to convert
+ * @sec_channel: 0 = non-HT40, 1 = sec. channel above, -1 = sec. channel below
+ * @vht: 0 - non-VHT, 1 - 80 MHz
+ * @op_class: Buffer for returning operating class
+ * @channel: Buffer for returning channel number
+ * Returns: hw_mode on success, NUM_HOSTAPD_MODES on failure
+ */
+enum hostapd_hw_mode ieee80211_freq_to_channel_ext(unsigned int freq,
+ int sec_channel, int vht,
+ u8 *op_class, u8 *channel)
+{
+ /* TODO: more operating classes */
+
+ if (sec_channel > 1 || sec_channel < -1)
+ return NUM_HOSTAPD_MODES;
+
+ if (freq >= 2412 && freq <= 2472) {
+ if ((freq - 2407) % 5)
+ return NUM_HOSTAPD_MODES;
+
+ if (vht)
+ return NUM_HOSTAPD_MODES;
+
+ /* 2.407 GHz, channels 1..13 */
+ if (sec_channel == 1)
+ *op_class = 83;
+ else if (sec_channel == -1)
+ *op_class = 84;
+ else
+ *op_class = 81;
+
+ *channel = (freq - 2407) / 5;
+
+ return HOSTAPD_MODE_IEEE80211G;
+ }
+
+ if (freq == 2484) {
+ if (sec_channel || vht)
+ return NUM_HOSTAPD_MODES;
+
+ *op_class = 82; /* channel 14 */
+ *channel = 14;
+
+ return HOSTAPD_MODE_IEEE80211B;
+ }
+
+ if (freq >= 4900 && freq < 5000) {
+ if ((freq - 4000) % 5)
+ return NUM_HOSTAPD_MODES;
+ *channel = (freq - 4000) / 5;
+ *op_class = 0; /* TODO */
+ return HOSTAPD_MODE_IEEE80211A;
+ }
+
+ /* 5 GHz, channels 36..48 */
+ if (freq >= 5180 && freq <= 5240) {
+ if ((freq - 5000) % 5)
+ return NUM_HOSTAPD_MODES;
+
+ if (sec_channel == 1)
+ *op_class = 116;
+ else if (sec_channel == -1)
+ *op_class = 117;
+ else if (vht)
+ *op_class = 128;
+ else
+ *op_class = 115;
+
+ *channel = (freq - 5000) / 5;
+
+ return HOSTAPD_MODE_IEEE80211A;
+ }
+
+ /* 5 GHz, channels 149..161 */
+ if (freq >= 5745 && freq <= 5805) {
+ if ((freq - 5000) % 5)
+ return NUM_HOSTAPD_MODES;
+
+ if (sec_channel == 1)
+ *op_class = 126;
+ else if (sec_channel == -1)
+ *op_class = 127;
+ else if (vht)
+ *op_class = 128;
+ else
+ *op_class = 124;
+
+ *channel = (freq - 5000) / 5;
+
+ return HOSTAPD_MODE_IEEE80211A;
+ }
+
+ /* 5 GHz, channels 149..169 */
+ if (freq >= 5745 && freq <= 5845) {
+ if ((freq - 5000) % 5)
+ return NUM_HOSTAPD_MODES;
+
+ *op_class = 125;
+
+ *channel = (freq - 5000) / 5;
+
+ return HOSTAPD_MODE_IEEE80211A;
+ }
+
+ if (freq >= 5000 && freq < 5900) {
+ if ((freq - 5000) % 5)
+ return NUM_HOSTAPD_MODES;
+ *channel = (freq - 5000) / 5;
+ *op_class = 0; /* TODO */
+ return HOSTAPD_MODE_IEEE80211A;
+ }
+
+ /* 56.16 GHz, channel 1..4 */
+ if (freq >= 56160 + 2160 * 1 && freq <= 56160 + 2160 * 4) {
+ if (sec_channel || vht)
+ return NUM_HOSTAPD_MODES;
+
+ *channel = (freq - 56160) / 2160;
+ *op_class = 180;
+
+ return HOSTAPD_MODE_IEEE80211AD;
+ }
+
+ return NUM_HOSTAPD_MODES;
+}
+
+
+static const char *const us_op_class_cc[] = {
+ "US", "CA", NULL
+};
+
+static const char *const eu_op_class_cc[] = {
+ "AL", "AM", "AT", "AZ", "BA", "BE", "BG", "BY", "CH", "CY", "CZ", "DE",
+ "DK", "EE", "EL", "ES", "FI", "FR", "GE", "HR", "HU", "IE", "IS", "IT",
+ "LI", "LT", "LU", "LV", "MD", "ME", "MK", "MT", "NL", "NO", "PL", "PT",
+ "RO", "RS", "RU", "SE", "SI", "SK", "TR", "UA", "UK", NULL
+};
+
+static const char *const jp_op_class_cc[] = {
+ "JP", NULL
+};
+
+static const char *const cn_op_class_cc[] = {
+ "CN", NULL
+};
+
+
+static int country_match(const char *const cc[], const char *const country)
+{
+ int i;
+
+ if (country == NULL)
+ return 0;
+ for (i = 0; cc[i]; i++) {
+ if (cc[i][0] == country[0] && cc[i][1] == country[1])
+ return 1;
+ }
+
+ return 0;
+}
+
+
+static int ieee80211_chan_to_freq_us(u8 op_class, u8 chan)
+{
+ switch (op_class) {
+ case 12: /* channels 1..11 */
+ case 32: /* channels 1..7; 40 MHz */
+ case 33: /* channels 5..11; 40 MHz */
+ if (chan < 1 || chan > 11)
+ return -1;
+ return 2407 + 5 * chan;
+ case 1: /* channels 36,40,44,48 */
+ case 2: /* channels 52,56,60,64; dfs */
+ case 22: /* channels 36,44; 40 MHz */
+ case 23: /* channels 52,60; 40 MHz */
+ case 27: /* channels 40,48; 40 MHz */
+ case 28: /* channels 56,64; 40 MHz */
+ if (chan < 36 || chan > 64)
+ return -1;
+ return 5000 + 5 * chan;
+ case 4: /* channels 100-144 */
+ case 24: /* channels 100-140; 40 MHz */
+ if (chan < 100 || chan > 144)
+ return -1;
+ return 5000 + 5 * chan;
+ case 3: /* channels 149,153,157,161 */
+ case 25: /* channels 149,157; 40 MHz */
+ case 26: /* channels 149,157; 40 MHz */
+ case 30: /* channels 153,161; 40 MHz */
+ case 31: /* channels 153,161; 40 MHz */
+ if (chan < 149 || chan > 161)
+ return -1;
+ return 5000 + 5 * chan;
+ case 5: /* channels 149,153,157,161,165 */
+ if (chan < 149 || chan > 165)
+ return -1;
+ return 5000 + 5 * chan;
+ case 34: /* 60 GHz band, channels 1..3 */
+ if (chan < 1 || chan > 3)
+ return -1;
+ return 56160 + 2160 * chan;
+ }
+ return -1;
+}
+
+
+static int ieee80211_chan_to_freq_eu(u8 op_class, u8 chan)
+{
+ switch (op_class) {
+ case 4: /* channels 1..13 */
+ case 11: /* channels 1..9; 40 MHz */
+ case 12: /* channels 5..13; 40 MHz */
+ if (chan < 1 || chan > 13)
+ return -1;
+ return 2407 + 5 * chan;
+ case 1: /* channels 36,40,44,48 */
+ case 2: /* channels 52,56,60,64; dfs */
+ case 5: /* channels 36,44; 40 MHz */
+ case 6: /* channels 52,60; 40 MHz */
+ case 8: /* channels 40,48; 40 MHz */
+ case 9: /* channels 56,64; 40 MHz */
+ if (chan < 36 || chan > 64)
+ return -1;
+ return 5000 + 5 * chan;
+ case 3: /* channels 100-140 */
+ case 7: /* channels 100-132; 40 MHz */
+ case 10: /* channels 104-136; 40 MHz */
+ case 16: /* channels 100-140 */
+ if (chan < 100 || chan > 140)
+ return -1;
+ return 5000 + 5 * chan;
+ case 17: /* channels 149,153,157,161,165,169 */
+ if (chan < 149 || chan > 169)
+ return -1;
+ return 5000 + 5 * chan;
+ case 18: /* 60 GHz band, channels 1..4 */
+ if (chan < 1 || chan > 4)
+ return -1;
+ return 56160 + 2160 * chan;
+ }
+ return -1;
+}
+
+
+static int ieee80211_chan_to_freq_jp(u8 op_class, u8 chan)
+{
+ switch (op_class) {
+ case 30: /* channels 1..13 */
+ case 56: /* channels 1..9; 40 MHz */
+ case 57: /* channels 5..13; 40 MHz */
+ if (chan < 1 || chan > 13)
+ return -1;
+ return 2407 + 5 * chan;
+ case 31: /* channel 14 */
+ if (chan != 14)
+ return -1;
+ return 2414 + 5 * chan;
+ case 1: /* channels 34,38,42,46(old) or 36,40,44,48 */
+ case 32: /* channels 52,56,60,64 */
+ case 33: /* channels 52,56,60,64 */
+ case 36: /* channels 36,44; 40 MHz */
+ case 37: /* channels 52,60; 40 MHz */
+ case 38: /* channels 52,60; 40 MHz */
+ case 41: /* channels 40,48; 40 MHz */
+ case 42: /* channels 56,64; 40 MHz */
+ case 43: /* channels 56,64; 40 MHz */
+ if (chan < 34 || chan > 64)
+ return -1;
+ return 5000 + 5 * chan;
+ case 34: /* channels 100-140 */
+ case 35: /* channels 100-140 */
+ case 39: /* channels 100-132; 40 MHz */
+ case 40: /* channels 100-132; 40 MHz */
+ case 44: /* channels 104-136; 40 MHz */
+ case 45: /* channels 104-136; 40 MHz */
+ case 58: /* channels 100-140 */
+ if (chan < 100 || chan > 140)
+ return -1;
+ return 5000 + 5 * chan;
+ case 59: /* 60 GHz band, channels 1..4 */
+ if (chan < 1 || chan > 3)
+ return -1;
+ return 56160 + 2160 * chan;
+ }
+ return -1;
+}
+
+
+static int ieee80211_chan_to_freq_cn(u8 op_class, u8 chan)
+{
+ switch (op_class) {
+ case 7: /* channels 1..13 */
+ case 8: /* channels 1..9; 40 MHz */
+ case 9: /* channels 5..13; 40 MHz */
+ if (chan < 1 || chan > 13)
+ return -1;
+ return 2407 + 5 * chan;
+ case 1: /* channels 36,40,44,48 */
+ case 2: /* channels 52,56,60,64; dfs */
+ case 4: /* channels 36,44; 40 MHz */
+ case 5: /* channels 52,60; 40 MHz */
+ if (chan < 36 || chan > 64)
+ return -1;
+ return 5000 + 5 * chan;
+ case 3: /* channels 149,153,157,161,165 */
+ case 6: /* channels 149,157; 40 MHz */
+ if (chan < 149 || chan > 165)
+ return -1;
+ return 5000 + 5 * chan;
+ }
+ return -1;
+}
+
+
+static int ieee80211_chan_to_freq_global(u8 op_class, u8 chan)
+{
+ /* Table E-4 in IEEE Std 802.11-2012 - Global operating classes */
+ switch (op_class) {
+ case 81:
+ /* channels 1..13 */
+ if (chan < 1 || chan > 13)
+ return -1;
+ return 2407 + 5 * chan;
+ case 82:
+ /* channel 14 */
+ if (chan != 14)
+ return -1;
+ return 2414 + 5 * chan;
+ case 83: /* channels 1..9; 40 MHz */
+ case 84: /* channels 5..13; 40 MHz */
+ if (chan < 1 || chan > 13)
+ return -1;
+ return 2407 + 5 * chan;
+ case 115: /* channels 36,40,44,48; indoor only */
+ case 116: /* channels 36,44; 40 MHz; indoor only */
+ case 117: /* channels 40,48; 40 MHz; indoor only */
+ case 118: /* channels 52,56,60,64; dfs */
+ case 119: /* channels 52,60; 40 MHz; dfs */
+ case 120: /* channels 56,64; 40 MHz; dfs */
+ if (chan < 36 || chan > 64)
+ return -1;
+ return 5000 + 5 * chan;
+ case 121: /* channels 100-140 */
+ case 122: /* channels 100-142; 40 MHz */
+ case 123: /* channels 104-136; 40 MHz */
+ if (chan < 100 || chan > 140)
+ return -1;
+ return 5000 + 5 * chan;
+ case 124: /* channels 149,153,157,161 */
+ case 126: /* channels 149,157; 40 MHz */
+ case 127: /* channels 153,161; 40 MHz */
+ if (chan < 149 || chan > 161)
+ return -1;
+ return 5000 + 5 * chan;
+ case 125: /* channels 149,153,157,161,165,169 */
+ if (chan < 149 || chan > 169)
+ return -1;
+ return 5000 + 5 * chan;
+ case 128: /* center freqs 42, 58, 106, 122, 138, 155; 80 MHz */
+ case 130: /* center freqs 42, 58, 106, 122, 138, 155; 80 MHz */
+ if (chan < 36 || chan > 161)
+ return -1;
+ return 5000 + 5 * chan;
+ case 129: /* center freqs 50, 114; 160 MHz */
+ if (chan < 50 || chan > 114)
+ return -1;
+ return 5000 + 5 * chan;
+ case 180: /* 60 GHz band, channels 1..4 */
+ if (chan < 1 || chan > 4)
+ return -1;
+ return 56160 + 2160 * chan;
+ }
+ return -1;
+}
+
+/**
+ * ieee80211_chan_to_freq - Convert channel info to frequency
+ * @country: Country code, if known; otherwise, global operating class is used
+ * @op_class: Operating class
+ * @chan: Channel number
+ * Returns: Frequency in MHz or -1 if the specified channel is unknown
+ */
+int ieee80211_chan_to_freq(const char *country, u8 op_class, u8 chan)
+{
+ int freq;
+
+ if (country_match(us_op_class_cc, country)) {
+ freq = ieee80211_chan_to_freq_us(op_class, chan);
+ if (freq > 0)
+ return freq;
+ }
+
+ if (country_match(eu_op_class_cc, country)) {
+ freq = ieee80211_chan_to_freq_eu(op_class, chan);
+ if (freq > 0)
+ return freq;
+ }
+
+ if (country_match(jp_op_class_cc, country)) {
+ freq = ieee80211_chan_to_freq_jp(op_class, chan);
+ if (freq > 0)
+ return freq;
+ }
+
+ if (country_match(cn_op_class_cc, country)) {
+ freq = ieee80211_chan_to_freq_cn(op_class, chan);
+ if (freq > 0)
+ return freq;
+ }
+
+ return ieee80211_chan_to_freq_global(op_class, chan);
+}
+
+
+int ieee80211_is_dfs(int freq)
+{
+ /* TODO: this could be more accurate to better cover all domains */
+ return (freq >= 5260 && freq <= 5320) || (freq >= 5500 && freq <= 5700);
+}
+
+
+static int is_11b(u8 rate)
+{
+ return rate == 0x02 || rate == 0x04 || rate == 0x0b || rate == 0x16;
+}
+
+
+int supp_rates_11b_only(struct ieee802_11_elems *elems)
+{
+ int num_11b = 0, num_others = 0;
+ int i;
+
+ if (elems->supp_rates == NULL && elems->ext_supp_rates == NULL)
+ return 0;
+
+ for (i = 0; elems->supp_rates && i < elems->supp_rates_len; i++) {
+ if (is_11b(elems->supp_rates[i]))
+ num_11b++;
+ else
+ num_others++;
+ }
+
+ for (i = 0; elems->ext_supp_rates && i < elems->ext_supp_rates_len;
+ i++) {
+ if (is_11b(elems->ext_supp_rates[i]))
+ num_11b++;
+ else
+ num_others++;
+ }
+
+ return num_11b > 0 && num_others == 0;
+}
+
+
+const char * fc2str(u16 fc)
+{
+ u16 stype = WLAN_FC_GET_STYPE(fc);
+#define C2S(x) case x: return #x;
+
+ switch (WLAN_FC_GET_TYPE(fc)) {
+ case WLAN_FC_TYPE_MGMT:
+ switch (stype) {
+ C2S(WLAN_FC_STYPE_ASSOC_REQ)
+ C2S(WLAN_FC_STYPE_ASSOC_RESP)
+ C2S(WLAN_FC_STYPE_REASSOC_REQ)
+ C2S(WLAN_FC_STYPE_REASSOC_RESP)
+ C2S(WLAN_FC_STYPE_PROBE_REQ)
+ C2S(WLAN_FC_STYPE_PROBE_RESP)
+ C2S(WLAN_FC_STYPE_BEACON)
+ C2S(WLAN_FC_STYPE_ATIM)
+ C2S(WLAN_FC_STYPE_DISASSOC)
+ C2S(WLAN_FC_STYPE_AUTH)
+ C2S(WLAN_FC_STYPE_DEAUTH)
+ C2S(WLAN_FC_STYPE_ACTION)
+ }
+ break;
+ case WLAN_FC_TYPE_CTRL:
+ switch (stype) {
+ C2S(WLAN_FC_STYPE_PSPOLL)
+ C2S(WLAN_FC_STYPE_RTS)
+ C2S(WLAN_FC_STYPE_CTS)
+ C2S(WLAN_FC_STYPE_ACK)
+ C2S(WLAN_FC_STYPE_CFEND)
+ C2S(WLAN_FC_STYPE_CFENDACK)
+ }
+ break;
+ case WLAN_FC_TYPE_DATA:
+ switch (stype) {
+ C2S(WLAN_FC_STYPE_DATA)
+ C2S(WLAN_FC_STYPE_DATA_CFACK)
+ C2S(WLAN_FC_STYPE_DATA_CFPOLL)
+ C2S(WLAN_FC_STYPE_DATA_CFACKPOLL)
+ C2S(WLAN_FC_STYPE_NULLFUNC)
+ C2S(WLAN_FC_STYPE_CFACK)
+ C2S(WLAN_FC_STYPE_CFPOLL)
+ C2S(WLAN_FC_STYPE_CFACKPOLL)
+ C2S(WLAN_FC_STYPE_QOS_DATA)
+ C2S(WLAN_FC_STYPE_QOS_DATA_CFACK)
+ C2S(WLAN_FC_STYPE_QOS_DATA_CFPOLL)
+ C2S(WLAN_FC_STYPE_QOS_DATA_CFACKPOLL)
+ C2S(WLAN_FC_STYPE_QOS_NULL)
+ C2S(WLAN_FC_STYPE_QOS_CFPOLL)
+ C2S(WLAN_FC_STYPE_QOS_CFACKPOLL)
+ }
+ break;
+ }
+ return "WLAN_FC_TYPE_UNKNOWN";
+#undef C2S
+}
+
+
+int mb_ies_info_by_ies(struct mb_ies_info *info, const u8 *ies_buf,
+ size_t ies_len)
+{
+ os_memset(info, 0, sizeof(*info));
+
+ while (ies_buf && ies_len >= 2 &&
+ info->nof_ies < MAX_NOF_MB_IES_SUPPORTED) {
+ size_t len = 2 + ies_buf[1];
+
+ if (len > ies_len) {
+ wpa_hexdump(MSG_DEBUG, "Truncated IEs",
+ ies_buf, ies_len);
+ return -1;
+ }
+
+ if (ies_buf[0] == WLAN_EID_MULTI_BAND) {
+ wpa_printf(MSG_DEBUG, "MB IE of %zu bytes found", len);
+ info->ies[info->nof_ies].ie = ies_buf + 2;
+ info->ies[info->nof_ies].ie_len = ies_buf[1];
+ info->nof_ies++;
+ }
+
+ ies_len -= len;
+ ies_buf += len;
+ }
+
+ return 0;
+}
+
+
+struct wpabuf * mb_ies_by_info(struct mb_ies_info *info)
+{
+ struct wpabuf *mb_ies = NULL;
+
+ WPA_ASSERT(info != NULL);
+
+ if (info->nof_ies) {
+ u8 i;
+ size_t mb_ies_size = 0;
+
+ for (i = 0; i < info->nof_ies; i++)
+ mb_ies_size += 2 + info->ies[i].ie_len;
+
+ mb_ies = wpabuf_alloc(mb_ies_size);
+ if (mb_ies) {
+ for (i = 0; i < info->nof_ies; i++) {
+ wpabuf_put_u8(mb_ies, WLAN_EID_MULTI_BAND);
+ wpabuf_put_u8(mb_ies, info->ies[i].ie_len);
+ wpabuf_put_data(mb_ies,
+ info->ies[i].ie,
+ info->ies[i].ie_len);
+ }
+ }
+ }
+
+ return mb_ies;
+}
diff --git a/freebsd/contrib/wpa/src/common/ieee802_11_common.h b/freebsd/contrib/wpa/src/common/ieee802_11_common.h
new file mode 100644
index 00000000..55ce0223
--- /dev/null
+++ b/freebsd/contrib/wpa/src/common/ieee802_11_common.h
@@ -0,0 +1,128 @@
+/*
+ * IEEE 802.11 Common routines
+ * Copyright (c) 2002-2012, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef IEEE802_11_COMMON_H
+#define IEEE802_11_COMMON_H
+
+#define MAX_NOF_MB_IES_SUPPORTED 5
+
+struct mb_ies_info {
+ struct {
+ const u8 *ie;
+ u8 ie_len;
+ } ies[MAX_NOF_MB_IES_SUPPORTED];
+ u8 nof_ies;
+};
+
+/* Parsed Information Elements */
+struct ieee802_11_elems {
+ const u8 *ssid;
+ const u8 *supp_rates;
+ const u8 *ds_params;
+ const u8 *challenge;
+ const u8 *erp_info;
+ const u8 *ext_supp_rates;
+ const u8 *wpa_ie;
+ const u8 *rsn_ie;
+ const u8 *wmm; /* WMM Information or Parameter Element */
+ const u8 *wmm_tspec;
+ const u8 *wps_ie;
+ const u8 *supp_channels;
+ const u8 *mdie;
+ const u8 *ftie;
+ const u8 *timeout_int;
+ const u8 *ht_capabilities;
+ const u8 *ht_operation;
+ const u8 *mesh_config;
+ const u8 *mesh_id;
+ const u8 *peer_mgmt;
+ const u8 *vht_capabilities;
+ const u8 *vht_operation;
+ const u8 *vht_opmode_notif;
+ const u8 *vendor_ht_cap;
+ const u8 *vendor_vht;
+ const u8 *p2p;
+ const u8 *wfd;
+ const u8 *link_id;
+ const u8 *interworking;
+ const u8 *qos_map_set;
+ const u8 *hs20;
+ const u8 *ext_capab;
+ const u8 *bss_max_idle_period;
+ const u8 *ssid_list;
+ const u8 *osen;
+ const u8 *ampe;
+ const u8 *mic;
+ const u8 *pref_freq_list;
+
+ u8 ssid_len;
+ u8 supp_rates_len;
+ u8 challenge_len;
+ u8 ext_supp_rates_len;
+ u8 wpa_ie_len;
+ u8 rsn_ie_len;
+ u8 wmm_len; /* 7 = WMM Information; 24 = WMM Parameter */
+ u8 wmm_tspec_len;
+ u8 wps_ie_len;
+ u8 supp_channels_len;
+ u8 mdie_len;
+ u8 ftie_len;
+ u8 mesh_config_len;
+ u8 mesh_id_len;
+ u8 peer_mgmt_len;
+ u8 vendor_ht_cap_len;
+ u8 vendor_vht_len;
+ u8 p2p_len;
+ u8 wfd_len;
+ u8 interworking_len;
+ u8 qos_map_set_len;
+ u8 hs20_len;
+ u8 ext_capab_len;
+ u8 ssid_list_len;
+ u8 osen_len;
+ u8 ampe_len;
+ u8 mic_len;
+ u8 pref_freq_list_len;
+ struct mb_ies_info mb_ies;
+};
+
+typedef enum { ParseOK = 0, ParseUnknown = 1, ParseFailed = -1 } ParseRes;
+
+ParseRes ieee802_11_parse_elems(const u8 *start, size_t len,
+ struct ieee802_11_elems *elems,
+ int show_errors);
+int ieee802_11_ie_count(const u8 *ies, size_t ies_len);
+struct wpabuf * ieee802_11_vendor_ie_concat(const u8 *ies, size_t ies_len,
+ u32 oui_type);
+struct ieee80211_hdr;
+const u8 * get_hdr_bssid(const struct ieee80211_hdr *hdr, size_t len);
+
+struct hostapd_wmm_ac_params {
+ int cwmin;
+ int cwmax;
+ int aifs;
+ int txop_limit; /* in units of 32us */
+ int admission_control_mandatory;
+};
+
+int hostapd_config_wmm_ac(struct hostapd_wmm_ac_params wmm_ac_params[],
+ const char *name, const char *val);
+enum hostapd_hw_mode ieee80211_freq_to_chan(int freq, u8 *channel);
+int ieee80211_chan_to_freq(const char *country, u8 op_class, u8 chan);
+enum hostapd_hw_mode ieee80211_freq_to_channel_ext(unsigned int freq,
+ int sec_channel, int vht,
+ u8 *op_class, u8 *channel);
+int ieee80211_is_dfs(int freq);
+
+int supp_rates_11b_only(struct ieee802_11_elems *elems);
+int mb_ies_info_by_ies(struct mb_ies_info *info, const u8 *ies_buf,
+ size_t ies_len);
+struct wpabuf * mb_ies_by_info(struct mb_ies_info *info);
+
+const char * fc2str(u16 fc);
+#endif /* IEEE802_11_COMMON_H */
diff --git a/freebsd/contrib/wpa/src/common/ieee802_11_defs.h b/freebsd/contrib/wpa/src/common/ieee802_11_defs.h
new file mode 100644
index 00000000..44530ce3
--- /dev/null
+++ b/freebsd/contrib/wpa/src/common/ieee802_11_defs.h
@@ -0,0 +1,1436 @@
+/*
+ * IEEE 802.11 Frame type definitions
+ * Copyright (c) 2002-2015, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2007-2008 Intel Corporation
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef IEEE802_11_DEFS_H
+#define IEEE802_11_DEFS_H
+
+#include <utils/common.h>
+
+/* IEEE 802.11 defines */
+
+#define WLAN_FC_PVER 0x0003
+#define WLAN_FC_TODS 0x0100
+#define WLAN_FC_FROMDS 0x0200
+#define WLAN_FC_MOREFRAG 0x0400
+#define WLAN_FC_RETRY 0x0800
+#define WLAN_FC_PWRMGT 0x1000
+#define WLAN_FC_MOREDATA 0x2000
+#define WLAN_FC_ISWEP 0x4000
+#define WLAN_FC_ORDER 0x8000
+
+#define WLAN_FC_GET_TYPE(fc) (((fc) & 0x000c) >> 2)
+#define WLAN_FC_GET_STYPE(fc) (((fc) & 0x00f0) >> 4)
+
+#define WLAN_INVALID_MGMT_SEQ 0xFFFF
+
+#define WLAN_GET_SEQ_FRAG(seq) ((seq) & (BIT(3) | BIT(2) | BIT(1) | BIT(0)))
+#define WLAN_GET_SEQ_SEQ(seq) \
+ (((seq) & (~(BIT(3) | BIT(2) | BIT(1) | BIT(0)))) >> 4)
+
+#define WLAN_FC_TYPE_MGMT 0
+#define WLAN_FC_TYPE_CTRL 1
+#define WLAN_FC_TYPE_DATA 2
+
+/* management */
+#define WLAN_FC_STYPE_ASSOC_REQ 0
+#define WLAN_FC_STYPE_ASSOC_RESP 1
+#define WLAN_FC_STYPE_REASSOC_REQ 2
+#define WLAN_FC_STYPE_REASSOC_RESP 3
+#define WLAN_FC_STYPE_PROBE_REQ 4
+#define WLAN_FC_STYPE_PROBE_RESP 5
+#define WLAN_FC_STYPE_BEACON 8
+#define WLAN_FC_STYPE_ATIM 9
+#define WLAN_FC_STYPE_DISASSOC 10
+#define WLAN_FC_STYPE_AUTH 11
+#define WLAN_FC_STYPE_DEAUTH 12
+#define WLAN_FC_STYPE_ACTION 13
+
+/* control */
+#define WLAN_FC_STYPE_PSPOLL 10
+#define WLAN_FC_STYPE_RTS 11
+#define WLAN_FC_STYPE_CTS 12
+#define WLAN_FC_STYPE_ACK 13
+#define WLAN_FC_STYPE_CFEND 14
+#define WLAN_FC_STYPE_CFENDACK 15
+
+/* data */
+#define WLAN_FC_STYPE_DATA 0
+#define WLAN_FC_STYPE_DATA_CFACK 1
+#define WLAN_FC_STYPE_DATA_CFPOLL 2
+#define WLAN_FC_STYPE_DATA_CFACKPOLL 3
+#define WLAN_FC_STYPE_NULLFUNC 4
+#define WLAN_FC_STYPE_CFACK 5
+#define WLAN_FC_STYPE_CFPOLL 6
+#define WLAN_FC_STYPE_CFACKPOLL 7
+#define WLAN_FC_STYPE_QOS_DATA 8
+#define WLAN_FC_STYPE_QOS_DATA_CFACK 9
+#define WLAN_FC_STYPE_QOS_DATA_CFPOLL 10
+#define WLAN_FC_STYPE_QOS_DATA_CFACKPOLL 11
+#define WLAN_FC_STYPE_QOS_NULL 12
+#define WLAN_FC_STYPE_QOS_CFPOLL 14
+#define WLAN_FC_STYPE_QOS_CFACKPOLL 15
+
+/* Authentication algorithms */
+#define WLAN_AUTH_OPEN 0
+#define WLAN_AUTH_SHARED_KEY 1
+#define WLAN_AUTH_FT 2
+#define WLAN_AUTH_SAE 3
+#define WLAN_AUTH_LEAP 128
+
+#define WLAN_AUTH_CHALLENGE_LEN 128
+
+#define WLAN_CAPABILITY_ESS BIT(0)
+#define WLAN_CAPABILITY_IBSS BIT(1)
+#define WLAN_CAPABILITY_CF_POLLABLE BIT(2)
+#define WLAN_CAPABILITY_CF_POLL_REQUEST BIT(3)
+#define WLAN_CAPABILITY_PRIVACY BIT(4)
+#define WLAN_CAPABILITY_SHORT_PREAMBLE BIT(5)
+#define WLAN_CAPABILITY_PBCC BIT(6)
+#define WLAN_CAPABILITY_CHANNEL_AGILITY BIT(7)
+#define WLAN_CAPABILITY_SPECTRUM_MGMT BIT(8)
+#define WLAN_CAPABILITY_SHORT_SLOT_TIME BIT(10)
+#define WLAN_CAPABILITY_DSSS_OFDM BIT(13)
+
+/* Status codes (IEEE 802.11-2007, 7.3.1.9, Table 7-23) */
+#define WLAN_STATUS_SUCCESS 0
+#define WLAN_STATUS_UNSPECIFIED_FAILURE 1
+#define WLAN_STATUS_TDLS_WAKEUP_ALTERNATE 2
+#define WLAN_STATUS_TDLS_WAKEUP_REJECT 3
+#define WLAN_STATUS_SECURITY_DISABLED 5
+#define WLAN_STATUS_UNACCEPTABLE_LIFETIME 6
+#define WLAN_STATUS_NOT_IN_SAME_BSS 7
+#define WLAN_STATUS_CAPS_UNSUPPORTED 10
+#define WLAN_STATUS_REASSOC_NO_ASSOC 11
+#define WLAN_STATUS_ASSOC_DENIED_UNSPEC 12
+#define WLAN_STATUS_NOT_SUPPORTED_AUTH_ALG 13
+#define WLAN_STATUS_UNKNOWN_AUTH_TRANSACTION 14
+#define WLAN_STATUS_CHALLENGE_FAIL 15
+#define WLAN_STATUS_AUTH_TIMEOUT 16
+#define WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA 17
+#define WLAN_STATUS_ASSOC_DENIED_RATES 18
+/* IEEE 802.11b */
+#define WLAN_STATUS_ASSOC_DENIED_NOSHORT 19
+#define WLAN_STATUS_ASSOC_DENIED_NOPBCC 20
+#define WLAN_STATUS_ASSOC_DENIED_NOAGILITY 21
+/* IEEE 802.11h */
+#define WLAN_STATUS_SPEC_MGMT_REQUIRED 22
+#define WLAN_STATUS_PWR_CAPABILITY_NOT_VALID 23
+#define WLAN_STATUS_SUPPORTED_CHANNEL_NOT_VALID 24
+/* IEEE 802.11g */
+#define WLAN_STATUS_ASSOC_DENIED_NO_SHORT_SLOT_TIME 25
+#define WLAN_STATUS_ASSOC_DENIED_NO_DSSS_OFDM 26
+#define WLAN_STATUS_ASSOC_DENIED_NO_HT 27
+#define WLAN_STATUS_R0KH_UNREACHABLE 28
+#define WLAN_STATUS_ASSOC_DENIED_NO_PCO 29
+/* IEEE 802.11w */
+#define WLAN_STATUS_ASSOC_REJECTED_TEMPORARILY 30
+#define WLAN_STATUS_ROBUST_MGMT_FRAME_POLICY_VIOLATION 31
+#define WLAN_STATUS_UNSPECIFIED_QOS_FAILURE 32
+#define WLAN_STATUS_REQUEST_DECLINED 37
+#define WLAN_STATUS_INVALID_PARAMETERS 38
+/* IEEE 802.11i */
+#define WLAN_STATUS_INVALID_IE 40
+#define WLAN_STATUS_GROUP_CIPHER_NOT_VALID 41
+#define WLAN_STATUS_PAIRWISE_CIPHER_NOT_VALID 42
+#define WLAN_STATUS_AKMP_NOT_VALID 43
+#define WLAN_STATUS_UNSUPPORTED_RSN_IE_VERSION 44
+#define WLAN_STATUS_INVALID_RSN_IE_CAPAB 45
+#define WLAN_STATUS_CIPHER_REJECTED_PER_POLICY 46
+#define WLAN_STATUS_TS_NOT_CREATED 47
+#define WLAN_STATUS_DIRECT_LINK_NOT_ALLOWED 48
+#define WLAN_STATUS_DEST_STA_NOT_PRESENT 49
+#define WLAN_STATUS_DEST_STA_NOT_QOS_STA 50
+#define WLAN_STATUS_ASSOC_DENIED_LISTEN_INT_TOO_LARGE 51
+/* IEEE 802.11r */
+#define WLAN_STATUS_INVALID_FT_ACTION_FRAME_COUNT 52
+#define WLAN_STATUS_INVALID_PMKID 53
+#define WLAN_STATUS_INVALID_MDIE 54
+#define WLAN_STATUS_INVALID_FTIE 55
+#define WLAN_STATUS_GAS_ADV_PROTO_NOT_SUPPORTED 59
+#define WLAN_STATUS_NO_OUTSTANDING_GAS_REQ 60
+#define WLAN_STATUS_GAS_RESP_NOT_RECEIVED 61
+#define WLAN_STATUS_STA_TIMED_OUT_WAITING_FOR_GAS_RESP 62
+#define WLAN_STATUS_GAS_RESP_LARGER_THAN_LIMIT 63
+#define WLAN_STATUS_REQ_REFUSED_HOME 64
+#define WLAN_STATUS_ADV_SRV_UNREACHABLE 65
+#define WLAN_STATUS_REQ_REFUSED_SSPN 67
+#define WLAN_STATUS_REQ_REFUSED_UNAUTH_ACCESS 68
+#define WLAN_STATUS_INVALID_RSNIE 72
+#define WLAN_STATUS_ANTI_CLOGGING_TOKEN_REQ 76
+#define WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED 77
+#define WLAN_STATUS_TRANSMISSION_FAILURE 79
+#define WLAN_STATUS_REJECTED_WITH_SUGGESTED_BSS_TRANSITION 82
+#define WLAN_STATUS_PENDING_ADMITTING_FST_SESSION 86
+#define WLAN_STATUS_QUERY_RESP_OUTSTANDING 95
+#define WLAN_STATUS_DENIED_WITH_SUGGESTED_BAND_AND_CHANNEL 99
+#define WLAN_STATUS_ASSOC_DENIED_NO_VHT 104
+
+/* Reason codes (IEEE 802.11-2007, 7.3.1.7, Table 7-22) */
+#define WLAN_REASON_UNSPECIFIED 1
+#define WLAN_REASON_PREV_AUTH_NOT_VALID 2
+#define WLAN_REASON_DEAUTH_LEAVING 3
+#define WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY 4
+#define WLAN_REASON_DISASSOC_AP_BUSY 5
+#define WLAN_REASON_CLASS2_FRAME_FROM_NONAUTH_STA 6
+#define WLAN_REASON_CLASS3_FRAME_FROM_NONASSOC_STA 7
+#define WLAN_REASON_DISASSOC_STA_HAS_LEFT 8
+#define WLAN_REASON_STA_REQ_ASSOC_WITHOUT_AUTH 9
+/* IEEE 802.11h */
+#define WLAN_REASON_PWR_CAPABILITY_NOT_VALID 10
+#define WLAN_REASON_SUPPORTED_CHANNEL_NOT_VALID 11
+/* IEEE 802.11i */
+#define WLAN_REASON_INVALID_IE 13
+#define WLAN_REASON_MICHAEL_MIC_FAILURE 14
+#define WLAN_REASON_4WAY_HANDSHAKE_TIMEOUT 15
+#define WLAN_REASON_GROUP_KEY_UPDATE_TIMEOUT 16
+#define WLAN_REASON_IE_IN_4WAY_DIFFERS 17
+#define WLAN_REASON_GROUP_CIPHER_NOT_VALID 18
+#define WLAN_REASON_PAIRWISE_CIPHER_NOT_VALID 19
+#define WLAN_REASON_AKMP_NOT_VALID 20
+#define WLAN_REASON_UNSUPPORTED_RSN_IE_VERSION 21
+#define WLAN_REASON_INVALID_RSN_IE_CAPAB 22
+#define WLAN_REASON_IEEE_802_1X_AUTH_FAILED 23
+#define WLAN_REASON_CIPHER_SUITE_REJECTED 24
+#define WLAN_REASON_TDLS_TEARDOWN_UNREACHABLE 25
+#define WLAN_REASON_TDLS_TEARDOWN_UNSPECIFIED 26
+/* IEEE 802.11e */
+#define WLAN_REASON_DISASSOC_LOW_ACK 34
+/* IEEE 802.11s */
+#define WLAN_REASON_MESH_PEERING_CANCELLED 52
+#define WLAN_REASON_MESH_MAX_PEERS 53
+#define WLAN_REASON_MESH_CONFIG_POLICY_VIOLATION 54
+#define WLAN_REASON_MESH_CLOSE_RCVD 55
+#define WLAN_REASON_MESH_MAX_RETRIES 56
+#define WLAN_REASON_MESH_CONFIRM_TIMEOUT 57
+#define WLAN_REASON_MESH_INVALID_GTK 58
+#define WLAN_REASON_MESH_INCONSISTENT_PARAMS 59
+#define WLAN_REASON_MESH_INVALID_SECURITY_CAP 60
+
+
+/* Information Element IDs */
+#define WLAN_EID_SSID 0
+#define WLAN_EID_SUPP_RATES 1
+#define WLAN_EID_FH_PARAMS 2
+#define WLAN_EID_DS_PARAMS 3
+#define WLAN_EID_CF_PARAMS 4
+#define WLAN_EID_TIM 5
+#define WLAN_EID_IBSS_PARAMS 6
+#define WLAN_EID_COUNTRY 7
+#define WLAN_EID_BSS_LOAD 11
+#define WLAN_EID_CHALLENGE 16
+/* EIDs defined by IEEE 802.11h - START */
+#define WLAN_EID_PWR_CONSTRAINT 32
+#define WLAN_EID_PWR_CAPABILITY 33
+#define WLAN_EID_TPC_REQUEST 34
+#define WLAN_EID_TPC_REPORT 35
+#define WLAN_EID_SUPPORTED_CHANNELS 36
+#define WLAN_EID_CHANNEL_SWITCH 37
+#define WLAN_EID_MEASURE_REQUEST 38
+#define WLAN_EID_MEASURE_REPORT 39
+#define WLAN_EID_QUITE 40
+#define WLAN_EID_IBSS_DFS 41
+/* EIDs defined by IEEE 802.11h - END */
+#define WLAN_EID_ERP_INFO 42
+#define WLAN_EID_HT_CAP 45
+#define WLAN_EID_QOS 46
+#define WLAN_EID_RSN 48
+#define WLAN_EID_EXT_SUPP_RATES 50
+#define WLAN_EID_NEIGHBOR_REPORT 52
+#define WLAN_EID_MOBILITY_DOMAIN 54
+#define WLAN_EID_FAST_BSS_TRANSITION 55
+#define WLAN_EID_TIMEOUT_INTERVAL 56
+#define WLAN_EID_RIC_DATA 57
+#define WLAN_EID_SUPPORTED_OPERATING_CLASSES 59
+#define WLAN_EID_HT_OPERATION 61
+#define WLAN_EID_SECONDARY_CHANNEL_OFFSET 62
+#define WLAN_EID_WAPI 68
+#define WLAN_EID_TIME_ADVERTISEMENT 69
+#define WLAN_EID_RRM_ENABLED_CAPABILITIES 70
+#define WLAN_EID_20_40_BSS_COEXISTENCE 72
+#define WLAN_EID_20_40_BSS_INTOLERANT 73
+#define WLAN_EID_OVERLAPPING_BSS_SCAN_PARAMS 74
+#define WLAN_EID_MMIE 76
+#define WLAN_EID_SSID_LIST 84
+#define WLAN_EID_BSS_MAX_IDLE_PERIOD 90
+#define WLAN_EID_TFS_REQ 91
+#define WLAN_EID_TFS_RESP 92
+#define WLAN_EID_WNMSLEEP 93
+#define WLAN_EID_TIME_ZONE 98
+#define WLAN_EID_LINK_ID 101
+#define WLAN_EID_INTERWORKING 107
+#define WLAN_EID_ADV_PROTO 108
+#define WLAN_EID_QOS_MAP_SET 110
+#define WLAN_EID_ROAMING_CONSORTIUM 111
+#define WLAN_EID_MESH_CONFIG 113
+#define WLAN_EID_MESH_ID 114
+#define WLAN_EID_PEER_MGMT 117
+#define WLAN_EID_EXT_CAPAB 127
+#define WLAN_EID_AMPE 139
+#define WLAN_EID_MIC 140
+#define WLAN_EID_CCKM 156
+#define WLAN_EID_MULTI_BAND 158
+#define WLAN_EID_SESSION_TRANSITION 164
+#define WLAN_EID_VHT_CAP 191
+#define WLAN_EID_VHT_OPERATION 192
+#define WLAN_EID_VHT_EXTENDED_BSS_LOAD 193
+#define WLAN_EID_VHT_WIDE_BW_CHSWITCH 194
+#define WLAN_EID_VHT_TRANSMIT_POWER_ENVELOPE 195
+#define WLAN_EID_VHT_CHANNEL_SWITCH_WRAPPER 196
+#define WLAN_EID_VHT_AID 197
+#define WLAN_EID_VHT_QUIET_CHANNEL 198
+#define WLAN_EID_VHT_OPERATING_MODE_NOTIFICATION 199
+#define WLAN_EID_VENDOR_SPECIFIC 221
+
+
+/* Action frame categories (IEEE 802.11-2007, 7.3.1.11, Table 7-24) */
+#define WLAN_ACTION_SPECTRUM_MGMT 0
+#define WLAN_ACTION_QOS 1
+#define WLAN_ACTION_DLS 2
+#define WLAN_ACTION_BLOCK_ACK 3
+#define WLAN_ACTION_PUBLIC 4
+#define WLAN_ACTION_RADIO_MEASUREMENT 5
+#define WLAN_ACTION_FT 6
+#define WLAN_ACTION_HT 7
+#define WLAN_ACTION_SA_QUERY 8
+#define WLAN_ACTION_PROTECTED_DUAL 9
+#define WLAN_ACTION_WNM 10
+#define WLAN_ACTION_UNPROTECTED_WNM 11
+#define WLAN_ACTION_TDLS 12
+#define WLAN_ACTION_SELF_PROTECTED 15
+#define WLAN_ACTION_WMM 17 /* WMM Specification 1.1 */
+#define WLAN_ACTION_FST 18
+#define WLAN_ACTION_VENDOR_SPECIFIC 127
+
+/* Public action codes */
+#define WLAN_PA_20_40_BSS_COEX 0
+#define WLAN_PA_VENDOR_SPECIFIC 9
+#define WLAN_PA_GAS_INITIAL_REQ 10
+#define WLAN_PA_GAS_INITIAL_RESP 11
+#define WLAN_PA_GAS_COMEBACK_REQ 12
+#define WLAN_PA_GAS_COMEBACK_RESP 13
+#define WLAN_TDLS_DISCOVERY_RESPONSE 14
+
+/* Protected Dual of Public Action frames */
+#define WLAN_PROT_DSE_ENABLEMENT 1
+#define WLAN_PROT_DSE_DEENABLEMENT 2
+#define WLAN_PROT_EXT_CSA 4
+#define WLAN_PROT_MEASUREMENT_REQ 5
+#define WLAN_PROT_MEASUREMENT_REPORT 6
+#define WLAN_PROT_DSE_POWER_CONSTRAINT 8
+#define WLAN_PROT_VENDOR_SPECIFIC 9
+#define WLAN_PROT_GAS_INITIAL_REQ 10
+#define WLAN_PROT_GAS_INITIAL_RESP 11
+#define WLAN_PROT_GAS_COMEBACK_REQ 12
+#define WLAN_PROT_GAS_COMEBACK_RESP 13
+
+/* SA Query Action frame (IEEE 802.11w/D8.0, 7.4.9) */
+#define WLAN_SA_QUERY_REQUEST 0
+#define WLAN_SA_QUERY_RESPONSE 1
+
+#define WLAN_SA_QUERY_TR_ID_LEN 2
+
+/* TDLS action codes */
+#define WLAN_TDLS_SETUP_REQUEST 0
+#define WLAN_TDLS_SETUP_RESPONSE 1
+#define WLAN_TDLS_SETUP_CONFIRM 2
+#define WLAN_TDLS_TEARDOWN 3
+#define WLAN_TDLS_PEER_TRAFFIC_INDICATION 4
+#define WLAN_TDLS_CHANNEL_SWITCH_REQUEST 5
+#define WLAN_TDLS_CHANNEL_SWITCH_RESPONSE 6
+#define WLAN_TDLS_PEER_PSM_REQUEST 7
+#define WLAN_TDLS_PEER_PSM_RESPONSE 8
+#define WLAN_TDLS_PEER_TRAFFIC_RESPONSE 9
+#define WLAN_TDLS_DISCOVERY_REQUEST 10
+
+/* Radio Measurement Action codes */
+#define WLAN_RRM_RADIO_MEASUREMENT_REQUEST 0
+#define WLAN_RRM_RADIO_MEASUREMENT_REPORT 1
+#define WLAN_RRM_LINK_MEASUREMENT_REQUEST 2
+#define WLAN_RRM_LINK_MEASUREMENT_REPORT 3
+#define WLAN_RRM_NEIGHBOR_REPORT_REQUEST 4
+#define WLAN_RRM_NEIGHBOR_REPORT_RESPONSE 5
+
+/* Radio Measurement capabilities (from RRM Capabilities IE) */
+/* byte 1 (out of 5) */
+#define WLAN_RRM_CAPS_LINK_MEASUREMENT BIT(0)
+#define WLAN_RRM_CAPS_NEIGHBOR_REPORT BIT(1)
+
+/* Timeout Interval Type */
+#define WLAN_TIMEOUT_REASSOC_DEADLINE 1
+#define WLAN_TIMEOUT_KEY_LIFETIME 2
+#define WLAN_TIMEOUT_ASSOC_COMEBACK 3
+
+/* Interworking element (IEEE 802.11u) - Access Network Options */
+#define INTERWORKING_ANO_ACCESS_NETWORK_MASK 0x0f
+#define INTERWORKING_ANO_INTERNET 0x10
+#define INTERWORKING_ANO_ASRA 0x20
+#define INTERWORKING_ANO_ESR 0x40
+#define INTERWORKING_ANO_UESA 0x80
+
+#define INTERWORKING_ANT_PRIVATE 0
+#define INTERWORKING_ANT_PRIVATE_WITH_GUEST 1
+#define INTERWORKING_ANT_CHARGEABLE_PUBLIC 2
+#define INTERWORKING_ANT_FREE_PUBLIC 3
+#define INTERWORKING_ANT_PERSONAL_DEVICE 4
+#define INTERWORKING_ANT_EMERGENCY_SERVICES 5
+#define INTERWORKING_ANT_TEST 6
+#define INTERWORKING_ANT_WILDCARD 15
+
+/* Advertisement Protocol ID definitions (IEEE Std 802.11u-2011) */
+enum adv_proto_id {
+ ACCESS_NETWORK_QUERY_PROTOCOL = 0,
+ MIH_INFO_SERVICE = 1,
+ MIH_CMD_AND_EVENT_DISCOVERY = 2,
+ EMERGENCY_ALERT_SYSTEM = 3,
+ ADV_PROTO_VENDOR_SPECIFIC = 221
+};
+
+/* Access Network Query Protocol info ID definitions (IEEE Std 802.11u-2011) */
+enum anqp_info_id {
+ ANQP_QUERY_LIST = 256,
+ ANQP_CAPABILITY_LIST = 257,
+ ANQP_VENUE_NAME = 258,
+ ANQP_EMERGENCY_CALL_NUMBER = 259,
+ ANQP_NETWORK_AUTH_TYPE = 260,
+ ANQP_ROAMING_CONSORTIUM = 261,
+ ANQP_IP_ADDR_TYPE_AVAILABILITY = 262,
+ ANQP_NAI_REALM = 263,
+ ANQP_3GPP_CELLULAR_NETWORK = 264,
+ ANQP_AP_GEOSPATIAL_LOCATION = 265,
+ ANQP_AP_CIVIC_LOCATION = 266,
+ ANQP_AP_LOCATION_PUBLIC_URI = 267,
+ ANQP_DOMAIN_NAME = 268,
+ ANQP_EMERGENCY_ALERT_URI = 269,
+ ANQP_EMERGENCY_NAI = 271,
+ ANQP_VENDOR_SPECIFIC = 56797
+};
+
+/* NAI Realm list - EAP Method subfield - Authentication Parameter ID */
+enum nai_realm_eap_auth_param {
+ NAI_REALM_EAP_AUTH_EXPANDED_EAP_METHOD = 1,
+ NAI_REALM_EAP_AUTH_NON_EAP_INNER_AUTH = 2,
+ NAI_REALM_EAP_AUTH_INNER_AUTH_EAP_METHOD = 3,
+ NAI_REALM_EAP_AUTH_EXPANDED_INNER_EAP_METHOD = 4,
+ NAI_REALM_EAP_AUTH_CRED_TYPE = 5,
+ NAI_REALM_EAP_AUTH_TUNNELED_CRED_TYPE = 6,
+ NAI_REALM_EAP_AUTH_VENDOR_SPECIFIC = 221
+};
+
+enum nai_realm_eap_auth_inner_non_eap {
+ NAI_REALM_INNER_NON_EAP_PAP = 1,
+ NAI_REALM_INNER_NON_EAP_CHAP = 2,
+ NAI_REALM_INNER_NON_EAP_MSCHAP = 3,
+ NAI_REALM_INNER_NON_EAP_MSCHAPV2 = 4
+};
+
+enum nai_realm_eap_cred_type {
+ NAI_REALM_CRED_TYPE_SIM = 1,
+ NAI_REALM_CRED_TYPE_USIM = 2,
+ NAI_REALM_CRED_TYPE_NFC_SECURE_ELEMENT = 3,
+ NAI_REALM_CRED_TYPE_HARDWARE_TOKEN = 4,
+ NAI_REALM_CRED_TYPE_SOFTOKEN = 5,
+ NAI_REALM_CRED_TYPE_CERTIFICATE = 6,
+ NAI_REALM_CRED_TYPE_USERNAME_PASSWORD = 7,
+ NAI_REALM_CRED_TYPE_NONE = 8,
+ NAI_REALM_CRED_TYPE_ANONYMOUS = 9,
+ NAI_REALM_CRED_TYPE_VENDOR_SPECIFIC = 10
+};
+
+#ifdef _MSC_VER
+#pragma pack(push, 1)
+#endif /* _MSC_VER */
+
+struct ieee80211_hdr {
+ le16 frame_control;
+ le16 duration_id;
+ u8 addr1[6];
+ u8 addr2[6];
+ u8 addr3[6];
+ le16 seq_ctrl;
+ /* followed by 'u8 addr4[6];' if ToDS and FromDS is set in data frame
+ */
+} STRUCT_PACKED;
+
+#define IEEE80211_DA_FROMDS addr1
+#define IEEE80211_BSSID_FROMDS addr2
+#define IEEE80211_SA_FROMDS addr3
+
+#define IEEE80211_HDRLEN (sizeof(struct ieee80211_hdr))
+
+#define IEEE80211_FC(type, stype) host_to_le16((type << 2) | (stype << 4))
+
+struct ieee80211_mgmt {
+ le16 frame_control;
+ le16 duration;
+ u8 da[6];
+ u8 sa[6];
+ u8 bssid[6];
+ le16 seq_ctrl;
+ union {
+ struct {
+ le16 auth_alg;
+ le16 auth_transaction;
+ le16 status_code;
+ /* possibly followed by Challenge text */
+ u8 variable[];
+ } STRUCT_PACKED auth;
+ struct {
+ le16 reason_code;
+ u8 variable[];
+ } STRUCT_PACKED deauth;
+ struct {
+ le16 capab_info;
+ le16 listen_interval;
+ /* followed by SSID and Supported rates */
+ u8 variable[];
+ } STRUCT_PACKED assoc_req;
+ struct {
+ le16 capab_info;
+ le16 status_code;
+ le16 aid;
+ /* followed by Supported rates */
+ u8 variable[];
+ } STRUCT_PACKED assoc_resp, reassoc_resp;
+ struct {
+ le16 capab_info;
+ le16 listen_interval;
+ u8 current_ap[6];
+ /* followed by SSID and Supported rates */
+ u8 variable[];
+ } STRUCT_PACKED reassoc_req;
+ struct {
+ le16 reason_code;
+ u8 variable[];
+ } STRUCT_PACKED disassoc;
+ struct {
+ u8 timestamp[8];
+ le16 beacon_int;
+ le16 capab_info;
+ /* followed by some of SSID, Supported rates,
+ * FH Params, DS Params, CF Params, IBSS Params, TIM */
+ u8 variable[];
+ } STRUCT_PACKED beacon;
+ struct {
+ /* only variable items: SSID, Supported rates */
+ u8 variable[0];
+ } STRUCT_PACKED probe_req;
+ struct {
+ u8 timestamp[8];
+ le16 beacon_int;
+ le16 capab_info;
+ /* followed by some of SSID, Supported rates,
+ * FH Params, DS Params, CF Params, IBSS Params */
+ u8 variable[];
+ } STRUCT_PACKED probe_resp;
+ struct {
+ u8 category;
+ union {
+ struct {
+ u8 action_code;
+ u8 dialog_token;
+ u8 status_code;
+ u8 variable[];
+ } STRUCT_PACKED wmm_action;
+ struct{
+ u8 action_code;
+ u8 element_id;
+ u8 length;
+ u8 switch_mode;
+ u8 new_chan;
+ u8 switch_count;
+ } STRUCT_PACKED chan_switch;
+ struct {
+ u8 action;
+ u8 sta_addr[ETH_ALEN];
+ u8 target_ap_addr[ETH_ALEN];
+ u8 variable[]; /* FT Request */
+ } STRUCT_PACKED ft_action_req;
+ struct {
+ u8 action;
+ u8 sta_addr[ETH_ALEN];
+ u8 target_ap_addr[ETH_ALEN];
+ le16 status_code;
+ u8 variable[]; /* FT Request */
+ } STRUCT_PACKED ft_action_resp;
+ struct {
+ u8 action;
+ u8 trans_id[WLAN_SA_QUERY_TR_ID_LEN];
+ } STRUCT_PACKED sa_query_req;
+ struct {
+ u8 action; /* */
+ u8 trans_id[WLAN_SA_QUERY_TR_ID_LEN];
+ } STRUCT_PACKED sa_query_resp;
+ struct {
+ u8 action;
+ u8 dialogtoken;
+ u8 variable[];
+ } STRUCT_PACKED wnm_sleep_req;
+ struct {
+ u8 action;
+ u8 dialogtoken;
+ le16 keydata_len;
+ u8 variable[];
+ } STRUCT_PACKED wnm_sleep_resp;
+ struct {
+ u8 action;
+ u8 variable[];
+ } STRUCT_PACKED public_action;
+ struct {
+ u8 action; /* 9 */
+ u8 oui[3];
+ /* Vendor-specific content */
+ u8 variable[];
+ } STRUCT_PACKED vs_public_action;
+ struct {
+ u8 action; /* 7 */
+ u8 dialog_token;
+ u8 req_mode;
+ le16 disassoc_timer;
+ u8 validity_interval;
+ /* BSS Termination Duration (optional),
+ * Session Information URL (optional),
+ * BSS Transition Candidate List
+ * Entries */
+ u8 variable[];
+ } STRUCT_PACKED bss_tm_req;
+ struct {
+ u8 action; /* 8 */
+ u8 dialog_token;
+ u8 status_code;
+ u8 bss_termination_delay;
+ /* Target BSSID (optional),
+ * BSS Transition Candidate List
+ * Entries (optional) */
+ u8 variable[];
+ } STRUCT_PACKED bss_tm_resp;
+ struct {
+ u8 action; /* 6 */
+ u8 dialog_token;
+ u8 query_reason;
+ /* BSS Transition Candidate List
+ * Entries (optional) */
+ u8 variable[];
+ } STRUCT_PACKED bss_tm_query;
+ struct {
+ u8 action; /* 15 */
+ u8 variable[];
+ } STRUCT_PACKED slf_prot_action;
+ struct {
+ u8 action;
+ u8 variable[];
+ } STRUCT_PACKED fst_action;
+ } u;
+ } STRUCT_PACKED action;
+ } u;
+} STRUCT_PACKED;
+
+
+/* Rx MCS bitmask is in the first 77 bits of supported_mcs_set */
+#define IEEE80211_HT_MCS_MASK_LEN 10
+
+/* HT Capabilities element */
+struct ieee80211_ht_capabilities {
+ le16 ht_capabilities_info;
+ u8 a_mpdu_params; /* Maximum A-MPDU Length Exponent B0..B1
+ * Minimum MPDU Start Spacing B2..B4
+ * Reserved B5..B7 */
+ u8 supported_mcs_set[16];
+ le16 ht_extended_capabilities;
+ le32 tx_bf_capability_info;
+ u8 asel_capabilities;
+} STRUCT_PACKED;
+
+
+/* HT Operation element */
+struct ieee80211_ht_operation {
+ u8 primary_chan;
+ /* Five octets of HT Operation Information */
+ u8 ht_param; /* B0..B7 */
+ le16 operation_mode; /* B8..B23 */
+ le16 param; /* B24..B39 */
+ u8 basic_mcs_set[16];
+} STRUCT_PACKED;
+
+
+struct ieee80211_obss_scan_parameters {
+ le16 scan_passive_dwell;
+ le16 scan_active_dwell;
+ le16 width_trigger_scan_interval;
+ le16 scan_passive_total_per_channel;
+ le16 scan_active_total_per_channel;
+ le16 channel_transition_delay_factor;
+ le16 scan_activity_threshold;
+} STRUCT_PACKED;
+
+
+struct ieee80211_vht_capabilities {
+ le32 vht_capabilities_info;
+ struct {
+ le16 rx_map;
+ le16 rx_highest;
+ le16 tx_map;
+ le16 tx_highest;
+ } vht_supported_mcs_set;
+} STRUCT_PACKED;
+
+struct ieee80211_vht_operation {
+ u8 vht_op_info_chwidth;
+ u8 vht_op_info_chan_center_freq_seg0_idx;
+ u8 vht_op_info_chan_center_freq_seg1_idx;
+ le16 vht_basic_mcs_set;
+} STRUCT_PACKED;
+
+struct ieee80211_ampe_ie {
+ u8 selected_pairwise_suite[4];
+ u8 local_nonce[32];
+ u8 peer_nonce[32];
+ u8 mgtk[16];
+ u8 key_rsc[8];
+ u8 key_expiration[4];
+} STRUCT_PACKED;
+
+#ifdef _MSC_VER
+#pragma pack(pop)
+#endif /* _MSC_VER */
+
+#define ERP_INFO_NON_ERP_PRESENT BIT(0)
+#define ERP_INFO_USE_PROTECTION BIT(1)
+#define ERP_INFO_BARKER_PREAMBLE_MODE BIT(2)
+
+#define OVERLAPPING_BSS_TRANS_DELAY_FACTOR 5
+
+/* HT Capabilities Info field within HT Capabilities element */
+#define HT_CAP_INFO_LDPC_CODING_CAP ((u16) BIT(0))
+#define HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET ((u16) BIT(1))
+#define HT_CAP_INFO_SMPS_MASK ((u16) (BIT(2) | BIT(3)))
+#define HT_CAP_INFO_SMPS_STATIC ((u16) 0)
+#define HT_CAP_INFO_SMPS_DYNAMIC ((u16) BIT(2))
+#define HT_CAP_INFO_SMPS_DISABLED ((u16) (BIT(2) | BIT(3)))
+#define HT_CAP_INFO_GREEN_FIELD ((u16) BIT(4))
+#define HT_CAP_INFO_SHORT_GI20MHZ ((u16) BIT(5))
+#define HT_CAP_INFO_SHORT_GI40MHZ ((u16) BIT(6))
+#define HT_CAP_INFO_TX_STBC ((u16) BIT(7))
+#define HT_CAP_INFO_RX_STBC_MASK ((u16) (BIT(8) | BIT(9)))
+#define HT_CAP_INFO_RX_STBC_1 ((u16) BIT(8))
+#define HT_CAP_INFO_RX_STBC_12 ((u16) BIT(9))
+#define HT_CAP_INFO_RX_STBC_123 ((u16) (BIT(8) | BIT(9)))
+#define HT_CAP_INFO_DELAYED_BA ((u16) BIT(10))
+#define HT_CAP_INFO_MAX_AMSDU_SIZE ((u16) BIT(11))
+#define HT_CAP_INFO_DSSS_CCK40MHZ ((u16) BIT(12))
+/* B13 - Reserved (was PSMP support during P802.11n development) */
+#define HT_CAP_INFO_40MHZ_INTOLERANT ((u16) BIT(14))
+#define HT_CAP_INFO_LSIG_TXOP_PROTECT_SUPPORT ((u16) BIT(15))
+
+/* HT Extended Capabilities field within HT Capabilities element */
+#define EXT_HT_CAP_INFO_PCO ((u16) BIT(0))
+#define EXT_HT_CAP_INFO_PCO_TRANS_TIME_MASK ((u16) (BIT(1) | BIT(2)))
+#define EXT_HT_CAP_INFO_TRANS_TIME_OFFSET 1
+/* B3..B7 - Reserved */
+#define EXT_HT_CAP_INFO_MCS_FEEDBACK_MASK ((u16) (BIT(8) | BIT(9)))
+#define EXT_HT_CAP_INFO_MCS_FEEDBACK_OFFSET 8
+#define EXT_HT_CAP_INFO_HTC_SUPPORT ((u16) BIT(10))
+#define EXT_HT_CAP_INFO_RD_RESPONDER ((u16) BIT(11))
+/* B12..B15 - Reserved */
+
+/* Transmit Beanforming Capabilities within HT Capabilities element */
+#define TX_BF_CAP_IMPLICIT_TXBF_RX_CAP ((u32) BIT(0))
+#define TX_BF_CAP_RX_STAGGERED_SOUNDING_CAP ((u32) BIT(1))
+#define TX_BF_CAP_TX_STAGGERED_SOUNDING_CAP ((u32) BIT(2))
+#define TX_BF_CAP_RX_NDP_CAP ((u32) BIT(3))
+#define TX_BF_CAP_TX_NDP_CAP ((u32) BIT(4))
+#define TX_BF_CAP_IMPLICIT_TX_BF_CAP ((u32) BIT(5))
+#define TX_BF_CAP_CALIBRATION_MASK ((u32) (BIT(6) | BIT(7))
+#define TX_BF_CAP_CALIB_OFFSET 6
+#define TX_BF_CAP_EXPLICIT_CSI_TXBF_CAP ((u32) BIT(8))
+#define TX_BF_CAP_EXPLICIT_NONCOMPR_STEERING_CAP ((u32) BIT(9))
+#define TX_BF_CAP_EXPLICIT_COMPR_STEERING_CAP ((u32) BIT(10))
+#define TX_BF_CAP_EXPLICIT_TX_BF_CSI_FEEDBACK_MASK ((u32) (BIT(10) | BIT(11)))
+#define TX_BF_CAP_EXPLICIT_BF_CSI_FEEDBACK_OFFSET 11
+#define TX_BF_CAP_EXPLICIT_UNCOMPR_STEERING_MATRIX_FEEDBACK_OFFSET 13
+#define TX_BF_CAP_EXPLICIT_COMPRESSED_STEERING_MATRIX_FEEDBACK_OFFSET 15
+#define TX_BF_CAP_MINIMAL_GROUPING_OFFSET 17
+#define TX_BF_CAP_CSI_NUM_BEAMFORMER_ANT_OFFSET 19
+#define TX_BF_CAP_UNCOMPRESSED_STEERING_MATRIX_BEAMFORMER_ANT_OFFSET 21
+#define TX_BF_CAP_COMPRESSED_STEERING_MATRIX_BEAMFORMER_ANT_OFFSET 23
+#define TX_BF_CAP_SCI_MAX_OF_ROWS_BEANFORMER_SUPPORTED_OFFSET 25
+#define TX_BF_CAP_CHANNEL_ESTIMATION_CAP_MASK ((u32) (BIT(27) | BIT(28)))
+#define TX_BF_CAP_CHANNEL_ESTIMATION_CAP_OFFSET 27
+/* B29..B31 - Reserved */
+
+/* ASEL Capability field within HT Capabilities element */
+#define ASEL_CAP_ASEL_CAPABLE ((u8) BIT(0))
+#define ASEL_CAP_EXPLICIT_CSI_FEEDBACK_BASED_TX_AS_CAP ((u8) BIT(1))
+#define ASEL_CAP_ANT_INDICES_FEEDBACK_BASED_TX_AS_CAP ((u8) BIT(2))
+#define ASEL_CAP_EXPLICIT_CSI_FEEDBACK_CAP ((u8) BIT(3))
+#define ASEL_CAP_ANT_INDICES_FEEDBACK_CAP ((u8) BIT(4))
+#define ASEL_CAP_RX_AS_CAP ((u8) BIT(5))
+#define ASEL_CAP_TX_SOUNDING_PPDUS_CAP ((u8) BIT(6))
+/* B7 - Reserved */
+
+/* First octet of HT Operation Information within HT Operation element */
+#define HT_INFO_HT_PARAM_SECONDARY_CHNL_OFF_MASK ((u8) BIT(0) | BIT(1))
+#define HT_INFO_HT_PARAM_SECONDARY_CHNL_ABOVE ((u8) BIT(0))
+#define HT_INFO_HT_PARAM_SECONDARY_CHNL_BELOW ((u8) BIT(0) | BIT(1))
+#define HT_INFO_HT_PARAM_STA_CHNL_WIDTH ((u8) BIT(2))
+#define HT_INFO_HT_PARAM_RIFS_MODE ((u8) BIT(3))
+/* B4..B7 - Reserved */
+
+/* HT Protection (B8..B9 of HT Operation Information) */
+#define HT_PROT_NO_PROTECTION 0
+#define HT_PROT_NONMEMBER_PROTECTION 1
+#define HT_PROT_20MHZ_PROTECTION 2
+#define HT_PROT_NON_HT_MIXED 3
+/* Bits within ieee80211_ht_operation::operation_mode (BIT(0) maps to B8 in
+ * HT Operation Information) */
+#define HT_OPER_OP_MODE_HT_PROT_MASK ((u16) (BIT(0) | BIT(1))) /* B8..B9 */
+#define HT_OPER_OP_MODE_NON_GF_HT_STAS_PRESENT ((u16) BIT(2)) /* B10 */
+/* BIT(3), i.e., B11 in HT Operation Information field - Reserved */
+#define HT_OPER_OP_MODE_OBSS_NON_HT_STAS_PRESENT ((u16) BIT(4)) /* B12 */
+/* BIT(5)..BIT(15), i.e., B13..B23 - Reserved */
+
+/* Last two octets of HT Operation Information (BIT(0) = B24) */
+/* B24..B29 - Reserved */
+#define HT_OPER_PARAM_DUAL_BEACON ((u16) BIT(6))
+#define HT_OPER_PARAM_DUAL_CTS_PROTECTION ((u16) BIT(7))
+#define HT_OPER_PARAM_STBC_BEACON ((u16) BIT(8))
+#define HT_OPER_PARAM_LSIG_TXOP_PROT_FULL_SUPP ((u16) BIT(9))
+#define HT_OPER_PARAM_PCO_ACTIVE ((u16) BIT(10))
+#define HT_OPER_PARAM_PCO_PHASE ((u16) BIT(11))
+/* B36..B39 - Reserved */
+
+#define BSS_MEMBERSHIP_SELECTOR_VHT_PHY 126
+#define BSS_MEMBERSHIP_SELECTOR_HT_PHY 127
+
+/* VHT Defines */
+#define VHT_CAP_MAX_MPDU_LENGTH_7991 ((u32) BIT(0))
+#define VHT_CAP_MAX_MPDU_LENGTH_11454 ((u32) BIT(1))
+#define VHT_CAP_MAX_MPDU_LENGTH_MASK ((u32) BIT(0) | BIT(1))
+#define VHT_CAP_MAX_MPDU_LENGTH_MASK_SHIFT 0
+#define VHT_CAP_SUPP_CHAN_WIDTH_160MHZ ((u32) BIT(2))
+#define VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ ((u32) BIT(3))
+#define VHT_CAP_SUPP_CHAN_WIDTH_MASK ((u32) BIT(2) | BIT(3))
+#define VHT_CAP_RXLDPC ((u32) BIT(4))
+#define VHT_CAP_SHORT_GI_80 ((u32) BIT(5))
+#define VHT_CAP_SHORT_GI_160 ((u32) BIT(6))
+#define VHT_CAP_TXSTBC ((u32) BIT(7))
+#define VHT_CAP_RXSTBC_1 ((u32) BIT(8))
+#define VHT_CAP_RXSTBC_2 ((u32) BIT(9))
+#define VHT_CAP_RXSTBC_3 ((u32) BIT(8) | BIT(9))
+#define VHT_CAP_RXSTBC_4 ((u32) BIT(10))
+#define VHT_CAP_RXSTBC_MASK ((u32) BIT(8) | BIT(9) | \
+ BIT(10))
+#define VHT_CAP_RXSTBC_MASK_SHIFT 8
+#define VHT_CAP_SU_BEAMFORMER_CAPABLE ((u32) BIT(11))
+#define VHT_CAP_SU_BEAMFORMEE_CAPABLE ((u32) BIT(12))
+#define VHT_CAP_BEAMFORMEE_STS_MAX ((u32) BIT(13) | \
+ BIT(14) | BIT(15))
+#define VHT_CAP_BEAMFORMEE_STS_MAX_SHIFT 13
+#define VHT_CAP_BEAMFORMEE_STS_OFFSET 13
+#define VHT_CAP_SOUNDING_DIMENSION_MAX ((u32) BIT(16) | \
+ BIT(17) | BIT(18))
+#define VHT_CAP_SOUNDING_DIMENSION_MAX_SHIFT 16
+#define VHT_CAP_SOUNDING_DIMENSION_OFFSET 16
+#define VHT_CAP_MU_BEAMFORMER_CAPABLE ((u32) BIT(19))
+#define VHT_CAP_MU_BEAMFORMEE_CAPABLE ((u32) BIT(20))
+#define VHT_CAP_VHT_TXOP_PS ((u32) BIT(21))
+#define VHT_CAP_HTC_VHT ((u32) BIT(22))
+
+#define VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_1 ((u32) BIT(23))
+#define VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_2 ((u32) BIT(24))
+#define VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_3 ((u32) BIT(23) | BIT(24))
+#define VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_4 ((u32) BIT(25))
+#define VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_5 ((u32) BIT(23) | BIT(25))
+#define VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_6 ((u32) BIT(24) | BIT(25))
+#define VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MAX ((u32) BIT(23) | \
+ BIT(24) | BIT(25))
+#define VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MAX_SHIFT 23
+#define VHT_CAP_VHT_LINK_ADAPTATION_VHT_UNSOL_MFB ((u32) BIT(27))
+#define VHT_CAP_VHT_LINK_ADAPTATION_VHT_MRQ_MFB ((u32) BIT(26) | BIT(27))
+#define VHT_CAP_RX_ANTENNA_PATTERN ((u32) BIT(28))
+#define VHT_CAP_TX_ANTENNA_PATTERN ((u32) BIT(29))
+
+#define VHT_OPMODE_CHANNEL_WIDTH_MASK ((u8) BIT(0) | BIT(1))
+#define VHT_OPMODE_CHANNEL_RxNSS_MASK ((u8) BIT(4) | BIT(5) | \
+ BIT(6))
+#define VHT_OPMODE_NOTIF_RX_NSS_SHIFT 4
+
+#define VHT_RX_NSS_MAX_STREAMS 8
+
+/* VHT channel widths */
+#define VHT_CHANWIDTH_USE_HT 0
+#define VHT_CHANWIDTH_80MHZ 1
+#define VHT_CHANWIDTH_160MHZ 2
+#define VHT_CHANWIDTH_80P80MHZ 3
+
+#define OUI_MICROSOFT 0x0050f2 /* Microsoft (also used in Wi-Fi specs)
+ * 00:50:F2 */
+#define WPA_IE_VENDOR_TYPE 0x0050f201
+#define WMM_IE_VENDOR_TYPE 0x0050f202
+#define WPS_IE_VENDOR_TYPE 0x0050f204
+#define OUI_WFA 0x506f9a
+#define P2P_IE_VENDOR_TYPE 0x506f9a09
+#define WFD_IE_VENDOR_TYPE 0x506f9a0a
+#define WFD_OUI_TYPE 10
+#define HS20_IE_VENDOR_TYPE 0x506f9a10
+#define OSEN_IE_VENDOR_TYPE 0x506f9a12
+
+#define WMM_OUI_TYPE 2
+#define WMM_OUI_SUBTYPE_INFORMATION_ELEMENT 0
+#define WMM_OUI_SUBTYPE_PARAMETER_ELEMENT 1
+#define WMM_OUI_SUBTYPE_TSPEC_ELEMENT 2
+#define WMM_VERSION 1
+
+#define WMM_ACTION_CODE_ADDTS_REQ 0
+#define WMM_ACTION_CODE_ADDTS_RESP 1
+#define WMM_ACTION_CODE_DELTS 2
+
+#define WMM_ADDTS_STATUS_ADMISSION_ACCEPTED 0
+#define WMM_ADDTS_STATUS_INVALID_PARAMETERS 1
+/* 2 - Reserved */
+#define WMM_ADDTS_STATUS_REFUSED 3
+/* 4-255 - Reserved */
+
+/* WMM TSPEC Direction Field Values */
+#define WMM_TSPEC_DIRECTION_UPLINK 0
+#define WMM_TSPEC_DIRECTION_DOWNLINK 1
+/* 2 - Reserved */
+#define WMM_TSPEC_DIRECTION_BI_DIRECTIONAL 3
+
+/*
+ * WMM Information Element (used in (Re)Association Request frames; may also be
+ * used in Beacon frames)
+ */
+struct wmm_information_element {
+ /* Element ID: 221 (0xdd); Length: 7 */
+ /* required fields for WMM version 1 */
+ u8 oui[3]; /* 00:50:f2 */
+ u8 oui_type; /* 2 */
+ u8 oui_subtype; /* 0 */
+ u8 version; /* 1 for WMM version 1.0 */
+ u8 qos_info; /* AP/STA specific QoS info */
+
+} STRUCT_PACKED;
+
+#define WMM_QOSINFO_AP_UAPSD 0x80
+
+#define WMM_QOSINFO_STA_AC_MASK 0x0f
+#define WMM_QOSINFO_STA_SP_MASK 0x03
+#define WMM_QOSINFO_STA_SP_SHIFT 5
+
+#define WMM_AC_AIFSN_MASK 0x0f
+#define WMM_AC_AIFNS_SHIFT 0
+#define WMM_AC_ACM 0x10
+#define WMM_AC_ACI_MASK 0x60
+#define WMM_AC_ACI_SHIFT 5
+
+#define WMM_AC_ECWMIN_MASK 0x0f
+#define WMM_AC_ECWMIN_SHIFT 0
+#define WMM_AC_ECWMAX_MASK 0xf0
+#define WMM_AC_ECWMAX_SHIFT 4
+
+struct wmm_ac_parameter {
+ u8 aci_aifsn; /* AIFSN, ACM, ACI */
+ u8 cw; /* ECWmin, ECWmax (CW = 2^ECW - 1) */
+ le16 txop_limit;
+} STRUCT_PACKED;
+
+/*
+ * WMM Parameter Element (used in Beacon, Probe Response, and (Re)Association
+ * Response frmaes)
+ */
+struct wmm_parameter_element {
+ /* Element ID: 221 (0xdd); Length: 24 */
+ /* required fields for WMM version 1 */
+ u8 oui[3]; /* 00:50:f2 */
+ u8 oui_type; /* 2 */
+ u8 oui_subtype; /* 1 */
+ u8 version; /* 1 for WMM version 1.0 */
+ u8 qos_info; /* AP/STA specific QoS info */
+ u8 reserved; /* 0 */
+ struct wmm_ac_parameter ac[4]; /* AC_BE, AC_BK, AC_VI, AC_VO */
+
+} STRUCT_PACKED;
+
+/* WMM TSPEC Element */
+struct wmm_tspec_element {
+ u8 eid; /* 221 = 0xdd */
+ u8 length; /* 6 + 55 = 61 */
+ u8 oui[3]; /* 00:50:f2 */
+ u8 oui_type; /* 2 */
+ u8 oui_subtype; /* 2 */
+ u8 version; /* 1 */
+ /* WMM TSPEC body (55 octets): */
+ u8 ts_info[3];
+ le16 nominal_msdu_size;
+ le16 maximum_msdu_size;
+ le32 minimum_service_interval;
+ le32 maximum_service_interval;
+ le32 inactivity_interval;
+ le32 suspension_interval;
+ le32 service_start_time;
+ le32 minimum_data_rate;
+ le32 mean_data_rate;
+ le32 peak_data_rate;
+ le32 maximum_burst_size;
+ le32 delay_bound;
+ le32 minimum_phy_rate;
+ le16 surplus_bandwidth_allowance;
+ le16 medium_time;
+} STRUCT_PACKED;
+
+
+/* Access Categories / ACI to AC coding */
+enum wmm_ac {
+ WMM_AC_BE = 0 /* Best Effort */,
+ WMM_AC_BK = 1 /* Background */,
+ WMM_AC_VI = 2 /* Video */,
+ WMM_AC_VO = 3 /* Voice */,
+ WMM_AC_NUM = 4
+};
+
+
+#define HS20_INDICATION_OUI_TYPE 16
+#define HS20_ANQP_OUI_TYPE 17
+#define HS20_OSEN_OUI_TYPE 18
+#define HS20_STYPE_QUERY_LIST 1
+#define HS20_STYPE_CAPABILITY_LIST 2
+#define HS20_STYPE_OPERATOR_FRIENDLY_NAME 3
+#define HS20_STYPE_WAN_METRICS 4
+#define HS20_STYPE_CONNECTION_CAPABILITY 5
+#define HS20_STYPE_NAI_HOME_REALM_QUERY 6
+#define HS20_STYPE_OPERATING_CLASS 7
+#define HS20_STYPE_OSU_PROVIDERS_LIST 8
+#define HS20_STYPE_ICON_REQUEST 10
+#define HS20_STYPE_ICON_BINARY_FILE 11
+
+#define HS20_DGAF_DISABLED 0x01
+#define HS20_PPS_MO_ID_PRESENT 0x02
+#define HS20_ANQP_DOMAIN_ID_PRESENT 0x04
+#define HS20_VERSION 0x10 /* Release 2 */
+
+/* WNM-Notification WFA vendors specific subtypes */
+#define HS20_WNM_SUB_REM_NEEDED 0
+#define HS20_WNM_DEAUTH_IMMINENT_NOTICE 1
+
+#define HS20_DEAUTH_REASON_CODE_BSS 0
+#define HS20_DEAUTH_REASON_CODE_ESS 1
+
+/* Wi-Fi Direct (P2P) */
+
+#define P2P_OUI_TYPE 9
+
+enum p2p_attr_id {
+ P2P_ATTR_STATUS = 0,
+ P2P_ATTR_MINOR_REASON_CODE = 1,
+ P2P_ATTR_CAPABILITY = 2,
+ P2P_ATTR_DEVICE_ID = 3,
+ P2P_ATTR_GROUP_OWNER_INTENT = 4,
+ P2P_ATTR_CONFIGURATION_TIMEOUT = 5,
+ P2P_ATTR_LISTEN_CHANNEL = 6,
+ P2P_ATTR_GROUP_BSSID = 7,
+ P2P_ATTR_EXT_LISTEN_TIMING = 8,
+ P2P_ATTR_INTENDED_INTERFACE_ADDR = 9,
+ P2P_ATTR_MANAGEABILITY = 10,
+ P2P_ATTR_CHANNEL_LIST = 11,
+ P2P_ATTR_NOTICE_OF_ABSENCE = 12,
+ P2P_ATTR_DEVICE_INFO = 13,
+ P2P_ATTR_GROUP_INFO = 14,
+ P2P_ATTR_GROUP_ID = 15,
+ P2P_ATTR_INTERFACE = 16,
+ P2P_ATTR_OPERATING_CHANNEL = 17,
+ P2P_ATTR_INVITATION_FLAGS = 18,
+ P2P_ATTR_OOB_GO_NEG_CHANNEL = 19,
+ P2P_ATTR_SERVICE_HASH = 21,
+ P2P_ATTR_SESSION_INFORMATION_DATA = 22,
+ P2P_ATTR_CONNECTION_CAPABILITY = 23,
+ P2P_ATTR_ADVERTISEMENT_ID = 24,
+ P2P_ATTR_ADVERTISED_SERVICE = 25,
+ P2P_ATTR_SESSION_ID = 26,
+ P2P_ATTR_FEATURE_CAPABILITY = 27,
+ P2P_ATTR_PERSISTENT_GROUP = 28,
+ P2P_ATTR_VENDOR_SPECIFIC = 221
+};
+
+#define P2P_MAX_GO_INTENT 15
+
+/* P2P Capability - Device Capability bitmap */
+#define P2P_DEV_CAPAB_SERVICE_DISCOVERY BIT(0)
+#define P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY BIT(1)
+#define P2P_DEV_CAPAB_CONCURRENT_OPER BIT(2)
+#define P2P_DEV_CAPAB_INFRA_MANAGED BIT(3)
+#define P2P_DEV_CAPAB_DEVICE_LIMIT BIT(4)
+#define P2P_DEV_CAPAB_INVITATION_PROCEDURE BIT(5)
+
+/* P2P Capability - Group Capability bitmap */
+#define P2P_GROUP_CAPAB_GROUP_OWNER BIT(0)
+#define P2P_GROUP_CAPAB_PERSISTENT_GROUP BIT(1)
+#define P2P_GROUP_CAPAB_GROUP_LIMIT BIT(2)
+#define P2P_GROUP_CAPAB_INTRA_BSS_DIST BIT(3)
+#define P2P_GROUP_CAPAB_CROSS_CONN BIT(4)
+#define P2P_GROUP_CAPAB_PERSISTENT_RECONN BIT(5)
+#define P2P_GROUP_CAPAB_GROUP_FORMATION BIT(6)
+#define P2P_GROUP_CAPAB_IP_ADDR_ALLOCATION BIT(7)
+
+/* P2PS Coordination Protocol Transport Bitmap */
+#define P2PS_FEATURE_CAPAB_UDP_TRANSPORT BIT(0)
+#define P2PS_FEATURE_CAPAB_MAC_TRANSPORT BIT(1)
+
+struct p2ps_feature_capab {
+ u8 cpt;
+ u8 reserved;
+} STRUCT_PACKED;
+
+/* Invitation Flags */
+#define P2P_INVITATION_FLAGS_TYPE BIT(0)
+
+/* P2P Manageability */
+#define P2P_MAN_DEVICE_MANAGEMENT BIT(0)
+#define P2P_MAN_CROSS_CONNECTION_PERMITTED BIT(1)
+#define P2P_MAN_COEXISTENCE_OPTIONAL BIT(2)
+
+enum p2p_status_code {
+ P2P_SC_SUCCESS = 0,
+ P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE = 1,
+ P2P_SC_FAIL_INCOMPATIBLE_PARAMS = 2,
+ P2P_SC_FAIL_LIMIT_REACHED = 3,
+ P2P_SC_FAIL_INVALID_PARAMS = 4,
+ P2P_SC_FAIL_UNABLE_TO_ACCOMMODATE = 5,
+ P2P_SC_FAIL_PREV_PROTOCOL_ERROR = 6,
+ P2P_SC_FAIL_NO_COMMON_CHANNELS = 7,
+ P2P_SC_FAIL_UNKNOWN_GROUP = 8,
+ P2P_SC_FAIL_BOTH_GO_INTENT_15 = 9,
+ P2P_SC_FAIL_INCOMPATIBLE_PROV_METHOD = 10,
+ P2P_SC_FAIL_REJECTED_BY_USER = 11,
+ P2P_SC_SUCCESS_DEFERRED = 12,
+};
+
+enum p2p_role_indication {
+ P2P_DEVICE_NOT_IN_GROUP = 0x00,
+ P2P_CLIENT_IN_A_GROUP = 0x01,
+ P2P_GO_IN_A_GROUP = 0x02,
+};
+
+#define P2P_WILDCARD_SSID "DIRECT-"
+#define P2P_WILDCARD_SSID_LEN 7
+
+/* P2P action frames */
+enum p2p_act_frame_type {
+ P2P_NOA = 0,
+ P2P_PRESENCE_REQ = 1,
+ P2P_PRESENCE_RESP = 2,
+ P2P_GO_DISC_REQ = 3
+};
+
+/* P2P public action frames */
+enum p2p_action_frame_type {
+ P2P_GO_NEG_REQ = 0,
+ P2P_GO_NEG_RESP = 1,
+ P2P_GO_NEG_CONF = 2,
+ P2P_INVITATION_REQ = 3,
+ P2P_INVITATION_RESP = 4,
+ P2P_DEV_DISC_REQ = 5,
+ P2P_DEV_DISC_RESP = 6,
+ P2P_PROV_DISC_REQ = 7,
+ P2P_PROV_DISC_RESP = 8
+};
+
+enum p2p_service_protocol_type {
+ P2P_SERV_ALL_SERVICES = 0,
+ P2P_SERV_BONJOUR = 1,
+ P2P_SERV_UPNP = 2,
+ P2P_SERV_WS_DISCOVERY = 3,
+ P2P_SERV_WIFI_DISPLAY = 4,
+ P2P_SERV_P2PS = 11,
+ P2P_SERV_VENDOR_SPECIFIC = 255
+};
+
+enum p2p_sd_status {
+ P2P_SD_SUCCESS = 0,
+ P2P_SD_PROTO_NOT_AVAILABLE = 1,
+ P2P_SD_REQUESTED_INFO_NOT_AVAILABLE = 2,
+ P2P_SD_BAD_REQUEST = 3
+};
+
+
+enum wifi_display_subelem {
+ WFD_SUBELEM_DEVICE_INFO = 0,
+ WFD_SUBELEM_ASSOCIATED_BSSID = 1,
+ WFD_SUBELEM_AUDIO_FORMATS = 2,
+ WFD_SUBELEM_VIDEO_FORMATS = 3,
+ WFD_SUBELEM_3D_VIDEO_FORMATS = 4,
+ WFD_SUBELEM_CONTENT_PROTECTION = 5,
+ WFD_SUBELEM_COUPLED_SINK = 6,
+ WFD_SUBELEM_EXT_CAPAB = 7,
+ WFD_SUBELEM_LOCAL_IP_ADDRESS = 8,
+ WFD_SUBELEM_SESSION_INFO = 9
+};
+
+/* 802.11s */
+#define MESH_SYNC_METHOD_NEIGHBOR_OFFSET 1
+#define MESH_SYNC_METHOD_VENDOR 255
+#define MESH_PATH_PROTOCOL_HWMP 1
+#define MESH_PATH_PROTOCOL_VENDOR 255
+#define MESH_PATH_METRIC_AIRTIME 1
+#define MESH_PATH_METRIC_VENDOR 255
+
+enum plink_action_field {
+ PLINK_OPEN = 1,
+ PLINK_CONFIRM,
+ PLINK_CLOSE
+};
+
+#define OUI_BROADCOM 0x00904c /* Broadcom (Epigram) */
+#define VENDOR_VHT_TYPE 0x04
+#define VENDOR_VHT_SUBTYPE 0x08
+#define VENDOR_VHT_SUBTYPE2 0x00
+
+#define VENDOR_HT_CAPAB_OUI_TYPE 0x33 /* 00-90-4c:0x33 */
+
+/* cipher suite selectors */
+#define WLAN_CIPHER_SUITE_USE_GROUP 0x000FAC00
+#define WLAN_CIPHER_SUITE_WEP40 0x000FAC01
+#define WLAN_CIPHER_SUITE_TKIP 0x000FAC02
+/* reserved: 0x000FAC03 */
+#define WLAN_CIPHER_SUITE_CCMP 0x000FAC04
+#define WLAN_CIPHER_SUITE_WEP104 0x000FAC05
+#define WLAN_CIPHER_SUITE_AES_CMAC 0x000FAC06
+#define WLAN_CIPHER_SUITE_NO_GROUP_ADDR 0x000FAC07
+#define WLAN_CIPHER_SUITE_GCMP 0x000FAC08
+#define WLAN_CIPHER_SUITE_GCMP_256 0x000FAC09
+#define WLAN_CIPHER_SUITE_CCMP_256 0x000FAC0A
+#define WLAN_CIPHER_SUITE_BIP_GMAC_128 0x000FAC0B
+#define WLAN_CIPHER_SUITE_BIP_GMAC_256 0x000FAC0C
+#define WLAN_CIPHER_SUITE_BIP_CMAC_256 0x000FAC0D
+
+#define WLAN_CIPHER_SUITE_SMS4 0x00147201
+
+#define WLAN_CIPHER_SUITE_CKIP 0x00409600
+#define WLAN_CIPHER_SUITE_CKIP_CMIC 0x00409601
+#define WLAN_CIPHER_SUITE_CMIC 0x00409602
+#define WLAN_CIPHER_SUITE_KRK 0x004096FF /* for nl80211 use only */
+
+/* AKM suite selectors */
+#define WLAN_AKM_SUITE_8021X 0x000FAC01
+#define WLAN_AKM_SUITE_PSK 0x000FAC02
+#define WLAN_AKM_SUITE_FT_8021X 0x000FAC03
+#define WLAN_AKM_SUITE_FT_PSK 0x000FAC04
+#define WLAN_AKM_SUITE_8021X_SHA256 0x000FAC05
+#define WLAN_AKM_SUITE_PSK_SHA256 0x000FAC06
+#define WLAN_AKM_SUITE_8021X_SUITE_B 0x000FAC11
+#define WLAN_AKM_SUITE_8021X_SUITE_B_192 0x000FAC12
+#define WLAN_AKM_SUITE_CCKM 0x00409600
+#define WLAN_AKM_SUITE_OSEN 0x506f9a01
+
+
+/* IEEE 802.11v - WNM Action field values */
+enum wnm_action {
+ WNM_EVENT_REQ = 0,
+ WNM_EVENT_REPORT = 1,
+ WNM_DIAGNOSTIC_REQ = 2,
+ WNM_DIAGNOSTIC_REPORT = 3,
+ WNM_LOCATION_CFG_REQ = 4,
+ WNM_LOCATION_CFG_RESP = 5,
+ WNM_BSS_TRANS_MGMT_QUERY = 6,
+ WNM_BSS_TRANS_MGMT_REQ = 7,
+ WNM_BSS_TRANS_MGMT_RESP = 8,
+ WNM_FMS_REQ = 9,
+ WNM_FMS_RESP = 10,
+ WNM_COLLOCATED_INTERFERENCE_REQ = 11,
+ WNM_COLLOCATED_INTERFERENCE_REPORT = 12,
+ WNM_TFS_REQ = 13,
+ WNM_TFS_RESP = 14,
+ WNM_TFS_NOTIFY = 15,
+ WNM_SLEEP_MODE_REQ = 16,
+ WNM_SLEEP_MODE_RESP = 17,
+ WNM_TIM_BROADCAST_REQ = 18,
+ WNM_TIM_BROADCAST_RESP = 19,
+ WNM_QOS_TRAFFIC_CAPAB_UPDATE = 20,
+ WNM_CHANNEL_USAGE_REQ = 21,
+ WNM_CHANNEL_USAGE_RESP = 22,
+ WNM_DMS_REQ = 23,
+ WNM_DMS_RESP = 24,
+ WNM_TIMING_MEASUREMENT_REQ = 25,
+ WNM_NOTIFICATION_REQ = 26,
+ WNM_NOTIFICATION_RESP = 27
+};
+
+/* IEEE 802.11v - BSS Transition Management Request - Request Mode */
+#define WNM_BSS_TM_REQ_PREF_CAND_LIST_INCLUDED BIT(0)
+#define WNM_BSS_TM_REQ_ABRIDGED BIT(1)
+#define WNM_BSS_TM_REQ_DISASSOC_IMMINENT BIT(2)
+#define WNM_BSS_TM_REQ_BSS_TERMINATION_INCLUDED BIT(3)
+#define WNM_BSS_TM_REQ_ESS_DISASSOC_IMMINENT BIT(4)
+
+/* IEEE Std 802.11-2012 - Table 8-253 */
+enum bss_trans_mgmt_status_code {
+ WNM_BSS_TM_ACCEPT = 0,
+ WNM_BSS_TM_REJECT_UNSPECIFIED = 1,
+ WNM_BSS_TM_REJECT_INSUFFICIENT_BEACON = 2,
+ WNM_BSS_TM_REJECT_INSUFFICIENT_CAPABITY = 3,
+ WNM_BSS_TM_REJECT_UNDESIRED = 4,
+ WNM_BSS_TM_REJECT_DELAY_REQUEST = 5,
+ WNM_BSS_TM_REJECT_STA_CANDIDATE_LIST_PROVIDED = 6,
+ WNM_BSS_TM_REJECT_NO_SUITABLE_CANDIDATES = 7,
+ WNM_BSS_TM_REJECT_LEAVING_ESS = 8
+};
+
+#define WNM_NEIGHBOR_TSF 1
+#define WNM_NEIGHBOR_CONDENSED_COUNTRY_STRING 2
+#define WNM_NEIGHBOR_BSS_TRANSITION_CANDIDATE 3
+#define WNM_NEIGHBOR_BSS_TERMINATION_DURATION 4
+#define WNM_NEIGHBOR_BEARING 5
+#define WNM_NEIGHBOR_MEASUREMENT_PILOT 66
+#define WNM_NEIGHBOR_RRM_ENABLED_CAPABILITIES 70
+#define WNM_NEIGHBOR_MULTIPLE_BSSID 71
+
+/* QoS action */
+enum qos_action {
+ QOS_ADDTS_REQ = 0,
+ QOS_ADDTS_RESP = 1,
+ QOS_DELTS = 2,
+ QOS_SCHEDULE = 3,
+ QOS_QOS_MAP_CONFIG = 4,
+};
+
+/* IEEE Std 802.11-2012, 8.4.2.62 20/40 BSS Coexistence element */
+#define WLAN_20_40_BSS_COEX_INFO_REQ BIT(0)
+#define WLAN_20_40_BSS_COEX_40MHZ_INTOL BIT(1)
+#define WLAN_20_40_BSS_COEX_20MHZ_WIDTH_REQ BIT(2)
+#define WLAN_20_40_BSS_COEX_OBSS_EXEMPT_REQ BIT(3)
+#define WLAN_20_40_BSS_COEX_OBSS_EXEMPT_GRNT BIT(4)
+
+struct ieee80211_2040_bss_coex_ie {
+ u8 element_id;
+ u8 length;
+ u8 coex_param;
+} STRUCT_PACKED;
+
+struct ieee80211_2040_intol_chan_report {
+ u8 element_id;
+ u8 length;
+ u8 op_class;
+ u8 variable[0]; /* Channel List */
+} STRUCT_PACKED;
+
+/* IEEE 802.11v - WNM-Sleep Mode element */
+struct wnm_sleep_element {
+ u8 eid; /* WLAN_EID_WNMSLEEP */
+ u8 len;
+ u8 action_type; /* WNM_SLEEP_ENTER/WNM_SLEEP_MODE_EXIT */
+ u8 status;
+ le16 intval;
+} STRUCT_PACKED;
+
+#define WNM_SLEEP_MODE_ENTER 0
+#define WNM_SLEEP_MODE_EXIT 1
+
+enum wnm_sleep_mode_response_status {
+ WNM_STATUS_SLEEP_ACCEPT = 0,
+ WNM_STATUS_SLEEP_EXIT_ACCEPT_GTK_UPDATE = 1,
+ WNM_STATUS_DENIED_ACTION = 2,
+ WNM_STATUS_DENIED_TMP = 3,
+ WNM_STATUS_DENIED_KEY = 4,
+ WNM_STATUS_DENIED_OTHER_WNM_SERVICE = 5
+};
+
+/* WNM-Sleep Mode subelement IDs */
+enum wnm_sleep_mode_subelement_id {
+ WNM_SLEEP_SUBELEM_GTK = 0,
+ WNM_SLEEP_SUBELEM_IGTK = 1
+};
+
+/* Channel Switch modes (802.11h) */
+#define CHAN_SWITCH_MODE_ALLOW_TX 0
+#define CHAN_SWITCH_MODE_BLOCK_TX 1
+
+struct tpc_report {
+ u8 eid;
+ u8 len;
+ u8 tx_power;
+ u8 link_margin;
+} STRUCT_PACKED;
+
+/* IEEE Std 802.11-2012, 8.5.7.4 - Link Measurement Request frame format */
+struct rrm_link_measurement_request {
+ u8 dialog_token;
+ s8 tx_power;
+ s8 max_tp;
+ u8 variable[0];
+} STRUCT_PACKED;
+
+/* IEEE Std 802.11-2012, 8.5.7.5 - Link Measurement Report frame format */
+struct rrm_link_measurement_report {
+ u8 dialog_token;
+ struct tpc_report tpc;
+ u8 rx_ant_id;
+ u8 tx_ant_id;
+ u8 rcpi;
+ u8 rsni;
+ u8 variable[0];
+} STRUCT_PACKED;
+
+#define SSID_MAX_LEN 32
+
+/* IEEE Std 802.11ad-2012 - Multi-band element */
+struct multi_band_ie {
+ u8 eid; /* WLAN_EID_MULTI_BAND */
+ u8 len;
+ u8 mb_ctrl;
+ u8 band_id;
+ u8 op_class;
+ u8 chan;
+ u8 bssid[ETH_ALEN];
+ le16 beacon_int;
+ u8 tsf_offs[8];
+ u8 mb_connection_capability;
+ u8 fst_session_tmout;
+ /* Optional:
+ * STA MAC Address
+ * Pairwise Cipher Suite Count
+ * Pairwise Cipher Suite List
+ */
+ u8 variable[0];
+} STRUCT_PACKED;
+
+enum mb_ctrl_sta_role {
+ MB_STA_ROLE_AP = 0,
+ MB_STA_ROLE_TDLS_STA = 1,
+ MB_STA_ROLE_IBSS_STA = 2,
+ MB_STA_ROLE_PCP = 3,
+ MB_STA_ROLE_NON_PCP_NON_AP = 4
+};
+
+#define MB_CTRL_ROLE_MASK (BIT(0) | BIT(1) | BIT(2))
+#define MB_CTRL_ROLE(ctrl) ((u8) ((ctrl) & MB_CTRL_ROLE_MASK))
+#define MB_CTRL_STA_MAC_PRESENT ((u8) (BIT(3)))
+#define MB_CTRL_PAIRWISE_CIPHER_SUITE_PRESENT ((u8) (BIT(4)))
+
+enum mb_band_id {
+ MB_BAND_ID_WIFI_2_4GHZ = 2, /* 2.4 GHz */
+ MB_BAND_ID_WIFI_5GHZ = 4, /* 4.9 and 5 GHz */
+ MB_BAND_ID_WIFI_60GHZ = 5, /* 60 GHz */
+};
+
+#define MB_CONNECTION_CAPABILITY_AP ((u8) (BIT(0)))
+#define MB_CONNECTION_CAPABILITY_PCP ((u8) (BIT(1)))
+#define MB_CONNECTION_CAPABILITY_DLS ((u8) (BIT(2)))
+#define MB_CONNECTION_CAPABILITY_TDLS ((u8) (BIT(3)))
+#define MB_CONNECTION_CAPABILITY_IBSS ((u8) (BIT(4)))
+
+/* IEEE Std 802.11ad-2014 - FST Action field */
+enum fst_action {
+ FST_ACTION_SETUP_REQUEST = 0,
+ FST_ACTION_SETUP_RESPONSE = 1,
+ FST_ACTION_TEAR_DOWN = 2,
+ FST_ACTION_ACK_REQUEST = 3,
+ FST_ACTION_ACK_RESPONSE = 4,
+ FST_ACTION_ON_CHANNEL_TUNNEL = 5,
+};
+
+#endif /* IEEE802_11_DEFS_H */
diff --git a/freebsd/contrib/wpa/src/common/qca-vendor.h b/freebsd/contrib/wpa/src/common/qca-vendor.h
new file mode 100644
index 00000000..28985f51
--- /dev/null
+++ b/freebsd/contrib/wpa/src/common/qca-vendor.h
@@ -0,0 +1,357 @@
+/*
+ * Qualcomm Atheros OUI and vendor specific assignments
+ * Copyright (c) 2014-2015, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef QCA_VENDOR_H
+#define QCA_VENDOR_H
+
+/*
+ * This file is a registry of identifier assignments from the Qualcomm Atheros
+ * OUI 00:13:74 for purposes other than MAC address assignment. New identifiers
+ * can be assigned through normal review process for changes to the upstream
+ * hostap.git repository.
+ */
+
+#define OUI_QCA 0x001374
+
+/**
+ * enum qca_radiotap_vendor_ids - QCA radiotap vendor namespace IDs
+ */
+enum qca_radiotap_vendor_ids {
+ QCA_RADIOTAP_VID_WLANTEST = 0,
+};
+
+/**
+ * enum qca_nl80211_vendor_subcmds - QCA nl80211 vendor command identifiers
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_UNSPEC: Reserved value 0
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_TEST: Test command/event
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_ROAMING: Set roaming policy for drivers that use
+ * internal BSS-selection. This command uses
+ * @QCA_WLAN_VENDOR_ATTR_ROAMING_POLICY to specify the new roaming policy
+ * for the current connection (i.e., changes policy set by the nl80211
+ * Connect command). @QCA_WLAN_VENDOR_ATTR_MAC_ADDR may optionally be
+ * included to indicate which BSS to use in case roaming is disabled.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_AVOID_FREQUENCY: Recommendation of frequency
+ * ranges to avoid to reduce issues due to interference or internal
+ * co-existence information in the driver. The event data structure is
+ * defined in struct qca_avoid_freq_list.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_DFS_CAPABILITY: Command to check driver support
+ * for DFS offloading.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_NAN: NAN command/event which is used to pass
+ * NAN Request/Response and NAN Indication messages. These messages are
+ * interpreted between the framework and the firmware component.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_KEY_MGMT_SET_KEY: Set key operation that can be
+ * used to configure PMK to the driver even when not connected. This can
+ * be used to request offloading of key management operations. Only used
+ * if device supports QCA_WLAN_VENDOR_FEATURE_KEY_MGMT_OFFLOAD.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_KEY_MGMT_ROAM_AUTH: An extended version of
+ * NL80211_CMD_ROAM event with optional attributes including information
+ * from offloaded key management operation. Uses
+ * enum qca_wlan_vendor_attr_roam_auth attributes. Only used
+ * if device supports QCA_WLAN_VENDOR_FEATURE_KEY_MGMT_OFFLOAD.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_DO_ACS: ACS command/event which is used to
+ * invoke the ACS function in device and pass selected channels to
+ * hostapd.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_GET_FEATURES: Command to get the features
+ * supported by the driver. enum qca_wlan_vendor_features defines
+ * the possible features.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_DFS_OFFLOAD_CAC_STARTED: Event used by driver,
+ * which supports DFS offloading, to indicate a channel availability check
+ * start.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_DFS_OFFLOAD_CAC_FINISHED: Event used by driver,
+ * which supports DFS offloading, to indicate a channel availability check
+ * completion.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_DFS_OFFLOAD_CAC_ABORTED: Event used by driver,
+ * which supports DFS offloading, to indicate that the channel availability
+ * check aborted, no change to the channel status.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_DFS_OFFLOAD_CAC_NOP_FINISHED: Event used by
+ * driver, which supports DFS offloading, to indicate that the
+ * Non-Occupancy Period for this channel is over, channel becomes usable.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_DFS_OFFLOAD_RADAR_DETECTED: Event used by driver,
+ * which supports DFS offloading, to indicate a radar pattern has been
+ * detected. The channel is now unusable.
+ */
+enum qca_nl80211_vendor_subcmds {
+ QCA_NL80211_VENDOR_SUBCMD_UNSPEC = 0,
+ QCA_NL80211_VENDOR_SUBCMD_TEST = 1,
+ /* subcmds 2..8 not yet allocated */
+ QCA_NL80211_VENDOR_SUBCMD_ROAMING = 9,
+ QCA_NL80211_VENDOR_SUBCMD_AVOID_FREQUENCY = 10,
+ QCA_NL80211_VENDOR_SUBCMD_DFS_CAPABILITY = 11,
+ QCA_NL80211_VENDOR_SUBCMD_NAN = 12,
+ QCA_NL80211_VENDOR_SUBMCD_STATS_EXT = 13,
+ QCA_NL80211_VENDOR_SUBCMD_LL_STATS_SET = 14,
+ QCA_NL80211_VENDOR_SUBCMD_LL_STATS_GET = 15,
+ QCA_NL80211_VENDOR_SUBCMD_LL_STATS_CLR = 16,
+ QCA_NL80211_VENDOR_SUBCMD_LL_STATS_RADIO_RESULTS = 17,
+ QCA_NL80211_VENDOR_SUBCMD_LL_STATS_IFACE_RESULTS = 18,
+ QCA_NL80211_VENDOR_SUBCMD_LL_STATS_PEERS_RESULTS = 19,
+ QCA_NL80211_VENDOR_SUBCMD_GSCAN_START = 20,
+ QCA_NL80211_VENDOR_SUBCMD_GSCAN_STOP = 21,
+ QCA_NL80211_VENDOR_SUBCMD_GSCAN_GET_VALID_CHANNELS = 22,
+ QCA_NL80211_VENDOR_SUBCMD_GSCAN_GET_CAPABILITIES = 23,
+ QCA_NL80211_VENDOR_SUBCMD_GSCAN_GET_CACHED_RESULTS = 24,
+ QCA_NL80211_VENDOR_SUBCMD_GSCAN_SCAN_RESULTS_AVAILABLE = 25,
+ QCA_NL80211_VENDOR_SUBCMD_GSCAN_FULL_SCAN_RESULT = 26,
+ QCA_NL80211_VENDOR_SUBCMD_GSCAN_SCAN_EVENT = 27,
+ QCA_NL80211_VENDOR_SUBCMD_GSCAN_HOTLIST_AP_FOUND = 28,
+ QCA_NL80211_VENDOR_SUBCMD_GSCAN_SET_BSSID_HOTLIST = 29,
+ QCA_NL80211_VENDOR_SUBCMD_GSCAN_RESET_BSSID_HOTLIST = 30,
+ QCA_NL80211_VENDOR_SUBCMD_GSCAN_SIGNIFICANT_CHANGE = 31,
+ QCA_NL80211_VENDOR_SUBCMD_GSCAN_SET_SIGNIFICANT_CHANGE = 32,
+ QCA_NL80211_VENDOR_SUBCMD_GSCAN_RESET_SIGNIFICANT_CHANGE = 33,
+ QCA_NL80211_VENDOR_SUBCMD_TDLS_ENABLE = 34,
+ QCA_NL80211_VENDOR_SUBCMD_TDLS_DISABLE = 35,
+ QCA_NL80211_VENDOR_SUBCMD_TDLS_GET_STATUS = 36,
+ QCA_NL80211_VENDOR_SUBCMD_TDLS_STATE = 37,
+ QCA_NL80211_VENDOR_SUBCMD_GET_SUPPORTED_FEATURES = 38,
+ QCA_NL80211_VENDOR_SUBCMD_SCANNING_MAC_OUI = 39,
+ QCA_NL80211_VENDOR_SUBCMD_NO_DFS_FLAG = 40,
+ QCA_NL80211_VENDOR_SUBCMD_GSCAN_HOTLIST_AP_LOST = 41,
+ QCA_NL80211_VENDOR_SUBCMD_GET_CONCURRENCY_MATRIX = 42,
+ /* 43..49 - reserved for QCA */
+ QCA_NL80211_VENDOR_SUBCMD_KEY_MGMT_SET_KEY = 50,
+ QCA_NL80211_VENDOR_SUBCMD_KEY_MGMT_ROAM_AUTH = 51,
+ QCA_NL80211_VENDOR_SUBCMD_APFIND = 52,
+ /* 53 - reserved - was used by QCA, but not in use anymore */
+ QCA_NL80211_VENDOR_SUBCMD_DO_ACS = 54,
+ QCA_NL80211_VENDOR_SUBCMD_GET_FEATURES = 55,
+ QCA_NL80211_VENDOR_SUBCMD_DFS_OFFLOAD_CAC_STARTED = 56,
+ QCA_NL80211_VENDOR_SUBCMD_DFS_OFFLOAD_CAC_FINISHED = 57,
+ QCA_NL80211_VENDOR_SUBCMD_DFS_OFFLOAD_CAC_ABORTED = 58,
+ QCA_NL80211_VENDOR_SUBCMD_DFS_OFFLOAD_CAC_NOP_FINISHED = 59,
+ QCA_NL80211_VENDOR_SUBCMD_DFS_OFFLOAD_RADAR_DETECTED = 60,
+ /* 61-90 - reserved for QCA */
+ QCA_NL80211_VENDOR_SUBCMD_DATA_OFFLOAD = 91,
+ QCA_NL80211_VENDOR_SUBCMD_OCB_SET_CONFIG = 92,
+ QCA_NL80211_VENDOR_SUBCMD_OCB_SET_UTC_TIME = 93,
+ QCA_NL80211_VENDOR_SUBCMD_OCB_START_TIMING_ADVERT = 94,
+ QCA_NL80211_VENDOR_SUBCMD_OCB_STOP_TIMING_ADVERT = 95,
+ QCA_NL80211_VENDOR_SUBCMD_OCB_GET_TSF_TIMER = 96,
+ QCA_NL80211_VENDOR_SUBCMD_DCC_GET_STATS = 97,
+ QCA_NL80211_VENDOR_SUBCMD_DCC_CLEAR_STATS = 98,
+ QCA_NL80211_VENDOR_SUBCMD_DCC_UPDATE_NDL = 99,
+ QCA_NL80211_VENDOR_SUBCMD_DCC_STATS_EVENT = 100,
+ QCA_NL80211_VENDOR_SUBCMD_LINK_PROPERTIES = 101,
+ QCA_NL80211_VENDOR_SUBCMD_GW_PARAM_CONFIG = 102,
+ QCA_NL80211_VENDOR_SUBCMD_GET_PREFERRED_FREQ_LIST = 103,
+ QCA_NL80211_VENDOR_SUBCMD_SET_PROBABLE_OPER_CHANNEL = 104,
+ QCA_NL80211_VENDOR_SUBCMD_SETBAND = 105,
+};
+
+
+enum qca_wlan_vendor_attr {
+ QCA_WLAN_VENDOR_ATTR_INVALID = 0,
+ /* used by QCA_NL80211_VENDOR_SUBCMD_DFS_CAPABILITY */
+ QCA_WLAN_VENDOR_ATTR_DFS = 1,
+ /* used by QCA_NL80211_VENDOR_SUBCMD_NAN */
+ QCA_WLAN_VENDOR_ATTR_NAN = 2,
+ /* used by QCA_NL80211_VENDOR_SUBCMD_STATS_EXT */
+ QCA_WLAN_VENDOR_ATTR_STATS_EXT = 3,
+ /* used by QCA_NL80211_VENDOR_SUBCMD_STATS_EXT */
+ QCA_WLAN_VENDOR_ATTR_IFINDEX = 4,
+ /* used by QCA_NL80211_VENDOR_SUBCMD_ROAMING, u32 with values defined
+ * by enum qca_roaming_policy. */
+ QCA_WLAN_VENDOR_ATTR_ROAMING_POLICY = 5,
+ QCA_WLAN_VENDOR_ATTR_MAC_ADDR = 6,
+ /* used by QCA_NL80211_VENDOR_SUBCMD_GET_FEATURES */
+ QCA_WLAN_VENDOR_ATTR_FEATURE_FLAGS = 7,
+ QCA_WLAN_VENDOR_ATTR_TEST = 8,
+ /* used by QCA_NL80211_VENDOR_SUBCMD_GET_FEATURES */
+ /* Unsigned 32-bit value. */
+ QCA_WLAN_VENDOR_ATTR_CONCURRENCY_CAPA = 9,
+ /* Unsigned 32-bit value */
+ QCA_WLAN_VENDOR_ATTR_MAX_CONCURRENT_CHANNELS_2_4_BAND = 10,
+ /* Unsigned 32-bit value */
+ QCA_WLAN_VENDOR_ATTR_MAX_CONCURRENT_CHANNELS_5_0_BAND = 11,
+ /* Unsigned 32-bit value from enum qca_set_band. */
+ QCA_WLAN_VENDOR_ATTR_SETBAND_VALUE = 12,
+ /* keep last */
+ QCA_WLAN_VENDOR_ATTR_AFTER_LAST,
+ QCA_WLAN_VENDOR_ATTR_MAX = QCA_WLAN_VENDOR_ATTR_AFTER_LAST - 1,
+};
+
+
+enum qca_roaming_policy {
+ QCA_ROAMING_NOT_ALLOWED,
+ QCA_ROAMING_ALLOWED_WITHIN_ESS,
+};
+
+enum qca_wlan_vendor_attr_roam_auth {
+ QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_INVALID = 0,
+ QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_BSSID,
+ QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_REQ_IE,
+ QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_RESP_IE,
+ QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_AUTHORIZED,
+ QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_KEY_REPLAY_CTR,
+ QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_PTK_KCK,
+ QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_PTK_KEK,
+ /* keep last */
+ QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_AFTER_LAST,
+ QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_MAX =
+ QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_AFTER_LAST - 1
+};
+
+enum qca_wlan_vendor_attr_acs_offload {
+ QCA_WLAN_VENDOR_ATTR_ACS_CHANNEL_INVALID = 0,
+ QCA_WLAN_VENDOR_ATTR_ACS_PRIMARY_CHANNEL,
+ QCA_WLAN_VENDOR_ATTR_ACS_SECONDARY_CHANNEL,
+ QCA_WLAN_VENDOR_ATTR_ACS_HW_MODE,
+ QCA_WLAN_VENDOR_ATTR_ACS_HT_ENABLED,
+ QCA_WLAN_VENDOR_ATTR_ACS_HT40_ENABLED,
+ QCA_WLAN_VENDOR_ATTR_ACS_VHT_ENABLED,
+ QCA_WLAN_VENDOR_ATTR_ACS_CHWIDTH,
+ QCA_WLAN_VENDOR_ATTR_ACS_CH_LIST,
+ QCA_WLAN_VENDOR_ATTR_ACS_VHT_SEG0_CENTER_CHANNEL,
+ QCA_WLAN_VENDOR_ATTR_ACS_VHT_SEG1_CENTER_CHANNEL,
+ QCA_WLAN_VENDOR_ATTR_ACS_FREQ_LIST,
+ /* keep last */
+ QCA_WLAN_VENDOR_ATTR_ACS_AFTER_LAST,
+ QCA_WLAN_VENDOR_ATTR_ACS_MAX =
+ QCA_WLAN_VENDOR_ATTR_ACS_AFTER_LAST - 1
+};
+
+enum qca_wlan_vendor_acs_hw_mode {
+ QCA_ACS_MODE_IEEE80211B,
+ QCA_ACS_MODE_IEEE80211G,
+ QCA_ACS_MODE_IEEE80211A,
+ QCA_ACS_MODE_IEEE80211AD,
+ QCA_ACS_MODE_IEEE80211ANY,
+};
+
+/**
+ * enum qca_wlan_vendor_features - Vendor device/driver feature flags
+ *
+ * @QCA_WLAN_VENDOR_FEATURE_KEY_MGMT_OFFLOAD: Device supports key
+ * management offload, a mechanism where the station's firmware
+ * does the exchange with the AP to establish the temporal keys
+ * after roaming, rather than having the user space wpa_supplicant do it.
+ * @QCA_WLAN_VENDOR_FEATURE_SUPPORT_HW_MODE_ANY: Device supports automatic
+ * band selection based on channel selection results.
+ * @NUM_QCA_WLAN_VENDOR_FEATURES: Number of assigned feature bits
+ */
+enum qca_wlan_vendor_features {
+ QCA_WLAN_VENDOR_FEATURE_KEY_MGMT_OFFLOAD = 0,
+ QCA_WLAN_VENDOR_FEATURE_SUPPORT_HW_MODE_ANY = 1,
+ NUM_QCA_WLAN_VENDOR_FEATURES /* keep last */
+};
+
+/**
+ * enum qca_wlan_vendor_attr_data_offload_ind - Vendor Data Offload Indication
+ *
+ * @QCA_WLAN_VENDOR_ATTR_DATA_OFFLOAD_IND_SESSION: Session corresponding to
+ * the offloaded data.
+ * @QCA_WLAN_VENDOR_ATTR_DATA_OFFLOAD_IND_PROTOCOL: Protocol of the offloaded
+ * data.
+ * @QCA_WLAN_VENDOR_ATTR_DATA_OFFLOAD_IND_EVENT: Event type for the data offload
+ * indication.
+ */
+enum qca_wlan_vendor_attr_data_offload_ind {
+ QCA_WLAN_VENDOR_ATTR_DATA_OFFLOAD_IND_INVALID = 0,
+ QCA_WLAN_VENDOR_ATTR_DATA_OFFLOAD_IND_SESSION,
+ QCA_WLAN_VENDOR_ATTR_DATA_OFFLOAD_IND_PROTOCOL,
+ QCA_WLAN_VENDOR_ATTR_DATA_OFFLOAD_IND_EVENT,
+
+ /* keep last */
+ QCA_WLAN_VENDOR_ATTR_DATA_OFFLOAD_IND_AFTER_LAST,
+ QCA_WLAN_VENDOR_ATTR_DATA_OFFLOAD_IND_MAX =
+ QCA_WLAN_VENDOR_ATTR_DATA_OFFLOAD_IND_AFTER_LAST - 1
+};
+
+enum qca_vendor_attr_get_preferred_freq_list {
+ QCA_WLAN_VENDOR_ATTR_GET_PREFERRED_FREQ_LIST_INVALID,
+ /* A 32-unsigned value; the interface type/mode for which the preferred
+ * frequency list is requested (see enum qca_iface_type for possible
+ * values); used in GET_PREFERRED_FREQ_LIST command from user-space to
+ * kernel and in the kernel response back to user-space.
+ */
+ QCA_WLAN_VENDOR_ATTR_GET_PREFERRED_FREQ_LIST_IFACE_TYPE,
+ /* An array of 32-unsigned values; values are frequency (MHz); sent
+ * from kernel space to user space.
+ */
+ QCA_WLAN_VENDOR_ATTR_GET_PREFERRED_FREQ_LIST,
+ /* keep last */
+ QCA_WLAN_VENDOR_ATTR_GET_PREFERRED_FREQ_LIST_AFTER_LAST,
+ QCA_WLAN_VENDOR_ATTR_GET_PREFERRED_FREQ_LIST_MAX =
+ QCA_WLAN_VENDOR_ATTR_GET_PREFERRED_FREQ_LIST_AFTER_LAST - 1
+};
+
+enum qca_vendor_attr_probable_oper_channel {
+ QCA_WLAN_VENDOR_ATTR_PROBABLE_OPER_CHANNEL_INVALID,
+ /* 32-bit unsigned value; indicates the connection/iface type likely to
+ * come on this channel (see enum qca_iface_type).
+ */
+ QCA_WLAN_VENDOR_ATTR_PROBABLE_OPER_CHANNEL_IFACE_TYPE,
+ /* 32-bit unsigned value; the frequency (MHz) of the probable channel */
+ QCA_WLAN_VENDOR_ATTR_PROBABLE_OPER_CHANNEL_FREQ,
+ /* keep last */
+ QCA_WLAN_VENDOR_ATTR_PROBABLE_OPER_CHANNEL_AFTER_LAST,
+ QCA_WLAN_VENDOR_ATTR_PROBABLE_OPER_CHANNEL_MAX =
+ QCA_WLAN_VENDOR_ATTR_PROBABLE_OPER_CHANNEL_AFTER_LAST - 1
+};
+
+enum qca_iface_type {
+ QCA_IFACE_TYPE_STA,
+ QCA_IFACE_TYPE_AP,
+ QCA_IFACE_TYPE_P2P_CLIENT,
+ QCA_IFACE_TYPE_P2P_GO,
+ QCA_IFACE_TYPE_IBSS,
+ QCA_IFACE_TYPE_TDLS,
+};
+
+enum qca_set_band {
+ QCA_SETBAND_AUTO,
+ QCA_SETBAND_5G,
+ QCA_SETBAND_2G,
+};
+
+/* IEEE 802.11 Vendor Specific elements */
+
+/**
+ * enum qca_vendor_element_id - QCA Vendor Specific element types
+ *
+ * These values are used to identify QCA Vendor Specific elements. The
+ * payload of the element starts with the three octet OUI (OUI_QCA) and
+ * is followed by a single octet type which is defined by this enum.
+ *
+ * @QCA_VENDOR_ELEM_P2P_PREF_CHAN_LIST: P2P preferred channel list.
+ * This element can be used to specify preference order for supported
+ * channels. The channels in this list are in preference order (the first
+ * one has the highest preference) and are described as a pair of
+ * (global) Operating Class and Channel Number (each one octet) fields.
+ *
+ * This extends the standard P2P functionality by providing option to have
+ * more than one preferred operating channel. When this element is present,
+ * it replaces the preference indicated in the Operating Channel attribute.
+ * For supporting other implementations, the Operating Channel attribute is
+ * expected to be used with the highest preference channel. Similarly, all
+ * the channels included in this Preferred channel list element are
+ * expected to be included in the Channel List attribute.
+ *
+ * This vendor element may be included in GO Negotiation Request, P2P
+ * Invitation Request, and Provision Discovery Request frames.
+ */
+enum qca_vendor_element_id {
+ QCA_VENDOR_ELEM_P2P_PREF_CHAN_LIST = 0,
+};
+
+#endif /* QCA_VENDOR_H */
diff --git a/freebsd/contrib/wpa/src/common/sae.h b/freebsd/contrib/wpa/src/common/sae.h
new file mode 100644
index 00000000..c07026cd
--- /dev/null
+++ b/freebsd/contrib/wpa/src/common/sae.h
@@ -0,0 +1,70 @@
+/*
+ * Simultaneous authentication of equals
+ * Copyright (c) 2012-2013, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef SAE_H
+#define SAE_H
+
+#define SAE_KCK_LEN 32
+#define SAE_PMK_LEN 32
+#define SAE_PMKID_LEN 16
+#define SAE_KEYSEED_KEY_LEN 32
+#define SAE_MAX_PRIME_LEN 512
+#define SAE_MAX_ECC_PRIME_LEN 66
+#define SAE_COMMIT_MAX_LEN (2 + 3 * SAE_MAX_PRIME_LEN)
+#define SAE_CONFIRM_MAX_LEN (2 + SAE_MAX_PRIME_LEN)
+
+/* Special value returned by sae_parse_commit() */
+#define SAE_SILENTLY_DISCARD 65535
+
+struct sae_temporary_data {
+ u8 kck[SAE_KCK_LEN];
+ struct crypto_bignum *own_commit_scalar;
+ struct crypto_bignum *own_commit_element_ffc;
+ struct crypto_ec_point *own_commit_element_ecc;
+ struct crypto_bignum *peer_commit_element_ffc;
+ struct crypto_ec_point *peer_commit_element_ecc;
+ struct crypto_ec_point *pwe_ecc;
+ struct crypto_bignum *pwe_ffc;
+ struct crypto_bignum *sae_rand;
+ struct crypto_ec *ec;
+ int prime_len;
+ const struct dh_group *dh;
+ const struct crypto_bignum *prime;
+ const struct crypto_bignum *order;
+ struct crypto_bignum *prime_buf;
+ struct crypto_bignum *order_buf;
+ struct wpabuf *anti_clogging_token;
+};
+
+struct sae_data {
+ enum { SAE_NOTHING, SAE_COMMITTED, SAE_CONFIRMED, SAE_ACCEPTED } state;
+ u16 send_confirm;
+ u8 pmk[SAE_PMK_LEN];
+ struct crypto_bignum *peer_commit_scalar;
+ int group;
+ int sync;
+ struct sae_temporary_data *tmp;
+};
+
+int sae_set_group(struct sae_data *sae, int group);
+void sae_clear_temp_data(struct sae_data *sae);
+void sae_clear_data(struct sae_data *sae);
+
+int sae_prepare_commit(const u8 *addr1, const u8 *addr2,
+ const u8 *password, size_t password_len,
+ struct sae_data *sae);
+int sae_process_commit(struct sae_data *sae);
+void sae_write_commit(struct sae_data *sae, struct wpabuf *buf,
+ const struct wpabuf *token);
+u16 sae_parse_commit(struct sae_data *sae, const u8 *data, size_t len,
+ const u8 **token, size_t *token_len, int *allowed_groups);
+void sae_write_confirm(struct sae_data *sae, struct wpabuf *buf);
+int sae_check_confirm(struct sae_data *sae, const u8 *data, size_t len);
+u16 sae_group_allowed(struct sae_data *sae, int *allowed_groups, u16 group);
+
+#endif /* SAE_H */
diff --git a/freebsd/contrib/wpa/src/common/version.h b/freebsd/contrib/wpa/src/common/version.h
new file mode 100644
index 00000000..a5cc5b7b
--- /dev/null
+++ b/freebsd/contrib/wpa/src/common/version.h
@@ -0,0 +1,10 @@
+#ifndef VERSION_H
+#define VERSION_H
+
+#ifndef VERSION_STR_POSTFIX
+#define VERSION_STR_POSTFIX ""
+#endif /* VERSION_STR_POSTFIX */
+
+#define VERSION_STR "2.5" VERSION_STR_POSTFIX
+
+#endif /* VERSION_H */
diff --git a/freebsd/contrib/wpa/src/common/wpa_common.c b/freebsd/contrib/wpa/src/common/wpa_common.c
new file mode 100644
index 00000000..f2c2d56d
--- /dev/null
+++ b/freebsd/contrib/wpa/src/common/wpa_common.c
@@ -0,0 +1,1666 @@
+#include <machine/rtems-bsd-user-space.h>
+
+/*
+ * WPA/RSN - Shared functions for supplicant and authenticator
+ * Copyright (c) 2002-2015, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "crypto/md5.h"
+#include "crypto/sha1.h"
+#include "crypto/sha256.h"
+#include "crypto/sha384.h"
+#include "crypto/aes_wrap.h"
+#include "crypto/crypto.h"
+#include "ieee802_11_defs.h"
+#include "defs.h"
+#include "wpa_common.h"
+
+
+static unsigned int wpa_kck_len(int akmp)
+{
+ if (akmp == WPA_KEY_MGMT_IEEE8021X_SUITE_B_192)
+ return 24;
+ return 16;
+}
+
+
+static unsigned int wpa_kek_len(int akmp)
+{
+ if (akmp == WPA_KEY_MGMT_IEEE8021X_SUITE_B_192)
+ return 32;
+ return 16;
+}
+
+
+unsigned int wpa_mic_len(int akmp)
+{
+ if (akmp == WPA_KEY_MGMT_IEEE8021X_SUITE_B_192)
+ return 24;
+ return 16;
+}
+
+
+/**
+ * wpa_eapol_key_mic - Calculate EAPOL-Key MIC
+ * @key: EAPOL-Key Key Confirmation Key (KCK)
+ * @key_len: KCK length in octets
+ * @akmp: WPA_KEY_MGMT_* used in key derivation
+ * @ver: Key descriptor version (WPA_KEY_INFO_TYPE_*)
+ * @buf: Pointer to the beginning of the EAPOL header (version field)
+ * @len: Length of the EAPOL frame (from EAPOL header to the end of the frame)
+ * @mic: Pointer to the buffer to which the EAPOL-Key MIC is written
+ * Returns: 0 on success, -1 on failure
+ *
+ * Calculate EAPOL-Key MIC for an EAPOL-Key packet. The EAPOL-Key MIC field has
+ * to be cleared (all zeroes) when calling this function.
+ *
+ * Note: 'IEEE Std 802.11i-2004 - 8.5.2 EAPOL-Key frames' has an error in the
+ * description of the Key MIC calculation. It includes packet data from the
+ * beginning of the EAPOL-Key header, not EAPOL header. This incorrect change
+ * happened during final editing of the standard and the correct behavior is
+ * defined in the last draft (IEEE 802.11i/D10).
+ */
+int wpa_eapol_key_mic(const u8 *key, size_t key_len, int akmp, int ver,
+ const u8 *buf, size_t len, u8 *mic)
+{
+ u8 hash[SHA384_MAC_LEN];
+
+ switch (ver) {
+#ifndef CONFIG_FIPS
+ case WPA_KEY_INFO_TYPE_HMAC_MD5_RC4:
+ return hmac_md5(key, key_len, buf, len, mic);
+#endif /* CONFIG_FIPS */
+ case WPA_KEY_INFO_TYPE_HMAC_SHA1_AES:
+ if (hmac_sha1(key, key_len, buf, len, hash))
+ return -1;
+ os_memcpy(mic, hash, MD5_MAC_LEN);
+ break;
+#if defined(CONFIG_IEEE80211R) || defined(CONFIG_IEEE80211W)
+ case WPA_KEY_INFO_TYPE_AES_128_CMAC:
+ return omac1_aes_128(key, buf, len, mic);
+#endif /* CONFIG_IEEE80211R || CONFIG_IEEE80211W */
+ case WPA_KEY_INFO_TYPE_AKM_DEFINED:
+ switch (akmp) {
+#ifdef CONFIG_HS20
+ case WPA_KEY_MGMT_OSEN:
+ return omac1_aes_128(key, buf, len, mic);
+#endif /* CONFIG_HS20 */
+#ifdef CONFIG_SUITEB
+ case WPA_KEY_MGMT_IEEE8021X_SUITE_B:
+ if (hmac_sha256(key, key_len, buf, len, hash))
+ return -1;
+ os_memcpy(mic, hash, MD5_MAC_LEN);
+ break;
+#endif /* CONFIG_SUITEB */
+#ifdef CONFIG_SUITEB192
+ case WPA_KEY_MGMT_IEEE8021X_SUITE_B_192:
+ if (hmac_sha384(key, key_len, buf, len, hash))
+ return -1;
+ os_memcpy(mic, hash, 24);
+ break;
+#endif /* CONFIG_SUITEB192 */
+ default:
+ return -1;
+ }
+ break;
+ default:
+ return -1;
+ }
+
+ return 0;
+}
+
+
+/**
+ * wpa_pmk_to_ptk - Calculate PTK from PMK, addresses, and nonces
+ * @pmk: Pairwise master key
+ * @pmk_len: Length of PMK
+ * @label: Label to use in derivation
+ * @addr1: AA or SA
+ * @addr2: SA or AA
+ * @nonce1: ANonce or SNonce
+ * @nonce2: SNonce or ANonce
+ * @ptk: Buffer for pairwise transient key
+ * @akmp: Negotiated AKM
+ * @cipher: Negotiated pairwise cipher
+ * Returns: 0 on success, -1 on failure
+ *
+ * IEEE Std 802.11i-2004 - 8.5.1.2 Pairwise key hierarchy
+ * PTK = PRF-X(PMK, "Pairwise key expansion",
+ * Min(AA, SA) || Max(AA, SA) ||
+ * Min(ANonce, SNonce) || Max(ANonce, SNonce))
+ *
+ * STK = PRF-X(SMK, "Peer key expansion",
+ * Min(MAC_I, MAC_P) || Max(MAC_I, MAC_P) ||
+ * Min(INonce, PNonce) || Max(INonce, PNonce))
+ */
+int wpa_pmk_to_ptk(const u8 *pmk, size_t pmk_len, const char *label,
+ const u8 *addr1, const u8 *addr2,
+ const u8 *nonce1, const u8 *nonce2,
+ struct wpa_ptk *ptk, int akmp, int cipher)
+{
+ u8 data[2 * ETH_ALEN + 2 * WPA_NONCE_LEN];
+ u8 tmp[WPA_KCK_MAX_LEN + WPA_KEK_MAX_LEN + WPA_TK_MAX_LEN];
+ size_t ptk_len;
+
+ if (os_memcmp(addr1, addr2, ETH_ALEN) < 0) {
+ os_memcpy(data, addr1, ETH_ALEN);
+ os_memcpy(data + ETH_ALEN, addr2, ETH_ALEN);
+ } else {
+ os_memcpy(data, addr2, ETH_ALEN);
+ os_memcpy(data + ETH_ALEN, addr1, ETH_ALEN);
+ }
+
+ if (os_memcmp(nonce1, nonce2, WPA_NONCE_LEN) < 0) {
+ os_memcpy(data + 2 * ETH_ALEN, nonce1, WPA_NONCE_LEN);
+ os_memcpy(data + 2 * ETH_ALEN + WPA_NONCE_LEN, nonce2,
+ WPA_NONCE_LEN);
+ } else {
+ os_memcpy(data + 2 * ETH_ALEN, nonce2, WPA_NONCE_LEN);
+ os_memcpy(data + 2 * ETH_ALEN + WPA_NONCE_LEN, nonce1,
+ WPA_NONCE_LEN);
+ }
+
+ ptk->kck_len = wpa_kck_len(akmp);
+ ptk->kek_len = wpa_kek_len(akmp);
+ ptk->tk_len = wpa_cipher_key_len(cipher);
+ ptk_len = ptk->kck_len + ptk->kek_len + ptk->tk_len;
+
+#ifdef CONFIG_SUITEB192
+ if (wpa_key_mgmt_sha384(akmp))
+ sha384_prf(pmk, pmk_len, label, data, sizeof(data),
+ tmp, ptk_len);
+ else
+#endif /* CONFIG_SUITEB192 */
+#ifdef CONFIG_IEEE80211W
+ if (wpa_key_mgmt_sha256(akmp))
+ sha256_prf(pmk, pmk_len, label, data, sizeof(data),
+ tmp, ptk_len);
+ else
+#endif /* CONFIG_IEEE80211W */
+ sha1_prf(pmk, pmk_len, label, data, sizeof(data), tmp, ptk_len);
+
+ wpa_printf(MSG_DEBUG, "WPA: PTK derivation - A1=" MACSTR " A2=" MACSTR,
+ MAC2STR(addr1), MAC2STR(addr2));
+ wpa_hexdump(MSG_DEBUG, "WPA: Nonce1", nonce1, WPA_NONCE_LEN);
+ wpa_hexdump(MSG_DEBUG, "WPA: Nonce2", nonce2, WPA_NONCE_LEN);
+ wpa_hexdump_key(MSG_DEBUG, "WPA: PMK", pmk, pmk_len);
+ wpa_hexdump_key(MSG_DEBUG, "WPA: PTK", tmp, ptk_len);
+
+ os_memcpy(ptk->kck, tmp, ptk->kck_len);
+ wpa_hexdump_key(MSG_DEBUG, "WPA: KCK", ptk->kck, ptk->kck_len);
+
+ os_memcpy(ptk->kek, tmp + ptk->kck_len, ptk->kek_len);
+ wpa_hexdump_key(MSG_DEBUG, "WPA: KEK", ptk->kek, ptk->kek_len);
+
+ os_memcpy(ptk->tk, tmp + ptk->kck_len + ptk->kek_len, ptk->tk_len);
+ wpa_hexdump_key(MSG_DEBUG, "WPA: TK", ptk->tk, ptk->tk_len);
+
+ os_memset(tmp, 0, sizeof(tmp));
+ return 0;
+}
+
+
+#ifdef CONFIG_IEEE80211R
+int wpa_ft_mic(const u8 *kck, size_t kck_len, const u8 *sta_addr,
+ const u8 *ap_addr, u8 transaction_seqnum,
+ const u8 *mdie, size_t mdie_len,
+ const u8 *ftie, size_t ftie_len,
+ const u8 *rsnie, size_t rsnie_len,
+ const u8 *ric, size_t ric_len, u8 *mic)
+{
+ const u8 *addr[9];
+ size_t len[9];
+ size_t i, num_elem = 0;
+ u8 zero_mic[16];
+
+ if (kck_len != 16) {
+ wpa_printf(MSG_WARNING, "FT: Unsupported KCK length %u",
+ (unsigned int) kck_len);
+ return -1;
+ }
+
+ addr[num_elem] = sta_addr;
+ len[num_elem] = ETH_ALEN;
+ num_elem++;
+
+ addr[num_elem] = ap_addr;
+ len[num_elem] = ETH_ALEN;
+ num_elem++;
+
+ addr[num_elem] = &transaction_seqnum;
+ len[num_elem] = 1;
+ num_elem++;
+
+ if (rsnie) {
+ addr[num_elem] = rsnie;
+ len[num_elem] = rsnie_len;
+ num_elem++;
+ }
+ if (mdie) {
+ addr[num_elem] = mdie;
+ len[num_elem] = mdie_len;
+ num_elem++;
+ }
+ if (ftie) {
+ if (ftie_len < 2 + sizeof(struct rsn_ftie))
+ return -1;
+
+ /* IE hdr and mic_control */
+ addr[num_elem] = ftie;
+ len[num_elem] = 2 + 2;
+ num_elem++;
+
+ /* MIC field with all zeros */
+ os_memset(zero_mic, 0, sizeof(zero_mic));
+ addr[num_elem] = zero_mic;
+ len[num_elem] = sizeof(zero_mic);
+ num_elem++;
+
+ /* Rest of FTIE */
+ addr[num_elem] = ftie + 2 + 2 + 16;
+ len[num_elem] = ftie_len - (2 + 2 + 16);
+ num_elem++;
+ }
+ if (ric) {
+ addr[num_elem] = ric;
+ len[num_elem] = ric_len;
+ num_elem++;
+ }
+
+ for (i = 0; i < num_elem; i++)
+ wpa_hexdump(MSG_MSGDUMP, "FT: MIC data", addr[i], len[i]);
+ if (omac1_aes_128_vector(kck, num_elem, addr, len, mic))
+ return -1;
+
+ return 0;
+}
+
+
+static int wpa_ft_parse_ftie(const u8 *ie, size_t ie_len,
+ struct wpa_ft_ies *parse)
+{
+ const u8 *end, *pos;
+
+ parse->ftie = ie;
+ parse->ftie_len = ie_len;
+
+ pos = ie + sizeof(struct rsn_ftie);
+ end = ie + ie_len;
+
+ while (pos + 2 <= end && pos + 2 + pos[1] <= end) {
+ switch (pos[0]) {
+ case FTIE_SUBELEM_R1KH_ID:
+ if (pos[1] != FT_R1KH_ID_LEN) {
+ wpa_printf(MSG_DEBUG, "FT: Invalid R1KH-ID "
+ "length in FTIE: %d", pos[1]);
+ return -1;
+ }
+ parse->r1kh_id = pos + 2;
+ break;
+ case FTIE_SUBELEM_GTK:
+ parse->gtk = pos + 2;
+ parse->gtk_len = pos[1];
+ break;
+ case FTIE_SUBELEM_R0KH_ID:
+ if (pos[1] < 1 || pos[1] > FT_R0KH_ID_MAX_LEN) {
+ wpa_printf(MSG_DEBUG, "FT: Invalid R0KH-ID "
+ "length in FTIE: %d", pos[1]);
+ return -1;
+ }
+ parse->r0kh_id = pos + 2;
+ parse->r0kh_id_len = pos[1];
+ break;
+#ifdef CONFIG_IEEE80211W
+ case FTIE_SUBELEM_IGTK:
+ parse->igtk = pos + 2;
+ parse->igtk_len = pos[1];
+ break;
+#endif /* CONFIG_IEEE80211W */
+ }
+
+ pos += 2 + pos[1];
+ }
+
+ return 0;
+}
+
+
+int wpa_ft_parse_ies(const u8 *ies, size_t ies_len,
+ struct wpa_ft_ies *parse)
+{
+ const u8 *end, *pos;
+ struct wpa_ie_data data;
+ int ret;
+ const struct rsn_ftie *ftie;
+ int prot_ie_count = 0;
+
+ os_memset(parse, 0, sizeof(*parse));
+ if (ies == NULL)
+ return 0;
+
+ pos = ies;
+ end = ies + ies_len;
+ while (pos + 2 <= end && pos + 2 + pos[1] <= end) {
+ switch (pos[0]) {
+ case WLAN_EID_RSN:
+ parse->rsn = pos + 2;
+ parse->rsn_len = pos[1];
+ ret = wpa_parse_wpa_ie_rsn(parse->rsn - 2,
+ parse->rsn_len + 2,
+ &data);
+ if (ret < 0) {
+ wpa_printf(MSG_DEBUG, "FT: Failed to parse "
+ "RSN IE: %d", ret);
+ return -1;
+ }
+ if (data.num_pmkid == 1 && data.pmkid)
+ parse->rsn_pmkid = data.pmkid;
+ break;
+ case WLAN_EID_MOBILITY_DOMAIN:
+ if (pos[1] < sizeof(struct rsn_mdie))
+ return -1;
+ parse->mdie = pos + 2;
+ parse->mdie_len = pos[1];
+ break;
+ case WLAN_EID_FAST_BSS_TRANSITION:
+ if (pos[1] < sizeof(*ftie))
+ return -1;
+ ftie = (const struct rsn_ftie *) (pos + 2);
+ prot_ie_count = ftie->mic_control[1];
+ if (wpa_ft_parse_ftie(pos + 2, pos[1], parse) < 0)
+ return -1;
+ break;
+ case WLAN_EID_TIMEOUT_INTERVAL:
+ if (pos[1] != 5)
+ break;
+ parse->tie = pos + 2;
+ parse->tie_len = pos[1];
+ break;
+ case WLAN_EID_RIC_DATA:
+ if (parse->ric == NULL)
+ parse->ric = pos;
+ break;
+ }
+
+ pos += 2 + pos[1];
+ }
+
+ if (prot_ie_count == 0)
+ return 0; /* no MIC */
+
+ /*
+ * Check that the protected IE count matches with IEs included in the
+ * frame.
+ */
+ if (parse->rsn)
+ prot_ie_count--;
+ if (parse->mdie)
+ prot_ie_count--;
+ if (parse->ftie)
+ prot_ie_count--;
+ if (prot_ie_count < 0) {
+ wpa_printf(MSG_DEBUG, "FT: Some required IEs not included in "
+ "the protected IE count");
+ return -1;
+ }
+
+ if (prot_ie_count == 0 && parse->ric) {
+ wpa_printf(MSG_DEBUG, "FT: RIC IE(s) in the frame, but not "
+ "included in protected IE count");
+ return -1;
+ }
+
+ /* Determine the end of the RIC IE(s) */
+ pos = parse->ric;
+ while (pos && pos + 2 <= end && pos + 2 + pos[1] <= end &&
+ prot_ie_count) {
+ prot_ie_count--;
+ pos += 2 + pos[1];
+ }
+ parse->ric_len = pos - parse->ric;
+ if (prot_ie_count) {
+ wpa_printf(MSG_DEBUG, "FT: %d protected IEs missing from "
+ "frame", (int) prot_ie_count);
+ return -1;
+ }
+
+ return 0;
+}
+#endif /* CONFIG_IEEE80211R */
+
+
+static int rsn_selector_to_bitfield(const u8 *s)
+{
+ if (RSN_SELECTOR_GET(s) == RSN_CIPHER_SUITE_NONE)
+ return WPA_CIPHER_NONE;
+ if (RSN_SELECTOR_GET(s) == RSN_CIPHER_SUITE_TKIP)
+ return WPA_CIPHER_TKIP;
+ if (RSN_SELECTOR_GET(s) == RSN_CIPHER_SUITE_CCMP)
+ return WPA_CIPHER_CCMP;
+#ifdef CONFIG_IEEE80211W
+ if (RSN_SELECTOR_GET(s) == RSN_CIPHER_SUITE_AES_128_CMAC)
+ return WPA_CIPHER_AES_128_CMAC;
+#endif /* CONFIG_IEEE80211W */
+ if (RSN_SELECTOR_GET(s) == RSN_CIPHER_SUITE_GCMP)
+ return WPA_CIPHER_GCMP;
+ if (RSN_SELECTOR_GET(s) == RSN_CIPHER_SUITE_CCMP_256)
+ return WPA_CIPHER_CCMP_256;
+ if (RSN_SELECTOR_GET(s) == RSN_CIPHER_SUITE_GCMP_256)
+ return WPA_CIPHER_GCMP_256;
+ if (RSN_SELECTOR_GET(s) == RSN_CIPHER_SUITE_BIP_GMAC_128)
+ return WPA_CIPHER_BIP_GMAC_128;
+ if (RSN_SELECTOR_GET(s) == RSN_CIPHER_SUITE_BIP_GMAC_256)
+ return WPA_CIPHER_BIP_GMAC_256;
+ if (RSN_SELECTOR_GET(s) == RSN_CIPHER_SUITE_BIP_CMAC_256)
+ return WPA_CIPHER_BIP_CMAC_256;
+ if (RSN_SELECTOR_GET(s) == RSN_CIPHER_SUITE_NO_GROUP_ADDRESSED)
+ return WPA_CIPHER_GTK_NOT_USED;
+ return 0;
+}
+
+
+static int rsn_key_mgmt_to_bitfield(const u8 *s)
+{
+ if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_UNSPEC_802_1X)
+ return WPA_KEY_MGMT_IEEE8021X;
+ if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_PSK_OVER_802_1X)
+ return WPA_KEY_MGMT_PSK;
+#ifdef CONFIG_IEEE80211R
+ if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_FT_802_1X)
+ return WPA_KEY_MGMT_FT_IEEE8021X;
+ if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_FT_PSK)
+ return WPA_KEY_MGMT_FT_PSK;
+#endif /* CONFIG_IEEE80211R */
+#ifdef CONFIG_IEEE80211W
+ if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_802_1X_SHA256)
+ return WPA_KEY_MGMT_IEEE8021X_SHA256;
+ if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_PSK_SHA256)
+ return WPA_KEY_MGMT_PSK_SHA256;
+#endif /* CONFIG_IEEE80211W */
+#ifdef CONFIG_SAE
+ if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_SAE)
+ return WPA_KEY_MGMT_SAE;
+ if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_FT_SAE)
+ return WPA_KEY_MGMT_FT_SAE;
+#endif /* CONFIG_SAE */
+ if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_802_1X_SUITE_B)
+ return WPA_KEY_MGMT_IEEE8021X_SUITE_B;
+ if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_802_1X_SUITE_B_192)
+ return WPA_KEY_MGMT_IEEE8021X_SUITE_B_192;
+ if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_OSEN)
+ return WPA_KEY_MGMT_OSEN;
+ return 0;
+}
+
+
+int wpa_cipher_valid_group(int cipher)
+{
+ return wpa_cipher_valid_pairwise(cipher) ||
+ cipher == WPA_CIPHER_GTK_NOT_USED;
+}
+
+
+#ifdef CONFIG_IEEE80211W
+int wpa_cipher_valid_mgmt_group(int cipher)
+{
+ return cipher == WPA_CIPHER_AES_128_CMAC ||
+ cipher == WPA_CIPHER_BIP_GMAC_128 ||
+ cipher == WPA_CIPHER_BIP_GMAC_256 ||
+ cipher == WPA_CIPHER_BIP_CMAC_256;
+}
+#endif /* CONFIG_IEEE80211W */
+
+
+/**
+ * wpa_parse_wpa_ie_rsn - Parse RSN IE
+ * @rsn_ie: Buffer containing RSN IE
+ * @rsn_ie_len: RSN IE buffer length (including IE number and length octets)
+ * @data: Pointer to structure that will be filled in with parsed data
+ * Returns: 0 on success, <0 on failure
+ */
+int wpa_parse_wpa_ie_rsn(const u8 *rsn_ie, size_t rsn_ie_len,
+ struct wpa_ie_data *data)
+{
+ const u8 *pos;
+ int left;
+ int i, count;
+
+ os_memset(data, 0, sizeof(*data));
+ data->proto = WPA_PROTO_RSN;
+ data->pairwise_cipher = WPA_CIPHER_CCMP;
+ data->group_cipher = WPA_CIPHER_CCMP;
+ data->key_mgmt = WPA_KEY_MGMT_IEEE8021X;
+ data->capabilities = 0;
+ data->pmkid = NULL;
+ data->num_pmkid = 0;
+#ifdef CONFIG_IEEE80211W
+ data->mgmt_group_cipher = WPA_CIPHER_AES_128_CMAC;
+#else /* CONFIG_IEEE80211W */
+ data->mgmt_group_cipher = 0;
+#endif /* CONFIG_IEEE80211W */
+
+ if (rsn_ie_len == 0) {
+ /* No RSN IE - fail silently */
+ return -1;
+ }
+
+ if (rsn_ie_len < sizeof(struct rsn_ie_hdr)) {
+ wpa_printf(MSG_DEBUG, "%s: ie len too short %lu",
+ __func__, (unsigned long) rsn_ie_len);
+ return -1;
+ }
+
+ if (rsn_ie_len >= 6 && rsn_ie[1] >= 4 &&
+ rsn_ie[1] == rsn_ie_len - 2 &&
+ WPA_GET_BE32(&rsn_ie[2]) == OSEN_IE_VENDOR_TYPE) {
+ pos = rsn_ie + 6;
+ left = rsn_ie_len - 6;
+
+ data->proto = WPA_PROTO_OSEN;
+ } else {
+ const struct rsn_ie_hdr *hdr;
+
+ hdr = (const struct rsn_ie_hdr *) rsn_ie;
+
+ if (hdr->elem_id != WLAN_EID_RSN ||
+ hdr->len != rsn_ie_len - 2 ||
+ WPA_GET_LE16(hdr->version) != RSN_VERSION) {
+ wpa_printf(MSG_DEBUG, "%s: malformed ie or unknown version",
+ __func__);
+ return -2;
+ }
+
+ pos = (const u8 *) (hdr + 1);
+ left = rsn_ie_len - sizeof(*hdr);
+ }
+
+ if (left >= RSN_SELECTOR_LEN) {
+ data->group_cipher = rsn_selector_to_bitfield(pos);
+ if (!wpa_cipher_valid_group(data->group_cipher)) {
+ wpa_printf(MSG_DEBUG, "%s: invalid group cipher 0x%x",
+ __func__, data->group_cipher);
+ return -1;
+ }
+ pos += RSN_SELECTOR_LEN;
+ left -= RSN_SELECTOR_LEN;
+ } else if (left > 0) {
+ wpa_printf(MSG_DEBUG, "%s: ie length mismatch, %u too much",
+ __func__, left);
+ return -3;
+ }
+
+ if (left >= 2) {
+ data->pairwise_cipher = 0;
+ count = WPA_GET_LE16(pos);
+ pos += 2;
+ left -= 2;
+ if (count == 0 || count > left / RSN_SELECTOR_LEN) {
+ wpa_printf(MSG_DEBUG, "%s: ie count botch (pairwise), "
+ "count %u left %u", __func__, count, left);
+ return -4;
+ }
+ for (i = 0; i < count; i++) {
+ data->pairwise_cipher |= rsn_selector_to_bitfield(pos);
+ pos += RSN_SELECTOR_LEN;
+ left -= RSN_SELECTOR_LEN;
+ }
+#ifdef CONFIG_IEEE80211W
+ if (data->pairwise_cipher & WPA_CIPHER_AES_128_CMAC) {
+ wpa_printf(MSG_DEBUG, "%s: AES-128-CMAC used as "
+ "pairwise cipher", __func__);
+ return -1;
+ }
+#endif /* CONFIG_IEEE80211W */
+ } else if (left == 1) {
+ wpa_printf(MSG_DEBUG, "%s: ie too short (for key mgmt)",
+ __func__);
+ return -5;
+ }
+
+ if (left >= 2) {
+ data->key_mgmt = 0;
+ count = WPA_GET_LE16(pos);
+ pos += 2;
+ left -= 2;
+ if (count == 0 || count > left / RSN_SELECTOR_LEN) {
+ wpa_printf(MSG_DEBUG, "%s: ie count botch (key mgmt), "
+ "count %u left %u", __func__, count, left);
+ return -6;
+ }
+ for (i = 0; i < count; i++) {
+ data->key_mgmt |= rsn_key_mgmt_to_bitfield(pos);
+ pos += RSN_SELECTOR_LEN;
+ left -= RSN_SELECTOR_LEN;
+ }
+ } else if (left == 1) {
+ wpa_printf(MSG_DEBUG, "%s: ie too short (for capabilities)",
+ __func__);
+ return -7;
+ }
+
+ if (left >= 2) {
+ data->capabilities = WPA_GET_LE16(pos);
+ pos += 2;
+ left -= 2;
+ }
+
+ if (left >= 2) {
+ u16 num_pmkid = WPA_GET_LE16(pos);
+ pos += 2;
+ left -= 2;
+ if (num_pmkid > (unsigned int) left / PMKID_LEN) {
+ wpa_printf(MSG_DEBUG, "%s: PMKID underflow "
+ "(num_pmkid=%u left=%d)",
+ __func__, num_pmkid, left);
+ data->num_pmkid = 0;
+ return -9;
+ } else {
+ data->num_pmkid = num_pmkid;
+ data->pmkid = pos;
+ pos += data->num_pmkid * PMKID_LEN;
+ left -= data->num_pmkid * PMKID_LEN;
+ }
+ }
+
+#ifdef CONFIG_IEEE80211W
+ if (left >= 4) {
+ data->mgmt_group_cipher = rsn_selector_to_bitfield(pos);
+ if (!wpa_cipher_valid_mgmt_group(data->mgmt_group_cipher)) {
+ wpa_printf(MSG_DEBUG, "%s: Unsupported management "
+ "group cipher 0x%x", __func__,
+ data->mgmt_group_cipher);
+ return -10;
+ }
+ pos += RSN_SELECTOR_LEN;
+ left -= RSN_SELECTOR_LEN;
+ }
+#endif /* CONFIG_IEEE80211W */
+
+ if (left > 0) {
+ wpa_hexdump(MSG_DEBUG,
+ "wpa_parse_wpa_ie_rsn: ignore trailing bytes",
+ pos, left);
+ }
+
+ return 0;
+}
+
+
+static int wpa_selector_to_bitfield(const u8 *s)
+{
+ if (RSN_SELECTOR_GET(s) == WPA_CIPHER_SUITE_NONE)
+ return WPA_CIPHER_NONE;
+ if (RSN_SELECTOR_GET(s) == WPA_CIPHER_SUITE_TKIP)
+ return WPA_CIPHER_TKIP;
+ if (RSN_SELECTOR_GET(s) == WPA_CIPHER_SUITE_CCMP)
+ return WPA_CIPHER_CCMP;
+ return 0;
+}
+
+
+static int wpa_key_mgmt_to_bitfield(const u8 *s)
+{
+ if (RSN_SELECTOR_GET(s) == WPA_AUTH_KEY_MGMT_UNSPEC_802_1X)
+ return WPA_KEY_MGMT_IEEE8021X;
+ if (RSN_SELECTOR_GET(s) == WPA_AUTH_KEY_MGMT_PSK_OVER_802_1X)
+ return WPA_KEY_MGMT_PSK;
+ if (RSN_SELECTOR_GET(s) == WPA_AUTH_KEY_MGMT_NONE)
+ return WPA_KEY_MGMT_WPA_NONE;
+ return 0;
+}
+
+
+int wpa_parse_wpa_ie_wpa(const u8 *wpa_ie, size_t wpa_ie_len,
+ struct wpa_ie_data *data)
+{
+ const struct wpa_ie_hdr *hdr;
+ const u8 *pos;
+ int left;
+ int i, count;
+
+ os_memset(data, 0, sizeof(*data));
+ data->proto = WPA_PROTO_WPA;
+ data->pairwise_cipher = WPA_CIPHER_TKIP;
+ data->group_cipher = WPA_CIPHER_TKIP;
+ data->key_mgmt = WPA_KEY_MGMT_IEEE8021X;
+ data->capabilities = 0;
+ data->pmkid = NULL;
+ data->num_pmkid = 0;
+ data->mgmt_group_cipher = 0;
+
+ if (wpa_ie_len < sizeof(struct wpa_ie_hdr)) {
+ wpa_printf(MSG_DEBUG, "%s: ie len too short %lu",
+ __func__, (unsigned long) wpa_ie_len);
+ return -1;
+ }
+
+ hdr = (const struct wpa_ie_hdr *) wpa_ie;
+
+ if (hdr->elem_id != WLAN_EID_VENDOR_SPECIFIC ||
+ hdr->len != wpa_ie_len - 2 ||
+ RSN_SELECTOR_GET(hdr->oui) != WPA_OUI_TYPE ||
+ WPA_GET_LE16(hdr->version) != WPA_VERSION) {
+ wpa_printf(MSG_DEBUG, "%s: malformed ie or unknown version",
+ __func__);
+ return -2;
+ }
+
+ pos = (const u8 *) (hdr + 1);
+ left = wpa_ie_len - sizeof(*hdr);
+
+ if (left >= WPA_SELECTOR_LEN) {
+ data->group_cipher = wpa_selector_to_bitfield(pos);
+ pos += WPA_SELECTOR_LEN;
+ left -= WPA_SELECTOR_LEN;
+ } else if (left > 0) {
+ wpa_printf(MSG_DEBUG, "%s: ie length mismatch, %u too much",
+ __func__, left);
+ return -3;
+ }
+
+ if (left >= 2) {
+ data->pairwise_cipher = 0;
+ count = WPA_GET_LE16(pos);
+ pos += 2;
+ left -= 2;
+ if (count == 0 || count > left / WPA_SELECTOR_LEN) {
+ wpa_printf(MSG_DEBUG, "%s: ie count botch (pairwise), "
+ "count %u left %u", __func__, count, left);
+ return -4;
+ }
+ for (i = 0; i < count; i++) {
+ data->pairwise_cipher |= wpa_selector_to_bitfield(pos);
+ pos += WPA_SELECTOR_LEN;
+ left -= WPA_SELECTOR_LEN;
+ }
+ } else if (left == 1) {
+ wpa_printf(MSG_DEBUG, "%s: ie too short (for key mgmt)",
+ __func__);
+ return -5;
+ }
+
+ if (left >= 2) {
+ data->key_mgmt = 0;
+ count = WPA_GET_LE16(pos);
+ pos += 2;
+ left -= 2;
+ if (count == 0 || count > left / WPA_SELECTOR_LEN) {
+ wpa_printf(MSG_DEBUG, "%s: ie count botch (key mgmt), "
+ "count %u left %u", __func__, count, left);
+ return -6;
+ }
+ for (i = 0; i < count; i++) {
+ data->key_mgmt |= wpa_key_mgmt_to_bitfield(pos);
+ pos += WPA_SELECTOR_LEN;
+ left -= WPA_SELECTOR_LEN;
+ }
+ } else if (left == 1) {
+ wpa_printf(MSG_DEBUG, "%s: ie too short (for capabilities)",
+ __func__);
+ return -7;
+ }
+
+ if (left >= 2) {
+ data->capabilities = WPA_GET_LE16(pos);
+ pos += 2;
+ left -= 2;
+ }
+
+ if (left > 0) {
+ wpa_hexdump(MSG_DEBUG,
+ "wpa_parse_wpa_ie_wpa: ignore trailing bytes",
+ pos, left);
+ }
+
+ return 0;
+}
+
+
+#ifdef CONFIG_IEEE80211R
+
+/**
+ * wpa_derive_pmk_r0 - Derive PMK-R0 and PMKR0Name
+ *
+ * IEEE Std 802.11r-2008 - 8.5.1.5.3
+ */
+void wpa_derive_pmk_r0(const u8 *xxkey, size_t xxkey_len,
+ const u8 *ssid, size_t ssid_len,
+ const u8 *mdid, const u8 *r0kh_id, size_t r0kh_id_len,
+ const u8 *s0kh_id, u8 *pmk_r0, u8 *pmk_r0_name)
+{
+ u8 buf[1 + SSID_MAX_LEN + MOBILITY_DOMAIN_ID_LEN + 1 +
+ FT_R0KH_ID_MAX_LEN + ETH_ALEN];
+ u8 *pos, r0_key_data[48], hash[32];
+ const u8 *addr[2];
+ size_t len[2];
+
+ /*
+ * R0-Key-Data = KDF-384(XXKey, "FT-R0",
+ * SSIDlength || SSID || MDID || R0KHlength ||
+ * R0KH-ID || S0KH-ID)
+ * XXKey is either the second 256 bits of MSK or PSK.
+ * PMK-R0 = L(R0-Key-Data, 0, 256)
+ * PMK-R0Name-Salt = L(R0-Key-Data, 256, 128)
+ */
+ if (ssid_len > SSID_MAX_LEN || r0kh_id_len > FT_R0KH_ID_MAX_LEN)
+ return;
+ pos = buf;
+ *pos++ = ssid_len;
+ os_memcpy(pos, ssid, ssid_len);
+ pos += ssid_len;
+ os_memcpy(pos, mdid, MOBILITY_DOMAIN_ID_LEN);
+ pos += MOBILITY_DOMAIN_ID_LEN;
+ *pos++ = r0kh_id_len;
+ os_memcpy(pos, r0kh_id, r0kh_id_len);
+ pos += r0kh_id_len;
+ os_memcpy(pos, s0kh_id, ETH_ALEN);
+ pos += ETH_ALEN;
+
+ sha256_prf(xxkey, xxkey_len, "FT-R0", buf, pos - buf,
+ r0_key_data, sizeof(r0_key_data));
+ os_memcpy(pmk_r0, r0_key_data, PMK_LEN);
+
+ /*
+ * PMKR0Name = Truncate-128(SHA-256("FT-R0N" || PMK-R0Name-Salt)
+ */
+ addr[0] = (const u8 *) "FT-R0N";
+ len[0] = 6;
+ addr[1] = r0_key_data + PMK_LEN;
+ len[1] = 16;
+
+ sha256_vector(2, addr, len, hash);
+ os_memcpy(pmk_r0_name, hash, WPA_PMK_NAME_LEN);
+}
+
+
+/**
+ * wpa_derive_pmk_r1_name - Derive PMKR1Name
+ *
+ * IEEE Std 802.11r-2008 - 8.5.1.5.4
+ */
+void wpa_derive_pmk_r1_name(const u8 *pmk_r0_name, const u8 *r1kh_id,
+ const u8 *s1kh_id, u8 *pmk_r1_name)
+{
+ u8 hash[32];
+ const u8 *addr[4];
+ size_t len[4];
+
+ /*
+ * PMKR1Name = Truncate-128(SHA-256("FT-R1N" || PMKR0Name ||
+ * R1KH-ID || S1KH-ID))
+ */
+ addr[0] = (const u8 *) "FT-R1N";
+ len[0] = 6;
+ addr[1] = pmk_r0_name;
+ len[1] = WPA_PMK_NAME_LEN;
+ addr[2] = r1kh_id;
+ len[2] = FT_R1KH_ID_LEN;
+ addr[3] = s1kh_id;
+ len[3] = ETH_ALEN;
+
+ sha256_vector(4, addr, len, hash);
+ os_memcpy(pmk_r1_name, hash, WPA_PMK_NAME_LEN);
+}
+
+
+/**
+ * wpa_derive_pmk_r1 - Derive PMK-R1 and PMKR1Name from PMK-R0
+ *
+ * IEEE Std 802.11r-2008 - 8.5.1.5.4
+ */
+void wpa_derive_pmk_r1(const u8 *pmk_r0, const u8 *pmk_r0_name,
+ const u8 *r1kh_id, const u8 *s1kh_id,
+ u8 *pmk_r1, u8 *pmk_r1_name)
+{
+ u8 buf[FT_R1KH_ID_LEN + ETH_ALEN];
+ u8 *pos;
+
+ /* PMK-R1 = KDF-256(PMK-R0, "FT-R1", R1KH-ID || S1KH-ID) */
+ pos = buf;
+ os_memcpy(pos, r1kh_id, FT_R1KH_ID_LEN);
+ pos += FT_R1KH_ID_LEN;
+ os_memcpy(pos, s1kh_id, ETH_ALEN);
+ pos += ETH_ALEN;
+
+ sha256_prf(pmk_r0, PMK_LEN, "FT-R1", buf, pos - buf, pmk_r1, PMK_LEN);
+
+ wpa_derive_pmk_r1_name(pmk_r0_name, r1kh_id, s1kh_id, pmk_r1_name);
+}
+
+
+/**
+ * wpa_pmk_r1_to_ptk - Derive PTK and PTKName from PMK-R1
+ *
+ * IEEE Std 802.11r-2008 - 8.5.1.5.5
+ */
+int wpa_pmk_r1_to_ptk(const u8 *pmk_r1, const u8 *snonce, const u8 *anonce,
+ const u8 *sta_addr, const u8 *bssid,
+ const u8 *pmk_r1_name,
+ struct wpa_ptk *ptk, u8 *ptk_name, int akmp, int cipher)
+{
+ u8 buf[2 * WPA_NONCE_LEN + 2 * ETH_ALEN];
+ u8 *pos, hash[32];
+ const u8 *addr[6];
+ size_t len[6];
+ u8 tmp[WPA_KCK_MAX_LEN + WPA_KEK_MAX_LEN + WPA_TK_MAX_LEN];
+ size_t ptk_len;
+
+ /*
+ * PTK = KDF-PTKLen(PMK-R1, "FT-PTK", SNonce || ANonce ||
+ * BSSID || STA-ADDR)
+ */
+ pos = buf;
+ os_memcpy(pos, snonce, WPA_NONCE_LEN);
+ pos += WPA_NONCE_LEN;
+ os_memcpy(pos, anonce, WPA_NONCE_LEN);
+ pos += WPA_NONCE_LEN;
+ os_memcpy(pos, bssid, ETH_ALEN);
+ pos += ETH_ALEN;
+ os_memcpy(pos, sta_addr, ETH_ALEN);
+ pos += ETH_ALEN;
+
+ ptk->kck_len = wpa_kck_len(akmp);
+ ptk->kek_len = wpa_kek_len(akmp);
+ ptk->tk_len = wpa_cipher_key_len(cipher);
+ ptk_len = ptk->kck_len + ptk->kek_len + ptk->tk_len;
+
+ sha256_prf(pmk_r1, PMK_LEN, "FT-PTK", buf, pos - buf, tmp, ptk_len);
+
+ /*
+ * PTKName = Truncate-128(SHA-256(PMKR1Name || "FT-PTKN" || SNonce ||
+ * ANonce || BSSID || STA-ADDR))
+ */
+ addr[0] = pmk_r1_name;
+ len[0] = WPA_PMK_NAME_LEN;
+ addr[1] = (const u8 *) "FT-PTKN";
+ len[1] = 7;
+ addr[2] = snonce;
+ len[2] = WPA_NONCE_LEN;
+ addr[3] = anonce;
+ len[3] = WPA_NONCE_LEN;
+ addr[4] = bssid;
+ len[4] = ETH_ALEN;
+ addr[5] = sta_addr;
+ len[5] = ETH_ALEN;
+
+ sha256_vector(6, addr, len, hash);
+ os_memcpy(ptk_name, hash, WPA_PMK_NAME_LEN);
+
+ os_memcpy(ptk->kck, tmp, ptk->kck_len);
+ os_memcpy(ptk->kek, tmp + ptk->kck_len, ptk->kek_len);
+ os_memcpy(ptk->tk, tmp + ptk->kck_len + ptk->kek_len, ptk->tk_len);
+
+ wpa_hexdump_key(MSG_DEBUG, "FT: KCK", ptk->kck, ptk->kck_len);
+ wpa_hexdump_key(MSG_DEBUG, "FT: KEK", ptk->kek, ptk->kek_len);
+ wpa_hexdump_key(MSG_DEBUG, "FT: TK", ptk->tk, ptk->tk_len);
+ wpa_hexdump(MSG_DEBUG, "FT: PTKName", ptk_name, WPA_PMK_NAME_LEN);
+
+ os_memset(tmp, 0, sizeof(tmp));
+
+ return 0;
+}
+
+#endif /* CONFIG_IEEE80211R */
+
+
+/**
+ * rsn_pmkid - Calculate PMK identifier
+ * @pmk: Pairwise master key
+ * @pmk_len: Length of pmk in bytes
+ * @aa: Authenticator address
+ * @spa: Supplicant address
+ * @pmkid: Buffer for PMKID
+ * @use_sha256: Whether to use SHA256-based KDF
+ *
+ * IEEE Std 802.11i-2004 - 8.5.1.2 Pairwise key hierarchy
+ * PMKID = HMAC-SHA1-128(PMK, "PMK Name" || AA || SPA)
+ */
+void rsn_pmkid(const u8 *pmk, size_t pmk_len, const u8 *aa, const u8 *spa,
+ u8 *pmkid, int use_sha256)
+{
+ char *title = "PMK Name";
+ const u8 *addr[3];
+ const size_t len[3] = { 8, ETH_ALEN, ETH_ALEN };
+ unsigned char hash[SHA256_MAC_LEN];
+
+ addr[0] = (u8 *) title;
+ addr[1] = aa;
+ addr[2] = spa;
+
+#ifdef CONFIG_IEEE80211W
+ if (use_sha256)
+ hmac_sha256_vector(pmk, pmk_len, 3, addr, len, hash);
+ else
+#endif /* CONFIG_IEEE80211W */
+ hmac_sha1_vector(pmk, pmk_len, 3, addr, len, hash);
+ os_memcpy(pmkid, hash, PMKID_LEN);
+}
+
+
+#ifdef CONFIG_SUITEB
+/**
+ * rsn_pmkid_suite_b - Calculate PMK identifier for Suite B AKM
+ * @kck: Key confirmation key
+ * @kck_len: Length of kck in bytes
+ * @aa: Authenticator address
+ * @spa: Supplicant address
+ * @pmkid: Buffer for PMKID
+ * Returns: 0 on success, -1 on failure
+ *
+ * IEEE Std 802.11ac-2013 - 11.6.1.3 Pairwise key hierarchy
+ * PMKID = Truncate(HMAC-SHA-256(KCK, "PMK Name" || AA || SPA))
+ */
+int rsn_pmkid_suite_b(const u8 *kck, size_t kck_len, const u8 *aa,
+ const u8 *spa, u8 *pmkid)
+{
+ char *title = "PMK Name";
+ const u8 *addr[3];
+ const size_t len[3] = { 8, ETH_ALEN, ETH_ALEN };
+ unsigned char hash[SHA256_MAC_LEN];
+
+ addr[0] = (u8 *) title;
+ addr[1] = aa;
+ addr[2] = spa;
+
+ if (hmac_sha256_vector(kck, kck_len, 3, addr, len, hash) < 0)
+ return -1;
+ os_memcpy(pmkid, hash, PMKID_LEN);
+ return 0;
+}
+#endif /* CONFIG_SUITEB */
+
+
+#ifdef CONFIG_SUITEB192
+/**
+ * rsn_pmkid_suite_b_192 - Calculate PMK identifier for Suite B AKM
+ * @kck: Key confirmation key
+ * @kck_len: Length of kck in bytes
+ * @aa: Authenticator address
+ * @spa: Supplicant address
+ * @pmkid: Buffer for PMKID
+ * Returns: 0 on success, -1 on failure
+ *
+ * IEEE Std 802.11ac-2013 - 11.6.1.3 Pairwise key hierarchy
+ * PMKID = Truncate(HMAC-SHA-384(KCK, "PMK Name" || AA || SPA))
+ */
+int rsn_pmkid_suite_b_192(const u8 *kck, size_t kck_len, const u8 *aa,
+ const u8 *spa, u8 *pmkid)
+{
+ char *title = "PMK Name";
+ const u8 *addr[3];
+ const size_t len[3] = { 8, ETH_ALEN, ETH_ALEN };
+ unsigned char hash[SHA384_MAC_LEN];
+
+ addr[0] = (u8 *) title;
+ addr[1] = aa;
+ addr[2] = spa;
+
+ if (hmac_sha384_vector(kck, kck_len, 3, addr, len, hash) < 0)
+ return -1;
+ os_memcpy(pmkid, hash, PMKID_LEN);
+ return 0;
+}
+#endif /* CONFIG_SUITEB192 */
+
+
+/**
+ * wpa_cipher_txt - Convert cipher suite to a text string
+ * @cipher: Cipher suite (WPA_CIPHER_* enum)
+ * Returns: Pointer to a text string of the cipher suite name
+ */
+const char * wpa_cipher_txt(int cipher)
+{
+ switch (cipher) {
+ case WPA_CIPHER_NONE:
+ return "NONE";
+ case WPA_CIPHER_WEP40:
+ return "WEP-40";
+ case WPA_CIPHER_WEP104:
+ return "WEP-104";
+ case WPA_CIPHER_TKIP:
+ return "TKIP";
+ case WPA_CIPHER_CCMP:
+ return "CCMP";
+ case WPA_CIPHER_CCMP | WPA_CIPHER_TKIP:
+ return "CCMP+TKIP";
+ case WPA_CIPHER_GCMP:
+ return "GCMP";
+ case WPA_CIPHER_GCMP_256:
+ return "GCMP-256";
+ case WPA_CIPHER_CCMP_256:
+ return "CCMP-256";
+ case WPA_CIPHER_GTK_NOT_USED:
+ return "GTK_NOT_USED";
+ default:
+ return "UNKNOWN";
+ }
+}
+
+
+/**
+ * wpa_key_mgmt_txt - Convert key management suite to a text string
+ * @key_mgmt: Key management suite (WPA_KEY_MGMT_* enum)
+ * @proto: WPA/WPA2 version (WPA_PROTO_*)
+ * Returns: Pointer to a text string of the key management suite name
+ */
+const char * wpa_key_mgmt_txt(int key_mgmt, int proto)
+{
+ switch (key_mgmt) {
+ case WPA_KEY_MGMT_IEEE8021X:
+ if (proto == (WPA_PROTO_RSN | WPA_PROTO_WPA))
+ return "WPA2+WPA/IEEE 802.1X/EAP";
+ return proto == WPA_PROTO_RSN ?
+ "WPA2/IEEE 802.1X/EAP" : "WPA/IEEE 802.1X/EAP";
+ case WPA_KEY_MGMT_PSK:
+ if (proto == (WPA_PROTO_RSN | WPA_PROTO_WPA))
+ return "WPA2-PSK+WPA-PSK";
+ return proto == WPA_PROTO_RSN ?
+ "WPA2-PSK" : "WPA-PSK";
+ case WPA_KEY_MGMT_NONE:
+ return "NONE";
+ case WPA_KEY_MGMT_IEEE8021X_NO_WPA:
+ return "IEEE 802.1X (no WPA)";
+#ifdef CONFIG_IEEE80211R
+ case WPA_KEY_MGMT_FT_IEEE8021X:
+ return "FT-EAP";
+ case WPA_KEY_MGMT_FT_PSK:
+ return "FT-PSK";
+#endif /* CONFIG_IEEE80211R */
+#ifdef CONFIG_IEEE80211W
+ case WPA_KEY_MGMT_IEEE8021X_SHA256:
+ return "WPA2-EAP-SHA256";
+ case WPA_KEY_MGMT_PSK_SHA256:
+ return "WPA2-PSK-SHA256";
+#endif /* CONFIG_IEEE80211W */
+ case WPA_KEY_MGMT_WPS:
+ return "WPS";
+ case WPA_KEY_MGMT_SAE:
+ return "SAE";
+ case WPA_KEY_MGMT_FT_SAE:
+ return "FT-SAE";
+ case WPA_KEY_MGMT_OSEN:
+ return "OSEN";
+ case WPA_KEY_MGMT_IEEE8021X_SUITE_B:
+ return "WPA2-EAP-SUITE-B";
+ case WPA_KEY_MGMT_IEEE8021X_SUITE_B_192:
+ return "WPA2-EAP-SUITE-B-192";
+ default:
+ return "UNKNOWN";
+ }
+}
+
+
+u32 wpa_akm_to_suite(int akm)
+{
+ if (akm & WPA_KEY_MGMT_FT_IEEE8021X)
+ return WLAN_AKM_SUITE_FT_8021X;
+ if (akm & WPA_KEY_MGMT_FT_PSK)
+ return WLAN_AKM_SUITE_FT_PSK;
+ if (akm & WPA_KEY_MGMT_IEEE8021X)
+ return WLAN_AKM_SUITE_8021X;
+ if (akm & WPA_KEY_MGMT_IEEE8021X_SHA256)
+ return WLAN_AKM_SUITE_8021X_SHA256;
+ if (akm & WPA_KEY_MGMT_IEEE8021X)
+ return WLAN_AKM_SUITE_8021X;
+ if (akm & WPA_KEY_MGMT_PSK_SHA256)
+ return WLAN_AKM_SUITE_PSK_SHA256;
+ if (akm & WPA_KEY_MGMT_PSK)
+ return WLAN_AKM_SUITE_PSK;
+ if (akm & WPA_KEY_MGMT_CCKM)
+ return WLAN_AKM_SUITE_CCKM;
+ if (akm & WPA_KEY_MGMT_OSEN)
+ return WLAN_AKM_SUITE_OSEN;
+ if (akm & WPA_KEY_MGMT_IEEE8021X_SUITE_B)
+ return WLAN_AKM_SUITE_8021X_SUITE_B;
+ if (akm & WPA_KEY_MGMT_IEEE8021X_SUITE_B_192)
+ return WLAN_AKM_SUITE_8021X_SUITE_B_192;
+ return 0;
+}
+
+
+int wpa_compare_rsn_ie(int ft_initial_assoc,
+ const u8 *ie1, size_t ie1len,
+ const u8 *ie2, size_t ie2len)
+{
+ if (ie1 == NULL || ie2 == NULL)
+ return -1;
+
+ if (ie1len == ie2len && os_memcmp(ie1, ie2, ie1len) == 0)
+ return 0; /* identical IEs */
+
+#ifdef CONFIG_IEEE80211R
+ if (ft_initial_assoc) {
+ struct wpa_ie_data ie1d, ie2d;
+ /*
+ * The PMKID-List in RSN IE is different between Beacon/Probe
+ * Response/(Re)Association Request frames and EAPOL-Key
+ * messages in FT initial mobility domain association. Allow
+ * for this, but verify that other parts of the RSN IEs are
+ * identical.
+ */
+ if (wpa_parse_wpa_ie_rsn(ie1, ie1len, &ie1d) < 0 ||
+ wpa_parse_wpa_ie_rsn(ie2, ie2len, &ie2d) < 0)
+ return -1;
+ if (ie1d.proto == ie2d.proto &&
+ ie1d.pairwise_cipher == ie2d.pairwise_cipher &&
+ ie1d.group_cipher == ie2d.group_cipher &&
+ ie1d.key_mgmt == ie2d.key_mgmt &&
+ ie1d.capabilities == ie2d.capabilities &&
+ ie1d.mgmt_group_cipher == ie2d.mgmt_group_cipher)
+ return 0;
+ }
+#endif /* CONFIG_IEEE80211R */
+
+ return -1;
+}
+
+
+#ifdef CONFIG_IEEE80211R
+int wpa_insert_pmkid(u8 *ies, size_t ies_len, const u8 *pmkid)
+{
+ u8 *start, *end, *rpos, *rend;
+ int added = 0;
+
+ start = ies;
+ end = ies + ies_len;
+
+ while (start < end) {
+ if (*start == WLAN_EID_RSN)
+ break;
+ start += 2 + start[1];
+ }
+ if (start >= end) {
+ wpa_printf(MSG_ERROR, "FT: Could not find RSN IE in "
+ "IEs data");
+ return -1;
+ }
+ wpa_hexdump(MSG_DEBUG, "FT: RSN IE before modification",
+ start, 2 + start[1]);
+
+ /* Find start of PMKID-Count */
+ rpos = start + 2;
+ rend = rpos + start[1];
+
+ /* Skip Version and Group Data Cipher Suite */
+ rpos += 2 + 4;
+ /* Skip Pairwise Cipher Suite Count and List */
+ rpos += 2 + WPA_GET_LE16(rpos) * RSN_SELECTOR_LEN;
+ /* Skip AKM Suite Count and List */
+ rpos += 2 + WPA_GET_LE16(rpos) * RSN_SELECTOR_LEN;
+
+ if (rpos == rend) {
+ /* Add RSN Capabilities */
+ os_memmove(rpos + 2, rpos, end - rpos);
+ *rpos++ = 0;
+ *rpos++ = 0;
+ added += 2;
+ start[1] += 2;
+ rend = rpos;
+ } else {
+ /* Skip RSN Capabilities */
+ rpos += 2;
+ if (rpos > rend) {
+ wpa_printf(MSG_ERROR, "FT: Could not parse RSN IE in "
+ "IEs data");
+ return -1;
+ }
+ }
+
+ if (rpos == rend) {
+ /* No PMKID-Count field included; add it */
+ os_memmove(rpos + 2 + PMKID_LEN, rpos, end + added - rpos);
+ WPA_PUT_LE16(rpos, 1);
+ rpos += 2;
+ os_memcpy(rpos, pmkid, PMKID_LEN);
+ added += 2 + PMKID_LEN;
+ start[1] += 2 + PMKID_LEN;
+ } else {
+ /* PMKID-Count was included; use it */
+ if (WPA_GET_LE16(rpos) != 0) {
+ wpa_printf(MSG_ERROR, "FT: Unexpected PMKID "
+ "in RSN IE in EAPOL-Key data");
+ return -1;
+ }
+ WPA_PUT_LE16(rpos, 1);
+ rpos += 2;
+ os_memmove(rpos + PMKID_LEN, rpos, end + added - rpos);
+ os_memcpy(rpos, pmkid, PMKID_LEN);
+ added += PMKID_LEN;
+ start[1] += PMKID_LEN;
+ }
+
+ wpa_hexdump(MSG_DEBUG, "FT: RSN IE after modification "
+ "(PMKID inserted)", start, 2 + start[1]);
+
+ return added;
+}
+#endif /* CONFIG_IEEE80211R */
+
+
+int wpa_cipher_key_len(int cipher)
+{
+ switch (cipher) {
+ case WPA_CIPHER_CCMP_256:
+ case WPA_CIPHER_GCMP_256:
+ case WPA_CIPHER_BIP_GMAC_256:
+ case WPA_CIPHER_BIP_CMAC_256:
+ return 32;
+ case WPA_CIPHER_CCMP:
+ case WPA_CIPHER_GCMP:
+ case WPA_CIPHER_AES_128_CMAC:
+ case WPA_CIPHER_BIP_GMAC_128:
+ return 16;
+ case WPA_CIPHER_TKIP:
+ return 32;
+ }
+
+ return 0;
+}
+
+
+int wpa_cipher_rsc_len(int cipher)
+{
+ switch (cipher) {
+ case WPA_CIPHER_CCMP_256:
+ case WPA_CIPHER_GCMP_256:
+ case WPA_CIPHER_CCMP:
+ case WPA_CIPHER_GCMP:
+ case WPA_CIPHER_TKIP:
+ return 6;
+ }
+
+ return 0;
+}
+
+
+int wpa_cipher_to_alg(int cipher)
+{
+ switch (cipher) {
+ case WPA_CIPHER_CCMP_256:
+ return WPA_ALG_CCMP_256;
+ case WPA_CIPHER_GCMP_256:
+ return WPA_ALG_GCMP_256;
+ case WPA_CIPHER_CCMP:
+ return WPA_ALG_CCMP;
+ case WPA_CIPHER_GCMP:
+ return WPA_ALG_GCMP;
+ case WPA_CIPHER_TKIP:
+ return WPA_ALG_TKIP;
+ case WPA_CIPHER_AES_128_CMAC:
+ return WPA_ALG_IGTK;
+ case WPA_CIPHER_BIP_GMAC_128:
+ return WPA_ALG_BIP_GMAC_128;
+ case WPA_CIPHER_BIP_GMAC_256:
+ return WPA_ALG_BIP_GMAC_256;
+ case WPA_CIPHER_BIP_CMAC_256:
+ return WPA_ALG_BIP_CMAC_256;
+ }
+ return WPA_ALG_NONE;
+}
+
+
+int wpa_cipher_valid_pairwise(int cipher)
+{
+ return cipher == WPA_CIPHER_CCMP_256 ||
+ cipher == WPA_CIPHER_GCMP_256 ||
+ cipher == WPA_CIPHER_CCMP ||
+ cipher == WPA_CIPHER_GCMP ||
+ cipher == WPA_CIPHER_TKIP;
+}
+
+
+u32 wpa_cipher_to_suite(int proto, int cipher)
+{
+ if (cipher & WPA_CIPHER_CCMP_256)
+ return RSN_CIPHER_SUITE_CCMP_256;
+ if (cipher & WPA_CIPHER_GCMP_256)
+ return RSN_CIPHER_SUITE_GCMP_256;
+ if (cipher & WPA_CIPHER_CCMP)
+ return (proto == WPA_PROTO_RSN ?
+ RSN_CIPHER_SUITE_CCMP : WPA_CIPHER_SUITE_CCMP);
+ if (cipher & WPA_CIPHER_GCMP)
+ return RSN_CIPHER_SUITE_GCMP;
+ if (cipher & WPA_CIPHER_TKIP)
+ return (proto == WPA_PROTO_RSN ?
+ RSN_CIPHER_SUITE_TKIP : WPA_CIPHER_SUITE_TKIP);
+ if (cipher & WPA_CIPHER_NONE)
+ return (proto == WPA_PROTO_RSN ?
+ RSN_CIPHER_SUITE_NONE : WPA_CIPHER_SUITE_NONE);
+ if (cipher & WPA_CIPHER_GTK_NOT_USED)
+ return RSN_CIPHER_SUITE_NO_GROUP_ADDRESSED;
+ if (cipher & WPA_CIPHER_AES_128_CMAC)
+ return RSN_CIPHER_SUITE_AES_128_CMAC;
+ if (cipher & WPA_CIPHER_BIP_GMAC_128)
+ return RSN_CIPHER_SUITE_BIP_GMAC_128;
+ if (cipher & WPA_CIPHER_BIP_GMAC_256)
+ return RSN_CIPHER_SUITE_BIP_GMAC_256;
+ if (cipher & WPA_CIPHER_BIP_CMAC_256)
+ return RSN_CIPHER_SUITE_BIP_CMAC_256;
+ return 0;
+}
+
+
+int rsn_cipher_put_suites(u8 *start, int ciphers)
+{
+ u8 *pos = start;
+
+ if (ciphers & WPA_CIPHER_CCMP_256) {
+ RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_CCMP_256);
+ pos += RSN_SELECTOR_LEN;
+ }
+ if (ciphers & WPA_CIPHER_GCMP_256) {
+ RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_GCMP_256);
+ pos += RSN_SELECTOR_LEN;
+ }
+ if (ciphers & WPA_CIPHER_CCMP) {
+ RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_CCMP);
+ pos += RSN_SELECTOR_LEN;
+ }
+ if (ciphers & WPA_CIPHER_GCMP) {
+ RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_GCMP);
+ pos += RSN_SELECTOR_LEN;
+ }
+ if (ciphers & WPA_CIPHER_TKIP) {
+ RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_TKIP);
+ pos += RSN_SELECTOR_LEN;
+ }
+ if (ciphers & WPA_CIPHER_NONE) {
+ RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_NONE);
+ pos += RSN_SELECTOR_LEN;
+ }
+
+ return (pos - start) / RSN_SELECTOR_LEN;
+}
+
+
+int wpa_cipher_put_suites(u8 *start, int ciphers)
+{
+ u8 *pos = start;
+
+ if (ciphers & WPA_CIPHER_CCMP) {
+ RSN_SELECTOR_PUT(pos, WPA_CIPHER_SUITE_CCMP);
+ pos += WPA_SELECTOR_LEN;
+ }
+ if (ciphers & WPA_CIPHER_TKIP) {
+ RSN_SELECTOR_PUT(pos, WPA_CIPHER_SUITE_TKIP);
+ pos += WPA_SELECTOR_LEN;
+ }
+ if (ciphers & WPA_CIPHER_NONE) {
+ RSN_SELECTOR_PUT(pos, WPA_CIPHER_SUITE_NONE);
+ pos += WPA_SELECTOR_LEN;
+ }
+
+ return (pos - start) / RSN_SELECTOR_LEN;
+}
+
+
+int wpa_pick_pairwise_cipher(int ciphers, int none_allowed)
+{
+ if (ciphers & WPA_CIPHER_CCMP_256)
+ return WPA_CIPHER_CCMP_256;
+ if (ciphers & WPA_CIPHER_GCMP_256)
+ return WPA_CIPHER_GCMP_256;
+ if (ciphers & WPA_CIPHER_CCMP)
+ return WPA_CIPHER_CCMP;
+ if (ciphers & WPA_CIPHER_GCMP)
+ return WPA_CIPHER_GCMP;
+ if (ciphers & WPA_CIPHER_TKIP)
+ return WPA_CIPHER_TKIP;
+ if (none_allowed && (ciphers & WPA_CIPHER_NONE))
+ return WPA_CIPHER_NONE;
+ return -1;
+}
+
+
+int wpa_pick_group_cipher(int ciphers)
+{
+ if (ciphers & WPA_CIPHER_CCMP_256)
+ return WPA_CIPHER_CCMP_256;
+ if (ciphers & WPA_CIPHER_GCMP_256)
+ return WPA_CIPHER_GCMP_256;
+ if (ciphers & WPA_CIPHER_CCMP)
+ return WPA_CIPHER_CCMP;
+ if (ciphers & WPA_CIPHER_GCMP)
+ return WPA_CIPHER_GCMP;
+ if (ciphers & WPA_CIPHER_GTK_NOT_USED)
+ return WPA_CIPHER_GTK_NOT_USED;
+ if (ciphers & WPA_CIPHER_TKIP)
+ return WPA_CIPHER_TKIP;
+ return -1;
+}
+
+
+int wpa_parse_cipher(const char *value)
+{
+ int val = 0, last;
+ char *start, *end, *buf;
+
+ buf = os_strdup(value);
+ if (buf == NULL)
+ return -1;
+ start = buf;
+
+ while (*start != '\0') {
+ while (*start == ' ' || *start == '\t')
+ start++;
+ if (*start == '\0')
+ break;
+ end = start;
+ while (*end != ' ' && *end != '\t' && *end != '\0')
+ end++;
+ last = *end == '\0';
+ *end = '\0';
+ if (os_strcmp(start, "CCMP-256") == 0)
+ val |= WPA_CIPHER_CCMP_256;
+ else if (os_strcmp(start, "GCMP-256") == 0)
+ val |= WPA_CIPHER_GCMP_256;
+ else if (os_strcmp(start, "CCMP") == 0)
+ val |= WPA_CIPHER_CCMP;
+ else if (os_strcmp(start, "GCMP") == 0)
+ val |= WPA_CIPHER_GCMP;
+ else if (os_strcmp(start, "TKIP") == 0)
+ val |= WPA_CIPHER_TKIP;
+ else if (os_strcmp(start, "WEP104") == 0)
+ val |= WPA_CIPHER_WEP104;
+ else if (os_strcmp(start, "WEP40") == 0)
+ val |= WPA_CIPHER_WEP40;
+ else if (os_strcmp(start, "NONE") == 0)
+ val |= WPA_CIPHER_NONE;
+ else if (os_strcmp(start, "GTK_NOT_USED") == 0)
+ val |= WPA_CIPHER_GTK_NOT_USED;
+ else {
+ os_free(buf);
+ return -1;
+ }
+
+ if (last)
+ break;
+ start = end + 1;
+ }
+ os_free(buf);
+
+ return val;
+}
+
+
+int wpa_write_ciphers(char *start, char *end, int ciphers, const char *delim)
+{
+ char *pos = start;
+ int ret;
+
+ if (ciphers & WPA_CIPHER_CCMP_256) {
+ ret = os_snprintf(pos, end - pos, "%sCCMP-256",
+ pos == start ? "" : delim);
+ if (os_snprintf_error(end - pos, ret))
+ return -1;
+ pos += ret;
+ }
+ if (ciphers & WPA_CIPHER_GCMP_256) {
+ ret = os_snprintf(pos, end - pos, "%sGCMP-256",
+ pos == start ? "" : delim);
+ if (os_snprintf_error(end - pos, ret))
+ return -1;
+ pos += ret;
+ }
+ if (ciphers & WPA_CIPHER_CCMP) {
+ ret = os_snprintf(pos, end - pos, "%sCCMP",
+ pos == start ? "" : delim);
+ if (os_snprintf_error(end - pos, ret))
+ return -1;
+ pos += ret;
+ }
+ if (ciphers & WPA_CIPHER_GCMP) {
+ ret = os_snprintf(pos, end - pos, "%sGCMP",
+ pos == start ? "" : delim);
+ if (os_snprintf_error(end - pos, ret))
+ return -1;
+ pos += ret;
+ }
+ if (ciphers & WPA_CIPHER_TKIP) {
+ ret = os_snprintf(pos, end - pos, "%sTKIP",
+ pos == start ? "" : delim);
+ if (os_snprintf_error(end - pos, ret))
+ return -1;
+ pos += ret;
+ }
+ if (ciphers & WPA_CIPHER_NONE) {
+ ret = os_snprintf(pos, end - pos, "%sNONE",
+ pos == start ? "" : delim);
+ if (os_snprintf_error(end - pos, ret))
+ return -1;
+ pos += ret;
+ }
+
+ return pos - start;
+}
+
+
+int wpa_select_ap_group_cipher(int wpa, int wpa_pairwise, int rsn_pairwise)
+{
+ int pairwise = 0;
+
+ /* Select group cipher based on the enabled pairwise cipher suites */
+ if (wpa & 1)
+ pairwise |= wpa_pairwise;
+ if (wpa & 2)
+ pairwise |= rsn_pairwise;
+
+ if (pairwise & WPA_CIPHER_TKIP)
+ return WPA_CIPHER_TKIP;
+ if ((pairwise & (WPA_CIPHER_CCMP | WPA_CIPHER_GCMP)) == WPA_CIPHER_GCMP)
+ return WPA_CIPHER_GCMP;
+ if ((pairwise & (WPA_CIPHER_GCMP_256 | WPA_CIPHER_CCMP |
+ WPA_CIPHER_GCMP)) == WPA_CIPHER_GCMP_256)
+ return WPA_CIPHER_GCMP_256;
+ if ((pairwise & (WPA_CIPHER_CCMP_256 | WPA_CIPHER_CCMP |
+ WPA_CIPHER_GCMP)) == WPA_CIPHER_CCMP_256)
+ return WPA_CIPHER_CCMP_256;
+ return WPA_CIPHER_CCMP;
+}
diff --git a/freebsd/contrib/wpa/src/common/wpa_common.h b/freebsd/contrib/wpa/src/common/wpa_common.h
new file mode 100644
index 00000000..c08f6514
--- /dev/null
+++ b/freebsd/contrib/wpa/src/common/wpa_common.h
@@ -0,0 +1,451 @@
+/*
+ * WPA definitions shared between hostapd and wpa_supplicant
+ * Copyright (c) 2002-2015, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef WPA_COMMON_H
+#define WPA_COMMON_H
+
+/* IEEE 802.11i */
+#define PMKID_LEN 16
+#define PMK_LEN 32
+#define WPA_REPLAY_COUNTER_LEN 8
+#define WPA_NONCE_LEN 32
+#define WPA_KEY_RSC_LEN 8
+#define WPA_GMK_LEN 32
+#define WPA_GTK_MAX_LEN 32
+
+#define WPA_ALLOWED_PAIRWISE_CIPHERS \
+(WPA_CIPHER_CCMP | WPA_CIPHER_GCMP | WPA_CIPHER_TKIP | WPA_CIPHER_NONE | \
+WPA_CIPHER_GCMP_256 | WPA_CIPHER_CCMP_256)
+#define WPA_ALLOWED_GROUP_CIPHERS \
+(WPA_CIPHER_CCMP | WPA_CIPHER_GCMP | WPA_CIPHER_TKIP | \
+WPA_CIPHER_GCMP_256 | WPA_CIPHER_CCMP_256 | \
+WPA_CIPHER_GTK_NOT_USED)
+
+#define WPA_SELECTOR_LEN 4
+#define WPA_VERSION 1
+#define RSN_SELECTOR_LEN 4
+#define RSN_VERSION 1
+
+#define RSN_SELECTOR(a, b, c, d) \
+ ((((u32) (a)) << 24) | (((u32) (b)) << 16) | (((u32) (c)) << 8) | \
+ (u32) (d))
+
+#define WPA_AUTH_KEY_MGMT_NONE RSN_SELECTOR(0x00, 0x50, 0xf2, 0)
+#define WPA_AUTH_KEY_MGMT_UNSPEC_802_1X RSN_SELECTOR(0x00, 0x50, 0xf2, 1)
+#define WPA_AUTH_KEY_MGMT_PSK_OVER_802_1X RSN_SELECTOR(0x00, 0x50, 0xf2, 2)
+#define WPA_AUTH_KEY_MGMT_CCKM RSN_SELECTOR(0x00, 0x40, 0x96, 0)
+#define WPA_CIPHER_SUITE_NONE RSN_SELECTOR(0x00, 0x50, 0xf2, 0)
+#define WPA_CIPHER_SUITE_TKIP RSN_SELECTOR(0x00, 0x50, 0xf2, 2)
+#define WPA_CIPHER_SUITE_CCMP RSN_SELECTOR(0x00, 0x50, 0xf2, 4)
+
+
+#define RSN_AUTH_KEY_MGMT_UNSPEC_802_1X RSN_SELECTOR(0x00, 0x0f, 0xac, 1)
+#define RSN_AUTH_KEY_MGMT_PSK_OVER_802_1X RSN_SELECTOR(0x00, 0x0f, 0xac, 2)
+#ifdef CONFIG_IEEE80211R
+#define RSN_AUTH_KEY_MGMT_FT_802_1X RSN_SELECTOR(0x00, 0x0f, 0xac, 3)
+#define RSN_AUTH_KEY_MGMT_FT_PSK RSN_SELECTOR(0x00, 0x0f, 0xac, 4)
+#endif /* CONFIG_IEEE80211R */
+#define RSN_AUTH_KEY_MGMT_802_1X_SHA256 RSN_SELECTOR(0x00, 0x0f, 0xac, 5)
+#define RSN_AUTH_KEY_MGMT_PSK_SHA256 RSN_SELECTOR(0x00, 0x0f, 0xac, 6)
+#define RSN_AUTH_KEY_MGMT_TPK_HANDSHAKE RSN_SELECTOR(0x00, 0x0f, 0xac, 7)
+#define RSN_AUTH_KEY_MGMT_SAE RSN_SELECTOR(0x00, 0x0f, 0xac, 8)
+#define RSN_AUTH_KEY_MGMT_FT_SAE RSN_SELECTOR(0x00, 0x0f, 0xac, 9)
+#define RSN_AUTH_KEY_MGMT_802_1X_SUITE_B RSN_SELECTOR(0x00, 0x0f, 0xac, 11)
+#define RSN_AUTH_KEY_MGMT_802_1X_SUITE_B_192 RSN_SELECTOR(0x00, 0x0f, 0xac, 12)
+#define RSN_AUTH_KEY_MGMT_FT_802_1X_SUITE_B_192 \
+RSN_SELECTOR(0x00, 0x0f, 0xac, 13)
+#define RSN_AUTH_KEY_MGMT_CCKM RSN_SELECTOR(0x00, 0x40, 0x96, 0x00)
+#define RSN_AUTH_KEY_MGMT_OSEN RSN_SELECTOR(0x50, 0x6f, 0x9a, 0x01)
+
+#define RSN_CIPHER_SUITE_NONE RSN_SELECTOR(0x00, 0x0f, 0xac, 0)
+#define RSN_CIPHER_SUITE_TKIP RSN_SELECTOR(0x00, 0x0f, 0xac, 2)
+#if 0
+#define RSN_CIPHER_SUITE_WRAP RSN_SELECTOR(0x00, 0x0f, 0xac, 3)
+#endif
+#define RSN_CIPHER_SUITE_CCMP RSN_SELECTOR(0x00, 0x0f, 0xac, 4)
+#define RSN_CIPHER_SUITE_AES_128_CMAC RSN_SELECTOR(0x00, 0x0f, 0xac, 6)
+#define RSN_CIPHER_SUITE_NO_GROUP_ADDRESSED RSN_SELECTOR(0x00, 0x0f, 0xac, 7)
+#define RSN_CIPHER_SUITE_GCMP RSN_SELECTOR(0x00, 0x0f, 0xac, 8)
+#define RSN_CIPHER_SUITE_GCMP_256 RSN_SELECTOR(0x00, 0x0f, 0xac, 9)
+#define RSN_CIPHER_SUITE_CCMP_256 RSN_SELECTOR(0x00, 0x0f, 0xac, 10)
+#define RSN_CIPHER_SUITE_BIP_GMAC_128 RSN_SELECTOR(0x00, 0x0f, 0xac, 11)
+#define RSN_CIPHER_SUITE_BIP_GMAC_256 RSN_SELECTOR(0x00, 0x0f, 0xac, 12)
+#define RSN_CIPHER_SUITE_BIP_CMAC_256 RSN_SELECTOR(0x00, 0x0f, 0xac, 13)
+
+/* EAPOL-Key Key Data Encapsulation
+ * GroupKey and PeerKey require encryption, otherwise, encryption is optional.
+ */
+#define RSN_KEY_DATA_GROUPKEY RSN_SELECTOR(0x00, 0x0f, 0xac, 1)
+#if 0
+#define RSN_KEY_DATA_STAKEY RSN_SELECTOR(0x00, 0x0f, 0xac, 2)
+#endif
+#define RSN_KEY_DATA_MAC_ADDR RSN_SELECTOR(0x00, 0x0f, 0xac, 3)
+#define RSN_KEY_DATA_PMKID RSN_SELECTOR(0x00, 0x0f, 0xac, 4)
+#ifdef CONFIG_PEERKEY
+#define RSN_KEY_DATA_SMK RSN_SELECTOR(0x00, 0x0f, 0xac, 5)
+#define RSN_KEY_DATA_NONCE RSN_SELECTOR(0x00, 0x0f, 0xac, 6)
+#define RSN_KEY_DATA_LIFETIME RSN_SELECTOR(0x00, 0x0f, 0xac, 7)
+#define RSN_KEY_DATA_ERROR RSN_SELECTOR(0x00, 0x0f, 0xac, 8)
+#endif /* CONFIG_PEERKEY */
+#ifdef CONFIG_IEEE80211W
+#define RSN_KEY_DATA_IGTK RSN_SELECTOR(0x00, 0x0f, 0xac, 9)
+#endif /* CONFIG_IEEE80211W */
+#define RSN_KEY_DATA_KEYID RSN_SELECTOR(0x00, 0x0f, 0xac, 10)
+#define RSN_KEY_DATA_MULTIBAND_GTK RSN_SELECTOR(0x00, 0x0f, 0xac, 11)
+#define RSN_KEY_DATA_MULTIBAND_KEYID RSN_SELECTOR(0x00, 0x0f, 0xac, 12)
+
+#define WFA_KEY_DATA_IP_ADDR_REQ RSN_SELECTOR(0x50, 0x6f, 0x9a, 4)
+#define WFA_KEY_DATA_IP_ADDR_ALLOC RSN_SELECTOR(0x50, 0x6f, 0x9a, 5)
+
+#define WPA_OUI_TYPE RSN_SELECTOR(0x00, 0x50, 0xf2, 1)
+
+#define RSN_SELECTOR_PUT(a, val) WPA_PUT_BE32((u8 *) (a), (val))
+#define RSN_SELECTOR_GET(a) WPA_GET_BE32((const u8 *) (a))
+
+#define RSN_NUM_REPLAY_COUNTERS_1 0
+#define RSN_NUM_REPLAY_COUNTERS_2 1
+#define RSN_NUM_REPLAY_COUNTERS_4 2
+#define RSN_NUM_REPLAY_COUNTERS_16 3
+
+
+#ifdef _MSC_VER
+#pragma pack(push, 1)
+#endif /* _MSC_VER */
+
+#ifdef CONFIG_IEEE80211W
+#define WPA_IGTK_LEN 16
+#define WPA_IGTK_MAX_LEN 32
+#endif /* CONFIG_IEEE80211W */
+
+
+/* IEEE 802.11, 7.3.2.25.3 RSN Capabilities */
+#define WPA_CAPABILITY_PREAUTH BIT(0)
+#define WPA_CAPABILITY_NO_PAIRWISE BIT(1)
+/* B2-B3: PTKSA Replay Counter */
+/* B4-B5: GTKSA Replay Counter */
+#define WPA_CAPABILITY_MFPR BIT(6)
+#define WPA_CAPABILITY_MFPC BIT(7)
+/* B8: Reserved */
+#define WPA_CAPABILITY_PEERKEY_ENABLED BIT(9)
+#define WPA_CAPABILITY_SPP_A_MSDU_CAPABLE BIT(10)
+#define WPA_CAPABILITY_SPP_A_MSDU_REQUIRED BIT(11)
+#define WPA_CAPABILITY_PBAC BIT(12)
+#define WPA_CAPABILITY_EXT_KEY_ID_FOR_UNICAST BIT(13)
+/* B14-B15: Reserved */
+
+
+/* IEEE 802.11r */
+#define MOBILITY_DOMAIN_ID_LEN 2
+#define FT_R0KH_ID_MAX_LEN 48
+#define FT_R1KH_ID_LEN 6
+#define WPA_PMK_NAME_LEN 16
+
+
+/* IEEE 802.11, 8.5.2 EAPOL-Key frames */
+#define WPA_KEY_INFO_TYPE_MASK ((u16) (BIT(0) | BIT(1) | BIT(2)))
+#define WPA_KEY_INFO_TYPE_AKM_DEFINED 0
+#define WPA_KEY_INFO_TYPE_HMAC_MD5_RC4 BIT(0)
+#define WPA_KEY_INFO_TYPE_HMAC_SHA1_AES BIT(1)
+#define WPA_KEY_INFO_TYPE_AES_128_CMAC 3
+#define WPA_KEY_INFO_KEY_TYPE BIT(3) /* 1 = Pairwise, 0 = Group key */
+/* bit4..5 is used in WPA, but is reserved in IEEE 802.11i/RSN */
+#define WPA_KEY_INFO_KEY_INDEX_MASK (BIT(4) | BIT(5))
+#define WPA_KEY_INFO_KEY_INDEX_SHIFT 4
+#define WPA_KEY_INFO_INSTALL BIT(6) /* pairwise */
+#define WPA_KEY_INFO_TXRX BIT(6) /* group */
+#define WPA_KEY_INFO_ACK BIT(7)
+#define WPA_KEY_INFO_MIC BIT(8)
+#define WPA_KEY_INFO_SECURE BIT(9)
+#define WPA_KEY_INFO_ERROR BIT(10)
+#define WPA_KEY_INFO_REQUEST BIT(11)
+#define WPA_KEY_INFO_ENCR_KEY_DATA BIT(12) /* IEEE 802.11i/RSN only */
+#define WPA_KEY_INFO_SMK_MESSAGE BIT(13)
+
+
+struct wpa_eapol_key {
+ u8 type;
+ /* Note: key_info, key_length, and key_data_length are unaligned */
+ u8 key_info[2]; /* big endian */
+ u8 key_length[2]; /* big endian */
+ u8 replay_counter[WPA_REPLAY_COUNTER_LEN];
+ u8 key_nonce[WPA_NONCE_LEN];
+ u8 key_iv[16];
+ u8 key_rsc[WPA_KEY_RSC_LEN];
+ u8 key_id[8]; /* Reserved in IEEE 802.11i/RSN */
+ u8 key_mic[16];
+ u8 key_data_length[2]; /* big endian */
+ /* followed by key_data_length bytes of key_data */
+} STRUCT_PACKED;
+
+struct wpa_eapol_key_192 {
+ u8 type;
+ /* Note: key_info, key_length, and key_data_length are unaligned */
+ u8 key_info[2]; /* big endian */
+ u8 key_length[2]; /* big endian */
+ u8 replay_counter[WPA_REPLAY_COUNTER_LEN];
+ u8 key_nonce[WPA_NONCE_LEN];
+ u8 key_iv[16];
+ u8 key_rsc[WPA_KEY_RSC_LEN];
+ u8 key_id[8]; /* Reserved in IEEE 802.11i/RSN */
+ u8 key_mic[24];
+ u8 key_data_length[2]; /* big endian */
+ /* followed by key_data_length bytes of key_data */
+} STRUCT_PACKED;
+
+#define WPA_EAPOL_KEY_MIC_MAX_LEN 24
+#define WPA_KCK_MAX_LEN 24
+#define WPA_KEK_MAX_LEN 32
+#define WPA_TK_MAX_LEN 32
+
+/**
+ * struct wpa_ptk - WPA Pairwise Transient Key
+ * IEEE Std 802.11i-2004 - 8.5.1.2 Pairwise key hierarchy
+ */
+struct wpa_ptk {
+ u8 kck[WPA_KCK_MAX_LEN]; /* EAPOL-Key Key Confirmation Key (KCK) */
+ u8 kek[WPA_KEK_MAX_LEN]; /* EAPOL-Key Key Encryption Key (KEK) */
+ u8 tk[WPA_TK_MAX_LEN]; /* Temporal Key (TK) */
+ size_t kck_len;
+ size_t kek_len;
+ size_t tk_len;
+};
+
+
+/* WPA IE version 1
+ * 00-50-f2:1 (OUI:OUI type)
+ * 0x01 0x00 (version; little endian)
+ * (all following fields are optional:)
+ * Group Suite Selector (4 octets) (default: TKIP)
+ * Pairwise Suite Count (2 octets, little endian) (default: 1)
+ * Pairwise Suite List (4 * n octets) (default: TKIP)
+ * Authenticated Key Management Suite Count (2 octets, little endian)
+ * (default: 1)
+ * Authenticated Key Management Suite List (4 * n octets)
+ * (default: unspec 802.1X)
+ * WPA Capabilities (2 octets, little endian) (default: 0)
+ */
+
+struct wpa_ie_hdr {
+ u8 elem_id;
+ u8 len;
+ u8 oui[4]; /* 24-bit OUI followed by 8-bit OUI type */
+ u8 version[2]; /* little endian */
+} STRUCT_PACKED;
+
+
+/* 1/4: PMKID
+ * 2/4: RSN IE
+ * 3/4: one or two RSN IEs + GTK IE (encrypted)
+ * 4/4: empty
+ * 1/2: GTK IE (encrypted)
+ * 2/2: empty
+ */
+
+/* RSN IE version 1
+ * 0x01 0x00 (version; little endian)
+ * (all following fields are optional:)
+ * Group Suite Selector (4 octets) (default: CCMP)
+ * Pairwise Suite Count (2 octets, little endian) (default: 1)
+ * Pairwise Suite List (4 * n octets) (default: CCMP)
+ * Authenticated Key Management Suite Count (2 octets, little endian)
+ * (default: 1)
+ * Authenticated Key Management Suite List (4 * n octets)
+ * (default: unspec 802.1X)
+ * RSN Capabilities (2 octets, little endian) (default: 0)
+ * PMKID Count (2 octets) (default: 0)
+ * PMKID List (16 * n octets)
+ * Management Group Cipher Suite (4 octets) (default: AES-128-CMAC)
+ */
+
+struct rsn_ie_hdr {
+ u8 elem_id; /* WLAN_EID_RSN */
+ u8 len;
+ u8 version[2]; /* little endian */
+} STRUCT_PACKED;
+
+
+#ifdef CONFIG_PEERKEY
+enum {
+ STK_MUI_4WAY_STA_AP = 1,
+ STK_MUI_4WAY_STAT_STA = 2,
+ STK_MUI_GTK = 3,
+ STK_MUI_SMK = 4
+};
+
+enum {
+ STK_ERR_STA_NR = 1,
+ STK_ERR_STA_NRSN = 2,
+ STK_ERR_CPHR_NS = 3,
+ STK_ERR_NO_STSL = 4
+};
+#endif /* CONFIG_PEERKEY */
+
+struct rsn_error_kde {
+ be16 mui;
+ be16 error_type;
+} STRUCT_PACKED;
+
+#ifdef CONFIG_IEEE80211W
+#define WPA_IGTK_KDE_PREFIX_LEN (2 + 6)
+struct wpa_igtk_kde {
+ u8 keyid[2];
+ u8 pn[6];
+ u8 igtk[WPA_IGTK_MAX_LEN];
+} STRUCT_PACKED;
+#endif /* CONFIG_IEEE80211W */
+
+struct rsn_mdie {
+ u8 mobility_domain[MOBILITY_DOMAIN_ID_LEN];
+ u8 ft_capab;
+} STRUCT_PACKED;
+
+#define RSN_FT_CAPAB_FT_OVER_DS BIT(0)
+#define RSN_FT_CAPAB_FT_RESOURCE_REQ_SUPP BIT(1)
+
+struct rsn_ftie {
+ u8 mic_control[2];
+ u8 mic[16];
+ u8 anonce[WPA_NONCE_LEN];
+ u8 snonce[WPA_NONCE_LEN];
+ /* followed by optional parameters */
+} STRUCT_PACKED;
+
+#define FTIE_SUBELEM_R1KH_ID 1
+#define FTIE_SUBELEM_GTK 2
+#define FTIE_SUBELEM_R0KH_ID 3
+#define FTIE_SUBELEM_IGTK 4
+
+struct rsn_rdie {
+ u8 id;
+ u8 descr_count;
+ le16 status_code;
+} STRUCT_PACKED;
+
+
+#ifdef _MSC_VER
+#pragma pack(pop)
+#endif /* _MSC_VER */
+
+
+int wpa_eapol_key_mic(const u8 *key, size_t key_len, int akmp, int ver,
+ const u8 *buf, size_t len, u8 *mic);
+int wpa_pmk_to_ptk(const u8 *pmk, size_t pmk_len, const char *label,
+ const u8 *addr1, const u8 *addr2,
+ const u8 *nonce1, const u8 *nonce2,
+ struct wpa_ptk *ptk, int akmp, int cipher);
+
+#ifdef CONFIG_IEEE80211R
+int wpa_ft_mic(const u8 *kck, size_t kck_len, const u8 *sta_addr,
+ const u8 *ap_addr, u8 transaction_seqnum,
+ const u8 *mdie, size_t mdie_len,
+ const u8 *ftie, size_t ftie_len,
+ const u8 *rsnie, size_t rsnie_len,
+ const u8 *ric, size_t ric_len, u8 *mic);
+void wpa_derive_pmk_r0(const u8 *xxkey, size_t xxkey_len,
+ const u8 *ssid, size_t ssid_len,
+ const u8 *mdid, const u8 *r0kh_id, size_t r0kh_id_len,
+ const u8 *s0kh_id, u8 *pmk_r0, u8 *pmk_r0_name);
+void wpa_derive_pmk_r1_name(const u8 *pmk_r0_name, const u8 *r1kh_id,
+ const u8 *s1kh_id, u8 *pmk_r1_name);
+void wpa_derive_pmk_r1(const u8 *pmk_r0, const u8 *pmk_r0_name,
+ const u8 *r1kh_id, const u8 *s1kh_id,
+ u8 *pmk_r1, u8 *pmk_r1_name);
+int wpa_pmk_r1_to_ptk(const u8 *pmk_r1, const u8 *snonce, const u8 *anonce,
+ const u8 *sta_addr, const u8 *bssid,
+ const u8 *pmk_r1_name,
+ struct wpa_ptk *ptk, u8 *ptk_name, int akmp, int cipher);
+#endif /* CONFIG_IEEE80211R */
+
+struct wpa_ie_data {
+ int proto;
+ int pairwise_cipher;
+ int group_cipher;
+ int key_mgmt;
+ int capabilities;
+ size_t num_pmkid;
+ const u8 *pmkid;
+ int mgmt_group_cipher;
+};
+
+
+int wpa_parse_wpa_ie_rsn(const u8 *rsn_ie, size_t rsn_ie_len,
+ struct wpa_ie_data *data);
+int wpa_parse_wpa_ie_wpa(const u8 *wpa_ie, size_t wpa_ie_len,
+ struct wpa_ie_data *data);
+
+void rsn_pmkid(const u8 *pmk, size_t pmk_len, const u8 *aa, const u8 *spa,
+ u8 *pmkid, int use_sha256);
+#ifdef CONFIG_SUITEB
+int rsn_pmkid_suite_b(const u8 *kck, size_t kck_len, const u8 *aa,
+ const u8 *spa, u8 *pmkid);
+#else /* CONFIG_SUITEB */
+static inline int rsn_pmkid_suite_b(const u8 *kck, size_t kck_len, const u8 *aa,
+ const u8 *spa, u8 *pmkid)
+{
+ return -1;
+}
+#endif /* CONFIG_SUITEB */
+#ifdef CONFIG_SUITEB192
+int rsn_pmkid_suite_b_192(const u8 *kck, size_t kck_len, const u8 *aa,
+ const u8 *spa, u8 *pmkid);
+#else /* CONFIG_SUITEB192 */
+static inline int rsn_pmkid_suite_b_192(const u8 *kck, size_t kck_len,
+ const u8 *aa, const u8 *spa, u8 *pmkid)
+{
+ return -1;
+}
+#endif /* CONFIG_SUITEB192 */
+
+const char * wpa_cipher_txt(int cipher);
+const char * wpa_key_mgmt_txt(int key_mgmt, int proto);
+u32 wpa_akm_to_suite(int akm);
+int wpa_compare_rsn_ie(int ft_initial_assoc,
+ const u8 *ie1, size_t ie1len,
+ const u8 *ie2, size_t ie2len);
+int wpa_insert_pmkid(u8 *ies, size_t ies_len, const u8 *pmkid);
+
+struct wpa_ft_ies {
+ const u8 *mdie;
+ size_t mdie_len;
+ const u8 *ftie;
+ size_t ftie_len;
+ const u8 *r1kh_id;
+ const u8 *gtk;
+ size_t gtk_len;
+ const u8 *r0kh_id;
+ size_t r0kh_id_len;
+ const u8 *rsn;
+ size_t rsn_len;
+ const u8 *rsn_pmkid;
+ const u8 *tie;
+ size_t tie_len;
+ const u8 *igtk;
+ size_t igtk_len;
+ const u8 *ric;
+ size_t ric_len;
+};
+
+int wpa_ft_parse_ies(const u8 *ies, size_t ies_len, struct wpa_ft_ies *parse);
+
+int wpa_cipher_key_len(int cipher);
+int wpa_cipher_rsc_len(int cipher);
+int wpa_cipher_to_alg(int cipher);
+int wpa_cipher_valid_group(int cipher);
+int wpa_cipher_valid_pairwise(int cipher);
+int wpa_cipher_valid_mgmt_group(int cipher);
+u32 wpa_cipher_to_suite(int proto, int cipher);
+int rsn_cipher_put_suites(u8 *pos, int ciphers);
+int wpa_cipher_put_suites(u8 *pos, int ciphers);
+int wpa_pick_pairwise_cipher(int ciphers, int none_allowed);
+int wpa_pick_group_cipher(int ciphers);
+int wpa_parse_cipher(const char *value);
+int wpa_write_ciphers(char *start, char *end, int ciphers, const char *delim);
+int wpa_select_ap_group_cipher(int wpa, int wpa_pairwise, int rsn_pairwise);
+unsigned int wpa_mic_len(int akmp);
+
+#endif /* WPA_COMMON_H */
diff --git a/freebsd/contrib/wpa/src/common/wpa_ctrl.h b/freebsd/contrib/wpa/src/common/wpa_ctrl.h
new file mode 100644
index 00000000..3de46823
--- /dev/null
+++ b/freebsd/contrib/wpa/src/common/wpa_ctrl.h
@@ -0,0 +1,472 @@
+/*
+ * wpa_supplicant/hostapd control interface library
+ * Copyright (c) 2004-2006, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef WPA_CTRL_H
+#define WPA_CTRL_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* wpa_supplicant control interface - fixed message prefixes */
+
+/** Interactive request for identity/password/pin */
+#define WPA_CTRL_REQ "CTRL-REQ-"
+
+/** Response to identity/password/pin request */
+#define WPA_CTRL_RSP "CTRL-RSP-"
+
+/* Event messages with fixed prefix */
+/** Authentication completed successfully and data connection enabled */
+#define WPA_EVENT_CONNECTED "CTRL-EVENT-CONNECTED "
+/** Disconnected, data connection is not available */
+#define WPA_EVENT_DISCONNECTED "CTRL-EVENT-DISCONNECTED "
+/** Association rejected during connection attempt */
+#define WPA_EVENT_ASSOC_REJECT "CTRL-EVENT-ASSOC-REJECT "
+/** Authentication rejected during connection attempt */
+#define WPA_EVENT_AUTH_REJECT "CTRL-EVENT-AUTH-REJECT "
+/** wpa_supplicant is exiting */
+#define WPA_EVENT_TERMINATING "CTRL-EVENT-TERMINATING "
+/** Password change was completed successfully */
+#define WPA_EVENT_PASSWORD_CHANGED "CTRL-EVENT-PASSWORD-CHANGED "
+/** EAP-Request/Notification received */
+#define WPA_EVENT_EAP_NOTIFICATION "CTRL-EVENT-EAP-NOTIFICATION "
+/** EAP authentication started (EAP-Request/Identity received) */
+#define WPA_EVENT_EAP_STARTED "CTRL-EVENT-EAP-STARTED "
+/** EAP method proposed by the server */
+#define WPA_EVENT_EAP_PROPOSED_METHOD "CTRL-EVENT-EAP-PROPOSED-METHOD "
+/** EAP method selected */
+#define WPA_EVENT_EAP_METHOD "CTRL-EVENT-EAP-METHOD "
+/** EAP peer certificate from TLS */
+#define WPA_EVENT_EAP_PEER_CERT "CTRL-EVENT-EAP-PEER-CERT "
+/** EAP peer certificate alternative subject name component from TLS */
+#define WPA_EVENT_EAP_PEER_ALT "CTRL-EVENT-EAP-PEER-ALT "
+/** EAP TLS certificate chain validation error */
+#define WPA_EVENT_EAP_TLS_CERT_ERROR "CTRL-EVENT-EAP-TLS-CERT-ERROR "
+/** EAP status */
+#define WPA_EVENT_EAP_STATUS "CTRL-EVENT-EAP-STATUS "
+/** EAP authentication completed successfully */
+#define WPA_EVENT_EAP_SUCCESS "CTRL-EVENT-EAP-SUCCESS "
+/** EAP authentication failed (EAP-Failure received) */
+#define WPA_EVENT_EAP_FAILURE "CTRL-EVENT-EAP-FAILURE "
+/** Network block temporarily disabled (e.g., due to authentication failure) */
+#define WPA_EVENT_TEMP_DISABLED "CTRL-EVENT-SSID-TEMP-DISABLED "
+/** Temporarily disabled network block re-enabled */
+#define WPA_EVENT_REENABLED "CTRL-EVENT-SSID-REENABLED "
+/** New scan started */
+#define WPA_EVENT_SCAN_STARTED "CTRL-EVENT-SCAN-STARTED "
+/** New scan results available */
+#define WPA_EVENT_SCAN_RESULTS "CTRL-EVENT-SCAN-RESULTS "
+/** Scan command failed */
+#define WPA_EVENT_SCAN_FAILED "CTRL-EVENT-SCAN-FAILED "
+/** wpa_supplicant state change */
+#define WPA_EVENT_STATE_CHANGE "CTRL-EVENT-STATE-CHANGE "
+/** A new BSS entry was added (followed by BSS entry id and BSSID) */
+#define WPA_EVENT_BSS_ADDED "CTRL-EVENT-BSS-ADDED "
+/** A BSS entry was removed (followed by BSS entry id and BSSID) */
+#define WPA_EVENT_BSS_REMOVED "CTRL-EVENT-BSS-REMOVED "
+/** No suitable network was found */
+#define WPA_EVENT_NETWORK_NOT_FOUND "CTRL-EVENT-NETWORK-NOT-FOUND "
+/** Change in the signal level was reported by the driver */
+#define WPA_EVENT_SIGNAL_CHANGE "CTRL-EVENT-SIGNAL-CHANGE "
+/** Regulatory domain channel */
+#define WPA_EVENT_REGDOM_CHANGE "CTRL-EVENT-REGDOM-CHANGE "
+
+/** RSN IBSS 4-way handshakes completed with specified peer */
+#define IBSS_RSN_COMPLETED "IBSS-RSN-COMPLETED "
+
+/** Notification of frequency conflict due to a concurrent operation.
+ *
+ * The indicated network is disabled and needs to be re-enabled before it can
+ * be used again.
+ */
+#define WPA_EVENT_FREQ_CONFLICT "CTRL-EVENT-FREQ-CONFLICT "
+/** Frequency ranges that the driver recommends to avoid */
+#define WPA_EVENT_AVOID_FREQ "CTRL-EVENT-AVOID-FREQ "
+/** WPS overlap detected in PBC mode */
+#define WPS_EVENT_OVERLAP "WPS-OVERLAP-DETECTED "
+/** Available WPS AP with active PBC found in scan results */
+#define WPS_EVENT_AP_AVAILABLE_PBC "WPS-AP-AVAILABLE-PBC "
+/** Available WPS AP with our address as authorized in scan results */
+#define WPS_EVENT_AP_AVAILABLE_AUTH "WPS-AP-AVAILABLE-AUTH "
+/** Available WPS AP with recently selected PIN registrar found in scan results
+ */
+#define WPS_EVENT_AP_AVAILABLE_PIN "WPS-AP-AVAILABLE-PIN "
+/** Available WPS AP found in scan results */
+#define WPS_EVENT_AP_AVAILABLE "WPS-AP-AVAILABLE "
+/** A new credential received */
+#define WPS_EVENT_CRED_RECEIVED "WPS-CRED-RECEIVED "
+/** M2D received */
+#define WPS_EVENT_M2D "WPS-M2D "
+/** WPS registration failed after M2/M2D */
+#define WPS_EVENT_FAIL "WPS-FAIL "
+/** WPS registration completed successfully */
+#define WPS_EVENT_SUCCESS "WPS-SUCCESS "
+/** WPS enrollment attempt timed out and was terminated */
+#define WPS_EVENT_TIMEOUT "WPS-TIMEOUT "
+/* PBC mode was activated */
+#define WPS_EVENT_ACTIVE "WPS-PBC-ACTIVE "
+/* PBC mode was disabled */
+#define WPS_EVENT_DISABLE "WPS-PBC-DISABLE "
+
+#define WPS_EVENT_ENROLLEE_SEEN "WPS-ENROLLEE-SEEN "
+
+#define WPS_EVENT_OPEN_NETWORK "WPS-OPEN-NETWORK "
+
+/* WPS ER events */
+#define WPS_EVENT_ER_AP_ADD "WPS-ER-AP-ADD "
+#define WPS_EVENT_ER_AP_REMOVE "WPS-ER-AP-REMOVE "
+#define WPS_EVENT_ER_ENROLLEE_ADD "WPS-ER-ENROLLEE-ADD "
+#define WPS_EVENT_ER_ENROLLEE_REMOVE "WPS-ER-ENROLLEE-REMOVE "
+#define WPS_EVENT_ER_AP_SETTINGS "WPS-ER-AP-SETTINGS "
+#define WPS_EVENT_ER_SET_SEL_REG "WPS-ER-AP-SET-SEL-REG "
+
+/* MESH events */
+#define MESH_GROUP_STARTED "MESH-GROUP-STARTED "
+#define MESH_GROUP_REMOVED "MESH-GROUP-REMOVED "
+#define MESH_PEER_CONNECTED "MESH-PEER-CONNECTED "
+#define MESH_PEER_DISCONNECTED "MESH-PEER-DISCONNECTED "
+/** Mesh SAE authentication failure. Wrong password suspected. */
+#define MESH_SAE_AUTH_FAILURE "MESH-SAE-AUTH-FAILURE "
+#define MESH_SAE_AUTH_BLOCKED "MESH-SAE-AUTH-BLOCKED "
+
+/* WMM AC events */
+#define WMM_AC_EVENT_TSPEC_ADDED "TSPEC-ADDED "
+#define WMM_AC_EVENT_TSPEC_REMOVED "TSPEC-REMOVED "
+#define WMM_AC_EVENT_TSPEC_REQ_FAILED "TSPEC-REQ-FAILED "
+
+/** P2P device found */
+#define P2P_EVENT_DEVICE_FOUND "P2P-DEVICE-FOUND "
+
+/** P2P device lost */
+#define P2P_EVENT_DEVICE_LOST "P2P-DEVICE-LOST "
+
+/** A P2P device requested GO negotiation, but we were not ready to start the
+ * negotiation */
+#define P2P_EVENT_GO_NEG_REQUEST "P2P-GO-NEG-REQUEST "
+#define P2P_EVENT_GO_NEG_SUCCESS "P2P-GO-NEG-SUCCESS "
+#define P2P_EVENT_GO_NEG_FAILURE "P2P-GO-NEG-FAILURE "
+#define P2P_EVENT_GROUP_FORMATION_SUCCESS "P2P-GROUP-FORMATION-SUCCESS "
+#define P2P_EVENT_GROUP_FORMATION_FAILURE "P2P-GROUP-FORMATION-FAILURE "
+#define P2P_EVENT_GROUP_STARTED "P2P-GROUP-STARTED "
+#define P2P_EVENT_GROUP_REMOVED "P2P-GROUP-REMOVED "
+#define P2P_EVENT_CROSS_CONNECT_ENABLE "P2P-CROSS-CONNECT-ENABLE "
+#define P2P_EVENT_CROSS_CONNECT_DISABLE "P2P-CROSS-CONNECT-DISABLE "
+/* parameters: <peer address> <PIN> */
+#define P2P_EVENT_PROV_DISC_SHOW_PIN "P2P-PROV-DISC-SHOW-PIN "
+/* parameters: <peer address> */
+#define P2P_EVENT_PROV_DISC_ENTER_PIN "P2P-PROV-DISC-ENTER-PIN "
+/* parameters: <peer address> */
+#define P2P_EVENT_PROV_DISC_PBC_REQ "P2P-PROV-DISC-PBC-REQ "
+/* parameters: <peer address> */
+#define P2P_EVENT_PROV_DISC_PBC_RESP "P2P-PROV-DISC-PBC-RESP "
+/* parameters: <peer address> <status> */
+#define P2P_EVENT_PROV_DISC_FAILURE "P2P-PROV-DISC-FAILURE"
+/* parameters: <freq> <src addr> <dialog token> <update indicator> <TLVs> */
+#define P2P_EVENT_SERV_DISC_REQ "P2P-SERV-DISC-REQ "
+/* parameters: <src addr> <update indicator> <TLVs> */
+#define P2P_EVENT_SERV_DISC_RESP "P2P-SERV-DISC-RESP "
+#define P2P_EVENT_SERV_ASP_RESP "P2P-SERV-ASP-RESP "
+#define P2P_EVENT_INVITATION_RECEIVED "P2P-INVITATION-RECEIVED "
+#define P2P_EVENT_INVITATION_RESULT "P2P-INVITATION-RESULT "
+#define P2P_EVENT_FIND_STOPPED "P2P-FIND-STOPPED "
+#define P2P_EVENT_PERSISTENT_PSK_FAIL "P2P-PERSISTENT-PSK-FAIL id="
+#define P2P_EVENT_PRESENCE_RESPONSE "P2P-PRESENCE-RESPONSE "
+#define P2P_EVENT_NFC_BOTH_GO "P2P-NFC-BOTH-GO "
+#define P2P_EVENT_NFC_PEER_CLIENT "P2P-NFC-PEER-CLIENT "
+#define P2P_EVENT_NFC_WHILE_CLIENT "P2P-NFC-WHILE-CLIENT "
+#define P2P_EVENT_FALLBACK_TO_GO_NEG "P2P-FALLBACK-TO-GO-NEG "
+#define P2P_EVENT_FALLBACK_TO_GO_NEG_ENABLED "P2P-FALLBACK-TO-GO-NEG-ENABLED "
+
+/* parameters: <PMF enabled> <timeout in ms> <Session Information URL> */
+#define ESS_DISASSOC_IMMINENT "ESS-DISASSOC-IMMINENT "
+#define P2P_EVENT_REMOVE_AND_REFORM_GROUP "P2P-REMOVE-AND-REFORM-GROUP "
+
+#define P2P_EVENT_P2PS_PROVISION_START "P2PS-PROV-START "
+#define P2P_EVENT_P2PS_PROVISION_DONE "P2PS-PROV-DONE "
+
+#define INTERWORKING_AP "INTERWORKING-AP "
+#define INTERWORKING_BLACKLISTED "INTERWORKING-BLACKLISTED "
+#define INTERWORKING_NO_MATCH "INTERWORKING-NO-MATCH "
+#define INTERWORKING_ALREADY_CONNECTED "INTERWORKING-ALREADY-CONNECTED "
+#define INTERWORKING_SELECTED "INTERWORKING-SELECTED "
+
+/* Credential block added; parameters: <id> */
+#define CRED_ADDED "CRED-ADDED "
+/* Credential block modified; parameters: <id> <field> */
+#define CRED_MODIFIED "CRED-MODIFIED "
+/* Credential block removed; parameters: <id> */
+#define CRED_REMOVED "CRED-REMOVED "
+
+#define GAS_RESPONSE_INFO "GAS-RESPONSE-INFO "
+/* parameters: <addr> <dialog_token> <freq> */
+#define GAS_QUERY_START "GAS-QUERY-START "
+/* parameters: <addr> <dialog_token> <freq> <status_code> <result> */
+#define GAS_QUERY_DONE "GAS-QUERY-DONE "
+
+/* parameters: <addr> <result> */
+#define ANQP_QUERY_DONE "ANQP-QUERY-DONE "
+
+#define HS20_SUBSCRIPTION_REMEDIATION "HS20-SUBSCRIPTION-REMEDIATION "
+#define HS20_DEAUTH_IMMINENT_NOTICE "HS20-DEAUTH-IMMINENT-NOTICE "
+
+#define EXT_RADIO_WORK_START "EXT-RADIO-WORK-START "
+#define EXT_RADIO_WORK_TIMEOUT "EXT-RADIO-WORK-TIMEOUT "
+
+#define RRM_EVENT_NEIGHBOR_REP_RXED "RRM-NEIGHBOR-REP-RECEIVED "
+#define RRM_EVENT_NEIGHBOR_REP_FAILED "RRM-NEIGHBOR-REP-REQUEST-FAILED "
+
+/* hostapd control interface - fixed message prefixes */
+#define WPS_EVENT_PIN_NEEDED "WPS-PIN-NEEDED "
+#define WPS_EVENT_NEW_AP_SETTINGS "WPS-NEW-AP-SETTINGS "
+#define WPS_EVENT_REG_SUCCESS "WPS-REG-SUCCESS "
+#define WPS_EVENT_AP_SETUP_LOCKED "WPS-AP-SETUP-LOCKED "
+#define WPS_EVENT_AP_SETUP_UNLOCKED "WPS-AP-SETUP-UNLOCKED "
+#define WPS_EVENT_AP_PIN_ENABLED "WPS-AP-PIN-ENABLED "
+#define WPS_EVENT_AP_PIN_DISABLED "WPS-AP-PIN-DISABLED "
+#define AP_STA_CONNECTED "AP-STA-CONNECTED "
+#define AP_STA_DISCONNECTED "AP-STA-DISCONNECTED "
+#define AP_STA_POSSIBLE_PSK_MISMATCH "AP-STA-POSSIBLE-PSK-MISMATCH "
+
+#define AP_REJECTED_MAX_STA "AP-REJECTED-MAX-STA "
+#define AP_REJECTED_BLOCKED_STA "AP-REJECTED-BLOCKED-STA "
+
+#define AP_EVENT_ENABLED "AP-ENABLED "
+#define AP_EVENT_DISABLED "AP-DISABLED "
+
+#define INTERFACE_ENABLED "INTERFACE-ENABLED "
+#define INTERFACE_DISABLED "INTERFACE-DISABLED "
+
+#define ACS_EVENT_STARTED "ACS-STARTED "
+#define ACS_EVENT_COMPLETED "ACS-COMPLETED "
+#define ACS_EVENT_FAILED "ACS-FAILED "
+
+#define DFS_EVENT_RADAR_DETECTED "DFS-RADAR-DETECTED "
+#define DFS_EVENT_NEW_CHANNEL "DFS-NEW-CHANNEL "
+#define DFS_EVENT_CAC_START "DFS-CAC-START "
+#define DFS_EVENT_CAC_COMPLETED "DFS-CAC-COMPLETED "
+#define DFS_EVENT_NOP_FINISHED "DFS-NOP-FINISHED "
+
+#define AP_CSA_FINISHED "AP-CSA-FINISHED "
+
+/* BSS Transition Management Response frame received */
+#define BSS_TM_RESP "BSS-TM-RESP "
+
+/* BSS command information masks */
+
+#define WPA_BSS_MASK_ALL 0xFFFDFFFF
+#define WPA_BSS_MASK_ID BIT(0)
+#define WPA_BSS_MASK_BSSID BIT(1)
+#define WPA_BSS_MASK_FREQ BIT(2)
+#define WPA_BSS_MASK_BEACON_INT BIT(3)
+#define WPA_BSS_MASK_CAPABILITIES BIT(4)
+#define WPA_BSS_MASK_QUAL BIT(5)
+#define WPA_BSS_MASK_NOISE BIT(6)
+#define WPA_BSS_MASK_LEVEL BIT(7)
+#define WPA_BSS_MASK_TSF BIT(8)
+#define WPA_BSS_MASK_AGE BIT(9)
+#define WPA_BSS_MASK_IE BIT(10)
+#define WPA_BSS_MASK_FLAGS BIT(11)
+#define WPA_BSS_MASK_SSID BIT(12)
+#define WPA_BSS_MASK_WPS_SCAN BIT(13)
+#define WPA_BSS_MASK_P2P_SCAN BIT(14)
+#define WPA_BSS_MASK_INTERNETW BIT(15)
+#define WPA_BSS_MASK_WIFI_DISPLAY BIT(16)
+#define WPA_BSS_MASK_DELIM BIT(17)
+#define WPA_BSS_MASK_MESH_SCAN BIT(18)
+#define WPA_BSS_MASK_SNR BIT(19)
+#define WPA_BSS_MASK_EST_THROUGHPUT BIT(20)
+#define WPA_BSS_MASK_FST BIT(21)
+
+
+/* VENDOR_ELEM_* frame id values */
+enum wpa_vendor_elem_frame {
+ VENDOR_ELEM_PROBE_REQ_P2P = 0,
+ VENDOR_ELEM_PROBE_RESP_P2P = 1,
+ VENDOR_ELEM_PROBE_RESP_P2P_GO = 2,
+ VENDOR_ELEM_BEACON_P2P_GO = 3,
+ VENDOR_ELEM_P2P_PD_REQ = 4,
+ VENDOR_ELEM_P2P_PD_RESP = 5,
+ VENDOR_ELEM_P2P_GO_NEG_REQ = 6,
+ VENDOR_ELEM_P2P_GO_NEG_RESP = 7,
+ VENDOR_ELEM_P2P_GO_NEG_CONF = 8,
+ VENDOR_ELEM_P2P_INV_REQ = 9,
+ VENDOR_ELEM_P2P_INV_RESP = 10,
+ VENDOR_ELEM_P2P_ASSOC_REQ = 11,
+ VENDOR_ELEM_P2P_ASSOC_RESP = 12,
+ VENDOR_ELEM_ASSOC_REQ = 13,
+ NUM_VENDOR_ELEM_FRAMES
+};
+
+
+/* wpa_supplicant/hostapd control interface access */
+
+/**
+ * wpa_ctrl_open - Open a control interface to wpa_supplicant/hostapd
+ * @ctrl_path: Path for UNIX domain sockets; ignored if UDP sockets are used.
+ * Returns: Pointer to abstract control interface data or %NULL on failure
+ *
+ * This function is used to open a control interface to wpa_supplicant/hostapd.
+ * ctrl_path is usually /var/run/wpa_supplicant or /var/run/hostapd. This path
+ * is configured in wpa_supplicant/hostapd and other programs using the control
+ * interface need to use matching path configuration.
+ */
+struct wpa_ctrl * wpa_ctrl_open(const char *ctrl_path);
+
+/**
+ * wpa_ctrl_open2 - Open a control interface to wpa_supplicant/hostapd
+ * @ctrl_path: Path for UNIX domain sockets; ignored if UDP sockets are used.
+ * @cli_path: Path for client UNIX domain sockets; ignored if UDP socket
+ * is used.
+ * Returns: Pointer to abstract control interface data or %NULL on failure
+ *
+ * This function is used to open a control interface to wpa_supplicant/hostapd
+ * when the socket path for client need to be specified explicitly. Default
+ * ctrl_path is usually /var/run/wpa_supplicant or /var/run/hostapd and client
+ * socket path is /tmp.
+ */
+struct wpa_ctrl * wpa_ctrl_open2(const char *ctrl_path, const char *cli_path);
+
+
+/**
+ * wpa_ctrl_close - Close a control interface to wpa_supplicant/hostapd
+ * @ctrl: Control interface data from wpa_ctrl_open()
+ *
+ * This function is used to close a control interface.
+ */
+void wpa_ctrl_close(struct wpa_ctrl *ctrl);
+
+
+/**
+ * wpa_ctrl_request - Send a command to wpa_supplicant/hostapd
+ * @ctrl: Control interface data from wpa_ctrl_open()
+ * @cmd: Command; usually, ASCII text, e.g., "PING"
+ * @cmd_len: Length of the cmd in bytes
+ * @reply: Buffer for the response
+ * @reply_len: Reply buffer length
+ * @msg_cb: Callback function for unsolicited messages or %NULL if not used
+ * Returns: 0 on success, -1 on error (send or receive failed), -2 on timeout
+ *
+ * This function is used to send commands to wpa_supplicant/hostapd. Received
+ * response will be written to reply and reply_len is set to the actual length
+ * of the reply. This function will block for up to two seconds while waiting
+ * for the reply. If unsolicited messages are received, the blocking time may
+ * be longer.
+ *
+ * msg_cb can be used to register a callback function that will be called for
+ * unsolicited messages received while waiting for the command response. These
+ * messages may be received if wpa_ctrl_request() is called at the same time as
+ * wpa_supplicant/hostapd is sending such a message. This can happen only if
+ * the program has used wpa_ctrl_attach() to register itself as a monitor for
+ * event messages. Alternatively to msg_cb, programs can register two control
+ * interface connections and use one of them for commands and the other one for
+ * receiving event messages, in other words, call wpa_ctrl_attach() only for
+ * the control interface connection that will be used for event messages.
+ */
+int wpa_ctrl_request(struct wpa_ctrl *ctrl, const char *cmd, size_t cmd_len,
+ char *reply, size_t *reply_len,
+ void (*msg_cb)(char *msg, size_t len));
+
+
+/**
+ * wpa_ctrl_attach - Register as an event monitor for the control interface
+ * @ctrl: Control interface data from wpa_ctrl_open()
+ * Returns: 0 on success, -1 on failure, -2 on timeout
+ *
+ * This function registers the control interface connection as a monitor for
+ * wpa_supplicant/hostapd events. After a success wpa_ctrl_attach() call, the
+ * control interface connection starts receiving event messages that can be
+ * read with wpa_ctrl_recv().
+ */
+int wpa_ctrl_attach(struct wpa_ctrl *ctrl);
+
+
+/**
+ * wpa_ctrl_detach - Unregister event monitor from the control interface
+ * @ctrl: Control interface data from wpa_ctrl_open()
+ * Returns: 0 on success, -1 on failure, -2 on timeout
+ *
+ * This function unregisters the control interface connection as a monitor for
+ * wpa_supplicant/hostapd events, i.e., cancels the registration done with
+ * wpa_ctrl_attach().
+ */
+int wpa_ctrl_detach(struct wpa_ctrl *ctrl);
+
+
+/**
+ * wpa_ctrl_recv - Receive a pending control interface message
+ * @ctrl: Control interface data from wpa_ctrl_open()
+ * @reply: Buffer for the message data
+ * @reply_len: Length of the reply buffer
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function will receive a pending control interface message. The received
+ * response will be written to reply and reply_len is set to the actual length
+ * of the reply.
+
+ * wpa_ctrl_recv() is only used for event messages, i.e., wpa_ctrl_attach()
+ * must have been used to register the control interface as an event monitor.
+ */
+int wpa_ctrl_recv(struct wpa_ctrl *ctrl, char *reply, size_t *reply_len);
+
+
+/**
+ * wpa_ctrl_pending - Check whether there are pending event messages
+ * @ctrl: Control interface data from wpa_ctrl_open()
+ * Returns: 1 if there are pending messages, 0 if no, or -1 on error
+ *
+ * This function will check whether there are any pending control interface
+ * message available to be received with wpa_ctrl_recv(). wpa_ctrl_pending() is
+ * only used for event messages, i.e., wpa_ctrl_attach() must have been used to
+ * register the control interface as an event monitor.
+ */
+int wpa_ctrl_pending(struct wpa_ctrl *ctrl);
+
+
+/**
+ * wpa_ctrl_get_fd - Get file descriptor used by the control interface
+ * @ctrl: Control interface data from wpa_ctrl_open()
+ * Returns: File descriptor used for the connection
+ *
+ * This function can be used to get the file descriptor that is used for the
+ * control interface connection. The returned value can be used, e.g., with
+ * select() while waiting for multiple events.
+ *
+ * The returned file descriptor must not be used directly for sending or
+ * receiving packets; instead, the library functions wpa_ctrl_request() and
+ * wpa_ctrl_recv() must be used for this.
+ */
+int wpa_ctrl_get_fd(struct wpa_ctrl *ctrl);
+
+#ifdef ANDROID
+/**
+ * wpa_ctrl_cleanup() - Delete any local UNIX domain socket files that
+ * may be left over from clients that were previously connected to
+ * wpa_supplicant. This keeps these files from being orphaned in the
+ * event of crashes that prevented them from being removed as part
+ * of the normal orderly shutdown.
+ */
+void wpa_ctrl_cleanup(void);
+#endif /* ANDROID */
+
+#ifdef CONFIG_CTRL_IFACE_UDP
+/* Port range for multiple wpa_supplicant instances and multiple VIFs */
+#define WPA_CTRL_IFACE_PORT 9877
+#define WPA_CTRL_IFACE_PORT_LIMIT 50 /* decremented from start */
+#define WPA_GLOBAL_CTRL_IFACE_PORT 9878
+#define WPA_GLOBAL_CTRL_IFACE_PORT_LIMIT 20 /* incremented from start */
+
+char * wpa_ctrl_get_remote_ifname(struct wpa_ctrl *ctrl);
+#endif /* CONFIG_CTRL_IFACE_UDP */
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* WPA_CTRL_H */
diff --git a/freebsd/contrib/wpa/src/crypto/aes-ctr.c b/freebsd/contrib/wpa/src/crypto/aes-ctr.c
new file mode 100644
index 00000000..f92be022
--- /dev/null
+++ b/freebsd/contrib/wpa/src/crypto/aes-ctr.c
@@ -0,0 +1,57 @@
+#include <machine/rtems-bsd-user-space.h>
+
+/*
+ * AES-128 CTR
+ *
+ * Copyright (c) 2003-2007, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "aes.h"
+#include "aes_wrap.h"
+
+/**
+ * aes_128_ctr_encrypt - AES-128 CTR mode encryption
+ * @key: Key for encryption (16 bytes)
+ * @nonce: Nonce for counter mode (16 bytes)
+ * @data: Data to encrypt in-place
+ * @data_len: Length of data in bytes
+ * Returns: 0 on success, -1 on failure
+ */
+int aes_128_ctr_encrypt(const u8 *key, const u8 *nonce,
+ u8 *data, size_t data_len)
+{
+ void *ctx;
+ size_t j, len, left = data_len;
+ int i;
+ u8 *pos = data;
+ u8 counter[AES_BLOCK_SIZE], buf[AES_BLOCK_SIZE];
+
+ ctx = aes_encrypt_init(key, 16);
+ if (ctx == NULL)
+ return -1;
+ os_memcpy(counter, nonce, AES_BLOCK_SIZE);
+
+ while (left > 0) {
+ aes_encrypt(ctx, counter, buf);
+
+ len = (left < AES_BLOCK_SIZE) ? left : AES_BLOCK_SIZE;
+ for (j = 0; j < len; j++)
+ pos[j] ^= buf[j];
+ pos += len;
+ left -= len;
+
+ for (i = AES_BLOCK_SIZE - 1; i >= 0; i--) {
+ counter[i]++;
+ if (counter[i])
+ break;
+ }
+ }
+ aes_encrypt_deinit(ctx);
+ return 0;
+}
diff --git a/freebsd/contrib/wpa/src/crypto/aes-eax.c b/freebsd/contrib/wpa/src/crypto/aes-eax.c
new file mode 100644
index 00000000..db3cf089
--- /dev/null
+++ b/freebsd/contrib/wpa/src/crypto/aes-eax.c
@@ -0,0 +1,147 @@
+#include <machine/rtems-bsd-user-space.h>
+
+/*
+ * AES-128 EAX
+ *
+ * Copyright (c) 2003-2007, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "aes.h"
+#include "aes_wrap.h"
+
+/**
+ * aes_128_eax_encrypt - AES-128 EAX mode encryption
+ * @key: Key for encryption (16 bytes)
+ * @nonce: Nonce for counter mode
+ * @nonce_len: Nonce length in bytes
+ * @hdr: Header data to be authenticity protected
+ * @hdr_len: Length of the header data bytes
+ * @data: Data to encrypt in-place
+ * @data_len: Length of data in bytes
+ * @tag: 16-byte tag value
+ * Returns: 0 on success, -1 on failure
+ */
+int aes_128_eax_encrypt(const u8 *key, const u8 *nonce, size_t nonce_len,
+ const u8 *hdr, size_t hdr_len,
+ u8 *data, size_t data_len, u8 *tag)
+{
+ u8 *buf;
+ size_t buf_len;
+ u8 nonce_mac[AES_BLOCK_SIZE], hdr_mac[AES_BLOCK_SIZE],
+ data_mac[AES_BLOCK_SIZE];
+ int i, ret = -1;
+
+ if (nonce_len > data_len)
+ buf_len = nonce_len;
+ else
+ buf_len = data_len;
+ if (hdr_len > buf_len)
+ buf_len = hdr_len;
+ buf_len += 16;
+
+ buf = os_malloc(buf_len);
+ if (buf == NULL)
+ return -1;
+
+ os_memset(buf, 0, 15);
+
+ buf[15] = 0;
+ os_memcpy(buf + 16, nonce, nonce_len);
+ if (omac1_aes_128(key, buf, 16 + nonce_len, nonce_mac))
+ goto fail;
+
+ buf[15] = 1;
+ os_memcpy(buf + 16, hdr, hdr_len);
+ if (omac1_aes_128(key, buf, 16 + hdr_len, hdr_mac))
+ goto fail;
+
+ if (aes_128_ctr_encrypt(key, nonce_mac, data, data_len))
+ goto fail;
+ buf[15] = 2;
+ os_memcpy(buf + 16, data, data_len);
+ if (omac1_aes_128(key, buf, 16 + data_len, data_mac))
+ goto fail;
+
+ for (i = 0; i < AES_BLOCK_SIZE; i++)
+ tag[i] = nonce_mac[i] ^ data_mac[i] ^ hdr_mac[i];
+
+ ret = 0;
+fail:
+ bin_clear_free(buf, buf_len);
+
+ return ret;
+}
+
+
+/**
+ * aes_128_eax_decrypt - AES-128 EAX mode decryption
+ * @key: Key for decryption (16 bytes)
+ * @nonce: Nonce for counter mode
+ * @nonce_len: Nonce length in bytes
+ * @hdr: Header data to be authenticity protected
+ * @hdr_len: Length of the header data bytes
+ * @data: Data to encrypt in-place
+ * @data_len: Length of data in bytes
+ * @tag: 16-byte tag value
+ * Returns: 0 on success, -1 on failure, -2 if tag does not match
+ */
+int aes_128_eax_decrypt(const u8 *key, const u8 *nonce, size_t nonce_len,
+ const u8 *hdr, size_t hdr_len,
+ u8 *data, size_t data_len, const u8 *tag)
+{
+ u8 *buf;
+ size_t buf_len;
+ u8 nonce_mac[AES_BLOCK_SIZE], hdr_mac[AES_BLOCK_SIZE],
+ data_mac[AES_BLOCK_SIZE];
+ int i;
+
+ if (nonce_len > data_len)
+ buf_len = nonce_len;
+ else
+ buf_len = data_len;
+ if (hdr_len > buf_len)
+ buf_len = hdr_len;
+ buf_len += 16;
+
+ buf = os_malloc(buf_len);
+ if (buf == NULL)
+ return -1;
+
+ os_memset(buf, 0, 15);
+
+ buf[15] = 0;
+ os_memcpy(buf + 16, nonce, nonce_len);
+ if (omac1_aes_128(key, buf, 16 + nonce_len, nonce_mac)) {
+ os_free(buf);
+ return -1;
+ }
+
+ buf[15] = 1;
+ os_memcpy(buf + 16, hdr, hdr_len);
+ if (omac1_aes_128(key, buf, 16 + hdr_len, hdr_mac)) {
+ os_free(buf);
+ return -1;
+ }
+
+ buf[15] = 2;
+ os_memcpy(buf + 16, data, data_len);
+ if (omac1_aes_128(key, buf, 16 + data_len, data_mac)) {
+ os_free(buf);
+ return -1;
+ }
+
+ os_free(buf);
+
+ for (i = 0; i < AES_BLOCK_SIZE; i++) {
+ if (tag[i] != (nonce_mac[i] ^ data_mac[i] ^ hdr_mac[i]))
+ return -2;
+ }
+
+ return aes_128_ctr_encrypt(key, nonce_mac, data, data_len);
+}
diff --git a/freebsd/contrib/wpa/src/crypto/aes-encblock.c b/freebsd/contrib/wpa/src/crypto/aes-encblock.c
new file mode 100644
index 00000000..059f1e2d
--- /dev/null
+++ b/freebsd/contrib/wpa/src/crypto/aes-encblock.c
@@ -0,0 +1,34 @@
+#include <machine/rtems-bsd-user-space.h>
+
+/*
+ * AES encrypt_block
+ *
+ * Copyright (c) 2003-2007, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "aes.h"
+#include "aes_wrap.h"
+
+/**
+ * aes_128_encrypt_block - Perform one AES 128-bit block operation
+ * @key: Key for AES
+ * @in: Input data (16 bytes)
+ * @out: Output of the AES block operation (16 bytes)
+ * Returns: 0 on success, -1 on failure
+ */
+int aes_128_encrypt_block(const u8 *key, const u8 *in, u8 *out)
+{
+ void *ctx;
+ ctx = aes_encrypt_init(key, 16);
+ if (ctx == NULL)
+ return -1;
+ aes_encrypt(ctx, in, out);
+ aes_encrypt_deinit(ctx);
+ return 0;
+}
diff --git a/freebsd/contrib/wpa/src/crypto/aes-omac1.c b/freebsd/contrib/wpa/src/crypto/aes-omac1.c
new file mode 100644
index 00000000..7e62ef85
--- /dev/null
+++ b/freebsd/contrib/wpa/src/crypto/aes-omac1.c
@@ -0,0 +1,172 @@
+#include <machine/rtems-bsd-user-space.h>
+
+/*
+ * One-key CBC MAC (OMAC1) hash with AES
+ *
+ * Copyright (c) 2003-2007, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "aes.h"
+#include "aes_wrap.h"
+
+static void gf_mulx(u8 *pad)
+{
+ int i, carry;
+
+ carry = pad[0] & 0x80;
+ for (i = 0; i < AES_BLOCK_SIZE - 1; i++)
+ pad[i] = (pad[i] << 1) | (pad[i + 1] >> 7);
+ pad[AES_BLOCK_SIZE - 1] <<= 1;
+ if (carry)
+ pad[AES_BLOCK_SIZE - 1] ^= 0x87;
+}
+
+
+/**
+ * omac1_aes_vector - One-Key CBC MAC (OMAC1) hash with AES
+ * @key: Key for the hash operation
+ * @key_len: Key length in octets
+ * @num_elem: Number of elements in the data vector
+ * @addr: Pointers to the data areas
+ * @len: Lengths of the data blocks
+ * @mac: Buffer for MAC (128 bits, i.e., 16 bytes)
+ * Returns: 0 on success, -1 on failure
+ *
+ * This is a mode for using block cipher (AES in this case) for authentication.
+ * OMAC1 was standardized with the name CMAC by NIST in a Special Publication
+ * (SP) 800-38B.
+ */
+int omac1_aes_vector(const u8 *key, size_t key_len, size_t num_elem,
+ const u8 *addr[], const size_t *len, u8 *mac)
+{
+ void *ctx;
+ u8 cbc[AES_BLOCK_SIZE], pad[AES_BLOCK_SIZE];
+ const u8 *pos, *end;
+ size_t i, e, left, total_len;
+
+ ctx = aes_encrypt_init(key, key_len);
+ if (ctx == NULL)
+ return -1;
+ os_memset(cbc, 0, AES_BLOCK_SIZE);
+
+ total_len = 0;
+ for (e = 0; e < num_elem; e++)
+ total_len += len[e];
+ left = total_len;
+
+ e = 0;
+ pos = addr[0];
+ end = pos + len[0];
+
+ while (left >= AES_BLOCK_SIZE) {
+ for (i = 0; i < AES_BLOCK_SIZE; i++) {
+ cbc[i] ^= *pos++;
+ if (pos >= end) {
+ /*
+ * Stop if there are no more bytes to process
+ * since there are no more entries in the array.
+ */
+ if (i + 1 == AES_BLOCK_SIZE &&
+ left == AES_BLOCK_SIZE)
+ break;
+ e++;
+ pos = addr[e];
+ end = pos + len[e];
+ }
+ }
+ if (left > AES_BLOCK_SIZE)
+ aes_encrypt(ctx, cbc, cbc);
+ left -= AES_BLOCK_SIZE;
+ }
+
+ os_memset(pad, 0, AES_BLOCK_SIZE);
+ aes_encrypt(ctx, pad, pad);
+ gf_mulx(pad);
+
+ if (left || total_len == 0) {
+ for (i = 0; i < left; i++) {
+ cbc[i] ^= *pos++;
+ if (pos >= end) {
+ /*
+ * Stop if there are no more bytes to process
+ * since there are no more entries in the array.
+ */
+ if (i + 1 == left)
+ break;
+ e++;
+ pos = addr[e];
+ end = pos + len[e];
+ }
+ }
+ cbc[left] ^= 0x80;
+ gf_mulx(pad);
+ }
+
+ for (i = 0; i < AES_BLOCK_SIZE; i++)
+ pad[i] ^= cbc[i];
+ aes_encrypt(ctx, pad, mac);
+ aes_encrypt_deinit(ctx);
+ return 0;
+}
+
+
+/**
+ * omac1_aes_128_vector - One-Key CBC MAC (OMAC1) hash with AES-128
+ * @key: 128-bit key for the hash operation
+ * @num_elem: Number of elements in the data vector
+ * @addr: Pointers to the data areas
+ * @len: Lengths of the data blocks
+ * @mac: Buffer for MAC (128 bits, i.e., 16 bytes)
+ * Returns: 0 on success, -1 on failure
+ *
+ * This is a mode for using block cipher (AES in this case) for authentication.
+ * OMAC1 was standardized with the name CMAC by NIST in a Special Publication
+ * (SP) 800-38B.
+ */
+int omac1_aes_128_vector(const u8 *key, size_t num_elem,
+ const u8 *addr[], const size_t *len, u8 *mac)
+{
+ return omac1_aes_vector(key, 16, num_elem, addr, len, mac);
+}
+
+
+/**
+ * omac1_aes_128 - One-Key CBC MAC (OMAC1) hash with AES-128 (aka AES-CMAC)
+ * @key: 128-bit key for the hash operation
+ * @data: Data buffer for which a MAC is determined
+ * @data_len: Length of data buffer in bytes
+ * @mac: Buffer for MAC (128 bits, i.e., 16 bytes)
+ * Returns: 0 on success, -1 on failure
+ *
+ * This is a mode for using block cipher (AES in this case) for authentication.
+ * OMAC1 was standardized with the name CMAC by NIST in a Special Publication
+ * (SP) 800-38B.
+ */
+int omac1_aes_128(const u8 *key, const u8 *data, size_t data_len, u8 *mac)
+{
+ return omac1_aes_128_vector(key, 1, &data, &data_len, mac);
+}
+
+
+/**
+ * omac1_aes_256 - One-Key CBC MAC (OMAC1) hash with AES-256 (aka AES-CMAC)
+ * @key: 256-bit key for the hash operation
+ * @data: Data buffer for which a MAC is determined
+ * @data_len: Length of data buffer in bytes
+ * @mac: Buffer for MAC (128 bits, i.e., 16 bytes)
+ * Returns: 0 on success, -1 on failure
+ *
+ * This is a mode for using block cipher (AES in this case) for authentication.
+ * OMAC1 was standardized with the name CMAC by NIST in a Special Publication
+ * (SP) 800-38B.
+ */
+int omac1_aes_256(const u8 *key, const u8 *data, size_t data_len, u8 *mac)
+{
+ return omac1_aes_vector(key, 32, 1, &data, &data_len, mac);
+}
diff --git a/freebsd/contrib/wpa/src/crypto/aes-unwrap.c b/freebsd/contrib/wpa/src/crypto/aes-unwrap.c
new file mode 100644
index 00000000..63e6c34e
--- /dev/null
+++ b/freebsd/contrib/wpa/src/crypto/aes-unwrap.c
@@ -0,0 +1,82 @@
+#include <machine/rtems-bsd-user-space.h>
+
+/*
+ * AES key unwrap (RFC3394)
+ *
+ * Copyright (c) 2003-2007, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "aes.h"
+#include "aes_wrap.h"
+
+/**
+ * aes_unwrap - Unwrap key with AES Key Wrap Algorithm (RFC3394)
+ * @kek: Key encryption key (KEK)
+ * @kek_len: Length of KEK in octets
+ * @n: Length of the plaintext key in 64-bit units; e.g., 2 = 128-bit = 16
+ * bytes
+ * @cipher: Wrapped key to be unwrapped, (n + 1) * 64 bits
+ * @plain: Plaintext key, n * 64 bits
+ * Returns: 0 on success, -1 on failure (e.g., integrity verification failed)
+ */
+int aes_unwrap(const u8 *kek, size_t kek_len, int n, const u8 *cipher,
+ u8 *plain)
+{
+ u8 a[8], *r, b[AES_BLOCK_SIZE];
+ int i, j;
+ void *ctx;
+ unsigned int t;
+
+ /* 1) Initialize variables. */
+ os_memcpy(a, cipher, 8);
+ r = plain;
+ os_memcpy(r, cipher + 8, 8 * n);
+
+ ctx = aes_decrypt_init(kek, kek_len);
+ if (ctx == NULL)
+ return -1;
+
+ /* 2) Compute intermediate values.
+ * For j = 5 to 0
+ * For i = n to 1
+ * B = AES-1(K, (A ^ t) | R[i]) where t = n*j+i
+ * A = MSB(64, B)
+ * R[i] = LSB(64, B)
+ */
+ for (j = 5; j >= 0; j--) {
+ r = plain + (n - 1) * 8;
+ for (i = n; i >= 1; i--) {
+ os_memcpy(b, a, 8);
+ t = n * j + i;
+ b[7] ^= t;
+ b[6] ^= t >> 8;
+ b[5] ^= t >> 16;
+ b[4] ^= t >> 24;
+
+ os_memcpy(b + 8, r, 8);
+ aes_decrypt(ctx, b, b);
+ os_memcpy(a, b, 8);
+ os_memcpy(r, b + 8, 8);
+ r -= 8;
+ }
+ }
+ aes_decrypt_deinit(ctx);
+
+ /* 3) Output results.
+ *
+ * These are already in @plain due to the location of temporary
+ * variables. Just verify that the IV matches with the expected value.
+ */
+ for (i = 0; i < 8; i++) {
+ if (a[i] != 0xa6)
+ return -1;
+ }
+
+ return 0;
+}
diff --git a/freebsd/contrib/wpa/src/crypto/aes.h b/freebsd/contrib/wpa/src/crypto/aes.h
new file mode 100644
index 00000000..2de59e04
--- /dev/null
+++ b/freebsd/contrib/wpa/src/crypto/aes.h
@@ -0,0 +1,21 @@
+/*
+ * AES functions
+ * Copyright (c) 2003-2006, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef AES_H
+#define AES_H
+
+#define AES_BLOCK_SIZE 16
+
+void * aes_encrypt_init(const u8 *key, size_t len);
+void aes_encrypt(void *ctx, const u8 *plain, u8 *crypt);
+void aes_encrypt_deinit(void *ctx);
+void * aes_decrypt_init(const u8 *key, size_t len);
+void aes_decrypt(void *ctx, const u8 *crypt, u8 *plain);
+void aes_decrypt_deinit(void *ctx);
+
+#endif /* AES_H */
diff --git a/freebsd/contrib/wpa/src/crypto/aes_wrap.h b/freebsd/contrib/wpa/src/crypto/aes_wrap.h
new file mode 100644
index 00000000..4a142093
--- /dev/null
+++ b/freebsd/contrib/wpa/src/crypto/aes_wrap.h
@@ -0,0 +1,71 @@
+/*
+ * AES-based functions
+ *
+ * - AES Key Wrap Algorithm (RFC3394)
+ * - One-Key CBC MAC (OMAC1) hash with AES-128 and AES-256
+ * - AES-128 CTR mode encryption
+ * - AES-128 EAX mode encryption/decryption
+ * - AES-128 CBC
+ * - AES-GCM
+ * - AES-CCM
+ *
+ * Copyright (c) 2003-2012, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef AES_WRAP_H
+#define AES_WRAP_H
+
+int __must_check aes_wrap(const u8 *kek, size_t kek_len, int n, const u8 *plain,
+ u8 *cipher);
+int __must_check aes_unwrap(const u8 *kek, size_t kek_len, int n,
+ const u8 *cipher, u8 *plain);
+int __must_check omac1_aes_vector(const u8 *key, size_t key_len,
+ size_t num_elem, const u8 *addr[],
+ const size_t *len, u8 *mac);
+int __must_check omac1_aes_128_vector(const u8 *key, size_t num_elem,
+ const u8 *addr[], const size_t *len,
+ u8 *mac);
+int __must_check omac1_aes_128(const u8 *key, const u8 *data, size_t data_len,
+ u8 *mac);
+int __must_check omac1_aes_256(const u8 *key, const u8 *data, size_t data_len,
+ u8 *mac);
+int __must_check aes_128_encrypt_block(const u8 *key, const u8 *in, u8 *out);
+int __must_check aes_128_ctr_encrypt(const u8 *key, const u8 *nonce,
+ u8 *data, size_t data_len);
+int __must_check aes_128_eax_encrypt(const u8 *key,
+ const u8 *nonce, size_t nonce_len,
+ const u8 *hdr, size_t hdr_len,
+ u8 *data, size_t data_len, u8 *tag);
+int __must_check aes_128_eax_decrypt(const u8 *key,
+ const u8 *nonce, size_t nonce_len,
+ const u8 *hdr, size_t hdr_len,
+ u8 *data, size_t data_len, const u8 *tag);
+int __must_check aes_128_cbc_encrypt(const u8 *key, const u8 *iv, u8 *data,
+ size_t data_len);
+int __must_check aes_128_cbc_decrypt(const u8 *key, const u8 *iv, u8 *data,
+ size_t data_len);
+int __must_check aes_gcm_ae(const u8 *key, size_t key_len,
+ const u8 *iv, size_t iv_len,
+ const u8 *plain, size_t plain_len,
+ const u8 *aad, size_t aad_len,
+ u8 *crypt, u8 *tag);
+int __must_check aes_gcm_ad(const u8 *key, size_t key_len,
+ const u8 *iv, size_t iv_len,
+ const u8 *crypt, size_t crypt_len,
+ const u8 *aad, size_t aad_len, const u8 *tag,
+ u8 *plain);
+int __must_check aes_gmac(const u8 *key, size_t key_len,
+ const u8 *iv, size_t iv_len,
+ const u8 *aad, size_t aad_len, u8 *tag);
+int __must_check aes_ccm_ae(const u8 *key, size_t key_len, const u8 *nonce,
+ size_t M, const u8 *plain, size_t plain_len,
+ const u8 *aad, size_t aad_len, u8 *crypt, u8 *auth);
+int __must_check aes_ccm_ad(const u8 *key, size_t key_len, const u8 *nonce,
+ size_t M, const u8 *crypt, size_t crypt_len,
+ const u8 *aad, size_t aad_len, const u8 *auth,
+ u8 *plain);
+
+#endif /* AES_WRAP_H */
diff --git a/freebsd/contrib/wpa/src/crypto/crypto.h b/freebsd/contrib/wpa/src/crypto/crypto.h
new file mode 100644
index 00000000..534c4bd7
--- /dev/null
+++ b/freebsd/contrib/wpa/src/crypto/crypto.h
@@ -0,0 +1,809 @@
+/*
+ * Wrapper functions for crypto libraries
+ * 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.
+ *
+ * This file defines the cryptographic functions that need to be implemented
+ * for wpa_supplicant and hostapd. When TLS is not used, internal
+ * implementation of MD5, SHA1, and AES is used and no external libraries are
+ * required. When TLS is enabled (e.g., by enabling EAP-TLS or EAP-PEAP), the
+ * crypto library used by the TLS implementation is expected to be used for
+ * non-TLS needs, too, in order to save space by not implementing these
+ * functions twice.
+ *
+ * Wrapper code for using each crypto library is in its own file (crypto*.c)
+ * and one of these files is build and linked in to provide the functions
+ * defined here.
+ */
+
+#ifndef CRYPTO_H
+#define CRYPTO_H
+
+/**
+ * md4_vector - MD4 hash for data vector
+ * @num_elem: Number of elements in the data vector
+ * @addr: Pointers to the data areas
+ * @len: Lengths of the data blocks
+ * @mac: Buffer for the hash
+ * Returns: 0 on success, -1 on failure
+ */
+int md4_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac);
+
+/**
+ * md5_vector - MD5 hash for data vector
+ * @num_elem: Number of elements in the data vector
+ * @addr: Pointers to the data areas
+ * @len: Lengths of the data blocks
+ * @mac: Buffer for the hash
+ * Returns: 0 on success, -1 on failure
+ */
+int md5_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac);
+
+
+/**
+ * sha1_vector - SHA-1 hash for data vector
+ * @num_elem: Number of elements in the data vector
+ * @addr: Pointers to the data areas
+ * @len: Lengths of the data blocks
+ * @mac: Buffer for the hash
+ * Returns: 0 on success, -1 on failure
+ */
+int sha1_vector(size_t num_elem, const u8 *addr[], const size_t *len,
+ u8 *mac);
+
+/**
+ * fips186_2-prf - NIST FIPS Publication 186-2 change notice 1 PRF
+ * @seed: Seed/key for the PRF
+ * @seed_len: Seed length in bytes
+ * @x: Buffer for PRF output
+ * @xlen: Output length in bytes
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function implements random number generation specified in NIST FIPS
+ * Publication 186-2 for EAP-SIM. This PRF uses a function that is similar to
+ * SHA-1, but has different message padding.
+ */
+int __must_check fips186_2_prf(const u8 *seed, size_t seed_len, u8 *x,
+ size_t xlen);
+
+/**
+ * sha256_vector - SHA256 hash for data vector
+ * @num_elem: Number of elements in the data vector
+ * @addr: Pointers to the data areas
+ * @len: Lengths of the data blocks
+ * @mac: Buffer for the hash
+ * Returns: 0 on success, -1 on failure
+ */
+int sha256_vector(size_t num_elem, const u8 *addr[], const size_t *len,
+ u8 *mac);
+
+/**
+ * des_encrypt - Encrypt one block with DES
+ * @clear: 8 octets (in)
+ * @key: 7 octets (in) (no parity bits included)
+ * @cypher: 8 octets (out)
+ */
+void des_encrypt(const u8 *clear, const u8 *key, u8 *cypher);
+
+/**
+ * aes_encrypt_init - Initialize AES for encryption
+ * @key: Encryption key
+ * @len: Key length in bytes (usually 16, i.e., 128 bits)
+ * Returns: Pointer to context data or %NULL on failure
+ */
+void * aes_encrypt_init(const u8 *key, size_t len);
+
+/**
+ * aes_encrypt - Encrypt one AES block
+ * @ctx: Context pointer from aes_encrypt_init()
+ * @plain: Plaintext data to be encrypted (16 bytes)
+ * @crypt: Buffer for the encrypted data (16 bytes)
+ */
+void aes_encrypt(void *ctx, const u8 *plain, u8 *crypt);
+
+/**
+ * aes_encrypt_deinit - Deinitialize AES encryption
+ * @ctx: Context pointer from aes_encrypt_init()
+ */
+void aes_encrypt_deinit(void *ctx);
+
+/**
+ * aes_decrypt_init - Initialize AES for decryption
+ * @key: Decryption key
+ * @len: Key length in bytes (usually 16, i.e., 128 bits)
+ * Returns: Pointer to context data or %NULL on failure
+ */
+void * aes_decrypt_init(const u8 *key, size_t len);
+
+/**
+ * aes_decrypt - Decrypt one AES block
+ * @ctx: Context pointer from aes_encrypt_init()
+ * @crypt: Encrypted data (16 bytes)
+ * @plain: Buffer for the decrypted data (16 bytes)
+ */
+void aes_decrypt(void *ctx, const u8 *crypt, u8 *plain);
+
+/**
+ * aes_decrypt_deinit - Deinitialize AES decryption
+ * @ctx: Context pointer from aes_encrypt_init()
+ */
+void aes_decrypt_deinit(void *ctx);
+
+
+enum crypto_hash_alg {
+ CRYPTO_HASH_ALG_MD5, CRYPTO_HASH_ALG_SHA1,
+ CRYPTO_HASH_ALG_HMAC_MD5, CRYPTO_HASH_ALG_HMAC_SHA1,
+ CRYPTO_HASH_ALG_SHA256, CRYPTO_HASH_ALG_HMAC_SHA256
+};
+
+struct crypto_hash;
+
+/**
+ * crypto_hash_init - Initialize hash/HMAC function
+ * @alg: Hash algorithm
+ * @key: Key for keyed hash (e.g., HMAC) or %NULL if not needed
+ * @key_len: Length of the key in bytes
+ * Returns: Pointer to hash context to use with other hash functions or %NULL
+ * on failure
+ *
+ * This function is only used with internal TLSv1 implementation
+ * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need
+ * to implement this.
+ */
+struct crypto_hash * crypto_hash_init(enum crypto_hash_alg alg, const u8 *key,
+ size_t key_len);
+
+/**
+ * crypto_hash_update - Add data to hash calculation
+ * @ctx: Context pointer from crypto_hash_init()
+ * @data: Data buffer to add
+ * @len: Length of the buffer
+ *
+ * This function is only used with internal TLSv1 implementation
+ * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need
+ * to implement this.
+ */
+void crypto_hash_update(struct crypto_hash *ctx, const u8 *data, size_t len);
+
+/**
+ * crypto_hash_finish - Complete hash calculation
+ * @ctx: Context pointer from crypto_hash_init()
+ * @hash: Buffer for hash value or %NULL if caller is just freeing the hash
+ * context
+ * @len: Pointer to length of the buffer or %NULL if caller is just freeing the
+ * hash context; on return, this is set to the actual length of the hash value
+ * Returns: 0 on success, -1 if buffer is too small (len set to needed length),
+ * or -2 on other failures (including failed crypto_hash_update() operations)
+ *
+ * This function calculates the hash value and frees the context buffer that
+ * was used for hash calculation.
+ *
+ * This function is only used with internal TLSv1 implementation
+ * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need
+ * to implement this.
+ */
+int crypto_hash_finish(struct crypto_hash *ctx, u8 *hash, size_t *len);
+
+
+enum crypto_cipher_alg {
+ CRYPTO_CIPHER_NULL = 0, CRYPTO_CIPHER_ALG_AES, CRYPTO_CIPHER_ALG_3DES,
+ CRYPTO_CIPHER_ALG_DES, CRYPTO_CIPHER_ALG_RC2, CRYPTO_CIPHER_ALG_RC4
+};
+
+struct crypto_cipher;
+
+/**
+ * crypto_cipher_init - Initialize block/stream cipher function
+ * @alg: Cipher algorithm
+ * @iv: Initialization vector for block ciphers or %NULL for stream ciphers
+ * @key: Cipher key
+ * @key_len: Length of key in bytes
+ * Returns: Pointer to cipher context to use with other cipher functions or
+ * %NULL on failure
+ *
+ * This function is only used with internal TLSv1 implementation
+ * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need
+ * to implement this.
+ */
+struct crypto_cipher * crypto_cipher_init(enum crypto_cipher_alg alg,
+ const u8 *iv, const u8 *key,
+ size_t key_len);
+
+/**
+ * crypto_cipher_encrypt - Cipher encrypt
+ * @ctx: Context pointer from crypto_cipher_init()
+ * @plain: Plaintext to cipher
+ * @crypt: Resulting ciphertext
+ * @len: Length of the plaintext
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function is only used with internal TLSv1 implementation
+ * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need
+ * to implement this.
+ */
+int __must_check crypto_cipher_encrypt(struct crypto_cipher *ctx,
+ const u8 *plain, u8 *crypt, size_t len);
+
+/**
+ * crypto_cipher_decrypt - Cipher decrypt
+ * @ctx: Context pointer from crypto_cipher_init()
+ * @crypt: Ciphertext to decrypt
+ * @plain: Resulting plaintext
+ * @len: Length of the cipher text
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function is only used with internal TLSv1 implementation
+ * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need
+ * to implement this.
+ */
+int __must_check crypto_cipher_decrypt(struct crypto_cipher *ctx,
+ const u8 *crypt, u8 *plain, size_t len);
+
+/**
+ * crypto_cipher_decrypt - Free cipher context
+ * @ctx: Context pointer from crypto_cipher_init()
+ *
+ * This function is only used with internal TLSv1 implementation
+ * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need
+ * to implement this.
+ */
+void crypto_cipher_deinit(struct crypto_cipher *ctx);
+
+
+struct crypto_public_key;
+struct crypto_private_key;
+
+/**
+ * crypto_public_key_import - Import an RSA public key
+ * @key: Key buffer (DER encoded RSA public key)
+ * @len: Key buffer length in bytes
+ * Returns: Pointer to the public key or %NULL on failure
+ *
+ * This function can just return %NULL if the crypto library supports X.509
+ * parsing. In that case, crypto_public_key_from_cert() is used to import the
+ * public key from a certificate.
+ *
+ * This function is only used with internal TLSv1 implementation
+ * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need
+ * to implement this.
+ */
+struct crypto_public_key * crypto_public_key_import(const u8 *key, size_t len);
+
+struct crypto_public_key *
+crypto_public_key_import_parts(const u8 *n, size_t n_len,
+ const u8 *e, size_t e_len);
+
+/**
+ * crypto_private_key_import - Import an RSA private key
+ * @key: Key buffer (DER encoded RSA private key)
+ * @len: Key buffer length in bytes
+ * @passwd: Key encryption password or %NULL if key is not encrypted
+ * Returns: Pointer to the private key or %NULL on failure
+ *
+ * This function is only used with internal TLSv1 implementation
+ * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need
+ * to implement this.
+ */
+struct crypto_private_key * crypto_private_key_import(const u8 *key,
+ size_t len,
+ const char *passwd);
+
+/**
+ * crypto_public_key_from_cert - Import an RSA public key from a certificate
+ * @buf: DER encoded X.509 certificate
+ * @len: Certificate buffer length in bytes
+ * Returns: Pointer to public key or %NULL on failure
+ *
+ * This function can just return %NULL if the crypto library does not support
+ * X.509 parsing. In that case, internal code will be used to parse the
+ * certificate and public key is imported using crypto_public_key_import().
+ *
+ * This function is only used with internal TLSv1 implementation
+ * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need
+ * to implement this.
+ */
+struct crypto_public_key * crypto_public_key_from_cert(const u8 *buf,
+ size_t len);
+
+/**
+ * crypto_public_key_encrypt_pkcs1_v15 - Public key encryption (PKCS #1 v1.5)
+ * @key: Public key
+ * @in: Plaintext buffer
+ * @inlen: Length of plaintext buffer in bytes
+ * @out: Output buffer for encrypted data
+ * @outlen: Length of output buffer in bytes; set to used length on success
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function is only used with internal TLSv1 implementation
+ * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need
+ * to implement this.
+ */
+int __must_check crypto_public_key_encrypt_pkcs1_v15(
+ struct crypto_public_key *key, const u8 *in, size_t inlen,
+ u8 *out, size_t *outlen);
+
+/**
+ * crypto_private_key_decrypt_pkcs1_v15 - Private key decryption (PKCS #1 v1.5)
+ * @key: Private key
+ * @in: Encrypted buffer
+ * @inlen: Length of encrypted buffer in bytes
+ * @out: Output buffer for encrypted data
+ * @outlen: Length of output buffer in bytes; set to used length on success
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function is only used with internal TLSv1 implementation
+ * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need
+ * to implement this.
+ */
+int __must_check crypto_private_key_decrypt_pkcs1_v15(
+ struct crypto_private_key *key, const u8 *in, size_t inlen,
+ u8 *out, size_t *outlen);
+
+/**
+ * crypto_private_key_sign_pkcs1 - Sign with private key (PKCS #1)
+ * @key: Private key from crypto_private_key_import()
+ * @in: Plaintext buffer
+ * @inlen: Length of plaintext buffer in bytes
+ * @out: Output buffer for encrypted (signed) data
+ * @outlen: Length of output buffer in bytes; set to used length on success
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function is only used with internal TLSv1 implementation
+ * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need
+ * to implement this.
+ */
+int __must_check crypto_private_key_sign_pkcs1(struct crypto_private_key *key,
+ const u8 *in, size_t inlen,
+ u8 *out, size_t *outlen);
+
+/**
+ * crypto_public_key_free - Free public key
+ * @key: Public key
+ *
+ * This function is only used with internal TLSv1 implementation
+ * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need
+ * to implement this.
+ */
+void crypto_public_key_free(struct crypto_public_key *key);
+
+/**
+ * crypto_private_key_free - Free private key
+ * @key: Private key from crypto_private_key_import()
+ *
+ * This function is only used with internal TLSv1 implementation
+ * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need
+ * to implement this.
+ */
+void crypto_private_key_free(struct crypto_private_key *key);
+
+/**
+ * crypto_public_key_decrypt_pkcs1 - Decrypt PKCS #1 signature
+ * @key: Public key
+ * @crypt: Encrypted signature data (using the private key)
+ * @crypt_len: Encrypted signature data length
+ * @plain: Buffer for plaintext (at least crypt_len bytes)
+ * @plain_len: Plaintext length (max buffer size on input, real len on output);
+ * Returns: 0 on success, -1 on failure
+ */
+int __must_check crypto_public_key_decrypt_pkcs1(
+ struct crypto_public_key *key, const u8 *crypt, size_t crypt_len,
+ u8 *plain, size_t *plain_len);
+
+/**
+ * crypto_global_init - Initialize crypto wrapper
+ *
+ * This function is only used with internal TLSv1 implementation
+ * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need
+ * to implement this.
+ */
+int __must_check crypto_global_init(void);
+
+/**
+ * crypto_global_deinit - Deinitialize crypto wrapper
+ *
+ * This function is only used with internal TLSv1 implementation
+ * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need
+ * to implement this.
+ */
+void crypto_global_deinit(void);
+
+/**
+ * crypto_mod_exp - Modular exponentiation of large integers
+ * @base: Base integer (big endian byte array)
+ * @base_len: Length of base integer in bytes
+ * @power: Power integer (big endian byte array)
+ * @power_len: Length of power integer in bytes
+ * @modulus: Modulus integer (big endian byte array)
+ * @modulus_len: Length of modulus integer in bytes
+ * @result: Buffer for the result
+ * @result_len: Result length (max buffer size on input, real len on output)
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function calculates result = base ^ power mod modulus. modules_len is
+ * used as the maximum size of modulus buffer. It is set to the used size on
+ * success.
+ *
+ * This function is only used with internal TLSv1 implementation
+ * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need
+ * to implement this.
+ */
+int __must_check crypto_mod_exp(const u8 *base, size_t base_len,
+ const u8 *power, size_t power_len,
+ const u8 *modulus, size_t modulus_len,
+ u8 *result, size_t *result_len);
+
+/**
+ * rc4_skip - XOR RC4 stream to given data with skip-stream-start
+ * @key: RC4 key
+ * @keylen: RC4 key length
+ * @skip: number of bytes to skip from the beginning of the RC4 stream
+ * @data: data to be XOR'ed with RC4 stream
+ * @data_len: buf length
+ * Returns: 0 on success, -1 on failure
+ *
+ * Generate RC4 pseudo random stream for the given key, skip beginning of the
+ * stream, and XOR the end result with the data buffer to perform RC4
+ * encryption/decryption.
+ */
+int rc4_skip(const u8 *key, size_t keylen, size_t skip,
+ u8 *data, size_t data_len);
+
+/**
+ * crypto_get_random - Generate cryptographically strong pseudy-random bytes
+ * @buf: Buffer for data
+ * @len: Number of bytes to generate
+ * Returns: 0 on success, -1 on failure
+ *
+ * If the PRNG does not have enough entropy to ensure unpredictable byte
+ * sequence, this functions must return -1.
+ */
+int crypto_get_random(void *buf, size_t len);
+
+
+/**
+ * struct crypto_bignum - bignum
+ *
+ * Internal data structure for bignum implementation. The contents is specific
+ * to the used crypto library.
+ */
+struct crypto_bignum;
+
+/**
+ * crypto_bignum_init - Allocate memory for bignum
+ * Returns: Pointer to allocated bignum or %NULL on failure
+ */
+struct crypto_bignum * crypto_bignum_init(void);
+
+/**
+ * crypto_bignum_init_set - Allocate memory for bignum and set the value
+ * @buf: Buffer with unsigned binary value
+ * @len: Length of buf in octets
+ * Returns: Pointer to allocated bignum or %NULL on failure
+ */
+struct crypto_bignum * crypto_bignum_init_set(const u8 *buf, size_t len);
+
+/**
+ * crypto_bignum_deinit - Free bignum
+ * @n: Bignum from crypto_bignum_init() or crypto_bignum_init_set()
+ * @clear: Whether to clear the value from memory
+ */
+void crypto_bignum_deinit(struct crypto_bignum *n, int clear);
+
+/**
+ * crypto_bignum_to_bin - Set binary buffer to unsigned bignum
+ * @a: Bignum
+ * @buf: Buffer for the binary number
+ * @len: Length of @buf in octets
+ * @padlen: Length in octets to pad the result to or 0 to indicate no padding
+ * Returns: Number of octets written on success, -1 on failure
+ */
+int crypto_bignum_to_bin(const struct crypto_bignum *a,
+ u8 *buf, size_t buflen, size_t padlen);
+
+/**
+ * crypto_bignum_add - c = a + b
+ * @a: Bignum
+ * @b: Bignum
+ * @c: Bignum; used to store the result of a + b
+ * Returns: 0 on success, -1 on failure
+ */
+int crypto_bignum_add(const struct crypto_bignum *a,
+ const struct crypto_bignum *b,
+ struct crypto_bignum *c);
+
+/**
+ * crypto_bignum_mod - c = a % b
+ * @a: Bignum
+ * @b: Bignum
+ * @c: Bignum; used to store the result of a % b
+ * Returns: 0 on success, -1 on failure
+ */
+int crypto_bignum_mod(const struct crypto_bignum *a,
+ const struct crypto_bignum *b,
+ struct crypto_bignum *c);
+
+/**
+ * crypto_bignum_exptmod - Modular exponentiation: d = a^b (mod c)
+ * @a: Bignum; base
+ * @b: Bignum; exponent
+ * @c: Bignum; modulus
+ * @d: Bignum; used to store the result of a^b (mod c)
+ * Returns: 0 on success, -1 on failure
+ */
+int crypto_bignum_exptmod(const struct crypto_bignum *a,
+ const struct crypto_bignum *b,
+ const struct crypto_bignum *c,
+ struct crypto_bignum *d);
+
+/**
+ * crypto_bignum_inverse - Inverse a bignum so that a * c = 1 (mod b)
+ * @a: Bignum
+ * @b: Bignum
+ * @c: Bignum; used to store the result
+ * Returns: 0 on success, -1 on failure
+ */
+int crypto_bignum_inverse(const struct crypto_bignum *a,
+ const struct crypto_bignum *b,
+ struct crypto_bignum *c);
+
+/**
+ * crypto_bignum_sub - c = a - b
+ * @a: Bignum
+ * @b: Bignum
+ * @c: Bignum; used to store the result of a - b
+ * Returns: 0 on success, -1 on failure
+ */
+int crypto_bignum_sub(const struct crypto_bignum *a,
+ const struct crypto_bignum *b,
+ struct crypto_bignum *c);
+
+/**
+ * crypto_bignum_div - c = a / b
+ * @a: Bignum
+ * @b: Bignum
+ * @c: Bignum; used to store the result of a / b
+ * Returns: 0 on success, -1 on failure
+ */
+int crypto_bignum_div(const struct crypto_bignum *a,
+ const struct crypto_bignum *b,
+ struct crypto_bignum *c);
+
+/**
+ * crypto_bignum_mulmod - d = a * b (mod c)
+ * @a: Bignum
+ * @b: Bignum
+ * @c: Bignum
+ * @d: Bignum; used to store the result of (a * b) % c
+ * Returns: 0 on success, -1 on failure
+ */
+int crypto_bignum_mulmod(const struct crypto_bignum *a,
+ const struct crypto_bignum *b,
+ const struct crypto_bignum *c,
+ struct crypto_bignum *d);
+
+/**
+ * crypto_bignum_cmp - Compare two bignums
+ * @a: Bignum
+ * @b: Bignum
+ * Returns: -1 if a < b, 0 if a == b, or 1 if a > b
+ */
+int crypto_bignum_cmp(const struct crypto_bignum *a,
+ const struct crypto_bignum *b);
+
+/**
+ * crypto_bignum_bits - Get size of a bignum in bits
+ * @a: Bignum
+ * Returns: Number of bits in the bignum
+ */
+int crypto_bignum_bits(const struct crypto_bignum *a);
+
+/**
+ * crypto_bignum_is_zero - Is the given bignum zero
+ * @a: Bignum
+ * Returns: 1 if @a is zero or 0 if not
+ */
+int crypto_bignum_is_zero(const struct crypto_bignum *a);
+
+/**
+ * crypto_bignum_is_one - Is the given bignum one
+ * @a: Bignum
+ * Returns: 1 if @a is one or 0 if not
+ */
+int crypto_bignum_is_one(const struct crypto_bignum *a);
+
+/**
+ * crypto_bignum_legendre - Compute the Legendre symbol (a/p)
+ * @a: Bignum
+ * @p: Bignum
+ * Returns: Legendre symbol -1,0,1 on success; -2 on calculation failure
+ */
+int crypto_bignum_legendre(const struct crypto_bignum *a,
+ const struct crypto_bignum *p);
+
+/**
+ * struct crypto_ec - Elliptic curve context
+ *
+ * Internal data structure for EC implementation. The contents is specific
+ * to the used crypto library.
+ */
+struct crypto_ec;
+
+/**
+ * crypto_ec_init - Initialize elliptic curve context
+ * @group: Identifying number for the ECC group (IANA "Group Description"
+ * attribute registrty for RFC 2409)
+ * Returns: Pointer to EC context or %NULL on failure
+ */
+struct crypto_ec * crypto_ec_init(int group);
+
+/**
+ * crypto_ec_deinit - Deinitialize elliptic curve context
+ * @e: EC context from crypto_ec_init()
+ */
+void crypto_ec_deinit(struct crypto_ec *e);
+
+/**
+ * crypto_ec_prime_len - Get length of the prime in octets
+ * @e: EC context from crypto_ec_init()
+ * Returns: Length of the prime defining the group
+ */
+size_t crypto_ec_prime_len(struct crypto_ec *e);
+
+/**
+ * crypto_ec_prime_len_bits - Get length of the prime in bits
+ * @e: EC context from crypto_ec_init()
+ * Returns: Length of the prime defining the group in bits
+ */
+size_t crypto_ec_prime_len_bits(struct crypto_ec *e);
+
+/**
+ * crypto_ec_get_prime - Get prime defining an EC group
+ * @e: EC context from crypto_ec_init()
+ * Returns: Prime (bignum) defining the group
+ */
+const struct crypto_bignum * crypto_ec_get_prime(struct crypto_ec *e);
+
+/**
+ * crypto_ec_get_order - Get order of an EC group
+ * @e: EC context from crypto_ec_init()
+ * Returns: Order (bignum) of the group
+ */
+const struct crypto_bignum * crypto_ec_get_order(struct crypto_ec *e);
+
+/**
+ * struct crypto_ec_point - Elliptic curve point
+ *
+ * Internal data structure for EC implementation to represent a point. The
+ * contents is specific to the used crypto library.
+ */
+struct crypto_ec_point;
+
+/**
+ * crypto_ec_point_init - Initialize data for an EC point
+ * @e: EC context from crypto_ec_init()
+ * Returns: Pointer to EC point data or %NULL on failure
+ */
+struct crypto_ec_point * crypto_ec_point_init(struct crypto_ec *e);
+
+/**
+ * crypto_ec_point_deinit - Deinitialize EC point data
+ * @p: EC point data from crypto_ec_point_init()
+ * @clear: Whether to clear the EC point value from memory
+ */
+void crypto_ec_point_deinit(struct crypto_ec_point *p, int clear);
+
+/**
+ * crypto_ec_point_to_bin - Write EC point value as binary data
+ * @e: EC context from crypto_ec_init()
+ * @p: EC point data from crypto_ec_point_init()
+ * @x: Buffer for writing the binary data for x coordinate or %NULL if not used
+ * @y: Buffer for writing the binary data for y coordinate or %NULL if not used
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function can be used to write an EC point as binary data in a format
+ * that has the x and y coordinates in big endian byte order fields padded to
+ * the length of the prime defining the group.
+ */
+int crypto_ec_point_to_bin(struct crypto_ec *e,
+ const struct crypto_ec_point *point, u8 *x, u8 *y);
+
+/**
+ * crypto_ec_point_from_bin - Create EC point from binary data
+ * @e: EC context from crypto_ec_init()
+ * @val: Binary data to read the EC point from
+ * Returns: Pointer to EC point data or %NULL on failure
+ *
+ * This function readers x and y coordinates of the EC point from the provided
+ * buffer assuming the values are in big endian byte order with fields padded to
+ * the length of the prime defining the group.
+ */
+struct crypto_ec_point * crypto_ec_point_from_bin(struct crypto_ec *e,
+ const u8 *val);
+
+/**
+ * crypto_bignum_add - c = a + b
+ * @e: EC context from crypto_ec_init()
+ * @a: Bignum
+ * @b: Bignum
+ * @c: Bignum; used to store the result of a + b
+ * Returns: 0 on success, -1 on failure
+ */
+int crypto_ec_point_add(struct crypto_ec *e, const struct crypto_ec_point *a,
+ const struct crypto_ec_point *b,
+ struct crypto_ec_point *c);
+
+/**
+ * crypto_bignum_mul - res = b * p
+ * @e: EC context from crypto_ec_init()
+ * @p: EC point
+ * @b: Bignum
+ * @res: EC point; used to store the result of b * p
+ * Returns: 0 on success, -1 on failure
+ */
+int crypto_ec_point_mul(struct crypto_ec *e, const struct crypto_ec_point *p,
+ const struct crypto_bignum *b,
+ struct crypto_ec_point *res);
+
+/**
+ * crypto_ec_point_invert - Compute inverse of an EC point
+ * @e: EC context from crypto_ec_init()
+ * @p: EC point to invert (and result of the operation)
+ * Returns: 0 on success, -1 on failure
+ */
+int crypto_ec_point_invert(struct crypto_ec *e, struct crypto_ec_point *p);
+
+/**
+ * crypto_ec_point_solve_y_coord - Solve y coordinate for an x coordinate
+ * @e: EC context from crypto_ec_init()
+ * @p: EC point to use for the returning the result
+ * @x: x coordinate
+ * @y_bit: y-bit (0 or 1) for selecting the y value to use
+ * Returns: 0 on success, -1 on failure
+ */
+int crypto_ec_point_solve_y_coord(struct crypto_ec *e,
+ struct crypto_ec_point *p,
+ const struct crypto_bignum *x, int y_bit);
+
+/**
+ * crypto_ec_point_compute_y_sqr - Compute y^2 = x^3 + ax + b
+ * @e: EC context from crypto_ec_init()
+ * @x: x coordinate
+ * Returns: y^2 on success, %NULL failure
+ */
+struct crypto_bignum *
+crypto_ec_point_compute_y_sqr(struct crypto_ec *e,
+ const struct crypto_bignum *x);
+
+/**
+ * crypto_ec_point_is_at_infinity - Check whether EC point is neutral element
+ * @e: EC context from crypto_ec_init()
+ * @p: EC point
+ * Returns: 1 if the specified EC point is the neutral element of the group or
+ * 0 if not
+ */
+int crypto_ec_point_is_at_infinity(struct crypto_ec *e,
+ const struct crypto_ec_point *p);
+
+/**
+ * crypto_ec_point_is_on_curve - Check whether EC point is on curve
+ * @e: EC context from crypto_ec_init()
+ * @p: EC point
+ * Returns: 1 if the specified EC point is on the curve or 0 if not
+ */
+int crypto_ec_point_is_on_curve(struct crypto_ec *e,
+ const struct crypto_ec_point *p);
+
+/**
+ * crypto_ec_point_cmp - Compare two EC points
+ * @e: EC context from crypto_ec_init()
+ * @a: EC point
+ * @b: EC point
+ * Returns: 0 on equal, non-zero otherwise
+ */
+int crypto_ec_point_cmp(const struct crypto_ec *e,
+ const struct crypto_ec_point *a,
+ const struct crypto_ec_point *b);
+
+#endif /* CRYPTO_H */
diff --git a/freebsd/contrib/wpa/src/crypto/crypto_openssl.c b/freebsd/contrib/wpa/src/crypto/crypto_openssl.c
new file mode 100644
index 00000000..83630422
--- /dev/null
+++ b/freebsd/contrib/wpa/src/crypto/crypto_openssl.c
@@ -0,0 +1,1444 @@
+#include <machine/rtems-bsd-user-space.h>
+
+/*
+ * Wrapper functions for OpenSSL libcrypto
+ * Copyright (c) 2004-2015, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+#include <openssl/opensslv.h>
+#include <openssl/err.h>
+#include <openssl/des.h>
+#include <openssl/aes.h>
+#include <openssl/bn.h>
+#include <openssl/evp.h>
+#include <openssl/dh.h>
+#include <openssl/hmac.h>
+#include <openssl/rand.h>
+#ifdef CONFIG_OPENSSL_CMAC
+#include <openssl/cmac.h>
+#endif /* CONFIG_OPENSSL_CMAC */
+#ifdef CONFIG_ECC
+#include <openssl/ec.h>
+#endif /* CONFIG_ECC */
+
+#include "common.h"
+#include "wpabuf.h"
+#include "dh_group5.h"
+#include "sha1.h"
+#include "sha256.h"
+#include "sha384.h"
+#include "crypto.h"
+
+static BIGNUM * get_group5_prime(void)
+{
+#ifdef OPENSSL_IS_BORINGSSL
+ static const unsigned char RFC3526_PRIME_1536[] = {
+ 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xC9,0x0F,0xDA,0xA2,
+ 0x21,0x68,0xC2,0x34,0xC4,0xC6,0x62,0x8B,0x80,0xDC,0x1C,0xD1,
+ 0x29,0x02,0x4E,0x08,0x8A,0x67,0xCC,0x74,0x02,0x0B,0xBE,0xA6,
+ 0x3B,0x13,0x9B,0x22,0x51,0x4A,0x08,0x79,0x8E,0x34,0x04,0xDD,
+ 0xEF,0x95,0x19,0xB3,0xCD,0x3A,0x43,0x1B,0x30,0x2B,0x0A,0x6D,
+ 0xF2,0x5F,0x14,0x37,0x4F,0xE1,0x35,0x6D,0x6D,0x51,0xC2,0x45,
+ 0xE4,0x85,0xB5,0x76,0x62,0x5E,0x7E,0xC6,0xF4,0x4C,0x42,0xE9,
+ 0xA6,0x37,0xED,0x6B,0x0B,0xFF,0x5C,0xB6,0xF4,0x06,0xB7,0xED,
+ 0xEE,0x38,0x6B,0xFB,0x5A,0x89,0x9F,0xA5,0xAE,0x9F,0x24,0x11,
+ 0x7C,0x4B,0x1F,0xE6,0x49,0x28,0x66,0x51,0xEC,0xE4,0x5B,0x3D,
+ 0xC2,0x00,0x7C,0xB8,0xA1,0x63,0xBF,0x05,0x98,0xDA,0x48,0x36,
+ 0x1C,0x55,0xD3,0x9A,0x69,0x16,0x3F,0xA8,0xFD,0x24,0xCF,0x5F,
+ 0x83,0x65,0x5D,0x23,0xDC,0xA3,0xAD,0x96,0x1C,0x62,0xF3,0x56,
+ 0x20,0x85,0x52,0xBB,0x9E,0xD5,0x29,0x07,0x70,0x96,0x96,0x6D,
+ 0x67,0x0C,0x35,0x4E,0x4A,0xBC,0x98,0x04,0xF1,0x74,0x6C,0x08,
+ 0xCA,0x23,0x73,0x27,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
+ };
+ return BN_bin2bn(RFC3526_PRIME_1536, sizeof(RFC3526_PRIME_1536), NULL);
+#else /* OPENSSL_IS_BORINGSSL */
+ return get_rfc3526_prime_1536(NULL);
+#endif /* OPENSSL_IS_BORINGSSL */
+}
+
+#ifdef OPENSSL_NO_SHA256
+#define NO_SHA256_WRAPPER
+#endif
+
+static int openssl_digest_vector(const EVP_MD *type, size_t num_elem,
+ const u8 *addr[], const size_t *len, u8 *mac)
+{
+ EVP_MD_CTX ctx;
+ size_t i;
+ unsigned int mac_len;
+
+ EVP_MD_CTX_init(&ctx);
+ if (!EVP_DigestInit_ex(&ctx, type, NULL)) {
+ wpa_printf(MSG_ERROR, "OpenSSL: EVP_DigestInit_ex failed: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ return -1;
+ }
+ for (i = 0; i < num_elem; i++) {
+ if (!EVP_DigestUpdate(&ctx, addr[i], len[i])) {
+ wpa_printf(MSG_ERROR, "OpenSSL: EVP_DigestUpdate "
+ "failed: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ return -1;
+ }
+ }
+ if (!EVP_DigestFinal(&ctx, mac, &mac_len)) {
+ wpa_printf(MSG_ERROR, "OpenSSL: EVP_DigestFinal failed: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ return -1;
+ }
+
+ return 0;
+}
+
+
+#ifndef CONFIG_FIPS
+int md4_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
+{
+ return openssl_digest_vector(EVP_md4(), num_elem, addr, len, mac);
+}
+#endif /* CONFIG_FIPS */
+
+
+void des_encrypt(const u8 *clear, const u8 *key, u8 *cypher)
+{
+ u8 pkey[8], next, tmp;
+ int i;
+ DES_key_schedule ks;
+
+ /* Add parity bits to the key */
+ next = 0;
+ for (i = 0; i < 7; i++) {
+ tmp = key[i];
+ pkey[i] = (tmp >> i) | next | 1;
+ next = tmp << (7 - i);
+ }
+ pkey[i] = next | 1;
+
+ DES_set_key((DES_cblock *) &pkey, &ks);
+ DES_ecb_encrypt((DES_cblock *) clear, (DES_cblock *) cypher, &ks,
+ DES_ENCRYPT);
+}
+
+
+#ifndef CONFIG_NO_RC4
+int rc4_skip(const u8 *key, size_t keylen, size_t skip,
+ u8 *data, size_t data_len)
+{
+#ifdef OPENSSL_NO_RC4
+ return -1;
+#else /* OPENSSL_NO_RC4 */
+ EVP_CIPHER_CTX ctx;
+ int outl;
+ int res = -1;
+ unsigned char skip_buf[16];
+
+ EVP_CIPHER_CTX_init(&ctx);
+ if (!EVP_CIPHER_CTX_set_padding(&ctx, 0) ||
+ !EVP_CipherInit_ex(&ctx, EVP_rc4(), NULL, NULL, NULL, 1) ||
+ !EVP_CIPHER_CTX_set_key_length(&ctx, keylen) ||
+ !EVP_CipherInit_ex(&ctx, NULL, NULL, key, NULL, 1))
+ goto out;
+
+ while (skip >= sizeof(skip_buf)) {
+ size_t len = skip;
+ if (len > sizeof(skip_buf))
+ len = sizeof(skip_buf);
+ if (!EVP_CipherUpdate(&ctx, skip_buf, &outl, skip_buf, len))
+ goto out;
+ skip -= len;
+ }
+
+ if (EVP_CipherUpdate(&ctx, data, &outl, data, data_len))
+ res = 0;
+
+out:
+ EVP_CIPHER_CTX_cleanup(&ctx);
+ return res;
+#endif /* OPENSSL_NO_RC4 */
+}
+#endif /* CONFIG_NO_RC4 */
+
+
+#ifndef CONFIG_FIPS
+int md5_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
+{
+ return openssl_digest_vector(EVP_md5(), num_elem, addr, len, mac);
+}
+#endif /* CONFIG_FIPS */
+
+
+int sha1_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
+{
+ return openssl_digest_vector(EVP_sha1(), num_elem, addr, len, mac);
+}
+
+
+#ifndef NO_SHA256_WRAPPER
+int sha256_vector(size_t num_elem, const u8 *addr[], const size_t *len,
+ u8 *mac)
+{
+ return openssl_digest_vector(EVP_sha256(), num_elem, addr, len, mac);
+}
+#endif /* NO_SHA256_WRAPPER */
+
+
+static const EVP_CIPHER * aes_get_evp_cipher(size_t keylen)
+{
+ switch (keylen) {
+ case 16:
+ return EVP_aes_128_ecb();
+#ifndef OPENSSL_IS_BORINGSSL
+ case 24:
+ return EVP_aes_192_ecb();
+#endif /* OPENSSL_IS_BORINGSSL */
+ case 32:
+ return EVP_aes_256_ecb();
+ }
+
+ return NULL;
+}
+
+
+void * aes_encrypt_init(const u8 *key, size_t len)
+{
+ EVP_CIPHER_CTX *ctx;
+ const EVP_CIPHER *type;
+
+ type = aes_get_evp_cipher(len);
+ if (type == NULL)
+ return NULL;
+
+ ctx = os_malloc(sizeof(*ctx));
+ if (ctx == NULL)
+ return NULL;
+ EVP_CIPHER_CTX_init(ctx);
+ if (EVP_EncryptInit_ex(ctx, type, NULL, key, NULL) != 1) {
+ os_free(ctx);
+ return NULL;
+ }
+ EVP_CIPHER_CTX_set_padding(ctx, 0);
+ return ctx;
+}
+
+
+void aes_encrypt(void *ctx, const u8 *plain, u8 *crypt)
+{
+ EVP_CIPHER_CTX *c = ctx;
+ int clen = 16;
+ if (EVP_EncryptUpdate(c, crypt, &clen, plain, 16) != 1) {
+ wpa_printf(MSG_ERROR, "OpenSSL: EVP_EncryptUpdate failed: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ }
+}
+
+
+void aes_encrypt_deinit(void *ctx)
+{
+ EVP_CIPHER_CTX *c = ctx;
+ u8 buf[16];
+ int len = sizeof(buf);
+ if (EVP_EncryptFinal_ex(c, buf, &len) != 1) {
+ wpa_printf(MSG_ERROR, "OpenSSL: EVP_EncryptFinal_ex failed: "
+ "%s", ERR_error_string(ERR_get_error(), NULL));
+ }
+ if (len != 0) {
+ wpa_printf(MSG_ERROR, "OpenSSL: Unexpected padding length %d "
+ "in AES encrypt", len);
+ }
+ EVP_CIPHER_CTX_cleanup(c);
+ bin_clear_free(c, sizeof(*c));
+}
+
+
+void * aes_decrypt_init(const u8 *key, size_t len)
+{
+ EVP_CIPHER_CTX *ctx;
+ const EVP_CIPHER *type;
+
+ type = aes_get_evp_cipher(len);
+ if (type == NULL)
+ return NULL;
+
+ ctx = os_malloc(sizeof(*ctx));
+ if (ctx == NULL)
+ return NULL;
+ EVP_CIPHER_CTX_init(ctx);
+ if (EVP_DecryptInit_ex(ctx, type, NULL, key, NULL) != 1) {
+ os_free(ctx);
+ return NULL;
+ }
+ EVP_CIPHER_CTX_set_padding(ctx, 0);
+ return ctx;
+}
+
+
+void aes_decrypt(void *ctx, const u8 *crypt, u8 *plain)
+{
+ EVP_CIPHER_CTX *c = ctx;
+ int plen = 16;
+ if (EVP_DecryptUpdate(c, plain, &plen, crypt, 16) != 1) {
+ wpa_printf(MSG_ERROR, "OpenSSL: EVP_DecryptUpdate failed: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ }
+}
+
+
+void aes_decrypt_deinit(void *ctx)
+{
+ EVP_CIPHER_CTX *c = ctx;
+ u8 buf[16];
+ int len = sizeof(buf);
+ if (EVP_DecryptFinal_ex(c, buf, &len) != 1) {
+ wpa_printf(MSG_ERROR, "OpenSSL: EVP_DecryptFinal_ex failed: "
+ "%s", ERR_error_string(ERR_get_error(), NULL));
+ }
+ if (len != 0) {
+ wpa_printf(MSG_ERROR, "OpenSSL: Unexpected padding length %d "
+ "in AES decrypt", len);
+ }
+ EVP_CIPHER_CTX_cleanup(c);
+ bin_clear_free(c, sizeof(*c));
+}
+
+
+#ifndef CONFIG_FIPS
+#ifndef CONFIG_OPENSSL_INTERNAL_AES_WRAP
+
+int aes_wrap(const u8 *kek, size_t kek_len, int n, const u8 *plain, u8 *cipher)
+{
+ AES_KEY actx;
+ int res;
+
+ if (AES_set_encrypt_key(kek, kek_len << 3, &actx))
+ return -1;
+ res = AES_wrap_key(&actx, NULL, cipher, plain, n * 8);
+ OPENSSL_cleanse(&actx, sizeof(actx));
+ return res <= 0 ? -1 : 0;
+}
+
+
+int aes_unwrap(const u8 *kek, size_t kek_len, int n, const u8 *cipher,
+ u8 *plain)
+{
+ AES_KEY actx;
+ int res;
+
+ if (AES_set_decrypt_key(kek, kek_len << 3, &actx))
+ return -1;
+ res = AES_unwrap_key(&actx, NULL, plain, cipher, (n + 1) * 8);
+ OPENSSL_cleanse(&actx, sizeof(actx));
+ return res <= 0 ? -1 : 0;
+}
+
+#endif /* CONFIG_OPENSSL_INTERNAL_AES_WRAP */
+#endif /* CONFIG_FIPS */
+
+
+int aes_128_cbc_encrypt(const u8 *key, const u8 *iv, u8 *data, size_t data_len)
+{
+ EVP_CIPHER_CTX ctx;
+ int clen, len;
+ u8 buf[16];
+
+ EVP_CIPHER_CTX_init(&ctx);
+ if (EVP_EncryptInit_ex(&ctx, EVP_aes_128_cbc(), NULL, key, iv) != 1)
+ return -1;
+ EVP_CIPHER_CTX_set_padding(&ctx, 0);
+
+ clen = data_len;
+ if (EVP_EncryptUpdate(&ctx, data, &clen, data, data_len) != 1 ||
+ clen != (int) data_len)
+ return -1;
+
+ len = sizeof(buf);
+ if (EVP_EncryptFinal_ex(&ctx, buf, &len) != 1 || len != 0)
+ return -1;
+ EVP_CIPHER_CTX_cleanup(&ctx);
+
+ return 0;
+}
+
+
+int aes_128_cbc_decrypt(const u8 *key, const u8 *iv, u8 *data, size_t data_len)
+{
+ EVP_CIPHER_CTX ctx;
+ int plen, len;
+ u8 buf[16];
+
+ EVP_CIPHER_CTX_init(&ctx);
+ if (EVP_DecryptInit_ex(&ctx, EVP_aes_128_cbc(), NULL, key, iv) != 1)
+ return -1;
+ EVP_CIPHER_CTX_set_padding(&ctx, 0);
+
+ plen = data_len;
+ if (EVP_DecryptUpdate(&ctx, data, &plen, data, data_len) != 1 ||
+ plen != (int) data_len)
+ return -1;
+
+ len = sizeof(buf);
+ if (EVP_DecryptFinal_ex(&ctx, buf, &len) != 1 || len != 0)
+ return -1;
+ EVP_CIPHER_CTX_cleanup(&ctx);
+
+ return 0;
+}
+
+
+int crypto_mod_exp(const u8 *base, size_t base_len,
+ const u8 *power, size_t power_len,
+ const u8 *modulus, size_t modulus_len,
+ u8 *result, size_t *result_len)
+{
+ BIGNUM *bn_base, *bn_exp, *bn_modulus, *bn_result;
+ int ret = -1;
+ BN_CTX *ctx;
+
+ ctx = BN_CTX_new();
+ if (ctx == NULL)
+ return -1;
+
+ bn_base = BN_bin2bn(base, base_len, NULL);
+ bn_exp = BN_bin2bn(power, power_len, NULL);
+ bn_modulus = BN_bin2bn(modulus, modulus_len, NULL);
+ bn_result = BN_new();
+
+ if (bn_base == NULL || bn_exp == NULL || bn_modulus == NULL ||
+ bn_result == NULL)
+ goto error;
+
+ if (BN_mod_exp(bn_result, bn_base, bn_exp, bn_modulus, ctx) != 1)
+ goto error;
+
+ *result_len = BN_bn2bin(bn_result, result);
+ ret = 0;
+
+error:
+ BN_clear_free(bn_base);
+ BN_clear_free(bn_exp);
+ BN_clear_free(bn_modulus);
+ BN_clear_free(bn_result);
+ BN_CTX_free(ctx);
+ return ret;
+}
+
+
+struct crypto_cipher {
+ EVP_CIPHER_CTX enc;
+ EVP_CIPHER_CTX dec;
+};
+
+
+struct crypto_cipher * crypto_cipher_init(enum crypto_cipher_alg alg,
+ const u8 *iv, const u8 *key,
+ size_t key_len)
+{
+ struct crypto_cipher *ctx;
+ const EVP_CIPHER *cipher;
+
+ ctx = os_zalloc(sizeof(*ctx));
+ if (ctx == NULL)
+ return NULL;
+
+ switch (alg) {
+#ifndef CONFIG_NO_RC4
+#ifndef OPENSSL_NO_RC4
+ case CRYPTO_CIPHER_ALG_RC4:
+ cipher = EVP_rc4();
+ break;
+#endif /* OPENSSL_NO_RC4 */
+#endif /* CONFIG_NO_RC4 */
+#ifndef OPENSSL_NO_AES
+ case CRYPTO_CIPHER_ALG_AES:
+ switch (key_len) {
+ case 16:
+ cipher = EVP_aes_128_cbc();
+ break;
+#ifndef OPENSSL_IS_BORINGSSL
+ case 24:
+ cipher = EVP_aes_192_cbc();
+ break;
+#endif /* OPENSSL_IS_BORINGSSL */
+ case 32:
+ cipher = EVP_aes_256_cbc();
+ break;
+ default:
+ os_free(ctx);
+ return NULL;
+ }
+ break;
+#endif /* OPENSSL_NO_AES */
+#ifndef OPENSSL_NO_DES
+ case CRYPTO_CIPHER_ALG_3DES:
+ cipher = EVP_des_ede3_cbc();
+ break;
+ case CRYPTO_CIPHER_ALG_DES:
+ cipher = EVP_des_cbc();
+ break;
+#endif /* OPENSSL_NO_DES */
+#ifndef OPENSSL_NO_RC2
+ case CRYPTO_CIPHER_ALG_RC2:
+ cipher = EVP_rc2_ecb();
+ break;
+#endif /* OPENSSL_NO_RC2 */
+ default:
+ os_free(ctx);
+ return NULL;
+ }
+
+ EVP_CIPHER_CTX_init(&ctx->enc);
+ EVP_CIPHER_CTX_set_padding(&ctx->enc, 0);
+ if (!EVP_EncryptInit_ex(&ctx->enc, cipher, NULL, NULL, NULL) ||
+ !EVP_CIPHER_CTX_set_key_length(&ctx->enc, key_len) ||
+ !EVP_EncryptInit_ex(&ctx->enc, NULL, NULL, key, iv)) {
+ EVP_CIPHER_CTX_cleanup(&ctx->enc);
+ os_free(ctx);
+ return NULL;
+ }
+
+ EVP_CIPHER_CTX_init(&ctx->dec);
+ EVP_CIPHER_CTX_set_padding(&ctx->dec, 0);
+ if (!EVP_DecryptInit_ex(&ctx->dec, cipher, NULL, NULL, NULL) ||
+ !EVP_CIPHER_CTX_set_key_length(&ctx->dec, key_len) ||
+ !EVP_DecryptInit_ex(&ctx->dec, NULL, NULL, key, iv)) {
+ EVP_CIPHER_CTX_cleanup(&ctx->enc);
+ EVP_CIPHER_CTX_cleanup(&ctx->dec);
+ os_free(ctx);
+ return NULL;
+ }
+
+ return ctx;
+}
+
+
+int crypto_cipher_encrypt(struct crypto_cipher *ctx, const u8 *plain,
+ u8 *crypt, size_t len)
+{
+ int outl;
+ if (!EVP_EncryptUpdate(&ctx->enc, crypt, &outl, plain, len))
+ return -1;
+ return 0;
+}
+
+
+int crypto_cipher_decrypt(struct crypto_cipher *ctx, const u8 *crypt,
+ u8 *plain, size_t len)
+{
+ int outl;
+ outl = len;
+ if (!EVP_DecryptUpdate(&ctx->dec, plain, &outl, crypt, len))
+ return -1;
+ return 0;
+}
+
+
+void crypto_cipher_deinit(struct crypto_cipher *ctx)
+{
+ EVP_CIPHER_CTX_cleanup(&ctx->enc);
+ EVP_CIPHER_CTX_cleanup(&ctx->dec);
+ os_free(ctx);
+}
+
+
+void * dh5_init(struct wpabuf **priv, struct wpabuf **publ)
+{
+ DH *dh;
+ struct wpabuf *pubkey = NULL, *privkey = NULL;
+ size_t publen, privlen;
+
+ *priv = NULL;
+ *publ = NULL;
+
+ dh = DH_new();
+ if (dh == NULL)
+ return NULL;
+
+ dh->g = BN_new();
+ if (dh->g == NULL || BN_set_word(dh->g, 2) != 1)
+ goto err;
+
+ dh->p = get_group5_prime();
+ if (dh->p == NULL)
+ goto err;
+
+ if (DH_generate_key(dh) != 1)
+ goto err;
+
+ publen = BN_num_bytes(dh->pub_key);
+ pubkey = wpabuf_alloc(publen);
+ if (pubkey == NULL)
+ goto err;
+ privlen = BN_num_bytes(dh->priv_key);
+ privkey = wpabuf_alloc(privlen);
+ if (privkey == NULL)
+ goto err;
+
+ BN_bn2bin(dh->pub_key, wpabuf_put(pubkey, publen));
+ BN_bn2bin(dh->priv_key, wpabuf_put(privkey, privlen));
+
+ *priv = privkey;
+ *publ = pubkey;
+ return dh;
+
+err:
+ wpabuf_clear_free(pubkey);
+ wpabuf_clear_free(privkey);
+ DH_free(dh);
+ return NULL;
+}
+
+
+void * dh5_init_fixed(const struct wpabuf *priv, const struct wpabuf *publ)
+{
+ DH *dh;
+
+ dh = DH_new();
+ if (dh == NULL)
+ return NULL;
+
+ dh->g = BN_new();
+ if (dh->g == NULL || BN_set_word(dh->g, 2) != 1)
+ goto err;
+
+ dh->p = get_group5_prime();
+ if (dh->p == NULL)
+ goto err;
+
+ dh->priv_key = BN_bin2bn(wpabuf_head(priv), wpabuf_len(priv), NULL);
+ if (dh->priv_key == NULL)
+ goto err;
+
+ dh->pub_key = BN_bin2bn(wpabuf_head(publ), wpabuf_len(publ), NULL);
+ if (dh->pub_key == NULL)
+ goto err;
+
+ if (DH_generate_key(dh) != 1)
+ goto err;
+
+ return dh;
+
+err:
+ DH_free(dh);
+ return NULL;
+}
+
+
+struct wpabuf * dh5_derive_shared(void *ctx, const struct wpabuf *peer_public,
+ const struct wpabuf *own_private)
+{
+ BIGNUM *pub_key;
+ struct wpabuf *res = NULL;
+ size_t rlen;
+ DH *dh = ctx;
+ int keylen;
+
+ if (ctx == NULL)
+ return NULL;
+
+ pub_key = BN_bin2bn(wpabuf_head(peer_public), wpabuf_len(peer_public),
+ NULL);
+ if (pub_key == NULL)
+ return NULL;
+
+ rlen = DH_size(dh);
+ res = wpabuf_alloc(rlen);
+ if (res == NULL)
+ goto err;
+
+ keylen = DH_compute_key(wpabuf_mhead(res), pub_key, dh);
+ if (keylen < 0)
+ goto err;
+ wpabuf_put(res, keylen);
+ BN_clear_free(pub_key);
+
+ return res;
+
+err:
+ BN_clear_free(pub_key);
+ wpabuf_clear_free(res);
+ return NULL;
+}
+
+
+void dh5_free(void *ctx)
+{
+ DH *dh;
+ if (ctx == NULL)
+ return;
+ dh = ctx;
+ DH_free(dh);
+}
+
+
+struct crypto_hash {
+ HMAC_CTX ctx;
+};
+
+
+struct crypto_hash * crypto_hash_init(enum crypto_hash_alg alg, const u8 *key,
+ size_t key_len)
+{
+ struct crypto_hash *ctx;
+ const EVP_MD *md;
+
+ switch (alg) {
+#ifndef OPENSSL_NO_MD5
+ case CRYPTO_HASH_ALG_HMAC_MD5:
+ md = EVP_md5();
+ break;
+#endif /* OPENSSL_NO_MD5 */
+#ifndef OPENSSL_NO_SHA
+ case CRYPTO_HASH_ALG_HMAC_SHA1:
+ md = EVP_sha1();
+ break;
+#endif /* OPENSSL_NO_SHA */
+#ifndef OPENSSL_NO_SHA256
+#ifdef CONFIG_SHA256
+ case CRYPTO_HASH_ALG_HMAC_SHA256:
+ md = EVP_sha256();
+ break;
+#endif /* CONFIG_SHA256 */
+#endif /* OPENSSL_NO_SHA256 */
+ default:
+ return NULL;
+ }
+
+ ctx = os_zalloc(sizeof(*ctx));
+ if (ctx == NULL)
+ return NULL;
+ HMAC_CTX_init(&ctx->ctx);
+
+#if OPENSSL_VERSION_NUMBER < 0x00909000
+ HMAC_Init_ex(&ctx->ctx, key, key_len, md, NULL);
+#else /* openssl < 0.9.9 */
+ if (HMAC_Init_ex(&ctx->ctx, key, key_len, md, NULL) != 1) {
+ bin_clear_free(ctx, sizeof(*ctx));
+ return NULL;
+ }
+#endif /* openssl < 0.9.9 */
+
+ return ctx;
+}
+
+
+void crypto_hash_update(struct crypto_hash *ctx, const u8 *data, size_t len)
+{
+ if (ctx == NULL)
+ return;
+ HMAC_Update(&ctx->ctx, data, len);
+}
+
+
+int crypto_hash_finish(struct crypto_hash *ctx, u8 *mac, size_t *len)
+{
+ unsigned int mdlen;
+ int res;
+
+ if (ctx == NULL)
+ return -2;
+
+ if (mac == NULL || len == NULL) {
+ bin_clear_free(ctx, sizeof(*ctx));
+ return 0;
+ }
+
+ mdlen = *len;
+#if OPENSSL_VERSION_NUMBER < 0x00909000
+ HMAC_Final(&ctx->ctx, mac, &mdlen);
+ res = 1;
+#else /* openssl < 0.9.9 */
+ res = HMAC_Final(&ctx->ctx, mac, &mdlen);
+#endif /* openssl < 0.9.9 */
+ HMAC_CTX_cleanup(&ctx->ctx);
+ bin_clear_free(ctx, sizeof(*ctx));
+
+ if (res == 1) {
+ *len = mdlen;
+ return 0;
+ }
+
+ return -1;
+}
+
+
+static int openssl_hmac_vector(const EVP_MD *type, const u8 *key,
+ size_t key_len, size_t num_elem,
+ const u8 *addr[], const size_t *len, u8 *mac,
+ unsigned int mdlen)
+{
+ HMAC_CTX ctx;
+ size_t i;
+ int res;
+
+ HMAC_CTX_init(&ctx);
+#if OPENSSL_VERSION_NUMBER < 0x00909000
+ HMAC_Init_ex(&ctx, key, key_len, type, NULL);
+#else /* openssl < 0.9.9 */
+ if (HMAC_Init_ex(&ctx, key, key_len, type, NULL) != 1)
+ return -1;
+#endif /* openssl < 0.9.9 */
+
+ for (i = 0; i < num_elem; i++)
+ HMAC_Update(&ctx, addr[i], len[i]);
+
+#if OPENSSL_VERSION_NUMBER < 0x00909000
+ HMAC_Final(&ctx, mac, &mdlen);
+ res = 1;
+#else /* openssl < 0.9.9 */
+ res = HMAC_Final(&ctx, mac, &mdlen);
+#endif /* openssl < 0.9.9 */
+ HMAC_CTX_cleanup(&ctx);
+
+ return res == 1 ? 0 : -1;
+}
+
+
+#ifndef CONFIG_FIPS
+
+int hmac_md5_vector(const u8 *key, size_t key_len, size_t num_elem,
+ const u8 *addr[], const size_t *len, u8 *mac)
+{
+ return openssl_hmac_vector(EVP_md5(), key ,key_len, num_elem, addr, len,
+ mac, 16);
+}
+
+
+int hmac_md5(const u8 *key, size_t key_len, const u8 *data, size_t data_len,
+ u8 *mac)
+{
+ return hmac_md5_vector(key, key_len, 1, &data, &data_len, mac);
+}
+
+#endif /* CONFIG_FIPS */
+
+
+int pbkdf2_sha1(const char *passphrase, const u8 *ssid, size_t ssid_len,
+ int iterations, u8 *buf, size_t buflen)
+{
+ if (PKCS5_PBKDF2_HMAC_SHA1(passphrase, os_strlen(passphrase), ssid,
+ ssid_len, iterations, buflen, buf) != 1)
+ return -1;
+ return 0;
+}
+
+
+int hmac_sha1_vector(const u8 *key, size_t key_len, size_t num_elem,
+ const u8 *addr[], const size_t *len, u8 *mac)
+{
+ return openssl_hmac_vector(EVP_sha1(), key, key_len, num_elem, addr,
+ len, mac, 20);
+}
+
+
+int hmac_sha1(const u8 *key, size_t key_len, const u8 *data, size_t data_len,
+ u8 *mac)
+{
+ return hmac_sha1_vector(key, key_len, 1, &data, &data_len, mac);
+}
+
+
+#ifdef CONFIG_SHA256
+
+int hmac_sha256_vector(const u8 *key, size_t key_len, size_t num_elem,
+ const u8 *addr[], const size_t *len, u8 *mac)
+{
+ return openssl_hmac_vector(EVP_sha256(), key, key_len, num_elem, addr,
+ len, mac, 32);
+}
+
+
+int hmac_sha256(const u8 *key, size_t key_len, const u8 *data,
+ size_t data_len, u8 *mac)
+{
+ return hmac_sha256_vector(key, key_len, 1, &data, &data_len, mac);
+}
+
+#endif /* CONFIG_SHA256 */
+
+
+#ifdef CONFIG_SHA384
+
+int hmac_sha384_vector(const u8 *key, size_t key_len, size_t num_elem,
+ const u8 *addr[], const size_t *len, u8 *mac)
+{
+ return openssl_hmac_vector(EVP_sha384(), key, key_len, num_elem, addr,
+ len, mac, 32);
+}
+
+
+int hmac_sha384(const u8 *key, size_t key_len, const u8 *data,
+ size_t data_len, u8 *mac)
+{
+ return hmac_sha384_vector(key, key_len, 1, &data, &data_len, mac);
+}
+
+#endif /* CONFIG_SHA384 */
+
+
+int crypto_get_random(void *buf, size_t len)
+{
+ if (RAND_bytes(buf, len) != 1)
+ return -1;
+ return 0;
+}
+
+
+#ifdef CONFIG_OPENSSL_CMAC
+int omac1_aes_vector(const u8 *key, size_t key_len, size_t num_elem,
+ const u8 *addr[], const size_t *len, u8 *mac)
+{
+ CMAC_CTX *ctx;
+ int ret = -1;
+ size_t outlen, i;
+
+ ctx = CMAC_CTX_new();
+ if (ctx == NULL)
+ return -1;
+
+ if (key_len == 32) {
+ if (!CMAC_Init(ctx, key, 32, EVP_aes_256_cbc(), NULL))
+ goto fail;
+ } else if (key_len == 16) {
+ if (!CMAC_Init(ctx, key, 16, EVP_aes_128_cbc(), NULL))
+ goto fail;
+ } else {
+ goto fail;
+ }
+ for (i = 0; i < num_elem; i++) {
+ if (!CMAC_Update(ctx, addr[i], len[i]))
+ goto fail;
+ }
+ if (!CMAC_Final(ctx, mac, &outlen) || outlen != 16)
+ goto fail;
+
+ ret = 0;
+fail:
+ CMAC_CTX_free(ctx);
+ return ret;
+}
+
+
+int omac1_aes_128_vector(const u8 *key, size_t num_elem,
+ const u8 *addr[], const size_t *len, u8 *mac)
+{
+ return omac1_aes_vector(key, 16, num_elem, addr, len, mac);
+}
+
+
+int omac1_aes_128(const u8 *key, const u8 *data, size_t data_len, u8 *mac)
+{
+ return omac1_aes_128_vector(key, 1, &data, &data_len, mac);
+}
+
+
+int omac1_aes_256(const u8 *key, const u8 *data, size_t data_len, u8 *mac)
+{
+ return omac1_aes_vector(key, 32, 1, &data, &data_len, mac);
+}
+#endif /* CONFIG_OPENSSL_CMAC */
+
+
+struct crypto_bignum * crypto_bignum_init(void)
+{
+ return (struct crypto_bignum *) BN_new();
+}
+
+
+struct crypto_bignum * crypto_bignum_init_set(const u8 *buf, size_t len)
+{
+ BIGNUM *bn = BN_bin2bn(buf, len, NULL);
+ return (struct crypto_bignum *) bn;
+}
+
+
+void crypto_bignum_deinit(struct crypto_bignum *n, int clear)
+{
+ if (clear)
+ BN_clear_free((BIGNUM *) n);
+ else
+ BN_free((BIGNUM *) n);
+}
+
+
+int crypto_bignum_to_bin(const struct crypto_bignum *a,
+ u8 *buf, size_t buflen, size_t padlen)
+{
+ int num_bytes, offset;
+
+ if (padlen > buflen)
+ return -1;
+
+ num_bytes = BN_num_bytes((const BIGNUM *) a);
+ if ((size_t) num_bytes > buflen)
+ return -1;
+ if (padlen > (size_t) num_bytes)
+ offset = padlen - num_bytes;
+ else
+ offset = 0;
+
+ os_memset(buf, 0, offset);
+ BN_bn2bin((const BIGNUM *) a, buf + offset);
+
+ return num_bytes + offset;
+}
+
+
+int crypto_bignum_add(const struct crypto_bignum *a,
+ const struct crypto_bignum *b,
+ struct crypto_bignum *c)
+{
+ return BN_add((BIGNUM *) c, (const BIGNUM *) a, (const BIGNUM *) b) ?
+ 0 : -1;
+}
+
+
+int crypto_bignum_mod(const struct crypto_bignum *a,
+ const struct crypto_bignum *b,
+ struct crypto_bignum *c)
+{
+ int res;
+ BN_CTX *bnctx;
+
+ bnctx = BN_CTX_new();
+ if (bnctx == NULL)
+ return -1;
+ res = BN_mod((BIGNUM *) c, (const BIGNUM *) a, (const BIGNUM *) b,
+ bnctx);
+ BN_CTX_free(bnctx);
+
+ return res ? 0 : -1;
+}
+
+
+int crypto_bignum_exptmod(const struct crypto_bignum *a,
+ const struct crypto_bignum *b,
+ const struct crypto_bignum *c,
+ struct crypto_bignum *d)
+{
+ int res;
+ BN_CTX *bnctx;
+
+ bnctx = BN_CTX_new();
+ if (bnctx == NULL)
+ return -1;
+ res = BN_mod_exp((BIGNUM *) d, (const BIGNUM *) a, (const BIGNUM *) b,
+ (const BIGNUM *) c, bnctx);
+ BN_CTX_free(bnctx);
+
+ return res ? 0 : -1;
+}
+
+
+int crypto_bignum_inverse(const struct crypto_bignum *a,
+ const struct crypto_bignum *b,
+ struct crypto_bignum *c)
+{
+ BIGNUM *res;
+ BN_CTX *bnctx;
+
+ bnctx = BN_CTX_new();
+ if (bnctx == NULL)
+ return -1;
+ res = BN_mod_inverse((BIGNUM *) c, (const BIGNUM *) a,
+ (const BIGNUM *) b, bnctx);
+ BN_CTX_free(bnctx);
+
+ return res ? 0 : -1;
+}
+
+
+int crypto_bignum_sub(const struct crypto_bignum *a,
+ const struct crypto_bignum *b,
+ struct crypto_bignum *c)
+{
+ return BN_sub((BIGNUM *) c, (const BIGNUM *) a, (const BIGNUM *) b) ?
+ 0 : -1;
+}
+
+
+int crypto_bignum_div(const struct crypto_bignum *a,
+ const struct crypto_bignum *b,
+ struct crypto_bignum *c)
+{
+ int res;
+
+ BN_CTX *bnctx;
+
+ bnctx = BN_CTX_new();
+ if (bnctx == NULL)
+ return -1;
+ res = BN_div((BIGNUM *) c, NULL, (const BIGNUM *) a,
+ (const BIGNUM *) b, bnctx);
+ BN_CTX_free(bnctx);
+
+ return res ? 0 : -1;
+}
+
+
+int crypto_bignum_mulmod(const struct crypto_bignum *a,
+ const struct crypto_bignum *b,
+ const struct crypto_bignum *c,
+ struct crypto_bignum *d)
+{
+ int res;
+
+ BN_CTX *bnctx;
+
+ bnctx = BN_CTX_new();
+ if (bnctx == NULL)
+ return -1;
+ res = BN_mod_mul((BIGNUM *) d, (const BIGNUM *) a, (const BIGNUM *) b,
+ (const BIGNUM *) c, bnctx);
+ BN_CTX_free(bnctx);
+
+ return res ? 0 : -1;
+}
+
+
+int crypto_bignum_cmp(const struct crypto_bignum *a,
+ const struct crypto_bignum *b)
+{
+ return BN_cmp((const BIGNUM *) a, (const BIGNUM *) b);
+}
+
+
+int crypto_bignum_bits(const struct crypto_bignum *a)
+{
+ return BN_num_bits((const BIGNUM *) a);
+}
+
+
+int crypto_bignum_is_zero(const struct crypto_bignum *a)
+{
+ return BN_is_zero((const BIGNUM *) a);
+}
+
+
+int crypto_bignum_is_one(const struct crypto_bignum *a)
+{
+ return BN_is_one((const BIGNUM *) a);
+}
+
+
+int crypto_bignum_legendre(const struct crypto_bignum *a,
+ const struct crypto_bignum *p)
+{
+ BN_CTX *bnctx;
+ BIGNUM *exp = NULL, *tmp = NULL;
+ int res = -2;
+
+ bnctx = BN_CTX_new();
+ if (bnctx == NULL)
+ return -2;
+
+ exp = BN_new();
+ tmp = BN_new();
+ if (!exp || !tmp ||
+ /* exp = (p-1) / 2 */
+ !BN_sub(exp, (const BIGNUM *) p, BN_value_one()) ||
+ !BN_rshift1(exp, exp) ||
+ !BN_mod_exp(tmp, (const BIGNUM *) a, exp, (const BIGNUM *) p,
+ bnctx))
+ goto fail;
+
+ if (BN_is_word(tmp, 1))
+ res = 1;
+ else if (BN_is_zero(tmp))
+ res = 0;
+ else
+ res = -1;
+
+fail:
+ BN_clear_free(tmp);
+ BN_clear_free(exp);
+ BN_CTX_free(bnctx);
+ return res;
+}
+
+
+#ifdef CONFIG_ECC
+
+struct crypto_ec {
+ EC_GROUP *group;
+ BN_CTX *bnctx;
+ BIGNUM *prime;
+ BIGNUM *order;
+ BIGNUM *a;
+ BIGNUM *b;
+};
+
+struct crypto_ec * crypto_ec_init(int group)
+{
+ struct crypto_ec *e;
+ int nid;
+
+ /* Map from IANA registry for IKE D-H groups to OpenSSL NID */
+ switch (group) {
+ case 19:
+ nid = NID_X9_62_prime256v1;
+ break;
+ case 20:
+ nid = NID_secp384r1;
+ break;
+ case 21:
+ nid = NID_secp521r1;
+ break;
+ case 25:
+ nid = NID_X9_62_prime192v1;
+ break;
+ case 26:
+ nid = NID_secp224r1;
+ break;
+#ifdef NID_brainpoolP224r1
+ case 27:
+ nid = NID_brainpoolP224r1;
+ break;
+#endif /* NID_brainpoolP224r1 */
+#ifdef NID_brainpoolP256r1
+ case 28:
+ nid = NID_brainpoolP256r1;
+ break;
+#endif /* NID_brainpoolP256r1 */
+#ifdef NID_brainpoolP384r1
+ case 29:
+ nid = NID_brainpoolP384r1;
+ break;
+#endif /* NID_brainpoolP384r1 */
+#ifdef NID_brainpoolP512r1
+ case 30:
+ nid = NID_brainpoolP512r1;
+ break;
+#endif /* NID_brainpoolP512r1 */
+ default:
+ return NULL;
+ }
+
+ e = os_zalloc(sizeof(*e));
+ if (e == NULL)
+ return NULL;
+
+ e->bnctx = BN_CTX_new();
+ e->group = EC_GROUP_new_by_curve_name(nid);
+ e->prime = BN_new();
+ e->order = BN_new();
+ e->a = BN_new();
+ e->b = BN_new();
+ if (e->group == NULL || e->bnctx == NULL || e->prime == NULL ||
+ e->order == NULL || e->a == NULL || e->b == NULL ||
+ !EC_GROUP_get_curve_GFp(e->group, e->prime, e->a, e->b, e->bnctx) ||
+ !EC_GROUP_get_order(e->group, e->order, e->bnctx)) {
+ crypto_ec_deinit(e);
+ e = NULL;
+ }
+
+ return e;
+}
+
+
+void crypto_ec_deinit(struct crypto_ec *e)
+{
+ if (e == NULL)
+ return;
+ BN_clear_free(e->b);
+ BN_clear_free(e->a);
+ BN_clear_free(e->order);
+ BN_clear_free(e->prime);
+ EC_GROUP_free(e->group);
+ BN_CTX_free(e->bnctx);
+ os_free(e);
+}
+
+
+struct crypto_ec_point * crypto_ec_point_init(struct crypto_ec *e)
+{
+ if (e == NULL)
+ return NULL;
+ return (struct crypto_ec_point *) EC_POINT_new(e->group);
+}
+
+
+size_t crypto_ec_prime_len(struct crypto_ec *e)
+{
+ return BN_num_bytes(e->prime);
+}
+
+
+size_t crypto_ec_prime_len_bits(struct crypto_ec *e)
+{
+ return BN_num_bits(e->prime);
+}
+
+
+const struct crypto_bignum * crypto_ec_get_prime(struct crypto_ec *e)
+{
+ return (const struct crypto_bignum *) e->prime;
+}
+
+
+const struct crypto_bignum * crypto_ec_get_order(struct crypto_ec *e)
+{
+ return (const struct crypto_bignum *) e->order;
+}
+
+
+void crypto_ec_point_deinit(struct crypto_ec_point *p, int clear)
+{
+ if (clear)
+ EC_POINT_clear_free((EC_POINT *) p);
+ else
+ EC_POINT_free((EC_POINT *) p);
+}
+
+
+int crypto_ec_point_to_bin(struct crypto_ec *e,
+ const struct crypto_ec_point *point, u8 *x, u8 *y)
+{
+ BIGNUM *x_bn, *y_bn;
+ int ret = -1;
+ int len = BN_num_bytes(e->prime);
+
+ x_bn = BN_new();
+ y_bn = BN_new();
+
+ if (x_bn && y_bn &&
+ EC_POINT_get_affine_coordinates_GFp(e->group, (EC_POINT *) point,
+ x_bn, y_bn, e->bnctx)) {
+ if (x) {
+ crypto_bignum_to_bin((struct crypto_bignum *) x_bn,
+ x, len, len);
+ }
+ if (y) {
+ crypto_bignum_to_bin((struct crypto_bignum *) y_bn,
+ y, len, len);
+ }
+ ret = 0;
+ }
+
+ BN_clear_free(x_bn);
+ BN_clear_free(y_bn);
+ return ret;
+}
+
+
+struct crypto_ec_point * crypto_ec_point_from_bin(struct crypto_ec *e,
+ const u8 *val)
+{
+ BIGNUM *x, *y;
+ EC_POINT *elem;
+ int len = BN_num_bytes(e->prime);
+
+ x = BN_bin2bn(val, len, NULL);
+ y = BN_bin2bn(val + len, len, NULL);
+ elem = EC_POINT_new(e->group);
+ if (x == NULL || y == NULL || elem == NULL) {
+ BN_clear_free(x);
+ BN_clear_free(y);
+ EC_POINT_clear_free(elem);
+ return NULL;
+ }
+
+ if (!EC_POINT_set_affine_coordinates_GFp(e->group, elem, x, y,
+ e->bnctx)) {
+ EC_POINT_clear_free(elem);
+ elem = NULL;
+ }
+
+ BN_clear_free(x);
+ BN_clear_free(y);
+
+ return (struct crypto_ec_point *) elem;
+}
+
+
+int crypto_ec_point_add(struct crypto_ec *e, const struct crypto_ec_point *a,
+ const struct crypto_ec_point *b,
+ struct crypto_ec_point *c)
+{
+ return EC_POINT_add(e->group, (EC_POINT *) c, (const EC_POINT *) a,
+ (const EC_POINT *) b, e->bnctx) ? 0 : -1;
+}
+
+
+int crypto_ec_point_mul(struct crypto_ec *e, const struct crypto_ec_point *p,
+ const struct crypto_bignum *b,
+ struct crypto_ec_point *res)
+{
+ return EC_POINT_mul(e->group, (EC_POINT *) res, NULL,
+ (const EC_POINT *) p, (const BIGNUM *) b, e->bnctx)
+ ? 0 : -1;
+}
+
+
+int crypto_ec_point_invert(struct crypto_ec *e, struct crypto_ec_point *p)
+{
+ return EC_POINT_invert(e->group, (EC_POINT *) p, e->bnctx) ? 0 : -1;
+}
+
+
+int crypto_ec_point_solve_y_coord(struct crypto_ec *e,
+ struct crypto_ec_point *p,
+ const struct crypto_bignum *x, int y_bit)
+{
+ if (!EC_POINT_set_compressed_coordinates_GFp(e->group, (EC_POINT *) p,
+ (const BIGNUM *) x, y_bit,
+ e->bnctx) ||
+ !EC_POINT_is_on_curve(e->group, (EC_POINT *) p, e->bnctx))
+ return -1;
+ return 0;
+}
+
+
+struct crypto_bignum *
+crypto_ec_point_compute_y_sqr(struct crypto_ec *e,
+ const struct crypto_bignum *x)
+{
+ BIGNUM *tmp, *tmp2, *y_sqr = NULL;
+
+ tmp = BN_new();
+ tmp2 = BN_new();
+
+ /* y^2 = x^3 + ax + b */
+ if (tmp && tmp2 &&
+ BN_mod_sqr(tmp, (const BIGNUM *) x, e->prime, e->bnctx) &&
+ BN_mod_mul(tmp, tmp, (const BIGNUM *) x, e->prime, e->bnctx) &&
+ BN_mod_mul(tmp2, e->a, (const BIGNUM *) x, e->prime, e->bnctx) &&
+ BN_mod_add_quick(tmp2, tmp2, tmp, e->prime) &&
+ BN_mod_add_quick(tmp2, tmp2, e->b, e->prime)) {
+ y_sqr = tmp2;
+ tmp2 = NULL;
+ }
+
+ BN_clear_free(tmp);
+ BN_clear_free(tmp2);
+
+ return (struct crypto_bignum *) y_sqr;
+}
+
+
+int crypto_ec_point_is_at_infinity(struct crypto_ec *e,
+ const struct crypto_ec_point *p)
+{
+ return EC_POINT_is_at_infinity(e->group, (const EC_POINT *) p);
+}
+
+
+int crypto_ec_point_is_on_curve(struct crypto_ec *e,
+ const struct crypto_ec_point *p)
+{
+ return EC_POINT_is_on_curve(e->group, (const EC_POINT *) p,
+ e->bnctx) == 1;
+}
+
+
+int crypto_ec_point_cmp(const struct crypto_ec *e,
+ const struct crypto_ec_point *a,
+ const struct crypto_ec_point *b)
+{
+ return EC_POINT_cmp(e->group, (const EC_POINT *) a,
+ (const EC_POINT *) b, e->bnctx);
+}
+
+#endif /* CONFIG_ECC */
diff --git a/freebsd/contrib/wpa/src/crypto/dh_group5.h b/freebsd/contrib/wpa/src/crypto/dh_group5.h
new file mode 100644
index 00000000..abee8eaa
--- /dev/null
+++ b/freebsd/contrib/wpa/src/crypto/dh_group5.h
@@ -0,0 +1,18 @@
+/*
+ * Diffie-Hellman group 5 operations
+ * Copyright (c) 2009, 2012, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef DH_GROUP5_H
+#define DH_GROUP5_H
+
+void * dh5_init(struct wpabuf **priv, struct wpabuf **publ);
+void * dh5_init_fixed(const struct wpabuf *priv, const struct wpabuf *publ);
+struct wpabuf * dh5_derive_shared(void *ctx, const struct wpabuf *peer_public,
+ const struct wpabuf *own_private);
+void dh5_free(void *ctx);
+
+#endif /* DH_GROUP5_H */
diff --git a/freebsd/contrib/wpa/src/crypto/md5.h b/freebsd/contrib/wpa/src/crypto/md5.h
new file mode 100644
index 00000000..33f8426c
--- /dev/null
+++ b/freebsd/contrib/wpa/src/crypto/md5.h
@@ -0,0 +1,19 @@
+/*
+ * MD5 hash implementation and interface functions
+ * Copyright (c) 2003-2009, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef MD5_H
+#define MD5_H
+
+#define MD5_MAC_LEN 16
+
+int hmac_md5_vector(const u8 *key, size_t key_len, size_t num_elem,
+ const u8 *addr[], const size_t *len, u8 *mac);
+int hmac_md5(const u8 *key, size_t key_len, const u8 *data, size_t data_len,
+ u8 *mac);
+
+#endif /* MD5_H */
diff --git a/freebsd/contrib/wpa/src/crypto/ms_funcs.c b/freebsd/contrib/wpa/src/crypto/ms_funcs.c
new file mode 100644
index 00000000..dda2ba7c
--- /dev/null
+++ b/freebsd/contrib/wpa/src/crypto/ms_funcs.c
@@ -0,0 +1,526 @@
+#include <machine/rtems-bsd-user-space.h>
+
+/*
+ * WPA Supplicant / shared MSCHAPV2 helper functions / RFC 2433 / RFC 2759
+ * Copyright (c) 2004-2012, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "sha1.h"
+#include "ms_funcs.h"
+#include "crypto.h"
+
+/**
+ * utf8_to_ucs2 - Convert UTF-8 string to UCS-2 encoding
+ * @utf8_string: UTF-8 string (IN)
+ * @utf8_string_len: Length of utf8_string (IN)
+ * @ucs2_buffer: UCS-2 buffer (OUT)
+ * @ucs2_buffer_size: Length of UCS-2 buffer (IN)
+ * @ucs2_string_size: Number of 2-byte words in the resulting UCS-2 string
+ * Returns: 0 on success, -1 on failure
+ */
+static int utf8_to_ucs2(const u8 *utf8_string, size_t utf8_string_len,
+ u8 *ucs2_buffer, size_t ucs2_buffer_size,
+ size_t *ucs2_string_size)
+{
+ size_t i, j;
+
+ for (i = 0, j = 0; i < utf8_string_len; i++) {
+ u8 c = utf8_string[i];
+ if (j >= ucs2_buffer_size) {
+ /* input too long */
+ return -1;
+ }
+ if (c <= 0x7F) {
+ WPA_PUT_LE16(ucs2_buffer + j, c);
+ j += 2;
+ } else if (i == utf8_string_len - 1 ||
+ j >= ucs2_buffer_size - 1) {
+ /* incomplete surrogate */
+ return -1;
+ } else {
+ u8 c2 = utf8_string[++i];
+ if ((c & 0xE0) == 0xC0) {
+ /* two-byte encoding */
+ WPA_PUT_LE16(ucs2_buffer + j,
+ ((c & 0x1F) << 6) | (c2 & 0x3F));
+ j += 2;
+ } else if (i == utf8_string_len ||
+ j >= ucs2_buffer_size - 1) {
+ /* incomplete surrogate */
+ return -1;
+ } else {
+ /* three-byte encoding */
+ u8 c3 = utf8_string[++i];
+ WPA_PUT_LE16(ucs2_buffer + j,
+ ((c & 0xF) << 12) |
+ ((c2 & 0x3F) << 6) | (c3 & 0x3F));
+ j += 2;
+ }
+ }
+ }
+
+ if (ucs2_string_size)
+ *ucs2_string_size = j / 2;
+ return 0;
+}
+
+
+/**
+ * challenge_hash - ChallengeHash() - RFC 2759, Sect. 8.2
+ * @peer_challenge: 16-octet PeerChallenge (IN)
+ * @auth_challenge: 16-octet AuthenticatorChallenge (IN)
+ * @username: 0-to-256-char UserName (IN)
+ * @username_len: Length of username
+ * @challenge: 8-octet Challenge (OUT)
+ * Returns: 0 on success, -1 on failure
+ */
+int challenge_hash(const u8 *peer_challenge, const u8 *auth_challenge,
+ const u8 *username, size_t username_len, u8 *challenge)
+{
+ u8 hash[SHA1_MAC_LEN];
+ const unsigned char *addr[3];
+ size_t len[3];
+
+ addr[0] = peer_challenge;
+ len[0] = 16;
+ addr[1] = auth_challenge;
+ len[1] = 16;
+ addr[2] = username;
+ len[2] = username_len;
+
+ if (sha1_vector(3, addr, len, hash))
+ return -1;
+ os_memcpy(challenge, hash, 8);
+ return 0;
+}
+
+
+/**
+ * nt_password_hash - NtPasswordHash() - RFC 2759, Sect. 8.3
+ * @password: 0-to-256-unicode-char Password (IN; UTF-8)
+ * @password_len: Length of password
+ * @password_hash: 16-octet PasswordHash (OUT)
+ * Returns: 0 on success, -1 on failure
+ */
+int nt_password_hash(const u8 *password, size_t password_len,
+ u8 *password_hash)
+{
+ u8 buf[512], *pos;
+ size_t len, max_len;
+
+ max_len = sizeof(buf);
+ if (utf8_to_ucs2(password, password_len, buf, max_len, &len) < 0)
+ return -1;
+
+ len *= 2;
+ pos = buf;
+ return md4_vector(1, (const u8 **) &pos, &len, password_hash);
+}
+
+
+/**
+ * hash_nt_password_hash - HashNtPasswordHash() - RFC 2759, Sect. 8.4
+ * @password_hash: 16-octet PasswordHash (IN)
+ * @password_hash_hash: 16-octet PasswordHashHash (OUT)
+ * Returns: 0 on success, -1 on failure
+ */
+int hash_nt_password_hash(const u8 *password_hash, u8 *password_hash_hash)
+{
+ size_t len = 16;
+ return md4_vector(1, &password_hash, &len, password_hash_hash);
+}
+
+
+/**
+ * challenge_response - ChallengeResponse() - RFC 2759, Sect. 8.5
+ * @challenge: 8-octet Challenge (IN)
+ * @password_hash: 16-octet PasswordHash (IN)
+ * @response: 24-octet Response (OUT)
+ */
+void challenge_response(const u8 *challenge, const u8 *password_hash,
+ u8 *response)
+{
+ u8 zpwd[7];
+ des_encrypt(challenge, password_hash, response);
+ des_encrypt(challenge, password_hash + 7, response + 8);
+ zpwd[0] = password_hash[14];
+ zpwd[1] = password_hash[15];
+ os_memset(zpwd + 2, 0, 5);
+ des_encrypt(challenge, zpwd, response + 16);
+}
+
+
+/**
+ * generate_nt_response - GenerateNTResponse() - RFC 2759, Sect. 8.1
+ * @auth_challenge: 16-octet AuthenticatorChallenge (IN)
+ * @peer_challenge: 16-octet PeerChallenge (IN)
+ * @username: 0-to-256-char UserName (IN)
+ * @username_len: Length of username
+ * @password: 0-to-256-unicode-char Password (IN; UTF-8)
+ * @password_len: Length of password
+ * @response: 24-octet Response (OUT)
+ * Returns: 0 on success, -1 on failure
+ */
+int generate_nt_response(const u8 *auth_challenge, const u8 *peer_challenge,
+ const u8 *username, size_t username_len,
+ const u8 *password, size_t password_len,
+ u8 *response)
+{
+ u8 challenge[8];
+ u8 password_hash[16];
+
+ if (challenge_hash(peer_challenge, auth_challenge, username,
+ username_len, challenge) ||
+ nt_password_hash(password, password_len, password_hash))
+ return -1;
+ challenge_response(challenge, password_hash, response);
+ return 0;
+}
+
+
+/**
+ * generate_nt_response_pwhash - GenerateNTResponse() - RFC 2759, Sect. 8.1
+ * @auth_challenge: 16-octet AuthenticatorChallenge (IN)
+ * @peer_challenge: 16-octet PeerChallenge (IN)
+ * @username: 0-to-256-char UserName (IN)
+ * @username_len: Length of username
+ * @password_hash: 16-octet PasswordHash (IN)
+ * @response: 24-octet Response (OUT)
+ * Returns: 0 on success, -1 on failure
+ */
+int generate_nt_response_pwhash(const u8 *auth_challenge,
+ const u8 *peer_challenge,
+ const u8 *username, size_t username_len,
+ const u8 *password_hash,
+ u8 *response)
+{
+ u8 challenge[8];
+
+ if (challenge_hash(peer_challenge, auth_challenge,
+ username, username_len,
+ challenge))
+ return -1;
+ challenge_response(challenge, password_hash, response);
+ return 0;
+}
+
+
+/**
+ * generate_authenticator_response_pwhash - GenerateAuthenticatorResponse() - RFC 2759, Sect. 8.7
+ * @password_hash: 16-octet PasswordHash (IN)
+ * @nt_response: 24-octet NT-Response (IN)
+ * @peer_challenge: 16-octet PeerChallenge (IN)
+ * @auth_challenge: 16-octet AuthenticatorChallenge (IN)
+ * @username: 0-to-256-char UserName (IN)
+ * @username_len: Length of username
+ * @response: 20-octet AuthenticatorResponse (OUT) (note: this value is usually
+ * encoded as a 42-octet ASCII string (S=hexdump_of_response)
+ * Returns: 0 on success, -1 on failure
+ */
+int generate_authenticator_response_pwhash(
+ const u8 *password_hash,
+ const u8 *peer_challenge, const u8 *auth_challenge,
+ const u8 *username, size_t username_len,
+ const u8 *nt_response, u8 *response)
+{
+ static const u8 magic1[39] = {
+ 0x4D, 0x61, 0x67, 0x69, 0x63, 0x20, 0x73, 0x65, 0x72, 0x76,
+ 0x65, 0x72, 0x20, 0x74, 0x6F, 0x20, 0x63, 0x6C, 0x69, 0x65,
+ 0x6E, 0x74, 0x20, 0x73, 0x69, 0x67, 0x6E, 0x69, 0x6E, 0x67,
+ 0x20, 0x63, 0x6F, 0x6E, 0x73, 0x74, 0x61, 0x6E, 0x74
+ };
+ static const u8 magic2[41] = {
+ 0x50, 0x61, 0x64, 0x20, 0x74, 0x6F, 0x20, 0x6D, 0x61, 0x6B,
+ 0x65, 0x20, 0x69, 0x74, 0x20, 0x64, 0x6F, 0x20, 0x6D, 0x6F,
+ 0x72, 0x65, 0x20, 0x74, 0x68, 0x61, 0x6E, 0x20, 0x6F, 0x6E,
+ 0x65, 0x20, 0x69, 0x74, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6F,
+ 0x6E
+ };
+
+ u8 password_hash_hash[16], challenge[8];
+ const unsigned char *addr1[3];
+ const size_t len1[3] = { 16, 24, sizeof(magic1) };
+ const unsigned char *addr2[3];
+ const size_t len2[3] = { SHA1_MAC_LEN, 8, sizeof(magic2) };
+
+ addr1[0] = password_hash_hash;
+ addr1[1] = nt_response;
+ addr1[2] = magic1;
+
+ addr2[0] = response;
+ addr2[1] = challenge;
+ addr2[2] = magic2;
+
+ if (hash_nt_password_hash(password_hash, password_hash_hash) ||
+ sha1_vector(3, addr1, len1, response) ||
+ challenge_hash(peer_challenge, auth_challenge, username,
+ username_len, challenge))
+ return -1;
+ return sha1_vector(3, addr2, len2, response);
+}
+
+
+/**
+ * generate_authenticator_response - GenerateAuthenticatorResponse() - RFC 2759, Sect. 8.7
+ * @password: 0-to-256-unicode-char Password (IN; UTF-8)
+ * @password_len: Length of password
+ * @nt_response: 24-octet NT-Response (IN)
+ * @peer_challenge: 16-octet PeerChallenge (IN)
+ * @auth_challenge: 16-octet AuthenticatorChallenge (IN)
+ * @username: 0-to-256-char UserName (IN)
+ * @username_len: Length of username
+ * @response: 20-octet AuthenticatorResponse (OUT) (note: this value is usually
+ * encoded as a 42-octet ASCII string (S=hexdump_of_response)
+ * Returns: 0 on success, -1 on failure
+ */
+int generate_authenticator_response(const u8 *password, size_t password_len,
+ const u8 *peer_challenge,
+ const u8 *auth_challenge,
+ const u8 *username, size_t username_len,
+ const u8 *nt_response, u8 *response)
+{
+ u8 password_hash[16];
+ if (nt_password_hash(password, password_len, password_hash))
+ return -1;
+ return generate_authenticator_response_pwhash(
+ password_hash, peer_challenge, auth_challenge,
+ username, username_len, nt_response, response);
+}
+
+
+/**
+ * nt_challenge_response - NtChallengeResponse() - RFC 2433, Sect. A.5
+ * @challenge: 8-octet Challenge (IN)
+ * @password: 0-to-256-unicode-char Password (IN; UTF-8)
+ * @password_len: Length of password
+ * @response: 24-octet Response (OUT)
+ * Returns: 0 on success, -1 on failure
+ */
+int nt_challenge_response(const u8 *challenge, const u8 *password,
+ size_t password_len, u8 *response)
+{
+ u8 password_hash[16];
+ if (nt_password_hash(password, password_len, password_hash))
+ return -1;
+ challenge_response(challenge, password_hash, response);
+ return 0;
+}
+
+
+/**
+ * get_master_key - GetMasterKey() - RFC 3079, Sect. 3.4
+ * @password_hash_hash: 16-octet PasswordHashHash (IN)
+ * @nt_response: 24-octet NTResponse (IN)
+ * @master_key: 16-octet MasterKey (OUT)
+ * Returns: 0 on success, -1 on failure
+ */
+int get_master_key(const u8 *password_hash_hash, const u8 *nt_response,
+ u8 *master_key)
+{
+ static const u8 magic1[27] = {
+ 0x54, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x74,
+ 0x68, 0x65, 0x20, 0x4d, 0x50, 0x50, 0x45, 0x20, 0x4d,
+ 0x61, 0x73, 0x74, 0x65, 0x72, 0x20, 0x4b, 0x65, 0x79
+ };
+ const unsigned char *addr[3];
+ const size_t len[3] = { 16, 24, sizeof(magic1) };
+ u8 hash[SHA1_MAC_LEN];
+
+ addr[0] = password_hash_hash;
+ addr[1] = nt_response;
+ addr[2] = magic1;
+
+ if (sha1_vector(3, addr, len, hash))
+ return -1;
+ os_memcpy(master_key, hash, 16);
+ return 0;
+}
+
+
+/**
+ * get_asymetric_start_key - GetAsymetricStartKey() - RFC 3079, Sect. 3.4
+ * @master_key: 16-octet MasterKey (IN)
+ * @session_key: 8-to-16 octet SessionKey (OUT)
+ * @session_key_len: SessionKeyLength (Length of session_key) (IN)
+ * @is_send: IsSend (IN, BOOLEAN)
+ * @is_server: IsServer (IN, BOOLEAN)
+ * Returns: 0 on success, -1 on failure
+ */
+int get_asymetric_start_key(const u8 *master_key, u8 *session_key,
+ size_t session_key_len, int is_send,
+ int is_server)
+{
+ static const u8 magic2[84] = {
+ 0x4f, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6c, 0x69,
+ 0x65, 0x6e, 0x74, 0x20, 0x73, 0x69, 0x64, 0x65, 0x2c, 0x20,
+ 0x74, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x74, 0x68,
+ 0x65, 0x20, 0x73, 0x65, 0x6e, 0x64, 0x20, 0x6b, 0x65, 0x79,
+ 0x3b, 0x20, 0x6f, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x73,
+ 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x73, 0x69, 0x64, 0x65,
+ 0x2c, 0x20, 0x69, 0x74, 0x20, 0x69, 0x73, 0x20, 0x74, 0x68,
+ 0x65, 0x20, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x20,
+ 0x6b, 0x65, 0x79, 0x2e
+ };
+ static const u8 magic3[84] = {
+ 0x4f, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6c, 0x69,
+ 0x65, 0x6e, 0x74, 0x20, 0x73, 0x69, 0x64, 0x65, 0x2c, 0x20,
+ 0x74, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x74, 0x68,
+ 0x65, 0x20, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x20,
+ 0x6b, 0x65, 0x79, 0x3b, 0x20, 0x6f, 0x6e, 0x20, 0x74, 0x68,
+ 0x65, 0x20, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x73,
+ 0x69, 0x64, 0x65, 0x2c, 0x20, 0x69, 0x74, 0x20, 0x69, 0x73,
+ 0x20, 0x74, 0x68, 0x65, 0x20, 0x73, 0x65, 0x6e, 0x64, 0x20,
+ 0x6b, 0x65, 0x79, 0x2e
+ };
+ static const u8 shs_pad1[40] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+ };
+
+ static const u8 shs_pad2[40] = {
+ 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2,
+ 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2,
+ 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2,
+ 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2
+ };
+ u8 digest[SHA1_MAC_LEN];
+ const unsigned char *addr[4];
+ const size_t len[4] = { 16, 40, 84, 40 };
+
+ addr[0] = master_key;
+ addr[1] = shs_pad1;
+ if (is_send) {
+ addr[2] = is_server ? magic3 : magic2;
+ } else {
+ addr[2] = is_server ? magic2 : magic3;
+ }
+ addr[3] = shs_pad2;
+
+ if (sha1_vector(4, addr, len, digest))
+ return -1;
+
+ if (session_key_len > SHA1_MAC_LEN)
+ session_key_len = SHA1_MAC_LEN;
+ os_memcpy(session_key, digest, session_key_len);
+ return 0;
+}
+
+
+#ifndef CONFIG_NO_RC4
+
+#define PWBLOCK_LEN 516
+
+/**
+ * encrypt_pw_block_with_password_hash - EncryptPwBlockWithPasswordHash() - RFC 2759, Sect. 8.10
+ * @password: 0-to-256-unicode-char Password (IN; UTF-8)
+ * @password_len: Length of password
+ * @password_hash: 16-octet PasswordHash (IN)
+ * @pw_block: 516-byte PwBlock (OUT)
+ * Returns: 0 on success, -1 on failure
+ */
+int encrypt_pw_block_with_password_hash(
+ const u8 *password, size_t password_len,
+ const u8 *password_hash, u8 *pw_block)
+{
+ size_t ucs2_len, offset;
+ u8 *pos;
+
+ os_memset(pw_block, 0, PWBLOCK_LEN);
+
+ if (utf8_to_ucs2(password, password_len, pw_block, 512, &ucs2_len) < 0
+ || ucs2_len > 256)
+ return -1;
+
+ offset = (256 - ucs2_len) * 2;
+ if (offset != 0) {
+ os_memmove(pw_block + offset, pw_block, ucs2_len * 2);
+ if (os_get_random(pw_block, offset) < 0)
+ return -1;
+ }
+ /*
+ * PasswordLength is 4 octets, but since the maximum password length is
+ * 256, only first two (in little endian byte order) can be non-zero.
+ */
+ pos = &pw_block[2 * 256];
+ WPA_PUT_LE16(pos, password_len * 2);
+ rc4_skip(password_hash, 16, 0, pw_block, PWBLOCK_LEN);
+ return 0;
+}
+
+
+/**
+ * new_password_encrypted_with_old_nt_password_hash - NewPasswordEncryptedWithOldNtPasswordHash() - RFC 2759, Sect. 8.9
+ * @new_password: 0-to-256-unicode-char NewPassword (IN; UTF-8)
+ * @new_password_len: Length of new_password
+ * @old_password: 0-to-256-unicode-char OldPassword (IN; UTF-8)
+ * @old_password_len: Length of old_password
+ * @encrypted_pw_block: 516-octet EncryptedPwBlock (OUT)
+ * Returns: 0 on success, -1 on failure
+ */
+int new_password_encrypted_with_old_nt_password_hash(
+ const u8 *new_password, size_t new_password_len,
+ const u8 *old_password, size_t old_password_len,
+ u8 *encrypted_pw_block)
+{
+ u8 password_hash[16];
+
+ if (nt_password_hash(old_password, old_password_len, password_hash))
+ return -1;
+ if (encrypt_pw_block_with_password_hash(new_password, new_password_len,
+ password_hash,
+ encrypted_pw_block))
+ return -1;
+ return 0;
+}
+
+#endif /* CONFIG_NO_RC4 */
+
+
+/**
+ * nt_password_hash_encrypted_with_block - NtPasswordHashEncryptedWithBlock() - RFC 2759, Sect 8.13
+ * @password_hash: 16-octer PasswordHash (IN)
+ * @block: 16-octet Block (IN)
+ * @cypher: 16-octer Cypher (OUT)
+ */
+void nt_password_hash_encrypted_with_block(const u8 *password_hash,
+ const u8 *block, u8 *cypher)
+{
+ des_encrypt(password_hash, block, cypher);
+ des_encrypt(password_hash + 8, block + 7, cypher + 8);
+}
+
+
+/**
+ * old_nt_password_hash_encrypted_with_new_nt_password_hash - OldNtPasswordHashEncryptedWithNewNtPasswordHash() - RFC 2759, Sect. 8.12
+ * @new_password: 0-to-256-unicode-char NewPassword (IN; UTF-8)
+ * @new_password_len: Length of new_password
+ * @old_password: 0-to-256-unicode-char OldPassword (IN; UTF-8)
+ * @old_password_len: Length of old_password
+ * @encrypted_password_hash: 16-octet EncryptedPasswordHash (OUT)
+ * Returns: 0 on success, -1 on failure
+ */
+int old_nt_password_hash_encrypted_with_new_nt_password_hash(
+ const u8 *new_password, size_t new_password_len,
+ const u8 *old_password, size_t old_password_len,
+ u8 *encrypted_password_hash)
+{
+ u8 old_password_hash[16], new_password_hash[16];
+
+ if (nt_password_hash(old_password, old_password_len,
+ old_password_hash) ||
+ nt_password_hash(new_password, new_password_len,
+ new_password_hash))
+ return -1;
+ nt_password_hash_encrypted_with_block(old_password_hash,
+ new_password_hash,
+ encrypted_password_hash);
+ return 0;
+}
diff --git a/freebsd/contrib/wpa/src/crypto/ms_funcs.h b/freebsd/contrib/wpa/src/crypto/ms_funcs.h
new file mode 100644
index 00000000..b5b5918e
--- /dev/null
+++ b/freebsd/contrib/wpa/src/crypto/ms_funcs.h
@@ -0,0 +1,60 @@
+/*
+ * WPA Supplicant / shared MSCHAPV2 helper functions / RFC 2433 / RFC 2759
+ * Copyright (c) 2004-2009, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef MS_FUNCS_H
+#define MS_FUNCS_H
+
+int generate_nt_response(const u8 *auth_challenge, const u8 *peer_challenge,
+ const u8 *username, size_t username_len,
+ const u8 *password, size_t password_len,
+ u8 *response);
+int generate_nt_response_pwhash(const u8 *auth_challenge,
+ const u8 *peer_challenge,
+ const u8 *username, size_t username_len,
+ const u8 *password_hash,
+ u8 *response);
+int generate_authenticator_response(const u8 *password, size_t password_len,
+ const u8 *peer_challenge,
+ const u8 *auth_challenge,
+ const u8 *username, size_t username_len,
+ const u8 *nt_response, u8 *response);
+int generate_authenticator_response_pwhash(
+ const u8 *password_hash,
+ const u8 *peer_challenge, const u8 *auth_challenge,
+ const u8 *username, size_t username_len,
+ const u8 *nt_response, u8 *response);
+int nt_challenge_response(const u8 *challenge, const u8 *password,
+ size_t password_len, u8 *response);
+
+void challenge_response(const u8 *challenge, const u8 *password_hash,
+ u8 *response);
+int challenge_hash(const u8 *peer_challenge, const u8 *auth_challenge,
+ const u8 *username, size_t username_len, u8 *challenge);
+int nt_password_hash(const u8 *password, size_t password_len,
+ u8 *password_hash);
+int hash_nt_password_hash(const u8 *password_hash, u8 *password_hash_hash);
+int get_master_key(const u8 *password_hash_hash, const u8 *nt_response,
+ u8 *master_key);
+int get_asymetric_start_key(const u8 *master_key, u8 *session_key,
+ size_t session_key_len, int is_send,
+ int is_server);
+int __must_check encrypt_pw_block_with_password_hash(
+ const u8 *password, size_t password_len,
+ const u8 *password_hash, u8 *pw_block);
+int __must_check new_password_encrypted_with_old_nt_password_hash(
+ const u8 *new_password, size_t new_password_len,
+ const u8 *old_password, size_t old_password_len,
+ u8 *encrypted_pw_block);
+void nt_password_hash_encrypted_with_block(const u8 *password_hash,
+ const u8 *block, u8 *cypher);
+int old_nt_password_hash_encrypted_with_new_nt_password_hash(
+ const u8 *new_password, size_t new_password_len,
+ const u8 *old_password, size_t old_password_len,
+ u8 *encrypted_password_hash);
+
+#endif /* MS_FUNCS_H */
diff --git a/freebsd/contrib/wpa/src/crypto/random.c b/freebsd/contrib/wpa/src/crypto/random.c
new file mode 100644
index 00000000..57880e31
--- /dev/null
+++ b/freebsd/contrib/wpa/src/crypto/random.c
@@ -0,0 +1,441 @@
+#include <machine/rtems-bsd-user-space.h>
+
+/*
+ * Random number generator
+ * Copyright (c) 2010-2011, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ *
+ * This random number generator is used to provide additional entropy to the
+ * one provided by the operating system (os_get_random()) for session key
+ * generation. The os_get_random() output is expected to be secure and the
+ * implementation here is expected to provide only limited protection against
+ * cases where os_get_random() cannot provide strong randomness. This
+ * implementation shall not be assumed to be secure as the sole source of
+ * randomness. The random_get_bytes() function mixes in randomness from
+ * os_get_random() and as such, calls to os_get_random() can be replaced with
+ * calls to random_get_bytes() without reducing security.
+ *
+ * The design here follows partially the design used in the Linux
+ * drivers/char/random.c, but the implementation here is simpler and not as
+ * strong. This is a compromise to reduce duplicated CPU effort and to avoid
+ * extra code/memory size. As pointed out above, os_get_random() needs to be
+ * guaranteed to be secure for any of the security assumptions to hold.
+ */
+
+#include "utils/includes.h"
+#ifdef __linux__
+#include <fcntl.h>
+#endif /* __linux__ */
+
+#include "utils/common.h"
+#include "utils/eloop.h"
+#include "crypto/crypto.h"
+#include "sha1.h"
+#include "random.h"
+
+#define POOL_WORDS 32
+#define POOL_WORDS_MASK (POOL_WORDS - 1)
+#define POOL_TAP1 26
+#define POOL_TAP2 20
+#define POOL_TAP3 14
+#define POOL_TAP4 7
+#define POOL_TAP5 1
+#define EXTRACT_LEN 16
+#define MIN_READY_MARK 2
+
+static u32 pool[POOL_WORDS];
+static unsigned int input_rotate = 0;
+static unsigned int pool_pos = 0;
+static u8 dummy_key[20];
+#ifdef __linux__
+static size_t dummy_key_avail = 0;
+static int random_fd = -1;
+#endif /* __linux__ */
+static unsigned int own_pool_ready = 0;
+#define RANDOM_ENTROPY_SIZE 20
+static char *random_entropy_file = NULL;
+static int random_entropy_file_read = 0;
+
+#define MIN_COLLECT_ENTROPY 1000
+static unsigned int entropy = 0;
+static unsigned int total_collected = 0;
+
+
+static void random_write_entropy(void);
+
+
+static u32 __ROL32(u32 x, u32 y)
+{
+ return (x << (y & 31)) | (x >> (32 - (y & 31)));
+}
+
+
+static void random_mix_pool(const void *buf, size_t len)
+{
+ static const u32 twist[8] = {
+ 0x00000000, 0x3b6e20c8, 0x76dc4190, 0x4db26158,
+ 0xedb88320, 0xd6d6a3e8, 0x9b64c2b0, 0xa00ae278
+ };
+ const u8 *pos = buf;
+ u32 w;
+
+ wpa_hexdump_key(MSG_EXCESSIVE, "random_mix_pool", buf, len);
+
+ while (len--) {
+ w = __ROL32(*pos++, input_rotate & 31);
+ input_rotate += pool_pos ? 7 : 14;
+ pool_pos = (pool_pos - 1) & POOL_WORDS_MASK;
+ w ^= pool[pool_pos];
+ w ^= pool[(pool_pos + POOL_TAP1) & POOL_WORDS_MASK];
+ w ^= pool[(pool_pos + POOL_TAP2) & POOL_WORDS_MASK];
+ w ^= pool[(pool_pos + POOL_TAP3) & POOL_WORDS_MASK];
+ w ^= pool[(pool_pos + POOL_TAP4) & POOL_WORDS_MASK];
+ w ^= pool[(pool_pos + POOL_TAP5) & POOL_WORDS_MASK];
+ pool[pool_pos] = (w >> 3) ^ twist[w & 7];
+ }
+}
+
+
+static void random_extract(u8 *out)
+{
+ unsigned int i;
+ u8 hash[SHA1_MAC_LEN];
+ u32 *hash_ptr;
+ u32 buf[POOL_WORDS / 2];
+
+ /* First, add hash back to pool to make backtracking more difficult. */
+ hmac_sha1(dummy_key, sizeof(dummy_key), (const u8 *) pool,
+ sizeof(pool), hash);
+ random_mix_pool(hash, sizeof(hash));
+ /* Hash half the pool to extra data */
+ for (i = 0; i < POOL_WORDS / 2; i++)
+ buf[i] = pool[(pool_pos - i) & POOL_WORDS_MASK];
+ hmac_sha1(dummy_key, sizeof(dummy_key), (const u8 *) buf,
+ sizeof(buf), hash);
+ /*
+ * Fold the hash to further reduce any potential output pattern.
+ * Though, compromise this to reduce CPU use for the most common output
+ * length (32) and return 16 bytes from instead of only half.
+ */
+ hash_ptr = (u32 *) hash;
+ hash_ptr[0] ^= hash_ptr[4];
+ os_memcpy(out, hash, EXTRACT_LEN);
+}
+
+
+void random_add_randomness(const void *buf, size_t len)
+{
+ struct os_time t;
+ static unsigned int count = 0;
+
+ count++;
+ if (entropy > MIN_COLLECT_ENTROPY && (count & 0x3ff) != 0) {
+ /*
+ * No need to add more entropy at this point, so save CPU and
+ * skip the update.
+ */
+ return;
+ }
+ wpa_printf(MSG_EXCESSIVE, "Add randomness: count=%u entropy=%u",
+ count, entropy);
+
+ os_get_time(&t);
+ wpa_hexdump_key(MSG_EXCESSIVE, "random pool",
+ (const u8 *) pool, sizeof(pool));
+ random_mix_pool(&t, sizeof(t));
+ random_mix_pool(buf, len);
+ wpa_hexdump_key(MSG_EXCESSIVE, "random pool",
+ (const u8 *) pool, sizeof(pool));
+ entropy++;
+ total_collected++;
+}
+
+
+int random_get_bytes(void *buf, size_t len)
+{
+ int ret;
+ u8 *bytes = buf;
+ size_t left;
+
+ wpa_printf(MSG_MSGDUMP, "Get randomness: len=%u entropy=%u",
+ (unsigned int) len, entropy);
+
+ /* Start with assumed strong randomness from OS */
+ ret = os_get_random(buf, len);
+ wpa_hexdump_key(MSG_EXCESSIVE, "random from os_get_random",
+ buf, len);
+
+ /* Mix in additional entropy extracted from the internal pool */
+ left = len;
+ while (left) {
+ size_t siz, i;
+ u8 tmp[EXTRACT_LEN];
+ random_extract(tmp);
+ wpa_hexdump_key(MSG_EXCESSIVE, "random from internal pool",
+ tmp, sizeof(tmp));
+ siz = left > EXTRACT_LEN ? EXTRACT_LEN : left;
+ for (i = 0; i < siz; i++)
+ *bytes++ ^= tmp[i];
+ left -= siz;
+ }
+
+#ifdef CONFIG_FIPS
+ /* Mix in additional entropy from the crypto module */
+ bytes = buf;
+ left = len;
+ while (left) {
+ size_t siz, i;
+ u8 tmp[EXTRACT_LEN];
+ if (crypto_get_random(tmp, sizeof(tmp)) < 0) {
+ wpa_printf(MSG_ERROR, "random: No entropy available "
+ "for generating strong random bytes");
+ return -1;
+ }
+ wpa_hexdump_key(MSG_EXCESSIVE, "random from crypto module",
+ tmp, sizeof(tmp));
+ siz = left > EXTRACT_LEN ? EXTRACT_LEN : left;
+ for (i = 0; i < siz; i++)
+ *bytes++ ^= tmp[i];
+ left -= siz;
+ }
+#endif /* CONFIG_FIPS */
+
+ wpa_hexdump_key(MSG_EXCESSIVE, "mixed random", buf, len);
+
+ if (entropy < len)
+ entropy = 0;
+ else
+ entropy -= len;
+
+ return ret;
+}
+
+
+int random_pool_ready(void)
+{
+#ifdef __linux__
+ int fd;
+ ssize_t res;
+
+ /*
+ * Make sure that there is reasonable entropy available before allowing
+ * some key derivation operations to proceed.
+ */
+
+ if (dummy_key_avail == sizeof(dummy_key))
+ return 1; /* Already initialized - good to continue */
+
+ /*
+ * Try to fetch some more data from the kernel high quality
+ * /dev/random. There may not be enough data available at this point,
+ * so use non-blocking read to avoid blocking the application
+ * completely.
+ */
+ fd = open("/dev/random", O_RDONLY | O_NONBLOCK);
+ if (fd < 0) {
+ wpa_printf(MSG_ERROR, "random: Cannot open /dev/random: %s",
+ strerror(errno));
+ return -1;
+ }
+
+ res = read(fd, dummy_key + dummy_key_avail,
+ sizeof(dummy_key) - dummy_key_avail);
+ if (res < 0) {
+ wpa_printf(MSG_ERROR, "random: Cannot read from /dev/random: "
+ "%s", strerror(errno));
+ res = 0;
+ }
+ wpa_printf(MSG_DEBUG, "random: Got %u/%u bytes from "
+ "/dev/random", (unsigned) res,
+ (unsigned) (sizeof(dummy_key) - dummy_key_avail));
+ dummy_key_avail += res;
+ close(fd);
+
+ if (dummy_key_avail == sizeof(dummy_key)) {
+ if (own_pool_ready < MIN_READY_MARK)
+ own_pool_ready = MIN_READY_MARK;
+ random_write_entropy();
+ return 1;
+ }
+
+ wpa_printf(MSG_INFO, "random: Only %u/%u bytes of strong "
+ "random data available from /dev/random",
+ (unsigned) dummy_key_avail, (unsigned) sizeof(dummy_key));
+
+ if (own_pool_ready >= MIN_READY_MARK ||
+ total_collected + 10 * own_pool_ready > MIN_COLLECT_ENTROPY) {
+ wpa_printf(MSG_INFO, "random: Allow operation to proceed "
+ "based on internal entropy");
+ return 1;
+ }
+
+ wpa_printf(MSG_INFO, "random: Not enough entropy pool available for "
+ "secure operations");
+ return 0;
+#else /* __linux__ */
+ /* TODO: could do similar checks on non-Linux platforms */
+ return 1;
+#endif /* __linux__ */
+}
+
+
+void random_mark_pool_ready(void)
+{
+ own_pool_ready++;
+ wpa_printf(MSG_DEBUG, "random: Mark internal entropy pool to be "
+ "ready (count=%u/%u)", own_pool_ready, MIN_READY_MARK);
+ random_write_entropy();
+}
+
+
+#ifdef __linux__
+
+static void random_close_fd(void)
+{
+ if (random_fd >= 0) {
+ eloop_unregister_read_sock(random_fd);
+ close(random_fd);
+ random_fd = -1;
+ }
+}
+
+
+static void random_read_fd(int sock, void *eloop_ctx, void *sock_ctx)
+{
+ ssize_t res;
+
+ if (dummy_key_avail == sizeof(dummy_key)) {
+ random_close_fd();
+ return;
+ }
+
+ res = read(sock, dummy_key + dummy_key_avail,
+ sizeof(dummy_key) - dummy_key_avail);
+ if (res < 0) {
+ wpa_printf(MSG_ERROR, "random: Cannot read from /dev/random: "
+ "%s", strerror(errno));
+ return;
+ }
+
+ wpa_printf(MSG_DEBUG, "random: Got %u/%u bytes from /dev/random",
+ (unsigned) res,
+ (unsigned) (sizeof(dummy_key) - dummy_key_avail));
+ dummy_key_avail += res;
+
+ if (dummy_key_avail == sizeof(dummy_key)) {
+ random_close_fd();
+ if (own_pool_ready < MIN_READY_MARK)
+ own_pool_ready = MIN_READY_MARK;
+ random_write_entropy();
+ }
+}
+
+#endif /* __linux__ */
+
+
+static void random_read_entropy(void)
+{
+ char *buf;
+ size_t len;
+
+ if (!random_entropy_file)
+ return;
+
+ buf = os_readfile(random_entropy_file, &len);
+ if (buf == NULL)
+ return; /* entropy file not yet available */
+
+ if (len != 1 + RANDOM_ENTROPY_SIZE) {
+ wpa_printf(MSG_DEBUG, "random: Invalid entropy file %s",
+ random_entropy_file);
+ os_free(buf);
+ return;
+ }
+
+ own_pool_ready = (u8) buf[0];
+ random_add_randomness(buf + 1, RANDOM_ENTROPY_SIZE);
+ random_entropy_file_read = 1;
+ os_free(buf);
+ wpa_printf(MSG_DEBUG, "random: Added entropy from %s "
+ "(own_pool_ready=%u)",
+ random_entropy_file, own_pool_ready);
+}
+
+
+static void random_write_entropy(void)
+{
+ char buf[RANDOM_ENTROPY_SIZE];
+ FILE *f;
+ u8 opr;
+ int fail = 0;
+
+ if (!random_entropy_file)
+ return;
+
+ if (random_get_bytes(buf, RANDOM_ENTROPY_SIZE) < 0)
+ return;
+
+ f = fopen(random_entropy_file, "wb");
+ if (f == NULL) {
+ wpa_printf(MSG_ERROR, "random: Could not open entropy file %s "
+ "for writing", random_entropy_file);
+ return;
+ }
+
+ opr = own_pool_ready > 0xff ? 0xff : own_pool_ready;
+ if (fwrite(&opr, 1, 1, f) != 1 ||
+ fwrite(buf, RANDOM_ENTROPY_SIZE, 1, f) != 1)
+ fail = 1;
+ fclose(f);
+ if (fail) {
+ wpa_printf(MSG_ERROR, "random: Could not write entropy data "
+ "to %s", random_entropy_file);
+ return;
+ }
+
+ wpa_printf(MSG_DEBUG, "random: Updated entropy file %s "
+ "(own_pool_ready=%u)",
+ random_entropy_file, own_pool_ready);
+}
+
+
+void random_init(const char *entropy_file)
+{
+ os_free(random_entropy_file);
+ if (entropy_file)
+ random_entropy_file = os_strdup(entropy_file);
+ else
+ random_entropy_file = NULL;
+ random_read_entropy();
+
+#ifdef __linux__
+ if (random_fd >= 0)
+ return;
+
+ random_fd = open("/dev/random", O_RDONLY | O_NONBLOCK);
+ if (random_fd < 0) {
+ wpa_printf(MSG_ERROR, "random: Cannot open /dev/random: %s",
+ strerror(errno));
+ return;
+ }
+ wpa_printf(MSG_DEBUG, "random: Trying to read entropy from "
+ "/dev/random");
+
+ eloop_register_read_sock(random_fd, random_read_fd, NULL, NULL);
+#endif /* __linux__ */
+
+ random_write_entropy();
+}
+
+
+void random_deinit(void)
+{
+#ifdef __linux__
+ random_close_fd();
+#endif /* __linux__ */
+ random_write_entropy();
+ os_free(random_entropy_file);
+ random_entropy_file = NULL;
+}
diff --git a/freebsd/contrib/wpa/src/crypto/random.h b/freebsd/contrib/wpa/src/crypto/random.h
new file mode 100644
index 00000000..d13e1c49
--- /dev/null
+++ b/freebsd/contrib/wpa/src/crypto/random.h
@@ -0,0 +1,28 @@
+/*
+ * Random number generator
+ * Copyright (c) 2010-2011, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef RANDOM_H
+#define RANDOM_H
+
+#ifdef CONFIG_NO_RANDOM_POOL
+#define random_init(e) do { } while (0)
+#define random_deinit() do { } while (0)
+#define random_add_randomness(b, l) do { } while (0)
+#define random_get_bytes(b, l) os_get_random((b), (l))
+#define random_pool_ready() 1
+#define random_mark_pool_ready() do { } while (0)
+#else /* CONFIG_NO_RANDOM_POOL */
+void random_init(const char *entropy_file);
+void random_deinit(void);
+void random_add_randomness(const void *buf, size_t len);
+int random_get_bytes(void *buf, size_t len);
+int random_pool_ready(void);
+void random_mark_pool_ready(void);
+#endif /* CONFIG_NO_RANDOM_POOL */
+
+#endif /* RANDOM_H */
diff --git a/freebsd/contrib/wpa/src/crypto/rc4.c b/freebsd/contrib/wpa/src/crypto/rc4.c
new file mode 100644
index 00000000..061160ea
--- /dev/null
+++ b/freebsd/contrib/wpa/src/crypto/rc4.c
@@ -0,0 +1,56 @@
+#include <machine/rtems-bsd-user-space.h>
+
+/*
+ * RC4 stream cipher
+ * Copyright (c) 2002-2005, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "crypto.h"
+
+#define S_SWAP(a,b) do { u8 t = S[a]; S[a] = S[b]; S[b] = t; } while(0)
+
+int rc4_skip(const u8 *key, size_t keylen, size_t skip,
+ u8 *data, size_t data_len)
+{
+ u32 i, j, k;
+ u8 S[256], *pos;
+ size_t kpos;
+
+ /* Setup RC4 state */
+ for (i = 0; i < 256; i++)
+ S[i] = i;
+ j = 0;
+ kpos = 0;
+ for (i = 0; i < 256; i++) {
+ j = (j + S[i] + key[kpos]) & 0xff;
+ kpos++;
+ if (kpos >= keylen)
+ kpos = 0;
+ S_SWAP(i, j);
+ }
+
+ /* Skip the start of the stream */
+ i = j = 0;
+ for (k = 0; k < skip; k++) {
+ i = (i + 1) & 0xff;
+ j = (j + S[i]) & 0xff;
+ S_SWAP(i, j);
+ }
+
+ /* Apply RC4 to data */
+ pos = data;
+ for (k = 0; k < data_len; k++) {
+ i = (i + 1) & 0xff;
+ j = (j + S[i]) & 0xff;
+ S_SWAP(i, j);
+ *pos++ ^= S[(S[i] + S[j]) & 0xff];
+ }
+
+ return 0;
+}
diff --git a/freebsd/contrib/wpa/src/crypto/sha1-pbkdf2.c b/freebsd/contrib/wpa/src/crypto/sha1-pbkdf2.c
new file mode 100644
index 00000000..121b642e
--- /dev/null
+++ b/freebsd/contrib/wpa/src/crypto/sha1-pbkdf2.c
@@ -0,0 +1,94 @@
+#include <machine/rtems-bsd-user-space.h>
+
+/*
+ * SHA1-based key derivation function (PBKDF2) for IEEE 802.11i
+ * Copyright (c) 2003-2005, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "sha1.h"
+
+static int pbkdf2_sha1_f(const char *passphrase, const u8 *ssid,
+ size_t ssid_len, int iterations, unsigned int count,
+ u8 *digest)
+{
+ unsigned char tmp[SHA1_MAC_LEN], tmp2[SHA1_MAC_LEN];
+ int i, j;
+ unsigned char count_buf[4];
+ const u8 *addr[2];
+ size_t len[2];
+ size_t passphrase_len = os_strlen(passphrase);
+
+ addr[0] = ssid;
+ len[0] = ssid_len;
+ addr[1] = count_buf;
+ len[1] = 4;
+
+ /* F(P, S, c, i) = U1 xor U2 xor ... Uc
+ * U1 = PRF(P, S || i)
+ * U2 = PRF(P, U1)
+ * Uc = PRF(P, Uc-1)
+ */
+
+ count_buf[0] = (count >> 24) & 0xff;
+ count_buf[1] = (count >> 16) & 0xff;
+ count_buf[2] = (count >> 8) & 0xff;
+ count_buf[3] = count & 0xff;
+ if (hmac_sha1_vector((u8 *) passphrase, passphrase_len, 2, addr, len,
+ tmp))
+ return -1;
+ os_memcpy(digest, tmp, SHA1_MAC_LEN);
+
+ for (i = 1; i < iterations; i++) {
+ if (hmac_sha1((u8 *) passphrase, passphrase_len, tmp,
+ SHA1_MAC_LEN, tmp2))
+ return -1;
+ os_memcpy(tmp, tmp2, SHA1_MAC_LEN);
+ for (j = 0; j < SHA1_MAC_LEN; j++)
+ digest[j] ^= tmp2[j];
+ }
+
+ return 0;
+}
+
+
+/**
+ * pbkdf2_sha1 - SHA1-based key derivation function (PBKDF2) for IEEE 802.11i
+ * @passphrase: ASCII passphrase
+ * @ssid: SSID
+ * @ssid_len: SSID length in bytes
+ * @iterations: Number of iterations to run
+ * @buf: Buffer for the generated key
+ * @buflen: Length of the buffer in bytes
+ * Returns: 0 on success, -1 of failure
+ *
+ * This function is used to derive PSK for WPA-PSK. For this protocol,
+ * iterations is set to 4096 and buflen to 32. This function is described in
+ * IEEE Std 802.11-2004, Clause H.4. The main construction is from PKCS#5 v2.0.
+ */
+int pbkdf2_sha1(const char *passphrase, const u8 *ssid, size_t ssid_len,
+ int iterations, u8 *buf, size_t buflen)
+{
+ unsigned int count = 0;
+ unsigned char *pos = buf;
+ size_t left = buflen, plen;
+ unsigned char digest[SHA1_MAC_LEN];
+
+ while (left > 0) {
+ count++;
+ if (pbkdf2_sha1_f(passphrase, ssid, ssid_len, iterations,
+ count, digest))
+ return -1;
+ plen = left > SHA1_MAC_LEN ? SHA1_MAC_LEN : left;
+ os_memcpy(pos, digest, plen);
+ pos += plen;
+ left -= plen;
+ }
+
+ return 0;
+}
diff --git a/freebsd/contrib/wpa/src/crypto/sha1-prf.c b/freebsd/contrib/wpa/src/crypto/sha1-prf.c
new file mode 100644
index 00000000..004260d7
--- /dev/null
+++ b/freebsd/contrib/wpa/src/crypto/sha1-prf.c
@@ -0,0 +1,69 @@
+#include <machine/rtems-bsd-user-space.h>
+
+/*
+ * SHA1-based PRF
+ * Copyright (c) 2003-2005, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "sha1.h"
+#include "crypto.h"
+
+
+/**
+ * sha1_prf - SHA1-based Pseudo-Random Function (PRF) (IEEE 802.11i, 8.5.1.1)
+ * @key: Key for PRF
+ * @key_len: Length of the key in bytes
+ * @label: A unique label for each purpose of the PRF
+ * @data: Extra data to bind into the key
+ * @data_len: Length of the data
+ * @buf: Buffer for the generated pseudo-random key
+ * @buf_len: Number of bytes of key to generate
+ * Returns: 0 on success, -1 of failure
+ *
+ * This function is used to derive new, cryptographically separate keys from a
+ * given key (e.g., PMK in IEEE 802.11i).
+ */
+int sha1_prf(const u8 *key, size_t key_len, const char *label,
+ const u8 *data, size_t data_len, u8 *buf, size_t buf_len)
+{
+ u8 counter = 0;
+ size_t pos, plen;
+ u8 hash[SHA1_MAC_LEN];
+ size_t label_len = os_strlen(label) + 1;
+ const unsigned char *addr[3];
+ size_t len[3];
+
+ addr[0] = (u8 *) label;
+ len[0] = label_len;
+ addr[1] = data;
+ len[1] = data_len;
+ addr[2] = &counter;
+ len[2] = 1;
+
+ pos = 0;
+ while (pos < buf_len) {
+ plen = buf_len - pos;
+ if (plen >= SHA1_MAC_LEN) {
+ if (hmac_sha1_vector(key, key_len, 3, addr, len,
+ &buf[pos]))
+ return -1;
+ pos += SHA1_MAC_LEN;
+ } else {
+ if (hmac_sha1_vector(key, key_len, 3, addr, len,
+ hash))
+ return -1;
+ os_memcpy(&buf[pos], hash, plen);
+ break;
+ }
+ counter++;
+ }
+ os_memset(hash, 0, sizeof(hash));
+
+ return 0;
+}
diff --git a/freebsd/contrib/wpa/src/crypto/sha1.c b/freebsd/contrib/wpa/src/crypto/sha1.c
new file mode 100644
index 00000000..de751fb9
--- /dev/null
+++ b/freebsd/contrib/wpa/src/crypto/sha1.c
@@ -0,0 +1,109 @@
+#include <machine/rtems-bsd-user-space.h>
+
+/*
+ * SHA1 hash implementation and interface functions
+ * Copyright (c) 2003-2005, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "sha1.h"
+#include "crypto.h"
+
+
+/**
+ * hmac_sha1_vector - HMAC-SHA1 over data vector (RFC 2104)
+ * @key: Key for HMAC operations
+ * @key_len: Length of the key in bytes
+ * @num_elem: Number of elements in the data vector
+ * @addr: Pointers to the data areas
+ * @len: Lengths of the data blocks
+ * @mac: Buffer for the hash (20 bytes)
+ * Returns: 0 on success, -1 on failure
+ */
+int hmac_sha1_vector(const u8 *key, size_t key_len, size_t num_elem,
+ const u8 *addr[], const size_t *len, u8 *mac)
+{
+ unsigned char k_pad[64]; /* padding - key XORd with ipad/opad */
+ unsigned char tk[20];
+ const u8 *_addr[6];
+ size_t _len[6], i;
+ int ret;
+
+ if (num_elem > 5) {
+ /*
+ * Fixed limit on the number of fragments to avoid having to
+ * allocate memory (which could fail).
+ */
+ return -1;
+ }
+
+ /* if key is longer than 64 bytes reset it to key = SHA1(key) */
+ if (key_len > 64) {
+ if (sha1_vector(1, &key, &key_len, tk))
+ return -1;
+ key = tk;
+ key_len = 20;
+ }
+
+ /* the HMAC_SHA1 transform looks like:
+ *
+ * SHA1(K XOR opad, SHA1(K XOR ipad, text))
+ *
+ * where K is an n byte key
+ * ipad is the byte 0x36 repeated 64 times
+ * opad is the byte 0x5c repeated 64 times
+ * and text is the data being protected */
+
+ /* start out by storing key in ipad */
+ os_memset(k_pad, 0, sizeof(k_pad));
+ os_memcpy(k_pad, key, key_len);
+ /* XOR key with ipad values */
+ for (i = 0; i < 64; i++)
+ k_pad[i] ^= 0x36;
+
+ /* perform inner SHA1 */
+ _addr[0] = k_pad;
+ _len[0] = 64;
+ for (i = 0; i < num_elem; i++) {
+ _addr[i + 1] = addr[i];
+ _len[i + 1] = len[i];
+ }
+ if (sha1_vector(1 + num_elem, _addr, _len, mac))
+ return -1;
+
+ os_memset(k_pad, 0, sizeof(k_pad));
+ os_memcpy(k_pad, key, key_len);
+ /* XOR key with opad values */
+ for (i = 0; i < 64; i++)
+ k_pad[i] ^= 0x5c;
+
+ /* perform outer SHA1 */
+ _addr[0] = k_pad;
+ _len[0] = 64;
+ _addr[1] = mac;
+ _len[1] = SHA1_MAC_LEN;
+ ret = sha1_vector(2, _addr, _len, mac);
+ os_memset(k_pad, 0, sizeof(k_pad));
+ return ret;
+}
+
+
+/**
+ * hmac_sha1 - HMAC-SHA1 over data buffer (RFC 2104)
+ * @key: Key for HMAC operations
+ * @key_len: Length of the key in bytes
+ * @data: Pointers to the data area
+ * @data_len: Length of the data area
+ * @mac: Buffer for the hash (20 bytes)
+ * Returns: 0 on success, -1 of failure
+ */
+int hmac_sha1(const u8 *key, size_t key_len, const u8 *data, size_t data_len,
+ u8 *mac)
+{
+ return hmac_sha1_vector(key, key_len, 1, &data, &data_len, mac);
+}
diff --git a/freebsd/contrib/wpa/src/crypto/sha1.h b/freebsd/contrib/wpa/src/crypto/sha1.h
new file mode 100644
index 00000000..933cd81b
--- /dev/null
+++ b/freebsd/contrib/wpa/src/crypto/sha1.h
@@ -0,0 +1,27 @@
+/*
+ * SHA1 hash implementation and interface functions
+ * Copyright (c) 2003-2009, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef SHA1_H
+#define SHA1_H
+
+#define SHA1_MAC_LEN 20
+
+int hmac_sha1_vector(const u8 *key, size_t key_len, size_t num_elem,
+ const u8 *addr[], const size_t *len, u8 *mac);
+int hmac_sha1(const u8 *key, size_t key_len, const u8 *data, size_t data_len,
+ u8 *mac);
+int sha1_prf(const u8 *key, size_t key_len, const char *label,
+ const u8 *data, size_t data_len, u8 *buf, size_t buf_len);
+int sha1_t_prf(const u8 *key, size_t key_len, const char *label,
+ const u8 *seed, size_t seed_len, u8 *buf, size_t buf_len);
+int __must_check 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);
+int pbkdf2_sha1(const char *passphrase, const u8 *ssid, size_t ssid_len,
+ int iterations, u8 *buf, size_t buflen);
+#endif /* SHA1_H */
diff --git a/freebsd/contrib/wpa/src/crypto/sha256-internal.c b/freebsd/contrib/wpa/src/crypto/sha256-internal.c
new file mode 100644
index 00000000..c2ef3154
--- /dev/null
+++ b/freebsd/contrib/wpa/src/crypto/sha256-internal.c
@@ -0,0 +1,228 @@
+#include <machine/rtems-bsd-user-space.h>
+
+/*
+ * SHA-256 hash implementation and interface functions
+ * Copyright (c) 2003-2011, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "sha256.h"
+#include "sha256_i.h"
+#include "crypto.h"
+
+
+/**
+ * sha256_vector - SHA256 hash for data vector
+ * @num_elem: Number of elements in the data vector
+ * @addr: Pointers to the data areas
+ * @len: Lengths of the data blocks
+ * @mac: Buffer for the hash
+ * Returns: 0 on success, -1 of failure
+ */
+int sha256_vector(size_t num_elem, const u8 *addr[], const size_t *len,
+ u8 *mac)
+{
+ struct sha256_state ctx;
+ size_t i;
+
+ sha256_init(&ctx);
+ for (i = 0; i < num_elem; i++)
+ if (sha256_process(&ctx, addr[i], len[i]))
+ return -1;
+ if (sha256_done(&ctx, mac))
+ return -1;
+ return 0;
+}
+
+
+/* ===== start - public domain SHA256 implementation ===== */
+
+/* This is based on SHA256 implementation in LibTomCrypt that was released into
+ * public domain by Tom St Denis. */
+
+/* the K array */
+static const unsigned long K[64] = {
+ 0x428a2f98UL, 0x71374491UL, 0xb5c0fbcfUL, 0xe9b5dba5UL, 0x3956c25bUL,
+ 0x59f111f1UL, 0x923f82a4UL, 0xab1c5ed5UL, 0xd807aa98UL, 0x12835b01UL,
+ 0x243185beUL, 0x550c7dc3UL, 0x72be5d74UL, 0x80deb1feUL, 0x9bdc06a7UL,
+ 0xc19bf174UL, 0xe49b69c1UL, 0xefbe4786UL, 0x0fc19dc6UL, 0x240ca1ccUL,
+ 0x2de92c6fUL, 0x4a7484aaUL, 0x5cb0a9dcUL, 0x76f988daUL, 0x983e5152UL,
+ 0xa831c66dUL, 0xb00327c8UL, 0xbf597fc7UL, 0xc6e00bf3UL, 0xd5a79147UL,
+ 0x06ca6351UL, 0x14292967UL, 0x27b70a85UL, 0x2e1b2138UL, 0x4d2c6dfcUL,
+ 0x53380d13UL, 0x650a7354UL, 0x766a0abbUL, 0x81c2c92eUL, 0x92722c85UL,
+ 0xa2bfe8a1UL, 0xa81a664bUL, 0xc24b8b70UL, 0xc76c51a3UL, 0xd192e819UL,
+ 0xd6990624UL, 0xf40e3585UL, 0x106aa070UL, 0x19a4c116UL, 0x1e376c08UL,
+ 0x2748774cUL, 0x34b0bcb5UL, 0x391c0cb3UL, 0x4ed8aa4aUL, 0x5b9cca4fUL,
+ 0x682e6ff3UL, 0x748f82eeUL, 0x78a5636fUL, 0x84c87814UL, 0x8cc70208UL,
+ 0x90befffaUL, 0xa4506cebUL, 0xbef9a3f7UL, 0xc67178f2UL
+};
+
+
+/* Various logical functions */
+#define RORc(x, y) \
+( ((((unsigned long) (x) & 0xFFFFFFFFUL) >> (unsigned long) ((y) & 31)) | \
+ ((unsigned long) (x) << (unsigned long) (32 - ((y) & 31)))) & 0xFFFFFFFFUL)
+#define Ch(x,y,z) (z ^ (x & (y ^ z)))
+#define Maj(x,y,z) (((x | y) & z) | (x & y))
+#define S(x, n) RORc((x), (n))
+#define R(x, n) (((x)&0xFFFFFFFFUL)>>(n))
+#define Sigma0(x) (S(x, 2) ^ S(x, 13) ^ S(x, 22))
+#define Sigma1(x) (S(x, 6) ^ S(x, 11) ^ S(x, 25))
+#define Gamma0(x) (S(x, 7) ^ S(x, 18) ^ R(x, 3))
+#define Gamma1(x) (S(x, 17) ^ S(x, 19) ^ R(x, 10))
+#ifndef MIN
+#define MIN(x, y) (((x) < (y)) ? (x) : (y))
+#endif
+
+/* compress 512-bits */
+static int sha256_compress(struct sha256_state *md, unsigned char *buf)
+{
+ u32 S[8], W[64], t0, t1;
+ u32 t;
+ int i;
+
+ /* copy state into S */
+ for (i = 0; i < 8; i++) {
+ S[i] = md->state[i];
+ }
+
+ /* copy the state into 512-bits into W[0..15] */
+ for (i = 0; i < 16; i++)
+ W[i] = WPA_GET_BE32(buf + (4 * i));
+
+ /* fill W[16..63] */
+ for (i = 16; i < 64; i++) {
+ W[i] = Gamma1(W[i - 2]) + W[i - 7] + Gamma0(W[i - 15]) +
+ W[i - 16];
+ }
+
+ /* Compress */
+#define RND(a,b,c,d,e,f,g,h,i) \
+ t0 = h + Sigma1(e) + Ch(e, f, g) + K[i] + W[i]; \
+ t1 = Sigma0(a) + Maj(a, b, c); \
+ d += t0; \
+ h = t0 + t1;
+
+ for (i = 0; i < 64; ++i) {
+ RND(S[0], S[1], S[2], S[3], S[4], S[5], S[6], S[7], i);
+ t = S[7]; S[7] = S[6]; S[6] = S[5]; S[5] = S[4];
+ S[4] = S[3]; S[3] = S[2]; S[2] = S[1]; S[1] = S[0]; S[0] = t;
+ }
+
+ /* feedback */
+ for (i = 0; i < 8; i++) {
+ md->state[i] = md->state[i] + S[i];
+ }
+ return 0;
+}
+
+
+/* Initialize the hash state */
+void sha256_init(struct sha256_state *md)
+{
+ md->curlen = 0;
+ md->length = 0;
+ md->state[0] = 0x6A09E667UL;
+ md->state[1] = 0xBB67AE85UL;
+ md->state[2] = 0x3C6EF372UL;
+ md->state[3] = 0xA54FF53AUL;
+ md->state[4] = 0x510E527FUL;
+ md->state[5] = 0x9B05688CUL;
+ md->state[6] = 0x1F83D9ABUL;
+ md->state[7] = 0x5BE0CD19UL;
+}
+
+/**
+ Process a block of memory though the hash
+ @param md The hash state
+ @param in The data to hash
+ @param inlen The length of the data (octets)
+ @return CRYPT_OK if successful
+*/
+int sha256_process(struct sha256_state *md, const unsigned char *in,
+ unsigned long inlen)
+{
+ unsigned long n;
+
+ if (md->curlen >= sizeof(md->buf))
+ return -1;
+
+ while (inlen > 0) {
+ if (md->curlen == 0 && inlen >= SHA256_BLOCK_SIZE) {
+ if (sha256_compress(md, (unsigned char *) in) < 0)
+ return -1;
+ md->length += SHA256_BLOCK_SIZE * 8;
+ in += SHA256_BLOCK_SIZE;
+ inlen -= SHA256_BLOCK_SIZE;
+ } else {
+ n = MIN(inlen, (SHA256_BLOCK_SIZE - md->curlen));
+ os_memcpy(md->buf + md->curlen, in, n);
+ md->curlen += n;
+ in += n;
+ inlen -= n;
+ if (md->curlen == SHA256_BLOCK_SIZE) {
+ if (sha256_compress(md, md->buf) < 0)
+ return -1;
+ md->length += 8 * SHA256_BLOCK_SIZE;
+ md->curlen = 0;
+ }
+ }
+ }
+
+ return 0;
+}
+
+
+/**
+ Terminate the hash to get the digest
+ @param md The hash state
+ @param out [out] The destination of the hash (32 bytes)
+ @return CRYPT_OK if successful
+*/
+int sha256_done(struct sha256_state *md, unsigned char *out)
+{
+ int i;
+
+ if (md->curlen >= sizeof(md->buf))
+ return -1;
+
+ /* increase the length of the message */
+ md->length += md->curlen * 8;
+
+ /* append the '1' bit */
+ md->buf[md->curlen++] = (unsigned char) 0x80;
+
+ /* if the length is currently above 56 bytes we append zeros
+ * then compress. Then we can fall back to padding zeros and length
+ * encoding like normal.
+ */
+ if (md->curlen > 56) {
+ while (md->curlen < SHA256_BLOCK_SIZE) {
+ md->buf[md->curlen++] = (unsigned char) 0;
+ }
+ sha256_compress(md, md->buf);
+ md->curlen = 0;
+ }
+
+ /* pad up to 56 bytes of zeroes */
+ while (md->curlen < 56) {
+ md->buf[md->curlen++] = (unsigned char) 0;
+ }
+
+ /* store length */
+ WPA_PUT_BE64(md->buf + 56, md->length);
+ sha256_compress(md, md->buf);
+
+ /* copy output */
+ for (i = 0; i < 8; i++)
+ WPA_PUT_BE32(out + (4 * i), md->state[i]);
+
+ return 0;
+}
+
+/* ===== end - public domain SHA256 implementation ===== */
diff --git a/freebsd/contrib/wpa/src/crypto/sha256-prf.c b/freebsd/contrib/wpa/src/crypto/sha256-prf.c
new file mode 100644
index 00000000..76496214
--- /dev/null
+++ b/freebsd/contrib/wpa/src/crypto/sha256-prf.c
@@ -0,0 +1,102 @@
+#include <machine/rtems-bsd-user-space.h>
+
+/*
+ * SHA256-based PRF (IEEE 802.11r)
+ * Copyright (c) 2003-2013, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "sha256.h"
+#include "crypto.h"
+
+
+/**
+ * sha256_prf - SHA256-based Pseudo-Random Function (IEEE 802.11r, 8.5.1.5.2)
+ * @key: Key for PRF
+ * @key_len: Length of the key in bytes
+ * @label: A unique label for each purpose of the PRF
+ * @data: Extra data to bind into the key
+ * @data_len: Length of the data
+ * @buf: Buffer for the generated pseudo-random key
+ * @buf_len: Number of bytes of key to generate
+ *
+ * This function is used to derive new, cryptographically separate keys from a
+ * given key.
+ */
+void sha256_prf(const u8 *key, size_t key_len, const char *label,
+ const u8 *data, size_t data_len, u8 *buf, size_t buf_len)
+{
+ sha256_prf_bits(key, key_len, label, data, data_len, buf, buf_len * 8);
+}
+
+
+/**
+ * sha256_prf_bits - IEEE Std 802.11-2012, 11.6.1.7.2 Key derivation function
+ * @key: Key for KDF
+ * @key_len: Length of the key in bytes
+ * @label: A unique label for each purpose of the PRF
+ * @data: Extra data to bind into the key
+ * @data_len: Length of the data
+ * @buf: Buffer for the generated pseudo-random key
+ * @buf_len: Number of bits of key to generate
+ *
+ * This function is used to derive new, cryptographically separate keys from a
+ * given key. If the requested buf_len is not divisible by eight, the least
+ * significant 1-7 bits of the last octet in the output are not part of the
+ * requested output.
+ */
+void sha256_prf_bits(const u8 *key, size_t key_len, const char *label,
+ const u8 *data, size_t data_len, u8 *buf,
+ size_t buf_len_bits)
+{
+ u16 counter = 1;
+ size_t pos, plen;
+ u8 hash[SHA256_MAC_LEN];
+ const u8 *addr[4];
+ size_t len[4];
+ u8 counter_le[2], length_le[2];
+ size_t buf_len = (buf_len_bits + 7) / 8;
+
+ addr[0] = counter_le;
+ len[0] = 2;
+ addr[1] = (u8 *) label;
+ len[1] = os_strlen(label);
+ addr[2] = data;
+ len[2] = data_len;
+ addr[3] = length_le;
+ len[3] = sizeof(length_le);
+
+ WPA_PUT_LE16(length_le, buf_len_bits);
+ pos = 0;
+ while (pos < buf_len) {
+ plen = buf_len - pos;
+ WPA_PUT_LE16(counter_le, counter);
+ if (plen >= SHA256_MAC_LEN) {
+ hmac_sha256_vector(key, key_len, 4, addr, len,
+ &buf[pos]);
+ pos += SHA256_MAC_LEN;
+ } else {
+ hmac_sha256_vector(key, key_len, 4, addr, len, hash);
+ os_memcpy(&buf[pos], hash, plen);
+ pos += plen;
+ break;
+ }
+ counter++;
+ }
+
+ /*
+ * Mask out unused bits in the last octet if it does not use all the
+ * bits.
+ */
+ if (buf_len_bits % 8) {
+ u8 mask = 0xff << (8 - buf_len_bits % 8);
+ buf[pos - 1] &= mask;
+ }
+
+ os_memset(hash, 0, sizeof(hash));
+}
diff --git a/freebsd/contrib/wpa/src/crypto/sha256.h b/freebsd/contrib/wpa/src/crypto/sha256.h
new file mode 100644
index 00000000..b15f5115
--- /dev/null
+++ b/freebsd/contrib/wpa/src/crypto/sha256.h
@@ -0,0 +1,30 @@
+/*
+ * SHA256 hash implementation and interface functions
+ * Copyright (c) 2003-2014, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef SHA256_H
+#define SHA256_H
+
+#define SHA256_MAC_LEN 32
+
+int hmac_sha256_vector(const u8 *key, size_t key_len, size_t num_elem,
+ const u8 *addr[], const size_t *len, u8 *mac);
+int hmac_sha256(const u8 *key, size_t key_len, const u8 *data,
+ size_t data_len, u8 *mac);
+void sha256_prf(const u8 *key, size_t key_len, const char *label,
+ const u8 *data, size_t data_len, u8 *buf, size_t buf_len);
+void sha256_prf_bits(const u8 *key, size_t key_len, const char *label,
+ const u8 *data, size_t data_len, u8 *buf,
+ size_t buf_len_bits);
+void tls_prf_sha256(const u8 *secret, size_t secret_len,
+ const char *label, const u8 *seed, size_t seed_len,
+ u8 *out, size_t outlen);
+int hmac_sha256_kdf(const u8 *secret, size_t secret_len,
+ const char *label, const u8 *seed, size_t seed_len,
+ u8 *out, size_t outlen);
+
+#endif /* SHA256_H */
diff --git a/freebsd/contrib/wpa/src/crypto/sha256_i.h b/freebsd/contrib/wpa/src/crypto/sha256_i.h
new file mode 100644
index 00000000..a502d2ba
--- /dev/null
+++ b/freebsd/contrib/wpa/src/crypto/sha256_i.h
@@ -0,0 +1,25 @@
+/*
+ * SHA-256 internal definitions
+ * Copyright (c) 2003-2011, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef SHA256_I_H
+#define SHA256_I_H
+
+#define SHA256_BLOCK_SIZE 64
+
+struct sha256_state {
+ u64 length;
+ u32 state[8], curlen;
+ u8 buf[SHA256_BLOCK_SIZE];
+};
+
+void sha256_init(struct sha256_state *md);
+int sha256_process(struct sha256_state *md, const unsigned char *in,
+ unsigned long inlen);
+int sha256_done(struct sha256_state *md, unsigned char *out);
+
+#endif /* SHA256_I_H */
diff --git a/freebsd/contrib/wpa/src/crypto/sha384.h b/freebsd/contrib/wpa/src/crypto/sha384.h
new file mode 100644
index 00000000..3deafa59
--- /dev/null
+++ b/freebsd/contrib/wpa/src/crypto/sha384.h
@@ -0,0 +1,24 @@
+/*
+ * SHA384 hash implementation and interface functions
+ * Copyright (c) 2015, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef SHA384_H
+#define SHA384_H
+
+#define SHA384_MAC_LEN 48
+
+int hmac_sha384_vector(const u8 *key, size_t key_len, size_t num_elem,
+ const u8 *addr[], const size_t *len, u8 *mac);
+int hmac_sha384(const u8 *key, size_t key_len, const u8 *data,
+ size_t data_len, u8 *mac);
+void sha384_prf(const u8 *key, size_t key_len, const char *label,
+ const u8 *data, size_t data_len, u8 *buf, size_t buf_len);
+void sha384_prf_bits(const u8 *key, size_t key_len, const char *label,
+ const u8 *data, size_t data_len, u8 *buf,
+ size_t buf_len_bits);
+
+#endif /* SHA384_H */
diff --git a/freebsd/contrib/wpa/src/crypto/tls.h b/freebsd/contrib/wpa/src/crypto/tls.h
new file mode 100644
index 00000000..2e562339
--- /dev/null
+++ b/freebsd/contrib/wpa/src/crypto/tls.h
@@ -0,0 +1,588 @@
+/*
+ * SSL/TLS interface definition
+ * 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.
+ */
+
+#ifndef TLS_H
+#define TLS_H
+
+struct tls_connection;
+
+struct tls_random {
+ const u8 *client_random;
+ size_t client_random_len;
+ const u8 *server_random;
+ size_t server_random_len;
+};
+
+enum tls_event {
+ TLS_CERT_CHAIN_SUCCESS,
+ TLS_CERT_CHAIN_FAILURE,
+ TLS_PEER_CERTIFICATE,
+ TLS_ALERT
+};
+
+/*
+ * Note: These are used as identifier with external programs and as such, the
+ * values must not be changed.
+ */
+enum tls_fail_reason {
+ TLS_FAIL_UNSPECIFIED = 0,
+ TLS_FAIL_UNTRUSTED = 1,
+ TLS_FAIL_REVOKED = 2,
+ TLS_FAIL_NOT_YET_VALID = 3,
+ TLS_FAIL_EXPIRED = 4,
+ TLS_FAIL_SUBJECT_MISMATCH = 5,
+ TLS_FAIL_ALTSUBJECT_MISMATCH = 6,
+ TLS_FAIL_BAD_CERTIFICATE = 7,
+ TLS_FAIL_SERVER_CHAIN_PROBE = 8,
+ TLS_FAIL_DOMAIN_SUFFIX_MISMATCH = 9,
+ TLS_FAIL_DOMAIN_MISMATCH = 10,
+};
+
+
+#define TLS_MAX_ALT_SUBJECT 10
+
+union tls_event_data {
+ struct {
+ int depth;
+ const char *subject;
+ enum tls_fail_reason reason;
+ const char *reason_txt;
+ const struct wpabuf *cert;
+ } cert_fail;
+
+ struct {
+ int depth;
+ const char *subject;
+ const struct wpabuf *cert;
+ const u8 *hash;
+ size_t hash_len;
+ const char *altsubject[TLS_MAX_ALT_SUBJECT];
+ int num_altsubject;
+ } peer_cert;
+
+ struct {
+ int is_local;
+ const char *type;
+ const char *description;
+ } alert;
+};
+
+struct tls_config {
+ const char *opensc_engine_path;
+ const char *pkcs11_engine_path;
+ const char *pkcs11_module_path;
+ int fips_mode;
+ int cert_in_cb;
+ const char *openssl_ciphers;
+ unsigned int tls_session_lifetime;
+
+ void (*event_cb)(void *ctx, enum tls_event ev,
+ union tls_event_data *data);
+ void *cb_ctx;
+};
+
+#define TLS_CONN_ALLOW_SIGN_RSA_MD5 BIT(0)
+#define TLS_CONN_DISABLE_TIME_CHECKS BIT(1)
+#define TLS_CONN_DISABLE_SESSION_TICKET BIT(2)
+#define TLS_CONN_REQUEST_OCSP BIT(3)
+#define TLS_CONN_REQUIRE_OCSP BIT(4)
+#define TLS_CONN_DISABLE_TLSv1_1 BIT(5)
+#define TLS_CONN_DISABLE_TLSv1_2 BIT(6)
+#define TLS_CONN_EAP_FAST BIT(7)
+#define TLS_CONN_DISABLE_TLSv1_0 BIT(8)
+
+/**
+ * struct tls_connection_params - Parameters for TLS connection
+ * @ca_cert: File or reference name for CA X.509 certificate in PEM or DER
+ * format
+ * @ca_cert_blob: ca_cert as inlined data or %NULL if not used
+ * @ca_cert_blob_len: ca_cert_blob length
+ * @ca_path: Path to CA certificates (OpenSSL specific)
+ * @subject_match: String to match in the subject of the peer certificate or
+ * %NULL to allow all subjects
+ * @altsubject_match: String to match in the alternative subject of the peer
+ * certificate or %NULL to allow all alternative subjects
+ * @suffix_match: String to suffix match in the dNSName or CN of the peer
+ * certificate or %NULL to allow all domain names. This may allow subdomains an
+ * wildcard certificates. Each domain name label must have a full match.
+ * @domain_match: String to match in the dNSName or CN of the peer
+ * certificate or %NULL to allow all domain names. This requires a full,
+ * case-insensitive match.
+ * @client_cert: File or reference name for client X.509 certificate in PEM or
+ * DER format
+ * @client_cert_blob: client_cert as inlined data or %NULL if not used
+ * @client_cert_blob_len: client_cert_blob length
+ * @private_key: File or reference name for client private key in PEM or DER
+ * format (traditional format (RSA PRIVATE KEY) or PKCS#8 (PRIVATE KEY)
+ * @private_key_blob: private_key as inlined data or %NULL if not used
+ * @private_key_blob_len: private_key_blob length
+ * @private_key_passwd: Passphrase for decrypted private key, %NULL if no
+ * passphrase is used.
+ * @dh_file: File name for DH/DSA data in PEM format, or %NULL if not used
+ * @dh_blob: dh_file as inlined data or %NULL if not used
+ * @dh_blob_len: dh_blob length
+ * @engine: 1 = use engine (e.g., a smartcard) for private key operations
+ * (this is OpenSSL specific for now)
+ * @engine_id: engine id string (this is OpenSSL specific for now)
+ * @ppin: pointer to the pin variable in the configuration
+ * (this is OpenSSL specific for now)
+ * @key_id: the private key's id when using engine (this is OpenSSL
+ * specific for now)
+ * @cert_id: the certificate's id when using engine
+ * @ca_cert_id: the CA certificate's id when using engine
+ * @openssl_ciphers: OpenSSL cipher configuration
+ * @flags: Parameter options (TLS_CONN_*)
+ * @ocsp_stapling_response: DER encoded file with cached OCSP stapling response
+ * or %NULL if OCSP is not enabled
+ *
+ * TLS connection parameters to be configured with tls_connection_set_params()
+ * and tls_global_set_params().
+ *
+ * Certificates and private key can be configured either as a reference name
+ * (file path or reference to certificate store) or by providing the same data
+ * as a pointer to the data in memory. Only one option will be used for each
+ * field.
+ */
+struct tls_connection_params {
+ const char *ca_cert;
+ const u8 *ca_cert_blob;
+ size_t ca_cert_blob_len;
+ const char *ca_path;
+ const char *subject_match;
+ const char *altsubject_match;
+ const char *suffix_match;
+ const char *domain_match;
+ const char *client_cert;
+ const u8 *client_cert_blob;
+ size_t client_cert_blob_len;
+ const char *private_key;
+ const u8 *private_key_blob;
+ size_t private_key_blob_len;
+ const char *private_key_passwd;
+ const char *dh_file;
+ const u8 *dh_blob;
+ size_t dh_blob_len;
+
+ /* OpenSSL specific variables */
+ int engine;
+ const char *engine_id;
+ const char *pin;
+ const char *key_id;
+ const char *cert_id;
+ const char *ca_cert_id;
+ const char *openssl_ciphers;
+
+ unsigned int flags;
+ const char *ocsp_stapling_response;
+};
+
+
+/**
+ * tls_init - Initialize TLS library
+ * @conf: Configuration data for TLS library
+ * Returns: Context data to be used as tls_ctx in calls to other functions,
+ * or %NULL on failure.
+ *
+ * Called once during program startup and once for each RSN pre-authentication
+ * session. In other words, there can be two concurrent TLS contexts. If global
+ * library initialization is needed (i.e., one that is shared between both
+ * authentication types), the TLS library wrapper should maintain a reference
+ * counter and do global initialization only when moving from 0 to 1 reference.
+ */
+void * tls_init(const struct tls_config *conf);
+
+/**
+ * tls_deinit - Deinitialize TLS library
+ * @tls_ctx: TLS context data from tls_init()
+ *
+ * Called once during program shutdown and once for each RSN pre-authentication
+ * session. If global library deinitialization is needed (i.e., one that is
+ * shared between both authentication types), the TLS library wrapper should
+ * maintain a reference counter and do global deinitialization only when moving
+ * from 1 to 0 references.
+ */
+void tls_deinit(void *tls_ctx);
+
+/**
+ * tls_get_errors - Process pending errors
+ * @tls_ctx: TLS context data from tls_init()
+ * Returns: Number of found error, 0 if no errors detected.
+ *
+ * Process all pending TLS errors.
+ */
+int tls_get_errors(void *tls_ctx);
+
+/**
+ * tls_connection_init - Initialize a new TLS connection
+ * @tls_ctx: TLS context data from tls_init()
+ * Returns: Connection context data, conn for other function calls
+ */
+struct tls_connection * tls_connection_init(void *tls_ctx);
+
+/**
+ * tls_connection_deinit - Free TLS connection data
+ * @tls_ctx: TLS context data from tls_init()
+ * @conn: Connection context data from tls_connection_init()
+ *
+ * Release all resources allocated for TLS connection.
+ */
+void tls_connection_deinit(void *tls_ctx, struct tls_connection *conn);
+
+/**
+ * tls_connection_established - Has the TLS connection been completed?
+ * @tls_ctx: TLS context data from tls_init()
+ * @conn: Connection context data from tls_connection_init()
+ * Returns: 1 if TLS connection has been completed, 0 if not.
+ */
+int tls_connection_established(void *tls_ctx, struct tls_connection *conn);
+
+/**
+ * tls_connection_shutdown - Shutdown TLS connection
+ * @tls_ctx: TLS context data from tls_init()
+ * @conn: Connection context data from tls_connection_init()
+ * Returns: 0 on success, -1 on failure
+ *
+ * Shutdown current TLS connection without releasing all resources. New
+ * connection can be started by using the same conn without having to call
+ * tls_connection_init() or setting certificates etc. again. The new
+ * connection should try to use session resumption.
+ */
+int tls_connection_shutdown(void *tls_ctx, struct tls_connection *conn);
+
+enum {
+ TLS_SET_PARAMS_ENGINE_PRV_BAD_PIN = -4,
+ TLS_SET_PARAMS_ENGINE_PRV_VERIFY_FAILED = -3,
+ TLS_SET_PARAMS_ENGINE_PRV_INIT_FAILED = -2
+};
+
+/**
+ * tls_connection_set_params - Set TLS connection parameters
+ * @tls_ctx: TLS context data from tls_init()
+ * @conn: Connection context data from tls_connection_init()
+ * @params: Connection parameters
+ * Returns: 0 on success, -1 on failure,
+ * TLS_SET_PARAMS_ENGINE_PRV_INIT_FAILED (-2) on error causing PKCS#11 engine
+ * failure, or
+ * TLS_SET_PARAMS_ENGINE_PRV_VERIFY_FAILED (-3) on failure to verify the
+ * PKCS#11 engine private key, or
+ * TLS_SET_PARAMS_ENGINE_PRV_BAD_PIN (-4) on PIN error causing PKCS#11 engine
+ * failure.
+ */
+int __must_check
+tls_connection_set_params(void *tls_ctx, struct tls_connection *conn,
+ const struct tls_connection_params *params);
+
+/**
+ * tls_global_set_params - Set TLS parameters for all TLS connection
+ * @tls_ctx: TLS context data from tls_init()
+ * @params: Global TLS parameters
+ * Returns: 0 on success, -1 on failure,
+ * TLS_SET_PARAMS_ENGINE_PRV_INIT_FAILED (-2) on error causing PKCS#11 engine
+ * failure, or
+ * TLS_SET_PARAMS_ENGINE_PRV_VERIFY_FAILED (-3) on failure to verify the
+ * PKCS#11 engine private key, or
+ * TLS_SET_PARAMS_ENGINE_PRV_BAD_PIN (-4) on PIN error causing PKCS#11 engine
+ * failure.
+ */
+int __must_check tls_global_set_params(
+ void *tls_ctx, const struct tls_connection_params *params);
+
+/**
+ * tls_global_set_verify - Set global certificate verification options
+ * @tls_ctx: TLS context data from tls_init()
+ * @check_crl: 0 = do not verify CRLs, 1 = verify CRL for the user certificate,
+ * 2 = verify CRL for all certificates
+ * Returns: 0 on success, -1 on failure
+ */
+int __must_check tls_global_set_verify(void *tls_ctx, int check_crl);
+
+/**
+ * tls_connection_set_verify - Set certificate verification options
+ * @tls_ctx: TLS context data from tls_init()
+ * @conn: Connection context data from tls_connection_init()
+ * @verify_peer: 1 = verify peer certificate
+ * @flags: Connection flags (TLS_CONN_*)
+ * @session_ctx: Session caching context or %NULL to use default
+ * @session_ctx_len: Length of @session_ctx in bytes.
+ * Returns: 0 on success, -1 on failure
+ */
+int __must_check tls_connection_set_verify(void *tls_ctx,
+ struct tls_connection *conn,
+ int verify_peer,
+ unsigned int flags,
+ const u8 *session_ctx,
+ size_t session_ctx_len);
+
+/**
+ * tls_connection_get_random - Get random data from TLS connection
+ * @tls_ctx: TLS context data from tls_init()
+ * @conn: Connection context data from tls_connection_init()
+ * @data: Structure of client/server random data (filled on success)
+ * Returns: 0 on success, -1 on failure
+ */
+int __must_check tls_connection_get_random(void *tls_ctx,
+ struct tls_connection *conn,
+ struct tls_random *data);
+
+/**
+ * tls_connection_prf - Use TLS-PRF to derive keying material
+ * @tls_ctx: TLS context data from tls_init()
+ * @conn: Connection context data from tls_connection_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
+ * @skip_keyblock: Skip TLS key block from the beginning of PRF output
+ * @out: Buffer for output data from TLS-PRF
+ * @out_len: Length of the output buffer
+ * Returns: 0 on success, -1 on failure
+ *
+ * tls_connection_prf() is required so that further keying material can be
+ * derived from the master secret. Example implementation of this function is in
+ * tls_prf_sha1_md5() when it is called with seed set to
+ * client_random|server_random (or server_random|client_random). For TLSv1.2 and
+ * newer, a different PRF is needed, though.
+ */
+int __must_check tls_connection_prf(void *tls_ctx,
+ struct tls_connection *conn,
+ const char *label,
+ int server_random_first,
+ int skip_keyblock,
+ u8 *out, size_t out_len);
+
+/**
+ * tls_connection_handshake - Process TLS handshake (client side)
+ * @tls_ctx: TLS context data from tls_init()
+ * @conn: Connection context data from tls_connection_init()
+ * @in_data: Input data from TLS server
+ * @appl_data: Pointer to application data pointer, or %NULL if dropped
+ * Returns: Output data, %NULL on failure
+ *
+ * The caller is responsible for freeing the returned output data. If the final
+ * handshake message includes application data, this is decrypted and
+ * appl_data (if not %NULL) is set to point this data. The caller is
+ * responsible for freeing appl_data.
+ *
+ * This function is used during TLS handshake. The first call is done with
+ * in_data == %NULL and the library is expected to return ClientHello packet.
+ * This packet is then send to the server and a response from server is given
+ * to TLS library by calling this function again with in_data pointing to the
+ * TLS message from the server.
+ *
+ * If the TLS handshake fails, this function may return %NULL. However, if the
+ * TLS library has a TLS alert to send out, that should be returned as the
+ * output data. In this case, tls_connection_get_failed() must return failure
+ * (> 0).
+ *
+ * tls_connection_established() should return 1 once the TLS handshake has been
+ * completed successfully.
+ */
+struct wpabuf * tls_connection_handshake(void *tls_ctx,
+ struct tls_connection *conn,
+ const struct wpabuf *in_data,
+ struct wpabuf **appl_data);
+
+struct wpabuf * tls_connection_handshake2(void *tls_ctx,
+ struct tls_connection *conn,
+ const struct wpabuf *in_data,
+ struct wpabuf **appl_data,
+ int *more_data_needed);
+
+/**
+ * tls_connection_server_handshake - Process TLS handshake (server side)
+ * @tls_ctx: TLS context data from tls_init()
+ * @conn: Connection context data from tls_connection_init()
+ * @in_data: Input data from TLS peer
+ * @appl_data: Pointer to application data pointer, or %NULL if dropped
+ * Returns: Output data, %NULL on failure
+ *
+ * The caller is responsible for freeing the returned output data.
+ */
+struct wpabuf * tls_connection_server_handshake(void *tls_ctx,
+ struct tls_connection *conn,
+ const struct wpabuf *in_data,
+ struct wpabuf **appl_data);
+
+/**
+ * tls_connection_encrypt - Encrypt data into TLS tunnel
+ * @tls_ctx: TLS context data from tls_init()
+ * @conn: Connection context data from tls_connection_init()
+ * @in_data: Plaintext data to be encrypted
+ * Returns: Encrypted TLS data or %NULL on failure
+ *
+ * This function is used after TLS handshake has been completed successfully to
+ * send data in the encrypted tunnel. The caller is responsible for freeing the
+ * returned output data.
+ */
+struct wpabuf * tls_connection_encrypt(void *tls_ctx,
+ struct tls_connection *conn,
+ const struct wpabuf *in_data);
+
+/**
+ * tls_connection_decrypt - Decrypt data from TLS tunnel
+ * @tls_ctx: TLS context data from tls_init()
+ * @conn: Connection context data from tls_connection_init()
+ * @in_data: Encrypted TLS data
+ * Returns: Decrypted TLS data or %NULL on failure
+ *
+ * This function is used after TLS handshake has been completed successfully to
+ * receive data from the encrypted tunnel. The caller is responsible for
+ * freeing the returned output data.
+ */
+struct wpabuf * tls_connection_decrypt(void *tls_ctx,
+ struct tls_connection *conn,
+ const struct wpabuf *in_data);
+
+struct wpabuf * tls_connection_decrypt2(void *tls_ctx,
+ struct tls_connection *conn,
+ const struct wpabuf *in_data,
+ int *more_data_needed);
+
+/**
+ * tls_connection_resumed - Was session resumption used
+ * @tls_ctx: TLS context data from tls_init()
+ * @conn: Connection context data from tls_connection_init()
+ * Returns: 1 if current session used session resumption, 0 if not
+ */
+int tls_connection_resumed(void *tls_ctx, struct tls_connection *conn);
+
+enum {
+ TLS_CIPHER_NONE,
+ TLS_CIPHER_RC4_SHA /* 0x0005 */,
+ TLS_CIPHER_AES128_SHA /* 0x002f */,
+ TLS_CIPHER_RSA_DHE_AES128_SHA /* 0x0031 */,
+ TLS_CIPHER_ANON_DH_AES128_SHA /* 0x0034 */
+};
+
+/**
+ * tls_connection_set_cipher_list - Configure acceptable cipher suites
+ * @tls_ctx: TLS context data from tls_init()
+ * @conn: Connection context data from tls_connection_init()
+ * @ciphers: Zero (TLS_CIPHER_NONE) terminated list of allowed ciphers
+ * (TLS_CIPHER_*).
+ * Returns: 0 on success, -1 on failure
+ */
+int __must_check tls_connection_set_cipher_list(void *tls_ctx,
+ struct tls_connection *conn,
+ u8 *ciphers);
+
+/**
+ * tls_get_version - Get the current TLS version number
+ * @tls_ctx: TLS context data from tls_init()
+ * @conn: Connection context data from tls_connection_init()
+ * @buf: Buffer for returning the TLS version number
+ * @buflen: buf size
+ * Returns: 0 on success, -1 on failure
+ *
+ * Get the currently used TLS version number.
+ */
+int __must_check tls_get_version(void *tls_ctx, struct tls_connection *conn,
+ char *buf, size_t buflen);
+
+/**
+ * tls_get_cipher - Get current cipher name
+ * @tls_ctx: TLS context data from tls_init()
+ * @conn: Connection context data from tls_connection_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 __must_check tls_get_cipher(void *tls_ctx, struct tls_connection *conn,
+ char *buf, size_t buflen);
+
+/**
+ * tls_connection_enable_workaround - Enable TLS workaround options
+ * @tls_ctx: TLS context data from tls_init()
+ * @conn: Connection context data from tls_connection_init()
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function is used to enable connection-specific workaround options for
+ * buffer SSL/TLS implementations.
+ */
+int __must_check tls_connection_enable_workaround(void *tls_ctx,
+ struct tls_connection *conn);
+
+/**
+ * tls_connection_client_hello_ext - Set TLS extension for ClientHello
+ * @tls_ctx: TLS context data from tls_init()
+ * @conn: Connection context data from tls_connection_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 __must_check tls_connection_client_hello_ext(void *tls_ctx,
+ struct tls_connection *conn,
+ int ext_type, const u8 *data,
+ size_t data_len);
+
+/**
+ * tls_connection_get_failed - Get connection failure status
+ * @tls_ctx: TLS context data from tls_init()
+ * @conn: Connection context data from tls_connection_init()
+ *
+ * Returns >0 if connection has failed, 0 if not.
+ */
+int tls_connection_get_failed(void *tls_ctx, struct tls_connection *conn);
+
+/**
+ * tls_connection_get_read_alerts - Get connection read alert status
+ * @tls_ctx: TLS context data from tls_init()
+ * @conn: Connection context data from tls_connection_init()
+ * Returns: Number of times a fatal read (remote end reported error) has
+ * happened during this connection.
+ */
+int tls_connection_get_read_alerts(void *tls_ctx, struct tls_connection *conn);
+
+/**
+ * tls_connection_get_write_alerts - Get connection write alert status
+ * @tls_ctx: TLS context data from tls_init()
+ * @conn: Connection context data from tls_connection_init()
+ * Returns: Number of times a fatal write (locally detected error) has happened
+ * during this connection.
+ */
+int tls_connection_get_write_alerts(void *tls_ctx,
+ struct tls_connection *conn);
+
+typedef int (*tls_session_ticket_cb)
+(void *ctx, const u8 *ticket, size_t len, const u8 *client_random,
+ const u8 *server_random, u8 *master_secret);
+
+int __must_check tls_connection_set_session_ticket_cb(
+ void *tls_ctx, struct tls_connection *conn,
+ tls_session_ticket_cb cb, void *ctx);
+
+void tls_connection_set_log_cb(struct tls_connection *conn,
+ void (*log_cb)(void *ctx, const char *msg),
+ void *ctx);
+
+#define TLS_BREAK_VERIFY_DATA BIT(0)
+#define TLS_BREAK_SRV_KEY_X_HASH BIT(1)
+#define TLS_BREAK_SRV_KEY_X_SIGNATURE BIT(2)
+#define TLS_DHE_PRIME_511B BIT(3)
+#define TLS_DHE_PRIME_767B BIT(4)
+#define TLS_DHE_PRIME_15 BIT(5)
+#define TLS_DHE_PRIME_58B BIT(6)
+#define TLS_DHE_NON_PRIME BIT(7)
+
+void tls_connection_set_test_flags(struct tls_connection *conn, u32 flags);
+
+int tls_get_library_version(char *buf, size_t buf_len);
+
+void tls_connection_set_success_data(struct tls_connection *conn,
+ struct wpabuf *data);
+
+void tls_connection_set_success_data_resumed(struct tls_connection *conn);
+
+const struct wpabuf *
+tls_connection_get_success_data(struct tls_connection *conn);
+
+void tls_connection_remove_session(struct tls_connection *conn);
+
+#endif /* TLS_H */
diff --git a/freebsd/contrib/wpa/src/crypto/tls_internal.c b/freebsd/contrib/wpa/src/crypto/tls_internal.c
new file mode 100644
index 00000000..6a91f497
--- /dev/null
+++ b/freebsd/contrib/wpa/src/crypto/tls_internal.c
@@ -0,0 +1,735 @@
+#include <machine/rtems-bsd-user-space.h>
+
+/*
+ * 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 "includes.h"
+
+#include "common.h"
+#include "tls.h"
+#include "tls/tlsv1_client.h"
+#include "tls/tlsv1_server.h"
+
+
+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;
+ struct tls_global *global;
+};
+
+
+void * tls_init(const struct tls_config *conf)
+{
+ 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 = 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 = os_zalloc(sizeof(*conn));
+ if (conn == NULL)
+ return NULL;
+ conn->global = global;
+
+#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;
+}
+
+
+#ifdef CONFIG_TESTING_OPTIONS
+#ifdef CONFIG_TLS_INTERNAL_SERVER
+void tls_connection_set_test_flags(struct tls_connection *conn, u32 flags)
+{
+ if (conn->server)
+ tlsv1_server_set_test_flags(conn->server, flags);
+}
+#endif /* CONFIG_TLS_INTERNAL_SERVER */
+#endif /* CONFIG_TESTING_OPTIONS */
+
+
+void tls_connection_set_log_cb(struct tls_connection *conn,
+ void (*log_cb)(void *ctx, const char *msg),
+ void *ctx)
+{
+#ifdef CONFIG_TLS_INTERNAL_SERVER
+ if (conn->server)
+ tlsv1_server_set_log_cb(conn->server, log_cb, ctx);
+#endif /* CONFIG_TLS_INTERNAL_SERVER */
+}
+
+
+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 (params->subject_match) {
+ wpa_printf(MSG_INFO, "TLS: subject_match not supported");
+ tlsv1_cred_free(cred);
+ return -1;
+ }
+
+ if (params->altsubject_match) {
+ wpa_printf(MSG_INFO, "TLS: altsubject_match not supported");
+ tlsv1_cred_free(cred);
+ return -1;
+ }
+
+ if (params->suffix_match) {
+ wpa_printf(MSG_INFO, "TLS: suffix_match not supported");
+ tlsv1_cred_free(cred);
+ return -1;
+ }
+
+ if (params->domain_match) {
+ wpa_printf(MSG_INFO, "TLS: domain_match not supported");
+ tlsv1_cred_free(cred);
+ return -1;
+ }
+
+ if (params->openssl_ciphers) {
+ wpa_printf(MSG_INFO, "TLS: openssl_ciphers not supported");
+ tlsv1_cred_free(cred);
+ 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_set_dhparams(cred, params->dh_file, params->dh_blob,
+ params->dh_blob_len)) {
+ wpa_printf(MSG_INFO, "TLS: Failed to load DH parameters");
+ 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));
+
+ 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, unsigned int flags,
+ const u8 *session_ctx, size_t session_ctx_len)
+{
+#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_random(void *tls_ctx, struct tls_connection *conn,
+ struct tls_random *data)
+{
+#ifdef CONFIG_TLS_INTERNAL_CLIENT
+ if (conn->client)
+ return tlsv1_client_get_random(conn->client, data);
+#endif /* CONFIG_TLS_INTERNAL_CLIENT */
+#ifdef CONFIG_TLS_INTERNAL_SERVER
+ if (conn->server)
+ return tlsv1_server_get_random(conn->server, data);
+#endif /* CONFIG_TLS_INTERNAL_SERVER */
+ return -1;
+}
+
+
+static int tls_get_keyblock_size(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;
+}
+
+
+int tls_connection_prf(void *tls_ctx, struct tls_connection *conn,
+ const char *label, int server_random_first,
+ int skip_keyblock, u8 *out, size_t out_len)
+{
+ int ret = -1, skip = 0;
+ u8 *tmp_out = NULL;
+ u8 *_out = out;
+
+ if (skip_keyblock) {
+ skip = tls_get_keyblock_size(conn);
+ if (skip < 0)
+ return -1;
+ tmp_out = os_malloc(skip + out_len);
+ if (!tmp_out)
+ return -1;
+ _out = tmp_out;
+ }
+
+#ifdef CONFIG_TLS_INTERNAL_CLIENT
+ if (conn->client) {
+ ret = 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) {
+ ret = tlsv1_server_prf(conn->server, label,
+ server_random_first,
+ _out, out_len);
+ }
+#endif /* CONFIG_TLS_INTERNAL_SERVER */
+ if (ret == 0 && skip_keyblock)
+ os_memcpy(out, _out + skip, out_len);
+ bin_clear_free(tmp_out, skip);
+
+ return ret;
+}
+
+
+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_version(void *ssl_ctx, struct tls_connection *conn,
+ char *buf, size_t buflen)
+{
+ /* TODO */
+ 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_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;
+}
+
+
+int tls_get_library_version(char *buf, size_t buf_len)
+{
+ return os_snprintf(buf, buf_len, "internal");
+}
+
+
+void tls_connection_set_success_data(struct tls_connection *conn,
+ struct wpabuf *data)
+{
+}
+
+
+void tls_connection_set_success_data_resumed(struct tls_connection *conn)
+{
+}
+
+
+const struct wpabuf *
+tls_connection_get_success_data(struct tls_connection *conn)
+{
+ return NULL;
+}
+
+
+void tls_connection_remove_session(struct tls_connection *conn)
+{
+}
diff --git a/freebsd/contrib/wpa/src/drivers/driver.h b/freebsd/contrib/wpa/src/drivers/driver.h
new file mode 100644
index 00000000..3cdab5a7
--- /dev/null
+++ b/freebsd/contrib/wpa/src/drivers/driver.h
@@ -0,0 +1,4704 @@
+/*
+ * Driver interface definition
+ * Copyright (c) 2003-2015, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ *
+ * This file defines a driver interface used by both %wpa_supplicant and
+ * hostapd. The first part of the file defines data structures used in various
+ * driver operations. This is followed by the struct wpa_driver_ops that each
+ * driver wrapper will beed to define with callback functions for requesting
+ * driver operations. After this, there are definitions for driver event
+ * reporting with wpa_supplicant_event() and some convenience helper functions
+ * that can be used to report events.
+ */
+
+#ifndef DRIVER_H
+#define DRIVER_H
+
+#define WPA_SUPPLICANT_DRIVER_VERSION 4
+
+#include "common/defs.h"
+#include "common/ieee802_11_defs.h"
+#include "utils/list.h"
+
+#define HOSTAPD_CHAN_DISABLED 0x00000001
+#define HOSTAPD_CHAN_NO_IR 0x00000002
+#define HOSTAPD_CHAN_RADAR 0x00000008
+#define HOSTAPD_CHAN_HT40PLUS 0x00000010
+#define HOSTAPD_CHAN_HT40MINUS 0x00000020
+#define HOSTAPD_CHAN_HT40 0x00000040
+#define HOSTAPD_CHAN_SURVEY_LIST_INITIALIZED 0x00000080
+
+#define HOSTAPD_CHAN_DFS_UNKNOWN 0x00000000
+#define HOSTAPD_CHAN_DFS_USABLE 0x00000100
+#define HOSTAPD_CHAN_DFS_UNAVAILABLE 0x00000200
+#define HOSTAPD_CHAN_DFS_AVAILABLE 0x00000300
+#define HOSTAPD_CHAN_DFS_MASK 0x00000300
+
+#define HOSTAPD_CHAN_VHT_10_70 0x00000800
+#define HOSTAPD_CHAN_VHT_30_50 0x00001000
+#define HOSTAPD_CHAN_VHT_50_30 0x00002000
+#define HOSTAPD_CHAN_VHT_70_10 0x00004000
+
+#define HOSTAPD_CHAN_INDOOR_ONLY 0x00010000
+#define HOSTAPD_CHAN_GO_CONCURRENT 0x00020000
+
+/**
+ * enum reg_change_initiator - Regulatory change initiator
+ */
+enum reg_change_initiator {
+ REGDOM_SET_BY_CORE,
+ REGDOM_SET_BY_USER,
+ REGDOM_SET_BY_DRIVER,
+ REGDOM_SET_BY_COUNTRY_IE,
+ REGDOM_BEACON_HINT,
+};
+
+/**
+ * enum reg_type - Regulatory change types
+ */
+enum reg_type {
+ REGDOM_TYPE_UNKNOWN,
+ REGDOM_TYPE_COUNTRY,
+ REGDOM_TYPE_WORLD,
+ REGDOM_TYPE_CUSTOM_WORLD,
+ REGDOM_TYPE_INTERSECTION,
+};
+
+/**
+ * struct hostapd_channel_data - Channel information
+ */
+struct hostapd_channel_data {
+ /**
+ * chan - Channel number (IEEE 802.11)
+ */
+ short chan;
+
+ /**
+ * freq - Frequency in MHz
+ */
+ int freq;
+
+ /**
+ * flag - Channel flags (HOSTAPD_CHAN_*)
+ */
+ int flag;
+
+ /**
+ * max_tx_power - Regulatory transmit power limit in dBm
+ */
+ u8 max_tx_power;
+
+ /**
+ * survey_list - Linked list of surveys (struct freq_survey)
+ */
+ struct dl_list survey_list;
+
+ /**
+ * min_nf - Minimum observed noise floor, in dBm, based on all
+ * surveyed channel data
+ */
+ s8 min_nf;
+
+#ifdef CONFIG_ACS
+ /**
+ * interference_factor - Computed interference factor on this
+ * channel (used internally in src/ap/acs.c; driver wrappers do not
+ * need to set this)
+ */
+ long double interference_factor;
+#endif /* CONFIG_ACS */
+
+ /**
+ * dfs_cac_ms - DFS CAC time in milliseconds
+ */
+ unsigned int dfs_cac_ms;
+};
+
+#define HOSTAPD_MODE_FLAG_HT_INFO_KNOWN BIT(0)
+#define HOSTAPD_MODE_FLAG_VHT_INFO_KNOWN BIT(1)
+
+/**
+ * struct hostapd_hw_modes - Supported hardware mode information
+ */
+struct hostapd_hw_modes {
+ /**
+ * mode - Hardware mode
+ */
+ enum hostapd_hw_mode mode;
+
+ /**
+ * num_channels - Number of entries in the channels array
+ */
+ int num_channels;
+
+ /**
+ * channels - Array of supported channels
+ */
+ struct hostapd_channel_data *channels;
+
+ /**
+ * num_rates - Number of entries in the rates array
+ */
+ int num_rates;
+
+ /**
+ * rates - Array of supported rates in 100 kbps units
+ */
+ int *rates;
+
+ /**
+ * ht_capab - HT (IEEE 802.11n) capabilities
+ */
+ u16 ht_capab;
+
+ /**
+ * mcs_set - MCS (IEEE 802.11n) rate parameters
+ */
+ u8 mcs_set[16];
+
+ /**
+ * a_mpdu_params - A-MPDU (IEEE 802.11n) parameters
+ */
+ u8 a_mpdu_params;
+
+ /**
+ * vht_capab - VHT (IEEE 802.11ac) capabilities
+ */
+ u32 vht_capab;
+
+ /**
+ * vht_mcs_set - VHT MCS (IEEE 802.11ac) rate parameters
+ */
+ u8 vht_mcs_set[8];
+
+ unsigned int flags; /* HOSTAPD_MODE_FLAG_* */
+};
+
+
+#define IEEE80211_MODE_INFRA 0
+#define IEEE80211_MODE_IBSS 1
+#define IEEE80211_MODE_AP 2
+#define IEEE80211_MODE_MESH 5
+
+#define IEEE80211_CAP_ESS 0x0001
+#define IEEE80211_CAP_IBSS 0x0002
+#define IEEE80211_CAP_PRIVACY 0x0010
+#define IEEE80211_CAP_RRM 0x1000
+
+/* DMG (60 GHz) IEEE 802.11ad */
+/* type - bits 0..1 */
+#define IEEE80211_CAP_DMG_MASK 0x0003
+#define IEEE80211_CAP_DMG_IBSS 0x0001 /* Tx by: STA */
+#define IEEE80211_CAP_DMG_PBSS 0x0002 /* Tx by: PCP */
+#define IEEE80211_CAP_DMG_AP 0x0003 /* Tx by: AP */
+
+#define WPA_SCAN_QUAL_INVALID BIT(0)
+#define WPA_SCAN_NOISE_INVALID BIT(1)
+#define WPA_SCAN_LEVEL_INVALID BIT(2)
+#define WPA_SCAN_LEVEL_DBM BIT(3)
+#define WPA_SCAN_ASSOCIATED BIT(5)
+
+/**
+ * struct wpa_scan_res - Scan result for an BSS/IBSS
+ * @flags: information flags about the BSS/IBSS (WPA_SCAN_*)
+ * @bssid: BSSID
+ * @freq: frequency of the channel in MHz (e.g., 2412 = channel 1)
+ * @beacon_int: beacon interval in TUs (host byte order)
+ * @caps: capability information field in host byte order
+ * @qual: signal quality
+ * @noise: noise level
+ * @level: signal level
+ * @tsf: Timestamp
+ * @age: Age of the information in milliseconds (i.e., how many milliseconds
+ * ago the last Beacon or Probe Response frame was received)
+ * @est_throughput: Estimated throughput in kbps (this is calculated during
+ * scan result processing if left zero by the driver wrapper)
+ * @snr: Signal-to-noise ratio in dB (calculated during scan result processing)
+ * @ie_len: length of the following IE field in octets
+ * @beacon_ie_len: length of the following Beacon IE field in octets
+ *
+ * This structure is used as a generic format for scan results from the
+ * driver. Each driver interface implementation is responsible for converting
+ * the driver or OS specific scan results into this format.
+ *
+ * If the driver does not support reporting all IEs, the IE data structure is
+ * constructed of the IEs that are available. This field will also need to
+ * include SSID in IE format. All drivers are encouraged to be extended to
+ * report all IEs to make it easier to support future additions.
+ *
+ * This structure data is followed by ie_len octets of IEs from Probe Response
+ * frame (or if the driver does not indicate source of IEs, these may also be
+ * from Beacon frame). After the first set of IEs, another set of IEs may follow
+ * (with beacon_ie_len octets of data) if the driver provides both IE sets.
+ */
+struct wpa_scan_res {
+ unsigned int flags;
+ u8 bssid[ETH_ALEN];
+ int freq;
+ u16 beacon_int;
+ u16 caps;
+ int qual;
+ int noise;
+ int level;
+ u64 tsf;
+ unsigned int age;
+ unsigned int est_throughput;
+ int snr;
+ size_t ie_len;
+ size_t beacon_ie_len;
+ /* Followed by ie_len + beacon_ie_len octets of IE data */
+};
+
+/**
+ * struct wpa_scan_results - Scan results
+ * @res: Array of pointers to allocated variable length scan result entries
+ * @num: Number of entries in the scan result array
+ * @fetch_time: Time when the results were fetched from the driver
+ */
+struct wpa_scan_results {
+ struct wpa_scan_res **res;
+ size_t num;
+ struct os_reltime fetch_time;
+};
+
+/**
+ * struct wpa_interface_info - Network interface information
+ * @next: Pointer to the next interface or NULL if this is the last one
+ * @ifname: Interface name that can be used with init() or init2()
+ * @desc: Human readable adapter description (e.g., vendor/model) or NULL if
+ * not available
+ * @drv_name: struct wpa_driver_ops::name (note: unlike other strings, this one
+ * is not an allocated copy, i.e., get_interfaces() caller will not free
+ * this)
+ */
+struct wpa_interface_info {
+ struct wpa_interface_info *next;
+ char *ifname;
+ char *desc;
+ const char *drv_name;
+};
+
+#define WPAS_MAX_SCAN_SSIDS 16
+
+/**
+ * struct wpa_driver_scan_params - Scan parameters
+ * Data for struct wpa_driver_ops::scan2().
+ */
+struct wpa_driver_scan_params {
+ /**
+ * ssids - SSIDs to scan for
+ */
+ struct wpa_driver_scan_ssid {
+ /**
+ * ssid - specific SSID to scan for (ProbeReq)
+ * %NULL or zero-length SSID is used to indicate active scan
+ * with wildcard SSID.
+ */
+ const u8 *ssid;
+ /**
+ * ssid_len: Length of the SSID in octets
+ */
+ size_t ssid_len;
+ } ssids[WPAS_MAX_SCAN_SSIDS];
+
+ /**
+ * num_ssids - Number of entries in ssids array
+ * Zero indicates a request for a passive scan.
+ */
+ size_t num_ssids;
+
+ /**
+ * extra_ies - Extra IE(s) to add into Probe Request or %NULL
+ */
+ const u8 *extra_ies;
+
+ /**
+ * extra_ies_len - Length of extra_ies in octets
+ */
+ size_t extra_ies_len;
+
+ /**
+ * freqs - Array of frequencies to scan or %NULL for all frequencies
+ *
+ * The frequency is set in MHz. The array is zero-terminated.
+ */
+ int *freqs;
+
+ /**
+ * filter_ssids - Filter for reporting SSIDs
+ *
+ * This optional parameter can be used to request the driver wrapper to
+ * filter scan results to include only the specified SSIDs. %NULL
+ * indicates that no filtering is to be done. This can be used to
+ * reduce memory needs for scan results in environments that have large
+ * number of APs with different SSIDs.
+ *
+ * The driver wrapper is allowed to take this allocated buffer into its
+ * own use by setting the pointer to %NULL. In that case, the driver
+ * wrapper is responsible for freeing the buffer with os_free() once it
+ * is not needed anymore.
+ */
+ struct wpa_driver_scan_filter {
+ u8 ssid[SSID_MAX_LEN];
+ size_t ssid_len;
+ } *filter_ssids;
+
+ /**
+ * num_filter_ssids - Number of entries in filter_ssids array
+ */
+ size_t num_filter_ssids;
+
+ /**
+ * filter_rssi - Filter by RSSI
+ *
+ * The driver may filter scan results in firmware to reduce host
+ * wakeups and thereby save power. Specify the RSSI threshold in s32
+ * dBm.
+ */
+ s32 filter_rssi;
+
+ /**
+ * p2p_probe - Used to disable CCK (802.11b) rates for P2P probes
+ *
+ * When set, the driver is expected to remove rates 1, 2, 5.5, and 11
+ * Mbps from the support rates element(s) in the Probe Request frames
+ * and not to transmit the frames at any of those rates.
+ */
+ unsigned int p2p_probe:1;
+
+ /**
+ * only_new_results - Request driver to report only new results
+ *
+ * This is used to request the driver to report only BSSes that have
+ * been detected after this scan request has been started, i.e., to
+ * flush old cached BSS entries.
+ */
+ unsigned int only_new_results:1;
+
+ /**
+ * low_priority - Requests driver to use a lower scan priority
+ *
+ * This is used to request the driver to use a lower scan priority
+ * if it supports such a thing.
+ */
+ unsigned int low_priority:1;
+
+ /**
+ * mac_addr_rand - Requests driver to randomize MAC address
+ */
+ unsigned int mac_addr_rand:1;
+
+ /**
+ * mac_addr - MAC address used with randomization. The address cannot be
+ * a multicast one, i.e., bit 0 of byte 0 should not be set.
+ */
+ const u8 *mac_addr;
+
+ /**
+ * mac_addr_mask - MAC address mask used with randomization.
+ *
+ * Bits that are 0 in the mask should be randomized. Bits that are 1 in
+ * the mask should be taken as is from mac_addr. The mask should not
+ * allow the generation of a multicast address, i.e., bit 0 of byte 0
+ * must be set.
+ */
+ const u8 *mac_addr_mask;
+
+ /*
+ * NOTE: Whenever adding new parameters here, please make sure
+ * wpa_scan_clone_params() and wpa_scan_free_params() get updated with
+ * matching changes.
+ */
+};
+
+/**
+ * struct wpa_driver_auth_params - Authentication parameters
+ * Data for struct wpa_driver_ops::authenticate().
+ */
+struct wpa_driver_auth_params {
+ int freq;
+ const u8 *bssid;
+ const u8 *ssid;
+ size_t ssid_len;
+ int auth_alg;
+ const u8 *ie;
+ size_t ie_len;
+ const u8 *wep_key[4];
+ size_t wep_key_len[4];
+ int wep_tx_keyidx;
+ int local_state_change;
+
+ /**
+ * p2p - Whether this connection is a P2P group
+ */
+ int p2p;
+
+ /**
+ * sae_data - SAE elements for Authentication frame
+ *
+ * This buffer starts with the Authentication transaction sequence
+ * number field. If SAE is not used, this pointer is %NULL.
+ */
+ const u8 *sae_data;
+
+ /**
+ * sae_data_len - Length of sae_data buffer in octets
+ */
+ size_t sae_data_len;
+};
+
+/**
+ * enum wps_mode - WPS mode
+ */
+enum wps_mode {
+ /**
+ * WPS_MODE_NONE - No WPS provisioning being used
+ */
+ WPS_MODE_NONE,
+
+ /**
+ * WPS_MODE_OPEN - WPS provisioning with AP that is in open mode
+ */
+ WPS_MODE_OPEN,
+
+ /**
+ * WPS_MODE_PRIVACY - WPS provisioning with AP that is using protection
+ */
+ WPS_MODE_PRIVACY
+};
+
+/**
+ * struct hostapd_freq_params - Channel parameters
+ */
+struct hostapd_freq_params {
+ /**
+ * mode - Mode/band (HOSTAPD_MODE_IEEE80211A, ..)
+ */
+ enum hostapd_hw_mode mode;
+
+ /**
+ * freq - Primary channel center frequency in MHz
+ */
+ int freq;
+
+ /**
+ * channel - Channel number
+ */
+ int channel;
+
+ /**
+ * ht_enabled - Whether HT is enabled
+ */
+ int ht_enabled;
+
+ /**
+ * sec_channel_offset - Secondary channel offset for HT40
+ *
+ * 0 = HT40 disabled,
+ * -1 = HT40 enabled, secondary channel below primary,
+ * 1 = HT40 enabled, secondary channel above primary
+ */
+ int sec_channel_offset;
+
+ /**
+ * vht_enabled - Whether VHT is enabled
+ */
+ int vht_enabled;
+
+ /**
+ * center_freq1 - Segment 0 center frequency in MHz
+ *
+ * Valid for both HT and VHT.
+ */
+ int center_freq1;
+
+ /**
+ * center_freq2 - Segment 1 center frequency in MHz
+ *
+ * Non-zero only for bandwidth 80 and an 80+80 channel
+ */
+ int center_freq2;
+
+ /**
+ * bandwidth - Channel bandwidth in MHz (20, 40, 80, 160)
+ */
+ int bandwidth;
+};
+
+/**
+ * struct wpa_driver_associate_params - Association parameters
+ * Data for struct wpa_driver_ops::associate().
+ */
+struct wpa_driver_associate_params {
+ /**
+ * bssid - BSSID of the selected AP
+ * This can be %NULL, if ap_scan=2 mode is used and the driver is
+ * responsible for selecting with which BSS to associate. */
+ const u8 *bssid;
+
+ /**
+ * bssid_hint - BSSID of a proposed AP
+ *
+ * This indicates which BSS has been found a suitable candidate for
+ * initial association for drivers that use driver/firmwate-based BSS
+ * selection. Unlike the @bssid parameter, @bssid_hint does not limit
+ * the driver from selecting other BSSes in the ESS.
+ */
+ const u8 *bssid_hint;
+
+ /**
+ * ssid - The selected SSID
+ */
+ const u8 *ssid;
+
+ /**
+ * ssid_len - Length of the SSID (1..32)
+ */
+ size_t ssid_len;
+
+ /**
+ * freq - channel parameters
+ */
+ struct hostapd_freq_params freq;
+
+ /**
+ * freq_hint - Frequency of the channel the proposed AP is using
+ *
+ * This provides a channel on which a suitable BSS has been found as a
+ * hint for the driver. Unlike the @freq parameter, @freq_hint does not
+ * limit the driver from selecting other channels for
+ * driver/firmware-based BSS selection.
+ */
+ int freq_hint;
+
+ /**
+ * bg_scan_period - Background scan period in seconds, 0 to disable
+ * background scan, or -1 to indicate no change to default driver
+ * configuration
+ */
+ int bg_scan_period;
+
+ /**
+ * beacon_int - Beacon interval for IBSS or 0 to use driver default
+ */
+ int beacon_int;
+
+ /**
+ * wpa_ie - WPA information element for (Re)Association Request
+ * WPA information element to be included in (Re)Association
+ * Request (including information element id and length). Use
+ * of this WPA IE is optional. If the driver generates the WPA
+ * IE, it can use pairwise_suite, group_suite, and
+ * key_mgmt_suite to select proper algorithms. In this case,
+ * the driver has to notify wpa_supplicant about the used WPA
+ * IE by generating an event that the interface code will
+ * convert into EVENT_ASSOCINFO data (see below).
+ *
+ * When using WPA2/IEEE 802.11i, wpa_ie is used for RSN IE
+ * instead. The driver can determine which version is used by
+ * looking at the first byte of the IE (0xdd for WPA, 0x30 for
+ * WPA2/RSN).
+ *
+ * When using WPS, wpa_ie is used for WPS IE instead of WPA/RSN IE.
+ */
+ const u8 *wpa_ie;
+
+ /**
+ * wpa_ie_len - length of the wpa_ie
+ */
+ size_t wpa_ie_len;
+
+ /**
+ * wpa_proto - Bitfield of WPA_PROTO_* values to indicate WPA/WPA2
+ */
+ unsigned int wpa_proto;
+
+ /**
+ * pairwise_suite - Selected pairwise cipher suite (WPA_CIPHER_*)
+ *
+ * This is usually ignored if @wpa_ie is used.
+ */
+ unsigned int pairwise_suite;
+
+ /**
+ * group_suite - Selected group cipher suite (WPA_CIPHER_*)
+ *
+ * This is usually ignored if @wpa_ie is used.
+ */
+ unsigned int group_suite;
+
+ /**
+ * key_mgmt_suite - Selected key management suite (WPA_KEY_MGMT_*)
+ *
+ * This is usually ignored if @wpa_ie is used.
+ */
+ unsigned int key_mgmt_suite;
+
+ /**
+ * auth_alg - Allowed authentication algorithms
+ * Bit field of WPA_AUTH_ALG_*
+ */
+ int auth_alg;
+
+ /**
+ * mode - Operation mode (infra/ibss) IEEE80211_MODE_*
+ */
+ int mode;
+
+ /**
+ * wep_key - WEP keys for static WEP configuration
+ */
+ const u8 *wep_key[4];
+
+ /**
+ * wep_key_len - WEP key length for static WEP configuration
+ */
+ size_t wep_key_len[4];
+
+ /**
+ * wep_tx_keyidx - WEP TX key index for static WEP configuration
+ */
+ int wep_tx_keyidx;
+
+ /**
+ * mgmt_frame_protection - IEEE 802.11w management frame protection
+ */
+ enum mfp_options mgmt_frame_protection;
+
+ /**
+ * ft_ies - IEEE 802.11r / FT information elements
+ * If the supplicant is using IEEE 802.11r (FT) and has the needed keys
+ * for fast transition, this parameter is set to include the IEs that
+ * are to be sent in the next FT Authentication Request message.
+ * update_ft_ies() handler is called to update the IEs for further
+ * FT messages in the sequence.
+ *
+ * The driver should use these IEs only if the target AP is advertising
+ * the same mobility domain as the one included in the MDIE here.
+ *
+ * In ap_scan=2 mode, the driver can use these IEs when moving to a new
+ * AP after the initial association. These IEs can only be used if the
+ * target AP is advertising support for FT and is using the same MDIE
+ * and SSID as the current AP.
+ *
+ * The driver is responsible for reporting the FT IEs received from the
+ * AP's response using wpa_supplicant_event() with EVENT_FT_RESPONSE
+ * type. update_ft_ies() handler will then be called with the FT IEs to
+ * include in the next frame in the authentication sequence.
+ */
+ const u8 *ft_ies;
+
+ /**
+ * ft_ies_len - Length of ft_ies in bytes
+ */
+ size_t ft_ies_len;
+
+ /**
+ * ft_md - FT Mobility domain (6 octets) (also included inside ft_ies)
+ *
+ * This value is provided to allow the driver interface easier access
+ * to the current mobility domain. This value is set to %NULL if no
+ * mobility domain is currently active.
+ */
+ const u8 *ft_md;
+
+ /**
+ * passphrase - RSN passphrase for PSK
+ *
+ * This value is made available only for WPA/WPA2-Personal (PSK) and
+ * only for drivers that set WPA_DRIVER_FLAGS_4WAY_HANDSHAKE. This is
+ * the 8..63 character ASCII passphrase, if available. Please note that
+ * this can be %NULL if passphrase was not used to generate the PSK. In
+ * that case, the psk field must be used to fetch the PSK.
+ */
+ const char *passphrase;
+
+ /**
+ * psk - RSN PSK (alternative for passphrase for PSK)
+ *
+ * This value is made available only for WPA/WPA2-Personal (PSK) and
+ * only for drivers that set WPA_DRIVER_FLAGS_4WAY_HANDSHAKE. This is
+ * the 32-octet (256-bit) PSK, if available. The driver wrapper should
+ * be prepared to handle %NULL value as an error.
+ */
+ const u8 *psk;
+
+ /**
+ * drop_unencrypted - Enable/disable unencrypted frame filtering
+ *
+ * Configure the driver to drop all non-EAPOL frames (both receive and
+ * transmit paths). Unencrypted EAPOL frames (ethertype 0x888e) must
+ * still be allowed for key negotiation.
+ */
+ int drop_unencrypted;
+
+ /**
+ * prev_bssid - Previously used BSSID in this ESS
+ *
+ * When not %NULL, this is a request to use reassociation instead of
+ * association.
+ */
+ const u8 *prev_bssid;
+
+ /**
+ * wps - WPS mode
+ *
+ * If the driver needs to do special configuration for WPS association,
+ * this variable provides more information on what type of association
+ * is being requested. Most drivers should not need ot use this.
+ */
+ enum wps_mode wps;
+
+ /**
+ * p2p - Whether this connection is a P2P group
+ */
+ int p2p;
+
+ /**
+ * uapsd - UAPSD parameters for the network
+ * -1 = do not change defaults
+ * AP mode: 1 = enabled, 0 = disabled
+ * STA mode: bits 0..3 UAPSD enabled for VO,VI,BK,BE
+ */
+ int uapsd;
+
+ /**
+ * fixed_bssid - Whether to force this BSSID in IBSS mode
+ * 1 = Fix this BSSID and prevent merges.
+ * 0 = Do not fix BSSID.
+ */
+ int fixed_bssid;
+
+ /**
+ * fixed_freq - Fix control channel in IBSS mode
+ * 0 = don't fix control channel (default)
+ * 1 = fix control channel; this prevents IBSS merging with another
+ * channel
+ */
+ int fixed_freq;
+
+ /**
+ * disable_ht - Disable HT (IEEE 802.11n) for this connection
+ */
+ int disable_ht;
+
+ /**
+ * htcaps - HT Capabilities over-rides
+ *
+ * Only bits set in the mask will be used, and not all values are used
+ * by the kernel anyway. Currently, MCS, MPDU and MSDU fields are used.
+ *
+ * Pointer to struct ieee80211_ht_capabilities.
+ */
+ const u8 *htcaps;
+
+ /**
+ * htcaps_mask - HT Capabilities over-rides mask
+ *
+ * Pointer to struct ieee80211_ht_capabilities.
+ */
+ const u8 *htcaps_mask;
+
+#ifdef CONFIG_VHT_OVERRIDES
+ /**
+ * disable_vht - Disable VHT for this connection
+ */
+ int disable_vht;
+
+ /**
+ * VHT capability overrides.
+ */
+ const struct ieee80211_vht_capabilities *vhtcaps;
+ const struct ieee80211_vht_capabilities *vhtcaps_mask;
+#endif /* CONFIG_VHT_OVERRIDES */
+
+ /**
+ * req_key_mgmt_offload - Request key management offload for connection
+ *
+ * Request key management offload for this connection if the device
+ * supports it.
+ */
+ int req_key_mgmt_offload;
+
+ /**
+ * Flag for indicating whether this association includes support for
+ * RRM (Radio Resource Measurements)
+ */
+ int rrm_used;
+};
+
+enum hide_ssid {
+ NO_SSID_HIDING,
+ HIDDEN_SSID_ZERO_LEN,
+ HIDDEN_SSID_ZERO_CONTENTS
+};
+
+struct wowlan_triggers {
+ u8 any;
+ u8 disconnect;
+ u8 magic_pkt;
+ u8 gtk_rekey_failure;
+ u8 eap_identity_req;
+ u8 four_way_handshake;
+ u8 rfkill_release;
+};
+
+struct wpa_driver_ap_params {
+ /**
+ * head - Beacon head from IEEE 802.11 header to IEs before TIM IE
+ */
+ u8 *head;
+
+ /**
+ * head_len - Length of the head buffer in octets
+ */
+ size_t head_len;
+
+ /**
+ * tail - Beacon tail following TIM IE
+ */
+ u8 *tail;
+
+ /**
+ * tail_len - Length of the tail buffer in octets
+ */
+ size_t tail_len;
+
+ /**
+ * dtim_period - DTIM period
+ */
+ int dtim_period;
+
+ /**
+ * beacon_int - Beacon interval
+ */
+ int beacon_int;
+
+ /**
+ * basic_rates: -1 terminated array of basic rates in 100 kbps
+ *
+ * This parameter can be used to set a specific basic rate set for the
+ * BSS. If %NULL, default basic rate set is used.
+ */
+ int *basic_rates;
+
+ /**
+ * proberesp - Probe Response template
+ *
+ * This is used by drivers that reply to Probe Requests internally in
+ * AP mode and require the full Probe Response template.
+ */
+ u8 *proberesp;
+
+ /**
+ * proberesp_len - Length of the proberesp buffer in octets
+ */
+ size_t proberesp_len;
+
+ /**
+ * ssid - The SSID to use in Beacon/Probe Response frames
+ */
+ const u8 *ssid;
+
+ /**
+ * ssid_len - Length of the SSID (1..32)
+ */
+ size_t ssid_len;
+
+ /**
+ * hide_ssid - Whether to hide the SSID
+ */
+ enum hide_ssid hide_ssid;
+
+ /**
+ * pairwise_ciphers - WPA_CIPHER_* bitfield
+ */
+ unsigned int pairwise_ciphers;
+
+ /**
+ * group_cipher - WPA_CIPHER_*
+ */
+ unsigned int group_cipher;
+
+ /**
+ * key_mgmt_suites - WPA_KEY_MGMT_* bitfield
+ */
+ unsigned int key_mgmt_suites;
+
+ /**
+ * auth_algs - WPA_AUTH_ALG_* bitfield
+ */
+ unsigned int auth_algs;
+
+ /**
+ * wpa_version - WPA_PROTO_* bitfield
+ */
+ unsigned int wpa_version;
+
+ /**
+ * privacy - Whether privacy is used in the BSS
+ */
+ int privacy;
+
+ /**
+ * beacon_ies - WPS/P2P IE(s) for Beacon frames
+ *
+ * This is used to add IEs like WPS IE and P2P IE by drivers that do
+ * not use the full Beacon template.
+ */
+ const struct wpabuf *beacon_ies;
+
+ /**
+ * proberesp_ies - P2P/WPS IE(s) for Probe Response frames
+ *
+ * This is used to add IEs like WPS IE and P2P IE by drivers that
+ * reply to Probe Request frames internally.
+ */
+ const struct wpabuf *proberesp_ies;
+
+ /**
+ * assocresp_ies - WPS IE(s) for (Re)Association Response frames
+ *
+ * This is used to add IEs like WPS IE by drivers that reply to
+ * (Re)Association Request frames internally.
+ */
+ const struct wpabuf *assocresp_ies;
+
+ /**
+ * isolate - Whether to isolate frames between associated stations
+ *
+ * If this is non-zero, the AP is requested to disable forwarding of
+ * frames between associated stations.
+ */
+ int isolate;
+
+ /**
+ * cts_protect - Whether CTS protection is enabled
+ */
+ int cts_protect;
+
+ /**
+ * preamble - Whether short preamble is enabled
+ */
+ int preamble;
+
+ /**
+ * short_slot_time - Whether short slot time is enabled
+ *
+ * 0 = short slot time disable, 1 = short slot time enabled, -1 = do
+ * not set (e.g., when 802.11g mode is not in use)
+ */
+ int short_slot_time;
+
+ /**
+ * ht_opmode - HT operation mode or -1 if HT not in use
+ */
+ int ht_opmode;
+
+ /**
+ * interworking - Whether Interworking is enabled
+ */
+ int interworking;
+
+ /**
+ * hessid - Homogeneous ESS identifier or %NULL if not set
+ */
+ const u8 *hessid;
+
+ /**
+ * access_network_type - Access Network Type (0..15)
+ *
+ * This is used for filtering Probe Request frames when Interworking is
+ * enabled.
+ */
+ u8 access_network_type;
+
+ /**
+ * ap_max_inactivity - Timeout in seconds to detect STA's inactivity
+ *
+ * This is used by driver which advertises this capability.
+ */
+ int ap_max_inactivity;
+
+ /**
+ * ctwindow - Client Traffic Window (in TUs)
+ */
+ u8 p2p_go_ctwindow;
+
+ /**
+ * smps_mode - SMPS mode
+ *
+ * SMPS mode to be used by the AP, specified as the relevant bits of
+ * ht_capab (i.e. HT_CAP_INFO_SMPS_*).
+ */
+ unsigned int smps_mode;
+
+ /**
+ * disable_dgaf - Whether group-addressed frames are disabled
+ */
+ int disable_dgaf;
+
+ /**
+ * osen - Whether OSEN security is enabled
+ */
+ int osen;
+
+ /**
+ * freq - Channel parameters for dynamic bandwidth changes
+ */
+ struct hostapd_freq_params *freq;
+
+ /**
+ * reenable - Whether this is to re-enable beaconing
+ */
+ int reenable;
+};
+
+struct wpa_driver_mesh_bss_params {
+#define WPA_DRIVER_MESH_CONF_FLAG_AUTO_PLINKS 0x00000001
+ /*
+ * TODO: Other mesh configuration parameters would go here.
+ * See NL80211_MESHCONF_* for all the mesh config parameters.
+ */
+ unsigned int flags;
+ int peer_link_timeout;
+};
+
+struct wpa_driver_mesh_join_params {
+ const u8 *meshid;
+ int meshid_len;
+ const int *basic_rates;
+ const u8 *ies;
+ int ie_len;
+ struct hostapd_freq_params freq;
+ int beacon_int;
+ int max_peer_links;
+ struct wpa_driver_mesh_bss_params conf;
+#define WPA_DRIVER_MESH_FLAG_USER_MPM 0x00000001
+#define WPA_DRIVER_MESH_FLAG_DRIVER_MPM 0x00000002
+#define WPA_DRIVER_MESH_FLAG_SAE_AUTH 0x00000004
+#define WPA_DRIVER_MESH_FLAG_AMPE 0x00000008
+ unsigned int flags;
+};
+
+/**
+ * struct wpa_driver_capa - Driver capability information
+ */
+struct wpa_driver_capa {
+#define WPA_DRIVER_CAPA_KEY_MGMT_WPA 0x00000001
+#define WPA_DRIVER_CAPA_KEY_MGMT_WPA2 0x00000002
+#define WPA_DRIVER_CAPA_KEY_MGMT_WPA_PSK 0x00000004
+#define WPA_DRIVER_CAPA_KEY_MGMT_WPA2_PSK 0x00000008
+#define WPA_DRIVER_CAPA_KEY_MGMT_WPA_NONE 0x00000010
+#define WPA_DRIVER_CAPA_KEY_MGMT_FT 0x00000020
+#define WPA_DRIVER_CAPA_KEY_MGMT_FT_PSK 0x00000040
+#define WPA_DRIVER_CAPA_KEY_MGMT_WAPI_PSK 0x00000080
+#define WPA_DRIVER_CAPA_KEY_MGMT_SUITE_B 0x00000100
+#define WPA_DRIVER_CAPA_KEY_MGMT_SUITE_B_192 0x00000200
+ /** Bitfield of supported key management suites */
+ unsigned int key_mgmt;
+
+#define WPA_DRIVER_CAPA_ENC_WEP40 0x00000001
+#define WPA_DRIVER_CAPA_ENC_WEP104 0x00000002
+#define WPA_DRIVER_CAPA_ENC_TKIP 0x00000004
+#define WPA_DRIVER_CAPA_ENC_CCMP 0x00000008
+#define WPA_DRIVER_CAPA_ENC_WEP128 0x00000010
+#define WPA_DRIVER_CAPA_ENC_GCMP 0x00000020
+#define WPA_DRIVER_CAPA_ENC_GCMP_256 0x00000040
+#define WPA_DRIVER_CAPA_ENC_CCMP_256 0x00000080
+#define WPA_DRIVER_CAPA_ENC_BIP 0x00000100
+#define WPA_DRIVER_CAPA_ENC_BIP_GMAC_128 0x00000200
+#define WPA_DRIVER_CAPA_ENC_BIP_GMAC_256 0x00000400
+#define WPA_DRIVER_CAPA_ENC_BIP_CMAC_256 0x00000800
+#define WPA_DRIVER_CAPA_ENC_GTK_NOT_USED 0x00001000
+ /** Bitfield of supported cipher suites */
+ unsigned int enc;
+
+#define WPA_DRIVER_AUTH_OPEN 0x00000001
+#define WPA_DRIVER_AUTH_SHARED 0x00000002
+#define WPA_DRIVER_AUTH_LEAP 0x00000004
+ /** Bitfield of supported IEEE 802.11 authentication algorithms */
+ unsigned int auth;
+
+/** Driver generated WPA/RSN IE */
+#define WPA_DRIVER_FLAGS_DRIVER_IE 0x00000001
+/** Driver needs static WEP key setup after association command */
+#define WPA_DRIVER_FLAGS_SET_KEYS_AFTER_ASSOC 0x00000002
+/** Driver takes care of all DFS operations */
+#define WPA_DRIVER_FLAGS_DFS_OFFLOAD 0x00000004
+/** Driver takes care of RSN 4-way handshake internally; PMK is configured with
+ * struct wpa_driver_ops::set_key using alg = WPA_ALG_PMK */
+#define WPA_DRIVER_FLAGS_4WAY_HANDSHAKE 0x00000008
+/** Driver is for a wired Ethernet interface */
+#define WPA_DRIVER_FLAGS_WIRED 0x00000010
+/** Driver provides separate commands for authentication and association (SME in
+ * wpa_supplicant). */
+#define WPA_DRIVER_FLAGS_SME 0x00000020
+/** Driver supports AP mode */
+#define WPA_DRIVER_FLAGS_AP 0x00000040
+/** Driver needs static WEP key setup after association has been completed */
+#define WPA_DRIVER_FLAGS_SET_KEYS_AFTER_ASSOC_DONE 0x00000080
+/** Driver supports dynamic HT 20/40 MHz channel changes during BSS lifetime */
+#define WPA_DRIVER_FLAGS_HT_2040_COEX 0x00000100
+/** Driver supports concurrent P2P operations */
+#define WPA_DRIVER_FLAGS_P2P_CONCURRENT 0x00000200
+/**
+ * Driver uses the initial interface as a dedicated management interface, i.e.,
+ * it cannot be used for P2P group operations or non-P2P purposes.
+ */
+#define WPA_DRIVER_FLAGS_P2P_DEDICATED_INTERFACE 0x00000400
+/** This interface is P2P capable (P2P GO or P2P Client) */
+#define WPA_DRIVER_FLAGS_P2P_CAPABLE 0x00000800
+/** Driver supports station and key removal when stopping an AP */
+#define WPA_DRIVER_FLAGS_AP_TEARDOWN_SUPPORT 0x00001000
+/**
+ * Driver uses the initial interface for P2P management interface and non-P2P
+ * purposes (e.g., connect to infra AP), but this interface cannot be used for
+ * P2P group operations.
+ */
+#define WPA_DRIVER_FLAGS_P2P_MGMT_AND_NON_P2P 0x00002000
+/**
+ * Driver is known to use sane error codes, i.e., when it indicates that
+ * something (e.g., association) fails, there was indeed a failure and the
+ * operation does not end up getting completed successfully later.
+ */
+#define WPA_DRIVER_FLAGS_SANE_ERROR_CODES 0x00004000
+/** Driver supports off-channel TX */
+#define WPA_DRIVER_FLAGS_OFFCHANNEL_TX 0x00008000
+/** Driver indicates TX status events for EAPOL Data frames */
+#define WPA_DRIVER_FLAGS_EAPOL_TX_STATUS 0x00010000
+/** Driver indicates TX status events for Deauth/Disassoc frames */
+#define WPA_DRIVER_FLAGS_DEAUTH_TX_STATUS 0x00020000
+/** Driver supports roaming (BSS selection) in firmware */
+#define WPA_DRIVER_FLAGS_BSS_SELECTION 0x00040000
+/** Driver supports operating as a TDLS peer */
+#define WPA_DRIVER_FLAGS_TDLS_SUPPORT 0x00080000
+/** Driver requires external TDLS setup/teardown/discovery */
+#define WPA_DRIVER_FLAGS_TDLS_EXTERNAL_SETUP 0x00100000
+/** Driver indicates support for Probe Response offloading in AP mode */
+#define WPA_DRIVER_FLAGS_PROBE_RESP_OFFLOAD 0x00200000
+/** Driver supports U-APSD in AP mode */
+#define WPA_DRIVER_FLAGS_AP_UAPSD 0x00400000
+/** Driver supports inactivity timer in AP mode */
+#define WPA_DRIVER_FLAGS_INACTIVITY_TIMER 0x00800000
+/** Driver expects user space implementation of MLME in AP mode */
+#define WPA_DRIVER_FLAGS_AP_MLME 0x01000000
+/** Driver supports SAE with user space SME */
+#define WPA_DRIVER_FLAGS_SAE 0x02000000
+/** Driver makes use of OBSS scan mechanism in wpa_supplicant */
+#define WPA_DRIVER_FLAGS_OBSS_SCAN 0x04000000
+/** Driver supports IBSS (Ad-hoc) mode */
+#define WPA_DRIVER_FLAGS_IBSS 0x08000000
+/** Driver supports radar detection */
+#define WPA_DRIVER_FLAGS_RADAR 0x10000000
+/** Driver supports a dedicated interface for P2P Device */
+#define WPA_DRIVER_FLAGS_DEDICATED_P2P_DEVICE 0x20000000
+/** Driver supports QoS Mapping */
+#define WPA_DRIVER_FLAGS_QOS_MAPPING 0x40000000
+/** Driver supports CSA in AP mode */
+#define WPA_DRIVER_FLAGS_AP_CSA 0x80000000
+/** Driver supports mesh */
+#define WPA_DRIVER_FLAGS_MESH 0x0000000100000000ULL
+/** Driver support ACS offload */
+#define WPA_DRIVER_FLAGS_ACS_OFFLOAD 0x0000000200000000ULL
+/** Driver supports key management offload */
+#define WPA_DRIVER_FLAGS_KEY_MGMT_OFFLOAD 0x0000000400000000ULL
+/** Driver supports TDLS channel switching */
+#define WPA_DRIVER_FLAGS_TDLS_CHANNEL_SWITCH 0x0000000800000000ULL
+/** Driver supports IBSS with HT datarates */
+#define WPA_DRIVER_FLAGS_HT_IBSS 0x0000001000000000ULL
+/** Driver supports IBSS with VHT datarates */
+#define WPA_DRIVER_FLAGS_VHT_IBSS 0x0000002000000000ULL
+/** Driver supports automatic band selection */
+#define WPA_DRIVER_FLAGS_SUPPORT_HW_MODE_ANY 0x0000004000000000ULL
+ u64 flags;
+
+#define WPA_DRIVER_SMPS_MODE_STATIC 0x00000001
+#define WPA_DRIVER_SMPS_MODE_DYNAMIC 0x00000002
+ unsigned int smps_modes;
+
+ unsigned int wmm_ac_supported:1;
+
+ unsigned int mac_addr_rand_scan_supported:1;
+ unsigned int mac_addr_rand_sched_scan_supported:1;
+
+ /** Maximum number of supported active probe SSIDs */
+ int max_scan_ssids;
+
+ /** Maximum number of supported active probe SSIDs for sched_scan */
+ int max_sched_scan_ssids;
+
+ /** Whether sched_scan (offloaded scanning) is supported */
+ int sched_scan_supported;
+
+ /** Maximum number of supported match sets for sched_scan */
+ int max_match_sets;
+
+ /**
+ * max_remain_on_chan - Maximum remain-on-channel duration in msec
+ */
+ unsigned int max_remain_on_chan;
+
+ /**
+ * max_stations - Maximum number of associated stations the driver
+ * supports in AP mode
+ */
+ unsigned int max_stations;
+
+ /**
+ * probe_resp_offloads - Bitmap of supported protocols by the driver
+ * for Probe Response offloading.
+ */
+/** Driver Probe Response offloading support for WPS ver. 1 */
+#define WPA_DRIVER_PROBE_RESP_OFFLOAD_WPS 0x00000001
+/** Driver Probe Response offloading support for WPS ver. 2 */
+#define WPA_DRIVER_PROBE_RESP_OFFLOAD_WPS2 0x00000002
+/** Driver Probe Response offloading support for P2P */
+#define WPA_DRIVER_PROBE_RESP_OFFLOAD_P2P 0x00000004
+/** Driver Probe Response offloading support for IEEE 802.11u (Interworking) */
+#define WPA_DRIVER_PROBE_RESP_OFFLOAD_INTERWORKING 0x00000008
+ unsigned int probe_resp_offloads;
+
+ unsigned int max_acl_mac_addrs;
+
+ /**
+ * Number of supported concurrent channels
+ */
+ unsigned int num_multichan_concurrent;
+
+ /**
+ * extended_capa - extended capabilities in driver/device
+ *
+ * Must be allocated and freed by driver and the pointers must be
+ * valid for the lifetime of the driver, i.e., freed in deinit()
+ */
+ const u8 *extended_capa, *extended_capa_mask;
+ unsigned int extended_capa_len;
+
+ struct wowlan_triggers wowlan_triggers;
+
+/** Driver adds the DS Params Set IE in Probe Request frames */
+#define WPA_DRIVER_FLAGS_DS_PARAM_SET_IE_IN_PROBES 0x00000001
+/** Driver adds the WFA TPC IE in Probe Request frames */
+#define WPA_DRIVER_FLAGS_WFA_TPC_IE_IN_PROBES 0x00000002
+/** Driver handles quiet period requests */
+#define WPA_DRIVER_FLAGS_QUIET 0x00000004
+/**
+ * Driver is capable of inserting the current TX power value into the body of
+ * transmitted frames.
+ * Background: Some Action frames include a TPC Report IE. This IE contains a
+ * TX power field, which has to be updated by lower layers. One such Action
+ * frame is Link Measurement Report (part of RRM). Another is TPC Report (part
+ * of spectrum management). Note that this insertion takes place at a fixed
+ * offset, namely the 6th byte in the Action frame body.
+ */
+#define WPA_DRIVER_FLAGS_TX_POWER_INSERTION 0x00000008
+ u32 rrm_flags;
+
+ /* Driver concurrency capabilities */
+ unsigned int conc_capab;
+ /* Maximum number of concurrent channels on 2.4 GHz */
+ unsigned int max_conc_chan_2_4;
+ /* Maximum number of concurrent channels on 5 GHz */
+ unsigned int max_conc_chan_5_0;
+};
+
+
+struct hostapd_data;
+
+struct hostap_sta_driver_data {
+ unsigned long rx_packets, tx_packets, rx_bytes, tx_bytes;
+ unsigned long current_tx_rate;
+ unsigned long inactive_msec;
+ unsigned long flags;
+ unsigned long num_ps_buf_frames;
+ unsigned long tx_retry_failed;
+ unsigned long tx_retry_count;
+ int last_rssi;
+ int last_ack_rssi;
+};
+
+struct hostapd_sta_add_params {
+ const u8 *addr;
+ u16 aid;
+ u16 capability;
+ const u8 *supp_rates;
+ size_t supp_rates_len;
+ u16 listen_interval;
+ const struct ieee80211_ht_capabilities *ht_capabilities;
+ const struct ieee80211_vht_capabilities *vht_capabilities;
+ int vht_opmode_enabled;
+ u8 vht_opmode;
+ u32 flags; /* bitmask of WPA_STA_* flags */
+ u32 flags_mask; /* unset bits in flags */
+#ifdef CONFIG_MESH
+ enum mesh_plink_state plink_state;
+#endif /* CONFIG_MESH */
+ int set; /* Set STA parameters instead of add */
+ u8 qosinfo;
+ const u8 *ext_capab;
+ size_t ext_capab_len;
+ const u8 *supp_channels;
+ size_t supp_channels_len;
+ const u8 *supp_oper_classes;
+ size_t supp_oper_classes_len;
+};
+
+struct mac_address {
+ u8 addr[ETH_ALEN];
+};
+
+struct hostapd_acl_params {
+ u8 acl_policy;
+ unsigned int num_mac_acl;
+ struct mac_address mac_acl[0];
+};
+
+enum wpa_driver_if_type {
+ /**
+ * WPA_IF_STATION - Station mode interface
+ */
+ WPA_IF_STATION,
+
+ /**
+ * WPA_IF_AP_VLAN - AP mode VLAN interface
+ *
+ * This interface shares its address and Beacon frame with the main
+ * BSS.
+ */
+ WPA_IF_AP_VLAN,
+
+ /**
+ * WPA_IF_AP_BSS - AP mode BSS interface
+ *
+ * This interface has its own address and Beacon frame.
+ */
+ WPA_IF_AP_BSS,
+
+ /**
+ * WPA_IF_P2P_GO - P2P Group Owner
+ */
+ WPA_IF_P2P_GO,
+
+ /**
+ * WPA_IF_P2P_CLIENT - P2P Client
+ */
+ WPA_IF_P2P_CLIENT,
+
+ /**
+ * WPA_IF_P2P_GROUP - P2P Group interface (will become either
+ * WPA_IF_P2P_GO or WPA_IF_P2P_CLIENT, but the role is not yet known)
+ */
+ WPA_IF_P2P_GROUP,
+
+ /**
+ * WPA_IF_P2P_DEVICE - P2P Device interface is used to indentify the
+ * abstracted P2P Device function in the driver
+ */
+ WPA_IF_P2P_DEVICE,
+
+ /*
+ * WPA_IF_MESH - Mesh interface
+ */
+ WPA_IF_MESH,
+
+ /*
+ * WPA_IF_TDLS - TDLS offchannel interface (used for pref freq only)
+ */
+ WPA_IF_TDLS,
+
+ /*
+ * WPA_IF_IBSS - IBSS interface (used for pref freq only)
+ */
+ WPA_IF_IBSS,
+};
+
+struct wpa_init_params {
+ void *global_priv;
+ const u8 *bssid;
+ const char *ifname;
+ const char *driver_params;
+ int use_pae_group_addr;
+ char **bridge;
+ size_t num_bridge;
+
+ u8 *own_addr; /* buffer for writing own MAC address */
+};
+
+
+struct wpa_bss_params {
+ /** Interface name (for multi-SSID/VLAN support) */
+ const char *ifname;
+ /** Whether IEEE 802.1X or WPA/WPA2 is enabled */
+ int enabled;
+
+ int wpa;
+ int ieee802_1x;
+ int wpa_group;
+ int wpa_pairwise;
+ int wpa_key_mgmt;
+ int rsn_preauth;
+ enum mfp_options ieee80211w;
+};
+
+#define WPA_STA_AUTHORIZED BIT(0)
+#define WPA_STA_WMM BIT(1)
+#define WPA_STA_SHORT_PREAMBLE BIT(2)
+#define WPA_STA_MFP BIT(3)
+#define WPA_STA_TDLS_PEER BIT(4)
+#define WPA_STA_AUTHENTICATED BIT(5)
+
+enum tdls_oper {
+ TDLS_DISCOVERY_REQ,
+ TDLS_SETUP,
+ TDLS_TEARDOWN,
+ TDLS_ENABLE_LINK,
+ TDLS_DISABLE_LINK,
+ TDLS_ENABLE,
+ TDLS_DISABLE
+};
+
+enum wnm_oper {
+ WNM_SLEEP_ENTER_CONFIRM,
+ WNM_SLEEP_ENTER_FAIL,
+ WNM_SLEEP_EXIT_CONFIRM,
+ WNM_SLEEP_EXIT_FAIL,
+ WNM_SLEEP_TFS_REQ_IE_ADD, /* STA requests driver to add TFS req IE */
+ WNM_SLEEP_TFS_REQ_IE_NONE, /* STA requests empty TFS req IE */
+ WNM_SLEEP_TFS_REQ_IE_SET, /* AP requests driver to set TFS req IE for
+ * a STA */
+ WNM_SLEEP_TFS_RESP_IE_ADD, /* AP requests driver to add TFS resp IE
+ * for a STA */
+ WNM_SLEEP_TFS_RESP_IE_NONE, /* AP requests empty TFS resp IE */
+ WNM_SLEEP_TFS_RESP_IE_SET, /* AP requests driver to set TFS resp IE
+ * for a STA */
+ WNM_SLEEP_TFS_IE_DEL /* AP delete the TFS IE */
+};
+
+/* enum chan_width - Channel width definitions */
+enum chan_width {
+ CHAN_WIDTH_20_NOHT,
+ CHAN_WIDTH_20,
+ CHAN_WIDTH_40,
+ CHAN_WIDTH_80,
+ CHAN_WIDTH_80P80,
+ CHAN_WIDTH_160,
+ CHAN_WIDTH_UNKNOWN
+};
+
+/**
+ * struct wpa_signal_info - Information about channel signal quality
+ */
+struct wpa_signal_info {
+ u32 frequency;
+ int above_threshold;
+ int current_signal;
+ int avg_signal;
+ int avg_beacon_signal;
+ int current_noise;
+ int current_txrate;
+ enum chan_width chanwidth;
+ int center_frq1;
+ int center_frq2;
+};
+
+/**
+ * struct beacon_data - Beacon data
+ * @head: Head portion of Beacon frame (before TIM IE)
+ * @tail: Tail portion of Beacon frame (after TIM IE)
+ * @beacon_ies: Extra information element(s) to add into Beacon frames or %NULL
+ * @proberesp_ies: Extra information element(s) to add into Probe Response
+ * frames or %NULL
+ * @assocresp_ies: Extra information element(s) to add into (Re)Association
+ * Response frames or %NULL
+ * @probe_resp: Probe Response frame template
+ * @head_len: Length of @head
+ * @tail_len: Length of @tail
+ * @beacon_ies_len: Length of beacon_ies in octets
+ * @proberesp_ies_len: Length of proberesp_ies in octets
+ * @proberesp_ies_len: Length of proberesp_ies in octets
+ * @probe_resp_len: Length of probe response template (@probe_resp)
+ */
+struct beacon_data {
+ u8 *head, *tail;
+ u8 *beacon_ies;
+ u8 *proberesp_ies;
+ u8 *assocresp_ies;
+ u8 *probe_resp;
+
+ size_t head_len, tail_len;
+ size_t beacon_ies_len;
+ size_t proberesp_ies_len;
+ size_t assocresp_ies_len;
+ size_t probe_resp_len;
+};
+
+/**
+ * struct csa_settings - Settings for channel switch command
+ * @cs_count: Count in Beacon frames (TBTT) to perform the switch
+ * @block_tx: 1 - block transmission for CSA period
+ * @freq_params: Next channel frequency parameter
+ * @beacon_csa: Beacon/probe resp/asooc resp info for CSA period
+ * @beacon_after: Next beacon/probe resp/asooc resp info
+ * @counter_offset_beacon: Offset to the count field in beacon's tail
+ * @counter_offset_presp: Offset to the count field in probe resp.
+ */
+struct csa_settings {
+ u8 cs_count;
+ u8 block_tx;
+
+ struct hostapd_freq_params freq_params;
+ struct beacon_data beacon_csa;
+ struct beacon_data beacon_after;
+
+ u16 counter_offset_beacon;
+ u16 counter_offset_presp;
+};
+
+/* TDLS peer capabilities for send_tdls_mgmt() */
+enum tdls_peer_capability {
+ TDLS_PEER_HT = BIT(0),
+ TDLS_PEER_VHT = BIT(1),
+ TDLS_PEER_WMM = BIT(2),
+};
+
+/* valid info in the wmm_params struct */
+enum wmm_params_valid_info {
+ WMM_PARAMS_UAPSD_QUEUES_INFO = BIT(0),
+};
+
+/**
+ * struct wmm_params - WMM parameterss configured for this association
+ * @info_bitmap: Bitmap of valid wmm_params info; indicates what fields
+ * of the struct contain valid information.
+ * @uapsd_queues: Bitmap of ACs configured for uapsd (valid only if
+ * %WMM_PARAMS_UAPSD_QUEUES_INFO is set)
+ */
+struct wmm_params {
+ u8 info_bitmap;
+ u8 uapsd_queues;
+};
+
+#ifdef CONFIG_MACSEC
+struct macsec_init_params {
+ Boolean always_include_sci;
+ Boolean use_es;
+ Boolean use_scb;
+};
+#endif /* CONFIG_MACSEC */
+
+enum drv_br_port_attr {
+ DRV_BR_PORT_ATTR_PROXYARP,
+ DRV_BR_PORT_ATTR_HAIRPIN_MODE,
+};
+
+enum drv_br_net_param {
+ DRV_BR_NET_PARAM_GARP_ACCEPT,
+ DRV_BR_MULTICAST_SNOOPING,
+};
+
+struct drv_acs_params {
+ /* Selected mode (HOSTAPD_MODE_*) */
+ enum hostapd_hw_mode hw_mode;
+
+ /* Indicates whether HT is enabled */
+ int ht_enabled;
+
+ /* Indicates whether HT40 is enabled */
+ int ht40_enabled;
+
+ /* Indicates whether VHT is enabled */
+ int vht_enabled;
+
+ /* Configured ACS channel width */
+ u16 ch_width;
+
+ /* ACS channel list info */
+ unsigned int ch_list_len;
+ const u8 *ch_list;
+ const int *freq_list;
+};
+
+
+/**
+ * struct wpa_driver_ops - Driver interface API definition
+ *
+ * This structure defines the API that each driver interface needs to implement
+ * for core wpa_supplicant code. All driver specific functionality is captured
+ * in this wrapper.
+ */
+struct wpa_driver_ops {
+ /** Name of the driver interface */
+ const char *name;
+ /** One line description of the driver interface */
+ const char *desc;
+
+ /**
+ * get_bssid - Get the current BSSID
+ * @priv: private driver interface data
+ * @bssid: buffer for BSSID (ETH_ALEN = 6 bytes)
+ *
+ * Returns: 0 on success, -1 on failure
+ *
+ * Query kernel driver for the current BSSID and copy it to bssid.
+ * Setting bssid to 00:00:00:00:00:00 is recommended if the STA is not
+ * associated.
+ */
+ int (*get_bssid)(void *priv, u8 *bssid);
+
+ /**
+ * get_ssid - Get the current SSID
+ * @priv: private driver interface data
+ * @ssid: buffer for SSID (at least 32 bytes)
+ *
+ * Returns: Length of the SSID on success, -1 on failure
+ *
+ * Query kernel driver for the current SSID and copy it to ssid.
+ * Returning zero is recommended if the STA is not associated.
+ *
+ * Note: SSID is an array of octets, i.e., it is not nul terminated and
+ * can, at least in theory, contain control characters (including nul)
+ * and as such, should be processed as binary data, not a printable
+ * string.
+ */
+ int (*get_ssid)(void *priv, u8 *ssid);
+
+ /**
+ * set_key - Configure encryption key
+ * @ifname: Interface name (for multi-SSID/VLAN support)
+ * @priv: private driver interface data
+ * @alg: encryption algorithm (%WPA_ALG_NONE, %WPA_ALG_WEP,
+ * %WPA_ALG_TKIP, %WPA_ALG_CCMP, %WPA_ALG_IGTK, %WPA_ALG_PMK,
+ * %WPA_ALG_GCMP, %WPA_ALG_GCMP_256, %WPA_ALG_CCMP_256,
+ * %WPA_ALG_BIP_GMAC_128, %WPA_ALG_BIP_GMAC_256,
+ * %WPA_ALG_BIP_CMAC_256);
+ * %WPA_ALG_NONE clears the key.
+ * @addr: Address of the peer STA (BSSID of the current AP when setting
+ * pairwise key in station mode), ff:ff:ff:ff:ff:ff for
+ * broadcast keys, %NULL for default keys that are used both for
+ * broadcast and unicast; when clearing keys, %NULL is used to
+ * indicate that both the broadcast-only and default key of the
+ * specified key index is to be cleared
+ * @key_idx: key index (0..3), usually 0 for unicast keys; 0..4095 for
+ * IGTK
+ * @set_tx: configure this key as the default Tx key (only used when
+ * driver does not support separate unicast/individual key
+ * @seq: sequence number/packet number, seq_len octets, the next
+ * packet number to be used for in replay protection; configured
+ * for Rx keys (in most cases, this is only used with broadcast
+ * keys and set to zero for unicast keys); %NULL if not set
+ * @seq_len: length of the seq, depends on the algorithm:
+ * TKIP: 6 octets, CCMP/GCMP: 6 octets, IGTK: 6 octets
+ * @key: key buffer; TKIP: 16-byte temporal key, 8-byte Tx Mic key,
+ * 8-byte Rx Mic Key
+ * @key_len: length of the key buffer in octets (WEP: 5 or 13,
+ * TKIP: 32, CCMP/GCMP: 16, IGTK: 16)
+ *
+ * Returns: 0 on success, -1 on failure
+ *
+ * Configure the given key for the kernel driver. If the driver
+ * supports separate individual keys (4 default keys + 1 individual),
+ * addr can be used to determine whether the key is default or
+ * individual. If only 4 keys are supported, the default key with key
+ * index 0 is used as the individual key. STA must be configured to use
+ * it as the default Tx key (set_tx is set) and accept Rx for all the
+ * key indexes. In most cases, WPA uses only key indexes 1 and 2 for
+ * broadcast keys, so key index 0 is available for this kind of
+ * configuration.
+ *
+ * Please note that TKIP keys include separate TX and RX MIC keys and
+ * some drivers may expect them in different order than wpa_supplicant
+ * is using. If the TX/RX keys are swapped, all TKIP encrypted packets
+ * will trigger Michael MIC errors. This can be fixed by changing the
+ * order of MIC keys by swapping te bytes 16..23 and 24..31 of the key
+ * in driver_*.c set_key() implementation, see driver_ndis.c for an
+ * example on how this can be done.
+ */
+ int (*set_key)(const char *ifname, void *priv, enum wpa_alg alg,
+ const u8 *addr, int key_idx, int set_tx,
+ const u8 *seq, size_t seq_len,
+ const u8 *key, size_t key_len);
+
+ /**
+ * init - Initialize driver interface
+ * @ctx: context to be used when calling wpa_supplicant functions,
+ * e.g., wpa_supplicant_event()
+ * @ifname: interface name, e.g., wlan0
+ *
+ * Returns: Pointer to private data, %NULL on failure
+ *
+ * Initialize driver interface, including event processing for kernel
+ * driver events (e.g., associated, scan results, Michael MIC failure).
+ * This function can allocate a private configuration data area for
+ * @ctx, file descriptor, interface name, etc. information that may be
+ * needed in future driver operations. If this is not used, non-NULL
+ * value will need to be returned because %NULL is used to indicate
+ * failure. The returned value will be used as 'void *priv' data for
+ * all other driver_ops functions.
+ *
+ * The main event loop (eloop.c) of wpa_supplicant can be used to
+ * register callback for read sockets (eloop_register_read_sock()).
+ *
+ * See below for more information about events and
+ * wpa_supplicant_event() function.
+ */
+ void * (*init)(void *ctx, const char *ifname);
+
+ /**
+ * deinit - Deinitialize driver interface
+ * @priv: private driver interface data from init()
+ *
+ * Shut down driver interface and processing of driver events. Free
+ * private data buffer if one was allocated in init() handler.
+ */
+ void (*deinit)(void *priv);
+
+ /**
+ * set_param - Set driver configuration parameters
+ * @priv: private driver interface data from init()
+ * @param: driver specific configuration parameters
+ *
+ * Returns: 0 on success, -1 on failure
+ *
+ * Optional handler for notifying driver interface about configuration
+ * parameters (driver_param).
+ */
+ int (*set_param)(void *priv, const char *param);
+
+ /**
+ * set_countermeasures - Enable/disable TKIP countermeasures
+ * @priv: private driver interface data
+ * @enabled: 1 = countermeasures enabled, 0 = disabled
+ *
+ * Returns: 0 on success, -1 on failure
+ *
+ * Configure TKIP countermeasures. When these are enabled, the driver
+ * should drop all received and queued frames that are using TKIP.
+ */
+ int (*set_countermeasures)(void *priv, int enabled);
+
+ /**
+ * deauthenticate - Request driver to deauthenticate
+ * @priv: private driver interface data
+ * @addr: peer address (BSSID of the AP)
+ * @reason_code: 16-bit reason code to be sent in the deauthentication
+ * frame
+ *
+ * Returns: 0 on success, -1 on failure
+ */
+ int (*deauthenticate)(void *priv, const u8 *addr, int reason_code);
+
+ /**
+ * associate - Request driver to associate
+ * @priv: private driver interface data
+ * @params: association parameters
+ *
+ * Returns: 0 on success, -1 on failure
+ */
+ int (*associate)(void *priv,
+ struct wpa_driver_associate_params *params);
+
+ /**
+ * add_pmkid - Add PMKSA cache entry to the driver
+ * @priv: private driver interface data
+ * @bssid: BSSID for the PMKSA cache entry
+ * @pmkid: PMKID for the PMKSA cache entry
+ *
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function is called when a new PMK is received, as a result of
+ * either normal authentication or RSN pre-authentication.
+ *
+ * If the driver generates RSN IE, i.e., it does not use wpa_ie in
+ * associate(), add_pmkid() can be used to add new PMKSA cache entries
+ * in the driver. If the driver uses wpa_ie from wpa_supplicant, this
+ * driver_ops function does not need to be implemented. Likewise, if
+ * the driver does not support WPA, this function is not needed.
+ */
+ int (*add_pmkid)(void *priv, const u8 *bssid, const u8 *pmkid);
+
+ /**
+ * remove_pmkid - Remove PMKSA cache entry to the driver
+ * @priv: private driver interface data
+ * @bssid: BSSID for the PMKSA cache entry
+ * @pmkid: PMKID for the PMKSA cache entry
+ *
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function is called when the supplicant drops a PMKSA cache
+ * entry for any reason.
+ *
+ * If the driver generates RSN IE, i.e., it does not use wpa_ie in
+ * associate(), remove_pmkid() can be used to synchronize PMKSA caches
+ * between the driver and wpa_supplicant. If the driver uses wpa_ie
+ * from wpa_supplicant, this driver_ops function does not need to be
+ * implemented. Likewise, if the driver does not support WPA, this
+ * function is not needed.
+ */
+ int (*remove_pmkid)(void *priv, const u8 *bssid, const u8 *pmkid);
+
+ /**
+ * flush_pmkid - Flush PMKSA cache
+ * @priv: private driver interface data
+ *
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function is called when the supplicant drops all PMKSA cache
+ * entries for any reason.
+ *
+ * If the driver generates RSN IE, i.e., it does not use wpa_ie in
+ * associate(), remove_pmkid() can be used to synchronize PMKSA caches
+ * between the driver and wpa_supplicant. If the driver uses wpa_ie
+ * from wpa_supplicant, this driver_ops function does not need to be
+ * implemented. Likewise, if the driver does not support WPA, this
+ * function is not needed.
+ */
+ int (*flush_pmkid)(void *priv);
+
+ /**
+ * get_capa - Get driver capabilities
+ * @priv: private driver interface data
+ *
+ * Returns: 0 on success, -1 on failure
+ *
+ * Get driver/firmware/hardware capabilities.
+ */
+ int (*get_capa)(void *priv, struct wpa_driver_capa *capa);
+
+ /**
+ * poll - Poll driver for association information
+ * @priv: private driver interface data
+ *
+ * This is an option callback that can be used when the driver does not
+ * provide event mechanism for association events. This is called when
+ * receiving WPA EAPOL-Key messages that require association
+ * information. The driver interface is supposed to generate associnfo
+ * event before returning from this callback function. In addition, the
+ * driver interface should generate an association event after having
+ * sent out associnfo.
+ */
+ void (*poll)(void *priv);
+
+ /**
+ * get_ifname - Get interface name
+ * @priv: private driver interface data
+ *
+ * Returns: Pointer to the interface name. This can differ from the
+ * interface name used in init() call. Init() is called first.
+ *
+ * This optional function can be used to allow the driver interface to
+ * replace the interface name with something else, e.g., based on an
+ * interface mapping from a more descriptive name.
+ */
+ const char * (*get_ifname)(void *priv);
+
+ /**
+ * get_mac_addr - Get own MAC address
+ * @priv: private driver interface data
+ *
+ * Returns: Pointer to own MAC address or %NULL on failure
+ *
+ * This optional function can be used to get the own MAC address of the
+ * device from the driver interface code. This is only needed if the
+ * l2_packet implementation for the OS does not provide easy access to
+ * a MAC address. */
+ const u8 * (*get_mac_addr)(void *priv);
+
+ /**
+ * set_operstate - Sets device operating state to DORMANT or UP
+ * @priv: private driver interface data
+ * @state: 0 = dormant, 1 = up
+ * Returns: 0 on success, -1 on failure
+ *
+ * This is an optional function that can be used on operating systems
+ * that support a concept of controlling network device state from user
+ * space applications. This function, if set, gets called with
+ * state = 1 when authentication has been completed and with state = 0
+ * when connection is lost.
+ */
+ int (*set_operstate)(void *priv, int state);
+
+ /**
+ * mlme_setprotection - MLME-SETPROTECTION.request primitive
+ * @priv: Private driver interface data
+ * @addr: Address of the station for which to set protection (may be
+ * %NULL for group keys)
+ * @protect_type: MLME_SETPROTECTION_PROTECT_TYPE_*
+ * @key_type: MLME_SETPROTECTION_KEY_TYPE_*
+ * Returns: 0 on success, -1 on failure
+ *
+ * This is an optional function that can be used to set the driver to
+ * require protection for Tx and/or Rx frames. This uses the layer
+ * interface defined in IEEE 802.11i-2004 clause 10.3.22.1
+ * (MLME-SETPROTECTION.request). Many drivers do not use explicit
+ * set protection operation; instead, they set protection implicitly
+ * based on configured keys.
+ */
+ int (*mlme_setprotection)(void *priv, const u8 *addr, int protect_type,
+ int key_type);
+
+ /**
+ * get_hw_feature_data - Get hardware support data (channels and rates)
+ * @priv: Private driver interface data
+ * @num_modes: Variable for returning the number of returned modes
+ * flags: Variable for returning hardware feature flags
+ * Returns: Pointer to allocated hardware data on success or %NULL on
+ * failure. Caller is responsible for freeing this.
+ */
+ struct hostapd_hw_modes * (*get_hw_feature_data)(void *priv,
+ u16 *num_modes,
+ u16 *flags);
+
+ /**
+ * send_mlme - Send management frame from MLME
+ * @priv: Private driver interface data
+ * @data: IEEE 802.11 management frame with IEEE 802.11 header
+ * @data_len: Size of the management frame
+ * @noack: Do not wait for this frame to be acked (disable retries)
+ * @freq: Frequency (in MHz) to send the frame on, or 0 to let the
+ * driver decide
+ * Returns: 0 on success, -1 on failure
+ */
+ int (*send_mlme)(void *priv, const u8 *data, size_t data_len,
+ int noack, unsigned int freq);
+
+ /**
+ * update_ft_ies - Update FT (IEEE 802.11r) IEs
+ * @priv: Private driver interface data
+ * @md: Mobility domain (2 octets) (also included inside ies)
+ * @ies: FT IEs (MDIE, FTIE, ...) or %NULL to remove IEs
+ * @ies_len: Length of FT IEs in bytes
+ * Returns: 0 on success, -1 on failure
+ *
+ * The supplicant uses this callback to let the driver know that keying
+ * material for FT is available and that the driver can use the
+ * provided IEs in the next message in FT authentication sequence.
+ *
+ * This function is only needed for driver that support IEEE 802.11r
+ * (Fast BSS Transition).
+ */
+ int (*update_ft_ies)(void *priv, const u8 *md, const u8 *ies,
+ size_t ies_len);
+
+ /**
+ * get_scan_results2 - Fetch the latest scan results
+ * @priv: private driver interface data
+ *
+ * Returns: Allocated buffer of scan results (caller is responsible for
+ * freeing the data structure) on success, NULL on failure
+ */
+ struct wpa_scan_results * (*get_scan_results2)(void *priv);
+
+ /**
+ * set_country - Set country
+ * @priv: Private driver interface data
+ * @alpha2: country to which to switch to
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function is for drivers which support some form
+ * of setting a regulatory domain.
+ */
+ int (*set_country)(void *priv, const char *alpha2);
+
+ /**
+ * get_country - Get country
+ * @priv: Private driver interface data
+ * @alpha2: Buffer for returning country code (at least 3 octets)
+ * Returns: 0 on success, -1 on failure
+ */
+ int (*get_country)(void *priv, char *alpha2);
+
+ /**
+ * global_init - Global driver initialization
+ * Returns: Pointer to private data (global), %NULL on failure
+ *
+ * This optional function is called to initialize the driver wrapper
+ * for global data, i.e., data that applies to all interfaces. If this
+ * function is implemented, global_deinit() will also need to be
+ * implemented to free the private data. The driver will also likely
+ * use init2() function instead of init() to get the pointer to global
+ * data available to per-interface initializer.
+ */
+ void * (*global_init)(void);
+
+ /**
+ * global_deinit - Global driver deinitialization
+ * @priv: private driver global data from global_init()
+ *
+ * Terminate any global driver related functionality and free the
+ * global data structure.
+ */
+ void (*global_deinit)(void *priv);
+
+ /**
+ * init2 - Initialize driver interface (with global data)
+ * @ctx: context to be used when calling wpa_supplicant functions,
+ * e.g., wpa_supplicant_event()
+ * @ifname: interface name, e.g., wlan0
+ * @global_priv: private driver global data from global_init()
+ * Returns: Pointer to private data, %NULL on failure
+ *
+ * This function can be used instead of init() if the driver wrapper
+ * uses global data.
+ */
+ void * (*init2)(void *ctx, const char *ifname, void *global_priv);
+
+ /**
+ * get_interfaces - Get information about available interfaces
+ * @global_priv: private driver global data from global_init()
+ * Returns: Allocated buffer of interface information (caller is
+ * responsible for freeing the data structure) on success, NULL on
+ * failure
+ */
+ struct wpa_interface_info * (*get_interfaces)(void *global_priv);
+
+ /**
+ * scan2 - Request the driver to initiate scan
+ * @priv: private driver interface data
+ * @params: Scan parameters
+ *
+ * Returns: 0 on success, -1 on failure
+ *
+ * Once the scan results are ready, the driver should report scan
+ * results event for wpa_supplicant which will eventually request the
+ * results with wpa_driver_get_scan_results2().
+ */
+ int (*scan2)(void *priv, struct wpa_driver_scan_params *params);
+
+ /**
+ * authenticate - Request driver to authenticate
+ * @priv: private driver interface data
+ * @params: authentication parameters
+ * Returns: 0 on success, -1 on failure
+ *
+ * This is an optional function that can be used with drivers that
+ * support separate authentication and association steps, i.e., when
+ * wpa_supplicant can act as the SME. If not implemented, associate()
+ * function is expected to take care of IEEE 802.11 authentication,
+ * too.
+ */
+ int (*authenticate)(void *priv,
+ struct wpa_driver_auth_params *params);
+
+ /**
+ * set_ap - Set Beacon and Probe Response information for AP mode
+ * @priv: Private driver interface data
+ * @params: Parameters to use in AP mode
+ *
+ * This function is used to configure Beacon template and/or extra IEs
+ * to add for Beacon and Probe Response frames for the driver in
+ * AP mode. The driver is responsible for building the full Beacon
+ * frame by concatenating the head part with TIM IE generated by the
+ * driver/firmware and finishing with the tail part. Depending on the
+ * driver architectue, this can be done either by using the full
+ * template or the set of additional IEs (e.g., WPS and P2P IE).
+ * Similarly, Probe Response processing depends on the driver design.
+ * If the driver (or firmware) takes care of replying to Probe Request
+ * frames, the extra IEs provided here needs to be added to the Probe
+ * Response frames.
+ *
+ * Returns: 0 on success, -1 on failure
+ */
+ int (*set_ap)(void *priv, struct wpa_driver_ap_params *params);
+
+ /**
+ * set_acl - Set ACL in AP mode
+ * @priv: Private driver interface data
+ * @params: Parameters to configure ACL
+ * Returns: 0 on success, -1 on failure
+ *
+ * This is used only for the drivers which support MAC address ACL.
+ */
+ int (*set_acl)(void *priv, struct hostapd_acl_params *params);
+
+ /**
+ * hapd_init - Initialize driver interface (hostapd only)
+ * @hapd: Pointer to hostapd context
+ * @params: Configuration for the driver wrapper
+ * Returns: Pointer to private data, %NULL on failure
+ *
+ * This function is used instead of init() or init2() when the driver
+ * wrapper is used with hostapd.
+ */
+ void * (*hapd_init)(struct hostapd_data *hapd,
+ struct wpa_init_params *params);
+
+ /**
+ * hapd_deinit - Deinitialize driver interface (hostapd only)
+ * @priv: Private driver interface data from hapd_init()
+ */
+ void (*hapd_deinit)(void *priv);
+
+ /**
+ * set_ieee8021x - Enable/disable IEEE 802.1X support (AP only)
+ * @priv: Private driver interface data
+ * @params: BSS parameters
+ * Returns: 0 on success, -1 on failure
+ *
+ * This is an optional function to configure the kernel driver to
+ * enable/disable IEEE 802.1X support and set WPA/WPA2 parameters. This
+ * can be left undefined (set to %NULL) if IEEE 802.1X support is
+ * always enabled and the driver uses set_ap() to set WPA/RSN IE
+ * for Beacon frames.
+ *
+ * DEPRECATED - use set_ap() instead
+ */
+ int (*set_ieee8021x)(void *priv, struct wpa_bss_params *params);
+
+ /**
+ * set_privacy - Enable/disable privacy (AP only)
+ * @priv: Private driver interface data
+ * @enabled: 1 = privacy enabled, 0 = disabled
+ * Returns: 0 on success, -1 on failure
+ *
+ * This is an optional function to configure privacy field in the
+ * kernel driver for Beacon frames. This can be left undefined (set to
+ * %NULL) if the driver uses the Beacon template from set_ap().
+ *
+ * DEPRECATED - use set_ap() instead
+ */
+ int (*set_privacy)(void *priv, int enabled);
+
+ /**
+ * get_seqnum - Fetch the current TSC/packet number (AP only)
+ * @ifname: The interface name (main or virtual)
+ * @priv: Private driver interface data
+ * @addr: MAC address of the station or %NULL for group keys
+ * @idx: Key index
+ * @seq: Buffer for returning the latest used TSC/packet number
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function is used to fetch the last used TSC/packet number for
+ * a TKIP, CCMP, GCMP, or BIP/IGTK key. It is mainly used with group
+ * keys, so there is no strict requirement on implementing support for
+ * unicast keys (i.e., addr != %NULL).
+ */
+ int (*get_seqnum)(const char *ifname, void *priv, const u8 *addr,
+ int idx, u8 *seq);
+
+ /**
+ * flush - Flush all association stations (AP only)
+ * @priv: Private driver interface data
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function requests the driver to disassociate all associated
+ * stations. This function does not need to be implemented if the
+ * driver does not process association frames internally.
+ */
+ int (*flush)(void *priv);
+
+ /**
+ * set_generic_elem - Add IEs into Beacon/Probe Response frames (AP)
+ * @priv: Private driver interface data
+ * @elem: Information elements
+ * @elem_len: Length of the elem buffer in octets
+ * Returns: 0 on success, -1 on failure
+ *
+ * This is an optional function to add information elements in the
+ * kernel driver for Beacon and Probe Response frames. This can be left
+ * undefined (set to %NULL) if the driver uses the Beacon template from
+ * set_ap().
+ *
+ * DEPRECATED - use set_ap() instead
+ */
+ int (*set_generic_elem)(void *priv, const u8 *elem, size_t elem_len);
+
+ /**
+ * read_sta_data - Fetch station data
+ * @priv: Private driver interface data
+ * @data: Buffer for returning station information
+ * @addr: MAC address of the station
+ * Returns: 0 on success, -1 on failure
+ */
+ int (*read_sta_data)(void *priv, struct hostap_sta_driver_data *data,
+ const u8 *addr);
+
+ /**
+ * hapd_send_eapol - Send an EAPOL packet (AP only)
+ * @priv: private driver interface data
+ * @addr: Destination MAC address
+ * @data: EAPOL packet starting with IEEE 802.1X header
+ * @data_len: Length of the EAPOL packet in octets
+ * @encrypt: Whether the frame should be encrypted
+ * @own_addr: Source MAC address
+ * @flags: WPA_STA_* flags for the destination station
+ *
+ * Returns: 0 on success, -1 on failure
+ */
+ int (*hapd_send_eapol)(void *priv, const u8 *addr, const u8 *data,
+ size_t data_len, int encrypt,
+ const u8 *own_addr, u32 flags);
+
+ /**
+ * sta_deauth - Deauthenticate a station (AP only)
+ * @priv: Private driver interface data
+ * @own_addr: Source address and BSSID for the Deauthentication frame
+ * @addr: MAC address of the station to deauthenticate
+ * @reason: Reason code for the Deauthentiation frame
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function requests a specific station to be deauthenticated and
+ * a Deauthentication frame to be sent to it.
+ */
+ int (*sta_deauth)(void *priv, const u8 *own_addr, const u8 *addr,
+ int reason);
+
+ /**
+ * sta_disassoc - Disassociate a station (AP only)
+ * @priv: Private driver interface data
+ * @own_addr: Source address and BSSID for the Disassociation frame
+ * @addr: MAC address of the station to disassociate
+ * @reason: Reason code for the Disassociation frame
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function requests a specific station to be disassociated and
+ * a Disassociation frame to be sent to it.
+ */
+ int (*sta_disassoc)(void *priv, const u8 *own_addr, const u8 *addr,
+ int reason);
+
+ /**
+ * sta_remove - Remove a station entry (AP only)
+ * @priv: Private driver interface data
+ * @addr: MAC address of the station to be removed
+ * Returns: 0 on success, -1 on failure
+ */
+ int (*sta_remove)(void *priv, const u8 *addr);
+
+ /**
+ * hapd_get_ssid - Get the current SSID (AP only)
+ * @priv: Private driver interface data
+ * @buf: Buffer for returning the SSID
+ * @len: Maximum length of the buffer
+ * Returns: Length of the SSID on success, -1 on failure
+ *
+ * This function need not be implemented if the driver uses Beacon
+ * template from set_ap() and does not reply to Probe Request frames.
+ */
+ int (*hapd_get_ssid)(void *priv, u8 *buf, int len);
+
+ /**
+ * hapd_set_ssid - Set SSID (AP only)
+ * @priv: Private driver interface data
+ * @buf: SSID
+ * @len: Length of the SSID in octets
+ * Returns: 0 on success, -1 on failure
+ *
+ * DEPRECATED - use set_ap() instead
+ */
+ int (*hapd_set_ssid)(void *priv, const u8 *buf, int len);
+
+ /**
+ * hapd_set_countermeasures - Enable/disable TKIP countermeasures (AP)
+ * @priv: Private driver interface data
+ * @enabled: 1 = countermeasures enabled, 0 = disabled
+ * Returns: 0 on success, -1 on failure
+ *
+ * This need not be implemented if the driver does not take care of
+ * association processing.
+ */
+ int (*hapd_set_countermeasures)(void *priv, int enabled);
+
+ /**
+ * sta_add - Add a station entry
+ * @priv: Private driver interface data
+ * @params: Station parameters
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function is used to add a station entry to the driver once the
+ * station has completed association. This is only used if the driver
+ * does not take care of association processing.
+ *
+ * With TDLS, this function is also used to add or set (params->set 1)
+ * TDLS peer entries.
+ */
+ int (*sta_add)(void *priv, struct hostapd_sta_add_params *params);
+
+ /**
+ * get_inact_sec - Get station inactivity duration (AP only)
+ * @priv: Private driver interface data
+ * @addr: Station address
+ * Returns: Number of seconds station has been inactive, -1 on failure
+ */
+ int (*get_inact_sec)(void *priv, const u8 *addr);
+
+ /**
+ * sta_clear_stats - Clear station statistics (AP only)
+ * @priv: Private driver interface data
+ * @addr: Station address
+ * Returns: 0 on success, -1 on failure
+ */
+ int (*sta_clear_stats)(void *priv, const u8 *addr);
+
+ /**
+ * set_freq - Set channel/frequency (AP only)
+ * @priv: Private driver interface data
+ * @freq: Channel parameters
+ * Returns: 0 on success, -1 on failure
+ */
+ int (*set_freq)(void *priv, struct hostapd_freq_params *freq);
+
+ /**
+ * set_rts - Set RTS threshold
+ * @priv: Private driver interface data
+ * @rts: RTS threshold in octets
+ * Returns: 0 on success, -1 on failure
+ */
+ int (*set_rts)(void *priv, int rts);
+
+ /**
+ * set_frag - Set fragmentation threshold
+ * @priv: Private driver interface data
+ * @frag: Fragmentation threshold in octets
+ * Returns: 0 on success, -1 on failure
+ */
+ int (*set_frag)(void *priv, int frag);
+
+ /**
+ * sta_set_flags - Set station flags (AP only)
+ * @priv: Private driver interface data
+ * @addr: Station address
+ * @total_flags: Bitmap of all WPA_STA_* flags currently set
+ * @flags_or: Bitmap of WPA_STA_* flags to add
+ * @flags_and: Bitmap of WPA_STA_* flags to us as a mask
+ * Returns: 0 on success, -1 on failure
+ */
+ int (*sta_set_flags)(void *priv, const u8 *addr,
+ unsigned int total_flags, unsigned int flags_or,
+ unsigned int flags_and);
+
+ /**
+ * set_tx_queue_params - Set TX queue parameters
+ * @priv: Private driver interface data
+ * @queue: Queue number (0 = VO, 1 = VI, 2 = BE, 3 = BK)
+ * @aifs: AIFS
+ * @cw_min: cwMin
+ * @cw_max: cwMax
+ * @burst_time: Maximum length for bursting in 0.1 msec units
+ */
+ int (*set_tx_queue_params)(void *priv, int queue, int aifs, int cw_min,
+ int cw_max, int burst_time);
+
+ /**
+ * if_add - Add a virtual interface
+ * @priv: Private driver interface data
+ * @type: Interface type
+ * @ifname: Interface name for the new virtual interface
+ * @addr: Local address to use for the interface or %NULL to use the
+ * parent interface address
+ * @bss_ctx: BSS context for %WPA_IF_AP_BSS interfaces
+ * @drv_priv: Pointer for overwriting the driver context or %NULL if
+ * not allowed (applies only to %WPA_IF_AP_BSS type)
+ * @force_ifname: Buffer for returning an interface name that the
+ * driver ended up using if it differs from the requested ifname
+ * @if_addr: Buffer for returning the allocated interface address
+ * (this may differ from the requested addr if the driver cannot
+ * change interface address)
+ * @bridge: Bridge interface to use or %NULL if no bridge configured
+ * @use_existing: Whether to allow existing interface to be used
+ * Returns: 0 on success, -1 on failure
+ */
+ int (*if_add)(void *priv, enum wpa_driver_if_type type,
+ const char *ifname, const u8 *addr, void *bss_ctx,
+ void **drv_priv, char *force_ifname, u8 *if_addr,
+ const char *bridge, int use_existing);
+
+ /**
+ * if_remove - Remove a virtual interface
+ * @priv: Private driver interface data
+ * @type: Interface type
+ * @ifname: Interface name of the virtual interface to be removed
+ * Returns: 0 on success, -1 on failure
+ */
+ int (*if_remove)(void *priv, enum wpa_driver_if_type type,
+ const char *ifname);
+
+ /**
+ * set_sta_vlan - Bind a station into a specific interface (AP only)
+ * @priv: Private driver interface data
+ * @ifname: Interface (main or virtual BSS or VLAN)
+ * @addr: MAC address of the associated station
+ * @vlan_id: VLAN ID
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function is used to bind a station to a specific virtual
+ * interface. It is only used if when virtual interfaces are supported,
+ * e.g., to assign stations to different VLAN interfaces based on
+ * information from a RADIUS server. This allows separate broadcast
+ * domains to be used with a single BSS.
+ */
+ int (*set_sta_vlan)(void *priv, const u8 *addr, const char *ifname,
+ int vlan_id);
+
+ /**
+ * commit - Optional commit changes handler (AP only)
+ * @priv: driver private data
+ * Returns: 0 on success, -1 on failure
+ *
+ * This optional handler function can be registered if the driver
+ * interface implementation needs to commit changes (e.g., by setting
+ * network interface up) at the end of initial configuration. If set,
+ * this handler will be called after initial setup has been completed.
+ */
+ int (*commit)(void *priv);
+
+ /**
+ * send_ether - Send an ethernet packet (AP only)
+ * @priv: private driver interface data
+ * @dst: Destination MAC address
+ * @src: Source MAC address
+ * @proto: Ethertype
+ * @data: EAPOL packet starting with IEEE 802.1X header
+ * @data_len: Length of the EAPOL packet in octets
+ * Returns: 0 on success, -1 on failure
+ */
+ int (*send_ether)(void *priv, const u8 *dst, const u8 *src, u16 proto,
+ const u8 *data, size_t data_len);
+
+ /**
+ * set_radius_acl_auth - Notification of RADIUS ACL change
+ * @priv: Private driver interface data
+ * @mac: MAC address of the station
+ * @accepted: Whether the station was accepted
+ * @session_timeout: Session timeout for the station
+ * Returns: 0 on success, -1 on failure
+ */
+ int (*set_radius_acl_auth)(void *priv, const u8 *mac, int accepted,
+ u32 session_timeout);
+
+ /**
+ * set_radius_acl_expire - Notification of RADIUS ACL expiration
+ * @priv: Private driver interface data
+ * @mac: MAC address of the station
+ * Returns: 0 on success, -1 on failure
+ */
+ int (*set_radius_acl_expire)(void *priv, const u8 *mac);
+
+ /**
+ * set_ap_wps_ie - Add WPS IE(s) into Beacon/Probe Response frames (AP)
+ * @priv: Private driver interface data
+ * @beacon: WPS IE(s) for Beacon frames or %NULL to remove extra IE(s)
+ * @proberesp: WPS IE(s) for Probe Response frames or %NULL to remove
+ * extra IE(s)
+ * @assocresp: WPS IE(s) for (Re)Association Response frames or %NULL
+ * to remove extra IE(s)
+ * Returns: 0 on success, -1 on failure
+ *
+ * This is an optional function to add WPS IE in the kernel driver for
+ * Beacon and Probe Response frames. This can be left undefined (set
+ * to %NULL) if the driver uses the Beacon template from set_ap()
+ * and does not process Probe Request frames. If the driver takes care
+ * of (Re)Association frame processing, the assocresp buffer includes
+ * WPS IE(s) that need to be added to (Re)Association Response frames
+ * whenever a (Re)Association Request frame indicated use of WPS.
+ *
+ * This will also be used to add P2P IE(s) into Beacon/Probe Response
+ * frames when operating as a GO. The driver is responsible for adding
+ * timing related attributes (e.g., NoA) in addition to the IEs
+ * included here by appending them after these buffers. This call is
+ * also used to provide Probe Response IEs for P2P Listen state
+ * operations for drivers that generate the Probe Response frames
+ * internally.
+ *
+ * DEPRECATED - use set_ap() instead
+ */
+ int (*set_ap_wps_ie)(void *priv, const struct wpabuf *beacon,
+ const struct wpabuf *proberesp,
+ const struct wpabuf *assocresp);
+
+ /**
+ * set_supp_port - Set IEEE 802.1X Supplicant Port status
+ * @priv: Private driver interface data
+ * @authorized: Whether the port is authorized
+ * Returns: 0 on success, -1 on failure
+ */
+ int (*set_supp_port)(void *priv, int authorized);
+
+ /**
+ * set_wds_sta - Bind a station into a 4-address WDS (AP only)
+ * @priv: Private driver interface data
+ * @addr: MAC address of the associated station
+ * @aid: Association ID
+ * @val: 1 = bind to 4-address WDS; 0 = unbind
+ * @bridge_ifname: Bridge interface to use for the WDS station or %NULL
+ * to indicate that bridge is not to be used
+ * @ifname_wds: Buffer to return the interface name for the new WDS
+ * station or %NULL to indicate name is not returned.
+ * Returns: 0 on success, -1 on failure
+ */
+ int (*set_wds_sta)(void *priv, const u8 *addr, int aid, int val,
+ const char *bridge_ifname, char *ifname_wds);
+
+ /**
+ * send_action - Transmit an Action frame
+ * @priv: Private driver interface data
+ * @freq: Frequency (in MHz) of the channel
+ * @wait: Time to wait off-channel for a response (in ms), or zero
+ * @dst: Destination MAC address (Address 1)
+ * @src: Source MAC address (Address 2)
+ * @bssid: BSSID (Address 3)
+ * @data: Frame body
+ * @data_len: data length in octets
+ @ @no_cck: Whether CCK rates must not be used to transmit this frame
+ * Returns: 0 on success, -1 on failure
+ *
+ * This command can be used to request the driver to transmit an action
+ * frame to the specified destination.
+ *
+ * If the %WPA_DRIVER_FLAGS_OFFCHANNEL_TX flag is set, the frame will
+ * be transmitted on the given channel and the device will wait for a
+ * response on that channel for the given wait time.
+ *
+ * If the flag is not set, the wait time will be ignored. In this case,
+ * if a remain-on-channel duration is in progress, the frame must be
+ * transmitted on that channel; alternatively the frame may be sent on
+ * the current operational channel (if in associated state in station
+ * mode or while operating as an AP.)
+ */
+ int (*send_action)(void *priv, unsigned int freq, unsigned int wait,
+ const u8 *dst, const u8 *src, const u8 *bssid,
+ const u8 *data, size_t data_len, int no_cck);
+
+ /**
+ * send_action_cancel_wait - Cancel action frame TX wait
+ * @priv: Private driver interface data
+ *
+ * This command cancels the wait time associated with sending an action
+ * frame. It is only available when %WPA_DRIVER_FLAGS_OFFCHANNEL_TX is
+ * set in the driver flags.
+ */
+ void (*send_action_cancel_wait)(void *priv);
+
+ /**
+ * remain_on_channel - Remain awake on a channel
+ * @priv: Private driver interface data
+ * @freq: Frequency (in MHz) of the channel
+ * @duration: Duration in milliseconds
+ * Returns: 0 on success, -1 on failure
+ *
+ * This command is used to request the driver to remain awake on the
+ * specified channel for the specified duration and report received
+ * Action frames with EVENT_RX_MGMT events. Optionally, received
+ * Probe Request frames may also be requested to be reported by calling
+ * probe_req_report(). These will be reported with EVENT_RX_PROBE_REQ.
+ *
+ * The driver may not be at the requested channel when this function
+ * returns, i.e., the return code is only indicating whether the
+ * request was accepted. The caller will need to wait until the
+ * EVENT_REMAIN_ON_CHANNEL event indicates that the driver has
+ * completed the channel change. This may take some time due to other
+ * need for the radio and the caller should be prepared to timing out
+ * its wait since there are no guarantees on when this request can be
+ * executed.
+ */
+ int (*remain_on_channel)(void *priv, unsigned int freq,
+ unsigned int duration);
+
+ /**
+ * cancel_remain_on_channel - Cancel remain-on-channel operation
+ * @priv: Private driver interface data
+ *
+ * This command can be used to cancel a remain-on-channel operation
+ * before its originally requested duration has passed. This could be
+ * used, e.g., when remain_on_channel() is used to request extra time
+ * to receive a response to an Action frame and the response is
+ * received when there is still unneeded time remaining on the
+ * remain-on-channel operation.
+ */
+ int (*cancel_remain_on_channel)(void *priv);
+
+ /**
+ * probe_req_report - Request Probe Request frames to be indicated
+ * @priv: Private driver interface data
+ * @report: Whether to report received Probe Request frames
+ * Returns: 0 on success, -1 on failure (or if not supported)
+ *
+ * This command can be used to request the driver to indicate when
+ * Probe Request frames are received with EVENT_RX_PROBE_REQ events.
+ * Since this operation may require extra resources, e.g., due to less
+ * optimal hardware/firmware RX filtering, many drivers may disable
+ * Probe Request reporting at least in station mode. This command is
+ * used to notify the driver when the Probe Request frames need to be
+ * reported, e.g., during remain-on-channel operations.
+ */
+ int (*probe_req_report)(void *priv, int report);
+
+ /**
+ * deinit_ap - Deinitialize AP mode
+ * @priv: Private driver interface data
+ * Returns: 0 on success, -1 on failure (or if not supported)
+ *
+ * This optional function can be used to disable AP mode related
+ * configuration. If the interface was not dynamically added,
+ * change the driver mode to station mode to allow normal station
+ * operations like scanning to be completed.
+ */
+ int (*deinit_ap)(void *priv);
+
+ /**
+ * deinit_p2p_cli - Deinitialize P2P client mode
+ * @priv: Private driver interface data
+ * Returns: 0 on success, -1 on failure (or if not supported)
+ *
+ * This optional function can be used to disable P2P client mode. If the
+ * interface was not dynamically added, change the interface type back
+ * to station mode.
+ */
+ int (*deinit_p2p_cli)(void *priv);
+
+ /**
+ * suspend - Notification on system suspend/hibernate event
+ * @priv: Private driver interface data
+ */
+ void (*suspend)(void *priv);
+
+ /**
+ * resume - Notification on system resume/thaw event
+ * @priv: Private driver interface data
+ */
+ void (*resume)(void *priv);
+
+ /**
+ * signal_monitor - Set signal monitoring parameters
+ * @priv: Private driver interface data
+ * @threshold: Threshold value for signal change events; 0 = disabled
+ * @hysteresis: Minimum change in signal strength before indicating a
+ * new event
+ * Returns: 0 on success, -1 on failure (or if not supported)
+ *
+ * This function can be used to configure monitoring of signal strength
+ * with the current AP. Whenever signal strength drops below the
+ * %threshold value or increases above it, EVENT_SIGNAL_CHANGE event
+ * should be generated assuming the signal strength has changed at
+ * least %hysteresis from the previously indicated signal change event.
+ */
+ int (*signal_monitor)(void *priv, int threshold, int hysteresis);
+
+ /**
+ * send_frame - Send IEEE 802.11 frame (testing use only)
+ * @priv: Private driver interface data
+ * @data: IEEE 802.11 frame with IEEE 802.11 header
+ * @data_len: Size of the frame
+ * @encrypt: Whether to encrypt the frame (if keys are set)
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function is only used for debugging purposes and is not
+ * required to be implemented for normal operations.
+ */
+ int (*send_frame)(void *priv, const u8 *data, size_t data_len,
+ int encrypt);
+
+ /**
+ * get_noa - Get current Notice of Absence attribute payload
+ * @priv: Private driver interface data
+ * @buf: Buffer for returning NoA
+ * @buf_len: Buffer length in octets
+ * Returns: Number of octets used in buf, 0 to indicate no NoA is being
+ * advertized, or -1 on failure
+ *
+ * This function is used to fetch the current Notice of Absence
+ * attribute value from GO.
+ */
+ int (*get_noa)(void *priv, u8 *buf, size_t buf_len);
+
+ /**
+ * set_noa - Set Notice of Absence parameters for GO (testing)
+ * @priv: Private driver interface data
+ * @count: Count
+ * @start: Start time in ms from next TBTT
+ * @duration: Duration in ms
+ * Returns: 0 on success or -1 on failure
+ *
+ * This function is used to set Notice of Absence parameters for GO. It
+ * is used only for testing. To disable NoA, all parameters are set to
+ * 0.
+ */
+ int (*set_noa)(void *priv, u8 count, int start, int duration);
+
+ /**
+ * set_p2p_powersave - Set P2P power save options
+ * @priv: Private driver interface data
+ * @legacy_ps: 0 = disable, 1 = enable, 2 = maximum PS, -1 = no change
+ * @opp_ps: 0 = disable, 1 = enable, -1 = no change
+ * @ctwindow: 0.. = change (msec), -1 = no change
+ * Returns: 0 on success or -1 on failure
+ */
+ int (*set_p2p_powersave)(void *priv, int legacy_ps, int opp_ps,
+ int ctwindow);
+
+ /**
+ * ampdu - Enable/disable aggregation
+ * @priv: Private driver interface data
+ * @ampdu: 1/0 = enable/disable A-MPDU aggregation
+ * Returns: 0 on success or -1 on failure
+ */
+ int (*ampdu)(void *priv, int ampdu);
+
+ /**
+ * get_radio_name - Get physical radio name for the device
+ * @priv: Private driver interface data
+ * Returns: Radio name or %NULL if not known
+ *
+ * The returned data must not be modified by the caller. It is assumed
+ * that any interface that has the same radio name as another is
+ * sharing the same physical radio. This information can be used to
+ * share scan results etc. information between the virtual interfaces
+ * to speed up various operations.
+ */
+ const char * (*get_radio_name)(void *priv);
+
+ /**
+ * send_tdls_mgmt - for sending TDLS management packets
+ * @priv: private driver interface data
+ * @dst: Destination (peer) MAC address
+ * @action_code: TDLS action code for the mssage
+ * @dialog_token: Dialog Token to use in the message (if needed)
+ * @status_code: Status Code or Reason Code to use (if needed)
+ * @peer_capab: TDLS peer capability (TDLS_PEER_* bitfield)
+ * @initiator: Is the current end the TDLS link initiator
+ * @buf: TDLS IEs to add to the message
+ * @len: Length of buf in octets
+ * Returns: 0 on success, negative (<0) on failure
+ *
+ * This optional function can be used to send packet to driver which is
+ * responsible for receiving and sending all TDLS packets.
+ */
+ int (*send_tdls_mgmt)(void *priv, const u8 *dst, u8 action_code,
+ u8 dialog_token, u16 status_code, u32 peer_capab,
+ int initiator, const u8 *buf, size_t len);
+
+ /**
+ * tdls_oper - Ask the driver to perform high-level TDLS operations
+ * @priv: Private driver interface data
+ * @oper: TDLS high-level operation. See %enum tdls_oper
+ * @peer: Destination (peer) MAC address
+ * Returns: 0 on success, negative (<0) on failure
+ *
+ * This optional function can be used to send high-level TDLS commands
+ * to the driver.
+ */
+ int (*tdls_oper)(void *priv, enum tdls_oper oper, const u8 *peer);
+
+ /**
+ * wnm_oper - Notify driver of the WNM frame reception
+ * @priv: Private driver interface data
+ * @oper: WNM operation. See %enum wnm_oper
+ * @peer: Destination (peer) MAC address
+ * @buf: Buffer for the driver to fill in (for getting IE)
+ * @buf_len: Return the len of buf
+ * Returns: 0 on success, negative (<0) on failure
+ */
+ int (*wnm_oper)(void *priv, enum wnm_oper oper, const u8 *peer,
+ u8 *buf, u16 *buf_len);
+
+ /**
+ * set_qos_map - Set QoS Map
+ * @priv: Private driver interface data
+ * @qos_map_set: QoS Map
+ * @qos_map_set_len: Length of QoS Map
+ */
+ int (*set_qos_map)(void *priv, const u8 *qos_map_set,
+ u8 qos_map_set_len);
+
+ /**
+ * br_add_ip_neigh - Add a neigh to the bridge ip neigh table
+ * @priv: Private driver interface data
+ * @version: IP version of the IP address, 4 or 6
+ * @ipaddr: IP address for the neigh entry
+ * @prefixlen: IP address prefix length
+ * @addr: Corresponding MAC address
+ * Returns: 0 on success, negative (<0) on failure
+ */
+ int (*br_add_ip_neigh)(void *priv, u8 version, const u8 *ipaddr,
+ int prefixlen, const u8 *addr);
+
+ /**
+ * br_delete_ip_neigh - Remove a neigh from the bridge ip neigh table
+ * @priv: Private driver interface data
+ * @version: IP version of the IP address, 4 or 6
+ * @ipaddr: IP address for the neigh entry
+ * Returns: 0 on success, negative (<0) on failure
+ */
+ int (*br_delete_ip_neigh)(void *priv, u8 version, const u8 *ipaddr);
+
+ /**
+ * br_port_set_attr - Set a bridge port attribute
+ * @attr: Bridge port attribute to set
+ * @val: Value to be set
+ * Returns: 0 on success, negative (<0) on failure
+ */
+ int (*br_port_set_attr)(void *priv, enum drv_br_port_attr attr,
+ unsigned int val);
+
+ /**
+ * br_port_set_attr - Set a bridge network parameter
+ * @param: Bridge parameter to set
+ * @val: Value to be set
+ * Returns: 0 on success, negative (<0) on failure
+ */
+ int (*br_set_net_param)(void *priv, enum drv_br_net_param param,
+ unsigned int val);
+
+ /**
+ * set_wowlan - Set wake-on-wireless triggers
+ * @priv: Private driver interface data
+ * @triggers: wowlan triggers
+ */
+ int (*set_wowlan)(void *priv, const struct wowlan_triggers *triggers);
+
+ /**
+ * signal_poll - Get current connection information
+ * @priv: Private driver interface data
+ * @signal_info: Connection info structure
+ */
+ int (*signal_poll)(void *priv, struct wpa_signal_info *signal_info);
+
+ /**
+ * set_authmode - Set authentication algorithm(s) for static WEP
+ * @priv: Private driver interface data
+ * @authmode: 1=Open System, 2=Shared Key, 3=both
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function can be used to set authentication algorithms for AP
+ * mode when static WEP is used. If the driver uses user space MLME/SME
+ * implementation, there is no need to implement this function.
+ *
+ * DEPRECATED - use set_ap() instead
+ */
+ int (*set_authmode)(void *priv, int authmode);
+
+#ifdef ANDROID
+ /**
+ * driver_cmd - Execute driver-specific command
+ * @priv: Private driver interface data
+ * @cmd: Command to execute
+ * @buf: Return buffer
+ * @buf_len: Buffer length
+ * Returns: 0 on success, -1 on failure
+ */
+ int (*driver_cmd)(void *priv, char *cmd, char *buf, size_t buf_len);
+#endif /* ANDROID */
+
+ /**
+ * vendor_cmd - Execute vendor specific command
+ * @priv: Private driver interface data
+ * @vendor_id: Vendor id
+ * @subcmd: Vendor command id
+ * @data: Vendor command parameters (%NULL if no parameters)
+ * @data_len: Data length
+ * @buf: Return buffer (%NULL to ignore reply)
+ * Returns: 0 on success, negative (<0) on failure
+ *
+ * This function handles vendor specific commands that are passed to
+ * the driver/device. The command is identified by vendor id and
+ * command id. Parameters can be passed as argument to the command
+ * in the data buffer. Reply (if any) will be filled in the supplied
+ * return buffer.
+ *
+ * The exact driver behavior is driver interface and vendor specific. As
+ * an example, this will be converted to a vendor specific cfg80211
+ * command in case of the nl80211 driver interface.
+ */
+ int (*vendor_cmd)(void *priv, unsigned int vendor_id,
+ unsigned int subcmd, const u8 *data, size_t data_len,
+ struct wpabuf *buf);
+
+ /**
+ * set_rekey_info - Set rekey information
+ * @priv: Private driver interface data
+ * @kek: Current KEK
+ * @kek_len: KEK length in octets
+ * @kck: Current KCK
+ * @kck_len: KCK length in octets
+ * @replay_ctr: Current EAPOL-Key Replay Counter
+ *
+ * This optional function can be used to provide information for the
+ * driver/firmware to process EAPOL-Key frames in Group Key Handshake
+ * while the host (including wpa_supplicant) is sleeping.
+ */
+ void (*set_rekey_info)(void *priv, const u8 *kek, size_t kek_len,
+ const u8 *kck, size_t kck_len,
+ const u8 *replay_ctr);
+
+ /**
+ * sta_assoc - Station association indication
+ * @priv: Private driver interface data
+ * @own_addr: Source address and BSSID for association frame
+ * @addr: MAC address of the station to associate
+ * @reassoc: flag to indicate re-association
+ * @status: association response status code
+ * @ie: assoc response ie buffer
+ * @len: ie buffer length
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function indicates the driver to send (Re)Association
+ * Response frame to the station.
+ */
+ int (*sta_assoc)(void *priv, const u8 *own_addr, const u8 *addr,
+ int reassoc, u16 status, const u8 *ie, size_t len);
+
+ /**
+ * sta_auth - Station authentication indication
+ * @priv: Private driver interface data
+ * @own_addr: Source address and BSSID for authentication frame
+ * @addr: MAC address of the station to associate
+ * @seq: authentication sequence number
+ * @status: authentication response status code
+ * @ie: authentication frame ie buffer
+ * @len: ie buffer length
+ *
+ * This function indicates the driver to send Authentication frame
+ * to the station.
+ */
+ int (*sta_auth)(void *priv, const u8 *own_addr, const u8 *addr,
+ u16 seq, u16 status, const u8 *ie, size_t len);
+
+ /**
+ * add_tspec - Add traffic stream
+ * @priv: Private driver interface data
+ * @addr: MAC address of the station to associate
+ * @tspec_ie: tspec ie buffer
+ * @tspec_ielen: tspec ie length
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function adds the traffic steam for the station
+ * and fills the medium_time in tspec_ie.
+ */
+ int (*add_tspec)(void *priv, const u8 *addr, u8 *tspec_ie,
+ size_t tspec_ielen);
+
+ /**
+ * add_sta_node - Add a station node in the driver
+ * @priv: Private driver interface data
+ * @addr: MAC address of the station to add
+ * @auth_alg: authentication algorithm used by the station
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function adds the station node in the driver, when
+ * the station gets added by FT-over-DS.
+ */
+ int (*add_sta_node)(void *priv, const u8 *addr, u16 auth_alg);
+
+ /**
+ * sched_scan - Request the driver to initiate scheduled scan
+ * @priv: Private driver interface data
+ * @params: Scan parameters
+ * @interval: Interval between scan cycles in milliseconds
+ * Returns: 0 on success, -1 on failure
+ *
+ * This operation should be used for scheduled scan offload to
+ * the hardware. Every time scan results are available, the
+ * driver should report scan results event for wpa_supplicant
+ * which will eventually request the results with
+ * wpa_driver_get_scan_results2(). This operation is optional
+ * and if not provided or if it returns -1, we fall back to
+ * normal host-scheduled scans.
+ */
+ int (*sched_scan)(void *priv, struct wpa_driver_scan_params *params,
+ u32 interval);
+
+ /**
+ * stop_sched_scan - Request the driver to stop a scheduled scan
+ * @priv: Private driver interface data
+ * Returns: 0 on success, -1 on failure
+ *
+ * This should cause the scheduled scan to be stopped and
+ * results should stop being sent. Must be supported if
+ * sched_scan is supported.
+ */
+ int (*stop_sched_scan)(void *priv);
+
+ /**
+ * poll_client - Probe (null data or such) the given station
+ * @priv: Private driver interface data
+ * @own_addr: MAC address of sending interface
+ * @addr: MAC address of the station to probe
+ * @qos: Indicates whether station is QoS station
+ *
+ * This function is used to verify whether an associated station is
+ * still present. This function does not need to be implemented if the
+ * driver provides such inactivity polling mechanism.
+ */
+ void (*poll_client)(void *priv, const u8 *own_addr,
+ const u8 *addr, int qos);
+
+ /**
+ * radio_disable - Disable/enable radio
+ * @priv: Private driver interface data
+ * @disabled: 1=disable 0=enable radio
+ * Returns: 0 on success, -1 on failure
+ *
+ * This optional command is for testing purposes. It can be used to
+ * disable the radio on a testbed device to simulate out-of-radio-range
+ * conditions.
+ */
+ int (*radio_disable)(void *priv, int disabled);
+
+ /**
+ * switch_channel - Announce channel switch and migrate the GO to the
+ * given frequency
+ * @priv: Private driver interface data
+ * @settings: Settings for CSA period and new channel
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function is used to move the GO to the legacy STA channel to
+ * avoid frequency conflict in single channel concurrency.
+ */
+ int (*switch_channel)(void *priv, struct csa_settings *settings);
+
+ /**
+ * add_tx_ts - Add traffic stream
+ * @priv: Private driver interface data
+ * @tsid: Traffic stream ID
+ * @addr: Receiver address
+ * @user_prio: User priority of the traffic stream
+ * @admitted_time: Admitted time for this TS in units of
+ * 32 microsecond periods (per second).
+ * Returns: 0 on success, -1 on failure
+ */
+ int (*add_tx_ts)(void *priv, u8 tsid, const u8 *addr, u8 user_prio,
+ u16 admitted_time);
+
+ /**
+ * del_tx_ts - Delete traffic stream
+ * @priv: Private driver interface data
+ * @tsid: Traffic stream ID
+ * @addr: Receiver address
+ * Returns: 0 on success, -1 on failure
+ */
+ int (*del_tx_ts)(void *priv, u8 tsid, const u8 *addr);
+
+ /**
+ * Enable channel-switching with TDLS peer
+ * @priv: Private driver interface data
+ * @addr: MAC address of the TDLS peer
+ * @oper_class: Operating class of the switch channel
+ * @params: Channel specification
+ * Returns: 0 on success, -1 on failure
+ *
+ * The function indicates to driver that it can start switching to a
+ * different channel with a specified TDLS peer. The switching is
+ * assumed on until canceled with tdls_disable_channel_switch().
+ */
+ int (*tdls_enable_channel_switch)(
+ void *priv, const u8 *addr, u8 oper_class,
+ const struct hostapd_freq_params *params);
+
+ /**
+ * Disable channel switching with TDLS peer
+ * @priv: Private driver interface data
+ * @addr: MAC address of the TDLS peer
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function indicates to the driver that it should stop switching
+ * with a given TDLS peer.
+ */
+ int (*tdls_disable_channel_switch)(void *priv, const u8 *addr);
+
+ /**
+ * start_dfs_cac - Listen for radar interference on the channel
+ * @priv: Private driver interface data
+ * @freq: Channel parameters
+ * Returns: 0 on success, -1 on failure
+ */
+ int (*start_dfs_cac)(void *priv, struct hostapd_freq_params *freq);
+
+ /**
+ * stop_ap - Removes beacon from AP
+ * @priv: Private driver interface data
+ * Returns: 0 on success, -1 on failure (or if not supported)
+ *
+ * This optional function can be used to disable AP mode related
+ * configuration. Unlike deinit_ap, it does not change to station
+ * mode.
+ */
+ int (*stop_ap)(void *priv);
+
+ /**
+ * get_survey - Retrieve survey data
+ * @priv: Private driver interface data
+ * @freq: If set, survey data for the specified frequency is only
+ * being requested. If not set, all survey data is requested.
+ * Returns: 0 on success, -1 on failure
+ *
+ * Use this to retrieve:
+ *
+ * - the observed channel noise floor
+ * - the amount of time we have spent on the channel
+ * - the amount of time during which we have spent on the channel that
+ * the radio has determined the medium is busy and we cannot
+ * transmit
+ * - the amount of time we have spent receiving data
+ * - the amount of time we have spent transmitting data
+ *
+ * This data can be used for spectrum heuristics. One example is
+ * Automatic Channel Selection (ACS). The channel survey data is
+ * kept on a linked list on the channel data, one entry is added
+ * for each survey. The min_nf of the channel is updated for each
+ * survey.
+ */
+ int (*get_survey)(void *priv, unsigned int freq);
+
+ /**
+ * status - Get driver interface status information
+ * @priv: Private driver interface data
+ * @buf: Buffer for printing tou the status information
+ * @buflen: Maximum length of the buffer
+ * Returns: Length of written status information or -1 on failure
+ */
+ int (*status)(void *priv, char *buf, size_t buflen);
+
+ /**
+ * roaming - Set roaming policy for driver-based BSS selection
+ * @priv: Private driver interface data
+ * @allowed: Whether roaming within ESS is allowed
+ * @bssid: Forced BSSID if roaming is disabled or %NULL if not set
+ * Returns: Length of written status information or -1 on failure
+ *
+ * This optional callback can be used to update roaming policy from the
+ * associate() command (bssid being set there indicates that the driver
+ * should not roam before getting this roaming() call to allow roaming.
+ * If the driver does not indicate WPA_DRIVER_FLAGS_BSS_SELECTION
+ * capability, roaming policy is handled within wpa_supplicant and there
+ * is no need to implement or react to this callback.
+ */
+ int (*roaming)(void *priv, int allowed, const u8 *bssid);
+
+ /**
+ * set_mac_addr - Set MAC address
+ * @priv: Private driver interface data
+ * @addr: MAC address to use or %NULL for setting back to permanent
+ * Returns: 0 on success, -1 on failure
+ */
+ int (*set_mac_addr)(void *priv, const u8 *addr);
+
+#ifdef CONFIG_MACSEC
+ int (*macsec_init)(void *priv, struct macsec_init_params *params);
+
+ int (*macsec_deinit)(void *priv);
+
+ /**
+ * enable_protect_frames - Set protect frames status
+ * @priv: Private driver interface data
+ * @enabled: TRUE = protect frames enabled
+ * FALSE = protect frames disabled
+ * Returns: 0 on success, -1 on failure (or if not supported)
+ */
+ int (*enable_protect_frames)(void *priv, Boolean enabled);
+
+ /**
+ * set_replay_protect - Set replay protect status and window size
+ * @priv: Private driver interface data
+ * @enabled: TRUE = replay protect enabled
+ * FALSE = replay protect disabled
+ * @window: replay window size, valid only when replay protect enabled
+ * Returns: 0 on success, -1 on failure (or if not supported)
+ */
+ int (*set_replay_protect)(void *priv, Boolean enabled, u32 window);
+
+ /**
+ * set_current_cipher_suite - Set current cipher suite
+ * @priv: Private driver interface data
+ * @cs: EUI64 identifier
+ * @cs_len: Length of the cs buffer in octets
+ * Returns: 0 on success, -1 on failure (or if not supported)
+ */
+ int (*set_current_cipher_suite)(void *priv, const u8 *cs,
+ size_t cs_len);
+
+ /**
+ * enable_controlled_port - Set controlled port status
+ * @priv: Private driver interface data
+ * @enabled: TRUE = controlled port enabled
+ * FALSE = controlled port disabled
+ * Returns: 0 on success, -1 on failure (or if not supported)
+ */
+ int (*enable_controlled_port)(void *priv, Boolean enabled);
+
+ /**
+ * get_receive_lowest_pn - Get receive lowest pn
+ * @priv: Private driver interface data
+ * @channel: secure channel
+ * @an: association number
+ * @lowest_pn: lowest accept pn
+ * Returns: 0 on success, -1 on failure (or if not supported)
+ */
+ int (*get_receive_lowest_pn)(void *priv, u32 channel, u8 an,
+ u32 *lowest_pn);
+
+ /**
+ * get_transmit_next_pn - Get transmit next pn
+ * @priv: Private driver interface data
+ * @channel: secure channel
+ * @an: association number
+ * @next_pn: next pn
+ * Returns: 0 on success, -1 on failure (or if not supported)
+ */
+ int (*get_transmit_next_pn)(void *priv, u32 channel, u8 an,
+ u32 *next_pn);
+
+ /**
+ * set_transmit_next_pn - Set transmit next pn
+ * @priv: Private driver interface data
+ * @channel: secure channel
+ * @an: association number
+ * @next_pn: next pn
+ * Returns: 0 on success, -1 on failure (or if not supported)
+ */
+ int (*set_transmit_next_pn)(void *priv, u32 channel, u8 an,
+ u32 next_pn);
+
+ /**
+ * get_available_receive_sc - get available receive channel
+ * @priv: Private driver interface data
+ * @channel: secure channel
+ * Returns: 0 on success, -1 on failure (or if not supported)
+ */
+ int (*get_available_receive_sc)(void *priv, u32 *channel);
+
+ /**
+ * create_receive_sc - create secure channel for receiving
+ * @priv: Private driver interface data
+ * @channel: secure channel
+ * @sci_addr: secure channel identifier - address
+ * @sci_port: secure channel identifier - port
+ * @conf_offset: confidentiality offset (0, 30, or 50)
+ * @validation: frame validation policy (0 = Disabled, 1 = Checked,
+ * 2 = Strict)
+ * Returns: 0 on success, -1 on failure (or if not supported)
+ */
+ int (*create_receive_sc)(void *priv, u32 channel, const u8 *sci_addr,
+ u16 sci_port, unsigned int conf_offset,
+ int validation);
+
+ /**
+ * delete_receive_sc - delete secure connection for receiving
+ * @priv: private driver interface data from init()
+ * @channel: secure channel
+ * Returns: 0 on success, -1 on failure
+ */
+ int (*delete_receive_sc)(void *priv, u32 channel);
+
+ /**
+ * create_receive_sa - create secure association for receive
+ * @priv: private driver interface data from init()
+ * @channel: secure channel
+ * @an: association number
+ * @lowest_pn: the lowest packet number can be received
+ * @sak: the secure association key
+ * Returns: 0 on success, -1 on failure
+ */
+ int (*create_receive_sa)(void *priv, u32 channel, u8 an,
+ u32 lowest_pn, const u8 *sak);
+
+ /**
+ * enable_receive_sa - enable the SA for receive
+ * @priv: private driver interface data from init()
+ * @channel: secure channel
+ * @an: association number
+ * Returns: 0 on success, -1 on failure
+ */
+ int (*enable_receive_sa)(void *priv, u32 channel, u8 an);
+
+ /**
+ * disable_receive_sa - disable SA for receive
+ * @priv: private driver interface data from init()
+ * @channel: secure channel index
+ * @an: association number
+ * Returns: 0 on success, -1 on failure
+ */
+ int (*disable_receive_sa)(void *priv, u32 channel, u8 an);
+
+ /**
+ * get_available_transmit_sc - get available transmit channel
+ * @priv: Private driver interface data
+ * @channel: secure channel
+ * Returns: 0 on success, -1 on failure (or if not supported)
+ */
+ int (*get_available_transmit_sc)(void *priv, u32 *channel);
+
+ /**
+ * create_transmit_sc - create secure connection for transmit
+ * @priv: private driver interface data from init()
+ * @channel: secure channel
+ * @sci_addr: secure channel identifier - address
+ * @sci_port: secure channel identifier - port
+ * Returns: 0 on success, -1 on failure
+ */
+ int (*create_transmit_sc)(void *priv, u32 channel, const u8 *sci_addr,
+ u16 sci_port, unsigned int conf_offset);
+
+ /**
+ * delete_transmit_sc - delete secure connection for transmit
+ * @priv: private driver interface data from init()
+ * @channel: secure channel
+ * Returns: 0 on success, -1 on failure
+ */
+ int (*delete_transmit_sc)(void *priv, u32 channel);
+
+ /**
+ * create_transmit_sa - create secure association for transmit
+ * @priv: private driver interface data from init()
+ * @channel: secure channel index
+ * @an: association number
+ * @next_pn: the packet number used as next transmit packet
+ * @confidentiality: True if the SA is to provide confidentiality
+ * as well as integrity
+ * @sak: the secure association key
+ * Returns: 0 on success, -1 on failure
+ */
+ int (*create_transmit_sa)(void *priv, u32 channel, u8 an, u32 next_pn,
+ Boolean confidentiality, const u8 *sak);
+
+ /**
+ * enable_transmit_sa - enable SA for transmit
+ * @priv: private driver interface data from init()
+ * @channel: secure channel
+ * @an: association number
+ * Returns: 0 on success, -1 on failure
+ */
+ int (*enable_transmit_sa)(void *priv, u32 channel, u8 an);
+
+ /**
+ * disable_transmit_sa - disable SA for transmit
+ * @priv: private driver interface data from init()
+ * @channel: secure channel
+ * @an: association number
+ * Returns: 0 on success, -1 on failure
+ */
+ int (*disable_transmit_sa)(void *priv, u32 channel, u8 an);
+#endif /* CONFIG_MACSEC */
+
+ /**
+ * init_mesh - Driver specific initialization for mesh
+ * @priv: Private driver interface data
+ * Returns: 0 on success, -1 on failure
+ */
+ int (*init_mesh)(void *priv);
+
+ /**
+ * join_mesh - Join a mesh network
+ * @priv: Private driver interface data
+ * @params: Mesh configuration parameters
+ * Returns: 0 on success, -1 on failure
+ */
+ int (*join_mesh)(void *priv,
+ struct wpa_driver_mesh_join_params *params);
+
+ /**
+ * leave_mesh - Leave a mesh network
+ * @priv: Private driver interface data
+ * Returns 0 on success, -1 on failure
+ */
+ int (*leave_mesh)(void *priv);
+
+ /**
+ * do_acs - Automatically select channel
+ * @priv: Private driver interface data
+ * @params: Parameters for ACS
+ * Returns 0 on success, -1 on failure
+ *
+ * This command can be used to offload ACS to the driver if the driver
+ * indicates support for such offloading (WPA_DRIVER_FLAGS_ACS_OFFLOAD).
+ */
+ int (*do_acs)(void *priv, struct drv_acs_params *params);
+
+ /**
+ * set_band - Notify driver of band selection
+ * @priv: Private driver interface data
+ * @band: The selected band(s)
+ * Returns 0 on success, -1 on failure
+ */
+ int (*set_band)(void *priv, enum set_band band);
+
+ /**
+ * get_pref_freq_list - Get preferred frequency list for an interface
+ * @priv: Private driver interface data
+ * @if_type: Interface type
+ * @num: Number of channels
+ * @freq_list: Preferred channel frequency list encoded in MHz values
+ * Returns 0 on success, -1 on failure
+ *
+ * This command can be used to query the preferred frequency list from
+ * the driver specific to a particular interface type.
+ */
+ int (*get_pref_freq_list)(void *priv, enum wpa_driver_if_type if_type,
+ unsigned int *num, unsigned int *freq_list);
+
+ /**
+ * set_prob_oper_freq - Indicate probable P2P operating channel
+ * @priv: Private driver interface data
+ * @freq: Channel frequency in MHz
+ * Returns 0 on success, -1 on failure
+ *
+ * This command can be used to inform the driver of the operating
+ * frequency that an ongoing P2P group formation is likely to come up
+ * on. Local device is assuming P2P Client role.
+ */
+ int (*set_prob_oper_freq)(void *priv, unsigned int freq);
+};
+
+
+/**
+ * enum wpa_event_type - Event type for wpa_supplicant_event() calls
+ */
+enum wpa_event_type {
+ /**
+ * EVENT_ASSOC - Association completed
+ *
+ * This event needs to be delivered when the driver completes IEEE
+ * 802.11 association or reassociation successfully.
+ * wpa_driver_ops::get_bssid() is expected to provide the current BSSID
+ * after this event has been generated. In addition, optional
+ * EVENT_ASSOCINFO may be generated just before EVENT_ASSOC to provide
+ * more information about the association. If the driver interface gets
+ * both of these events at the same time, it can also include the
+ * assoc_info data in EVENT_ASSOC call.
+ */
+ EVENT_ASSOC,
+
+ /**
+ * EVENT_DISASSOC - Association lost
+ *
+ * This event should be called when association is lost either due to
+ * receiving deauthenticate or disassociate frame from the AP or when
+ * sending either of these frames to the current AP. If the driver
+ * supports separate deauthentication event, EVENT_DISASSOC should only
+ * be used for disassociation and EVENT_DEAUTH for deauthentication.
+ * In AP mode, union wpa_event_data::disassoc_info is required.
+ */
+ EVENT_DISASSOC,
+
+ /**
+ * EVENT_MICHAEL_MIC_FAILURE - Michael MIC (TKIP) detected
+ *
+ * This event must be delivered when a Michael MIC error is detected by
+ * the local driver. Additional data for event processing is
+ * provided with union wpa_event_data::michael_mic_failure. This
+ * information is used to request new encyption key and to initiate
+ * TKIP countermeasures if needed.
+ */
+ EVENT_MICHAEL_MIC_FAILURE,
+
+ /**
+ * EVENT_SCAN_RESULTS - Scan results available
+ *
+ * This event must be called whenever scan results are available to be
+ * fetched with struct wpa_driver_ops::get_scan_results(). This event
+ * is expected to be used some time after struct wpa_driver_ops::scan()
+ * is called. If the driver provides an unsolicited event when the scan
+ * has been completed, this event can be used to trigger
+ * EVENT_SCAN_RESULTS call. If such event is not available from the
+ * driver, the driver wrapper code is expected to use a registered
+ * timeout to generate EVENT_SCAN_RESULTS call after the time that the
+ * scan is expected to be completed. Optional information about
+ * completed scan can be provided with union wpa_event_data::scan_info.
+ */
+ EVENT_SCAN_RESULTS,
+
+ /**
+ * EVENT_ASSOCINFO - Report optional extra information for association
+ *
+ * This event can be used to report extra association information for
+ * EVENT_ASSOC processing. This extra information includes IEs from
+ * association frames and Beacon/Probe Response frames in union
+ * wpa_event_data::assoc_info. EVENT_ASSOCINFO must be send just before
+ * EVENT_ASSOC. Alternatively, the driver interface can include
+ * assoc_info data in the EVENT_ASSOC call if it has all the
+ * information available at the same point.
+ */
+ EVENT_ASSOCINFO,
+
+ /**
+ * EVENT_INTERFACE_STATUS - Report interface status changes
+ *
+ * This optional event can be used to report changes in interface
+ * status (interface added/removed) using union
+ * wpa_event_data::interface_status. This can be used to trigger
+ * wpa_supplicant to stop and re-start processing for the interface,
+ * e.g., when a cardbus card is ejected/inserted.
+ */
+ EVENT_INTERFACE_STATUS,
+
+ /**
+ * EVENT_PMKID_CANDIDATE - Report a candidate AP for pre-authentication
+ *
+ * This event can be used to inform wpa_supplicant about candidates for
+ * RSN (WPA2) pre-authentication. If wpa_supplicant is not responsible
+ * for scan request (ap_scan=2 mode), this event is required for
+ * pre-authentication. If wpa_supplicant is performing scan request
+ * (ap_scan=1), this event is optional since scan results can be used
+ * to add pre-authentication candidates. union
+ * wpa_event_data::pmkid_candidate is used to report the BSSID of the
+ * candidate and priority of the candidate, e.g., based on the signal
+ * strength, in order to try to pre-authenticate first with candidates
+ * that are most likely targets for re-association.
+ *
+ * EVENT_PMKID_CANDIDATE can be called whenever the driver has updates
+ * on the candidate list. In addition, it can be called for the current
+ * AP and APs that have existing PMKSA cache entries. wpa_supplicant
+ * will automatically skip pre-authentication in cases where a valid
+ * PMKSA exists. When more than one candidate exists, this event should
+ * be generated once for each candidate.
+ *
+ * Driver will be notified about successful pre-authentication with
+ * struct wpa_driver_ops::add_pmkid() calls.
+ */
+ EVENT_PMKID_CANDIDATE,
+
+ /**
+ * EVENT_STKSTART - Request STK handshake (MLME-STKSTART.request)
+ *
+ * This event can be used to inform wpa_supplicant about desire to set
+ * up secure direct link connection between two stations as defined in
+ * IEEE 802.11e with a new PeerKey mechanism that replaced the original
+ * STAKey negotiation. The caller will need to set peer address for the
+ * event.
+ */
+ EVENT_STKSTART,
+
+ /**
+ * EVENT_TDLS - Request TDLS operation
+ *
+ * This event can be used to request a TDLS operation to be performed.
+ */
+ EVENT_TDLS,
+
+ /**
+ * EVENT_FT_RESPONSE - Report FT (IEEE 802.11r) response IEs
+ *
+ * The driver is expected to report the received FT IEs from
+ * FT authentication sequence from the AP. The FT IEs are included in
+ * the extra information in union wpa_event_data::ft_ies.
+ */
+ EVENT_FT_RESPONSE,
+
+ /**
+ * EVENT_IBSS_RSN_START - Request RSN authentication in IBSS
+ *
+ * The driver can use this event to inform wpa_supplicant about a STA
+ * in an IBSS with which protected frames could be exchanged. This
+ * event starts RSN authentication with the other STA to authenticate
+ * the STA and set up encryption keys with it.
+ */
+ EVENT_IBSS_RSN_START,
+
+ /**
+ * EVENT_AUTH - Authentication result
+ *
+ * This event should be called when authentication attempt has been
+ * completed. This is only used if the driver supports separate
+ * authentication step (struct wpa_driver_ops::authenticate).
+ * Information about authentication result is included in
+ * union wpa_event_data::auth.
+ */
+ EVENT_AUTH,
+
+ /**
+ * EVENT_DEAUTH - Authentication lost
+ *
+ * This event should be called when authentication is lost either due
+ * to receiving deauthenticate frame from the AP or when sending that
+ * frame to the current AP.
+ * In AP mode, union wpa_event_data::deauth_info is required.
+ */
+ EVENT_DEAUTH,
+
+ /**
+ * EVENT_ASSOC_REJECT - Association rejected
+ *
+ * This event should be called when (re)association attempt has been
+ * rejected by the AP. Information about the association response is
+ * included in union wpa_event_data::assoc_reject.
+ */
+ EVENT_ASSOC_REJECT,
+
+ /**
+ * EVENT_AUTH_TIMED_OUT - Authentication timed out
+ */
+ EVENT_AUTH_TIMED_OUT,
+
+ /**
+ * EVENT_ASSOC_TIMED_OUT - Association timed out
+ */
+ EVENT_ASSOC_TIMED_OUT,
+
+ /**
+ * EVENT_WPS_BUTTON_PUSHED - Report hardware push button press for WPS
+ */
+ EVENT_WPS_BUTTON_PUSHED,
+
+ /**
+ * EVENT_TX_STATUS - Report TX status
+ */
+ EVENT_TX_STATUS,
+
+ /**
+ * EVENT_RX_FROM_UNKNOWN - Report RX from unknown STA
+ */
+ EVENT_RX_FROM_UNKNOWN,
+
+ /**
+ * EVENT_RX_MGMT - Report RX of a management frame
+ */
+ EVENT_RX_MGMT,
+
+ /**
+ * EVENT_REMAIN_ON_CHANNEL - Remain-on-channel duration started
+ *
+ * This event is used to indicate when the driver has started the
+ * requested remain-on-channel duration. Information about the
+ * operation is included in union wpa_event_data::remain_on_channel.
+ */
+ EVENT_REMAIN_ON_CHANNEL,
+
+ /**
+ * EVENT_CANCEL_REMAIN_ON_CHANNEL - Remain-on-channel timed out
+ *
+ * This event is used to indicate when the driver has completed
+ * remain-on-channel duration, i.e., may noot be available on the
+ * requested channel anymore. Information about the
+ * operation is included in union wpa_event_data::remain_on_channel.
+ */
+ EVENT_CANCEL_REMAIN_ON_CHANNEL,
+
+ /**
+ * EVENT_RX_PROBE_REQ - Indicate received Probe Request frame
+ *
+ * This event is used to indicate when a Probe Request frame has been
+ * received. Information about the received frame is included in
+ * union wpa_event_data::rx_probe_req. The driver is required to report
+ * these events only after successfully completed probe_req_report()
+ * commands to request the events (i.e., report parameter is non-zero)
+ * in station mode. In AP mode, Probe Request frames should always be
+ * reported.
+ */
+ EVENT_RX_PROBE_REQ,
+
+ /**
+ * EVENT_NEW_STA - New wired device noticed
+ *
+ * This event is used to indicate that a new device has been detected
+ * in a network that does not use association-like functionality (i.e.,
+ * mainly wired Ethernet). This can be used to start EAPOL
+ * authenticator when receiving a frame from a device. The address of
+ * the device is included in union wpa_event_data::new_sta.
+ */
+ EVENT_NEW_STA,
+
+ /**
+ * EVENT_EAPOL_RX - Report received EAPOL frame
+ *
+ * When in AP mode with hostapd, this event is required to be used to
+ * deliver the receive EAPOL frames from the driver.
+ */
+ EVENT_EAPOL_RX,
+
+ /**
+ * EVENT_SIGNAL_CHANGE - Indicate change in signal strength
+ *
+ * This event is used to indicate changes in the signal strength
+ * observed in frames received from the current AP if signal strength
+ * monitoring has been enabled with signal_monitor().
+ */
+ EVENT_SIGNAL_CHANGE,
+
+ /**
+ * EVENT_INTERFACE_ENABLED - Notify that interface was enabled
+ *
+ * This event is used to indicate that the interface was enabled after
+ * having been previously disabled, e.g., due to rfkill.
+ */
+ EVENT_INTERFACE_ENABLED,
+
+ /**
+ * EVENT_INTERFACE_DISABLED - Notify that interface was disabled
+ *
+ * This event is used to indicate that the interface was disabled,
+ * e.g., due to rfkill.
+ */
+ EVENT_INTERFACE_DISABLED,
+
+ /**
+ * EVENT_CHANNEL_LIST_CHANGED - Channel list changed
+ *
+ * This event is used to indicate that the channel list has changed,
+ * e.g., because of a regulatory domain change triggered by scan
+ * results including an AP advertising a country code.
+ */
+ EVENT_CHANNEL_LIST_CHANGED,
+
+ /**
+ * EVENT_INTERFACE_UNAVAILABLE - Notify that interface is unavailable
+ *
+ * This event is used to indicate that the driver cannot maintain this
+ * interface in its operation mode anymore. The most likely use for
+ * this is to indicate that AP mode operation is not available due to
+ * operating channel would need to be changed to a DFS channel when
+ * the driver does not support radar detection and another virtual
+ * interfaces caused the operating channel to change. Other similar
+ * resource conflicts could also trigger this for station mode
+ * interfaces. This event can be propagated when channel switching
+ * fails.
+ */
+ EVENT_INTERFACE_UNAVAILABLE,
+
+ /**
+ * EVENT_BEST_CHANNEL
+ *
+ * Driver generates this event whenever it detects a better channel
+ * (e.g., based on RSSI or channel use). This information can be used
+ * to improve channel selection for a new AP/P2P group.
+ */
+ EVENT_BEST_CHANNEL,
+
+ /**
+ * EVENT_UNPROT_DEAUTH - Unprotected Deauthentication frame received
+ *
+ * This event should be called when a Deauthentication frame is dropped
+ * due to it not being protected (MFP/IEEE 802.11w).
+ * union wpa_event_data::unprot_deauth is required to provide more
+ * details of the frame.
+ */
+ EVENT_UNPROT_DEAUTH,
+
+ /**
+ * EVENT_UNPROT_DISASSOC - Unprotected Disassociation frame received
+ *
+ * This event should be called when a Disassociation frame is dropped
+ * due to it not being protected (MFP/IEEE 802.11w).
+ * union wpa_event_data::unprot_disassoc is required to provide more
+ * details of the frame.
+ */
+ EVENT_UNPROT_DISASSOC,
+
+ /**
+ * EVENT_STATION_LOW_ACK
+ *
+ * Driver generates this event whenever it detected that a particular
+ * station was lost. Detection can be through massive transmission
+ * failures for example.
+ */
+ EVENT_STATION_LOW_ACK,
+
+ /**
+ * EVENT_IBSS_PEER_LOST - IBSS peer not reachable anymore
+ */
+ EVENT_IBSS_PEER_LOST,
+
+ /**
+ * EVENT_DRIVER_GTK_REKEY - Device/driver did GTK rekey
+ *
+ * This event carries the new replay counter to notify wpa_supplicant
+ * of the current EAPOL-Key Replay Counter in case the driver/firmware
+ * completed Group Key Handshake while the host (including
+ * wpa_supplicant was sleeping).
+ */
+ EVENT_DRIVER_GTK_REKEY,
+
+ /**
+ * EVENT_SCHED_SCAN_STOPPED - Scheduled scan was stopped
+ */
+ EVENT_SCHED_SCAN_STOPPED,
+
+ /**
+ * EVENT_DRIVER_CLIENT_POLL_OK - Station responded to poll
+ *
+ * This event indicates that the station responded to the poll
+ * initiated with @poll_client.
+ */
+ EVENT_DRIVER_CLIENT_POLL_OK,
+
+ /**
+ * EVENT_EAPOL_TX_STATUS - notify of EAPOL TX status
+ */
+ EVENT_EAPOL_TX_STATUS,
+
+ /**
+ * EVENT_CH_SWITCH - AP or GO decided to switch channels
+ *
+ * Described in wpa_event_data.ch_switch
+ * */
+ EVENT_CH_SWITCH,
+
+ /**
+ * EVENT_WNM - Request WNM operation
+ *
+ * This event can be used to request a WNM operation to be performed.
+ */
+ EVENT_WNM,
+
+ /**
+ * EVENT_CONNECT_FAILED_REASON - Connection failure reason in AP mode
+ *
+ * This event indicates that the driver reported a connection failure
+ * with the specified client (for example, max client reached, etc.) in
+ * AP mode.
+ */
+ EVENT_CONNECT_FAILED_REASON,
+
+ /**
+ * EVENT_DFS_RADAR_DETECTED - Notify of radar detection
+ *
+ * A radar has been detected on the supplied frequency, hostapd should
+ * react accordingly (e.g., change channel).
+ */
+ EVENT_DFS_RADAR_DETECTED,
+
+ /**
+ * EVENT_DFS_CAC_FINISHED - Notify that channel availability check has been completed
+ *
+ * After a successful CAC, the channel can be marked clear and used.
+ */
+ EVENT_DFS_CAC_FINISHED,
+
+ /**
+ * EVENT_DFS_CAC_ABORTED - Notify that channel availability check has been aborted
+ *
+ * The CAC was not successful, and the channel remains in the previous
+ * state. This may happen due to a radar beeing detected or other
+ * external influences.
+ */
+ EVENT_DFS_CAC_ABORTED,
+
+ /**
+ * EVENT_DFS_NOP_FINISHED - Notify that non-occupancy period is over
+ *
+ * The channel which was previously unavailable is now available again.
+ */
+ EVENT_DFS_NOP_FINISHED,
+
+ /**
+ * EVENT_SURVEY - Received survey data
+ *
+ * This event gets triggered when a driver query is issued for survey
+ * data and the requested data becomes available. The returned data is
+ * stored in struct survey_results. The results provide at most one
+ * survey entry for each frequency and at minimum will provide one
+ * survey entry for one frequency. The survey data can be os_malloc()'d
+ * and then os_free()'d, so the event callback must only copy data.
+ */
+ EVENT_SURVEY,
+
+ /**
+ * EVENT_SCAN_STARTED - Scan started
+ *
+ * This indicates that driver has started a scan operation either based
+ * on a request from wpa_supplicant/hostapd or from another application.
+ * EVENT_SCAN_RESULTS is used to indicate when the scan has been
+ * completed (either successfully or by getting cancelled).
+ */
+ EVENT_SCAN_STARTED,
+
+ /**
+ * EVENT_AVOID_FREQUENCIES - Received avoid frequency range
+ *
+ * This event indicates a set of frequency ranges that should be avoided
+ * to reduce issues due to interference or internal co-existence
+ * information in the driver.
+ */
+ EVENT_AVOID_FREQUENCIES,
+
+ /**
+ * EVENT_NEW_PEER_CANDIDATE - new (unknown) mesh peer notification
+ */
+ EVENT_NEW_PEER_CANDIDATE,
+
+ /**
+ * EVENT_ACS_CHANNEL_SELECTED - Received selected channels by ACS
+ *
+ * Indicates a pair of primary and secondary channels chosen by ACS
+ * in device.
+ */
+ EVENT_ACS_CHANNEL_SELECTED,
+
+ /**
+ * EVENT_DFS_CAC_STARTED - Notify that channel availability check has
+ * been started.
+ *
+ * This event indicates that channel availability check has been started
+ * on a DFS frequency by a driver that supports DFS Offload.
+ */
+ EVENT_DFS_CAC_STARTED,
+};
+
+
+/**
+ * struct freq_survey - Channel survey info
+ *
+ * @ifidx: Interface index in which this survey was observed
+ * @freq: Center of frequency of the surveyed channel
+ * @nf: Channel noise floor in dBm
+ * @channel_time: Amount of time in ms the radio spent on the channel
+ * @channel_time_busy: Amount of time in ms the radio detected some signal
+ * that indicated to the radio the channel was not clear
+ * @channel_time_rx: Amount of time the radio spent receiving data
+ * @channel_time_tx: Amount of time the radio spent transmitting data
+ * @filled: bitmask indicating which fields have been reported, see
+ * SURVEY_HAS_* defines.
+ * @list: Internal list pointers
+ */
+struct freq_survey {
+ u32 ifidx;
+ unsigned int freq;
+ s8 nf;
+ u64 channel_time;
+ u64 channel_time_busy;
+ u64 channel_time_rx;
+ u64 channel_time_tx;
+ unsigned int filled;
+ struct dl_list list;
+};
+
+#define SURVEY_HAS_NF BIT(0)
+#define SURVEY_HAS_CHAN_TIME BIT(1)
+#define SURVEY_HAS_CHAN_TIME_BUSY BIT(2)
+#define SURVEY_HAS_CHAN_TIME_RX BIT(3)
+#define SURVEY_HAS_CHAN_TIME_TX BIT(4)
+
+
+/**
+ * union wpa_event_data - Additional data for wpa_supplicant_event() calls
+ */
+union wpa_event_data {
+ /**
+ * struct assoc_info - Data for EVENT_ASSOC and EVENT_ASSOCINFO events
+ *
+ * This structure is optional for EVENT_ASSOC calls and required for
+ * EVENT_ASSOCINFO calls. By using EVENT_ASSOC with this data, the
+ * driver interface does not need to generate separate EVENT_ASSOCINFO
+ * calls.
+ */
+ struct assoc_info {
+ /**
+ * reassoc - Flag to indicate association or reassociation
+ */
+ int reassoc;
+
+ /**
+ * req_ies - (Re)Association Request IEs
+ *
+ * If the driver generates WPA/RSN IE, this event data must be
+ * returned for WPA handshake to have needed information. If
+ * wpa_supplicant-generated WPA/RSN IE is used, this
+ * information event is optional.
+ *
+ * This should start with the first IE (fixed fields before IEs
+ * are not included).
+ */
+ const u8 *req_ies;
+
+ /**
+ * req_ies_len - Length of req_ies in bytes
+ */
+ size_t req_ies_len;
+
+ /**
+ * resp_ies - (Re)Association Response IEs
+ *
+ * Optional association data from the driver. This data is not
+ * required WPA, but may be useful for some protocols and as
+ * such, should be reported if this is available to the driver
+ * interface.
+ *
+ * This should start with the first IE (fixed fields before IEs
+ * are not included).
+ */
+ const u8 *resp_ies;
+
+ /**
+ * resp_ies_len - Length of resp_ies in bytes
+ */
+ size_t resp_ies_len;
+
+ /**
+ * beacon_ies - Beacon or Probe Response IEs
+ *
+ * Optional Beacon/ProbeResp data: IEs included in Beacon or
+ * Probe Response frames from the current AP (i.e., the one
+ * that the client just associated with). This information is
+ * used to update WPA/RSN IE for the AP. If this field is not
+ * set, the results from previous scan will be used. If no
+ * data for the new AP is found, scan results will be requested
+ * again (without scan request). At this point, the driver is
+ * expected to provide WPA/RSN IE for the AP (if WPA/WPA2 is
+ * used).
+ *
+ * This should start with the first IE (fixed fields before IEs
+ * are not included).
+ */
+ const u8 *beacon_ies;
+
+ /**
+ * beacon_ies_len - Length of beacon_ies */
+ size_t beacon_ies_len;
+
+ /**
+ * freq - Frequency of the operational channel in MHz
+ */
+ unsigned int freq;
+
+ /**
+ * wmm_params - WMM parameters used in this association.
+ */
+ struct wmm_params wmm_params;
+
+ /**
+ * addr - Station address (for AP mode)
+ */
+ const u8 *addr;
+
+ /**
+ * The following is the key management offload information
+ * @authorized
+ * @key_replay_ctr
+ * @key_replay_ctr_len
+ * @ptk_kck
+ * @ptk_kek_len
+ * @ptk_kek
+ * @ptk_kek_len
+ */
+
+ /**
+ * authorized - Status of key management offload,
+ * 1 = successful
+ */
+ int authorized;
+
+ /**
+ * key_replay_ctr - Key replay counter value last used
+ * in a valid EAPOL-Key frame
+ */
+ const u8 *key_replay_ctr;
+
+ /**
+ * key_replay_ctr_len - The length of key_replay_ctr
+ */
+ size_t key_replay_ctr_len;
+
+ /**
+ * ptk_kck - The derived PTK KCK
+ */
+ const u8 *ptk_kck;
+
+ /**
+ * ptk_kek_len - The length of ptk_kck
+ */
+ size_t ptk_kck_len;
+
+ /**
+ * ptk_kek - The derived PTK KEK
+ */
+ const u8 *ptk_kek;
+
+ /**
+ * ptk_kek_len - The length of ptk_kek
+ */
+ size_t ptk_kek_len;
+ } assoc_info;
+
+ /**
+ * struct disassoc_info - Data for EVENT_DISASSOC events
+ */
+ struct disassoc_info {
+ /**
+ * addr - Station address (for AP mode)
+ */
+ const u8 *addr;
+
+ /**
+ * reason_code - Reason Code (host byte order) used in
+ * Deauthentication frame
+ */
+ u16 reason_code;
+
+ /**
+ * ie - Optional IE(s) in Disassociation frame
+ */
+ const u8 *ie;
+
+ /**
+ * ie_len - Length of ie buffer in octets
+ */
+ size_t ie_len;
+
+ /**
+ * locally_generated - Whether the frame was locally generated
+ */
+ int locally_generated;
+ } disassoc_info;
+
+ /**
+ * struct deauth_info - Data for EVENT_DEAUTH events
+ */
+ struct deauth_info {
+ /**
+ * addr - Station address (for AP mode)
+ */
+ const u8 *addr;
+
+ /**
+ * reason_code - Reason Code (host byte order) used in
+ * Deauthentication frame
+ */
+ u16 reason_code;
+
+ /**
+ * ie - Optional IE(s) in Deauthentication frame
+ */
+ const u8 *ie;
+
+ /**
+ * ie_len - Length of ie buffer in octets
+ */
+ size_t ie_len;
+
+ /**
+ * locally_generated - Whether the frame was locally generated
+ */
+ int locally_generated;
+ } deauth_info;
+
+ /**
+ * struct michael_mic_failure - Data for EVENT_MICHAEL_MIC_FAILURE
+ */
+ struct michael_mic_failure {
+ int unicast;
+ const u8 *src;
+ } michael_mic_failure;
+
+ /**
+ * struct interface_status - Data for EVENT_INTERFACE_STATUS
+ */
+ struct interface_status {
+ char ifname[100];
+ enum {
+ EVENT_INTERFACE_ADDED, EVENT_INTERFACE_REMOVED
+ } ievent;
+ } interface_status;
+
+ /**
+ * struct pmkid_candidate - Data for EVENT_PMKID_CANDIDATE
+ */
+ struct pmkid_candidate {
+ /** BSSID of the PMKID candidate */
+ u8 bssid[ETH_ALEN];
+ /** Smaller the index, higher the priority */
+ int index;
+ /** Whether RSN IE includes pre-authenticate flag */
+ int preauth;
+ } pmkid_candidate;
+
+ /**
+ * struct stkstart - Data for EVENT_STKSTART
+ */
+ struct stkstart {
+ u8 peer[ETH_ALEN];
+ } stkstart;
+
+ /**
+ * struct tdls - Data for EVENT_TDLS
+ */
+ struct tdls {
+ u8 peer[ETH_ALEN];
+ enum {
+ TDLS_REQUEST_SETUP,
+ TDLS_REQUEST_TEARDOWN,
+ TDLS_REQUEST_DISCOVER,
+ } oper;
+ u16 reason_code; /* for teardown */
+ } tdls;
+
+ /**
+ * struct wnm - Data for EVENT_WNM
+ */
+ struct wnm {
+ u8 addr[ETH_ALEN];
+ enum {
+ WNM_OPER_SLEEP,
+ } oper;
+ enum {
+ WNM_SLEEP_ENTER,
+ WNM_SLEEP_EXIT
+ } sleep_action;
+ int sleep_intval;
+ u16 reason_code;
+ u8 *buf;
+ u16 buf_len;
+ } wnm;
+
+ /**
+ * struct ft_ies - FT information elements (EVENT_FT_RESPONSE)
+ *
+ * During FT (IEEE 802.11r) authentication sequence, the driver is
+ * expected to use this event to report received FT IEs (MDIE, FTIE,
+ * RSN IE, TIE, possible resource request) to the supplicant. The FT
+ * IEs for the next message will be delivered through the
+ * struct wpa_driver_ops::update_ft_ies() callback.
+ */
+ struct ft_ies {
+ const u8 *ies;
+ size_t ies_len;
+ int ft_action;
+ u8 target_ap[ETH_ALEN];
+ /** Optional IE(s), e.g., WMM TSPEC(s), for RIC-Request */
+ const u8 *ric_ies;
+ /** Length of ric_ies buffer in octets */
+ size_t ric_ies_len;
+ } ft_ies;
+
+ /**
+ * struct ibss_rsn_start - Data for EVENT_IBSS_RSN_START
+ */
+ struct ibss_rsn_start {
+ u8 peer[ETH_ALEN];
+ } ibss_rsn_start;
+
+ /**
+ * struct auth_info - Data for EVENT_AUTH events
+ */
+ struct auth_info {
+ u8 peer[ETH_ALEN];
+ u8 bssid[ETH_ALEN];
+ u16 auth_type;
+ u16 auth_transaction;
+ u16 status_code;
+ const u8 *ies;
+ size_t ies_len;
+ } auth;
+
+ /**
+ * struct assoc_reject - Data for EVENT_ASSOC_REJECT events
+ */
+ struct assoc_reject {
+ /**
+ * bssid - BSSID of the AP that rejected association
+ */
+ const u8 *bssid;
+
+ /**
+ * resp_ies - (Re)Association Response IEs
+ *
+ * Optional association data from the driver. This data is not
+ * required WPA, but may be useful for some protocols and as
+ * such, should be reported if this is available to the driver
+ * interface.
+ *
+ * This should start with the first IE (fixed fields before IEs
+ * are not included).
+ */
+ const u8 *resp_ies;
+
+ /**
+ * resp_ies_len - Length of resp_ies in bytes
+ */
+ size_t resp_ies_len;
+
+ /**
+ * status_code - Status Code from (Re)association Response
+ */
+ u16 status_code;
+ } assoc_reject;
+
+ struct timeout_event {
+ u8 addr[ETH_ALEN];
+ } timeout_event;
+
+ /**
+ * struct tx_status - Data for EVENT_TX_STATUS events
+ */
+ struct tx_status {
+ u16 type;
+ u16 stype;
+ const u8 *dst;
+ const u8 *data;
+ size_t data_len;
+ int ack;
+ } tx_status;
+
+ /**
+ * struct rx_from_unknown - Data for EVENT_RX_FROM_UNKNOWN events
+ */
+ struct rx_from_unknown {
+ const u8 *bssid;
+ const u8 *addr;
+ int wds;
+ } rx_from_unknown;
+
+ /**
+ * struct rx_mgmt - Data for EVENT_RX_MGMT events
+ */
+ struct rx_mgmt {
+ const u8 *frame;
+ size_t frame_len;
+ u32 datarate;
+
+ /**
+ * drv_priv - Pointer to store driver private BSS information
+ *
+ * If not set to NULL, this is used for comparison with
+ * hostapd_data->drv_priv to determine which BSS should process
+ * the frame.
+ */
+ void *drv_priv;
+
+ /**
+ * freq - Frequency (in MHz) on which the frame was received
+ */
+ int freq;
+
+ /**
+ * ssi_signal - Signal strength in dBm (or 0 if not available)
+ */
+ int ssi_signal;
+ } rx_mgmt;
+
+ /**
+ * struct remain_on_channel - Data for EVENT_REMAIN_ON_CHANNEL events
+ *
+ * This is also used with EVENT_CANCEL_REMAIN_ON_CHANNEL events.
+ */
+ struct remain_on_channel {
+ /**
+ * freq - Channel frequency in MHz
+ */
+ unsigned int freq;
+
+ /**
+ * duration - Duration to remain on the channel in milliseconds
+ */
+ unsigned int duration;
+ } remain_on_channel;
+
+ /**
+ * struct scan_info - Optional data for EVENT_SCAN_RESULTS events
+ * @aborted: Whether the scan was aborted
+ * @freqs: Scanned frequencies in MHz (%NULL = all channels scanned)
+ * @num_freqs: Number of entries in freqs array
+ * @ssids: Scanned SSIDs (%NULL or zero-length SSID indicates wildcard
+ * SSID)
+ * @num_ssids: Number of entries in ssids array
+ */
+ struct scan_info {
+ int aborted;
+ const int *freqs;
+ size_t num_freqs;
+ struct wpa_driver_scan_ssid ssids[WPAS_MAX_SCAN_SSIDS];
+ size_t num_ssids;
+ } scan_info;
+
+ /**
+ * struct rx_probe_req - Data for EVENT_RX_PROBE_REQ events
+ */
+ struct rx_probe_req {
+ /**
+ * sa - Source address of the received Probe Request frame
+ */
+ const u8 *sa;
+
+ /**
+ * da - Destination address of the received Probe Request frame
+ * or %NULL if not available
+ */
+ const u8 *da;
+
+ /**
+ * bssid - BSSID of the received Probe Request frame or %NULL
+ * if not available
+ */
+ const u8 *bssid;
+
+ /**
+ * ie - IEs from the Probe Request body
+ */
+ const u8 *ie;
+
+ /**
+ * ie_len - Length of ie buffer in octets
+ */
+ size_t ie_len;
+
+ /**
+ * signal - signal strength in dBm (or 0 if not available)
+ */
+ int ssi_signal;
+ } rx_probe_req;
+
+ /**
+ * struct new_sta - Data for EVENT_NEW_STA events
+ */
+ struct new_sta {
+ const u8 *addr;
+ } new_sta;
+
+ /**
+ * struct eapol_rx - Data for EVENT_EAPOL_RX events
+ */
+ struct eapol_rx {
+ const u8 *src;
+ const u8 *data;
+ size_t data_len;
+ } eapol_rx;
+
+ /**
+ * signal_change - Data for EVENT_SIGNAL_CHANGE events
+ */
+ struct wpa_signal_info signal_change;
+
+ /**
+ * struct best_channel - Data for EVENT_BEST_CHANNEL events
+ * @freq_24: Best 2.4 GHz band channel frequency in MHz
+ * @freq_5: Best 5 GHz band channel frequency in MHz
+ * @freq_overall: Best channel frequency in MHz
+ *
+ * 0 can be used to indicate no preference in either band.
+ */
+ struct best_channel {
+ int freq_24;
+ int freq_5;
+ int freq_overall;
+ } best_chan;
+
+ struct unprot_deauth {
+ const u8 *sa;
+ const u8 *da;
+ u16 reason_code;
+ } unprot_deauth;
+
+ struct unprot_disassoc {
+ const u8 *sa;
+ const u8 *da;
+ u16 reason_code;
+ } unprot_disassoc;
+
+ /**
+ * struct low_ack - Data for EVENT_STATION_LOW_ACK events
+ * @addr: station address
+ */
+ struct low_ack {
+ u8 addr[ETH_ALEN];
+ } low_ack;
+
+ /**
+ * struct ibss_peer_lost - Data for EVENT_IBSS_PEER_LOST
+ */
+ struct ibss_peer_lost {
+ u8 peer[ETH_ALEN];
+ } ibss_peer_lost;
+
+ /**
+ * struct driver_gtk_rekey - Data for EVENT_DRIVER_GTK_REKEY
+ */
+ struct driver_gtk_rekey {
+ const u8 *bssid;
+ const u8 *replay_ctr;
+ } driver_gtk_rekey;
+
+ /**
+ * struct client_poll - Data for EVENT_DRIVER_CLIENT_POLL_OK events
+ * @addr: station address
+ */
+ struct client_poll {
+ u8 addr[ETH_ALEN];
+ } client_poll;
+
+ /**
+ * struct eapol_tx_status
+ * @dst: Original destination
+ * @data: Data starting with IEEE 802.1X header (!)
+ * @data_len: Length of data
+ * @ack: Indicates ack or lost frame
+ *
+ * This corresponds to hapd_send_eapol if the frame sent
+ * there isn't just reported as EVENT_TX_STATUS.
+ */
+ struct eapol_tx_status {
+ const u8 *dst;
+ const u8 *data;
+ int data_len;
+ int ack;
+ } eapol_tx_status;
+
+ /**
+ * struct ch_switch
+ * @freq: Frequency of new channel in MHz
+ * @ht_enabled: Whether this is an HT channel
+ * @ch_offset: Secondary channel offset
+ * @ch_width: Channel width
+ * @cf1: Center frequency 1
+ * @cf2: Center frequency 2
+ */
+ struct ch_switch {
+ int freq;
+ int ht_enabled;
+ int ch_offset;
+ enum chan_width ch_width;
+ int cf1;
+ int cf2;
+ } ch_switch;
+
+ /**
+ * struct connect_failed - Data for EVENT_CONNECT_FAILED_REASON
+ * @addr: Remote client address
+ * @code: Reason code for connection failure
+ */
+ struct connect_failed_reason {
+ u8 addr[ETH_ALEN];
+ enum {
+ MAX_CLIENT_REACHED,
+ BLOCKED_CLIENT
+ } code;
+ } connect_failed_reason;
+
+ /**
+ * struct dfs_event - Data for radar detected events
+ * @freq: Frequency of the channel in MHz
+ */
+ struct dfs_event {
+ int freq;
+ int ht_enabled;
+ int chan_offset;
+ enum chan_width chan_width;
+ int cf1;
+ int cf2;
+ } dfs_event;
+
+ /**
+ * survey_results - Survey result data for EVENT_SURVEY
+ * @freq_filter: Requested frequency survey filter, 0 if request
+ * was for all survey data
+ * @survey_list: Linked list of survey data (struct freq_survey)
+ */
+ struct survey_results {
+ unsigned int freq_filter;
+ struct dl_list survey_list; /* struct freq_survey */
+ } survey_results;
+
+ /**
+ * channel_list_changed - Data for EVENT_CHANNEL_LIST_CHANGED
+ * @initiator: Initiator of the regulatory change
+ * @type: Regulatory change type
+ * @alpha2: Country code (or "" if not available)
+ */
+ struct channel_list_changed {
+ enum reg_change_initiator initiator;
+ enum reg_type type;
+ char alpha2[3];
+ } channel_list_changed;
+
+ /**
+ * freq_range - List of frequency ranges
+ *
+ * This is used as the data with EVENT_AVOID_FREQUENCIES.
+ */
+ struct wpa_freq_range_list freq_range;
+
+ /**
+ * struct mesh_peer
+ *
+ * @peer: Peer address
+ * @ies: Beacon IEs
+ * @ie_len: Length of @ies
+ *
+ * Notification of new candidate mesh peer.
+ */
+ struct mesh_peer {
+ const u8 *peer;
+ const u8 *ies;
+ size_t ie_len;
+ } mesh_peer;
+
+ /**
+ * struct acs_selected_channels - Data for EVENT_ACS_CHANNEL_SELECTED
+ * @pri_channel: Selected primary channel
+ * @sec_channel: Selected secondary channel
+ * @vht_seg0_center_ch: VHT mode Segment0 center channel
+ * @vht_seg1_center_ch: VHT mode Segment1 center channel
+ * @ch_width: Selected Channel width by driver. Driver may choose to
+ * change hostapd configured ACS channel width due driver internal
+ * channel restrictions.
+ * hw_mode: Selected band (used with hw_mode=any)
+ */
+ struct acs_selected_channels {
+ u8 pri_channel;
+ u8 sec_channel;
+ u8 vht_seg0_center_ch;
+ u8 vht_seg1_center_ch;
+ u16 ch_width;
+ enum hostapd_hw_mode hw_mode;
+ } acs_selected_channels;
+};
+
+/**
+ * wpa_supplicant_event - Report a driver event for wpa_supplicant
+ * @ctx: Context pointer (wpa_s); this is the ctx variable registered
+ * with struct wpa_driver_ops::init()
+ * @event: event type (defined above)
+ * @data: possible extra data for the event
+ *
+ * Driver wrapper code should call this function whenever an event is received
+ * from the driver.
+ */
+void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
+ union wpa_event_data *data);
+
+
+/*
+ * The following inline functions are provided for convenience to simplify
+ * event indication for some of the common events.
+ */
+
+static inline void drv_event_assoc(void *ctx, const u8 *addr, const u8 *ie,
+ size_t ielen, int reassoc)
+{
+ union wpa_event_data event;
+ os_memset(&event, 0, sizeof(event));
+ event.assoc_info.reassoc = reassoc;
+ event.assoc_info.req_ies = ie;
+ event.assoc_info.req_ies_len = ielen;
+ event.assoc_info.addr = addr;
+ wpa_supplicant_event(ctx, EVENT_ASSOC, &event);
+}
+
+static inline void drv_event_disassoc(void *ctx, const u8 *addr)
+{
+ union wpa_event_data event;
+ os_memset(&event, 0, sizeof(event));
+ event.disassoc_info.addr = addr;
+ wpa_supplicant_event(ctx, EVENT_DISASSOC, &event);
+}
+
+static inline void drv_event_eapol_rx(void *ctx, const u8 *src, const u8 *data,
+ size_t data_len)
+{
+ union wpa_event_data event;
+ os_memset(&event, 0, sizeof(event));
+ event.eapol_rx.src = src;
+ event.eapol_rx.data = data;
+ event.eapol_rx.data_len = data_len;
+ wpa_supplicant_event(ctx, EVENT_EAPOL_RX, &event);
+}
+
+/* driver_common.c */
+void wpa_scan_results_free(struct wpa_scan_results *res);
+
+/* Convert wpa_event_type to a string for logging */
+const char * event_to_string(enum wpa_event_type event);
+
+/* Convert chan_width to a string for logging and control interfaces */
+const char * channel_width_to_string(enum chan_width width);
+
+int ht_supported(const struct hostapd_hw_modes *mode);
+int vht_supported(const struct hostapd_hw_modes *mode);
+
+struct wowlan_triggers *
+wpa_get_wowlan_triggers(const char *wowlan_triggers,
+ const struct wpa_driver_capa *capa);
+
+/* NULL terminated array of linked in driver wrappers */
+extern const struct wpa_driver_ops *const wpa_drivers[];
+
+#endif /* DRIVER_H */
diff --git a/freebsd/contrib/wpa/src/drivers/driver_bsd.c b/freebsd/contrib/wpa/src/drivers/driver_bsd.c
new file mode 100644
index 00000000..5f6e129d
--- /dev/null
+++ b/freebsd/contrib/wpa/src/drivers/driver_bsd.c
@@ -0,0 +1,1675 @@
+#include <machine/rtems-bsd-user-space.h>
+
+/*
+ * WPA Supplicant - driver interaction with BSD net80211 layer
+ * Copyright (c) 2004, Sam Leffler <sam@errno.com>
+ * Copyright (c) 2004, 2Wire, Inc
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+#include <sys/ioctl.h>
+#include <sys/sysctl.h>
+
+#include "common.h"
+#include "driver.h"
+#include "eloop.h"
+#include "common/ieee802_11_defs.h"
+#include "common/wpa_common.h"
+
+#include <net/if.h>
+#include <net/if_media.h>
+
+#ifdef __NetBSD__
+#include <net/if_ether.h>
+#else
+#include <net/ethernet.h>
+#endif
+#include <net/route.h>
+
+#ifdef __DragonFly__
+#include <netproto/802_11/ieee80211_ioctl.h>
+#include <netproto/802_11/ieee80211_dragonfly.h>
+#else /* __DragonFly__ */
+#ifdef __GLIBC__
+#include <netinet/ether.h>
+#endif /* __GLIBC__ */
+#include <net80211/ieee80211.h>
+#include <net80211/ieee80211_ioctl.h>
+#include <net80211/ieee80211_crypto.h>
+#endif /* __DragonFly__ || __GLIBC__ */
+#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
+#include <net80211/ieee80211_freebsd.h>
+#endif
+#if __NetBSD__
+#include <net80211/ieee80211_netbsd.h>
+#endif
+
+#include "l2_packet/l2_packet.h"
+
+struct bsd_driver_data {
+ struct hostapd_data *hapd; /* back pointer */
+
+ int sock; /* open socket for 802.11 ioctls */
+ struct l2_packet_data *sock_xmit;/* raw packet xmit socket */
+ int route; /* routing socket for events */
+ char ifname[IFNAMSIZ+1]; /* interface name */
+ unsigned int ifindex; /* interface index */
+ void *ctx;
+ struct wpa_driver_capa capa; /* driver capability */
+ int is_ap; /* Access point mode */
+ int prev_roaming; /* roaming state to restore on deinit */
+ int prev_privacy; /* privacy state to restore on deinit */
+ int prev_wpa; /* wpa state to restore on deinit */
+ enum ieee80211_opmode opmode; /* operation mode */
+ char *event_buf;
+ size_t event_buf_len;
+};
+
+/* Generic functions for hostapd and wpa_supplicant */
+static int
+bsd_set80211(void *priv, int op, int val, const void *arg, int arg_len)
+{
+ struct bsd_driver_data *drv = priv;
+ struct ieee80211req ireq;
+
+ os_memset(&ireq, 0, sizeof(ireq));
+ os_strlcpy(ireq.i_name, drv->ifname, sizeof(ireq.i_name));
+ ireq.i_type = op;
+ ireq.i_val = val;
+ ireq.i_data = (void *) arg;
+ ireq.i_len = arg_len;
+
+ if (ioctl(drv->sock, SIOCS80211, &ireq) < 0) {
+ wpa_printf(MSG_ERROR, "ioctl[SIOCS80211, op=%u, val=%u, "
+ "arg_len=%u]: %s", op, val, arg_len,
+ strerror(errno));
+ return -1;
+ }
+ return 0;
+}
+
+static int
+bsd_get80211(void *priv, struct ieee80211req *ireq, int op, void *arg,
+ int arg_len)
+{
+ struct bsd_driver_data *drv = priv;
+
+ os_memset(ireq, 0, sizeof(*ireq));
+ os_strlcpy(ireq->i_name, drv->ifname, sizeof(ireq->i_name));
+ ireq->i_type = op;
+ ireq->i_len = arg_len;
+ ireq->i_data = arg;
+
+ if (ioctl(drv->sock, SIOCG80211, ireq) < 0) {
+ wpa_printf(MSG_ERROR, "ioctl[SIOCS80211, op=%u, "
+ "arg_len=%u]: %s", op, arg_len, strerror(errno));
+ return -1;
+ }
+ return 0;
+}
+
+static int
+get80211var(struct bsd_driver_data *drv, int op, void *arg, int arg_len)
+{
+ struct ieee80211req ireq;
+
+ if (bsd_get80211(drv, &ireq, op, arg, arg_len) < 0)
+ return -1;
+ return ireq.i_len;
+}
+
+static int
+set80211var(struct bsd_driver_data *drv, int op, const void *arg, int arg_len)
+{
+ return bsd_set80211(drv, op, 0, arg, arg_len);
+}
+
+static int
+set80211param(struct bsd_driver_data *drv, int op, int arg)
+{
+ return bsd_set80211(drv, op, arg, NULL, 0);
+}
+
+static int
+bsd_get_ssid(void *priv, u8 *ssid, int len)
+{
+ struct bsd_driver_data *drv = priv;
+#ifdef SIOCG80211NWID
+ struct ieee80211_nwid nwid;
+ struct ifreq ifr;
+
+ os_memset(&ifr, 0, sizeof(ifr));
+ os_strlcpy(ifr.ifr_name, drv->ifname, sizeof(ifr.ifr_name));
+ ifr.ifr_data = (void *)&nwid;
+ if (ioctl(drv->sock, SIOCG80211NWID, &ifr) < 0 ||
+ nwid.i_len > IEEE80211_NWID_LEN)
+ return -1;
+ os_memcpy(ssid, nwid.i_nwid, nwid.i_len);
+ return nwid.i_len;
+#else
+ return get80211var(drv, IEEE80211_IOC_SSID, ssid, IEEE80211_NWID_LEN);
+#endif
+}
+
+static int
+bsd_set_ssid(void *priv, const u8 *ssid, int ssid_len)
+{
+ struct bsd_driver_data *drv = priv;
+#ifdef SIOCS80211NWID
+ struct ieee80211_nwid nwid;
+ struct ifreq ifr;
+
+ os_memcpy(nwid.i_nwid, ssid, ssid_len);
+ nwid.i_len = ssid_len;
+ os_memset(&ifr, 0, sizeof(ifr));
+ os_strlcpy(ifr.ifr_name, drv->ifname, sizeof(ifr.ifr_name));
+ ifr.ifr_data = (void *)&nwid;
+ return ioctl(drv->sock, SIOCS80211NWID, &ifr);
+#else
+ return set80211var(drv, IEEE80211_IOC_SSID, ssid, ssid_len);
+#endif
+}
+
+static int
+bsd_get_if_media(void *priv)
+{
+ struct bsd_driver_data *drv = priv;
+ struct ifmediareq ifmr;
+
+ os_memset(&ifmr, 0, sizeof(ifmr));
+ os_strlcpy(ifmr.ifm_name, drv->ifname, sizeof(ifmr.ifm_name));
+
+ if (ioctl(drv->sock, SIOCGIFMEDIA, &ifmr) < 0) {
+ wpa_printf(MSG_ERROR, "%s: SIOCGIFMEDIA %s", __func__,
+ strerror(errno));
+ return -1;
+ }
+
+ return ifmr.ifm_current;
+}
+
+static int
+bsd_set_if_media(void *priv, int media)
+{
+ struct bsd_driver_data *drv = priv;
+ struct ifreq ifr;
+
+ os_memset(&ifr, 0, sizeof(ifr));
+ os_strlcpy(ifr.ifr_name, drv->ifname, sizeof(ifr.ifr_name));
+ ifr.ifr_media = media;
+
+ if (ioctl(drv->sock, SIOCSIFMEDIA, &ifr) < 0) {
+ wpa_printf(MSG_ERROR, "%s: SIOCSIFMEDIA %s", __func__,
+ strerror(errno));
+ return -1;
+ }
+
+ return 0;
+}
+
+static int
+bsd_set_mediaopt(void *priv, uint32_t mask, uint32_t mode)
+{
+ int media = bsd_get_if_media(priv);
+
+ if (media < 0)
+ return -1;
+ media &= ~mask;
+ media |= mode;
+ if (bsd_set_if_media(priv, media) < 0)
+ return -1;
+ return 0;
+}
+
+static int
+bsd_del_key(void *priv, const u8 *addr, int key_idx)
+{
+ struct ieee80211req_del_key wk;
+
+ os_memset(&wk, 0, sizeof(wk));
+ if (addr == NULL) {
+ wpa_printf(MSG_DEBUG, "%s: key_idx=%d", __func__, key_idx);
+ wk.idk_keyix = key_idx;
+ } else {
+ wpa_printf(MSG_DEBUG, "%s: addr=" MACSTR, __func__,
+ MAC2STR(addr));
+ os_memcpy(wk.idk_macaddr, addr, IEEE80211_ADDR_LEN);
+ wk.idk_keyix = (u_int8_t) IEEE80211_KEYIX_NONE; /* XXX */
+ }
+
+ return set80211var(priv, IEEE80211_IOC_DELKEY, &wk, sizeof(wk));
+}
+
+static int
+bsd_send_mlme_param(void *priv, const u8 op, const u16 reason, const u8 *addr)
+{
+ struct ieee80211req_mlme mlme;
+
+ os_memset(&mlme, 0, sizeof(mlme));
+ mlme.im_op = op;
+ mlme.im_reason = reason;
+ os_memcpy(mlme.im_macaddr, addr, IEEE80211_ADDR_LEN);
+ return set80211var(priv, IEEE80211_IOC_MLME, &mlme, sizeof(mlme));
+}
+
+static int
+bsd_ctrl_iface(void *priv, int enable)
+{
+ struct bsd_driver_data *drv = priv;
+ struct ifreq ifr;
+
+ os_memset(&ifr, 0, sizeof(ifr));
+ os_strlcpy(ifr.ifr_name, drv->ifname, sizeof(ifr.ifr_name));
+
+ if (ioctl(drv->sock, SIOCGIFFLAGS, &ifr) < 0) {
+ wpa_printf(MSG_ERROR, "ioctl[SIOCGIFFLAGS]: %s",
+ strerror(errno));
+ return -1;
+ }
+
+ if (enable) {
+ if (ifr.ifr_flags & IFF_UP)
+ return 0;
+ ifr.ifr_flags |= IFF_UP;
+ } else {
+ if (!(ifr.ifr_flags & IFF_UP))
+ return 0;
+ ifr.ifr_flags &= ~IFF_UP;
+ }
+
+ if (ioctl(drv->sock, SIOCSIFFLAGS, &ifr) < 0) {
+ wpa_printf(MSG_ERROR, "ioctl[SIOCSIFFLAGS]: %s",
+ strerror(errno));
+ return -1;
+ }
+
+ return 0;
+}
+
+static int
+bsd_set_key(const char *ifname, void *priv, enum wpa_alg alg,
+ const unsigned char *addr, int key_idx, int set_tx, const u8 *seq,
+ size_t seq_len, const u8 *key, size_t key_len)
+{
+ struct ieee80211req_key wk;
+#ifdef IEEE80211_KEY_NOREPLAY
+ struct bsd_driver_data *drv = priv;
+#endif /* IEEE80211_KEY_NOREPLAY */
+
+ wpa_printf(MSG_DEBUG, "%s: alg=%d addr=%p key_idx=%d set_tx=%d "
+ "seq_len=%zu key_len=%zu", __func__, alg, addr, key_idx,
+ set_tx, seq_len, key_len);
+
+ if (alg == WPA_ALG_NONE) {
+#ifndef HOSTAPD
+ if (addr == NULL || is_broadcast_ether_addr(addr))
+ return bsd_del_key(priv, NULL, key_idx);
+ else
+#endif /* HOSTAPD */
+ return bsd_del_key(priv, addr, key_idx);
+ }
+
+ os_memset(&wk, 0, sizeof(wk));
+ switch (alg) {
+ case WPA_ALG_WEP:
+ wk.ik_type = IEEE80211_CIPHER_WEP;
+ break;
+ case WPA_ALG_TKIP:
+ wk.ik_type = IEEE80211_CIPHER_TKIP;
+ break;
+ case WPA_ALG_CCMP:
+ wk.ik_type = IEEE80211_CIPHER_AES_CCM;
+ break;
+ default:
+ wpa_printf(MSG_ERROR, "%s: unknown alg=%d", __func__, alg);
+ return -1;
+ }
+
+ wk.ik_flags = IEEE80211_KEY_RECV;
+ if (set_tx)
+ wk.ik_flags |= IEEE80211_KEY_XMIT;
+
+ if (addr == NULL) {
+ os_memset(wk.ik_macaddr, 0xff, IEEE80211_ADDR_LEN);
+ wk.ik_keyix = key_idx;
+ } else {
+ os_memcpy(wk.ik_macaddr, addr, IEEE80211_ADDR_LEN);
+ /*
+ * Deduce whether group/global or unicast key by checking
+ * the address (yech). Note also that we can only mark global
+ * keys default; doing this for a unicast key is an error.
+ */
+ if (is_broadcast_ether_addr(addr)) {
+ wk.ik_flags |= IEEE80211_KEY_GROUP;
+ wk.ik_keyix = key_idx;
+ } else {
+ wk.ik_keyix = key_idx == 0 ? IEEE80211_KEYIX_NONE :
+ key_idx;
+ }
+ }
+ if (wk.ik_keyix != IEEE80211_KEYIX_NONE && set_tx)
+ wk.ik_flags |= IEEE80211_KEY_DEFAULT;
+#ifndef HOSTAPD
+#ifdef IEEE80211_KEY_NOREPLAY
+ /*
+ * Ignore replay failures in IBSS and AHDEMO mode.
+ */
+ if (drv->opmode == IEEE80211_M_IBSS ||
+ drv->opmode == IEEE80211_M_AHDEMO)
+ wk.ik_flags |= IEEE80211_KEY_NOREPLAY;
+#endif /* IEEE80211_KEY_NOREPLAY */
+#endif /* HOSTAPD */
+ wk.ik_keylen = key_len;
+ if (seq) {
+#ifdef WORDS_BIGENDIAN
+ /*
+ * wk.ik_keyrsc is in host byte order (big endian), need to
+ * swap it to match with the byte order used in WPA.
+ */
+ int i;
+ u8 *keyrsc = (u8 *) &wk.ik_keyrsc;
+ for (i = 0; i < seq_len; i++)
+ keyrsc[WPA_KEY_RSC_LEN - i - 1] = seq[i];
+#else /* WORDS_BIGENDIAN */
+ os_memcpy(&wk.ik_keyrsc, seq, seq_len);
+#endif /* WORDS_BIGENDIAN */
+ }
+ os_memcpy(wk.ik_keydata, key, key_len);
+
+ return set80211var(priv, IEEE80211_IOC_WPAKEY, &wk, sizeof(wk));
+}
+
+static int
+bsd_configure_wpa(void *priv, struct wpa_bss_params *params)
+{
+#ifndef IEEE80211_IOC_APPIE
+ static const char *ciphernames[] =
+ { "WEP", "TKIP", "AES-OCB", "AES-CCM", "CKIP", "NONE" };
+ int v;
+
+ switch (params->wpa_group) {
+ case WPA_CIPHER_CCMP:
+ v = IEEE80211_CIPHER_AES_CCM;
+ break;
+ case WPA_CIPHER_TKIP:
+ v = IEEE80211_CIPHER_TKIP;
+ break;
+ case WPA_CIPHER_WEP104:
+ v = IEEE80211_CIPHER_WEP;
+ break;
+ case WPA_CIPHER_WEP40:
+ v = IEEE80211_CIPHER_WEP;
+ break;
+ case WPA_CIPHER_NONE:
+ v = IEEE80211_CIPHER_NONE;
+ break;
+ default:
+ wpa_printf(MSG_INFO, "Unknown group key cipher %u",
+ params->wpa_group);
+ return -1;
+ }
+ wpa_printf(MSG_DEBUG, "%s: group key cipher=%s (%u)",
+ __func__, ciphernames[v], v);
+ if (set80211param(priv, IEEE80211_IOC_MCASTCIPHER, v)) {
+ wpa_printf(MSG_INFO,
+ "Unable to set group key cipher to %u (%s)",
+ v, ciphernames[v]);
+ return -1;
+ }
+ if (v == IEEE80211_CIPHER_WEP) {
+ /* key length is done only for specific ciphers */
+ v = (params->wpa_group == WPA_CIPHER_WEP104 ? 13 : 5);
+ if (set80211param(priv, IEEE80211_IOC_MCASTKEYLEN, v)) {
+ wpa_printf(MSG_INFO,
+ "Unable to set group key length to %u", v);
+ return -1;
+ }
+ }
+
+ v = 0;
+ if (params->wpa_pairwise & WPA_CIPHER_CCMP)
+ v |= 1<<IEEE80211_CIPHER_AES_CCM;
+ if (params->wpa_pairwise & WPA_CIPHER_TKIP)
+ v |= 1<<IEEE80211_CIPHER_TKIP;
+ if (params->wpa_pairwise & WPA_CIPHER_NONE)
+ v |= 1<<IEEE80211_CIPHER_NONE;
+ wpa_printf(MSG_DEBUG, "%s: pairwise key ciphers=0x%x", __func__, v);
+ if (set80211param(priv, IEEE80211_IOC_UCASTCIPHERS, v)) {
+ wpa_printf(MSG_INFO,
+ "Unable to set pairwise key ciphers to 0x%x", v);
+ return -1;
+ }
+
+ wpa_printf(MSG_DEBUG, "%s: key management algorithms=0x%x",
+ __func__, params->wpa_key_mgmt);
+ if (set80211param(priv, IEEE80211_IOC_KEYMGTALGS,
+ params->wpa_key_mgmt)) {
+ wpa_printf(MSG_INFO,
+ "Unable to set key management algorithms to 0x%x",
+ params->wpa_key_mgmt);
+ return -1;
+ }
+
+ v = 0;
+ if (params->rsn_preauth)
+ v |= BIT(0);
+ wpa_printf(MSG_DEBUG, "%s: rsn capabilities=0x%x",
+ __func__, params->rsn_preauth);
+ if (set80211param(priv, IEEE80211_IOC_RSNCAPS, v)) {
+ wpa_printf(MSG_INFO, "Unable to set RSN capabilities to 0x%x",
+ v);
+ return -1;
+ }
+#endif /* IEEE80211_IOC_APPIE */
+
+ wpa_printf(MSG_DEBUG, "%s: enable WPA= 0x%x", __func__, params->wpa);
+ if (set80211param(priv, IEEE80211_IOC_WPA, params->wpa)) {
+ wpa_printf(MSG_INFO, "Unable to set WPA to %u", params->wpa);
+ return -1;
+ }
+ return 0;
+}
+
+static int
+bsd_set_ieee8021x(void *priv, struct wpa_bss_params *params)
+{
+ wpa_printf(MSG_DEBUG, "%s: enabled=%d", __func__, params->enabled);
+
+ if (!params->enabled) {
+ /* XXX restore state */
+ return set80211param(priv, IEEE80211_IOC_AUTHMODE,
+ IEEE80211_AUTH_AUTO);
+ }
+ if (!params->wpa && !params->ieee802_1x) {
+ wpa_printf(MSG_ERROR, "%s: No 802.1X or WPA enabled",
+ __func__);
+ return -1;
+ }
+ if (params->wpa && bsd_configure_wpa(priv, params) != 0) {
+ wpa_printf(MSG_ERROR, "%s: Failed to configure WPA state",
+ __func__);
+ return -1;
+ }
+ if (set80211param(priv, IEEE80211_IOC_AUTHMODE,
+ (params->wpa ? IEEE80211_AUTH_WPA : IEEE80211_AUTH_8021X))) {
+ wpa_printf(MSG_ERROR, "%s: Failed to enable WPA/802.1X",
+ __func__);
+ return -1;
+ }
+ return bsd_ctrl_iface(priv, 1);
+}
+
+static void
+bsd_new_sta(void *priv, void *ctx, u8 addr[IEEE80211_ADDR_LEN])
+{
+ struct ieee80211req_wpaie ie;
+ int ielen = 0;
+ u8 *iebuf = NULL;
+
+ /*
+ * Fetch and validate any negotiated WPA/RSN parameters.
+ */
+ memset(&ie, 0, sizeof(ie));
+ memcpy(ie.wpa_macaddr, addr, IEEE80211_ADDR_LEN);
+ if (get80211var(priv, IEEE80211_IOC_WPAIE, &ie, sizeof(ie)) < 0) {
+ wpa_printf(MSG_INFO,
+ "Failed to get WPA/RSN information element");
+ goto no_ie;
+ }
+ iebuf = ie.wpa_ie;
+ ielen = ie.wpa_ie[1];
+ if (ielen == 0)
+ iebuf = NULL;
+ else
+ ielen += 2;
+
+no_ie:
+ drv_event_assoc(ctx, addr, iebuf, ielen, 0);
+}
+
+static int
+bsd_send_eapol(void *priv, const u8 *addr, const u8 *data, size_t data_len,
+ int encrypt, const u8 *own_addr, u32 flags)
+{
+ struct bsd_driver_data *drv = priv;
+
+ wpa_hexdump(MSG_MSGDUMP, "TX EAPOL", data, data_len);
+
+ return l2_packet_send(drv->sock_xmit, addr, ETH_P_EAPOL, data,
+ data_len);
+}
+
+static int
+bsd_set_freq(void *priv, struct hostapd_freq_params *freq)
+{
+ struct bsd_driver_data *drv = priv;
+#ifdef SIOCS80211CHANNEL
+ struct ieee80211chanreq creq;
+#endif /* SIOCS80211CHANNEL */
+ u32 mode;
+ int channel = freq->channel;
+
+ if (channel < 14) {
+ mode =
+#ifdef CONFIG_IEEE80211N
+ freq->ht_enabled ? IFM_IEEE80211_11NG :
+#endif /* CONFIG_IEEE80211N */
+ IFM_IEEE80211_11G;
+ } else if (channel == 14) {
+ mode = IFM_IEEE80211_11B;
+ } else {
+ mode =
+#ifdef CONFIG_IEEE80211N
+ freq->ht_enabled ? IFM_IEEE80211_11NA :
+#endif /* CONFIG_IEEE80211N */
+ IFM_IEEE80211_11A;
+ }
+ if (bsd_set_mediaopt(drv, IFM_MMASK, mode) < 0) {
+ wpa_printf(MSG_ERROR, "%s: failed to set modulation mode",
+ __func__);
+ return -1;
+ }
+
+#ifdef SIOCS80211CHANNEL
+ os_memset(&creq, 0, sizeof(creq));
+ os_strlcpy(creq.i_name, drv->ifname, sizeof(creq.i_name));
+ creq.i_channel = (u_int16_t)channel;
+ return ioctl(drv->sock, SIOCS80211CHANNEL, &creq);
+#else /* SIOCS80211CHANNEL */
+ return set80211param(priv, IEEE80211_IOC_CHANNEL, channel);
+#endif /* SIOCS80211CHANNEL */
+}
+
+static int
+bsd_set_opt_ie(void *priv, const u8 *ie, size_t ie_len)
+{
+#ifdef IEEE80211_IOC_APPIE
+ wpa_printf(MSG_DEBUG, "%s: set WPA+RSN ie (len %lu)", __func__,
+ (unsigned long)ie_len);
+ return bsd_set80211(priv, IEEE80211_IOC_APPIE, IEEE80211_APPIE_WPA,
+ ie, ie_len);
+#endif /* IEEE80211_IOC_APPIE */
+ return 0;
+}
+
+static size_t
+rtbuf_len(void)
+{
+ size_t len;
+
+ int mib[6] = {CTL_NET, AF_ROUTE, 0, AF_INET, NET_RT_DUMP, 0};
+
+ if (sysctl(mib, 6, NULL, &len, NULL, 0) < 0) {
+ wpa_printf(MSG_WARNING, "%s failed: %s", __func__,
+ strerror(errno));
+ len = 2048;
+ }
+
+ return len;
+}
+
+#ifdef HOSTAPD
+
+/*
+ * Avoid conflicts with hostapd definitions by undefining couple of defines
+ * from net80211 header files.
+ */
+#undef RSN_VERSION
+#undef WPA_VERSION
+#undef WPA_OUI_TYPE
+
+static int bsd_sta_deauth(void *priv, const u8 *own_addr, const u8 *addr,
+ int reason_code);
+
+static const char *
+ether_sprintf(const u8 *addr)
+{
+ static char buf[sizeof(MACSTR)];
+
+ if (addr != NULL)
+ snprintf(buf, sizeof(buf), MACSTR, MAC2STR(addr));
+ else
+ snprintf(buf, sizeof(buf), MACSTR, 0,0,0,0,0,0);
+ return buf;
+}
+
+static int
+bsd_set_privacy(void *priv, int enabled)
+{
+ wpa_printf(MSG_DEBUG, "%s: enabled=%d", __func__, enabled);
+
+ return set80211param(priv, IEEE80211_IOC_PRIVACY, enabled);
+}
+
+static int
+bsd_get_seqnum(const char *ifname, void *priv, const u8 *addr, int idx,
+ u8 *seq)
+{
+ struct ieee80211req_key wk;
+
+ wpa_printf(MSG_DEBUG, "%s: addr=%s idx=%d",
+ __func__, ether_sprintf(addr), idx);
+
+ memset(&wk, 0, sizeof(wk));
+ if (addr == NULL)
+ memset(wk.ik_macaddr, 0xff, IEEE80211_ADDR_LEN);
+ else
+ memcpy(wk.ik_macaddr, addr, IEEE80211_ADDR_LEN);
+ wk.ik_keyix = idx;
+
+ if (get80211var(priv, IEEE80211_IOC_WPAKEY, &wk, sizeof(wk)) < 0) {
+ wpa_printf(MSG_INFO, "Failed to get encryption");
+ return -1;
+ }
+
+#ifdef WORDS_BIGENDIAN
+ {
+ /*
+ * wk.ik_keytsc is in host byte order (big endian), need to
+ * swap it to match with the byte order used in WPA.
+ */
+ int i;
+ u8 tmp[WPA_KEY_RSC_LEN];
+ memcpy(tmp, &wk.ik_keytsc, sizeof(wk.ik_keytsc));
+ for (i = 0; i < WPA_KEY_RSC_LEN; i++) {
+ seq[i] = tmp[WPA_KEY_RSC_LEN - i - 1];
+ }
+ }
+#else /* WORDS_BIGENDIAN */
+ memcpy(seq, &wk.ik_keytsc, sizeof(wk.ik_keytsc));
+#endif /* WORDS_BIGENDIAN */
+ return 0;
+}
+
+
+static int
+bsd_flush(void *priv)
+{
+ u8 allsta[IEEE80211_ADDR_LEN];
+
+ memset(allsta, 0xff, IEEE80211_ADDR_LEN);
+ return bsd_sta_deauth(priv, NULL, allsta, IEEE80211_REASON_AUTH_LEAVE);
+}
+
+
+static int
+bsd_read_sta_driver_data(void *priv, struct hostap_sta_driver_data *data,
+ const u8 *addr)
+{
+ struct ieee80211req_sta_stats stats;
+
+ memcpy(stats.is_u.macaddr, addr, IEEE80211_ADDR_LEN);
+ if (get80211var(priv, IEEE80211_IOC_STA_STATS, &stats, sizeof(stats))
+ > 0) {
+ /* XXX? do packets counts include non-data frames? */
+ data->rx_packets = stats.is_stats.ns_rx_data;
+ data->rx_bytes = stats.is_stats.ns_rx_bytes;
+ data->tx_packets = stats.is_stats.ns_tx_data;
+ data->tx_bytes = stats.is_stats.ns_tx_bytes;
+ }
+ return 0;
+}
+
+static int
+bsd_sta_deauth(void *priv, const u8 *own_addr, const u8 *addr, int reason_code)
+{
+ return bsd_send_mlme_param(priv, IEEE80211_MLME_DEAUTH, reason_code,
+ addr);
+}
+
+static int
+bsd_sta_disassoc(void *priv, const u8 *own_addr, const u8 *addr,
+ int reason_code)
+{
+ return bsd_send_mlme_param(priv, IEEE80211_MLME_DISASSOC, reason_code,
+ addr);
+}
+
+static void
+bsd_wireless_event_receive(int sock, void *ctx, void *sock_ctx)
+{
+ struct bsd_driver_data *drv = ctx;
+ struct if_announcemsghdr *ifan;
+ struct rt_msghdr *rtm;
+ struct ieee80211_michael_event *mic;
+ struct ieee80211_join_event *join;
+ struct ieee80211_leave_event *leave;
+ int n;
+ union wpa_event_data data;
+
+ n = read(sock, drv->event_buf, drv->event_buf_len);
+ if (n < 0) {
+ if (errno != EINTR && errno != EAGAIN)
+ wpa_printf(MSG_ERROR, "%s read() failed: %s",
+ __func__, strerror(errno));
+ return;
+ }
+
+ rtm = (struct rt_msghdr *) drv->event_buf;
+ if (rtm->rtm_version != RTM_VERSION) {
+ wpa_printf(MSG_DEBUG, "Invalid routing message version=%d",
+ rtm->rtm_version);
+ return;
+ }
+ ifan = (struct if_announcemsghdr *) rtm;
+ switch (rtm->rtm_type) {
+ case RTM_IEEE80211:
+ switch (ifan->ifan_what) {
+ case RTM_IEEE80211_ASSOC:
+ case RTM_IEEE80211_REASSOC:
+ case RTM_IEEE80211_DISASSOC:
+ case RTM_IEEE80211_SCAN:
+ break;
+ case RTM_IEEE80211_LEAVE:
+ leave = (struct ieee80211_leave_event *) &ifan[1];
+ drv_event_disassoc(drv->hapd, leave->iev_addr);
+ break;
+ case RTM_IEEE80211_JOIN:
+#ifdef RTM_IEEE80211_REJOIN
+ case RTM_IEEE80211_REJOIN:
+#endif
+ join = (struct ieee80211_join_event *) &ifan[1];
+ bsd_new_sta(drv, drv->hapd, join->iev_addr);
+ break;
+ case RTM_IEEE80211_REPLAY:
+ /* ignore */
+ break;
+ case RTM_IEEE80211_MICHAEL:
+ mic = (struct ieee80211_michael_event *) &ifan[1];
+ wpa_printf(MSG_DEBUG,
+ "Michael MIC failure wireless event: "
+ "keyix=%u src_addr=" MACSTR, mic->iev_keyix,
+ MAC2STR(mic->iev_src));
+ os_memset(&data, 0, sizeof(data));
+ data.michael_mic_failure.unicast = 1;
+ data.michael_mic_failure.src = mic->iev_src;
+ wpa_supplicant_event(drv->hapd,
+ EVENT_MICHAEL_MIC_FAILURE, &data);
+ break;
+ }
+ break;
+ }
+}
+
+static void
+handle_read(void *ctx, const u8 *src_addr, const u8 *buf, size_t len)
+{
+ struct bsd_driver_data *drv = ctx;
+ drv_event_eapol_rx(drv->hapd, src_addr, buf, len);
+}
+
+static void *
+bsd_init(struct hostapd_data *hapd, struct wpa_init_params *params)
+{
+ struct bsd_driver_data *drv;
+
+ drv = os_zalloc(sizeof(struct bsd_driver_data));
+ if (drv == NULL) {
+ wpa_printf(MSG_ERROR, "Could not allocate memory for bsd driver data");
+ return NULL;
+ }
+
+ drv->event_buf_len = rtbuf_len();
+
+ drv->event_buf = os_malloc(drv->event_buf_len);
+ if (drv->event_buf == NULL) {
+ wpa_printf(MSG_ERROR, "%s: os_malloc() failed", __func__);
+ goto bad;
+ }
+
+ drv->hapd = hapd;
+ drv->sock = socket(PF_INET, SOCK_DGRAM, 0);
+ if (drv->sock < 0) {
+ wpa_printf(MSG_ERROR, "socket[PF_INET,SOCK_DGRAM]: %s",
+ strerror(errno));
+ goto bad;
+ }
+ os_strlcpy(drv->ifname, params->ifname, sizeof(drv->ifname));
+
+ drv->sock_xmit = l2_packet_init(drv->ifname, NULL, ETH_P_EAPOL,
+ handle_read, drv, 0);
+ if (drv->sock_xmit == NULL)
+ goto bad;
+ if (l2_packet_get_own_addr(drv->sock_xmit, params->own_addr))
+ goto bad;
+
+ /* mark down during setup */
+ if (bsd_ctrl_iface(drv, 0) < 0)
+ goto bad;
+
+ drv->route = socket(PF_ROUTE, SOCK_RAW, 0);
+ if (drv->route < 0) {
+ wpa_printf(MSG_ERROR, "socket(PF_ROUTE,SOCK_RAW): %s",
+ strerror(errno));
+ goto bad;
+ }
+ eloop_register_read_sock(drv->route, bsd_wireless_event_receive, drv,
+ NULL);
+
+ if (bsd_set_mediaopt(drv, IFM_OMASK, IFM_IEEE80211_HOSTAP) < 0) {
+ wpa_printf(MSG_ERROR, "%s: failed to set operation mode",
+ __func__);
+ goto bad;
+ }
+
+ return drv;
+bad:
+ if (drv->sock_xmit != NULL)
+ l2_packet_deinit(drv->sock_xmit);
+ if (drv->sock >= 0)
+ close(drv->sock);
+ os_free(drv->event_buf);
+ os_free(drv);
+ return NULL;
+}
+
+
+static void
+bsd_deinit(void *priv)
+{
+ struct bsd_driver_data *drv = priv;
+
+ if (drv->route >= 0) {
+ eloop_unregister_read_sock(drv->route);
+ close(drv->route);
+ }
+ bsd_ctrl_iface(drv, 0);
+ if (drv->sock >= 0)
+ close(drv->sock);
+ if (drv->sock_xmit != NULL)
+ l2_packet_deinit(drv->sock_xmit);
+ os_free(drv->event_buf);
+ os_free(drv);
+}
+
+
+static int
+bsd_commit(void *priv)
+{
+ return bsd_ctrl_iface(priv, 1);
+}
+
+
+static int
+bsd_set_sta_authorized(void *priv, const u8 *addr,
+ unsigned int total_flags, unsigned int flags_or,
+ unsigned int flags_and)
+{
+ int authorized = -1;
+
+ /* For now, only support setting Authorized flag */
+ if (flags_or & WPA_STA_AUTHORIZED)
+ authorized = 1;
+ if (!(flags_and & WPA_STA_AUTHORIZED))
+ authorized = 0;
+
+ if (authorized < 0)
+ return 0;
+
+ return bsd_send_mlme_param(priv, authorized ?
+ IEEE80211_MLME_AUTHORIZE :
+ IEEE80211_MLME_UNAUTHORIZE, 0, addr);
+}
+#else /* HOSTAPD */
+
+static int
+get80211param(struct bsd_driver_data *drv, int op)
+{
+ struct ieee80211req ireq;
+
+ if (bsd_get80211(drv, &ireq, op, NULL, 0) < 0)
+ return -1;
+ return ireq.i_val;
+}
+
+static int
+wpa_driver_bsd_get_bssid(void *priv, u8 *bssid)
+{
+ struct bsd_driver_data *drv = priv;
+#ifdef SIOCG80211BSSID
+ struct ieee80211_bssid bs;
+
+ os_strlcpy(bs.i_name, drv->ifname, sizeof(bs.i_name));
+ if (ioctl(drv->sock, SIOCG80211BSSID, &bs) < 0)
+ return -1;
+ os_memcpy(bssid, bs.i_bssid, sizeof(bs.i_bssid));
+ return 0;
+#else
+ return get80211var(drv, IEEE80211_IOC_BSSID,
+ bssid, IEEE80211_ADDR_LEN) < 0 ? -1 : 0;
+#endif
+}
+
+static int
+wpa_driver_bsd_get_ssid(void *priv, u8 *ssid)
+{
+ struct bsd_driver_data *drv = priv;
+ return bsd_get_ssid(drv, ssid, 0);
+}
+
+static int
+wpa_driver_bsd_set_wpa_ie(struct bsd_driver_data *drv, const u8 *wpa_ie,
+ size_t wpa_ie_len)
+{
+#ifdef IEEE80211_IOC_APPIE
+ return bsd_set_opt_ie(drv, wpa_ie, wpa_ie_len);
+#else /* IEEE80211_IOC_APPIE */
+ return set80211var(drv, IEEE80211_IOC_OPTIE, wpa_ie, wpa_ie_len);
+#endif /* IEEE80211_IOC_APPIE */
+}
+
+static int
+wpa_driver_bsd_set_wpa_internal(void *priv, int wpa, int privacy)
+{
+ int ret = 0;
+
+ wpa_printf(MSG_DEBUG, "%s: wpa=%d privacy=%d",
+ __FUNCTION__, wpa, privacy);
+
+ if (!wpa && wpa_driver_bsd_set_wpa_ie(priv, NULL, 0) < 0)
+ ret = -1;
+ if (set80211param(priv, IEEE80211_IOC_PRIVACY, privacy) < 0)
+ ret = -1;
+ if (set80211param(priv, IEEE80211_IOC_WPA, wpa) < 0)
+ ret = -1;
+
+ return ret;
+}
+
+static int
+wpa_driver_bsd_set_wpa(void *priv, int enabled)
+{
+ wpa_printf(MSG_DEBUG, "%s: enabled=%d", __FUNCTION__, enabled);
+
+ return wpa_driver_bsd_set_wpa_internal(priv, enabled ? 3 : 0, enabled);
+}
+
+static int
+wpa_driver_bsd_set_countermeasures(void *priv, int enabled)
+{
+ wpa_printf(MSG_DEBUG, "%s: enabled=%d", __func__, enabled);
+ return set80211param(priv, IEEE80211_IOC_COUNTERMEASURES, enabled);
+}
+
+
+static int
+wpa_driver_bsd_set_drop_unencrypted(void *priv, int enabled)
+{
+ wpa_printf(MSG_DEBUG, "%s: enabled=%d", __func__, enabled);
+ return set80211param(priv, IEEE80211_IOC_DROPUNENCRYPTED, enabled);
+}
+
+static int
+wpa_driver_bsd_deauthenticate(void *priv, const u8 *addr, int reason_code)
+{
+ return bsd_send_mlme_param(priv, IEEE80211_MLME_DEAUTH, reason_code,
+ addr);
+}
+
+static int
+wpa_driver_bsd_set_auth_alg(void *priv, int auth_alg)
+{
+ int authmode;
+
+ if ((auth_alg & WPA_AUTH_ALG_OPEN) &&
+ (auth_alg & WPA_AUTH_ALG_SHARED))
+ authmode = IEEE80211_AUTH_AUTO;
+ else if (auth_alg & WPA_AUTH_ALG_SHARED)
+ authmode = IEEE80211_AUTH_SHARED;
+ else
+ authmode = IEEE80211_AUTH_OPEN;
+
+ return set80211param(priv, IEEE80211_IOC_AUTHMODE, authmode);
+}
+
+static void
+handle_read(void *ctx, const u8 *src_addr, const u8 *buf, size_t len)
+{
+ struct bsd_driver_data *drv = ctx;
+
+ drv_event_eapol_rx(drv->ctx, src_addr, buf, len);
+}
+
+static int
+wpa_driver_bsd_associate(void *priv, struct wpa_driver_associate_params *params)
+{
+ struct bsd_driver_data *drv = priv;
+ struct ieee80211req_mlme mlme;
+ u32 mode;
+ int privacy;
+ int ret = 0;
+
+ wpa_printf(MSG_DEBUG,
+ "%s: ssid '%.*s' wpa ie len %u pairwise %u group %u key mgmt %u"
+ , __func__
+ , (unsigned int) params->ssid_len, params->ssid
+ , (unsigned int) params->wpa_ie_len
+ , params->pairwise_suite
+ , params->group_suite
+ , params->key_mgmt_suite
+ );
+
+ switch (params->mode) {
+ case IEEE80211_MODE_INFRA:
+ mode = 0 /* STA */;
+ break;
+ case IEEE80211_MODE_IBSS:
+ /*
+ * Ref bin/203086 - FreeBSD's net80211 currently uses
+ * IFM_IEEE80211_ADHOC.
+ */
+#if 0
+ mode = IFM_IEEE80211_IBSS;
+#endif
+ mode = IFM_IEEE80211_ADHOC;
+ break;
+ case IEEE80211_MODE_AP:
+ mode = IFM_IEEE80211_HOSTAP;
+ break;
+ default:
+ wpa_printf(MSG_ERROR, "%s: unknown operation mode", __func__);
+ return -1;
+ }
+ if (bsd_set_mediaopt(drv, IFM_OMASK, mode) < 0) {
+ wpa_printf(MSG_ERROR, "%s: failed to set operation mode",
+ __func__);
+ return -1;
+ }
+
+ if (params->mode == IEEE80211_MODE_AP) {
+ drv->sock_xmit = l2_packet_init(drv->ifname, NULL, ETH_P_EAPOL,
+ handle_read, drv, 0);
+ if (drv->sock_xmit == NULL)
+ return -1;
+ drv->is_ap = 1;
+ return 0;
+ }
+
+ if (wpa_driver_bsd_set_drop_unencrypted(drv, params->drop_unencrypted)
+ < 0)
+ ret = -1;
+ if (wpa_driver_bsd_set_auth_alg(drv, params->auth_alg) < 0)
+ ret = -1;
+ /* XXX error handling is wrong but unclear what to do... */
+ if (wpa_driver_bsd_set_wpa_ie(drv, params->wpa_ie, params->wpa_ie_len) < 0)
+ return -1;
+
+ privacy = !(params->pairwise_suite == WPA_CIPHER_NONE &&
+ params->group_suite == WPA_CIPHER_NONE &&
+ params->key_mgmt_suite == WPA_KEY_MGMT_NONE &&
+ params->wpa_ie_len == 0);
+ wpa_printf(MSG_DEBUG, "%s: set PRIVACY %u", __func__, privacy);
+
+ if (set80211param(drv, IEEE80211_IOC_PRIVACY, privacy) < 0)
+ return -1;
+
+ if (params->wpa_ie_len &&
+ set80211param(drv, IEEE80211_IOC_WPA,
+ params->wpa_ie[0] == WLAN_EID_RSN ? 2 : 1) < 0)
+ return -1;
+
+ /*
+ * NB: interface must be marked UP for association
+ * or scanning (ap_scan=2)
+ */
+ if (bsd_ctrl_iface(drv, 1) < 0)
+ return -1;
+
+ os_memset(&mlme, 0, sizeof(mlme));
+ mlme.im_op = IEEE80211_MLME_ASSOC;
+ if (params->ssid != NULL)
+ os_memcpy(mlme.im_ssid, params->ssid, params->ssid_len);
+ mlme.im_ssid_len = params->ssid_len;
+ if (params->bssid != NULL)
+ os_memcpy(mlme.im_macaddr, params->bssid, IEEE80211_ADDR_LEN);
+ if (set80211var(drv, IEEE80211_IOC_MLME, &mlme, sizeof(mlme)) < 0)
+ return -1;
+ return ret;
+}
+
+static int
+wpa_driver_bsd_scan(void *priv, struct wpa_driver_scan_params *params)
+{
+ struct bsd_driver_data *drv = priv;
+#ifdef IEEE80211_IOC_SCAN_MAX_SSID
+ struct ieee80211_scan_req sr;
+ int i;
+#endif /* IEEE80211_IOC_SCAN_MAX_SSID */
+
+ if (bsd_set_mediaopt(drv, IFM_OMASK, 0 /* STA */) < 0) {
+ wpa_printf(MSG_ERROR, "%s: failed to set operation mode",
+ __func__);
+ return -1;
+ }
+
+ if (set80211param(drv, IEEE80211_IOC_ROAMING,
+ IEEE80211_ROAMING_MANUAL) < 0) {
+ wpa_printf(MSG_ERROR, "%s: failed to set "
+ "wpa_supplicant-based roaming: %s", __func__,
+ strerror(errno));
+ return -1;
+ }
+
+ if (wpa_driver_bsd_set_wpa(drv, 1) < 0) {
+ wpa_printf(MSG_ERROR, "%s: failed to set wpa: %s", __func__,
+ strerror(errno));
+ return -1;
+ }
+
+ /* NB: interface must be marked UP to do a scan */
+ if (bsd_ctrl_iface(drv, 1) < 0)
+ return -1;
+
+#ifdef IEEE80211_IOC_SCAN_MAX_SSID
+ os_memset(&sr, 0, sizeof(sr));
+ sr.sr_flags = IEEE80211_IOC_SCAN_ACTIVE | IEEE80211_IOC_SCAN_ONCE |
+ IEEE80211_IOC_SCAN_NOJOIN;
+ sr.sr_duration = IEEE80211_IOC_SCAN_FOREVER;
+ if (params->num_ssids > 0) {
+ sr.sr_nssid = params->num_ssids;
+#if 0
+ /* Boundary check is done by upper layer */
+ if (sr.sr_nssid > IEEE80211_IOC_SCAN_MAX_SSID)
+ sr.sr_nssid = IEEE80211_IOC_SCAN_MAX_SSID;
+#endif
+
+ /* NB: check scan cache first */
+ sr.sr_flags |= IEEE80211_IOC_SCAN_CHECK;
+ }
+ for (i = 0; i < sr.sr_nssid; i++) {
+ sr.sr_ssid[i].len = params->ssids[i].ssid_len;
+ os_memcpy(sr.sr_ssid[i].ssid, params->ssids[i].ssid,
+ sr.sr_ssid[i].len);
+ }
+
+ /* NB: net80211 delivers a scan complete event so no need to poll */
+ return set80211var(drv, IEEE80211_IOC_SCAN_REQ, &sr, sizeof(sr));
+#else /* IEEE80211_IOC_SCAN_MAX_SSID */
+ /* set desired ssid before scan */
+ if (bsd_set_ssid(drv, params->ssids[0].ssid,
+ params->ssids[0].ssid_len) < 0)
+ return -1;
+
+ /* NB: net80211 delivers a scan complete event so no need to poll */
+ return set80211param(drv, IEEE80211_IOC_SCAN_REQ, 0);
+#endif /* IEEE80211_IOC_SCAN_MAX_SSID */
+}
+
+static void
+wpa_driver_bsd_event_receive(int sock, void *ctx, void *sock_ctx)
+{
+ struct bsd_driver_data *drv = sock_ctx;
+ struct if_announcemsghdr *ifan;
+ struct if_msghdr *ifm;
+ struct rt_msghdr *rtm;
+ union wpa_event_data event;
+ struct ieee80211_michael_event *mic;
+ struct ieee80211_leave_event *leave;
+ struct ieee80211_join_event *join;
+ int n;
+
+ n = read(sock, drv->event_buf, drv->event_buf_len);
+ if (n < 0) {
+ if (errno != EINTR && errno != EAGAIN)
+ wpa_printf(MSG_ERROR, "%s read() failed: %s",
+ __func__, strerror(errno));
+ return;
+ }
+
+ rtm = (struct rt_msghdr *) drv->event_buf;
+ if (rtm->rtm_version != RTM_VERSION) {
+ wpa_printf(MSG_DEBUG, "Invalid routing message version=%d",
+ rtm->rtm_version);
+ return;
+ }
+ os_memset(&event, 0, sizeof(event));
+ switch (rtm->rtm_type) {
+ case RTM_IFANNOUNCE:
+ ifan = (struct if_announcemsghdr *) rtm;
+ if (ifan->ifan_index != drv->ifindex)
+ break;
+ os_strlcpy(event.interface_status.ifname, drv->ifname,
+ sizeof(event.interface_status.ifname));
+ switch (ifan->ifan_what) {
+ case IFAN_DEPARTURE:
+ event.interface_status.ievent = EVENT_INTERFACE_REMOVED;
+ default:
+ return;
+ }
+ wpa_printf(MSG_DEBUG, "RTM_IFANNOUNCE: Interface '%s' %s",
+ event.interface_status.ifname,
+ ifan->ifan_what == IFAN_DEPARTURE ?
+ "removed" : "added");
+ wpa_supplicant_event(ctx, EVENT_INTERFACE_STATUS, &event);
+ break;
+ case RTM_IEEE80211:
+ ifan = (struct if_announcemsghdr *) rtm;
+ if (ifan->ifan_index != drv->ifindex)
+ break;
+ switch (ifan->ifan_what) {
+ case RTM_IEEE80211_ASSOC:
+ case RTM_IEEE80211_REASSOC:
+ if (drv->is_ap)
+ break;
+ wpa_supplicant_event(ctx, EVENT_ASSOC, NULL);
+ break;
+ case RTM_IEEE80211_DISASSOC:
+ if (drv->is_ap)
+ break;
+ wpa_supplicant_event(ctx, EVENT_DISASSOC, NULL);
+ break;
+ case RTM_IEEE80211_SCAN:
+ if (drv->is_ap)
+ break;
+ wpa_supplicant_event(ctx, EVENT_SCAN_RESULTS, NULL);
+ break;
+ case RTM_IEEE80211_LEAVE:
+ leave = (struct ieee80211_leave_event *) &ifan[1];
+ drv_event_disassoc(ctx, leave->iev_addr);
+ break;
+ case RTM_IEEE80211_JOIN:
+#ifdef RTM_IEEE80211_REJOIN
+ case RTM_IEEE80211_REJOIN:
+#endif
+ join = (struct ieee80211_join_event *) &ifan[1];
+ bsd_new_sta(drv, ctx, join->iev_addr);
+ break;
+ case RTM_IEEE80211_REPLAY:
+ /* ignore */
+ break;
+ case RTM_IEEE80211_MICHAEL:
+ mic = (struct ieee80211_michael_event *) &ifan[1];
+ wpa_printf(MSG_DEBUG,
+ "Michael MIC failure wireless event: "
+ "keyix=%u src_addr=" MACSTR, mic->iev_keyix,
+ MAC2STR(mic->iev_src));
+
+ os_memset(&event, 0, sizeof(event));
+ event.michael_mic_failure.unicast =
+ !IEEE80211_IS_MULTICAST(mic->iev_dst);
+ wpa_supplicant_event(ctx, EVENT_MICHAEL_MIC_FAILURE,
+ &event);
+ break;
+ }
+ break;
+ case RTM_IFINFO:
+ ifm = (struct if_msghdr *) rtm;
+ if (ifm->ifm_index != drv->ifindex)
+ break;
+ if ((rtm->rtm_flags & RTF_UP) == 0) {
+ os_strlcpy(event.interface_status.ifname, drv->ifname,
+ sizeof(event.interface_status.ifname));
+ event.interface_status.ievent = EVENT_INTERFACE_REMOVED;
+ wpa_printf(MSG_DEBUG, "RTM_IFINFO: Interface '%s' DOWN",
+ event.interface_status.ifname);
+ wpa_supplicant_event(ctx, EVENT_INTERFACE_STATUS, &event);
+ }
+ break;
+ }
+}
+
+static void
+wpa_driver_bsd_add_scan_entry(struct wpa_scan_results *res,
+ struct ieee80211req_scan_result *sr)
+{
+ struct wpa_scan_res *result, **tmp;
+ size_t extra_len;
+ u8 *pos;
+
+ extra_len = 2 + sr->isr_ssid_len;
+ extra_len += 2 + sr->isr_nrates;
+ extra_len += 3; /* ERP IE */
+ extra_len += sr->isr_ie_len;
+
+ result = os_zalloc(sizeof(*result) + extra_len);
+ if (result == NULL)
+ return;
+ os_memcpy(result->bssid, sr->isr_bssid, ETH_ALEN);
+ result->freq = sr->isr_freq;
+ result->beacon_int = sr->isr_intval;
+ result->caps = sr->isr_capinfo;
+ result->qual = sr->isr_rssi;
+ result->noise = sr->isr_noise;
+ /*
+ * the rssi value reported by the kernel is in 0.5dB steps relative to
+ * the reported noise floor. see ieee80211_node.h for details.
+ */
+ result->level = sr->isr_rssi / 2 + sr->isr_noise;
+
+ pos = (u8 *)(result + 1);
+
+ *pos++ = WLAN_EID_SSID;
+ *pos++ = sr->isr_ssid_len;
+ os_memcpy(pos, sr + 1, sr->isr_ssid_len);
+ pos += sr->isr_ssid_len;
+
+ /*
+ * Deal all rates as supported rate.
+ * Because net80211 doesn't report extended supported rate or not.
+ */
+ *pos++ = WLAN_EID_SUPP_RATES;
+ *pos++ = sr->isr_nrates;
+ os_memcpy(pos, sr->isr_rates, sr->isr_nrates);
+ pos += sr->isr_nrates;
+
+ *pos++ = WLAN_EID_ERP_INFO;
+ *pos++ = 1;
+ *pos++ = sr->isr_erp;
+
+#if defined(__DragonFly__) || defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
+ os_memcpy(pos, (u8 *)(sr + 1) + sr->isr_ssid_len + sr->isr_meshid_len,
+ sr->isr_ie_len);
+#else
+ os_memcpy(pos, (u8 *)(sr + 1) + sr->isr_ssid_len, sr->isr_ie_len);
+#endif
+ pos += sr->isr_ie_len;
+
+ result->ie_len = pos - (u8 *)(result + 1);
+
+ tmp = os_realloc_array(res->res, res->num + 1,
+ sizeof(struct wpa_scan_res *));
+ if (tmp == NULL) {
+ os_free(result);
+ return;
+ }
+ tmp[res->num++] = result;
+ res->res = tmp;
+}
+
+struct wpa_scan_results *
+wpa_driver_bsd_get_scan_results2(void *priv)
+{
+ struct ieee80211req_scan_result *sr;
+ struct wpa_scan_results *res;
+ int len, rest;
+ uint8_t buf[24*1024], *pos;
+
+ len = get80211var(priv, IEEE80211_IOC_SCAN_RESULTS, buf, 24*1024);
+ if (len < 0)
+ return NULL;
+
+ res = os_zalloc(sizeof(*res));
+ if (res == NULL)
+ return NULL;
+
+ pos = buf;
+ rest = len;
+ while (rest >= sizeof(struct ieee80211req_scan_result)) {
+ sr = (struct ieee80211req_scan_result *)pos;
+ wpa_driver_bsd_add_scan_entry(res, sr);
+ pos += sr->isr_len;
+ rest -= sr->isr_len;
+ }
+
+ wpa_printf(MSG_DEBUG, "Received %d bytes of scan results (%lu BSSes)",
+ len, (unsigned long)res->num);
+
+ return res;
+}
+
+static int wpa_driver_bsd_capa(struct bsd_driver_data *drv)
+{
+#ifdef IEEE80211_IOC_DEVCAPS
+/* kernel definitions copied from net80211/ieee80211_var.h */
+#define IEEE80211_CIPHER_WEP 0
+#define IEEE80211_CIPHER_TKIP 1
+#define IEEE80211_CIPHER_AES_CCM 3
+#define IEEE80211_CRYPTO_WEP (1<<IEEE80211_CIPHER_WEP)
+#define IEEE80211_CRYPTO_TKIP (1<<IEEE80211_CIPHER_TKIP)
+#define IEEE80211_CRYPTO_AES_CCM (1<<IEEE80211_CIPHER_AES_CCM)
+#define IEEE80211_C_HOSTAP 0x00000400 /* CAPABILITY: HOSTAP avail */
+#define IEEE80211_C_WPA1 0x00800000 /* CAPABILITY: WPA1 avail */
+#define IEEE80211_C_WPA2 0x01000000 /* CAPABILITY: WPA2 avail */
+ struct ieee80211_devcaps_req devcaps;
+
+ if (get80211var(drv, IEEE80211_IOC_DEVCAPS, &devcaps,
+ sizeof(devcaps)) < 0) {
+ wpa_printf(MSG_ERROR, "failed to IEEE80211_IOC_DEVCAPS: %s",
+ strerror(errno));
+ return -1;
+ }
+
+ wpa_printf(MSG_DEBUG, "%s: drivercaps=0x%08x,cryptocaps=0x%08x",
+ __func__, devcaps.dc_drivercaps, devcaps.dc_cryptocaps);
+
+ if (devcaps.dc_drivercaps & IEEE80211_C_WPA1)
+ drv->capa.key_mgmt = WPA_DRIVER_CAPA_KEY_MGMT_WPA |
+ WPA_DRIVER_CAPA_KEY_MGMT_WPA_PSK;
+ if (devcaps.dc_drivercaps & IEEE80211_C_WPA2)
+ drv->capa.key_mgmt = WPA_DRIVER_CAPA_KEY_MGMT_WPA2 |
+ WPA_DRIVER_CAPA_KEY_MGMT_WPA2_PSK;
+
+#ifdef __FreeBSD__
+ drv->capa.enc |= WPA_DRIVER_CAPA_ENC_WEP40 |
+ WPA_DRIVER_CAPA_ENC_WEP104 |
+ WPA_DRIVER_CAPA_ENC_TKIP |
+ WPA_DRIVER_CAPA_ENC_CCMP;
+#else
+ /*
+ * XXX
+ * FreeBSD exports hardware cryptocaps. These have no meaning for wpa
+ * since net80211 performs software crypto.
+ */
+ if (devcaps.dc_cryptocaps & IEEE80211_CRYPTO_WEP)
+ drv->capa.enc |= WPA_DRIVER_CAPA_ENC_WEP40 |
+ WPA_DRIVER_CAPA_ENC_WEP104;
+ if (devcaps.dc_cryptocaps & IEEE80211_CRYPTO_TKIP)
+ drv->capa.enc |= WPA_DRIVER_CAPA_ENC_TKIP;
+ if (devcaps.dc_cryptocaps & IEEE80211_CRYPTO_AES_CCM)
+ drv->capa.enc |= WPA_DRIVER_CAPA_ENC_CCMP;
+#endif
+
+ if (devcaps.dc_drivercaps & IEEE80211_C_HOSTAP)
+ drv->capa.flags |= WPA_DRIVER_FLAGS_AP;
+#undef IEEE80211_CIPHER_WEP
+#undef IEEE80211_CIPHER_TKIP
+#undef IEEE80211_CIPHER_AES_CCM
+#undef IEEE80211_CRYPTO_WEP
+#undef IEEE80211_CRYPTO_TKIP
+#undef IEEE80211_CRYPTO_AES_CCM
+#undef IEEE80211_C_HOSTAP
+#undef IEEE80211_C_WPA1
+#undef IEEE80211_C_WPA2
+#else /* IEEE80211_IOC_DEVCAPS */
+ /* For now, assume TKIP, CCMP, WPA, WPA2 are supported */
+ drv->capa.key_mgmt = WPA_DRIVER_CAPA_KEY_MGMT_WPA |
+ WPA_DRIVER_CAPA_KEY_MGMT_WPA_PSK |
+ WPA_DRIVER_CAPA_KEY_MGMT_WPA2 |
+ WPA_DRIVER_CAPA_KEY_MGMT_WPA2_PSK;
+ drv->capa.enc = WPA_DRIVER_CAPA_ENC_WEP40 |
+ WPA_DRIVER_CAPA_ENC_WEP104 |
+ WPA_DRIVER_CAPA_ENC_TKIP |
+ WPA_DRIVER_CAPA_ENC_CCMP;
+ drv->capa.flags |= WPA_DRIVER_FLAGS_AP;
+#endif /* IEEE80211_IOC_DEVCAPS */
+#ifdef IEEE80211_IOC_SCAN_MAX_SSID
+ drv->capa.max_scan_ssids = IEEE80211_IOC_SCAN_MAX_SSID;
+#else /* IEEE80211_IOC_SCAN_MAX_SSID */
+ drv->capa.max_scan_ssids = 1;
+#endif /* IEEE80211_IOC_SCAN_MAX_SSID */
+ drv->capa.auth = WPA_DRIVER_AUTH_OPEN |
+ WPA_DRIVER_AUTH_SHARED |
+ WPA_DRIVER_AUTH_LEAP;
+ return 0;
+}
+
+static enum ieee80211_opmode
+get80211opmode(struct bsd_driver_data *drv)
+{
+ struct ifmediareq ifmr;
+
+ (void) memset(&ifmr, 0, sizeof(ifmr));
+ (void) os_strlcpy(ifmr.ifm_name, drv->ifname, sizeof(ifmr.ifm_name));
+
+ if (ioctl(drv->sock, SIOCGIFMEDIA, (caddr_t)&ifmr) >= 0) {
+ if (ifmr.ifm_current & IFM_IEEE80211_ADHOC) {
+ if (ifmr.ifm_current & IFM_FLAG0)
+ return IEEE80211_M_AHDEMO;
+ else
+ return IEEE80211_M_IBSS;
+ }
+ if (ifmr.ifm_current & IFM_IEEE80211_HOSTAP)
+ return IEEE80211_M_HOSTAP;
+ if (ifmr.ifm_current & IFM_IEEE80211_IBSS)
+ return IEEE80211_M_IBSS;
+ if (ifmr.ifm_current & IFM_IEEE80211_MONITOR)
+ return IEEE80211_M_MONITOR;
+#ifdef IEEE80211_M_MBSS
+ if (ifmr.ifm_current & IFM_IEEE80211_MBSS)
+ return IEEE80211_M_MBSS;
+#endif /* IEEE80211_M_MBSS */
+ }
+ return IEEE80211_M_STA;
+}
+
+static void *
+wpa_driver_bsd_init(void *ctx, const char *ifname)
+{
+#define GETPARAM(drv, param, v) \
+ (((v) = get80211param(drv, param)) != -1)
+ struct bsd_driver_data *drv;
+
+ drv = os_zalloc(sizeof(*drv));
+ if (drv == NULL)
+ return NULL;
+
+ drv->event_buf_len = rtbuf_len();
+
+ drv->event_buf = os_malloc(drv->event_buf_len);
+ if (drv->event_buf == NULL) {
+ wpa_printf(MSG_ERROR, "%s: os_malloc() failed", __func__);
+ goto fail1;
+ }
+
+ /*
+ * NB: We require the interface name be mappable to an index.
+ * This implies we do not support having wpa_supplicant
+ * wait for an interface to appear. This seems ok; that
+ * doesn't belong here; it's really the job of devd.
+ */
+ drv->ifindex = if_nametoindex(ifname);
+ if (drv->ifindex == 0) {
+ wpa_printf(MSG_DEBUG, "%s: interface %s does not exist",
+ __func__, ifname);
+ goto fail1;
+ }
+ drv->sock = socket(PF_INET, SOCK_DGRAM, 0);
+ if (drv->sock < 0)
+ goto fail1;
+
+ os_strlcpy(drv->ifname, ifname, sizeof(drv->ifname));
+ /* Down interface during setup. */
+ if (bsd_ctrl_iface(drv, 0) < 0)
+ goto fail;
+
+ drv->route = socket(PF_ROUTE, SOCK_RAW, 0);
+ if (drv->route < 0)
+ goto fail;
+ eloop_register_read_sock(drv->route,
+ wpa_driver_bsd_event_receive, ctx, drv);
+
+ drv->ctx = ctx;
+
+ if (!GETPARAM(drv, IEEE80211_IOC_ROAMING, drv->prev_roaming)) {
+ wpa_printf(MSG_DEBUG, "%s: failed to get roaming state: %s",
+ __func__, strerror(errno));
+ goto fail;
+ }
+ if (!GETPARAM(drv, IEEE80211_IOC_PRIVACY, drv->prev_privacy)) {
+ wpa_printf(MSG_DEBUG, "%s: failed to get privacy state: %s",
+ __func__, strerror(errno));
+ goto fail;
+ }
+ if (!GETPARAM(drv, IEEE80211_IOC_WPA, drv->prev_wpa)) {
+ wpa_printf(MSG_DEBUG, "%s: failed to get wpa state: %s",
+ __func__, strerror(errno));
+ goto fail;
+ }
+
+ if (wpa_driver_bsd_capa(drv))
+ goto fail;
+
+ drv->opmode = get80211opmode(drv);
+
+ return drv;
+fail:
+ close(drv->sock);
+fail1:
+ os_free(drv->event_buf);
+ os_free(drv);
+ return NULL;
+#undef GETPARAM
+}
+
+static void
+wpa_driver_bsd_deinit(void *priv)
+{
+ struct bsd_driver_data *drv = priv;
+
+ wpa_driver_bsd_set_wpa(drv, 0);
+ eloop_unregister_read_sock(drv->route);
+
+ /* NB: mark interface down */
+ bsd_ctrl_iface(drv, 0);
+
+ wpa_driver_bsd_set_wpa_internal(drv, drv->prev_wpa, drv->prev_privacy);
+ if (set80211param(drv, IEEE80211_IOC_ROAMING, drv->prev_roaming) < 0)
+ wpa_printf(MSG_DEBUG, "%s: failed to restore roaming state",
+ __func__);
+
+ if (drv->sock_xmit != NULL)
+ l2_packet_deinit(drv->sock_xmit);
+ (void) close(drv->route); /* ioctl socket */
+ (void) close(drv->sock); /* event socket */
+ os_free(drv->event_buf);
+ os_free(drv);
+}
+
+static int
+wpa_driver_bsd_get_capa(void *priv, struct wpa_driver_capa *capa)
+{
+ struct bsd_driver_data *drv = priv;
+
+ os_memcpy(capa, &drv->capa, sizeof(*capa));
+ return 0;
+}
+#endif /* HOSTAPD */
+
+
+const struct wpa_driver_ops wpa_driver_bsd_ops = {
+ .name = "bsd",
+ .desc = "BSD 802.11 support",
+#ifdef HOSTAPD
+ .hapd_init = bsd_init,
+ .hapd_deinit = bsd_deinit,
+ .set_privacy = bsd_set_privacy,
+ .get_seqnum = bsd_get_seqnum,
+ .flush = bsd_flush,
+ .read_sta_data = bsd_read_sta_driver_data,
+ .sta_disassoc = bsd_sta_disassoc,
+ .sta_deauth = bsd_sta_deauth,
+ .sta_set_flags = bsd_set_sta_authorized,
+ .commit = bsd_commit,
+#else /* HOSTAPD */
+ .init = wpa_driver_bsd_init,
+ .deinit = wpa_driver_bsd_deinit,
+ .get_bssid = wpa_driver_bsd_get_bssid,
+ .get_ssid = wpa_driver_bsd_get_ssid,
+ .set_countermeasures = wpa_driver_bsd_set_countermeasures,
+ .scan2 = wpa_driver_bsd_scan,
+ .get_scan_results2 = wpa_driver_bsd_get_scan_results2,
+ .deauthenticate = wpa_driver_bsd_deauthenticate,
+ .associate = wpa_driver_bsd_associate,
+ .get_capa = wpa_driver_bsd_get_capa,
+#endif /* HOSTAPD */
+ .set_freq = bsd_set_freq,
+ .set_key = bsd_set_key,
+ .set_ieee8021x = bsd_set_ieee8021x,
+ .hapd_set_ssid = bsd_set_ssid,
+ .hapd_get_ssid = bsd_get_ssid,
+ .hapd_send_eapol = bsd_send_eapol,
+ .set_generic_elem = bsd_set_opt_ie,
+};
diff --git a/freebsd/contrib/wpa/src/drivers/driver_common.c b/freebsd/contrib/wpa/src/drivers/driver_common.c
new file mode 100644
index 00000000..28f483e9
--- /dev/null
+++ b/freebsd/contrib/wpa/src/drivers/driver_common.c
@@ -0,0 +1,222 @@
+#include <machine/rtems-bsd-user-space.h>
+
+/*
+ * Common driver-related functions
+ * Copyright (c) 2003-2011, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+#include "utils/common.h"
+#include "driver.h"
+
+void wpa_scan_results_free(struct wpa_scan_results *res)
+{
+ size_t i;
+
+ if (res == NULL)
+ return;
+
+ for (i = 0; i < res->num; i++)
+ os_free(res->res[i]);
+ os_free(res->res);
+ os_free(res);
+}
+
+
+const char * event_to_string(enum wpa_event_type event)
+{
+#define E2S(n) case EVENT_ ## n: return #n
+ switch (event) {
+ E2S(ASSOC);
+ E2S(DISASSOC);
+ E2S(MICHAEL_MIC_FAILURE);
+ E2S(SCAN_RESULTS);
+ E2S(ASSOCINFO);
+ E2S(INTERFACE_STATUS);
+ E2S(PMKID_CANDIDATE);
+ E2S(STKSTART);
+ E2S(TDLS);
+ E2S(FT_RESPONSE);
+ E2S(IBSS_RSN_START);
+ E2S(AUTH);
+ E2S(DEAUTH);
+ E2S(ASSOC_REJECT);
+ E2S(AUTH_TIMED_OUT);
+ E2S(ASSOC_TIMED_OUT);
+ E2S(WPS_BUTTON_PUSHED);
+ E2S(TX_STATUS);
+ E2S(RX_FROM_UNKNOWN);
+ E2S(RX_MGMT);
+ E2S(REMAIN_ON_CHANNEL);
+ E2S(CANCEL_REMAIN_ON_CHANNEL);
+ E2S(RX_PROBE_REQ);
+ E2S(NEW_STA);
+ E2S(EAPOL_RX);
+ E2S(SIGNAL_CHANGE);
+ E2S(INTERFACE_ENABLED);
+ E2S(INTERFACE_DISABLED);
+ E2S(CHANNEL_LIST_CHANGED);
+ E2S(INTERFACE_UNAVAILABLE);
+ E2S(BEST_CHANNEL);
+ E2S(UNPROT_DEAUTH);
+ E2S(UNPROT_DISASSOC);
+ E2S(STATION_LOW_ACK);
+ E2S(IBSS_PEER_LOST);
+ E2S(DRIVER_GTK_REKEY);
+ E2S(SCHED_SCAN_STOPPED);
+ E2S(DRIVER_CLIENT_POLL_OK);
+ E2S(EAPOL_TX_STATUS);
+ E2S(CH_SWITCH);
+ E2S(WNM);
+ E2S(CONNECT_FAILED_REASON);
+ E2S(DFS_RADAR_DETECTED);
+ E2S(DFS_CAC_FINISHED);
+ E2S(DFS_CAC_ABORTED);
+ E2S(DFS_NOP_FINISHED);
+ E2S(SURVEY);
+ E2S(SCAN_STARTED);
+ E2S(AVOID_FREQUENCIES);
+ E2S(NEW_PEER_CANDIDATE);
+ E2S(ACS_CHANNEL_SELECTED);
+ E2S(DFS_CAC_STARTED);
+ }
+
+ return "UNKNOWN";
+#undef E2S
+}
+
+
+const char * channel_width_to_string(enum chan_width width)
+{
+ switch (width) {
+ case CHAN_WIDTH_20_NOHT:
+ return "20 MHz (no HT)";
+ case CHAN_WIDTH_20:
+ return "20 MHz";
+ case CHAN_WIDTH_40:
+ return "40 MHz";
+ case CHAN_WIDTH_80:
+ return "80 MHz";
+ case CHAN_WIDTH_80P80:
+ return "80+80 MHz";
+ case CHAN_WIDTH_160:
+ return "160 MHz";
+ default:
+ return "unknown";
+ }
+}
+
+
+int ht_supported(const struct hostapd_hw_modes *mode)
+{
+ if (!(mode->flags & HOSTAPD_MODE_FLAG_HT_INFO_KNOWN)) {
+ /*
+ * The driver did not indicate whether it supports HT. Assume
+ * it does to avoid connection issues.
+ */
+ return 1;
+ }
+
+ /*
+ * IEEE Std 802.11n-2009 20.1.1:
+ * An HT non-AP STA shall support all EQM rates for one spatial stream.
+ */
+ return mode->mcs_set[0] == 0xff;
+}
+
+
+int vht_supported(const struct hostapd_hw_modes *mode)
+{
+ if (!(mode->flags & HOSTAPD_MODE_FLAG_VHT_INFO_KNOWN)) {
+ /*
+ * The driver did not indicate whether it supports VHT. Assume
+ * it does to avoid connection issues.
+ */
+ return 1;
+ }
+
+ /*
+ * A VHT non-AP STA shall support MCS 0-7 for one spatial stream.
+ * TODO: Verify if this complies with the standard
+ */
+ return (mode->vht_mcs_set[0] & 0x3) != 3;
+}
+
+
+static int wpa_check_wowlan_trigger(const char *start, const char *trigger,
+ int capa_trigger, u8 *param_trigger)
+{
+ if (os_strcmp(start, trigger) != 0)
+ return 0;
+ if (!capa_trigger)
+ return 0;
+
+ *param_trigger = 1;
+ return 1;
+}
+
+
+struct wowlan_triggers *
+wpa_get_wowlan_triggers(const char *wowlan_triggers,
+ const struct wpa_driver_capa *capa)
+{
+ struct wowlan_triggers *triggers;
+ char *start, *end, *buf;
+ int last;
+
+ if (!wowlan_triggers)
+ return NULL;
+
+ buf = os_strdup(wowlan_triggers);
+ if (buf == NULL)
+ return NULL;
+
+ triggers = os_zalloc(sizeof(*triggers));
+ if (triggers == NULL)
+ goto out;
+
+#define CHECK_TRIGGER(trigger) \
+ wpa_check_wowlan_trigger(start, #trigger, \
+ capa->wowlan_triggers.trigger, \
+ &triggers->trigger)
+
+ start = buf;
+ while (*start != '\0') {
+ while (isblank(*start))
+ start++;
+ if (*start == '\0')
+ break;
+ end = start;
+ while (!isblank(*end) && *end != '\0')
+ end++;
+ last = *end == '\0';
+ *end = '\0';
+
+ if (!CHECK_TRIGGER(any) &&
+ !CHECK_TRIGGER(disconnect) &&
+ !CHECK_TRIGGER(magic_pkt) &&
+ !CHECK_TRIGGER(gtk_rekey_failure) &&
+ !CHECK_TRIGGER(eap_identity_req) &&
+ !CHECK_TRIGGER(four_way_handshake) &&
+ !CHECK_TRIGGER(rfkill_release)) {
+ wpa_printf(MSG_DEBUG,
+ "Unknown/unsupported wowlan trigger '%s'",
+ start);
+ os_free(triggers);
+ triggers = NULL;
+ goto out;
+ }
+
+ if (last)
+ break;
+ start = end + 1;
+ }
+#undef CHECK_TRIGGER
+
+out:
+ os_free(buf);
+ return triggers;
+}
diff --git a/freebsd/contrib/wpa/src/drivers/driver_ndis.c b/freebsd/contrib/wpa/src/drivers/driver_ndis.c
new file mode 100644
index 00000000..6b3b26bc
--- /dev/null
+++ b/freebsd/contrib/wpa/src/drivers/driver_ndis.c
@@ -0,0 +1,3221 @@
+#include <machine/rtems-bsd-user-space.h>
+
+/*
+ * WPA Supplicant - Windows/NDIS driver interface
+ * 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.
+ */
+
+#ifdef __CYGWIN__
+/* Avoid some header file conflicts by not including standard headers for
+ * cygwin builds when Packet32.h is included. */
+#include "build_config.h"
+int close(int fd);
+#else /* __CYGWIN__ */
+#include "includes.h"
+#endif /* __CYGWIN__ */
+#ifdef CONFIG_USE_NDISUIO
+#include <winsock2.h>
+#else /* CONFIG_USE_NDISUIO */
+#include <Packet32.h>
+#endif /* CONFIG_USE_NDISUIO */
+#ifdef __MINGW32_VERSION
+#include <ddk/ntddndis.h>
+#else /* __MINGW32_VERSION */
+#include <ntddndis.h>
+#endif /* __MINGW32_VERSION */
+
+#ifdef _WIN32_WCE
+#include <winioctl.h>
+#include <nuiouser.h>
+#include <devload.h>
+#endif /* _WIN32_WCE */
+
+#include "common.h"
+#include "driver.h"
+#include "eloop.h"
+#include "common/ieee802_11_defs.h"
+#include "driver_ndis.h"
+
+int wpa_driver_register_event_cb(struct wpa_driver_ndis_data *drv);
+#ifdef CONFIG_NDIS_EVENTS_INTEGRATED
+void wpa_driver_ndis_event_pipe_cb(void *eloop_data, void *user_data);
+#endif /* CONFIG_NDIS_EVENTS_INTEGRATED */
+
+static void wpa_driver_ndis_deinit(void *priv);
+static void wpa_driver_ndis_poll(void *drv);
+static void wpa_driver_ndis_poll_timeout(void *eloop_ctx, void *timeout_ctx);
+static int wpa_driver_ndis_adapter_init(struct wpa_driver_ndis_data *drv);
+static int wpa_driver_ndis_adapter_open(struct wpa_driver_ndis_data *drv);
+static void wpa_driver_ndis_adapter_close(struct wpa_driver_ndis_data *drv);
+
+
+static const u8 pae_group_addr[ETH_ALEN] =
+{ 0x01, 0x80, 0xc2, 0x00, 0x00, 0x03 };
+
+
+/* FIX: to be removed once this can be compiled with the complete NDIS
+ * header files */
+#ifndef OID_802_11_BSSID
+#define OID_802_3_MULTICAST_LIST 0x01010103
+#define OID_802_11_BSSID 0x0d010101
+#define OID_802_11_SSID 0x0d010102
+#define OID_802_11_INFRASTRUCTURE_MODE 0x0d010108
+#define OID_802_11_ADD_WEP 0x0D010113
+#define OID_802_11_REMOVE_WEP 0x0D010114
+#define OID_802_11_DISASSOCIATE 0x0D010115
+#define OID_802_11_BSSID_LIST 0x0d010217
+#define OID_802_11_AUTHENTICATION_MODE 0x0d010118
+#define OID_802_11_PRIVACY_FILTER 0x0d010119
+#define OID_802_11_BSSID_LIST_SCAN 0x0d01011A
+#define OID_802_11_WEP_STATUS 0x0d01011B
+#define OID_802_11_ENCRYPTION_STATUS OID_802_11_WEP_STATUS
+#define OID_802_11_ADD_KEY 0x0d01011D
+#define OID_802_11_REMOVE_KEY 0x0d01011E
+#define OID_802_11_ASSOCIATION_INFORMATION 0x0d01011F
+#define OID_802_11_TEST 0x0d010120
+#define OID_802_11_CAPABILITY 0x0d010122
+#define OID_802_11_PMKID 0x0d010123
+
+#define NDIS_802_11_LENGTH_SSID 32
+#define NDIS_802_11_LENGTH_RATES 8
+#define NDIS_802_11_LENGTH_RATES_EX 16
+
+typedef UCHAR NDIS_802_11_MAC_ADDRESS[6];
+
+typedef struct NDIS_802_11_SSID {
+ ULONG SsidLength;
+ UCHAR Ssid[NDIS_802_11_LENGTH_SSID];
+} NDIS_802_11_SSID;
+
+typedef LONG NDIS_802_11_RSSI;
+
+typedef enum NDIS_802_11_NETWORK_TYPE {
+ Ndis802_11FH,
+ Ndis802_11DS,
+ Ndis802_11OFDM5,
+ Ndis802_11OFDM24,
+ Ndis802_11NetworkTypeMax
+} NDIS_802_11_NETWORK_TYPE;
+
+typedef struct NDIS_802_11_CONFIGURATION_FH {
+ ULONG Length;
+ ULONG HopPattern;
+ ULONG HopSet;
+ ULONG DwellTime;
+} NDIS_802_11_CONFIGURATION_FH;
+
+typedef struct NDIS_802_11_CONFIGURATION {
+ ULONG Length;
+ ULONG BeaconPeriod;
+ ULONG ATIMWindow;
+ ULONG DSConfig;
+ NDIS_802_11_CONFIGURATION_FH FHConfig;
+} NDIS_802_11_CONFIGURATION;
+
+typedef enum NDIS_802_11_NETWORK_INFRASTRUCTURE {
+ Ndis802_11IBSS,
+ Ndis802_11Infrastructure,
+ Ndis802_11AutoUnknown,
+ Ndis802_11InfrastructureMax
+} NDIS_802_11_NETWORK_INFRASTRUCTURE;
+
+typedef enum NDIS_802_11_AUTHENTICATION_MODE {
+ Ndis802_11AuthModeOpen,
+ Ndis802_11AuthModeShared,
+ Ndis802_11AuthModeAutoSwitch,
+ Ndis802_11AuthModeWPA,
+ Ndis802_11AuthModeWPAPSK,
+ Ndis802_11AuthModeWPANone,
+ Ndis802_11AuthModeWPA2,
+ Ndis802_11AuthModeWPA2PSK,
+ Ndis802_11AuthModeMax
+} NDIS_802_11_AUTHENTICATION_MODE;
+
+typedef enum NDIS_802_11_WEP_STATUS {
+ Ndis802_11WEPEnabled,
+ Ndis802_11Encryption1Enabled = Ndis802_11WEPEnabled,
+ Ndis802_11WEPDisabled,
+ Ndis802_11EncryptionDisabled = Ndis802_11WEPDisabled,
+ Ndis802_11WEPKeyAbsent,
+ Ndis802_11Encryption1KeyAbsent = Ndis802_11WEPKeyAbsent,
+ Ndis802_11WEPNotSupported,
+ Ndis802_11EncryptionNotSupported = Ndis802_11WEPNotSupported,
+ Ndis802_11Encryption2Enabled,
+ Ndis802_11Encryption2KeyAbsent,
+ Ndis802_11Encryption3Enabled,
+ Ndis802_11Encryption3KeyAbsent
+} NDIS_802_11_WEP_STATUS, NDIS_802_11_ENCRYPTION_STATUS;
+
+typedef enum NDIS_802_11_PRIVACY_FILTER {
+ Ndis802_11PrivFilterAcceptAll,
+ Ndis802_11PrivFilter8021xWEP
+} NDIS_802_11_PRIVACY_FILTER;
+
+typedef UCHAR NDIS_802_11_RATES[NDIS_802_11_LENGTH_RATES];
+typedef UCHAR NDIS_802_11_RATES_EX[NDIS_802_11_LENGTH_RATES_EX];
+
+typedef struct NDIS_WLAN_BSSID_EX {
+ ULONG Length;
+ NDIS_802_11_MAC_ADDRESS MacAddress; /* BSSID */
+ UCHAR Reserved[2];
+ NDIS_802_11_SSID Ssid;
+ ULONG Privacy;
+ NDIS_802_11_RSSI Rssi;
+ NDIS_802_11_NETWORK_TYPE NetworkTypeInUse;
+ NDIS_802_11_CONFIGURATION Configuration;
+ NDIS_802_11_NETWORK_INFRASTRUCTURE InfrastructureMode;
+ NDIS_802_11_RATES_EX SupportedRates;
+ ULONG IELength;
+ UCHAR IEs[1];
+} NDIS_WLAN_BSSID_EX;
+
+typedef struct NDIS_802_11_BSSID_LIST_EX {
+ ULONG NumberOfItems;
+ NDIS_WLAN_BSSID_EX Bssid[1];
+} NDIS_802_11_BSSID_LIST_EX;
+
+typedef struct NDIS_802_11_FIXED_IEs {
+ UCHAR Timestamp[8];
+ USHORT BeaconInterval;
+ USHORT Capabilities;
+} NDIS_802_11_FIXED_IEs;
+
+typedef struct NDIS_802_11_WEP {
+ ULONG Length;
+ ULONG KeyIndex;
+ ULONG KeyLength;
+ UCHAR KeyMaterial[1];
+} NDIS_802_11_WEP;
+
+typedef ULONG NDIS_802_11_KEY_INDEX;
+typedef ULONGLONG NDIS_802_11_KEY_RSC;
+
+typedef struct NDIS_802_11_KEY {
+ ULONG Length;
+ ULONG KeyIndex;
+ ULONG KeyLength;
+ NDIS_802_11_MAC_ADDRESS BSSID;
+ NDIS_802_11_KEY_RSC KeyRSC;
+ UCHAR KeyMaterial[1];
+} NDIS_802_11_KEY;
+
+typedef struct NDIS_802_11_REMOVE_KEY {
+ ULONG Length;
+ ULONG KeyIndex;
+ NDIS_802_11_MAC_ADDRESS BSSID;
+} NDIS_802_11_REMOVE_KEY;
+
+typedef struct NDIS_802_11_AI_REQFI {
+ USHORT Capabilities;
+ USHORT ListenInterval;
+ NDIS_802_11_MAC_ADDRESS CurrentAPAddress;
+} NDIS_802_11_AI_REQFI;
+
+typedef struct NDIS_802_11_AI_RESFI {
+ USHORT Capabilities;
+ USHORT StatusCode;
+ USHORT AssociationId;
+} NDIS_802_11_AI_RESFI;
+
+typedef struct NDIS_802_11_ASSOCIATION_INFORMATION {
+ ULONG Length;
+ USHORT AvailableRequestFixedIEs;
+ NDIS_802_11_AI_REQFI RequestFixedIEs;
+ ULONG RequestIELength;
+ ULONG OffsetRequestIEs;
+ USHORT AvailableResponseFixedIEs;
+ NDIS_802_11_AI_RESFI ResponseFixedIEs;
+ ULONG ResponseIELength;
+ ULONG OffsetResponseIEs;
+} NDIS_802_11_ASSOCIATION_INFORMATION;
+
+typedef struct NDIS_802_11_AUTHENTICATION_ENCRYPTION {
+ NDIS_802_11_AUTHENTICATION_MODE AuthModeSupported;
+ NDIS_802_11_ENCRYPTION_STATUS EncryptStatusSupported;
+} NDIS_802_11_AUTHENTICATION_ENCRYPTION;
+
+typedef struct NDIS_802_11_CAPABILITY {
+ ULONG Length;
+ ULONG Version;
+ ULONG NoOfPMKIDs;
+ ULONG NoOfAuthEncryptPairsSupported;
+ NDIS_802_11_AUTHENTICATION_ENCRYPTION
+ AuthenticationEncryptionSupported[1];
+} NDIS_802_11_CAPABILITY;
+
+typedef UCHAR NDIS_802_11_PMKID_VALUE[16];
+
+typedef struct BSSID_INFO {
+ NDIS_802_11_MAC_ADDRESS BSSID;
+ NDIS_802_11_PMKID_VALUE PMKID;
+} BSSID_INFO;
+
+typedef struct NDIS_802_11_PMKID {
+ ULONG Length;
+ ULONG BSSIDInfoCount;
+ BSSID_INFO BSSIDInfo[1];
+} NDIS_802_11_PMKID;
+
+typedef enum NDIS_802_11_STATUS_TYPE {
+ Ndis802_11StatusType_Authentication,
+ Ndis802_11StatusType_PMKID_CandidateList = 2,
+ Ndis802_11StatusTypeMax
+} NDIS_802_11_STATUS_TYPE;
+
+typedef struct NDIS_802_11_STATUS_INDICATION {
+ NDIS_802_11_STATUS_TYPE StatusType;
+} NDIS_802_11_STATUS_INDICATION;
+
+typedef struct PMKID_CANDIDATE {
+ NDIS_802_11_MAC_ADDRESS BSSID;
+ ULONG Flags;
+} PMKID_CANDIDATE;
+
+#define NDIS_802_11_PMKID_CANDIDATE_PREAUTH_ENABLED 0x01
+
+typedef struct NDIS_802_11_PMKID_CANDIDATE_LIST {
+ ULONG Version;
+ ULONG NumCandidates;
+ PMKID_CANDIDATE CandidateList[1];
+} NDIS_802_11_PMKID_CANDIDATE_LIST;
+
+typedef struct NDIS_802_11_AUTHENTICATION_REQUEST {
+ ULONG Length;
+ NDIS_802_11_MAC_ADDRESS Bssid;
+ ULONG Flags;
+} NDIS_802_11_AUTHENTICATION_REQUEST;
+
+#define NDIS_802_11_AUTH_REQUEST_REAUTH 0x01
+#define NDIS_802_11_AUTH_REQUEST_KEYUPDATE 0x02
+#define NDIS_802_11_AUTH_REQUEST_PAIRWISE_ERROR 0x06
+#define NDIS_802_11_AUTH_REQUEST_GROUP_ERROR 0x0E
+
+#endif /* OID_802_11_BSSID */
+
+
+#ifndef OID_802_11_PMKID
+/* Platform SDK for XP did not include WPA2, so add needed definitions */
+
+#define OID_802_11_CAPABILITY 0x0d010122
+#define OID_802_11_PMKID 0x0d010123
+
+#define Ndis802_11AuthModeWPA2 6
+#define Ndis802_11AuthModeWPA2PSK 7
+
+#define Ndis802_11StatusType_PMKID_CandidateList 2
+
+typedef struct NDIS_802_11_AUTHENTICATION_ENCRYPTION {
+ NDIS_802_11_AUTHENTICATION_MODE AuthModeSupported;
+ NDIS_802_11_ENCRYPTION_STATUS EncryptStatusSupported;
+} NDIS_802_11_AUTHENTICATION_ENCRYPTION;
+
+typedef struct NDIS_802_11_CAPABILITY {
+ ULONG Length;
+ ULONG Version;
+ ULONG NoOfPMKIDs;
+ ULONG NoOfAuthEncryptPairsSupported;
+ NDIS_802_11_AUTHENTICATION_ENCRYPTION
+ AuthenticationEncryptionSupported[1];
+} NDIS_802_11_CAPABILITY;
+
+typedef UCHAR NDIS_802_11_PMKID_VALUE[16];
+
+typedef struct BSSID_INFO {
+ NDIS_802_11_MAC_ADDRESS BSSID;
+ NDIS_802_11_PMKID_VALUE PMKID;
+} BSSID_INFO;
+
+typedef struct NDIS_802_11_PMKID {
+ ULONG Length;
+ ULONG BSSIDInfoCount;
+ BSSID_INFO BSSIDInfo[1];
+} NDIS_802_11_PMKID;
+
+typedef struct PMKID_CANDIDATE {
+ NDIS_802_11_MAC_ADDRESS BSSID;
+ ULONG Flags;
+} PMKID_CANDIDATE;
+
+#define NDIS_802_11_PMKID_CANDIDATE_PREAUTH_ENABLED 0x01
+
+typedef struct NDIS_802_11_PMKID_CANDIDATE_LIST {
+ ULONG Version;
+ ULONG NumCandidates;
+ PMKID_CANDIDATE CandidateList[1];
+} NDIS_802_11_PMKID_CANDIDATE_LIST;
+
+#endif /* OID_802_11_CAPABILITY */
+
+
+#ifndef OID_DOT11_CURRENT_OPERATION_MODE
+/* Native 802.11 OIDs */
+#define OID_DOT11_NDIS_START 0x0D010300
+#define OID_DOT11_CURRENT_OPERATION_MODE (OID_DOT11_NDIS_START + 8)
+#define OID_DOT11_SCAN_REQUEST (OID_DOT11_NDIS_START + 11)
+
+typedef enum _DOT11_BSS_TYPE {
+ dot11_BSS_type_infrastructure = 1,
+ dot11_BSS_type_independent = 2,
+ dot11_BSS_type_any = 3
+} DOT11_BSS_TYPE, * PDOT11_BSS_TYPE;
+
+typedef UCHAR DOT11_MAC_ADDRESS[6];
+typedef DOT11_MAC_ADDRESS * PDOT11_MAC_ADDRESS;
+
+typedef enum _DOT11_SCAN_TYPE {
+ dot11_scan_type_active = 1,
+ dot11_scan_type_passive = 2,
+ dot11_scan_type_auto = 3,
+ dot11_scan_type_forced = 0x80000000
+} DOT11_SCAN_TYPE, * PDOT11_SCAN_TYPE;
+
+typedef struct _DOT11_SCAN_REQUEST_V2 {
+ DOT11_BSS_TYPE dot11BSSType;
+ DOT11_MAC_ADDRESS dot11BSSID;
+ DOT11_SCAN_TYPE dot11ScanType;
+ BOOLEAN bRestrictedScan;
+ ULONG udot11SSIDsOffset;
+ ULONG uNumOfdot11SSIDs;
+ BOOLEAN bUseRequestIE;
+ ULONG uRequestIDsOffset;
+ ULONG uNumOfRequestIDs;
+ ULONG uPhyTypeInfosOffset;
+ ULONG uNumOfPhyTypeInfos;
+ ULONG uIEsOffset;
+ ULONG uIEsLength;
+ UCHAR ucBuffer[1];
+} DOT11_SCAN_REQUEST_V2, * PDOT11_SCAN_REQUEST_V2;
+
+#endif /* OID_DOT11_CURRENT_OPERATION_MODE */
+
+#ifdef CONFIG_USE_NDISUIO
+#ifndef _WIN32_WCE
+#ifdef __MINGW32_VERSION
+typedef ULONG NDIS_OID;
+#endif /* __MINGW32_VERSION */
+/* from nuiouser.h */
+#define FSCTL_NDISUIO_BASE FILE_DEVICE_NETWORK
+
+#define _NDISUIO_CTL_CODE(_Function, _Method, _Access) \
+ CTL_CODE(FSCTL_NDISUIO_BASE, _Function, _Method, _Access)
+
+#define IOCTL_NDISUIO_OPEN_DEVICE \
+ _NDISUIO_CTL_CODE(0x200, METHOD_BUFFERED, \
+ FILE_READ_ACCESS | FILE_WRITE_ACCESS)
+
+#define IOCTL_NDISUIO_QUERY_OID_VALUE \
+ _NDISUIO_CTL_CODE(0x201, METHOD_BUFFERED, \
+ FILE_READ_ACCESS | FILE_WRITE_ACCESS)
+
+#define IOCTL_NDISUIO_SET_OID_VALUE \
+ _NDISUIO_CTL_CODE(0x205, METHOD_BUFFERED, \
+ FILE_READ_ACCESS | FILE_WRITE_ACCESS)
+
+#define IOCTL_NDISUIO_SET_ETHER_TYPE \
+ _NDISUIO_CTL_CODE(0x202, METHOD_BUFFERED, \
+ FILE_READ_ACCESS | FILE_WRITE_ACCESS)
+
+#define IOCTL_NDISUIO_QUERY_BINDING \
+ _NDISUIO_CTL_CODE(0x203, METHOD_BUFFERED, \
+ FILE_READ_ACCESS | FILE_WRITE_ACCESS)
+
+#define IOCTL_NDISUIO_BIND_WAIT \
+ _NDISUIO_CTL_CODE(0x204, METHOD_BUFFERED, \
+ FILE_READ_ACCESS | FILE_WRITE_ACCESS)
+
+typedef struct _NDISUIO_QUERY_OID
+{
+ NDIS_OID Oid;
+ UCHAR Data[sizeof(ULONG)];
+} NDISUIO_QUERY_OID, *PNDISUIO_QUERY_OID;
+
+typedef struct _NDISUIO_SET_OID
+{
+ NDIS_OID Oid;
+ UCHAR Data[sizeof(ULONG)];
+} NDISUIO_SET_OID, *PNDISUIO_SET_OID;
+
+typedef struct _NDISUIO_QUERY_BINDING
+{
+ ULONG BindingIndex;
+ ULONG DeviceNameOffset;
+ ULONG DeviceNameLength;
+ ULONG DeviceDescrOffset;
+ ULONG DeviceDescrLength;
+} NDISUIO_QUERY_BINDING, *PNDISUIO_QUERY_BINDING;
+#endif /* _WIN32_WCE */
+#endif /* CONFIG_USE_NDISUIO */
+
+
+static int ndis_get_oid(struct wpa_driver_ndis_data *drv, unsigned int oid,
+ char *data, size_t len)
+{
+#ifdef CONFIG_USE_NDISUIO
+ NDISUIO_QUERY_OID *o;
+ size_t buflen = sizeof(*o) + len;
+ DWORD written;
+ int ret;
+ size_t hdrlen;
+
+ o = os_zalloc(buflen);
+ if (o == NULL)
+ return -1;
+ o->Oid = oid;
+#ifdef _WIN32_WCE
+ o->ptcDeviceName = drv->adapter_name;
+#endif /* _WIN32_WCE */
+ if (!DeviceIoControl(drv->ndisuio, IOCTL_NDISUIO_QUERY_OID_VALUE,
+ o, sizeof(NDISUIO_QUERY_OID), o, buflen, &written,
+ NULL)) {
+ wpa_printf(MSG_DEBUG, "NDIS: IOCTL_NDISUIO_QUERY_OID_VALUE "
+ "failed (oid=%08x): %d", oid, (int) GetLastError());
+ os_free(o);
+ return -1;
+ }
+ hdrlen = sizeof(NDISUIO_QUERY_OID) - sizeof(o->Data);
+ if (written < hdrlen) {
+ wpa_printf(MSG_DEBUG, "NDIS: query oid=%08x written (%d); "
+ "too short", oid, (unsigned int) written);
+ os_free(o);
+ return -1;
+ }
+ written -= hdrlen;
+ if (written > len) {
+ wpa_printf(MSG_DEBUG, "NDIS: query oid=%08x written (%d) > "
+ "len (%d)",oid, (unsigned int) written, len);
+ os_free(o);
+ return -1;
+ }
+ os_memcpy(data, o->Data, written);
+ ret = written;
+ os_free(o);
+ return ret;
+#else /* CONFIG_USE_NDISUIO */
+ char *buf;
+ PACKET_OID_DATA *o;
+ int ret;
+
+ buf = os_zalloc(sizeof(*o) + len);
+ if (buf == NULL)
+ return -1;
+ o = (PACKET_OID_DATA *) buf;
+ o->Oid = oid;
+ o->Length = len;
+
+ if (!PacketRequest(drv->adapter, FALSE, o)) {
+ wpa_printf(MSG_DEBUG, "%s: oid=0x%x len (%d) failed",
+ __func__, oid, len);
+ os_free(buf);
+ return -1;
+ }
+ if (o->Length > len) {
+ wpa_printf(MSG_DEBUG, "%s: oid=0x%x Length (%d) > len (%d)",
+ __func__, oid, (unsigned int) o->Length, len);
+ os_free(buf);
+ return -1;
+ }
+ os_memcpy(data, o->Data, o->Length);
+ ret = o->Length;
+ os_free(buf);
+ return ret;
+#endif /* CONFIG_USE_NDISUIO */
+}
+
+
+static int ndis_set_oid(struct wpa_driver_ndis_data *drv, unsigned int oid,
+ const char *data, size_t len)
+{
+#ifdef CONFIG_USE_NDISUIO
+ NDISUIO_SET_OID *o;
+ size_t buflen, reallen;
+ DWORD written;
+ char txt[50];
+
+ os_snprintf(txt, sizeof(txt), "NDIS: Set OID %08x", oid);
+ wpa_hexdump_key(MSG_MSGDUMP, txt, (const u8 *) data, len);
+
+ buflen = sizeof(*o) + len;
+ reallen = buflen - sizeof(o->Data);
+ o = os_zalloc(buflen);
+ if (o == NULL)
+ return -1;
+ o->Oid = oid;
+#ifdef _WIN32_WCE
+ o->ptcDeviceName = drv->adapter_name;
+#endif /* _WIN32_WCE */
+ if (data)
+ os_memcpy(o->Data, data, len);
+ if (!DeviceIoControl(drv->ndisuio, IOCTL_NDISUIO_SET_OID_VALUE,
+ o, reallen, NULL, 0, &written, NULL)) {
+ wpa_printf(MSG_DEBUG, "NDIS: IOCTL_NDISUIO_SET_OID_VALUE "
+ "(oid=%08x) failed: %d", oid, (int) GetLastError());
+ os_free(o);
+ return -1;
+ }
+ os_free(o);
+ return 0;
+#else /* CONFIG_USE_NDISUIO */
+ char *buf;
+ PACKET_OID_DATA *o;
+ char txt[50];
+
+ os_snprintf(txt, sizeof(txt), "NDIS: Set OID %08x", oid);
+ wpa_hexdump_key(MSG_MSGDUMP, txt, (const u8 *) data, len);
+
+ buf = os_zalloc(sizeof(*o) + len);
+ if (buf == NULL)
+ return -1;
+ o = (PACKET_OID_DATA *) buf;
+ o->Oid = oid;
+ o->Length = len;
+ if (data)
+ os_memcpy(o->Data, data, len);
+
+ if (!PacketRequest(drv->adapter, TRUE, o)) {
+ wpa_printf(MSG_DEBUG, "%s: oid=0x%x len (%d) failed",
+ __func__, oid, len);
+ os_free(buf);
+ return -1;
+ }
+ os_free(buf);
+ return 0;
+#endif /* CONFIG_USE_NDISUIO */
+}
+
+
+static int ndis_set_auth_mode(struct wpa_driver_ndis_data *drv, int mode)
+{
+ u32 auth_mode = mode;
+ if (ndis_set_oid(drv, OID_802_11_AUTHENTICATION_MODE,
+ (char *) &auth_mode, sizeof(auth_mode)) < 0) {
+ wpa_printf(MSG_DEBUG, "NDIS: Failed to set "
+ "OID_802_11_AUTHENTICATION_MODE (%d)",
+ (int) auth_mode);
+ return -1;
+ }
+ return 0;
+}
+
+
+static int ndis_get_auth_mode(struct wpa_driver_ndis_data *drv)
+{
+ u32 auth_mode;
+ int res;
+ res = ndis_get_oid(drv, OID_802_11_AUTHENTICATION_MODE,
+ (char *) &auth_mode, sizeof(auth_mode));
+ if (res != sizeof(auth_mode)) {
+ wpa_printf(MSG_DEBUG, "NDIS: Failed to get "
+ "OID_802_11_AUTHENTICATION_MODE");
+ return -1;
+ }
+ return auth_mode;
+}
+
+
+static int ndis_set_encr_status(struct wpa_driver_ndis_data *drv, int encr)
+{
+ u32 encr_status = encr;
+ if (ndis_set_oid(drv, OID_802_11_ENCRYPTION_STATUS,
+ (char *) &encr_status, sizeof(encr_status)) < 0) {
+ wpa_printf(MSG_DEBUG, "NDIS: Failed to set "
+ "OID_802_11_ENCRYPTION_STATUS (%d)", encr);
+ return -1;
+ }
+ return 0;
+}
+
+
+static int ndis_get_encr_status(struct wpa_driver_ndis_data *drv)
+{
+ u32 encr;
+ int res;
+ res = ndis_get_oid(drv, OID_802_11_ENCRYPTION_STATUS,
+ (char *) &encr, sizeof(encr));
+ if (res != sizeof(encr)) {
+ wpa_printf(MSG_DEBUG, "NDIS: Failed to get "
+ "OID_802_11_ENCRYPTION_STATUS");
+ return -1;
+ }
+ return encr;
+}
+
+
+static int wpa_driver_ndis_get_bssid(void *priv, u8 *bssid)
+{
+ struct wpa_driver_ndis_data *drv = priv;
+
+ if (drv->wired) {
+ /*
+ * Report PAE group address as the "BSSID" for wired
+ * connection.
+ */
+ os_memcpy(bssid, pae_group_addr, ETH_ALEN);
+ return 0;
+ }
+
+ return ndis_get_oid(drv, OID_802_11_BSSID, (char *) bssid, ETH_ALEN) <
+ 0 ? -1 : 0;
+}
+
+
+static int wpa_driver_ndis_get_ssid(void *priv, u8 *ssid)
+{
+ struct wpa_driver_ndis_data *drv = priv;
+ NDIS_802_11_SSID buf;
+ int res;
+
+ res = ndis_get_oid(drv, OID_802_11_SSID, (char *) &buf, sizeof(buf));
+ if (res < 4) {
+ wpa_printf(MSG_DEBUG, "NDIS: Failed to get SSID");
+ if (drv->wired) {
+ wpa_printf(MSG_DEBUG, "NDIS: Allow get_ssid failure "
+ "with a wired interface");
+ return 0;
+ }
+ return -1;
+ }
+ os_memcpy(ssid, buf.Ssid, buf.SsidLength);
+ return buf.SsidLength;
+}
+
+
+static int wpa_driver_ndis_set_ssid(struct wpa_driver_ndis_data *drv,
+ const u8 *ssid, size_t ssid_len)
+{
+ NDIS_802_11_SSID buf;
+
+ os_memset(&buf, 0, sizeof(buf));
+ buf.SsidLength = ssid_len;
+ os_memcpy(buf.Ssid, ssid, ssid_len);
+ /*
+ * Make sure radio is marked enabled here so that scan request will not
+ * force SSID to be changed to a random one in order to enable radio at
+ * that point.
+ */
+ drv->radio_enabled = 1;
+ return ndis_set_oid(drv, OID_802_11_SSID, (char *) &buf, sizeof(buf));
+}
+
+
+/* Disconnect using OID_802_11_DISASSOCIATE. This will also turn the radio off.
+ */
+static int wpa_driver_ndis_radio_off(struct wpa_driver_ndis_data *drv)
+{
+ drv->radio_enabled = 0;
+ return ndis_set_oid(drv, OID_802_11_DISASSOCIATE, " ", 4);
+}
+
+
+/* Disconnect by setting SSID to random (i.e., likely not used). */
+static int wpa_driver_ndis_disconnect(struct wpa_driver_ndis_data *drv)
+{
+ char ssid[SSID_MAX_LEN];
+ int i;
+ for (i = 0; i < SSID_MAX_LEN; i++)
+ ssid[i] = rand() & 0xff;
+ return wpa_driver_ndis_set_ssid(drv, (u8 *) ssid, SSID_MAX_LEN);
+}
+
+
+static int wpa_driver_ndis_deauthenticate(void *priv, const u8 *addr,
+ int reason_code)
+{
+ struct wpa_driver_ndis_data *drv = priv;
+ return wpa_driver_ndis_disconnect(drv);
+}
+
+
+static void wpa_driver_ndis_scan_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+ wpa_printf(MSG_DEBUG, "Scan timeout - try to get results");
+ wpa_supplicant_event(timeout_ctx, EVENT_SCAN_RESULTS, NULL);
+}
+
+
+static int wpa_driver_ndis_scan_native80211(
+ struct wpa_driver_ndis_data *drv,
+ struct wpa_driver_scan_params *params)
+{
+ DOT11_SCAN_REQUEST_V2 req;
+ int res;
+
+ os_memset(&req, 0, sizeof(req));
+ req.dot11BSSType = dot11_BSS_type_any;
+ os_memset(req.dot11BSSID, 0xff, ETH_ALEN);
+ req.dot11ScanType = dot11_scan_type_auto;
+ res = ndis_set_oid(drv, OID_DOT11_SCAN_REQUEST, (char *) &req,
+ sizeof(req));
+ eloop_cancel_timeout(wpa_driver_ndis_scan_timeout, drv, drv->ctx);
+ eloop_register_timeout(7, 0, wpa_driver_ndis_scan_timeout, drv,
+ drv->ctx);
+ return res;
+}
+
+
+static int wpa_driver_ndis_scan(void *priv,
+ struct wpa_driver_scan_params *params)
+{
+ struct wpa_driver_ndis_data *drv = priv;
+ int res;
+
+ if (drv->native80211)
+ return wpa_driver_ndis_scan_native80211(drv, params);
+
+ if (!drv->radio_enabled) {
+ wpa_printf(MSG_DEBUG, "NDIS: turning radio on before the first"
+ " scan");
+ if (wpa_driver_ndis_disconnect(drv) < 0) {
+ wpa_printf(MSG_DEBUG, "NDIS: failed to enable radio");
+ }
+ drv->radio_enabled = 1;
+ }
+
+ res = ndis_set_oid(drv, OID_802_11_BSSID_LIST_SCAN, " ", 4);
+ eloop_cancel_timeout(wpa_driver_ndis_scan_timeout, drv, drv->ctx);
+ eloop_register_timeout(7, 0, wpa_driver_ndis_scan_timeout, drv,
+ drv->ctx);
+ return res;
+}
+
+
+static const u8 * wpa_scan_get_ie(const struct wpa_scan_res *res, u8 ie)
+{
+ const u8 *end, *pos;
+
+ pos = (const u8 *) (res + 1);
+ end = pos + res->ie_len;
+
+ while (pos + 1 < end) {
+ if (pos + 2 + pos[1] > end)
+ break;
+ if (pos[0] == ie)
+ return pos;
+ pos += 2 + pos[1];
+ }
+
+ return NULL;
+}
+
+
+static struct wpa_scan_res * wpa_driver_ndis_add_scan_ssid(
+ struct wpa_scan_res *r, NDIS_802_11_SSID *ssid)
+{
+ struct wpa_scan_res *nr;
+ u8 *pos;
+
+ if (wpa_scan_get_ie(r, WLAN_EID_SSID))
+ return r; /* SSID IE already present */
+
+ if (ssid->SsidLength == 0 || ssid->SsidLength > SSID_MAX_LEN)
+ return r; /* No valid SSID inside scan data */
+
+ nr = os_realloc(r, sizeof(*r) + r->ie_len + 2 + ssid->SsidLength);
+ if (nr == NULL)
+ return r;
+
+ pos = ((u8 *) (nr + 1)) + nr->ie_len;
+ *pos++ = WLAN_EID_SSID;
+ *pos++ = ssid->SsidLength;
+ os_memcpy(pos, ssid->Ssid, ssid->SsidLength);
+ nr->ie_len += 2 + ssid->SsidLength;
+
+ return nr;
+}
+
+
+static struct wpa_scan_results * wpa_driver_ndis_get_scan_results(void *priv)
+{
+ struct wpa_driver_ndis_data *drv = priv;
+ NDIS_802_11_BSSID_LIST_EX *b;
+ size_t blen, count, i;
+ int len;
+ char *pos;
+ struct wpa_scan_results *results;
+ struct wpa_scan_res *r;
+
+ blen = 65535;
+ b = os_zalloc(blen);
+ if (b == NULL)
+ return NULL;
+ len = ndis_get_oid(drv, OID_802_11_BSSID_LIST, (char *) b, blen);
+ if (len < 0) {
+ wpa_printf(MSG_DEBUG, "NDIS: failed to get scan results");
+ os_free(b);
+ return NULL;
+ }
+ count = b->NumberOfItems;
+
+ results = os_zalloc(sizeof(*results));
+ if (results == NULL) {
+ os_free(b);
+ return NULL;
+ }
+ results->res = os_calloc(count, sizeof(struct wpa_scan_res *));
+ if (results->res == NULL) {
+ os_free(results);
+ os_free(b);
+ return NULL;
+ }
+
+ pos = (char *) &b->Bssid[0];
+ for (i = 0; i < count; i++) {
+ NDIS_WLAN_BSSID_EX *bss = (NDIS_WLAN_BSSID_EX *) pos;
+ NDIS_802_11_FIXED_IEs *fixed;
+
+ if (bss->IELength < sizeof(NDIS_802_11_FIXED_IEs)) {
+ wpa_printf(MSG_DEBUG, "NDIS: too small IELength=%d",
+ (int) bss->IELength);
+ break;
+ }
+ if (((char *) bss->IEs) + bss->IELength > (char *) b + blen) {
+ /*
+ * Some NDIS drivers have been reported to include an
+ * entry with an invalid IELength in scan results and
+ * this has crashed wpa_supplicant, so validate the
+ * returned value before using it.
+ */
+ wpa_printf(MSG_DEBUG, "NDIS: skipped invalid scan "
+ "result IE (BSSID=" MACSTR ") IELength=%d",
+ MAC2STR(bss->MacAddress),
+ (int) bss->IELength);
+ break;
+ }
+
+ r = os_zalloc(sizeof(*r) + bss->IELength -
+ sizeof(NDIS_802_11_FIXED_IEs));
+ if (r == NULL)
+ break;
+
+ os_memcpy(r->bssid, bss->MacAddress, ETH_ALEN);
+ r->level = (int) bss->Rssi;
+ r->freq = bss->Configuration.DSConfig / 1000;
+ fixed = (NDIS_802_11_FIXED_IEs *) bss->IEs;
+ r->beacon_int = WPA_GET_LE16((u8 *) &fixed->BeaconInterval);
+ r->caps = WPA_GET_LE16((u8 *) &fixed->Capabilities);
+ r->tsf = WPA_GET_LE64(fixed->Timestamp);
+ os_memcpy(r + 1, bss->IEs + sizeof(NDIS_802_11_FIXED_IEs),
+ bss->IELength - sizeof(NDIS_802_11_FIXED_IEs));
+ r->ie_len = bss->IELength - sizeof(NDIS_802_11_FIXED_IEs);
+ r = wpa_driver_ndis_add_scan_ssid(r, &bss->Ssid);
+
+ results->res[results->num++] = r;
+
+ pos += bss->Length;
+ if (pos > (char *) b + blen)
+ break;
+ }
+
+ os_free(b);
+
+ return results;
+}
+
+
+static int wpa_driver_ndis_remove_key(struct wpa_driver_ndis_data *drv,
+ int key_idx, const u8 *addr,
+ const u8 *bssid, int pairwise)
+{
+ NDIS_802_11_REMOVE_KEY rkey;
+ NDIS_802_11_KEY_INDEX index;
+ int res, res2;
+
+ os_memset(&rkey, 0, sizeof(rkey));
+
+ rkey.Length = sizeof(rkey);
+ rkey.KeyIndex = key_idx;
+ if (pairwise)
+ rkey.KeyIndex |= 1 << 30;
+ os_memcpy(rkey.BSSID, bssid, ETH_ALEN);
+
+ res = ndis_set_oid(drv, OID_802_11_REMOVE_KEY, (char *) &rkey,
+ sizeof(rkey));
+ if (!pairwise) {
+ index = key_idx;
+ res2 = ndis_set_oid(drv, OID_802_11_REMOVE_WEP,
+ (char *) &index, sizeof(index));
+ } else
+ res2 = 0;
+
+ if (res < 0 && res2 < 0)
+ return -1;
+ return 0;
+}
+
+
+static int wpa_driver_ndis_add_wep(struct wpa_driver_ndis_data *drv,
+ int pairwise, int key_idx, int set_tx,
+ const u8 *key, size_t key_len)
+{
+ NDIS_802_11_WEP *wep;
+ size_t len;
+ int res;
+
+ len = 12 + key_len;
+ wep = os_zalloc(len);
+ if (wep == NULL)
+ return -1;
+ wep->Length = len;
+ wep->KeyIndex = key_idx;
+ if (set_tx)
+ wep->KeyIndex |= 1 << 31;
+#if 0 /* Setting bit30 does not seem to work with some NDIS drivers */
+ if (pairwise)
+ wep->KeyIndex |= 1 << 30;
+#endif
+ wep->KeyLength = key_len;
+ os_memcpy(wep->KeyMaterial, key, key_len);
+
+ wpa_hexdump_key(MSG_MSGDUMP, "NDIS: OID_802_11_ADD_WEP",
+ (u8 *) wep, len);
+ res = ndis_set_oid(drv, OID_802_11_ADD_WEP, (char *) wep, len);
+
+ os_free(wep);
+
+ return res;
+}
+
+
+static int wpa_driver_ndis_set_key(const char *ifname, void *priv,
+ enum wpa_alg alg, const u8 *addr,
+ int key_idx, int set_tx,
+ const u8 *seq, size_t seq_len,
+ const u8 *key, size_t key_len)
+{
+ struct wpa_driver_ndis_data *drv = priv;
+ size_t len, i;
+ NDIS_802_11_KEY *nkey;
+ int res, pairwise;
+ u8 bssid[ETH_ALEN];
+
+ if (addr == NULL || is_broadcast_ether_addr(addr)) {
+ /* Group Key */
+ pairwise = 0;
+ if (wpa_driver_ndis_get_bssid(drv, bssid) < 0)
+ os_memset(bssid, 0xff, ETH_ALEN);
+ } else {
+ /* Pairwise Key */
+ pairwise = 1;
+ os_memcpy(bssid, addr, ETH_ALEN);
+ }
+
+ if (alg == WPA_ALG_NONE || key_len == 0) {
+ return wpa_driver_ndis_remove_key(drv, key_idx, addr, bssid,
+ pairwise);
+ }
+
+ if (alg == WPA_ALG_WEP) {
+ return wpa_driver_ndis_add_wep(drv, pairwise, key_idx, set_tx,
+ key, key_len);
+ }
+
+ len = 12 + 6 + 6 + 8 + key_len;
+
+ nkey = os_zalloc(len);
+ if (nkey == NULL)
+ return -1;
+
+ nkey->Length = len;
+ nkey->KeyIndex = key_idx;
+ if (set_tx)
+ nkey->KeyIndex |= 1 << 31;
+ if (pairwise)
+ nkey->KeyIndex |= 1 << 30;
+ if (seq && seq_len)
+ nkey->KeyIndex |= 1 << 29;
+ nkey->KeyLength = key_len;
+ os_memcpy(nkey->BSSID, bssid, ETH_ALEN);
+ if (seq && seq_len) {
+ for (i = 0; i < seq_len; i++)
+ nkey->KeyRSC |= (ULONGLONG) seq[i] << (i * 8);
+ }
+ if (alg == WPA_ALG_TKIP && key_len == 32) {
+ os_memcpy(nkey->KeyMaterial, key, 16);
+ os_memcpy(nkey->KeyMaterial + 16, key + 24, 8);
+ os_memcpy(nkey->KeyMaterial + 24, key + 16, 8);
+ } else {
+ os_memcpy(nkey->KeyMaterial, key, key_len);
+ }
+
+ wpa_hexdump_key(MSG_MSGDUMP, "NDIS: OID_802_11_ADD_KEY",
+ (u8 *) nkey, len);
+ res = ndis_set_oid(drv, OID_802_11_ADD_KEY, (char *) nkey, len);
+ os_free(nkey);
+
+ return res;
+}
+
+
+static int
+wpa_driver_ndis_associate(void *priv,
+ struct wpa_driver_associate_params *params)
+{
+ struct wpa_driver_ndis_data *drv = priv;
+ u32 auth_mode, encr, priv_mode, mode;
+ u8 bcast[ETH_ALEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
+
+ drv->mode = params->mode;
+
+ /* Note: Setting OID_802_11_INFRASTRUCTURE_MODE clears current keys,
+ * so static WEP keys needs to be set again after this. */
+ if (params->mode == IEEE80211_MODE_IBSS) {
+ mode = Ndis802_11IBSS;
+ /* Need to make sure that BSSID polling is enabled for
+ * IBSS mode. */
+ eloop_cancel_timeout(wpa_driver_ndis_poll_timeout, drv, NULL);
+ eloop_register_timeout(1, 0, wpa_driver_ndis_poll_timeout,
+ drv, NULL);
+ } else
+ mode = Ndis802_11Infrastructure;
+ if (ndis_set_oid(drv, OID_802_11_INFRASTRUCTURE_MODE,
+ (char *) &mode, sizeof(mode)) < 0) {
+ wpa_printf(MSG_DEBUG, "NDIS: Failed to set "
+ "OID_802_11_INFRASTRUCTURE_MODE (%d)",
+ (int) mode);
+ /* Try to continue anyway */
+ }
+
+ if (params->key_mgmt_suite == WPA_KEY_MGMT_NONE ||
+ params->key_mgmt_suite == WPA_KEY_MGMT_IEEE8021X_NO_WPA) {
+ /* Re-set WEP keys if static WEP configuration is used. */
+ int i;
+ for (i = 0; i < 4; i++) {
+ if (!params->wep_key[i])
+ continue;
+ wpa_printf(MSG_DEBUG, "NDIS: Re-setting static WEP "
+ "key %d", i);
+ wpa_driver_ndis_set_key(drv->ifname, drv, WPA_ALG_WEP,
+ bcast, i,
+ i == params->wep_tx_keyidx,
+ NULL, 0, params->wep_key[i],
+ params->wep_key_len[i]);
+ }
+ }
+
+ if (params->wpa_ie == NULL || params->wpa_ie_len == 0) {
+ if (params->auth_alg & WPA_AUTH_ALG_SHARED) {
+ if (params->auth_alg & WPA_AUTH_ALG_OPEN)
+ auth_mode = Ndis802_11AuthModeAutoSwitch;
+ else
+ auth_mode = Ndis802_11AuthModeShared;
+ } else
+ auth_mode = Ndis802_11AuthModeOpen;
+ priv_mode = Ndis802_11PrivFilterAcceptAll;
+ } else if (params->wpa_ie[0] == WLAN_EID_RSN) {
+ priv_mode = Ndis802_11PrivFilter8021xWEP;
+ if (params->key_mgmt_suite == WPA_KEY_MGMT_PSK)
+ auth_mode = Ndis802_11AuthModeWPA2PSK;
+ else
+ auth_mode = Ndis802_11AuthModeWPA2;
+#ifdef CONFIG_WPS
+ } else if (params->key_mgmt_suite == WPA_KEY_MGMT_WPS) {
+ auth_mode = Ndis802_11AuthModeOpen;
+ priv_mode = Ndis802_11PrivFilterAcceptAll;
+ if (params->wps == WPS_MODE_PRIVACY) {
+ u8 dummy_key[5] = { 0x11, 0x22, 0x33, 0x44, 0x55 };
+ /*
+ * Some NDIS drivers refuse to associate in open mode
+ * configuration due to Privacy field mismatch, so use
+ * a workaround to make the configuration look like
+ * matching one for WPS provisioning.
+ */
+ wpa_printf(MSG_DEBUG, "NDIS: Set dummy WEP key as a "
+ "workaround to allow driver to associate "
+ "for WPS");
+ wpa_driver_ndis_set_key(drv->ifname, drv, WPA_ALG_WEP,
+ bcast, 0, 1,
+ NULL, 0, dummy_key,
+ sizeof(dummy_key));
+ }
+#endif /* CONFIG_WPS */
+ } else {
+ priv_mode = Ndis802_11PrivFilter8021xWEP;
+ if (params->key_mgmt_suite == WPA_KEY_MGMT_WPA_NONE)
+ auth_mode = Ndis802_11AuthModeWPANone;
+ else if (params->key_mgmt_suite == WPA_KEY_MGMT_PSK)
+ auth_mode = Ndis802_11AuthModeWPAPSK;
+ else
+ auth_mode = Ndis802_11AuthModeWPA;
+ }
+
+ switch (params->pairwise_suite) {
+ case WPA_CIPHER_CCMP:
+ encr = Ndis802_11Encryption3Enabled;
+ break;
+ case WPA_CIPHER_TKIP:
+ encr = Ndis802_11Encryption2Enabled;
+ break;
+ case WPA_CIPHER_WEP40:
+ case WPA_CIPHER_WEP104:
+ encr = Ndis802_11Encryption1Enabled;
+ break;
+ case WPA_CIPHER_NONE:
+#ifdef CONFIG_WPS
+ if (params->wps == WPS_MODE_PRIVACY) {
+ encr = Ndis802_11Encryption1Enabled;
+ break;
+ }
+#endif /* CONFIG_WPS */
+ if (params->group_suite == WPA_CIPHER_CCMP)
+ encr = Ndis802_11Encryption3Enabled;
+ else if (params->group_suite == WPA_CIPHER_TKIP)
+ encr = Ndis802_11Encryption2Enabled;
+ else
+ encr = Ndis802_11EncryptionDisabled;
+ break;
+ default:
+#ifdef CONFIG_WPS
+ if (params->wps == WPS_MODE_PRIVACY) {
+ encr = Ndis802_11Encryption1Enabled;
+ break;
+ }
+#endif /* CONFIG_WPS */
+ encr = Ndis802_11EncryptionDisabled;
+ break;
+ };
+
+ if (ndis_set_oid(drv, OID_802_11_PRIVACY_FILTER,
+ (char *) &priv_mode, sizeof(priv_mode)) < 0) {
+ wpa_printf(MSG_DEBUG, "NDIS: Failed to set "
+ "OID_802_11_PRIVACY_FILTER (%d)",
+ (int) priv_mode);
+ /* Try to continue anyway */
+ }
+
+ ndis_set_auth_mode(drv, auth_mode);
+ ndis_set_encr_status(drv, encr);
+
+ if (params->bssid) {
+ ndis_set_oid(drv, OID_802_11_BSSID, (char *) params->bssid,
+ ETH_ALEN);
+ drv->oid_bssid_set = 1;
+ } else if (drv->oid_bssid_set) {
+ ndis_set_oid(drv, OID_802_11_BSSID, "\xff\xff\xff\xff\xff\xff",
+ ETH_ALEN);
+ drv->oid_bssid_set = 0;
+ }
+
+ return wpa_driver_ndis_set_ssid(drv, params->ssid, params->ssid_len);
+}
+
+
+static int wpa_driver_ndis_set_pmkid(struct wpa_driver_ndis_data *drv)
+{
+ int len, count, i, ret;
+ struct ndis_pmkid_entry *entry;
+ NDIS_802_11_PMKID *p;
+
+ count = 0;
+ entry = drv->pmkid;
+ while (entry) {
+ count++;
+ if (count >= drv->no_of_pmkid)
+ break;
+ entry = entry->next;
+ }
+ len = 8 + count * sizeof(BSSID_INFO);
+ p = os_zalloc(len);
+ if (p == NULL)
+ return -1;
+
+ p->Length = len;
+ p->BSSIDInfoCount = count;
+ entry = drv->pmkid;
+ for (i = 0; i < count; i++) {
+ os_memcpy(&p->BSSIDInfo[i].BSSID, entry->bssid, ETH_ALEN);
+ os_memcpy(&p->BSSIDInfo[i].PMKID, entry->pmkid, 16);
+ entry = entry->next;
+ }
+ wpa_hexdump(MSG_MSGDUMP, "NDIS: OID_802_11_PMKID", (u8 *) p, len);
+ ret = ndis_set_oid(drv, OID_802_11_PMKID, (char *) p, len);
+ os_free(p);
+ return ret;
+}
+
+
+static int wpa_driver_ndis_add_pmkid(void *priv, const u8 *bssid,
+ const u8 *pmkid)
+{
+ struct wpa_driver_ndis_data *drv = priv;
+ struct ndis_pmkid_entry *entry, *prev;
+
+ if (drv->no_of_pmkid == 0)
+ return 0;
+
+ prev = NULL;
+ entry = drv->pmkid;
+ while (entry) {
+ if (os_memcmp(entry->bssid, bssid, ETH_ALEN) == 0)
+ break;
+ prev = entry;
+ entry = entry->next;
+ }
+
+ if (entry) {
+ /* Replace existing entry for this BSSID and move it into the
+ * beginning of the list. */
+ os_memcpy(entry->pmkid, pmkid, 16);
+ if (prev) {
+ prev->next = entry->next;
+ entry->next = drv->pmkid;
+ drv->pmkid = entry;
+ }
+ } else {
+ entry = os_malloc(sizeof(*entry));
+ if (entry) {
+ os_memcpy(entry->bssid, bssid, ETH_ALEN);
+ os_memcpy(entry->pmkid, pmkid, 16);
+ entry->next = drv->pmkid;
+ drv->pmkid = entry;
+ }
+ }
+
+ return wpa_driver_ndis_set_pmkid(drv);
+}
+
+
+static int wpa_driver_ndis_remove_pmkid(void *priv, const u8 *bssid,
+ const u8 *pmkid)
+{
+ struct wpa_driver_ndis_data *drv = priv;
+ struct ndis_pmkid_entry *entry, *prev;
+
+ if (drv->no_of_pmkid == 0)
+ return 0;
+
+ entry = drv->pmkid;
+ prev = NULL;
+ while (entry) {
+ if (os_memcmp(entry->bssid, bssid, ETH_ALEN) == 0 &&
+ os_memcmp(entry->pmkid, pmkid, 16) == 0) {
+ if (prev)
+ prev->next = entry->next;
+ else
+ drv->pmkid = entry->next;
+ os_free(entry);
+ break;
+ }
+ prev = entry;
+ entry = entry->next;
+ }
+ return wpa_driver_ndis_set_pmkid(drv);
+}
+
+
+static int wpa_driver_ndis_flush_pmkid(void *priv)
+{
+ struct wpa_driver_ndis_data *drv = priv;
+ NDIS_802_11_PMKID p;
+ struct ndis_pmkid_entry *pmkid, *prev;
+ int prev_authmode, ret;
+
+ if (drv->no_of_pmkid == 0)
+ return 0;
+
+ pmkid = drv->pmkid;
+ drv->pmkid = NULL;
+ while (pmkid) {
+ prev = pmkid;
+ pmkid = pmkid->next;
+ os_free(prev);
+ }
+
+ /*
+ * Some drivers may refuse OID_802_11_PMKID if authMode is not set to
+ * WPA2, so change authMode temporarily, if needed.
+ */
+ prev_authmode = ndis_get_auth_mode(drv);
+ if (prev_authmode != Ndis802_11AuthModeWPA2)
+ ndis_set_auth_mode(drv, Ndis802_11AuthModeWPA2);
+
+ os_memset(&p, 0, sizeof(p));
+ p.Length = 8;
+ p.BSSIDInfoCount = 0;
+ wpa_hexdump(MSG_MSGDUMP, "NDIS: OID_802_11_PMKID (flush)",
+ (u8 *) &p, 8);
+ ret = ndis_set_oid(drv, OID_802_11_PMKID, (char *) &p, 8);
+
+ if (prev_authmode != Ndis802_11AuthModeWPA2)
+ ndis_set_auth_mode(drv, prev_authmode);
+
+ return ret;
+}
+
+
+static int wpa_driver_ndis_get_associnfo(struct wpa_driver_ndis_data *drv)
+{
+ char buf[512], *pos;
+ NDIS_802_11_ASSOCIATION_INFORMATION *ai;
+ int len;
+ union wpa_event_data data;
+ NDIS_802_11_BSSID_LIST_EX *b;
+ size_t blen, i;
+
+ len = ndis_get_oid(drv, OID_802_11_ASSOCIATION_INFORMATION, buf,
+ sizeof(buf));
+ if (len < 0) {
+ wpa_printf(MSG_DEBUG, "NDIS: failed to get association "
+ "information");
+ return -1;
+ }
+ if (len > sizeof(buf)) {
+ /* Some drivers seem to be producing incorrect length for this
+ * data. Limit the length to the current buffer size to avoid
+ * crashing in hexdump. The data seems to be otherwise valid,
+ * so better try to use it. */
+ wpa_printf(MSG_DEBUG, "NDIS: ignored bogus association "
+ "information length %d", len);
+ len = ndis_get_oid(drv, OID_802_11_ASSOCIATION_INFORMATION,
+ buf, sizeof(buf));
+ if (len < -1) {
+ wpa_printf(MSG_DEBUG, "NDIS: re-reading association "
+ "information failed");
+ return -1;
+ }
+ if (len > sizeof(buf)) {
+ wpa_printf(MSG_DEBUG, "NDIS: ignored bogus association"
+ " information length %d (re-read)", len);
+ len = sizeof(buf);
+ }
+ }
+ wpa_hexdump(MSG_MSGDUMP, "NDIS: association information",
+ (u8 *) buf, len);
+ if (len < sizeof(*ai)) {
+ wpa_printf(MSG_DEBUG, "NDIS: too short association "
+ "information");
+ return -1;
+ }
+ ai = (NDIS_802_11_ASSOCIATION_INFORMATION *) buf;
+ wpa_printf(MSG_DEBUG, "NDIS: ReqFixed=0x%x RespFixed=0x%x off_req=%d "
+ "off_resp=%d len_req=%d len_resp=%d",
+ ai->AvailableRequestFixedIEs, ai->AvailableResponseFixedIEs,
+ (int) ai->OffsetRequestIEs, (int) ai->OffsetResponseIEs,
+ (int) ai->RequestIELength, (int) ai->ResponseIELength);
+
+ if (ai->OffsetRequestIEs + ai->RequestIELength > (unsigned) len ||
+ ai->OffsetResponseIEs + ai->ResponseIELength > (unsigned) len) {
+ wpa_printf(MSG_DEBUG, "NDIS: association information - "
+ "IE overflow");
+ return -1;
+ }
+
+ wpa_hexdump(MSG_MSGDUMP, "NDIS: Request IEs",
+ (u8 *) buf + ai->OffsetRequestIEs, ai->RequestIELength);
+ wpa_hexdump(MSG_MSGDUMP, "NDIS: Response IEs",
+ (u8 *) buf + ai->OffsetResponseIEs, ai->ResponseIELength);
+
+ os_memset(&data, 0, sizeof(data));
+ data.assoc_info.req_ies = (u8 *) buf + ai->OffsetRequestIEs;
+ data.assoc_info.req_ies_len = ai->RequestIELength;
+ data.assoc_info.resp_ies = (u8 *) buf + ai->OffsetResponseIEs;
+ data.assoc_info.resp_ies_len = ai->ResponseIELength;
+
+ blen = 65535;
+ b = os_zalloc(blen);
+ if (b == NULL)
+ goto skip_scan_results;
+ len = ndis_get_oid(drv, OID_802_11_BSSID_LIST, (char *) b, blen);
+ if (len < 0) {
+ wpa_printf(MSG_DEBUG, "NDIS: failed to get scan results");
+ os_free(b);
+ b = NULL;
+ goto skip_scan_results;
+ }
+ wpa_printf(MSG_DEBUG, "NDIS: %d BSSID items to process for AssocInfo",
+ (unsigned int) b->NumberOfItems);
+
+ pos = (char *) &b->Bssid[0];
+ for (i = 0; i < b->NumberOfItems; i++) {
+ NDIS_WLAN_BSSID_EX *bss = (NDIS_WLAN_BSSID_EX *) pos;
+ if (os_memcmp(drv->bssid, bss->MacAddress, ETH_ALEN) == 0 &&
+ bss->IELength > sizeof(NDIS_802_11_FIXED_IEs)) {
+ data.assoc_info.beacon_ies =
+ ((u8 *) bss->IEs) +
+ sizeof(NDIS_802_11_FIXED_IEs);
+ data.assoc_info.beacon_ies_len =
+ bss->IELength - sizeof(NDIS_802_11_FIXED_IEs);
+ wpa_hexdump(MSG_MSGDUMP, "NDIS: Beacon IEs",
+ data.assoc_info.beacon_ies,
+ data.assoc_info.beacon_ies_len);
+ break;
+ }
+ pos += bss->Length;
+ if (pos > (char *) b + blen)
+ break;
+ }
+
+skip_scan_results:
+ wpa_supplicant_event(drv->ctx, EVENT_ASSOCINFO, &data);
+
+ os_free(b);
+
+ return 0;
+}
+
+
+static void wpa_driver_ndis_poll_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+ struct wpa_driver_ndis_data *drv = eloop_ctx;
+ u8 bssid[ETH_ALEN];
+ int poll;
+
+ if (drv->wired)
+ return;
+
+ if (wpa_driver_ndis_get_bssid(drv, bssid)) {
+ /* Disconnected */
+ if (!is_zero_ether_addr(drv->bssid)) {
+ os_memset(drv->bssid, 0, ETH_ALEN);
+ wpa_supplicant_event(drv->ctx, EVENT_DISASSOC, NULL);
+ }
+ } else {
+ /* Connected */
+ if (os_memcmp(drv->bssid, bssid, ETH_ALEN) != 0) {
+ os_memcpy(drv->bssid, bssid, ETH_ALEN);
+ wpa_driver_ndis_get_associnfo(drv);
+ wpa_supplicant_event(drv->ctx, EVENT_ASSOC, NULL);
+ }
+ }
+
+ /* When using integrated NDIS event receiver, we can skip BSSID
+ * polling when using infrastructure network. However, when using
+ * IBSS mode, many driver do not seem to generate connection event,
+ * so we need to enable BSSID polling to figure out when IBSS network
+ * has been formed.
+ */
+ poll = drv->mode == IEEE80211_MODE_IBSS;
+#ifndef CONFIG_NDIS_EVENTS_INTEGRATED
+#ifndef _WIN32_WCE
+ poll = 1;
+#endif /* _WIN32_WCE */
+#endif /* CONFIG_NDIS_EVENTS_INTEGRATED */
+
+ if (poll) {
+ eloop_register_timeout(1, 0, wpa_driver_ndis_poll_timeout,
+ drv, NULL);
+ }
+}
+
+
+static void wpa_driver_ndis_poll(void *priv)
+{
+ struct wpa_driver_ndis_data *drv = priv;
+ eloop_cancel_timeout(wpa_driver_ndis_poll_timeout, drv, NULL);
+ wpa_driver_ndis_poll_timeout(drv, NULL);
+}
+
+
+/* Called when driver generates Media Connect Event by calling
+ * NdisMIndicateStatus() with NDIS_STATUS_MEDIA_CONNECT */
+void wpa_driver_ndis_event_connect(struct wpa_driver_ndis_data *drv)
+{
+ wpa_printf(MSG_DEBUG, "NDIS: Media Connect Event");
+ if (wpa_driver_ndis_get_bssid(drv, drv->bssid) == 0) {
+ wpa_driver_ndis_get_associnfo(drv);
+ wpa_supplicant_event(drv->ctx, EVENT_ASSOC, NULL);
+ }
+}
+
+
+/* Called when driver generates Media Disconnect Event by calling
+ * NdisMIndicateStatus() with NDIS_STATUS_MEDIA_DISCONNECT */
+void wpa_driver_ndis_event_disconnect(struct wpa_driver_ndis_data *drv)
+{
+ wpa_printf(MSG_DEBUG, "NDIS: Media Disconnect Event");
+ os_memset(drv->bssid, 0, ETH_ALEN);
+ wpa_supplicant_event(drv->ctx, EVENT_DISASSOC, NULL);
+}
+
+
+static void wpa_driver_ndis_event_auth(struct wpa_driver_ndis_data *drv,
+ const u8 *data, size_t data_len)
+{
+ NDIS_802_11_AUTHENTICATION_REQUEST *req;
+ int pairwise = 0, group = 0;
+ union wpa_event_data event;
+
+ if (data_len < sizeof(*req)) {
+ wpa_printf(MSG_DEBUG, "NDIS: Too short Authentication Request "
+ "Event (len=%d)", data_len);
+ return;
+ }
+ req = (NDIS_802_11_AUTHENTICATION_REQUEST *) data;
+
+ wpa_printf(MSG_DEBUG, "NDIS: Authentication Request Event: "
+ "Bssid " MACSTR " Flags 0x%x",
+ MAC2STR(req->Bssid), (int) req->Flags);
+
+ if ((req->Flags & NDIS_802_11_AUTH_REQUEST_PAIRWISE_ERROR) ==
+ NDIS_802_11_AUTH_REQUEST_PAIRWISE_ERROR)
+ pairwise = 1;
+ else if ((req->Flags & NDIS_802_11_AUTH_REQUEST_GROUP_ERROR) ==
+ NDIS_802_11_AUTH_REQUEST_GROUP_ERROR)
+ group = 1;
+
+ if (pairwise || group) {
+ os_memset(&event, 0, sizeof(event));
+ event.michael_mic_failure.unicast = pairwise;
+ wpa_supplicant_event(drv->ctx, EVENT_MICHAEL_MIC_FAILURE,
+ &event);
+ }
+}
+
+
+static void wpa_driver_ndis_event_pmkid(struct wpa_driver_ndis_data *drv,
+ const u8 *data, size_t data_len)
+{
+ NDIS_802_11_PMKID_CANDIDATE_LIST *pmkid;
+ size_t i;
+ union wpa_event_data event;
+
+ if (data_len < 8) {
+ wpa_printf(MSG_DEBUG, "NDIS: Too short PMKID Candidate List "
+ "Event (len=%d)", data_len);
+ return;
+ }
+ pmkid = (NDIS_802_11_PMKID_CANDIDATE_LIST *) data;
+ wpa_printf(MSG_DEBUG, "NDIS: PMKID Candidate List Event - Version %d "
+ "NumCandidates %d",
+ (int) pmkid->Version, (int) pmkid->NumCandidates);
+
+ if (pmkid->Version != 1) {
+ wpa_printf(MSG_DEBUG, "NDIS: Unsupported PMKID Candidate List "
+ "Version %d", (int) pmkid->Version);
+ return;
+ }
+
+ if (data_len < 8 + pmkid->NumCandidates * sizeof(PMKID_CANDIDATE)) {
+ wpa_printf(MSG_DEBUG, "NDIS: PMKID Candidate List underflow");
+ return;
+ }
+
+ os_memset(&event, 0, sizeof(event));
+ for (i = 0; i < pmkid->NumCandidates; i++) {
+ PMKID_CANDIDATE *p = &pmkid->CandidateList[i];
+ wpa_printf(MSG_DEBUG, "NDIS: %d: " MACSTR " Flags 0x%x",
+ i, MAC2STR(p->BSSID), (int) p->Flags);
+ os_memcpy(event.pmkid_candidate.bssid, p->BSSID, ETH_ALEN);
+ event.pmkid_candidate.index = i;
+ event.pmkid_candidate.preauth =
+ p->Flags & NDIS_802_11_PMKID_CANDIDATE_PREAUTH_ENABLED;
+ wpa_supplicant_event(drv->ctx, EVENT_PMKID_CANDIDATE,
+ &event);
+ }
+}
+
+
+/* Called when driver calls NdisMIndicateStatus() with
+ * NDIS_STATUS_MEDIA_SPECIFIC_INDICATION */
+void wpa_driver_ndis_event_media_specific(struct wpa_driver_ndis_data *drv,
+ const u8 *data, size_t data_len)
+{
+ NDIS_802_11_STATUS_INDICATION *status;
+
+ if (data == NULL || data_len < sizeof(*status))
+ return;
+
+ wpa_hexdump(MSG_DEBUG, "NDIS: Media Specific Indication",
+ data, data_len);
+
+ status = (NDIS_802_11_STATUS_INDICATION *) data;
+ data += sizeof(status);
+ data_len -= sizeof(status);
+
+ switch (status->StatusType) {
+ case Ndis802_11StatusType_Authentication:
+ wpa_driver_ndis_event_auth(drv, data, data_len);
+ break;
+ case Ndis802_11StatusType_PMKID_CandidateList:
+ wpa_driver_ndis_event_pmkid(drv, data, data_len);
+ break;
+ default:
+ wpa_printf(MSG_DEBUG, "NDIS: Unknown StatusType %d",
+ (int) status->StatusType);
+ break;
+ }
+}
+
+
+/* Called when an adapter is added */
+void wpa_driver_ndis_event_adapter_arrival(struct wpa_driver_ndis_data *drv)
+{
+ union wpa_event_data event;
+ int i;
+
+ wpa_printf(MSG_DEBUG, "NDIS: Notify Adapter Arrival");
+
+ for (i = 0; i < 30; i++) {
+ /* Re-open Packet32/NDISUIO connection */
+ wpa_driver_ndis_adapter_close(drv);
+ if (wpa_driver_ndis_adapter_init(drv) < 0 ||
+ wpa_driver_ndis_adapter_open(drv) < 0) {
+ wpa_printf(MSG_DEBUG, "NDIS: Driver re-initialization "
+ "(%d) failed", i);
+ os_sleep(1, 0);
+ } else {
+ wpa_printf(MSG_DEBUG, "NDIS: Driver re-initialized");
+ break;
+ }
+ }
+
+ os_memset(&event, 0, sizeof(event));
+ os_strlcpy(event.interface_status.ifname, drv->ifname,
+ sizeof(event.interface_status.ifname));
+ event.interface_status.ievent = EVENT_INTERFACE_ADDED;
+ wpa_supplicant_event(drv->ctx, EVENT_INTERFACE_STATUS, &event);
+}
+
+
+/* Called when an adapter is removed */
+void wpa_driver_ndis_event_adapter_removal(struct wpa_driver_ndis_data *drv)
+{
+ union wpa_event_data event;
+
+ wpa_printf(MSG_DEBUG, "NDIS: Notify Adapter Removal");
+ os_memset(&event, 0, sizeof(event));
+ os_strlcpy(event.interface_status.ifname, drv->ifname,
+ sizeof(event.interface_status.ifname));
+ event.interface_status.ievent = EVENT_INTERFACE_REMOVED;
+ wpa_supplicant_event(drv->ctx, EVENT_INTERFACE_STATUS, &event);
+}
+
+
+static void
+wpa_driver_ndis_get_wpa_capability(struct wpa_driver_ndis_data *drv)
+{
+ wpa_printf(MSG_DEBUG, "NDIS: verifying driver WPA capability");
+
+ if (ndis_set_auth_mode(drv, Ndis802_11AuthModeWPA) == 0 &&
+ ndis_get_auth_mode(drv) == Ndis802_11AuthModeWPA) {
+ wpa_printf(MSG_DEBUG, "NDIS: WPA key management supported");
+ drv->capa.key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_WPA;
+ }
+
+ if (ndis_set_auth_mode(drv, Ndis802_11AuthModeWPAPSK) == 0 &&
+ ndis_get_auth_mode(drv) == Ndis802_11AuthModeWPAPSK) {
+ wpa_printf(MSG_DEBUG, "NDIS: WPA-PSK key management "
+ "supported");
+ drv->capa.key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_WPA_PSK;
+ }
+
+ if (ndis_set_encr_status(drv, Ndis802_11Encryption3Enabled) == 0 &&
+ ndis_get_encr_status(drv) == Ndis802_11Encryption3KeyAbsent) {
+ wpa_printf(MSG_DEBUG, "NDIS: CCMP encryption supported");
+ drv->capa.enc |= WPA_DRIVER_CAPA_ENC_CCMP;
+ }
+
+ if (ndis_set_encr_status(drv, Ndis802_11Encryption2Enabled) == 0 &&
+ ndis_get_encr_status(drv) == Ndis802_11Encryption2KeyAbsent) {
+ wpa_printf(MSG_DEBUG, "NDIS: TKIP encryption supported");
+ drv->capa.enc |= WPA_DRIVER_CAPA_ENC_TKIP;
+ }
+
+ if (ndis_set_encr_status(drv, Ndis802_11Encryption1Enabled) == 0 &&
+ ndis_get_encr_status(drv) == Ndis802_11Encryption1KeyAbsent) {
+ wpa_printf(MSG_DEBUG, "NDIS: WEP encryption supported");
+ drv->capa.enc |= WPA_DRIVER_CAPA_ENC_WEP40 |
+ WPA_DRIVER_CAPA_ENC_WEP104;
+ }
+
+ if (ndis_set_auth_mode(drv, Ndis802_11AuthModeShared) == 0 &&
+ ndis_get_auth_mode(drv) == Ndis802_11AuthModeShared) {
+ drv->capa.auth |= WPA_DRIVER_AUTH_SHARED;
+ }
+
+ if (ndis_set_auth_mode(drv, Ndis802_11AuthModeOpen) == 0 &&
+ ndis_get_auth_mode(drv) == Ndis802_11AuthModeOpen) {
+ drv->capa.auth |= WPA_DRIVER_AUTH_OPEN;
+ }
+
+ ndis_set_encr_status(drv, Ndis802_11EncryptionDisabled);
+
+ /* Could also verify OID_802_11_ADD_KEY error reporting and
+ * support for OID_802_11_ASSOCIATION_INFORMATION. */
+
+ if (drv->capa.key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_WPA &&
+ drv->capa.enc & (WPA_DRIVER_CAPA_ENC_TKIP |
+ WPA_DRIVER_CAPA_ENC_CCMP)) {
+ wpa_printf(MSG_DEBUG, "NDIS: driver supports WPA");
+ drv->has_capability = 1;
+ } else {
+ wpa_printf(MSG_DEBUG, "NDIS: no WPA support found");
+ }
+
+ wpa_printf(MSG_DEBUG, "NDIS: driver capabilities: key_mgmt 0x%x "
+ "enc 0x%x auth 0x%x",
+ drv->capa.key_mgmt, drv->capa.enc, drv->capa.auth);
+}
+
+
+static void wpa_driver_ndis_get_capability(struct wpa_driver_ndis_data *drv)
+{
+ char buf[512];
+ int len;
+ size_t i;
+ NDIS_802_11_CAPABILITY *c;
+
+ drv->capa.flags = WPA_DRIVER_FLAGS_DRIVER_IE;
+
+ len = ndis_get_oid(drv, OID_802_11_CAPABILITY, buf, sizeof(buf));
+ if (len < 0) {
+ wpa_driver_ndis_get_wpa_capability(drv);
+ return;
+ }
+
+ wpa_hexdump(MSG_MSGDUMP, "OID_802_11_CAPABILITY", (u8 *) buf, len);
+ c = (NDIS_802_11_CAPABILITY *) buf;
+ if (len < sizeof(*c) || c->Version != 2) {
+ wpa_printf(MSG_DEBUG, "NDIS: unsupported "
+ "OID_802_11_CAPABILITY data");
+ return;
+ }
+ wpa_printf(MSG_DEBUG, "NDIS: Driver supports OID_802_11_CAPABILITY - "
+ "NoOfPMKIDs %d NoOfAuthEncrPairs %d",
+ (int) c->NoOfPMKIDs,
+ (int) c->NoOfAuthEncryptPairsSupported);
+ drv->has_capability = 1;
+ drv->no_of_pmkid = c->NoOfPMKIDs;
+ for (i = 0; i < c->NoOfAuthEncryptPairsSupported; i++) {
+ NDIS_802_11_AUTHENTICATION_ENCRYPTION *ae;
+ ae = &c->AuthenticationEncryptionSupported[i];
+ if ((char *) (ae + 1) > buf + len) {
+ wpa_printf(MSG_DEBUG, "NDIS: auth/encr pair list "
+ "overflow");
+ break;
+ }
+ wpa_printf(MSG_MSGDUMP, "NDIS: %d - auth %d encr %d",
+ i, (int) ae->AuthModeSupported,
+ (int) ae->EncryptStatusSupported);
+ switch (ae->AuthModeSupported) {
+ case Ndis802_11AuthModeOpen:
+ drv->capa.auth |= WPA_DRIVER_AUTH_OPEN;
+ break;
+ case Ndis802_11AuthModeShared:
+ drv->capa.auth |= WPA_DRIVER_AUTH_SHARED;
+ break;
+ case Ndis802_11AuthModeWPA:
+ drv->capa.key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_WPA;
+ break;
+ case Ndis802_11AuthModeWPAPSK:
+ drv->capa.key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_WPA_PSK;
+ break;
+ case Ndis802_11AuthModeWPA2:
+ drv->capa.key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_WPA2;
+ break;
+ case Ndis802_11AuthModeWPA2PSK:
+ drv->capa.key_mgmt |=
+ WPA_DRIVER_CAPA_KEY_MGMT_WPA2_PSK;
+ break;
+ case Ndis802_11AuthModeWPANone:
+ drv->capa.key_mgmt |=
+ WPA_DRIVER_CAPA_KEY_MGMT_WPA_NONE;
+ break;
+ default:
+ break;
+ }
+ switch (ae->EncryptStatusSupported) {
+ case Ndis802_11Encryption1Enabled:
+ drv->capa.enc |= WPA_DRIVER_CAPA_ENC_WEP40;
+ drv->capa.enc |= WPA_DRIVER_CAPA_ENC_WEP104;
+ break;
+ case Ndis802_11Encryption2Enabled:
+ drv->capa.enc |= WPA_DRIVER_CAPA_ENC_TKIP;
+ break;
+ case Ndis802_11Encryption3Enabled:
+ drv->capa.enc |= WPA_DRIVER_CAPA_ENC_CCMP;
+ break;
+ default:
+ break;
+ }
+ }
+
+ wpa_printf(MSG_DEBUG, "NDIS: driver capabilities: key_mgmt 0x%x "
+ "enc 0x%x auth 0x%x",
+ drv->capa.key_mgmt, drv->capa.enc, drv->capa.auth);
+}
+
+
+static int wpa_driver_ndis_get_capa(void *priv, struct wpa_driver_capa *capa)
+{
+ struct wpa_driver_ndis_data *drv = priv;
+ if (!drv->has_capability)
+ return -1;
+ os_memcpy(capa, &drv->capa, sizeof(*capa));
+ return 0;
+}
+
+
+static const char * wpa_driver_ndis_get_ifname(void *priv)
+{
+ struct wpa_driver_ndis_data *drv = priv;
+ return drv->ifname;
+}
+
+
+static const u8 * wpa_driver_ndis_get_mac_addr(void *priv)
+{
+ struct wpa_driver_ndis_data *drv = priv;
+ return drv->own_addr;
+}
+
+
+#ifdef _WIN32_WCE
+
+#define NDISUIO_MSG_SIZE (sizeof(NDISUIO_DEVICE_NOTIFICATION) + 512)
+
+static void ndisuio_notification_receive(void *eloop_data, void *user_ctx)
+{
+ struct wpa_driver_ndis_data *drv = eloop_data;
+ NDISUIO_DEVICE_NOTIFICATION *hdr;
+ u8 buf[NDISUIO_MSG_SIZE];
+ DWORD len, flags;
+
+ if (!ReadMsgQueue(drv->event_queue, buf, NDISUIO_MSG_SIZE, &len, 0,
+ &flags)) {
+ wpa_printf(MSG_DEBUG, "ndisuio_notification_receive: "
+ "ReadMsgQueue failed: %d", (int) GetLastError());
+ return;
+ }
+
+ if (len < sizeof(NDISUIO_DEVICE_NOTIFICATION)) {
+ wpa_printf(MSG_DEBUG, "ndisuio_notification_receive: "
+ "Too short message (len=%d)", (int) len);
+ return;
+ }
+
+ hdr = (NDISUIO_DEVICE_NOTIFICATION *) buf;
+ wpa_printf(MSG_DEBUG, "NDIS: Notification received: len=%d type=0x%x",
+ (int) len, hdr->dwNotificationType);
+
+ switch (hdr->dwNotificationType) {
+#ifdef NDISUIO_NOTIFICATION_ADAPTER_ARRIVAL
+ case NDISUIO_NOTIFICATION_ADAPTER_ARRIVAL:
+ wpa_printf(MSG_DEBUG, "NDIS: ADAPTER_ARRIVAL");
+ wpa_driver_ndis_event_adapter_arrival(drv);
+ break;
+#endif
+#ifdef NDISUIO_NOTIFICATION_ADAPTER_REMOVAL
+ case NDISUIO_NOTIFICATION_ADAPTER_REMOVAL:
+ wpa_printf(MSG_DEBUG, "NDIS: ADAPTER_REMOVAL");
+ wpa_driver_ndis_event_adapter_removal(drv);
+ break;
+#endif
+ case NDISUIO_NOTIFICATION_MEDIA_CONNECT:
+ wpa_printf(MSG_DEBUG, "NDIS: MEDIA_CONNECT");
+ SetEvent(drv->connected_event);
+ wpa_driver_ndis_event_connect(drv);
+ break;
+ case NDISUIO_NOTIFICATION_MEDIA_DISCONNECT:
+ ResetEvent(drv->connected_event);
+ wpa_printf(MSG_DEBUG, "NDIS: MEDIA_DISCONNECT");
+ wpa_driver_ndis_event_disconnect(drv);
+ break;
+ case NDISUIO_NOTIFICATION_MEDIA_SPECIFIC_NOTIFICATION:
+ wpa_printf(MSG_DEBUG, "NDIS: MEDIA_SPECIFIC_NOTIFICATION");
+#if _WIN32_WCE == 420 || _WIN32_WCE == 0x420
+ wpa_driver_ndis_event_media_specific(
+ drv, hdr->pvStatusBuffer, hdr->uiStatusBufferSize);
+#else
+ wpa_driver_ndis_event_media_specific(
+ drv, ((const u8 *) hdr) + hdr->uiOffsetToStatusBuffer,
+ (size_t) hdr->uiStatusBufferSize);
+#endif
+ break;
+ default:
+ wpa_printf(MSG_DEBUG, "NDIS: Unknown notification type 0x%x",
+ hdr->dwNotificationType);
+ break;
+ }
+}
+
+
+static void ndisuio_notification_deinit(struct wpa_driver_ndis_data *drv)
+{
+ NDISUIO_REQUEST_NOTIFICATION req;
+
+ memset(&req, 0, sizeof(req));
+ req.hMsgQueue = drv->event_queue;
+ req.dwNotificationTypes = 0;
+
+ if (!DeviceIoControl(drv->ndisuio, IOCTL_NDISUIO_REQUEST_NOTIFICATION,
+ &req, sizeof(req), NULL, 0, NULL, NULL)) {
+ wpa_printf(MSG_INFO, "ndisuio_notification_deinit: "
+ "IOCTL_NDISUIO_REQUEST_NOTIFICATION failed: %d",
+ (int) GetLastError());
+ }
+
+ if (!DeviceIoControl(drv->ndisuio, IOCTL_NDISUIO_CANCEL_NOTIFICATION,
+ NULL, 0, NULL, 0, NULL, NULL)) {
+ wpa_printf(MSG_INFO, "ndisuio_notification_deinit: "
+ "IOCTL_NDISUIO_CANCEL_NOTIFICATION failed: %d",
+ (int) GetLastError());
+ }
+
+ if (drv->event_queue) {
+ eloop_unregister_event(drv->event_queue,
+ sizeof(drv->event_queue));
+ CloseHandle(drv->event_queue);
+ drv->event_queue = NULL;
+ }
+
+ if (drv->connected_event) {
+ CloseHandle(drv->connected_event);
+ drv->connected_event = NULL;
+ }
+}
+
+
+static int ndisuio_notification_init(struct wpa_driver_ndis_data *drv)
+{
+ MSGQUEUEOPTIONS opt;
+ NDISUIO_REQUEST_NOTIFICATION req;
+
+ drv->connected_event =
+ CreateEvent(NULL, TRUE, FALSE, TEXT("WpaSupplicantConnected"));
+ if (drv->connected_event == NULL) {
+ wpa_printf(MSG_INFO, "ndisuio_notification_init: "
+ "CreateEvent failed: %d",
+ (int) GetLastError());
+ return -1;
+ }
+
+ memset(&opt, 0, sizeof(opt));
+ opt.dwSize = sizeof(opt);
+ opt.dwMaxMessages = 5;
+ opt.cbMaxMessage = NDISUIO_MSG_SIZE;
+ opt.bReadAccess = TRUE;
+
+ drv->event_queue = CreateMsgQueue(NULL, &opt);
+ if (drv->event_queue == NULL) {
+ wpa_printf(MSG_INFO, "ndisuio_notification_init: "
+ "CreateMsgQueue failed: %d",
+ (int) GetLastError());
+ ndisuio_notification_deinit(drv);
+ return -1;
+ }
+
+ memset(&req, 0, sizeof(req));
+ req.hMsgQueue = drv->event_queue;
+ req.dwNotificationTypes =
+#ifdef NDISUIO_NOTIFICATION_ADAPTER_ARRIVAL
+ NDISUIO_NOTIFICATION_ADAPTER_ARRIVAL |
+#endif
+#ifdef NDISUIO_NOTIFICATION_ADAPTER_REMOVAL
+ NDISUIO_NOTIFICATION_ADAPTER_REMOVAL |
+#endif
+ NDISUIO_NOTIFICATION_MEDIA_CONNECT |
+ NDISUIO_NOTIFICATION_MEDIA_DISCONNECT |
+ NDISUIO_NOTIFICATION_MEDIA_SPECIFIC_NOTIFICATION;
+
+ if (!DeviceIoControl(drv->ndisuio, IOCTL_NDISUIO_REQUEST_NOTIFICATION,
+ &req, sizeof(req), NULL, 0, NULL, NULL)) {
+ wpa_printf(MSG_INFO, "ndisuio_notification_init: "
+ "IOCTL_NDISUIO_REQUEST_NOTIFICATION failed: %d",
+ (int) GetLastError());
+ ndisuio_notification_deinit(drv);
+ return -1;
+ }
+
+ eloop_register_event(drv->event_queue, sizeof(drv->event_queue),
+ ndisuio_notification_receive, drv, NULL);
+
+ return 0;
+}
+#endif /* _WIN32_WCE */
+
+
+static int wpa_driver_ndis_get_names(struct wpa_driver_ndis_data *drv)
+{
+#ifdef CONFIG_USE_NDISUIO
+ NDISUIO_QUERY_BINDING *b;
+ size_t blen = sizeof(*b) + 1024;
+ int i, error, found = 0;
+ DWORD written;
+ char name[256], desc[256], *dpos;
+ WCHAR *pos;
+ size_t j, len, dlen;
+
+ b = os_malloc(blen);
+ if (b == NULL)
+ return -1;
+
+ for (i = 0; ; i++) {
+ os_memset(b, 0, blen);
+ b->BindingIndex = i;
+ if (!DeviceIoControl(drv->ndisuio, IOCTL_NDISUIO_QUERY_BINDING,
+ b, sizeof(NDISUIO_QUERY_BINDING), b, blen,
+ &written, NULL)) {
+ error = (int) GetLastError();
+ if (error == ERROR_NO_MORE_ITEMS)
+ break;
+ wpa_printf(MSG_DEBUG, "IOCTL_NDISUIO_QUERY_BINDING "
+ "failed: %d", error);
+ break;
+ }
+
+ pos = (WCHAR *) ((char *) b + b->DeviceNameOffset);
+ len = b->DeviceNameLength;
+ if (len >= sizeof(name))
+ len = sizeof(name) - 1;
+ for (j = 0; j < len; j++)
+ name[j] = (char) pos[j];
+ name[len] = '\0';
+
+ pos = (WCHAR *) ((char *) b + b->DeviceDescrOffset);
+ len = b->DeviceDescrLength;
+ if (len >= sizeof(desc))
+ len = sizeof(desc) - 1;
+ for (j = 0; j < len; j++)
+ desc[j] = (char) pos[j];
+ desc[len] = '\0';
+
+ wpa_printf(MSG_DEBUG, "NDIS: %d - %s - %s", i, name, desc);
+
+ if (os_strstr(name, drv->ifname)) {
+ wpa_printf(MSG_DEBUG, "NDIS: Interface name match");
+ found = 1;
+ break;
+ }
+
+ if (os_strncmp(desc, drv->ifname, os_strlen(drv->ifname)) == 0)
+ {
+ wpa_printf(MSG_DEBUG, "NDIS: Interface description "
+ "match");
+ found = 1;
+ break;
+ }
+ }
+
+ if (!found) {
+ wpa_printf(MSG_DEBUG, "NDIS: Could not find interface '%s'",
+ drv->ifname);
+ os_free(b);
+ return -1;
+ }
+
+ os_strlcpy(drv->ifname,
+ os_strncmp(name, "\\DEVICE\\", 8) == 0 ? name + 8 : name,
+ sizeof(drv->ifname));
+#ifdef _WIN32_WCE
+ drv->adapter_name = wpa_strdup_tchar(drv->ifname);
+ if (drv->adapter_name == NULL) {
+ wpa_printf(MSG_ERROR, "NDIS: Failed to allocate memory for "
+ "adapter name");
+ os_free(b);
+ return -1;
+ }
+#endif /* _WIN32_WCE */
+
+ dpos = os_strstr(desc, " - ");
+ if (dpos)
+ dlen = dpos - desc;
+ else
+ dlen = os_strlen(desc);
+ drv->adapter_desc = dup_binstr(desc, dlen);
+ os_free(b);
+ if (drv->adapter_desc == NULL)
+ return -1;
+
+ wpa_printf(MSG_DEBUG, "NDIS: Adapter description prefix '%s'",
+ drv->adapter_desc);
+
+ return 0;
+#else /* CONFIG_USE_NDISUIO */
+ PTSTR _names;
+ char *names, *pos, *pos2;
+ ULONG len;
+ BOOLEAN res;
+#define MAX_ADAPTERS 32
+ char *name[MAX_ADAPTERS];
+ char *desc[MAX_ADAPTERS];
+ int num_name, num_desc, i, found_name, found_desc;
+ size_t dlen;
+
+ wpa_printf(MSG_DEBUG, "NDIS: Packet.dll version: %s",
+ PacketGetVersion());
+
+ len = 8192;
+ _names = os_zalloc(len);
+ if (_names == NULL)
+ return -1;
+
+ res = PacketGetAdapterNames(_names, &len);
+ if (!res && len > 8192) {
+ os_free(_names);
+ _names = os_zalloc(len);
+ if (_names == NULL)
+ return -1;
+ res = PacketGetAdapterNames(_names, &len);
+ }
+
+ if (!res) {
+ wpa_printf(MSG_ERROR, "NDIS: Failed to get adapter list "
+ "(PacketGetAdapterNames)");
+ os_free(_names);
+ return -1;
+ }
+
+ names = (char *) _names;
+ if (names[0] && names[1] == '\0' && names[2] && names[3] == '\0') {
+ wpa_printf(MSG_DEBUG, "NDIS: Looks like adapter names are in "
+ "UNICODE");
+ /* Convert to ASCII */
+ pos2 = pos = names;
+ while (pos2 < names + len) {
+ if (pos2[0] == '\0' && pos2[1] == '\0' &&
+ pos2[2] == '\0' && pos2[3] == '\0') {
+ pos2 += 4;
+ break;
+ }
+ *pos++ = pos2[0];
+ pos2 += 2;
+ }
+ os_memcpy(pos + 2, names, pos - names);
+ pos += 2;
+ } else
+ pos = names;
+
+ num_name = 0;
+ while (pos < names + len) {
+ name[num_name] = pos;
+ while (*pos && pos < names + len)
+ pos++;
+ if (pos + 1 >= names + len) {
+ os_free(names);
+ return -1;
+ }
+ pos++;
+ num_name++;
+ if (num_name >= MAX_ADAPTERS) {
+ wpa_printf(MSG_DEBUG, "NDIS: Too many adapters");
+ os_free(names);
+ return -1;
+ }
+ if (*pos == '\0') {
+ wpa_printf(MSG_DEBUG, "NDIS: %d adapter names found",
+ num_name);
+ pos++;
+ break;
+ }
+ }
+
+ num_desc = 0;
+ while (pos < names + len) {
+ desc[num_desc] = pos;
+ while (*pos && pos < names + len)
+ pos++;
+ if (pos + 1 >= names + len) {
+ os_free(names);
+ return -1;
+ }
+ pos++;
+ num_desc++;
+ if (num_desc >= MAX_ADAPTERS) {
+ wpa_printf(MSG_DEBUG, "NDIS: Too many adapter "
+ "descriptions");
+ os_free(names);
+ return -1;
+ }
+ if (*pos == '\0') {
+ wpa_printf(MSG_DEBUG, "NDIS: %d adapter descriptions "
+ "found", num_name);
+ pos++;
+ break;
+ }
+ }
+
+ /*
+ * Windows 98 with Packet.dll 3.0 alpha3 does not include adapter
+ * descriptions. Fill in dummy descriptors to work around this.
+ */
+ while (num_desc < num_name)
+ desc[num_desc++] = "dummy description";
+
+ if (num_name != num_desc) {
+ wpa_printf(MSG_DEBUG, "NDIS: mismatch in adapter name and "
+ "description counts (%d != %d)",
+ num_name, num_desc);
+ os_free(names);
+ return -1;
+ }
+
+ found_name = found_desc = -1;
+ for (i = 0; i < num_name; i++) {
+ wpa_printf(MSG_DEBUG, "NDIS: %d - %s - %s",
+ i, name[i], desc[i]);
+ if (found_name == -1 && os_strstr(name[i], drv->ifname))
+ found_name = i;
+ if (found_desc == -1 &&
+ os_strncmp(desc[i], drv->ifname, os_strlen(drv->ifname)) ==
+ 0)
+ found_desc = i;
+ }
+
+ if (found_name < 0 && found_desc >= 0) {
+ wpa_printf(MSG_DEBUG, "NDIS: Matched interface '%s' based on "
+ "description '%s'",
+ name[found_desc], desc[found_desc]);
+ found_name = found_desc;
+ os_strlcpy(drv->ifname,
+ os_strncmp(name[found_desc], "\\Device\\NPF_", 12)
+ == 0 ? name[found_desc] + 12 : name[found_desc],
+ sizeof(drv->ifname));
+ }
+
+ if (found_name < 0) {
+ wpa_printf(MSG_DEBUG, "NDIS: Could not find interface '%s'",
+ drv->ifname);
+ os_free(names);
+ return -1;
+ }
+
+ i = found_name;
+ pos = os_strrchr(desc[i], '(');
+ if (pos) {
+ dlen = pos - desc[i];
+ pos--;
+ if (pos > desc[i] && *pos == ' ')
+ dlen--;
+ } else {
+ dlen = os_strlen(desc[i]);
+ }
+ drv->adapter_desc = dup_binstr(desc[i], dlen);
+ os_free(names);
+ if (drv->adapter_desc == NULL)
+ return -1;
+
+ wpa_printf(MSG_DEBUG, "NDIS: Adapter description prefix '%s'",
+ drv->adapter_desc);
+
+ return 0;
+#endif /* CONFIG_USE_NDISUIO */
+}
+
+
+#if defined(CONFIG_NATIVE_WINDOWS) || defined(__CYGWIN__)
+#ifndef _WIN32_WCE
+/*
+ * These structures are undocumented for WinXP; only WinCE version is
+ * documented. These would be included wzcsapi.h if it were available. Some
+ * changes here have been needed to make the structures match with WinXP SP2.
+ * It is unclear whether these work with any other version.
+ */
+
+typedef struct {
+ LPWSTR wszGuid;
+} INTF_KEY_ENTRY, *PINTF_KEY_ENTRY;
+
+typedef struct {
+ DWORD dwNumIntfs;
+ PINTF_KEY_ENTRY pIntfs;
+} INTFS_KEY_TABLE, *PINTFS_KEY_TABLE;
+
+typedef struct {
+ DWORD dwDataLen;
+ LPBYTE pData;
+} RAW_DATA, *PRAW_DATA;
+
+typedef struct {
+ LPWSTR wszGuid;
+ LPWSTR wszDescr;
+ ULONG ulMediaState;
+ ULONG ulMediaType;
+ ULONG ulPhysicalMediaType;
+ INT nInfraMode;
+ INT nAuthMode;
+ INT nWepStatus;
+#ifndef _WIN32_WCE
+ u8 pad[2]; /* why is this needed? */
+#endif /* _WIN32_WCE */
+ DWORD dwCtlFlags;
+ DWORD dwCapabilities; /* something added for WinXP SP2(?) */
+ RAW_DATA rdSSID;
+ RAW_DATA rdBSSID;
+ RAW_DATA rdBSSIDList;
+ RAW_DATA rdStSSIDList;
+ RAW_DATA rdCtrlData;
+#ifdef UNDER_CE
+ BOOL bInitialized;
+#endif
+ DWORD nWPAMCastCipher;
+ /* add some extra buffer for later additions since this interface is
+ * far from stable */
+ u8 later_additions[100];
+} INTF_ENTRY, *PINTF_ENTRY;
+
+#define INTF_ALL 0xffffffff
+#define INTF_ALL_FLAGS 0x0000ffff
+#define INTF_CTLFLAGS 0x00000010
+#define INTFCTL_ENABLED 0x8000
+#endif /* _WIN32_WCE */
+
+
+#ifdef _WIN32_WCE
+static int wpa_driver_ndis_rebind_adapter(struct wpa_driver_ndis_data *drv)
+{
+ HANDLE ndis;
+ TCHAR multi[100];
+ int len;
+
+ len = _tcslen(drv->adapter_name);
+ if (len > 80)
+ return -1;
+
+ ndis = CreateFile(DD_NDIS_DEVICE_NAME, GENERIC_READ | GENERIC_WRITE,
+ 0, NULL, OPEN_EXISTING, 0, NULL);
+ if (ndis == INVALID_HANDLE_VALUE) {
+ wpa_printf(MSG_DEBUG, "NDIS: Failed to open file to NDIS "
+ "device: %d", (int) GetLastError());
+ return -1;
+ }
+
+ len++;
+ memcpy(multi, drv->adapter_name, len * sizeof(TCHAR));
+ memcpy(&multi[len], TEXT("NDISUIO\0"), 9 * sizeof(TCHAR));
+ len += 9;
+
+ if (!DeviceIoControl(ndis, IOCTL_NDIS_REBIND_ADAPTER,
+ multi, len * sizeof(TCHAR), NULL, 0, NULL, NULL))
+ {
+ wpa_printf(MSG_DEBUG, "NDIS: IOCTL_NDIS_REBIND_ADAPTER "
+ "failed: 0x%x", (int) GetLastError());
+ wpa_hexdump_ascii(MSG_DEBUG, "NDIS: rebind multi_sz",
+ (u8 *) multi, len * sizeof(TCHAR));
+ CloseHandle(ndis);
+ return -1;
+ }
+
+ CloseHandle(ndis);
+
+ wpa_printf(MSG_DEBUG, "NDIS: Requested NDIS rebind of NDISUIO "
+ "protocol");
+
+ return 0;
+}
+#endif /* _WIN32_WCE */
+
+
+static int wpa_driver_ndis_set_wzc(struct wpa_driver_ndis_data *drv,
+ int enable)
+{
+#ifdef _WIN32_WCE
+ HKEY hk, hk2;
+ LONG ret;
+ DWORD i, hnd, len;
+ TCHAR keyname[256], devname[256];
+
+#define WZC_DRIVER TEXT("Drivers\\BuiltIn\\ZeroConfig")
+
+ if (enable) {
+ HANDLE h;
+ h = ActivateDeviceEx(WZC_DRIVER, NULL, 0, NULL);
+ if (h == INVALID_HANDLE_VALUE || h == 0) {
+ wpa_printf(MSG_DEBUG, "NDIS: Failed to re-enable WZC "
+ "- ActivateDeviceEx failed: %d",
+ (int) GetLastError());
+ return -1;
+ }
+
+ wpa_printf(MSG_DEBUG, "NDIS: WZC re-enabled");
+ return wpa_driver_ndis_rebind_adapter(drv);
+ }
+
+ /*
+ * Unfortunately, just disabling the WZC for an interface is not enough
+ * to free NDISUIO for us, so need to disable and unload WZC completely
+ * for now when using WinCE with NDISUIO. In addition, must request
+ * NDISUIO protocol to be rebound to the adapter in order to free the
+ * NDISUIO binding that WZC hold before us.
+ */
+
+ /* Enumerate HKLM\Drivers\Active\* to find a handle to WZC. */
+ ret = RegOpenKeyEx(HKEY_LOCAL_MACHINE, DEVLOAD_ACTIVE_KEY, 0, 0, &hk);
+ if (ret != ERROR_SUCCESS) {
+ wpa_printf(MSG_DEBUG, "NDIS: RegOpenKeyEx(DEVLOAD_ACTIVE_KEY) "
+ "failed: %d %d", (int) ret, (int) GetLastError());
+ return -1;
+ }
+
+ for (i = 0; ; i++) {
+ len = sizeof(keyname);
+ ret = RegEnumKeyEx(hk, i, keyname, &len, NULL, NULL, NULL,
+ NULL);
+ if (ret != ERROR_SUCCESS) {
+ wpa_printf(MSG_DEBUG, "NDIS: Could not find active "
+ "WZC - assuming it is not running.");
+ RegCloseKey(hk);
+ return -1;
+ }
+
+ ret = RegOpenKeyEx(hk, keyname, 0, 0, &hk2);
+ if (ret != ERROR_SUCCESS) {
+ wpa_printf(MSG_DEBUG, "NDIS: RegOpenKeyEx(active dev) "
+ "failed: %d %d",
+ (int) ret, (int) GetLastError());
+ continue;
+ }
+
+ len = sizeof(devname);
+ ret = RegQueryValueEx(hk2, DEVLOAD_DEVKEY_VALNAME, NULL, NULL,
+ (LPBYTE) devname, &len);
+ if (ret != ERROR_SUCCESS) {
+ wpa_printf(MSG_DEBUG, "NDIS: RegQueryValueEx("
+ "DEVKEY_VALNAME) failed: %d %d",
+ (int) ret, (int) GetLastError());
+ RegCloseKey(hk2);
+ continue;
+ }
+
+ if (_tcscmp(devname, WZC_DRIVER) == 0)
+ break;
+
+ RegCloseKey(hk2);
+ }
+
+ RegCloseKey(hk);
+
+ /* Found WZC - get handle to it. */
+ len = sizeof(hnd);
+ ret = RegQueryValueEx(hk2, DEVLOAD_HANDLE_VALNAME, NULL, NULL,
+ (PUCHAR) &hnd, &len);
+ if (ret != ERROR_SUCCESS) {
+ wpa_printf(MSG_DEBUG, "NDIS: RegQueryValueEx(HANDLE_VALNAME) "
+ "failed: %d %d", (int) ret, (int) GetLastError());
+ RegCloseKey(hk2);
+ return -1;
+ }
+
+ RegCloseKey(hk2);
+
+ /* Deactivate WZC */
+ if (!DeactivateDevice((HANDLE) hnd)) {
+ wpa_printf(MSG_DEBUG, "NDIS: DeactivateDevice failed: %d",
+ (int) GetLastError());
+ return -1;
+ }
+
+ wpa_printf(MSG_DEBUG, "NDIS: Disabled WZC temporarily");
+ drv->wzc_disabled = 1;
+ return wpa_driver_ndis_rebind_adapter(drv);
+
+#else /* _WIN32_WCE */
+
+ HMODULE hm;
+ DWORD (WINAPI *wzc_enum_interf)(LPWSTR pSrvAddr,
+ PINTFS_KEY_TABLE pIntfs);
+ DWORD (WINAPI *wzc_query_interf)(LPWSTR pSrvAddr, DWORD dwInFlags,
+ PINTF_ENTRY pIntf,
+ LPDWORD pdwOutFlags);
+ DWORD (WINAPI *wzc_set_interf)(LPWSTR pSrvAddr, DWORD dwInFlags,
+ PINTF_ENTRY pIntf, LPDWORD pdwOutFlags);
+ int ret = -1, j;
+ DWORD res;
+ INTFS_KEY_TABLE guids;
+ INTF_ENTRY intf;
+ char guid[128];
+ WCHAR *pos;
+ DWORD flags, i;
+
+ hm = LoadLibrary(TEXT("wzcsapi.dll"));
+ if (hm == NULL) {
+ wpa_printf(MSG_DEBUG, "NDIS: Failed to load wzcsapi.dll (%u) "
+ "- WZC probably not running",
+ (unsigned int) GetLastError());
+ return -1;
+ }
+
+#ifdef _WIN32_WCE
+ wzc_enum_interf = (void *) GetProcAddressA(hm, "WZCEnumInterfaces");
+ wzc_query_interf = (void *) GetProcAddressA(hm, "WZCQueryInterface");
+ wzc_set_interf = (void *) GetProcAddressA(hm, "WZCSetInterface");
+#else /* _WIN32_WCE */
+ wzc_enum_interf = (void *) GetProcAddress(hm, "WZCEnumInterfaces");
+ wzc_query_interf = (void *) GetProcAddress(hm, "WZCQueryInterface");
+ wzc_set_interf = (void *) GetProcAddress(hm, "WZCSetInterface");
+#endif /* _WIN32_WCE */
+
+ if (wzc_enum_interf == NULL || wzc_query_interf == NULL ||
+ wzc_set_interf == NULL) {
+ wpa_printf(MSG_DEBUG, "NDIS: WZCEnumInterfaces, "
+ "WZCQueryInterface, or WZCSetInterface not found "
+ "in wzcsapi.dll");
+ goto fail;
+ }
+
+ os_memset(&guids, 0, sizeof(guids));
+ res = wzc_enum_interf(NULL, &guids);
+ if (res != 0) {
+ wpa_printf(MSG_DEBUG, "NDIS: WZCEnumInterfaces failed: %d; "
+ "WZC service is apparently not running",
+ (int) res);
+ goto fail;
+ }
+
+ wpa_printf(MSG_DEBUG, "NDIS: WZCEnumInterfaces: %d interfaces",
+ (int) guids.dwNumIntfs);
+
+ for (i = 0; i < guids.dwNumIntfs; i++) {
+ pos = guids.pIntfs[i].wszGuid;
+ for (j = 0; j < sizeof(guid); j++) {
+ guid[j] = (char) *pos;
+ if (*pos == 0)
+ break;
+ pos++;
+ }
+ guid[sizeof(guid) - 1] = '\0';
+ wpa_printf(MSG_DEBUG, "NDIS: intfs %d GUID '%s'",
+ (int) i, guid);
+ if (os_strstr(drv->ifname, guid) == NULL)
+ continue;
+
+ wpa_printf(MSG_DEBUG, "NDIS: Current interface found from "
+ "WZC");
+ break;
+ }
+
+ if (i >= guids.dwNumIntfs) {
+ wpa_printf(MSG_DEBUG, "NDIS: Current interface not found from "
+ "WZC");
+ goto fail;
+ }
+
+ os_memset(&intf, 0, sizeof(intf));
+ intf.wszGuid = guids.pIntfs[i].wszGuid;
+ /* Set flags to verify that the structure has not changed. */
+ intf.dwCtlFlags = -1;
+ flags = 0;
+ res = wzc_query_interf(NULL, INTFCTL_ENABLED, &intf, &flags);
+ if (res != 0) {
+ wpa_printf(MSG_DEBUG, "NDIS: Could not query flags for the "
+ "WZC interface: %d (0x%x)",
+ (int) res, (int) res);
+ wpa_printf(MSG_DEBUG, "NDIS: GetLastError: %u",
+ (unsigned int) GetLastError());
+ goto fail;
+ }
+
+ wpa_printf(MSG_DEBUG, "NDIS: WZC interface flags 0x%x dwCtlFlags 0x%x",
+ (int) flags, (int) intf.dwCtlFlags);
+
+ if (intf.dwCtlFlags == -1) {
+ wpa_printf(MSG_DEBUG, "NDIS: Looks like wzcsapi has changed "
+ "again - could not disable WZC");
+ wpa_hexdump(MSG_MSGDUMP, "NDIS: intf",
+ (u8 *) &intf, sizeof(intf));
+ goto fail;
+ }
+
+ if (enable) {
+ if (!(intf.dwCtlFlags & INTFCTL_ENABLED)) {
+ wpa_printf(MSG_DEBUG, "NDIS: Enabling WZC for this "
+ "interface");
+ intf.dwCtlFlags |= INTFCTL_ENABLED;
+ res = wzc_set_interf(NULL, INTFCTL_ENABLED, &intf,
+ &flags);
+ if (res != 0) {
+ wpa_printf(MSG_DEBUG, "NDIS: Failed to enable "
+ "WZC: %d (0x%x)",
+ (int) res, (int) res);
+ wpa_printf(MSG_DEBUG, "NDIS: GetLastError: %u",
+ (unsigned int) GetLastError());
+ goto fail;
+ }
+ wpa_printf(MSG_DEBUG, "NDIS: Re-enabled WZC for this "
+ "interface");
+ drv->wzc_disabled = 0;
+ }
+ } else {
+ if (intf.dwCtlFlags & INTFCTL_ENABLED) {
+ wpa_printf(MSG_DEBUG, "NDIS: Disabling WZC for this "
+ "interface");
+ intf.dwCtlFlags &= ~INTFCTL_ENABLED;
+ res = wzc_set_interf(NULL, INTFCTL_ENABLED, &intf,
+ &flags);
+ if (res != 0) {
+ wpa_printf(MSG_DEBUG, "NDIS: Failed to "
+ "disable WZC: %d (0x%x)",
+ (int) res, (int) res);
+ wpa_printf(MSG_DEBUG, "NDIS: GetLastError: %u",
+ (unsigned int) GetLastError());
+ goto fail;
+ }
+ wpa_printf(MSG_DEBUG, "NDIS: Disabled WZC temporarily "
+ "for this interface");
+ drv->wzc_disabled = 1;
+ } else {
+ wpa_printf(MSG_DEBUG, "NDIS: WZC was not enabled for "
+ "this interface");
+ }
+ }
+
+ ret = 0;
+
+fail:
+ FreeLibrary(hm);
+
+ return ret;
+#endif /* _WIN32_WCE */
+}
+
+#else /* CONFIG_NATIVE_WINDOWS || __CYGWIN__ */
+
+static int wpa_driver_ndis_set_wzc(struct wpa_driver_ndis_data *drv,
+ int enable)
+{
+ return 0;
+}
+
+#endif /* CONFIG_NATIVE_WINDOWS || __CYGWIN__ */
+
+
+#ifdef CONFIG_USE_NDISUIO
+/*
+ * l2_packet_ndis.c is sharing the same handle to NDISUIO, so we must be able
+ * to export this handle. This is somewhat ugly, but there is no better
+ * mechanism available to pass data from driver interface to l2_packet wrapper.
+ */
+static HANDLE driver_ndis_ndisuio_handle = INVALID_HANDLE_VALUE;
+
+HANDLE driver_ndis_get_ndisuio_handle(void)
+{
+ return driver_ndis_ndisuio_handle;
+}
+#endif /* CONFIG_USE_NDISUIO */
+
+
+static int wpa_driver_ndis_adapter_init(struct wpa_driver_ndis_data *drv)
+{
+#ifdef CONFIG_USE_NDISUIO
+#ifndef _WIN32_WCE
+#define NDISUIO_DEVICE_NAME TEXT("\\\\.\\\\Ndisuio")
+ DWORD written;
+#endif /* _WIN32_WCE */
+ drv->ndisuio = CreateFile(NDISUIO_DEVICE_NAME,
+ GENERIC_READ | GENERIC_WRITE, 0, NULL,
+ OPEN_EXISTING,
+ FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED,
+ INVALID_HANDLE_VALUE);
+ if (drv->ndisuio == INVALID_HANDLE_VALUE) {
+ wpa_printf(MSG_ERROR, "NDIS: Failed to open connection to "
+ "NDISUIO: %d", (int) GetLastError());
+ return -1;
+ }
+ driver_ndis_ndisuio_handle = drv->ndisuio;
+
+#ifndef _WIN32_WCE
+ if (!DeviceIoControl(drv->ndisuio, IOCTL_NDISUIO_BIND_WAIT, NULL, 0,
+ NULL, 0, &written, NULL)) {
+ wpa_printf(MSG_ERROR, "NDIS: IOCTL_NDISUIO_BIND_WAIT failed: "
+ "%d", (int) GetLastError());
+ CloseHandle(drv->ndisuio);
+ drv->ndisuio = INVALID_HANDLE_VALUE;
+ return -1;
+ }
+#endif /* _WIN32_WCE */
+
+ return 0;
+#else /* CONFIG_USE_NDISUIO */
+ return 0;
+#endif /* CONFIG_USE_NDISUIO */
+}
+
+
+static int wpa_driver_ndis_adapter_open(struct wpa_driver_ndis_data *drv)
+{
+#ifdef CONFIG_USE_NDISUIO
+ DWORD written;
+#define MAX_NDIS_DEVICE_NAME_LEN 256
+ WCHAR ifname[MAX_NDIS_DEVICE_NAME_LEN];
+ size_t len, i, pos;
+ const char *prefix = "\\DEVICE\\";
+
+#ifdef _WIN32_WCE
+ pos = 0;
+#else /* _WIN32_WCE */
+ pos = 8;
+#endif /* _WIN32_WCE */
+ len = pos + os_strlen(drv->ifname);
+ if (len >= MAX_NDIS_DEVICE_NAME_LEN)
+ return -1;
+ for (i = 0; i < pos; i++)
+ ifname[i] = (WCHAR) prefix[i];
+ for (i = pos; i < len; i++)
+ ifname[i] = (WCHAR) drv->ifname[i - pos];
+ ifname[i] = L'\0';
+
+ if (!DeviceIoControl(drv->ndisuio, IOCTL_NDISUIO_OPEN_DEVICE,
+ ifname, len * sizeof(WCHAR), NULL, 0, &written,
+ NULL)) {
+ wpa_printf(MSG_ERROR, "NDIS: IOCTL_NDISUIO_OPEN_DEVICE "
+ "failed: %d", (int) GetLastError());
+ wpa_hexdump_ascii(MSG_DEBUG, "NDIS: ifname",
+ (const u8 *) ifname, len * sizeof(WCHAR));
+ CloseHandle(drv->ndisuio);
+ drv->ndisuio = INVALID_HANDLE_VALUE;
+ return -1;
+ }
+
+ wpa_printf(MSG_DEBUG, "NDIS: Opened NDISUIO device successfully");
+
+ return 0;
+#else /* CONFIG_USE_NDISUIO */
+ char ifname[128];
+ os_snprintf(ifname, sizeof(ifname), "\\Device\\NPF_%s", drv->ifname);
+ drv->adapter = PacketOpenAdapter(ifname);
+ if (drv->adapter == NULL) {
+ wpa_printf(MSG_DEBUG, "NDIS: PacketOpenAdapter failed for "
+ "'%s'", ifname);
+ return -1;
+ }
+ return 0;
+#endif /* CONFIG_USE_NDISUIO */
+}
+
+
+static void wpa_driver_ndis_adapter_close(struct wpa_driver_ndis_data *drv)
+{
+#ifdef CONFIG_USE_NDISUIO
+ driver_ndis_ndisuio_handle = INVALID_HANDLE_VALUE;
+ if (drv->ndisuio != INVALID_HANDLE_VALUE)
+ CloseHandle(drv->ndisuio);
+#else /* CONFIG_USE_NDISUIO */
+ if (drv->adapter)
+ PacketCloseAdapter(drv->adapter);
+#endif /* CONFIG_USE_NDISUIO */
+}
+
+
+static int ndis_add_multicast(struct wpa_driver_ndis_data *drv)
+{
+ if (ndis_set_oid(drv, OID_802_3_MULTICAST_LIST,
+ (const char *) pae_group_addr, ETH_ALEN) < 0) {
+ wpa_printf(MSG_DEBUG, "NDIS: Failed to add PAE group address "
+ "to the multicast list");
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static void * wpa_driver_ndis_init(void *ctx, const char *ifname)
+{
+ struct wpa_driver_ndis_data *drv;
+ u32 mode;
+
+ drv = os_zalloc(sizeof(*drv));
+ if (drv == NULL)
+ return NULL;
+ drv->ctx = ctx;
+ /*
+ * Compatibility code to strip possible prefix from the GUID. Previous
+ * versions include \Device\NPF_ prefix for all names, but the internal
+ * interface name is now only the GUI. Both Packet32 and NDISUIO
+ * prefixes are supported.
+ */
+ if (os_strncmp(ifname, "\\Device\\NPF_", 12) == 0)
+ ifname += 12;
+ else if (os_strncmp(ifname, "\\DEVICE\\", 8) == 0)
+ ifname += 8;
+ os_strlcpy(drv->ifname, ifname, sizeof(drv->ifname));
+
+ if (wpa_driver_ndis_adapter_init(drv) < 0) {
+ os_free(drv);
+ return NULL;
+ }
+
+ if (wpa_driver_ndis_get_names(drv) < 0) {
+ wpa_driver_ndis_adapter_close(drv);
+ os_free(drv);
+ return NULL;
+ }
+
+ wpa_driver_ndis_set_wzc(drv, 0);
+
+ if (wpa_driver_ndis_adapter_open(drv) < 0) {
+ wpa_driver_ndis_adapter_close(drv);
+ os_free(drv);
+ return NULL;
+ }
+
+ if (ndis_get_oid(drv, OID_802_3_CURRENT_ADDRESS,
+ (char *) drv->own_addr, ETH_ALEN) < 0) {
+ wpa_printf(MSG_DEBUG, "NDIS: Get OID_802_3_CURRENT_ADDRESS "
+ "failed");
+ wpa_driver_ndis_adapter_close(drv);
+ os_free(drv);
+ return NULL;
+ }
+ wpa_driver_ndis_get_capability(drv);
+
+ /* Make sure that the driver does not have any obsolete PMKID entries.
+ */
+ wpa_driver_ndis_flush_pmkid(drv);
+
+ /*
+ * Disconnect to make sure that driver re-associates if it was
+ * connected.
+ */
+ wpa_driver_ndis_disconnect(drv);
+
+ eloop_register_timeout(1, 0, wpa_driver_ndis_poll_timeout, drv, NULL);
+
+#ifdef CONFIG_NDIS_EVENTS_INTEGRATED
+ drv->events = ndis_events_init(&drv->events_pipe, &drv->event_avail,
+ drv->ifname, drv->adapter_desc);
+ if (drv->events == NULL) {
+ wpa_driver_ndis_deinit(drv);
+ return NULL;
+ }
+ eloop_register_event(drv->event_avail, sizeof(drv->event_avail),
+ wpa_driver_ndis_event_pipe_cb, drv, NULL);
+#endif /* CONFIG_NDIS_EVENTS_INTEGRATED */
+
+#ifdef _WIN32_WCE
+ if (ndisuio_notification_init(drv) < 0) {
+ wpa_driver_ndis_deinit(drv);
+ return NULL;
+ }
+#endif /* _WIN32_WCE */
+
+ /* Set mode here in case card was configured for ad-hoc mode
+ * previously. */
+ mode = Ndis802_11Infrastructure;
+ if (ndis_set_oid(drv, OID_802_11_INFRASTRUCTURE_MODE,
+ (char *) &mode, sizeof(mode)) < 0) {
+ char buf[8];
+ int res;
+ wpa_printf(MSG_DEBUG, "NDIS: Failed to set "
+ "OID_802_11_INFRASTRUCTURE_MODE (%d)",
+ (int) mode);
+ /* Try to continue anyway */
+
+ res = ndis_get_oid(drv, OID_DOT11_CURRENT_OPERATION_MODE, buf,
+ sizeof(buf));
+ if (res > 0) {
+ wpa_printf(MSG_INFO, "NDIS: The driver seems to use "
+ "Native 802.11 OIDs. These are not yet "
+ "fully supported.");
+ drv->native80211 = 1;
+ } else if (!drv->has_capability || drv->capa.enc == 0) {
+ /*
+ * Note: This will also happen with NDIS 6 drivers with
+ * Vista.
+ */
+ wpa_printf(MSG_DEBUG, "NDIS: Driver did not provide "
+ "any wireless capabilities - assume it is "
+ "a wired interface");
+ drv->wired = 1;
+ drv->capa.flags |= WPA_DRIVER_FLAGS_WIRED;
+ drv->has_capability = 1;
+ ndis_add_multicast(drv);
+ }
+ }
+
+ return drv;
+}
+
+
+static void wpa_driver_ndis_deinit(void *priv)
+{
+ struct wpa_driver_ndis_data *drv = priv;
+
+#ifdef CONFIG_NDIS_EVENTS_INTEGRATED
+ if (drv->events) {
+ eloop_unregister_event(drv->event_avail,
+ sizeof(drv->event_avail));
+ ndis_events_deinit(drv->events);
+ }
+#endif /* CONFIG_NDIS_EVENTS_INTEGRATED */
+
+#ifdef _WIN32_WCE
+ ndisuio_notification_deinit(drv);
+#endif /* _WIN32_WCE */
+
+ eloop_cancel_timeout(wpa_driver_ndis_scan_timeout, drv, drv->ctx);
+ eloop_cancel_timeout(wpa_driver_ndis_poll_timeout, drv, NULL);
+ wpa_driver_ndis_flush_pmkid(drv);
+ wpa_driver_ndis_disconnect(drv);
+ if (wpa_driver_ndis_radio_off(drv) < 0) {
+ wpa_printf(MSG_DEBUG, "NDIS: failed to disassociate and turn "
+ "radio off");
+ }
+
+ wpa_driver_ndis_adapter_close(drv);
+
+ if (drv->wzc_disabled)
+ wpa_driver_ndis_set_wzc(drv, 1);
+
+#ifdef _WIN32_WCE
+ os_free(drv->adapter_name);
+#endif /* _WIN32_WCE */
+ os_free(drv->adapter_desc);
+ os_free(drv);
+}
+
+
+static struct wpa_interface_info *
+wpa_driver_ndis_get_interfaces(void *global_priv)
+{
+ struct wpa_interface_info *iface = NULL, *niface;
+
+#ifdef CONFIG_USE_NDISUIO
+ NDISUIO_QUERY_BINDING *b;
+ size_t blen = sizeof(*b) + 1024;
+ int i, error;
+ DWORD written;
+ char name[256], desc[256];
+ WCHAR *pos;
+ size_t j, len;
+ HANDLE ndisuio;
+
+ ndisuio = CreateFile(NDISUIO_DEVICE_NAME,
+ GENERIC_READ | GENERIC_WRITE, 0, NULL,
+ OPEN_EXISTING,
+ FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED,
+ INVALID_HANDLE_VALUE);
+ if (ndisuio == INVALID_HANDLE_VALUE) {
+ wpa_printf(MSG_ERROR, "NDIS: Failed to open connection to "
+ "NDISUIO: %d", (int) GetLastError());
+ return NULL;
+ }
+
+#ifndef _WIN32_WCE
+ if (!DeviceIoControl(ndisuio, IOCTL_NDISUIO_BIND_WAIT, NULL, 0,
+ NULL, 0, &written, NULL)) {
+ wpa_printf(MSG_ERROR, "NDIS: IOCTL_NDISUIO_BIND_WAIT failed: "
+ "%d", (int) GetLastError());
+ CloseHandle(ndisuio);
+ return NULL;
+ }
+#endif /* _WIN32_WCE */
+
+ b = os_malloc(blen);
+ if (b == NULL) {
+ CloseHandle(ndisuio);
+ return NULL;
+ }
+
+ for (i = 0; ; i++) {
+ os_memset(b, 0, blen);
+ b->BindingIndex = i;
+ if (!DeviceIoControl(ndisuio, IOCTL_NDISUIO_QUERY_BINDING,
+ b, sizeof(NDISUIO_QUERY_BINDING), b, blen,
+ &written, NULL)) {
+ error = (int) GetLastError();
+ if (error == ERROR_NO_MORE_ITEMS)
+ break;
+ wpa_printf(MSG_DEBUG, "IOCTL_NDISUIO_QUERY_BINDING "
+ "failed: %d", error);
+ break;
+ }
+
+ pos = (WCHAR *) ((char *) b + b->DeviceNameOffset);
+ len = b->DeviceNameLength;
+ if (len >= sizeof(name))
+ len = sizeof(name) - 1;
+ for (j = 0; j < len; j++)
+ name[j] = (char) pos[j];
+ name[len] = '\0';
+
+ pos = (WCHAR *) ((char *) b + b->DeviceDescrOffset);
+ len = b->DeviceDescrLength;
+ if (len >= sizeof(desc))
+ len = sizeof(desc) - 1;
+ for (j = 0; j < len; j++)
+ desc[j] = (char) pos[j];
+ desc[len] = '\0';
+
+ wpa_printf(MSG_DEBUG, "NDIS: %d - %s - %s", i, name, desc);
+
+ niface = os_zalloc(sizeof(*niface));
+ if (niface == NULL)
+ break;
+ niface->drv_name = "ndis";
+ if (os_strncmp(name, "\\DEVICE\\", 8) == 0)
+ niface->ifname = os_strdup(name + 8);
+ else
+ niface->ifname = os_strdup(name);
+ if (niface->ifname == NULL) {
+ os_free(niface);
+ break;
+ }
+ niface->desc = os_strdup(desc);
+ niface->next = iface;
+ iface = niface;
+ }
+
+ os_free(b);
+ CloseHandle(ndisuio);
+#else /* CONFIG_USE_NDISUIO */
+ PTSTR _names;
+ char *names, *pos, *pos2;
+ ULONG len;
+ BOOLEAN res;
+ char *name[MAX_ADAPTERS];
+ char *desc[MAX_ADAPTERS];
+ int num_name, num_desc, i;
+
+ wpa_printf(MSG_DEBUG, "NDIS: Packet.dll version: %s",
+ PacketGetVersion());
+
+ len = 8192;
+ _names = os_zalloc(len);
+ if (_names == NULL)
+ return NULL;
+
+ res = PacketGetAdapterNames(_names, &len);
+ if (!res && len > 8192) {
+ os_free(_names);
+ _names = os_zalloc(len);
+ if (_names == NULL)
+ return NULL;
+ res = PacketGetAdapterNames(_names, &len);
+ }
+
+ if (!res) {
+ wpa_printf(MSG_ERROR, "NDIS: Failed to get adapter list "
+ "(PacketGetAdapterNames)");
+ os_free(_names);
+ return NULL;
+ }
+
+ names = (char *) _names;
+ if (names[0] && names[1] == '\0' && names[2] && names[3] == '\0') {
+ wpa_printf(MSG_DEBUG, "NDIS: Looks like adapter names are in "
+ "UNICODE");
+ /* Convert to ASCII */
+ pos2 = pos = names;
+ while (pos2 < names + len) {
+ if (pos2[0] == '\0' && pos2[1] == '\0' &&
+ pos2[2] == '\0' && pos2[3] == '\0') {
+ pos2 += 4;
+ break;
+ }
+ *pos++ = pos2[0];
+ pos2 += 2;
+ }
+ os_memcpy(pos + 2, names, pos - names);
+ pos += 2;
+ } else
+ pos = names;
+
+ num_name = 0;
+ while (pos < names + len) {
+ name[num_name] = pos;
+ while (*pos && pos < names + len)
+ pos++;
+ if (pos + 1 >= names + len) {
+ os_free(names);
+ return NULL;
+ }
+ pos++;
+ num_name++;
+ if (num_name >= MAX_ADAPTERS) {
+ wpa_printf(MSG_DEBUG, "NDIS: Too many adapters");
+ os_free(names);
+ return NULL;
+ }
+ if (*pos == '\0') {
+ wpa_printf(MSG_DEBUG, "NDIS: %d adapter names found",
+ num_name);
+ pos++;
+ break;
+ }
+ }
+
+ num_desc = 0;
+ while (pos < names + len) {
+ desc[num_desc] = pos;
+ while (*pos && pos < names + len)
+ pos++;
+ if (pos + 1 >= names + len) {
+ os_free(names);
+ return NULL;
+ }
+ pos++;
+ num_desc++;
+ if (num_desc >= MAX_ADAPTERS) {
+ wpa_printf(MSG_DEBUG, "NDIS: Too many adapter "
+ "descriptions");
+ os_free(names);
+ return NULL;
+ }
+ if (*pos == '\0') {
+ wpa_printf(MSG_DEBUG, "NDIS: %d adapter descriptions "
+ "found", num_name);
+ pos++;
+ break;
+ }
+ }
+
+ /*
+ * Windows 98 with Packet.dll 3.0 alpha3 does not include adapter
+ * descriptions. Fill in dummy descriptors to work around this.
+ */
+ while (num_desc < num_name)
+ desc[num_desc++] = "dummy description";
+
+ if (num_name != num_desc) {
+ wpa_printf(MSG_DEBUG, "NDIS: mismatch in adapter name and "
+ "description counts (%d != %d)",
+ num_name, num_desc);
+ os_free(names);
+ return NULL;
+ }
+
+ for (i = 0; i < num_name; i++) {
+ niface = os_zalloc(sizeof(*niface));
+ if (niface == NULL)
+ break;
+ niface->drv_name = "ndis";
+ if (os_strncmp(name[i], "\\Device\\NPF_", 12) == 0)
+ niface->ifname = os_strdup(name[i] + 12);
+ else
+ niface->ifname = os_strdup(name[i]);
+ if (niface->ifname == NULL) {
+ os_free(niface);
+ break;
+ }
+ niface->desc = os_strdup(desc[i]);
+ niface->next = iface;
+ iface = niface;
+ }
+
+#endif /* CONFIG_USE_NDISUIO */
+
+ return iface;
+}
+
+
+static const char *ndis_drv_name = "ndis";
+static const char *ndis_drv_desc = "Windows NDIS driver";
+
+struct wpa_driver_ops wpa_driver_ndis_ops;
+
+void driver_ndis_init_ops(void)
+{
+ os_memset(&wpa_driver_ndis_ops, 0, sizeof(wpa_driver_ndis_ops));
+ wpa_driver_ndis_ops.name = ndis_drv_name;
+ wpa_driver_ndis_ops.desc = ndis_drv_desc;
+ wpa_driver_ndis_ops.get_bssid = wpa_driver_ndis_get_bssid;
+ wpa_driver_ndis_ops.get_ssid = wpa_driver_ndis_get_ssid;
+ wpa_driver_ndis_ops.set_key = wpa_driver_ndis_set_key;
+ wpa_driver_ndis_ops.init = wpa_driver_ndis_init;
+ wpa_driver_ndis_ops.deinit = wpa_driver_ndis_deinit;
+ wpa_driver_ndis_ops.deauthenticate = wpa_driver_ndis_deauthenticate;
+ wpa_driver_ndis_ops.associate = wpa_driver_ndis_associate;
+ wpa_driver_ndis_ops.add_pmkid = wpa_driver_ndis_add_pmkid;
+ wpa_driver_ndis_ops.remove_pmkid = wpa_driver_ndis_remove_pmkid;
+ wpa_driver_ndis_ops.flush_pmkid = wpa_driver_ndis_flush_pmkid;
+ wpa_driver_ndis_ops.get_capa = wpa_driver_ndis_get_capa;
+ wpa_driver_ndis_ops.poll = wpa_driver_ndis_poll;
+ wpa_driver_ndis_ops.get_ifname = wpa_driver_ndis_get_ifname;
+ wpa_driver_ndis_ops.get_mac_addr = wpa_driver_ndis_get_mac_addr;
+ wpa_driver_ndis_ops.get_scan_results2 =
+ wpa_driver_ndis_get_scan_results;
+ wpa_driver_ndis_ops.get_interfaces = wpa_driver_ndis_get_interfaces;
+ wpa_driver_ndis_ops.scan2 = wpa_driver_ndis_scan;
+}
diff --git a/freebsd/contrib/wpa/src/drivers/driver_ndis.h b/freebsd/contrib/wpa/src/drivers/driver_ndis.h
new file mode 100644
index 00000000..89d136d3
--- /dev/null
+++ b/freebsd/contrib/wpa/src/drivers/driver_ndis.h
@@ -0,0 +1,59 @@
+/*
+ * WPA Supplicant - Windows/NDIS driver interface
+ * Copyright (c) 2004-2006, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef DRIVER_NDIS_H
+#define DRIVER_NDIS_H
+
+#ifdef CONFIG_NDIS_EVENTS_INTEGRATED
+struct ndis_events_data;
+struct ndis_events_data * ndis_events_init(HANDLE *read_pipe, HANDLE *event,
+ const char *ifname,
+ const char *desc);
+void ndis_events_deinit(struct ndis_events_data *events);
+#endif /* CONFIG_NDIS_EVENTS_INTEGRATED */
+
+struct ndis_pmkid_entry {
+ struct ndis_pmkid_entry *next;
+ u8 bssid[ETH_ALEN];
+ u8 pmkid[16];
+};
+
+struct wpa_driver_ndis_data {
+ void *ctx;
+ char ifname[100]; /* GUID: {7EE3EFE5-C165-472F-986D-F6FBEDFE8C8D} */
+#ifdef _WIN32_WCE
+ TCHAR *adapter_name;
+ HANDLE event_queue; /* NDISUIO notifier MsgQueue */
+ HANDLE connected_event; /* WpaSupplicantConnected event */
+#endif /* _WIN32_WCE */
+ u8 own_addr[ETH_ALEN];
+#ifdef CONFIG_USE_NDISUIO
+ HANDLE ndisuio;
+#else /* CONFIG_USE_NDISUIO */
+ LPADAPTER adapter;
+#endif /* CONFIG_USE_NDISUIO */
+ u8 bssid[ETH_ALEN];
+
+ int has_capability;
+ int no_of_pmkid;
+ int radio_enabled;
+ struct wpa_driver_capa capa;
+ struct ndis_pmkid_entry *pmkid;
+ char *adapter_desc;
+ int wired;
+ int native80211;
+ int mode;
+ int wzc_disabled;
+ int oid_bssid_set;
+#ifdef CONFIG_NDIS_EVENTS_INTEGRATED
+ HANDLE events_pipe, event_avail;
+ struct ndis_events_data *events;
+#endif /* CONFIG_NDIS_EVENTS_INTEGRATED */
+};
+
+#endif /* DRIVER_NDIS_H */
diff --git a/freebsd/contrib/wpa/src/drivers/driver_nl80211.h b/freebsd/contrib/wpa/src/drivers/driver_nl80211.h
new file mode 100644
index 00000000..5c21e0fa
--- /dev/null
+++ b/freebsd/contrib/wpa/src/drivers/driver_nl80211.h
@@ -0,0 +1,277 @@
+/*
+ * Driver interaction with Linux nl80211/cfg80211 - definitions
+ * Copyright (c) 2002-2014, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2003-2004, Instant802 Networks, Inc.
+ * Copyright (c) 2005-2006, Devicescape Software, Inc.
+ * Copyright (c) 2007, Johannes Berg <johannes@sipsolutions.net>
+ * Copyright (c) 2009-2010, Atheros Communications
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef DRIVER_NL80211_H
+#define DRIVER_NL80211_H
+
+#include "nl80211_copy.h"
+#include "utils/list.h"
+#include "driver.h"
+
+#ifdef CONFIG_LIBNL20
+/* libnl 2.0 compatibility code */
+#define nl_handle nl_sock
+#define nl80211_handle_alloc nl_socket_alloc_cb
+#define nl80211_handle_destroy nl_socket_free
+#endif /* CONFIG_LIBNL20 */
+
+struct nl80211_global {
+ struct dl_list interfaces;
+ int if_add_ifindex;
+ u64 if_add_wdevid;
+ int if_add_wdevid_set;
+ struct netlink_data *netlink;
+ struct nl_cb *nl_cb;
+ struct nl_handle *nl;
+ int nl80211_id;
+ int ioctl_sock; /* socket for ioctl() use */
+
+ struct nl_handle *nl_event;
+};
+
+struct nl80211_wiphy_data {
+ struct dl_list list;
+ struct dl_list bsss;
+ struct dl_list drvs;
+
+ struct nl_handle *nl_beacons;
+ struct nl_cb *nl_cb;
+
+ int wiphy_idx;
+};
+
+struct i802_bss {
+ struct wpa_driver_nl80211_data *drv;
+ struct i802_bss *next;
+ int ifindex;
+ int br_ifindex;
+ u64 wdev_id;
+ char ifname[IFNAMSIZ + 1];
+ char brname[IFNAMSIZ];
+ unsigned int beacon_set:1;
+ unsigned int added_if_into_bridge:1;
+ unsigned int added_bridge:1;
+ unsigned int in_deinit:1;
+ unsigned int wdev_id_set:1;
+ unsigned int added_if:1;
+ unsigned int static_ap:1;
+
+ u8 addr[ETH_ALEN];
+
+ int freq;
+ int bandwidth;
+ int if_dynamic;
+
+ void *ctx;
+ struct nl_handle *nl_preq, *nl_mgmt;
+ struct nl_cb *nl_cb;
+
+ struct nl80211_wiphy_data *wiphy_data;
+ struct dl_list wiphy_list;
+};
+
+struct wpa_driver_nl80211_data {
+ struct nl80211_global *global;
+ struct dl_list list;
+ struct dl_list wiphy_list;
+ char phyname[32];
+ u8 perm_addr[ETH_ALEN];
+ void *ctx;
+ int ifindex;
+ int if_removed;
+ int if_disabled;
+ int ignore_if_down_event;
+ struct rfkill_data *rfkill;
+ struct wpa_driver_capa capa;
+ u8 *extended_capa, *extended_capa_mask;
+ unsigned int extended_capa_len;
+ int has_capability;
+
+ int operstate;
+
+ int scan_complete_events;
+ enum scan_states {
+ NO_SCAN, SCAN_REQUESTED, SCAN_STARTED, SCAN_COMPLETED,
+ SCAN_ABORTED, SCHED_SCAN_STARTED, SCHED_SCAN_STOPPED,
+ SCHED_SCAN_RESULTS
+ } scan_state;
+
+ u8 auth_bssid[ETH_ALEN];
+ u8 auth_attempt_bssid[ETH_ALEN];
+ u8 bssid[ETH_ALEN];
+ u8 prev_bssid[ETH_ALEN];
+ int associated;
+ u8 ssid[SSID_MAX_LEN];
+ size_t ssid_len;
+ enum nl80211_iftype nlmode;
+ enum nl80211_iftype ap_scan_as_station;
+ unsigned int assoc_freq;
+
+ int monitor_sock;
+ int monitor_ifidx;
+ int monitor_refcount;
+
+ unsigned int disabled_11b_rates:1;
+ unsigned int pending_remain_on_chan:1;
+ unsigned int in_interface_list:1;
+ unsigned int device_ap_sme:1;
+ unsigned int poll_command_supported:1;
+ unsigned int data_tx_status:1;
+ unsigned int scan_for_auth:1;
+ unsigned int retry_auth:1;
+ unsigned int use_monitor:1;
+ unsigned int ignore_next_local_disconnect:1;
+ unsigned int ignore_next_local_deauth:1;
+ unsigned int hostapd:1;
+ unsigned int start_mode_ap:1;
+ unsigned int start_iface_up:1;
+ unsigned int test_use_roc_tx:1;
+ unsigned int ignore_deauth_event:1;
+ unsigned int vendor_cmd_test_avail:1;
+ unsigned int roaming_vendor_cmd_avail:1;
+ unsigned int dfs_vendor_cmd_avail:1;
+ unsigned int have_low_prio_scan:1;
+ unsigned int force_connect_cmd:1;
+ unsigned int addr_changed:1;
+ unsigned int get_features_vendor_cmd_avail:1;
+ unsigned int set_rekey_offload:1;
+ unsigned int p2p_go_ctwindow_supported:1;
+ unsigned int setband_vendor_cmd_avail:1;
+ unsigned int get_pref_freq_list:1;
+ unsigned int set_prob_oper_freq:1;
+
+ u64 remain_on_chan_cookie;
+ u64 send_action_cookie;
+
+ unsigned int last_mgmt_freq;
+
+ struct wpa_driver_scan_filter *filter_ssids;
+ size_t num_filter_ssids;
+
+ struct i802_bss *first_bss;
+
+ int eapol_tx_sock;
+
+ int eapol_sock; /* socket for EAPOL frames */
+
+ struct nl_handle *rtnl_sk; /* nl_sock for NETLINK_ROUTE */
+
+ int default_if_indices[16];
+ int *if_indices;
+ int num_if_indices;
+
+ /* From failed authentication command */
+ int auth_freq;
+ u8 auth_bssid_[ETH_ALEN];
+ u8 auth_ssid[SSID_MAX_LEN];
+ size_t auth_ssid_len;
+ int auth_alg;
+ u8 *auth_ie;
+ size_t auth_ie_len;
+ u8 auth_wep_key[4][16];
+ size_t auth_wep_key_len[4];
+ int auth_wep_tx_keyidx;
+ int auth_local_state_change;
+ int auth_p2p;
+};
+
+struct nl_msg;
+
+void * nl80211_cmd(struct wpa_driver_nl80211_data *drv,
+ struct nl_msg *msg, int flags, uint8_t cmd);
+struct nl_msg * nl80211_cmd_msg(struct i802_bss *bss, int flags, uint8_t cmd);
+struct nl_msg * nl80211_drv_msg(struct wpa_driver_nl80211_data *drv, int flags,
+ uint8_t cmd);
+struct nl_msg * nl80211_bss_msg(struct i802_bss *bss, int flags, uint8_t cmd);
+int send_and_recv_msgs(struct wpa_driver_nl80211_data *drv, struct nl_msg *msg,
+ int (*valid_handler)(struct nl_msg *, void *),
+ void *valid_data);
+int nl80211_create_iface(struct wpa_driver_nl80211_data *drv,
+ const char *ifname, enum nl80211_iftype iftype,
+ const u8 *addr, int wds,
+ int (*handler)(struct nl_msg *, void *),
+ void *arg, int use_existing);
+void nl80211_remove_iface(struct wpa_driver_nl80211_data *drv, int ifidx);
+unsigned int nl80211_get_assoc_freq(struct wpa_driver_nl80211_data *drv);
+enum chan_width convert2width(int width);
+void nl80211_mark_disconnected(struct wpa_driver_nl80211_data *drv);
+struct i802_bss * get_bss_ifindex(struct wpa_driver_nl80211_data *drv,
+ int ifindex);
+int is_ap_interface(enum nl80211_iftype nlmode);
+int is_sta_interface(enum nl80211_iftype nlmode);
+int wpa_driver_nl80211_authenticate_retry(struct wpa_driver_nl80211_data *drv);
+int nl80211_get_link_signal(struct wpa_driver_nl80211_data *drv,
+ struct wpa_signal_info *sig);
+int nl80211_get_link_noise(struct wpa_driver_nl80211_data *drv,
+ struct wpa_signal_info *sig_change);
+int nl80211_get_wiphy_index(struct i802_bss *bss);
+int wpa_driver_nl80211_set_mode(struct i802_bss *bss,
+ enum nl80211_iftype nlmode);
+int wpa_driver_nl80211_mlme(struct wpa_driver_nl80211_data *drv,
+ const u8 *addr, int cmd, u16 reason_code,
+ int local_state_change);
+
+int nl80211_create_monitor_interface(struct wpa_driver_nl80211_data *drv);
+void nl80211_remove_monitor_interface(struct wpa_driver_nl80211_data *drv);
+int nl80211_send_monitor(struct wpa_driver_nl80211_data *drv,
+ const void *data, size_t len,
+ int encrypt, int noack);
+
+int wpa_driver_nl80211_capa(struct wpa_driver_nl80211_data *drv);
+struct hostapd_hw_modes *
+nl80211_get_hw_feature_data(void *priv, u16 *num_modes, u16 *flags);
+
+int process_global_event(struct nl_msg *msg, void *arg);
+int process_bss_event(struct nl_msg *msg, void *arg);
+
+#ifdef ANDROID
+int android_nl_socket_set_nonblocking(struct nl_handle *handle);
+int android_pno_start(struct i802_bss *bss,
+ struct wpa_driver_scan_params *params);
+int android_pno_stop(struct i802_bss *bss);
+extern int wpa_driver_nl80211_driver_cmd(void *priv, char *cmd, char *buf,
+ size_t buf_len);
+
+#ifdef ANDROID_P2P
+int wpa_driver_set_p2p_noa(void *priv, u8 count, int start, int duration);
+int wpa_driver_get_p2p_noa(void *priv, u8 *buf, size_t len);
+int wpa_driver_set_p2p_ps(void *priv, int legacy_ps, int opp_ps, int ctwindow);
+int wpa_driver_set_ap_wps_p2p_ie(void *priv, const struct wpabuf *beacon,
+ const struct wpabuf *proberesp,
+ const struct wpabuf *assocresp);
+#endif /* ANDROID_P2P */
+#endif /* ANDROID */
+
+
+/* driver_nl80211_scan.c */
+
+struct nl80211_bss_info_arg {
+ struct wpa_driver_nl80211_data *drv;
+ struct wpa_scan_results *res;
+ unsigned int assoc_freq;
+ unsigned int ibss_freq;
+ u8 assoc_bssid[ETH_ALEN];
+};
+
+int bss_info_handler(struct nl_msg *msg, void *arg);
+void wpa_driver_nl80211_scan_timeout(void *eloop_ctx, void *timeout_ctx);
+int wpa_driver_nl80211_scan(struct i802_bss *bss,
+ struct wpa_driver_scan_params *params);
+int wpa_driver_nl80211_sched_scan(void *priv,
+ struct wpa_driver_scan_params *params,
+ u32 interval);
+int wpa_driver_nl80211_stop_sched_scan(void *priv);
+struct wpa_scan_results * wpa_driver_nl80211_get_scan_results(void *priv);
+void nl80211_dump_scan(struct wpa_driver_nl80211_data *drv);
+const u8 * nl80211_get_ie(const u8 *ies, size_t ies_len, u8 ie);
+
+#endif /* DRIVER_NL80211_H */
diff --git a/freebsd/contrib/wpa/src/drivers/driver_wired.c b/freebsd/contrib/wpa/src/drivers/driver_wired.c
new file mode 100644
index 00000000..b30d47cf
--- /dev/null
+++ b/freebsd/contrib/wpa/src/drivers/driver_wired.c
@@ -0,0 +1,681 @@
+#include <machine/rtems-bsd-user-space.h>
+
+/*
+ * Wired Ethernet driver interface
+ * Copyright (c) 2005-2009, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004, Gunter Burchardt <tira@isx.de>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+#include <sys/ioctl.h>
+#include <net/if.h>
+#ifdef __linux__
+#include <netpacket/packet.h>
+#include <net/if_arp.h>
+#include <net/if.h>
+#endif /* __linux__ */
+#if defined(__FreeBSD__) || defined(__DragonFly__) || defined(__FreeBSD_kernel__)
+#include <net/if_dl.h>
+#include <net/if_media.h>
+#endif /* defined(__FreeBSD__) || defined(__DragonFly__) || defined(__FreeBSD_kernel__) */
+#ifdef __sun__
+#include <sys/sockio.h>
+#endif /* __sun__ */
+
+#include "common.h"
+#include "eloop.h"
+#include "driver.h"
+
+#ifdef _MSC_VER
+#pragma pack(push, 1)
+#endif /* _MSC_VER */
+
+struct ieee8023_hdr {
+ u8 dest[6];
+ u8 src[6];
+ u16 ethertype;
+} STRUCT_PACKED;
+
+#ifdef _MSC_VER
+#pragma pack(pop)
+#endif /* _MSC_VER */
+
+static const u8 pae_group_addr[ETH_ALEN] =
+{ 0x01, 0x80, 0xc2, 0x00, 0x00, 0x03 };
+
+
+struct wpa_driver_wired_data {
+ char ifname[IFNAMSIZ + 1];
+ void *ctx;
+
+ int sock; /* raw packet socket for driver access */
+ int dhcp_sock; /* socket for dhcp packets */
+ int use_pae_group_addr;
+
+ int pf_sock;
+ int membership, multi, iff_allmulti, iff_up;
+};
+
+
+/* TODO: detecting new devices should eventually be changed from using DHCP
+ * snooping to trigger on any packet from a new layer 2 MAC address, e.g.,
+ * based on ebtables, etc. */
+
+struct dhcp_message {
+ u_int8_t op;
+ u_int8_t htype;
+ u_int8_t hlen;
+ u_int8_t hops;
+ u_int32_t xid;
+ u_int16_t secs;
+ u_int16_t flags;
+ u_int32_t ciaddr;
+ u_int32_t yiaddr;
+ u_int32_t siaddr;
+ u_int32_t giaddr;
+ u_int8_t chaddr[16];
+ u_int8_t sname[64];
+ u_int8_t file[128];
+ u_int32_t cookie;
+ u_int8_t options[308]; /* 312 - cookie */
+};
+
+
+static int wired_multicast_membership(int sock, int ifindex,
+ const u8 *addr, int add)
+{
+#ifdef __linux__
+ struct packet_mreq mreq;
+
+ if (sock < 0)
+ return -1;
+
+ os_memset(&mreq, 0, sizeof(mreq));
+ mreq.mr_ifindex = ifindex;
+ mreq.mr_type = PACKET_MR_MULTICAST;
+ mreq.mr_alen = ETH_ALEN;
+ os_memcpy(mreq.mr_address, addr, ETH_ALEN);
+
+ if (setsockopt(sock, SOL_PACKET,
+ add ? PACKET_ADD_MEMBERSHIP : PACKET_DROP_MEMBERSHIP,
+ &mreq, sizeof(mreq)) < 0) {
+ wpa_printf(MSG_ERROR, "setsockopt: %s", strerror(errno));
+ return -1;
+ }
+ return 0;
+#else /* __linux__ */
+ return -1;
+#endif /* __linux__ */
+}
+
+
+#ifdef __linux__
+static void handle_data(void *ctx, unsigned char *buf, size_t len)
+{
+#ifdef HOSTAPD
+ struct ieee8023_hdr *hdr;
+ u8 *pos, *sa;
+ size_t left;
+ union wpa_event_data event;
+
+ /* must contain at least ieee8023_hdr 6 byte source, 6 byte dest,
+ * 2 byte ethertype */
+ if (len < 14) {
+ wpa_printf(MSG_MSGDUMP, "handle_data: too short (%lu)",
+ (unsigned long) len);
+ return;
+ }
+
+ hdr = (struct ieee8023_hdr *) buf;
+
+ switch (ntohs(hdr->ethertype)) {
+ case ETH_P_PAE:
+ wpa_printf(MSG_MSGDUMP, "Received EAPOL packet");
+ sa = hdr->src;
+ os_memset(&event, 0, sizeof(event));
+ event.new_sta.addr = sa;
+ wpa_supplicant_event(ctx, EVENT_NEW_STA, &event);
+
+ pos = (u8 *) (hdr + 1);
+ left = len - sizeof(*hdr);
+ drv_event_eapol_rx(ctx, sa, pos, left);
+ break;
+
+ default:
+ wpa_printf(MSG_DEBUG, "Unknown ethertype 0x%04x in data frame",
+ ntohs(hdr->ethertype));
+ break;
+ }
+#endif /* HOSTAPD */
+}
+
+
+static void handle_read(int sock, void *eloop_ctx, void *sock_ctx)
+{
+ int len;
+ unsigned char buf[3000];
+
+ len = recv(sock, buf, sizeof(buf), 0);
+ if (len < 0) {
+ wpa_printf(MSG_ERROR, "recv: %s", strerror(errno));
+ return;
+ }
+
+ handle_data(eloop_ctx, buf, len);
+}
+
+
+static void handle_dhcp(int sock, void *eloop_ctx, void *sock_ctx)
+{
+ int len;
+ unsigned char buf[3000];
+ struct dhcp_message *msg;
+ u8 *mac_address;
+ union wpa_event_data event;
+
+ len = recv(sock, buf, sizeof(buf), 0);
+ if (len < 0) {
+ wpa_printf(MSG_ERROR, "recv: %s", strerror(errno));
+ return;
+ }
+
+ /* must contain at least dhcp_message->chaddr */
+ if (len < 44) {
+ wpa_printf(MSG_MSGDUMP, "handle_dhcp: too short (%d)", len);
+ return;
+ }
+
+ msg = (struct dhcp_message *) buf;
+ mac_address = (u8 *) &(msg->chaddr);
+
+ wpa_printf(MSG_MSGDUMP, "Got DHCP broadcast packet from " MACSTR,
+ MAC2STR(mac_address));
+
+ os_memset(&event, 0, sizeof(event));
+ event.new_sta.addr = mac_address;
+ wpa_supplicant_event(eloop_ctx, EVENT_NEW_STA, &event);
+}
+#endif /* __linux__ */
+
+
+static int wired_init_sockets(struct wpa_driver_wired_data *drv, u8 *own_addr)
+{
+#ifdef __linux__
+ struct ifreq ifr;
+ struct sockaddr_ll addr;
+ struct sockaddr_in addr2;
+ int n = 1;
+
+ drv->sock = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_PAE));
+ if (drv->sock < 0) {
+ wpa_printf(MSG_ERROR, "socket[PF_PACKET,SOCK_RAW]: %s",
+ strerror(errno));
+ return -1;
+ }
+
+ if (eloop_register_read_sock(drv->sock, handle_read, drv->ctx, NULL)) {
+ wpa_printf(MSG_INFO, "Could not register read socket");
+ return -1;
+ }
+
+ os_memset(&ifr, 0, sizeof(ifr));
+ os_strlcpy(ifr.ifr_name, drv->ifname, sizeof(ifr.ifr_name));
+ if (ioctl(drv->sock, SIOCGIFINDEX, &ifr) != 0) {
+ wpa_printf(MSG_ERROR, "ioctl(SIOCGIFINDEX): %s",
+ strerror(errno));
+ return -1;
+ }
+
+ os_memset(&addr, 0, sizeof(addr));
+ addr.sll_family = AF_PACKET;
+ addr.sll_ifindex = ifr.ifr_ifindex;
+ wpa_printf(MSG_DEBUG, "Opening raw packet socket for ifindex %d",
+ addr.sll_ifindex);
+
+ if (bind(drv->sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+ wpa_printf(MSG_ERROR, "bind: %s", strerror(errno));
+ return -1;
+ }
+
+ /* filter multicast address */
+ if (wired_multicast_membership(drv->sock, ifr.ifr_ifindex,
+ pae_group_addr, 1) < 0) {
+ wpa_printf(MSG_ERROR, "wired: Failed to add multicast group "
+ "membership");
+ return -1;
+ }
+
+ os_memset(&ifr, 0, sizeof(ifr));
+ os_strlcpy(ifr.ifr_name, drv->ifname, sizeof(ifr.ifr_name));
+ if (ioctl(drv->sock, SIOCGIFHWADDR, &ifr) != 0) {
+ wpa_printf(MSG_ERROR, "ioctl(SIOCGIFHWADDR): %s",
+ strerror(errno));
+ return -1;
+ }
+
+ if (ifr.ifr_hwaddr.sa_family != ARPHRD_ETHER) {
+ wpa_printf(MSG_INFO, "Invalid HW-addr family 0x%04x",
+ ifr.ifr_hwaddr.sa_family);
+ return -1;
+ }
+ os_memcpy(own_addr, ifr.ifr_hwaddr.sa_data, ETH_ALEN);
+
+ /* setup dhcp listen socket for sta detection */
+ if ((drv->dhcp_sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) {
+ wpa_printf(MSG_ERROR, "socket call failed for dhcp: %s",
+ strerror(errno));
+ return -1;
+ }
+
+ if (eloop_register_read_sock(drv->dhcp_sock, handle_dhcp, drv->ctx,
+ NULL)) {
+ wpa_printf(MSG_INFO, "Could not register read socket");
+ return -1;
+ }
+
+ os_memset(&addr2, 0, sizeof(addr2));
+ addr2.sin_family = AF_INET;
+ addr2.sin_port = htons(67);
+ addr2.sin_addr.s_addr = INADDR_ANY;
+
+ if (setsockopt(drv->dhcp_sock, SOL_SOCKET, SO_REUSEADDR, (char *) &n,
+ sizeof(n)) == -1) {
+ wpa_printf(MSG_ERROR, "setsockopt[SOL_SOCKET,SO_REUSEADDR]: %s",
+ strerror(errno));
+ return -1;
+ }
+ if (setsockopt(drv->dhcp_sock, SOL_SOCKET, SO_BROADCAST, (char *) &n,
+ sizeof(n)) == -1) {
+ wpa_printf(MSG_ERROR, "setsockopt[SOL_SOCKET,SO_BROADCAST]: %s",
+ strerror(errno));
+ return -1;
+ }
+
+ os_memset(&ifr, 0, sizeof(ifr));
+ os_strlcpy(ifr.ifr_ifrn.ifrn_name, drv->ifname, IFNAMSIZ);
+ if (setsockopt(drv->dhcp_sock, SOL_SOCKET, SO_BINDTODEVICE,
+ (char *) &ifr, sizeof(ifr)) < 0) {
+ wpa_printf(MSG_ERROR,
+ "setsockopt[SOL_SOCKET,SO_BINDTODEVICE]: %s",
+ strerror(errno));
+ return -1;
+ }
+
+ if (bind(drv->dhcp_sock, (struct sockaddr *) &addr2,
+ sizeof(struct sockaddr)) == -1) {
+ wpa_printf(MSG_ERROR, "bind: %s", strerror(errno));
+ return -1;
+ }
+
+ return 0;
+#else /* __linux__ */
+ return -1;
+#endif /* __linux__ */
+}
+
+
+static int wired_send_eapol(void *priv, const u8 *addr,
+ const u8 *data, size_t data_len, int encrypt,
+ const u8 *own_addr, u32 flags)
+{
+ struct wpa_driver_wired_data *drv = priv;
+ struct ieee8023_hdr *hdr;
+ size_t len;
+ u8 *pos;
+ int res;
+
+ len = sizeof(*hdr) + data_len;
+ hdr = os_zalloc(len);
+ if (hdr == NULL) {
+ wpa_printf(MSG_INFO,
+ "malloc() failed for wired_send_eapol(len=%lu)",
+ (unsigned long) len);
+ return -1;
+ }
+
+ os_memcpy(hdr->dest, drv->use_pae_group_addr ? pae_group_addr : addr,
+ ETH_ALEN);
+ os_memcpy(hdr->src, own_addr, ETH_ALEN);
+ hdr->ethertype = htons(ETH_P_PAE);
+
+ pos = (u8 *) (hdr + 1);
+ os_memcpy(pos, data, data_len);
+
+ res = send(drv->sock, (u8 *) hdr, len, 0);
+ os_free(hdr);
+
+ if (res < 0) {
+ wpa_printf(MSG_ERROR,
+ "wired_send_eapol - packet len: %lu - failed: send: %s",
+ (unsigned long) len, strerror(errno));
+ }
+
+ return res;
+}
+
+
+static void * wired_driver_hapd_init(struct hostapd_data *hapd,
+ struct wpa_init_params *params)
+{
+ struct wpa_driver_wired_data *drv;
+
+ drv = os_zalloc(sizeof(struct wpa_driver_wired_data));
+ if (drv == NULL) {
+ wpa_printf(MSG_INFO,
+ "Could not allocate memory for wired driver data");
+ return NULL;
+ }
+
+ drv->ctx = hapd;
+ os_strlcpy(drv->ifname, params->ifname, sizeof(drv->ifname));
+ drv->use_pae_group_addr = params->use_pae_group_addr;
+
+ if (wired_init_sockets(drv, params->own_addr)) {
+ os_free(drv);
+ return NULL;
+ }
+
+ return drv;
+}
+
+
+static void wired_driver_hapd_deinit(void *priv)
+{
+ struct wpa_driver_wired_data *drv = priv;
+
+ if (drv->sock >= 0) {
+ eloop_unregister_read_sock(drv->sock);
+ close(drv->sock);
+ }
+
+ if (drv->dhcp_sock >= 0) {
+ eloop_unregister_read_sock(drv->dhcp_sock);
+ close(drv->dhcp_sock);
+ }
+
+ os_free(drv);
+}
+
+
+static int wpa_driver_wired_get_ssid(void *priv, u8 *ssid)
+{
+ ssid[0] = 0;
+ return 0;
+}
+
+
+static int wpa_driver_wired_get_bssid(void *priv, u8 *bssid)
+{
+ /* Report PAE group address as the "BSSID" for wired connection. */
+ os_memcpy(bssid, pae_group_addr, ETH_ALEN);
+ return 0;
+}
+
+
+static int wpa_driver_wired_get_capa(void *priv, struct wpa_driver_capa *capa)
+{
+ os_memset(capa, 0, sizeof(*capa));
+ capa->flags = WPA_DRIVER_FLAGS_WIRED;
+ return 0;
+}
+
+
+static int wpa_driver_wired_get_ifflags(const char *ifname, int *flags)
+{
+ struct ifreq ifr;
+ int s;
+
+ s = socket(PF_INET, SOCK_DGRAM, 0);
+ if (s < 0) {
+ wpa_printf(MSG_ERROR, "socket: %s", strerror(errno));
+ return -1;
+ }
+
+ os_memset(&ifr, 0, sizeof(ifr));
+ os_strlcpy(ifr.ifr_name, ifname, IFNAMSIZ);
+ if (ioctl(s, SIOCGIFFLAGS, (caddr_t) &ifr) < 0) {
+ wpa_printf(MSG_ERROR, "ioctl[SIOCGIFFLAGS]: %s",
+ strerror(errno));
+ close(s);
+ return -1;
+ }
+ close(s);
+ *flags = ifr.ifr_flags & 0xffff;
+ return 0;
+}
+
+
+static int wpa_driver_wired_set_ifflags(const char *ifname, int flags)
+{
+ struct ifreq ifr;
+ int s;
+
+ s = socket(PF_INET, SOCK_DGRAM, 0);
+ if (s < 0) {
+ wpa_printf(MSG_ERROR, "socket: %s", strerror(errno));
+ return -1;
+ }
+
+ os_memset(&ifr, 0, sizeof(ifr));
+ os_strlcpy(ifr.ifr_name, ifname, IFNAMSIZ);
+ ifr.ifr_flags = flags & 0xffff;
+ if (ioctl(s, SIOCSIFFLAGS, (caddr_t) &ifr) < 0) {
+ wpa_printf(MSG_ERROR, "ioctl[SIOCSIFFLAGS]: %s",
+ strerror(errno));
+ close(s);
+ return -1;
+ }
+ close(s);
+ return 0;
+}
+
+#if defined(__FreeBSD__) || defined(__DragonFly__) || defined(__FreeBSD_kernel__)
+static int wpa_driver_wired_get_ifstatus(const char *ifname, int *status)
+{
+ struct ifmediareq ifmr;
+ int s;
+
+ s = socket(PF_INET, SOCK_DGRAM, 0);
+ if (s < 0) {
+ wpa_printf(MSG_ERROR, "socket: %s", strerror(errno));
+ return -1;
+ }
+
+ os_memset(&ifmr, 0, sizeof(ifmr));
+ os_strlcpy(ifmr.ifm_name, ifname, IFNAMSIZ);
+ if (ioctl(s, SIOCGIFMEDIA, (caddr_t) &ifmr) < 0) {
+ wpa_printf(MSG_ERROR, "ioctl[SIOCGIFMEDIA]: %s",
+ strerror(errno));
+ close(s);
+ return -1;
+ }
+ close(s);
+ *status = (ifmr.ifm_status & (IFM_ACTIVE | IFM_AVALID)) ==
+ (IFM_ACTIVE | IFM_AVALID);
+
+ return 0;
+}
+#endif /* defined(__FreeBSD__) || defined(__DragonFly__) || defined(FreeBSD_kernel__) */
+
+
+static int wpa_driver_wired_multi(const char *ifname, const u8 *addr, int add)
+{
+ struct ifreq ifr;
+ int s;
+
+#ifdef __sun__
+ return -1;
+#endif /* __sun__ */
+
+ s = socket(PF_INET, SOCK_DGRAM, 0);
+ if (s < 0) {
+ wpa_printf(MSG_ERROR, "socket: %s", strerror(errno));
+ return -1;
+ }
+
+ os_memset(&ifr, 0, sizeof(ifr));
+ os_strlcpy(ifr.ifr_name, ifname, IFNAMSIZ);
+#ifdef __linux__
+ ifr.ifr_hwaddr.sa_family = AF_UNSPEC;
+ os_memcpy(ifr.ifr_hwaddr.sa_data, addr, ETH_ALEN);
+#endif /* __linux__ */
+#if defined(__FreeBSD__) || defined(__DragonFly__) || defined(__FreeBSD_kernel__)
+ {
+ struct sockaddr_dl *dlp;
+ dlp = (struct sockaddr_dl *) &ifr.ifr_addr;
+ dlp->sdl_len = sizeof(struct sockaddr_dl);
+ dlp->sdl_family = AF_LINK;
+ dlp->sdl_index = 0;
+ dlp->sdl_nlen = 0;
+ dlp->sdl_alen = ETH_ALEN;
+ dlp->sdl_slen = 0;
+ os_memcpy(LLADDR(dlp), addr, ETH_ALEN);
+ }
+#endif /* defined(__FreeBSD__) || defined(__DragonFly__) || defined(FreeBSD_kernel__) */
+#if defined(__NetBSD__) || defined(__OpenBSD__) || defined(__APPLE__)
+ {
+ struct sockaddr *sap;
+ sap = (struct sockaddr *) &ifr.ifr_addr;
+ sap->sa_len = sizeof(struct sockaddr);
+ sap->sa_family = AF_UNSPEC;
+ os_memcpy(sap->sa_data, addr, ETH_ALEN);
+ }
+#endif /* defined(__NetBSD__) || defined(__OpenBSD__) || defined(__APPLE__) */
+
+ if (ioctl(s, add ? SIOCADDMULTI : SIOCDELMULTI, (caddr_t) &ifr) < 0) {
+ wpa_printf(MSG_ERROR, "ioctl[SIOC{ADD/DEL}MULTI]: %s",
+ strerror(errno));
+ close(s);
+ return -1;
+ }
+ close(s);
+ return 0;
+}
+
+
+static void * wpa_driver_wired_init(void *ctx, const char *ifname)
+{
+ struct wpa_driver_wired_data *drv;
+ int flags, status;
+
+ drv = os_zalloc(sizeof(*drv));
+ if (drv == NULL)
+ return NULL;
+ os_strlcpy(drv->ifname, ifname, sizeof(drv->ifname));
+ drv->ctx = ctx;
+
+#ifdef __linux__
+ drv->pf_sock = socket(PF_PACKET, SOCK_DGRAM, 0);
+ if (drv->pf_sock < 0)
+ wpa_printf(MSG_ERROR, "socket(PF_PACKET): %s", strerror(errno));
+#else /* __linux__ */
+ drv->pf_sock = -1;
+#endif /* __linux__ */
+
+ if (wpa_driver_wired_get_ifflags(ifname, &flags) == 0 &&
+ !(flags & IFF_UP) &&
+ wpa_driver_wired_set_ifflags(ifname, flags | IFF_UP) == 0) {
+ drv->iff_up = 1;
+ }
+
+ if (wired_multicast_membership(drv->pf_sock,
+ if_nametoindex(drv->ifname),
+ pae_group_addr, 1) == 0) {
+ wpa_printf(MSG_DEBUG, "%s: Added multicast membership with "
+ "packet socket", __func__);
+ drv->membership = 1;
+ } else if (wpa_driver_wired_multi(ifname, pae_group_addr, 1) == 0) {
+ wpa_printf(MSG_DEBUG, "%s: Added multicast membership with "
+ "SIOCADDMULTI", __func__);
+ drv->multi = 1;
+ } else if (wpa_driver_wired_get_ifflags(ifname, &flags) < 0) {
+ wpa_printf(MSG_INFO, "%s: Could not get interface "
+ "flags", __func__);
+ os_free(drv);
+ return NULL;
+ } else if (flags & IFF_ALLMULTI) {
+ wpa_printf(MSG_DEBUG, "%s: Interface is already configured "
+ "for multicast", __func__);
+ } else if (wpa_driver_wired_set_ifflags(ifname,
+ flags | IFF_ALLMULTI) < 0) {
+ wpa_printf(MSG_INFO, "%s: Failed to enable allmulti",
+ __func__);
+ os_free(drv);
+ return NULL;
+ } else {
+ wpa_printf(MSG_DEBUG, "%s: Enabled allmulti mode",
+ __func__);
+ drv->iff_allmulti = 1;
+ }
+#if defined(__FreeBSD__) || defined(__DragonFly__) || defined(__FreeBSD_kernel__)
+ {
+ int status;
+ wpa_printf(MSG_DEBUG, "%s: waiting for link to become active",
+ __func__);
+ while (wpa_driver_wired_get_ifstatus(ifname, &status) == 0 &&
+ status == 0)
+ sleep(1);
+ }
+#endif /* defined(__FreeBSD__) || defined(__DragonFly__) || defined(FreeBSD_kernel__) */
+
+ return drv;
+}
+
+
+static void wpa_driver_wired_deinit(void *priv)
+{
+ struct wpa_driver_wired_data *drv = priv;
+ int flags;
+
+ if (drv->membership &&
+ wired_multicast_membership(drv->pf_sock,
+ if_nametoindex(drv->ifname),
+ pae_group_addr, 0) < 0) {
+ wpa_printf(MSG_DEBUG, "%s: Failed to remove PAE multicast "
+ "group (PACKET)", __func__);
+ }
+
+ if (drv->multi &&
+ wpa_driver_wired_multi(drv->ifname, pae_group_addr, 0) < 0) {
+ wpa_printf(MSG_DEBUG, "%s: Failed to remove PAE multicast "
+ "group (SIOCDELMULTI)", __func__);
+ }
+
+ if (drv->iff_allmulti &&
+ (wpa_driver_wired_get_ifflags(drv->ifname, &flags) < 0 ||
+ wpa_driver_wired_set_ifflags(drv->ifname,
+ flags & ~IFF_ALLMULTI) < 0)) {
+ wpa_printf(MSG_DEBUG, "%s: Failed to disable allmulti mode",
+ __func__);
+ }
+
+ if (drv->iff_up &&
+ wpa_driver_wired_get_ifflags(drv->ifname, &flags) == 0 &&
+ (flags & IFF_UP) &&
+ wpa_driver_wired_set_ifflags(drv->ifname, flags & ~IFF_UP) < 0) {
+ wpa_printf(MSG_DEBUG, "%s: Failed to set the interface down",
+ __func__);
+ }
+
+ if (drv->pf_sock != -1)
+ close(drv->pf_sock);
+
+ os_free(drv);
+}
+
+
+const struct wpa_driver_ops wpa_driver_wired_ops = {
+ .name = "wired",
+ .desc = "Wired Ethernet driver",
+ .hapd_init = wired_driver_hapd_init,
+ .hapd_deinit = wired_driver_hapd_deinit,
+ .hapd_send_eapol = wired_send_eapol,
+ .get_ssid = wpa_driver_wired_get_ssid,
+ .get_bssid = wpa_driver_wired_get_bssid,
+ .get_capa = wpa_driver_wired_get_capa,
+ .init = wpa_driver_wired_init,
+ .deinit = wpa_driver_wired_deinit,
+};
diff --git a/freebsd/contrib/wpa/src/drivers/drivers.c b/freebsd/contrib/wpa/src/drivers/drivers.c
new file mode 100644
index 00000000..5a0b58c2
--- /dev/null
+++ b/freebsd/contrib/wpa/src/drivers/drivers.c
@@ -0,0 +1,88 @@
+#include <machine/rtems-bsd-user-space.h>
+
+/*
+ * Driver interface list
+ * Copyright (c) 2004-2005, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+#include "utils/common.h"
+#include "driver.h"
+
+#ifdef CONFIG_DRIVER_WEXT
+extern struct wpa_driver_ops wpa_driver_wext_ops; /* driver_wext.c */
+#endif /* CONFIG_DRIVER_WEXT */
+#ifdef CONFIG_DRIVER_NL80211
+extern struct wpa_driver_ops wpa_driver_nl80211_ops; /* driver_nl80211.c */
+#endif /* CONFIG_DRIVER_NL80211 */
+#ifdef CONFIG_DRIVER_HOSTAP
+extern struct wpa_driver_ops wpa_driver_hostap_ops; /* driver_hostap.c */
+#endif /* CONFIG_DRIVER_HOSTAP */
+#ifdef CONFIG_DRIVER_BSD
+extern struct wpa_driver_ops wpa_driver_bsd_ops; /* driver_bsd.c */
+#endif /* CONFIG_DRIVER_BSD */
+#ifdef CONFIG_DRIVER_OPENBSD
+extern struct wpa_driver_ops wpa_driver_openbsd_ops; /* driver_openbsd.c */
+#endif /* CONFIG_DRIVER_OPENBSD */
+#ifdef CONFIG_DRIVER_NDIS
+extern struct wpa_driver_ops wpa_driver_ndis_ops; /* driver_ndis.c */
+#endif /* CONFIG_DRIVER_NDIS */
+#ifdef CONFIG_DRIVER_WIRED
+extern struct wpa_driver_ops wpa_driver_wired_ops; /* driver_wired.c */
+#endif /* CONFIG_DRIVER_WIRED */
+#ifdef CONFIG_DRIVER_MACSEC_QCA
+ /* driver_macsec_qca.c */
+extern struct wpa_driver_ops wpa_driver_macsec_qca_ops;
+#endif /* CONFIG_DRIVER_MACSEC_QCA */
+#ifdef CONFIG_DRIVER_ROBOSWITCH
+/* driver_roboswitch.c */
+extern struct wpa_driver_ops wpa_driver_roboswitch_ops;
+#endif /* CONFIG_DRIVER_ROBOSWITCH */
+#ifdef CONFIG_DRIVER_ATHEROS
+extern struct wpa_driver_ops wpa_driver_atheros_ops; /* driver_atheros.c */
+#endif /* CONFIG_DRIVER_ATHEROS */
+#ifdef CONFIG_DRIVER_NONE
+extern struct wpa_driver_ops wpa_driver_none_ops; /* driver_none.c */
+#endif /* CONFIG_DRIVER_NONE */
+
+
+const struct wpa_driver_ops *const wpa_drivers[] =
+{
+#ifdef CONFIG_DRIVER_NL80211
+ &wpa_driver_nl80211_ops,
+#endif /* CONFIG_DRIVER_NL80211 */
+#ifdef CONFIG_DRIVER_WEXT
+ &wpa_driver_wext_ops,
+#endif /* CONFIG_DRIVER_WEXT */
+#ifdef CONFIG_DRIVER_HOSTAP
+ &wpa_driver_hostap_ops,
+#endif /* CONFIG_DRIVER_HOSTAP */
+#ifdef CONFIG_DRIVER_BSD
+ &wpa_driver_bsd_ops,
+#endif /* CONFIG_DRIVER_BSD */
+#ifdef CONFIG_DRIVER_OPENBSD
+ &wpa_driver_openbsd_ops,
+#endif /* CONFIG_DRIVER_OPENBSD */
+#ifdef CONFIG_DRIVER_NDIS
+ &wpa_driver_ndis_ops,
+#endif /* CONFIG_DRIVER_NDIS */
+#ifdef CONFIG_DRIVER_WIRED
+ &wpa_driver_wired_ops,
+#endif /* CONFIG_DRIVER_WIRED */
+#ifdef CONFIG_DRIVER_MACSEC_QCA
+ &wpa_driver_macsec_qca_ops,
+#endif /* CONFIG_DRIVER_MACSEC_QCA */
+#ifdef CONFIG_DRIVER_ROBOSWITCH
+ &wpa_driver_roboswitch_ops,
+#endif /* CONFIG_DRIVER_ROBOSWITCH */
+#ifdef CONFIG_DRIVER_ATHEROS
+ &wpa_driver_atheros_ops,
+#endif /* CONFIG_DRIVER_ATHEROS */
+#ifdef CONFIG_DRIVER_NONE
+ &wpa_driver_none_ops,
+#endif /* CONFIG_DRIVER_NONE */
+ NULL
+};
diff --git a/freebsd/contrib/wpa/src/drivers/linux_defines.h b/freebsd/contrib/wpa/src/drivers/linux_defines.h
new file mode 100644
index 00000000..a107479a
--- /dev/null
+++ b/freebsd/contrib/wpa/src/drivers/linux_defines.h
@@ -0,0 +1,46 @@
+/*
+ * Linux defines for values that are not yet included in common C libraries
+ * Copyright (c) 2014, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef LINUX_DEFINES_H
+#define LINUX_DEFINES_H
+
+#ifndef SO_WIFI_STATUS
+# if defined(__sparc__)
+# define SO_WIFI_STATUS 0x0025
+# elif defined(__parisc__)
+# define SO_WIFI_STATUS 0x4022
+# else
+# define SO_WIFI_STATUS 41
+# endif
+
+# define SCM_WIFI_STATUS SO_WIFI_STATUS
+#endif
+
+#ifndef SO_EE_ORIGIN_TXSTATUS
+#define SO_EE_ORIGIN_TXSTATUS 4
+#endif
+
+#ifndef PACKET_TX_TIMESTAMP
+#define PACKET_TX_TIMESTAMP 16
+#endif
+
+#ifndef IFF_LOWER_UP
+#define IFF_LOWER_UP 0x10000 /* driver signals L1 up */
+#endif
+#ifndef IFF_DORMANT
+#define IFF_DORMANT 0x20000 /* driver signals dormant */
+#endif
+
+#ifndef IF_OPER_DORMANT
+#define IF_OPER_DORMANT 5
+#endif
+#ifndef IF_OPER_UP
+#define IF_OPER_UP 6
+#endif
+
+#endif /* LINUX_DEFINES_H */
diff --git a/freebsd/contrib/wpa/src/eap_common/chap.c b/freebsd/contrib/wpa/src/eap_common/chap.c
new file mode 100644
index 00000000..012000f0
--- /dev/null
+++ b/freebsd/contrib/wpa/src/eap_common/chap.c
@@ -0,0 +1,30 @@
+#include <machine/rtems-bsd-user-space.h>
+
+/*
+ * CHAP-MD5 (RFC 1994)
+ * 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 "includes.h"
+
+#include "common.h"
+#include "crypto/crypto.h"
+#include "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);
+}
diff --git a/freebsd/contrib/wpa/src/eap_common/chap.h b/freebsd/contrib/wpa/src/eap_common/chap.h
new file mode 100644
index 00000000..a791505f
--- /dev/null
+++ b/freebsd/contrib/wpa/src/eap_common/chap.h
@@ -0,0 +1,17 @@
+/*
+ * CHAP-MD5 (RFC 1994)
+ * 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.
+ */
+
+#ifndef CHAP_H
+#define CHAP_H
+
+#define CHAP_MD5_LEN 16
+
+int chap_md5(u8 id, const u8 *secret, size_t secret_len, const u8 *challenge,
+ size_t challenge_len, u8 *response);
+
+#endif /* CHAP_H */
diff --git a/freebsd/contrib/wpa/src/eap_common/eap_common.c b/freebsd/contrib/wpa/src/eap_common/eap_common.c
new file mode 100644
index 00000000..fc1909a3
--- /dev/null
+++ b/freebsd/contrib/wpa/src/eap_common/eap_common.c
@@ -0,0 +1,290 @@
+#include <machine/rtems-bsd-user-space.h>
+
+/*
+ * EAP common peer/server definitions
+ * Copyright (c) 2004-2014, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "eap_defs.h"
+#include "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_type - 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)];
+}
+
+
+#ifdef CONFIG_ERP
+int erp_parse_tlvs(const u8 *pos, const u8 *end, struct erp_tlvs *tlvs,
+ int stop_at_keyname)
+{
+ os_memset(tlvs, 0, sizeof(*tlvs));
+
+ while (pos < end) {
+ u8 tlv_type, tlv_len;
+
+ tlv_type = *pos++;
+ switch (tlv_type) {
+ case EAP_ERP_TV_RRK_LIFETIME:
+ case EAP_ERP_TV_RMSK_LIFETIME:
+ /* 4-octet TV */
+ if (pos + 4 > end) {
+ wpa_printf(MSG_DEBUG, "EAP: Too short TV");
+ return -1;
+ }
+ pos += 4;
+ break;
+ case EAP_ERP_TLV_DOMAIN_NAME:
+ case EAP_ERP_TLV_KEYNAME_NAI:
+ case EAP_ERP_TLV_CRYPTOSUITES:
+ case EAP_ERP_TLV_AUTHORIZATION_INDICATION:
+ case EAP_ERP_TLV_CALLED_STATION_ID:
+ case EAP_ERP_TLV_CALLING_STATION_ID:
+ case EAP_ERP_TLV_NAS_IDENTIFIER:
+ case EAP_ERP_TLV_NAS_IP_ADDRESS:
+ case EAP_ERP_TLV_NAS_IPV6_ADDRESS:
+ if (pos >= end) {
+ wpa_printf(MSG_DEBUG, "EAP: Too short TLV");
+ return -1;
+ }
+ tlv_len = *pos++;
+ if (tlv_len > (unsigned) (end - pos)) {
+ wpa_printf(MSG_DEBUG, "EAP: Truncated TLV");
+ return -1;
+ }
+ if (tlv_type == EAP_ERP_TLV_KEYNAME_NAI) {
+ if (tlvs->keyname) {
+ wpa_printf(MSG_DEBUG,
+ "EAP: More than one keyName-NAI");
+ return -1;
+ }
+ tlvs->keyname = pos;
+ tlvs->keyname_len = tlv_len;
+ if (stop_at_keyname)
+ return 0;
+ } else if (tlv_type == EAP_ERP_TLV_DOMAIN_NAME) {
+ tlvs->domain = pos;
+ tlvs->domain_len = tlv_len;
+ }
+ pos += tlv_len;
+ break;
+ default:
+ if (tlv_type >= 128 && tlv_type <= 191) {
+ /* Undefined TLV */
+ if (pos >= end) {
+ wpa_printf(MSG_DEBUG,
+ "EAP: Too short TLV");
+ return -1;
+ }
+ tlv_len = *pos++;
+ if (tlv_len > (unsigned) (end - pos)) {
+ wpa_printf(MSG_DEBUG,
+ "EAP: Truncated TLV");
+ return -1;
+ }
+ pos += tlv_len;
+ break;
+ }
+ wpa_printf(MSG_DEBUG, "EAP: Unknown TV/TLV type %u",
+ tlv_type);
+ pos = end;
+ break;
+ }
+ }
+
+ return 0;
+}
+#endif /* CONFIG_ERP */
diff --git a/freebsd/contrib/wpa/src/eap_common/eap_common.h b/freebsd/contrib/wpa/src/eap_common/eap_common.h
new file mode 100644
index 00000000..e62f1676
--- /dev/null
+++ b/freebsd/contrib/wpa/src/eap_common/eap_common.h
@@ -0,0 +1,33 @@
+/*
+ * EAP common peer/server definitions
+ * Copyright (c) 2004-2014, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef EAP_COMMON_H
+#define EAP_COMMON_H
+
+#include "wpabuf.h"
+
+struct erp_tlvs {
+ const u8 *keyname;
+ const u8 *domain;
+
+ u8 keyname_len;
+ u8 domain_len;
+};
+
+int eap_hdr_len_valid(const struct wpabuf *msg, size_t min_payload);
+const u8 * eap_hdr_validate(int vendor, EapType eap_type,
+ const struct wpabuf *msg, size_t *plen);
+struct wpabuf * eap_msg_alloc(int vendor, EapType type, size_t payload_len,
+ u8 code, u8 identifier);
+void eap_update_len(struct wpabuf *msg);
+u8 eap_get_id(const struct wpabuf *msg);
+EapType eap_get_type(const struct wpabuf *msg);
+int erp_parse_tlvs(const u8 *pos, const u8 *end, struct erp_tlvs *tlvs,
+ int stop_at_keyname);
+
+#endif /* EAP_COMMON_H */
diff --git a/freebsd/contrib/wpa/src/eap_common/eap_defs.h b/freebsd/contrib/wpa/src/eap_common/eap_defs.h
new file mode 100644
index 00000000..54f26ca3
--- /dev/null
+++ b/freebsd/contrib/wpa/src/eap_common/eap_defs.h
@@ -0,0 +1,118 @@
+/*
+ * EAP server/peer: Shared EAP definitions
+ * Copyright (c) 2004-2014, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef EAP_DEFS_H
+#define EAP_DEFS_H
+
+/* RFC 3748 - Extensible Authentication Protocol (EAP) */
+
+#ifdef _MSC_VER
+#pragma pack(push, 1)
+#endif /* _MSC_VER */
+
+struct eap_hdr {
+ u8 code;
+ u8 identifier;
+ be16 length; /* including code and identifier; network byte order */
+ /* followed by length-4 octets of data */
+} STRUCT_PACKED;
+
+#ifdef _MSC_VER
+#pragma pack(pop)
+#endif /* _MSC_VER */
+
+enum { EAP_CODE_REQUEST = 1, EAP_CODE_RESPONSE = 2, EAP_CODE_SUCCESS = 3,
+ EAP_CODE_FAILURE = 4, EAP_CODE_INITIATE = 5, EAP_CODE_FINISH = 6 };
+
+/* EAP Request and Response data begins with one octet Type. Success and
+ * Failure do not have additional data. */
+
+/* Type field in EAP-Initiate and EAP-Finish messages */
+enum eap_erp_type {
+ EAP_ERP_TYPE_REAUTH_START = 1,
+ EAP_ERP_TYPE_REAUTH = 2,
+};
+
+/* ERP TV/TLV types */
+enum eap_erp_tlv_type {
+ EAP_ERP_TLV_KEYNAME_NAI = 1,
+ EAP_ERP_TV_RRK_LIFETIME = 2,
+ EAP_ERP_TV_RMSK_LIFETIME = 3,
+ EAP_ERP_TLV_DOMAIN_NAME = 4,
+ EAP_ERP_TLV_CRYPTOSUITES = 5,
+ EAP_ERP_TLV_AUTHORIZATION_INDICATION = 6,
+ EAP_ERP_TLV_CALLED_STATION_ID = 128,
+ EAP_ERP_TLV_CALLING_STATION_ID = 129,
+ EAP_ERP_TLV_NAS_IDENTIFIER = 130,
+ EAP_ERP_TLV_NAS_IP_ADDRESS = 131,
+ EAP_ERP_TLV_NAS_IPV6_ADDRESS = 132,
+};
+
+/* ERP Cryptosuite */
+enum eap_erp_cryptosuite {
+ EAP_ERP_CS_HMAC_SHA256_64 = 1,
+ EAP_ERP_CS_HMAC_SHA256_128 = 2,
+ EAP_ERP_CS_HMAC_SHA256_256 = 3,
+};
+
+/*
+ * EAP Method Types as allocated by IANA:
+ * http://www.iana.org/assignments/eap-numbers
+ */
+typedef enum {
+ EAP_TYPE_NONE = 0,
+ EAP_TYPE_IDENTITY = 1 /* RFC 3748 */,
+ EAP_TYPE_NOTIFICATION = 2 /* RFC 3748 */,
+ EAP_TYPE_NAK = 3 /* Response only, RFC 3748 */,
+ EAP_TYPE_MD5 = 4, /* RFC 3748 */
+ EAP_TYPE_OTP = 5 /* RFC 3748 */,
+ EAP_TYPE_GTC = 6, /* RFC 3748 */
+ EAP_TYPE_TLS = 13 /* RFC 2716 */,
+ EAP_TYPE_LEAP = 17 /* Cisco proprietary */,
+ EAP_TYPE_SIM = 18 /* RFC 4186 */,
+ EAP_TYPE_TTLS = 21 /* RFC 5281 */,
+ EAP_TYPE_AKA = 23 /* RFC 4187 */,
+ EAP_TYPE_PEAP = 25 /* draft-josefsson-pppext-eap-tls-eap-06.txt */,
+ EAP_TYPE_MSCHAPV2 = 26 /* draft-kamath-pppext-eap-mschapv2-00.txt */,
+ EAP_TYPE_TLV = 33 /* draft-josefsson-pppext-eap-tls-eap-07.txt */,
+ EAP_TYPE_TNC = 38 /* TNC IF-T v1.0-r3; note: tentative assignment;
+ * type 38 has previously been allocated for
+ * EAP-HTTP Digest, (funk.com) */,
+ EAP_TYPE_FAST = 43 /* RFC 4851 */,
+ EAP_TYPE_PAX = 46 /* RFC 4746 */,
+ EAP_TYPE_PSK = 47 /* RFC 4764 */,
+ EAP_TYPE_SAKE = 48 /* RFC 4763 */,
+ EAP_TYPE_IKEV2 = 49 /* RFC 5106 */,
+ EAP_TYPE_AKA_PRIME = 50 /* RFC 5448 */,
+ EAP_TYPE_GPSK = 51 /* RFC 5433 */,
+ EAP_TYPE_PWD = 52 /* RFC 5931 */,
+ EAP_TYPE_EKE = 53 /* RFC 6124 */,
+ EAP_TYPE_EXPANDED = 254 /* RFC 3748 */
+} EapType;
+
+
+/* SMI Network Management Private Enterprise Code for vendor specific types */
+enum {
+ EAP_VENDOR_IETF = 0,
+ EAP_VENDOR_MICROSOFT = 0x000137 /* Microsoft */,
+ EAP_VENDOR_WFA = 0x00372A /* Wi-Fi Alliance (moved to WBA) */,
+ EAP_VENDOR_HOSTAP = 39068 /* hostapd/wpa_supplicant project */,
+ EAP_VENDOR_WFA_NEW = 40808 /* Wi-Fi Alliance */
+};
+
+#define EAP_VENDOR_UNAUTH_TLS EAP_VENDOR_HOSTAP
+#define EAP_VENDOR_TYPE_UNAUTH_TLS 1
+
+#define EAP_VENDOR_WFA_UNAUTH_TLS 13
+
+#define EAP_MSK_LEN 64
+#define EAP_EMSK_LEN 64
+#define EAP_EMSK_NAME_LEN 8
+#define ERP_MAX_KEY_LEN 64
+
+#endif /* EAP_DEFS_H */
diff --git a/freebsd/contrib/wpa/src/eap_common/eap_peap_common.c b/freebsd/contrib/wpa/src/eap_common/eap_peap_common.c
new file mode 100644
index 00000000..08474793
--- /dev/null
+++ b/freebsd/contrib/wpa/src/eap_common/eap_peap_common.c
@@ -0,0 +1,87 @@
+#include <machine/rtems-bsd-user-space.h>
+
+/*
+ * EAP-PEAP common routines
+ * Copyright (c) 2008-2011, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "crypto/sha1.h"
+#include "eap_peap_common.h"
+
+int peap_prfplus(int version, const u8 *key, size_t key_len,
+ const char *label, const u8 *seed, size_t seed_len,
+ u8 *buf, size_t buf_len)
+{
+ unsigned char counter = 0;
+ size_t pos, plen;
+ u8 hash[SHA1_MAC_LEN];
+ size_t label_len = os_strlen(label);
+ u8 extra[2];
+ const unsigned char *addr[5];
+ size_t len[5];
+
+ addr[0] = hash;
+ len[0] = 0;
+ addr[1] = (unsigned char *) label;
+ len[1] = label_len;
+ addr[2] = seed;
+ len[2] = seed_len;
+
+ if (version == 0) {
+ /*
+ * PRF+(K, S, LEN) = T1 | T2 | ... | Tn
+ * T1 = HMAC-SHA1(K, S | 0x01 | 0x00 | 0x00)
+ * T2 = HMAC-SHA1(K, T1 | S | 0x02 | 0x00 | 0x00)
+ * ...
+ * Tn = HMAC-SHA1(K, Tn-1 | S | n | 0x00 | 0x00)
+ */
+
+ extra[0] = 0;
+ extra[1] = 0;
+
+ addr[3] = &counter;
+ len[3] = 1;
+ addr[4] = extra;
+ len[4] = 2;
+ } else {
+ /*
+ * PRF (K,S,LEN) = T1 | T2 | T3 | T4 | ... where:
+ * T1 = HMAC-SHA1(K, S | LEN | 0x01)
+ * T2 = HMAC-SHA1 (K, T1 | S | LEN | 0x02)
+ * T3 = HMAC-SHA1 (K, T2 | S | LEN | 0x03)
+ * T4 = HMAC-SHA1 (K, T3 | S | LEN | 0x04)
+ * ...
+ */
+
+ extra[0] = buf_len & 0xff;
+
+ addr[3] = extra;
+ len[3] = 1;
+ addr[4] = &counter;
+ len[4] = 1;
+ }
+
+ pos = 0;
+ while (pos < buf_len) {
+ counter++;
+ plen = buf_len - pos;
+ if (hmac_sha1_vector(key, key_len, 5, addr, len, hash) < 0)
+ return -1;
+ if (plen >= SHA1_MAC_LEN) {
+ os_memcpy(&buf[pos], hash, SHA1_MAC_LEN);
+ pos += SHA1_MAC_LEN;
+ } else {
+ os_memcpy(&buf[pos], hash, plen);
+ break;
+ }
+ len[0] = SHA1_MAC_LEN;
+ }
+
+ return 0;
+}
diff --git a/freebsd/contrib/wpa/src/eap_common/eap_peap_common.h b/freebsd/contrib/wpa/src/eap_common/eap_peap_common.h
new file mode 100644
index 00000000..7aad0dff
--- /dev/null
+++ b/freebsd/contrib/wpa/src/eap_common/eap_peap_common.h
@@ -0,0 +1,16 @@
+/*
+ * EAP-PEAP common routines
+ * Copyright (c) 2008-2011, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef EAP_PEAP_COMMON_H
+#define EAP_PEAP_COMMON_H
+
+int peap_prfplus(int version, const u8 *key, size_t key_len,
+ const char *label, const u8 *seed, size_t seed_len,
+ u8 *buf, size_t buf_len);
+
+#endif /* EAP_PEAP_COMMON_H */
diff --git a/freebsd/contrib/wpa/src/eap_common/eap_psk_common.c b/freebsd/contrib/wpa/src/eap_common/eap_psk_common.c
new file mode 100644
index 00000000..eaea191b
--- /dev/null
+++ b/freebsd/contrib/wpa/src/eap_common/eap_psk_common.c
@@ -0,0 +1,70 @@
+#include <machine/rtems-bsd-user-space.h>
+
+/*
+ * EAP server/peer: EAP-PSK shared routines
+ * Copyright (c) 2004-2006, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "crypto/aes_wrap.h"
+#include "eap_defs.h"
+#include "eap_psk_common.h"
+
+#define aes_block_size 16
+
+
+int eap_psk_key_setup(const u8 *psk, u8 *ak, u8 *kdk)
+{
+ os_memset(ak, 0, aes_block_size);
+ if (aes_128_encrypt_block(psk, ak, ak))
+ return -1;
+ os_memcpy(kdk, ak, aes_block_size);
+ ak[aes_block_size - 1] ^= 0x01;
+ kdk[aes_block_size - 1] ^= 0x02;
+ if (aes_128_encrypt_block(psk, ak, ak) ||
+ aes_128_encrypt_block(psk, kdk, kdk))
+ return -1;
+ return 0;
+}
+
+
+int eap_psk_derive_keys(const u8 *kdk, const u8 *rand_p, u8 *tek, u8 *msk,
+ u8 *emsk)
+{
+ u8 hash[aes_block_size];
+ u8 counter = 1;
+ int i;
+
+ if (aes_128_encrypt_block(kdk, rand_p, hash))
+ return -1;
+
+ hash[aes_block_size - 1] ^= counter;
+ if (aes_128_encrypt_block(kdk, hash, tek))
+ return -1;
+ hash[aes_block_size - 1] ^= counter;
+ counter++;
+
+ for (i = 0; i < EAP_MSK_LEN / aes_block_size; i++) {
+ hash[aes_block_size - 1] ^= counter;
+ if (aes_128_encrypt_block(kdk, hash, &msk[i * aes_block_size]))
+ return -1;
+ hash[aes_block_size - 1] ^= counter;
+ counter++;
+ }
+
+ for (i = 0; i < EAP_EMSK_LEN / aes_block_size; i++) {
+ hash[aes_block_size - 1] ^= counter;
+ if (aes_128_encrypt_block(kdk, hash,
+ &emsk[i * aes_block_size]))
+ return -1;
+ hash[aes_block_size - 1] ^= counter;
+ counter++;
+ }
+
+ return 0;
+}
diff --git a/freebsd/contrib/wpa/src/eap_common/eap_psk_common.h b/freebsd/contrib/wpa/src/eap_common/eap_psk_common.h
new file mode 100644
index 00000000..8bc2c3c4
--- /dev/null
+++ b/freebsd/contrib/wpa/src/eap_common/eap_psk_common.h
@@ -0,0 +1,72 @@
+/*
+ * EAP server/peer: EAP-PSK shared routines
+ * 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_PSK_COMMON_H
+#define EAP_PSK_COMMON_H
+
+
+#define EAP_PSK_RAND_LEN 16
+#define EAP_PSK_MAC_LEN 16
+#define EAP_PSK_TEK_LEN 16
+#define EAP_PSK_PSK_LEN 16
+#define EAP_PSK_AK_LEN 16
+#define EAP_PSK_KDK_LEN 16
+
+#define EAP_PSK_R_FLAG_CONT 1
+#define EAP_PSK_R_FLAG_DONE_SUCCESS 2
+#define EAP_PSK_R_FLAG_DONE_FAILURE 3
+#define EAP_PSK_E_FLAG 0x20
+
+#define EAP_PSK_FLAGS_GET_T(flags) (((flags) & 0xc0) >> 6)
+#define EAP_PSK_FLAGS_SET_T(t) ((u8) (t) << 6)
+
+#ifdef _MSC_VER
+#pragma pack(push, 1)
+#endif /* _MSC_VER */
+
+/* EAP-PSK First Message (AS -> Supplicant) */
+struct eap_psk_hdr_1 {
+ u8 flags;
+ u8 rand_s[EAP_PSK_RAND_LEN];
+ /* Followed by variable length ID_S */
+} STRUCT_PACKED;
+
+/* EAP-PSK Second Message (Supplicant -> AS) */
+struct eap_psk_hdr_2 {
+ u8 flags;
+ u8 rand_s[EAP_PSK_RAND_LEN];
+ u8 rand_p[EAP_PSK_RAND_LEN];
+ u8 mac_p[EAP_PSK_MAC_LEN];
+ /* Followed by variable length ID_P */
+} STRUCT_PACKED;
+
+/* EAP-PSK Third Message (AS -> Supplicant) */
+struct eap_psk_hdr_3 {
+ u8 flags;
+ u8 rand_s[EAP_PSK_RAND_LEN];
+ u8 mac_s[EAP_PSK_MAC_LEN];
+ /* Followed by variable length PCHANNEL */
+} STRUCT_PACKED;
+
+/* EAP-PSK Fourth Message (Supplicant -> AS) */
+struct eap_psk_hdr_4 {
+ u8 flags;
+ u8 rand_s[EAP_PSK_RAND_LEN];
+ /* Followed by variable length PCHANNEL */
+} STRUCT_PACKED;
+
+#ifdef _MSC_VER
+#pragma pack(pop)
+#endif /* _MSC_VER */
+
+
+int __must_check eap_psk_key_setup(const u8 *psk, u8 *ak, u8 *kdk);
+int __must_check eap_psk_derive_keys(const u8 *kdk, const u8 *rand_p, u8 *tek,
+ u8 *msk, u8 *emsk);
+
+#endif /* EAP_PSK_COMMON_H */
diff --git a/freebsd/contrib/wpa/src/eap_common/eap_tlv_common.h b/freebsd/contrib/wpa/src/eap_common/eap_tlv_common.h
new file mode 100644
index 00000000..3286055a
--- /dev/null
+++ b/freebsd/contrib/wpa/src/eap_common/eap_tlv_common.h
@@ -0,0 +1,112 @@
+/*
+ * EAP-TLV definitions (draft-josefsson-pppext-eap-tls-eap-10.txt)
+ * Copyright (c) 2004-2008, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef EAP_TLV_COMMON_H
+#define EAP_TLV_COMMON_H
+
+/* EAP-TLV TLVs (draft-josefsson-ppext-eap-tls-eap-10.txt) */
+#define EAP_TLV_RESULT_TLV 3 /* Acknowledged Result */
+#define EAP_TLV_NAK_TLV 4
+#define EAP_TLV_ERROR_CODE_TLV 5
+#define EAP_TLV_CONNECTION_BINDING_TLV 6
+#define EAP_TLV_VENDOR_SPECIFIC_TLV 7
+#define EAP_TLV_URI_TLV 8
+#define EAP_TLV_EAP_PAYLOAD_TLV 9
+#define EAP_TLV_INTERMEDIATE_RESULT_TLV 10
+#define EAP_TLV_PAC_TLV 11 /* RFC 5422, Section 4.2 */
+#define EAP_TLV_CRYPTO_BINDING_TLV 12
+#define EAP_TLV_CALLING_STATION_ID_TLV 13
+#define EAP_TLV_CALLED_STATION_ID_TLV 14
+#define EAP_TLV_NAS_PORT_TYPE_TLV 15
+#define EAP_TLV_SERVER_IDENTIFIER_TLV 16
+#define EAP_TLV_IDENTITY_TYPE_TLV 17
+#define EAP_TLV_SERVER_TRUSTED_ROOT_TLV 18
+#define EAP_TLV_REQUEST_ACTION_TLV 19
+#define EAP_TLV_PKCS7_TLV 20
+
+#define EAP_TLV_RESULT_SUCCESS 1
+#define EAP_TLV_RESULT_FAILURE 2
+
+#define EAP_TLV_TYPE_MANDATORY 0x8000
+#define EAP_TLV_TYPE_MASK 0x3fff
+
+#ifdef _MSC_VER
+#pragma pack(push, 1)
+#endif /* _MSC_VER */
+
+struct eap_tlv_hdr {
+ be16 tlv_type;
+ be16 length;
+} STRUCT_PACKED;
+
+struct eap_tlv_nak_tlv {
+ be16 tlv_type;
+ be16 length;
+ be32 vendor_id;
+ be16 nak_type;
+} STRUCT_PACKED;
+
+struct eap_tlv_result_tlv {
+ be16 tlv_type;
+ be16 length;
+ be16 status;
+} STRUCT_PACKED;
+
+/* RFC 4851, Section 4.2.7 - Intermediate-Result TLV */
+struct eap_tlv_intermediate_result_tlv {
+ be16 tlv_type;
+ be16 length;
+ be16 status;
+ /* Followed by optional TLVs */
+} STRUCT_PACKED;
+
+/* RFC 4851, Section 4.2.8 - Crypto-Binding TLV */
+struct eap_tlv_crypto_binding_tlv {
+ be16 tlv_type;
+ be16 length;
+ u8 reserved;
+ u8 version;
+ u8 received_version;
+ u8 subtype;
+ u8 nonce[32];
+ u8 compound_mac[20];
+} STRUCT_PACKED;
+
+struct eap_tlv_pac_ack_tlv {
+ be16 tlv_type;
+ be16 length;
+ be16 pac_type;
+ be16 pac_len;
+ be16 result;
+} STRUCT_PACKED;
+
+/* RFC 4851, Section 4.2.9 - Request-Action TLV */
+struct eap_tlv_request_action_tlv {
+ be16 tlv_type;
+ be16 length;
+ be16 action;
+} STRUCT_PACKED;
+
+/* RFC 5422, Section 4.2.6 - PAC-Type TLV */
+struct eap_tlv_pac_type_tlv {
+ be16 tlv_type; /* PAC_TYPE_PAC_TYPE */
+ be16 length;
+ be16 pac_type;
+} STRUCT_PACKED;
+
+#ifdef _MSC_VER
+#pragma pack(pop)
+#endif /* _MSC_VER */
+
+#define EAP_TLV_CRYPTO_BINDING_SUBTYPE_REQUEST 0
+#define EAP_TLV_CRYPTO_BINDING_SUBTYPE_RESPONSE 1
+
+#define EAP_TLV_ACTION_PROCESS_TLV 1
+#define EAP_TLV_ACTION_NEGOTIATE_EAP 2
+
+#endif /* EAP_TLV_COMMON_H */
diff --git a/freebsd/contrib/wpa/src/eap_common/eap_ttls.h b/freebsd/contrib/wpa/src/eap_common/eap_ttls.h
new file mode 100644
index 00000000..17901d42
--- /dev/null
+++ b/freebsd/contrib/wpa/src/eap_common/eap_ttls.h
@@ -0,0 +1,65 @@
+/*
+ * EAP server/peer: EAP-TTLS (RFC 5281)
+ * 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_TTLS_H
+#define EAP_TTLS_H
+
+struct ttls_avp {
+ be32 avp_code;
+ be32 avp_length; /* 8-bit flags, 24-bit length;
+ * length includes AVP header */
+ /* optional 32-bit Vendor-ID */
+ /* Data */
+};
+
+struct ttls_avp_vendor {
+ be32 avp_code;
+ be32 avp_length; /* 8-bit flags, 24-bit length;
+ * length includes AVP header */
+ be32 vendor_id;
+ /* Data */
+};
+
+#define AVP_FLAGS_VENDOR 0x80
+#define AVP_FLAGS_MANDATORY 0x40
+
+#define AVP_PAD(start, pos) \
+do { \
+ int __pad; \
+ __pad = (4 - (((pos) - (start)) & 3)) & 3; \
+ os_memset((pos), 0, __pad); \
+ pos += __pad; \
+} while (0)
+
+
+/* RFC 2865 */
+#define RADIUS_ATTR_USER_NAME 1
+#define RADIUS_ATTR_USER_PASSWORD 2
+#define RADIUS_ATTR_CHAP_PASSWORD 3
+#define RADIUS_ATTR_REPLY_MESSAGE 18
+#define RADIUS_ATTR_CHAP_CHALLENGE 60
+#define RADIUS_ATTR_EAP_MESSAGE 79
+
+/* RFC 2548 */
+#define RADIUS_VENDOR_ID_MICROSOFT 311
+#define RADIUS_ATTR_MS_CHAP_RESPONSE 1
+#define RADIUS_ATTR_MS_CHAP_ERROR 2
+#define RADIUS_ATTR_MS_CHAP_NT_ENC_PW 6
+#define RADIUS_ATTR_MS_CHAP_CHALLENGE 11
+#define RADIUS_ATTR_MS_CHAP2_RESPONSE 25
+#define RADIUS_ATTR_MS_CHAP2_SUCCESS 26
+#define RADIUS_ATTR_MS_CHAP2_CPW 27
+
+#define EAP_TTLS_MSCHAPV2_CHALLENGE_LEN 16
+#define EAP_TTLS_MSCHAPV2_RESPONSE_LEN 50
+#define EAP_TTLS_MSCHAP_CHALLENGE_LEN 8
+#define EAP_TTLS_MSCHAP_RESPONSE_LEN 50
+#define EAP_TTLS_CHAP_CHALLENGE_LEN 16
+#define EAP_TTLS_CHAP_PASSWORD_LEN 16
+
+#endif /* EAP_TTLS_H */
diff --git a/freebsd/contrib/wpa/src/eap_common/eap_wsc_common.h b/freebsd/contrib/wpa/src/eap_common/eap_wsc_common.h
new file mode 100644
index 00000000..0e7b6530
--- /dev/null
+++ b/freebsd/contrib/wpa/src/eap_common/eap_wsc_common.h
@@ -0,0 +1,27 @@
+/*
+ * EAP-WSC definitions for Wi-Fi Protected Setup
+ * Copyright (c) 2007, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef EAP_WSC_COMMON_H
+#define EAP_WSC_COMMON_H
+
+#define EAP_VENDOR_TYPE_WSC 1
+
+#define WSC_FLAGS_MF 0x01
+#define WSC_FLAGS_LF 0x02
+
+#define WSC_ID_REGISTRAR "WFA-SimpleConfig-Registrar-1-0"
+#define WSC_ID_REGISTRAR_LEN 30
+#define WSC_ID_ENROLLEE "WFA-SimpleConfig-Enrollee-1-0"
+#define WSC_ID_ENROLLEE_LEN 29
+
+#define WSC_FRAGMENT_SIZE 1400
+
+
+struct wpabuf * eap_wsc_build_frag_ack(u8 id, u8 code);
+
+#endif /* EAP_WSC_COMMON_H */
diff --git a/freebsd/contrib/wpa/src/eap_peer/eap.c b/freebsd/contrib/wpa/src/eap_peer/eap.c
new file mode 100644
index 00000000..cfaf201a
--- /dev/null
+++ b/freebsd/contrib/wpa/src/eap_peer/eap.c
@@ -0,0 +1,2952 @@
+#include <machine/rtems-bsd-user-space.h>
+
+/*
+ * EAP peer state machines (RFC 4137)
+ * Copyright (c) 2004-2014, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ *
+ * 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 "includes.h"
+
+#include "common.h"
+#include "pcsc_funcs.h"
+#include "state_machine.h"
+#include "ext_password.h"
+#include "crypto/crypto.h"
+#include "crypto/tls.h"
+#include "crypto/sha256.h"
+#include "common/wpa_ctrl.h"
+#include "eap_common/eap_wsc_common.h"
+#include "eap_i.h"
+#include "eap_config.h"
+
+#define STATE_MACHINE_DATA struct eap_sm
+#define STATE_MACHINE_DEBUG_PREFIX "EAP"
+
+#define EAP_MAX_AUTH_ROUNDS 50
+#define EAP_CLIENT_TIMEOUT_DEFAULT 60
+
+
+static Boolean eap_sm_allowMethod(struct eap_sm *sm, int vendor,
+ EapType method);
+static struct wpabuf * eap_sm_buildNak(struct eap_sm *sm, int id);
+static void eap_sm_processIdentity(struct eap_sm *sm,
+ const struct wpabuf *req);
+static void eap_sm_processNotify(struct eap_sm *sm, const struct wpabuf *req);
+static struct wpabuf * eap_sm_buildNotify(int id);
+static void eap_sm_parseEapReq(struct eap_sm *sm, const struct wpabuf *req);
+#if defined(CONFIG_CTRL_IFACE) || !defined(CONFIG_NO_STDOUT_DEBUG)
+static const char * eap_sm_method_state_txt(EapMethodState state);
+static const char * eap_sm_decision_txt(EapDecision decision);
+#endif /* CONFIG_CTRL_IFACE || !CONFIG_NO_STDOUT_DEBUG */
+
+
+
+static Boolean eapol_get_bool(struct eap_sm *sm, enum eapol_bool_var var)
+{
+ return sm->eapol_cb->get_bool(sm->eapol_ctx, var);
+}
+
+
+static void eapol_set_bool(struct eap_sm *sm, enum eapol_bool_var var,
+ Boolean value)
+{
+ sm->eapol_cb->set_bool(sm->eapol_ctx, var, value);
+}
+
+
+static unsigned int eapol_get_int(struct eap_sm *sm, enum eapol_int_var var)
+{
+ return sm->eapol_cb->get_int(sm->eapol_ctx, var);
+}
+
+
+static void eapol_set_int(struct eap_sm *sm, enum eapol_int_var var,
+ unsigned int value)
+{
+ sm->eapol_cb->set_int(sm->eapol_ctx, var, value);
+}
+
+
+static struct wpabuf * eapol_get_eapReqData(struct eap_sm *sm)
+{
+ return sm->eapol_cb->get_eapReqData(sm->eapol_ctx);
+}
+
+
+static void eap_notify_status(struct eap_sm *sm, const char *status,
+ const char *parameter)
+{
+ wpa_printf(MSG_DEBUG, "EAP: Status notification: %s (param=%s)",
+ status, parameter);
+ if (sm->eapol_cb->notify_status)
+ sm->eapol_cb->notify_status(sm->eapol_ctx, status, parameter);
+}
+
+
+static void eap_sm_free_key(struct eap_sm *sm)
+{
+ if (sm->eapKeyData) {
+ bin_clear_free(sm->eapKeyData, sm->eapKeyDataLen);
+ sm->eapKeyData = NULL;
+ }
+}
+
+
+static void eap_deinit_prev_method(struct eap_sm *sm, const char *txt)
+{
+ ext_password_free(sm->ext_pw_buf);
+ sm->ext_pw_buf = NULL;
+
+ if (sm->m == NULL || sm->eap_method_priv == NULL)
+ return;
+
+ wpa_printf(MSG_DEBUG, "EAP: deinitialize previously used EAP method "
+ "(%d, %s) at %s", sm->selectedMethod, sm->m->name, txt);
+ sm->m->deinit(sm, sm->eap_method_priv);
+ sm->eap_method_priv = NULL;
+ sm->m = NULL;
+}
+
+
+/**
+ * eap_allowed_method - Check whether EAP method is allowed
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @vendor: Vendor-Id for expanded types or 0 = IETF for legacy types
+ * @method: EAP type
+ * Returns: 1 = allowed EAP method, 0 = not allowed
+ */
+int eap_allowed_method(struct eap_sm *sm, int vendor, u32 method)
+{
+ struct eap_peer_config *config = eap_get_config(sm);
+ int i;
+ struct eap_method_type *m;
+
+ if (config == NULL || config->eap_methods == NULL)
+ return 1;
+
+ m = config->eap_methods;
+ for (i = 0; m[i].vendor != EAP_VENDOR_IETF ||
+ m[i].method != EAP_TYPE_NONE; i++) {
+ if (m[i].vendor == vendor && m[i].method == method)
+ return 1;
+ }
+ return 0;
+}
+
+
+/*
+ * This state initializes state machine variables when the machine is
+ * activated (portEnabled = TRUE). This is also used when re-starting
+ * authentication (eapRestart == TRUE).
+ */
+SM_STATE(EAP, INITIALIZE)
+{
+ SM_ENTRY(EAP, INITIALIZE);
+ if (sm->fast_reauth && sm->m && sm->m->has_reauth_data &&
+ sm->m->has_reauth_data(sm, sm->eap_method_priv) &&
+ !sm->prev_failure &&
+ sm->last_config == eap_get_config(sm)) {
+ wpa_printf(MSG_DEBUG, "EAP: maintaining EAP method data for "
+ "fast reauthentication");
+ sm->m->deinit_for_reauth(sm, sm->eap_method_priv);
+ } else {
+ sm->last_config = eap_get_config(sm);
+ eap_deinit_prev_method(sm, "INITIALIZE");
+ }
+ sm->selectedMethod = EAP_TYPE_NONE;
+ sm->methodState = METHOD_NONE;
+ sm->allowNotifications = TRUE;
+ sm->decision = DECISION_FAIL;
+ sm->ClientTimeout = EAP_CLIENT_TIMEOUT_DEFAULT;
+ eapol_set_int(sm, EAPOL_idleWhile, sm->ClientTimeout);
+ eapol_set_bool(sm, EAPOL_eapSuccess, FALSE);
+ eapol_set_bool(sm, EAPOL_eapFail, FALSE);
+ eap_sm_free_key(sm);
+ os_free(sm->eapSessionId);
+ sm->eapSessionId = NULL;
+ sm->eapKeyAvailable = FALSE;
+ eapol_set_bool(sm, EAPOL_eapRestart, FALSE);
+ sm->lastId = -1; /* new session - make sure this does not match with
+ * the first EAP-Packet */
+ /*
+ * RFC 4137 does not reset eapResp and eapNoResp here. However, this
+ * seemed to be able to trigger cases where both were set and if EAPOL
+ * state machine uses eapNoResp first, it may end up not sending a real
+ * reply correctly. This occurred when the workaround in FAIL state set
+ * eapNoResp = TRUE.. Maybe that workaround needs to be fixed to do
+ * something else(?)
+ */
+ eapol_set_bool(sm, EAPOL_eapResp, FALSE);
+ eapol_set_bool(sm, EAPOL_eapNoResp, FALSE);
+ sm->num_rounds = 0;
+ sm->prev_failure = 0;
+ sm->expected_failure = 0;
+ sm->reauthInit = FALSE;
+ sm->erp_seq = (u32) -1;
+}
+
+
+/*
+ * This state is reached whenever service from the lower layer is interrupted
+ * or unavailable (portEnabled == FALSE). Immediate transition to INITIALIZE
+ * occurs when the port becomes enabled.
+ */
+SM_STATE(EAP, DISABLED)
+{
+ SM_ENTRY(EAP, DISABLED);
+ sm->num_rounds = 0;
+ /*
+ * RFC 4137 does not describe clearing of idleWhile here, but doing so
+ * allows the timer tick to be stopped more quickly when EAP is not in
+ * use.
+ */
+ eapol_set_int(sm, EAPOL_idleWhile, 0);
+}
+
+
+/*
+ * The state machine spends most of its time here, waiting for something to
+ * happen. This state is entered unconditionally from INITIALIZE, DISCARD, and
+ * SEND_RESPONSE states.
+ */
+SM_STATE(EAP, IDLE)
+{
+ SM_ENTRY(EAP, IDLE);
+}
+
+
+/*
+ * This state is entered when an EAP packet is received (eapReq == TRUE) to
+ * parse the packet header.
+ */
+SM_STATE(EAP, RECEIVED)
+{
+ const struct wpabuf *eapReqData;
+
+ SM_ENTRY(EAP, RECEIVED);
+ eapReqData = eapol_get_eapReqData(sm);
+ /* parse rxReq, rxSuccess, rxFailure, reqId, reqMethod */
+ eap_sm_parseEapReq(sm, eapReqData);
+ sm->num_rounds++;
+}
+
+
+/*
+ * This state is entered when a request for a new type comes in. Either the
+ * correct method is started, or a Nak response is built.
+ */
+SM_STATE(EAP, GET_METHOD)
+{
+ int reinit;
+ EapType method;
+ const struct eap_method *eap_method;
+
+ SM_ENTRY(EAP, GET_METHOD);
+
+ if (sm->reqMethod == EAP_TYPE_EXPANDED)
+ method = sm->reqVendorMethod;
+ else
+ method = sm->reqMethod;
+
+ eap_method = eap_peer_get_eap_method(sm->reqVendor, method);
+
+ if (!eap_sm_allowMethod(sm, sm->reqVendor, method)) {
+ wpa_printf(MSG_DEBUG, "EAP: vendor %u method %u not allowed",
+ sm->reqVendor, method);
+ wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_PROPOSED_METHOD
+ "vendor=%u method=%u -> NAK",
+ sm->reqVendor, method);
+ eap_notify_status(sm, "refuse proposed method",
+ eap_method ? eap_method->name : "unknown");
+ goto nak;
+ }
+
+ wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_PROPOSED_METHOD
+ "vendor=%u method=%u", sm->reqVendor, method);
+
+ eap_notify_status(sm, "accept proposed method",
+ eap_method ? eap_method->name : "unknown");
+ /*
+ * RFC 4137 does not define specific operation for fast
+ * re-authentication (session resumption). The design here is to allow
+ * the previously used method data to be maintained for
+ * re-authentication if the method support session resumption.
+ * Otherwise, the previously used method data is freed and a new method
+ * is allocated here.
+ */
+ if (sm->fast_reauth &&
+ sm->m && sm->m->vendor == sm->reqVendor &&
+ sm->m->method == method &&
+ sm->m->has_reauth_data &&
+ sm->m->has_reauth_data(sm, sm->eap_method_priv)) {
+ wpa_printf(MSG_DEBUG, "EAP: Using previous method data"
+ " for fast re-authentication");
+ reinit = 1;
+ } else {
+ eap_deinit_prev_method(sm, "GET_METHOD");
+ reinit = 0;
+ }
+
+ sm->selectedMethod = sm->reqMethod;
+ if (sm->m == NULL)
+ sm->m = eap_method;
+ if (!sm->m) {
+ wpa_printf(MSG_DEBUG, "EAP: Could not find selected method: "
+ "vendor %d method %d",
+ sm->reqVendor, method);
+ goto nak;
+ }
+
+ sm->ClientTimeout = EAP_CLIENT_TIMEOUT_DEFAULT;
+
+ wpa_printf(MSG_DEBUG, "EAP: Initialize selected EAP method: "
+ "vendor %u method %u (%s)",
+ sm->reqVendor, method, sm->m->name);
+ if (reinit)
+ sm->eap_method_priv = sm->m->init_for_reauth(
+ sm, sm->eap_method_priv);
+ else
+ sm->eap_method_priv = sm->m->init(sm);
+
+ if (sm->eap_method_priv == NULL) {
+ struct eap_peer_config *config = eap_get_config(sm);
+ wpa_msg(sm->msg_ctx, MSG_INFO,
+ "EAP: Failed to initialize EAP method: vendor %u "
+ "method %u (%s)",
+ sm->reqVendor, method, sm->m->name);
+ sm->m = NULL;
+ sm->methodState = METHOD_NONE;
+ sm->selectedMethod = EAP_TYPE_NONE;
+ if (sm->reqMethod == EAP_TYPE_TLS && config &&
+ (config->pending_req_pin ||
+ config->pending_req_passphrase)) {
+ /*
+ * Return without generating Nak in order to allow
+ * entering of PIN code or passphrase to retry the
+ * current EAP packet.
+ */
+ wpa_printf(MSG_DEBUG, "EAP: Pending PIN/passphrase "
+ "request - skip Nak");
+ return;
+ }
+
+ goto nak;
+ }
+
+ sm->methodState = METHOD_INIT;
+ wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_METHOD
+ "EAP vendor %u method %u (%s) selected",
+ sm->reqVendor, method, sm->m->name);
+ return;
+
+nak:
+ wpabuf_free(sm->eapRespData);
+ sm->eapRespData = NULL;
+ sm->eapRespData = eap_sm_buildNak(sm, sm->reqId);
+}
+
+
+#ifdef CONFIG_ERP
+
+static char * eap_home_realm(struct eap_sm *sm)
+{
+ struct eap_peer_config *config = eap_get_config(sm);
+ char *realm;
+ size_t i, realm_len;
+
+ if (!config)
+ return NULL;
+
+ if (config->identity) {
+ for (i = 0; i < config->identity_len; i++) {
+ if (config->identity[i] == '@')
+ break;
+ }
+ if (i < config->identity_len) {
+ realm_len = config->identity_len - i - 1;
+ realm = os_malloc(realm_len + 1);
+ if (realm == NULL)
+ return NULL;
+ os_memcpy(realm, &config->identity[i + 1], realm_len);
+ realm[realm_len] = '\0';
+ return realm;
+ }
+ }
+
+ if (config->anonymous_identity) {
+ for (i = 0; i < config->anonymous_identity_len; i++) {
+ if (config->anonymous_identity[i] == '@')
+ break;
+ }
+ if (i < config->anonymous_identity_len) {
+ realm_len = config->anonymous_identity_len - i - 1;
+ realm = os_malloc(realm_len + 1);
+ if (realm == NULL)
+ return NULL;
+ os_memcpy(realm, &config->anonymous_identity[i + 1],
+ realm_len);
+ realm[realm_len] = '\0';
+ return realm;
+ }
+ }
+
+ return os_strdup("");
+}
+
+
+static struct eap_erp_key *
+eap_erp_get_key(struct eap_sm *sm, const char *realm)
+{
+ struct eap_erp_key *erp;
+
+ dl_list_for_each(erp, &sm->erp_keys, struct eap_erp_key, list) {
+ char *pos;
+
+ pos = os_strchr(erp->keyname_nai, '@');
+ if (!pos)
+ continue;
+ pos++;
+ if (os_strcmp(pos, realm) == 0)
+ return erp;
+ }
+
+ return NULL;
+}
+
+
+static struct eap_erp_key *
+eap_erp_get_key_nai(struct eap_sm *sm, const char *nai)
+{
+ struct eap_erp_key *erp;
+
+ dl_list_for_each(erp, &sm->erp_keys, struct eap_erp_key, list) {
+ if (os_strcmp(erp->keyname_nai, nai) == 0)
+ return erp;
+ }
+
+ return NULL;
+}
+
+
+static void eap_peer_erp_free_key(struct eap_erp_key *erp)
+{
+ dl_list_del(&erp->list);
+ bin_clear_free(erp, sizeof(*erp));
+}
+
+
+static void eap_erp_remove_keys_realm(struct eap_sm *sm, const char *realm)
+{
+ struct eap_erp_key *erp;
+
+ while ((erp = eap_erp_get_key(sm, realm)) != NULL) {
+ wpa_printf(MSG_DEBUG, "EAP: Delete old ERP key %s",
+ erp->keyname_nai);
+ eap_peer_erp_free_key(erp);
+ }
+}
+
+#endif /* CONFIG_ERP */
+
+
+void eap_peer_erp_free_keys(struct eap_sm *sm)
+{
+#ifdef CONFIG_ERP
+ struct eap_erp_key *erp, *tmp;
+
+ dl_list_for_each_safe(erp, tmp, &sm->erp_keys, struct eap_erp_key, list)
+ eap_peer_erp_free_key(erp);
+#endif /* CONFIG_ERP */
+}
+
+
+static void eap_peer_erp_init(struct eap_sm *sm)
+{
+#ifdef CONFIG_ERP
+ u8 *emsk = NULL;
+ size_t emsk_len = 0;
+ u8 EMSKname[EAP_EMSK_NAME_LEN];
+ u8 len[2];
+ char *realm;
+ size_t realm_len, nai_buf_len;
+ struct eap_erp_key *erp = NULL;
+ int pos;
+
+ realm = eap_home_realm(sm);
+ if (!realm)
+ return;
+ realm_len = os_strlen(realm);
+ wpa_printf(MSG_DEBUG, "EAP: Realm for ERP keyName-NAI: %s", realm);
+ eap_erp_remove_keys_realm(sm, realm);
+
+ nai_buf_len = 2 * EAP_EMSK_NAME_LEN + 1 + realm_len;
+ if (nai_buf_len > 253) {
+ /*
+ * keyName-NAI has a maximum length of 253 octet to fit in
+ * RADIUS attributes.
+ */
+ wpa_printf(MSG_DEBUG,
+ "EAP: Too long realm for ERP keyName-NAI maximum length");
+ goto fail;
+ }
+ nai_buf_len++; /* null termination */
+ erp = os_zalloc(sizeof(*erp) + nai_buf_len);
+ if (erp == NULL)
+ goto fail;
+
+ emsk = sm->m->get_emsk(sm, sm->eap_method_priv, &emsk_len);
+ if (!emsk || emsk_len == 0 || emsk_len > ERP_MAX_KEY_LEN) {
+ wpa_printf(MSG_DEBUG,
+ "EAP: No suitable EMSK available for ERP");
+ goto fail;
+ }
+
+ wpa_hexdump_key(MSG_DEBUG, "EAP: EMSK", emsk, emsk_len);
+
+ WPA_PUT_BE16(len, 8);
+ if (hmac_sha256_kdf(sm->eapSessionId, sm->eapSessionIdLen, "EMSK",
+ len, sizeof(len),
+ EMSKname, EAP_EMSK_NAME_LEN) < 0) {
+ wpa_printf(MSG_DEBUG, "EAP: Could not derive EMSKname");
+ goto fail;
+ }
+ wpa_hexdump(MSG_DEBUG, "EAP: EMSKname", EMSKname, EAP_EMSK_NAME_LEN);
+
+ pos = wpa_snprintf_hex(erp->keyname_nai, nai_buf_len,
+ EMSKname, EAP_EMSK_NAME_LEN);
+ erp->keyname_nai[pos] = '@';
+ os_memcpy(&erp->keyname_nai[pos + 1], realm, realm_len);
+
+ WPA_PUT_BE16(len, emsk_len);
+ if (hmac_sha256_kdf(emsk, emsk_len,
+ "EAP Re-authentication Root Key@ietf.org",
+ len, sizeof(len), erp->rRK, emsk_len) < 0) {
+ wpa_printf(MSG_DEBUG, "EAP: Could not derive rRK for ERP");
+ goto fail;
+ }
+ erp->rRK_len = emsk_len;
+ wpa_hexdump_key(MSG_DEBUG, "EAP: ERP rRK", erp->rRK, erp->rRK_len);
+
+ if (hmac_sha256_kdf(erp->rRK, erp->rRK_len,
+ "EAP Re-authentication Integrity Key@ietf.org",
+ len, sizeof(len), erp->rIK, erp->rRK_len) < 0) {
+ wpa_printf(MSG_DEBUG, "EAP: Could not derive rIK for ERP");
+ goto fail;
+ }
+ erp->rIK_len = erp->rRK_len;
+ wpa_hexdump_key(MSG_DEBUG, "EAP: ERP rIK", erp->rIK, erp->rIK_len);
+
+ wpa_printf(MSG_DEBUG, "EAP: Stored ERP keys %s", erp->keyname_nai);
+ dl_list_add(&sm->erp_keys, &erp->list);
+ erp = NULL;
+fail:
+ bin_clear_free(emsk, emsk_len);
+ bin_clear_free(erp, sizeof(*erp));
+ os_free(realm);
+#endif /* CONFIG_ERP */
+}
+
+
+#ifdef CONFIG_ERP
+static int eap_peer_erp_reauth_start(struct eap_sm *sm,
+ const struct eap_hdr *hdr, size_t len)
+{
+ char *realm;
+ struct eap_erp_key *erp;
+ struct wpabuf *msg;
+ u8 hash[SHA256_MAC_LEN];
+
+ realm = eap_home_realm(sm);
+ if (!realm)
+ return -1;
+
+ erp = eap_erp_get_key(sm, realm);
+ os_free(realm);
+ realm = NULL;
+ if (!erp)
+ return -1;
+
+ if (erp->next_seq >= 65536)
+ return -1; /* SEQ has range of 0..65535 */
+
+ /* TODO: check rRK lifetime expiration */
+
+ wpa_printf(MSG_DEBUG, "EAP: Valid ERP key found %s (SEQ=%u)",
+ erp->keyname_nai, erp->next_seq);
+
+ msg = eap_msg_alloc(EAP_VENDOR_IETF, (EapType) EAP_ERP_TYPE_REAUTH,
+ 1 + 2 + 2 + os_strlen(erp->keyname_nai) + 1 + 16,
+ EAP_CODE_INITIATE, hdr->identifier);
+ if (msg == NULL)
+ return -1;
+
+ wpabuf_put_u8(msg, 0x20); /* Flags: R=0 B=0 L=1 */
+ wpabuf_put_be16(msg, erp->next_seq);
+
+ wpabuf_put_u8(msg, EAP_ERP_TLV_KEYNAME_NAI);
+ wpabuf_put_u8(msg, os_strlen(erp->keyname_nai));
+ wpabuf_put_str(msg, erp->keyname_nai);
+
+ wpabuf_put_u8(msg, EAP_ERP_CS_HMAC_SHA256_128); /* Cryptosuite */
+
+ if (hmac_sha256(erp->rIK, erp->rIK_len,
+ wpabuf_head(msg), wpabuf_len(msg), hash) < 0) {
+ wpabuf_free(msg);
+ return -1;
+ }
+ wpabuf_put_data(msg, hash, 16);
+
+ wpa_printf(MSG_DEBUG, "EAP: Sending EAP-Initiate/Re-auth");
+ sm->erp_seq = erp->next_seq;
+ erp->next_seq++;
+ wpabuf_free(sm->eapRespData);
+ sm->eapRespData = msg;
+ sm->reauthInit = TRUE;
+ return 0;
+}
+#endif /* CONFIG_ERP */
+
+
+/*
+ * The method processing happens here. The request from the authenticator is
+ * processed, and an appropriate response packet is built.
+ */
+SM_STATE(EAP, METHOD)
+{
+ struct wpabuf *eapReqData;
+ struct eap_method_ret ret;
+ int min_len = 1;
+
+ SM_ENTRY(EAP, METHOD);
+ if (sm->m == NULL) {
+ wpa_printf(MSG_WARNING, "EAP::METHOD - method not selected");
+ return;
+ }
+
+ eapReqData = eapol_get_eapReqData(sm);
+ if (sm->m->vendor == EAP_VENDOR_IETF && sm->m->method == EAP_TYPE_LEAP)
+ min_len = 0; /* LEAP uses EAP-Success without payload */
+ if (!eap_hdr_len_valid(eapReqData, min_len))
+ return;
+
+ /*
+ * Get ignore, methodState, decision, allowNotifications, and
+ * eapRespData. RFC 4137 uses three separate method procedure (check,
+ * process, and buildResp) in this state. These have been combined into
+ * a single function call to m->process() in order to optimize EAP
+ * method implementation interface a bit. These procedures are only
+ * used from within this METHOD state, so there is no need to keep
+ * these as separate C functions.
+ *
+ * The RFC 4137 procedures return values as follows:
+ * ignore = m.check(eapReqData)
+ * (methodState, decision, allowNotifications) = m.process(eapReqData)
+ * eapRespData = m.buildResp(reqId)
+ */
+ os_memset(&ret, 0, sizeof(ret));
+ ret.ignore = sm->ignore;
+ ret.methodState = sm->methodState;
+ ret.decision = sm->decision;
+ ret.allowNotifications = sm->allowNotifications;
+ wpabuf_free(sm->eapRespData);
+ sm->eapRespData = NULL;
+ sm->eapRespData = sm->m->process(sm, sm->eap_method_priv, &ret,
+ eapReqData);
+ wpa_printf(MSG_DEBUG, "EAP: method process -> ignore=%s "
+ "methodState=%s decision=%s eapRespData=%p",
+ ret.ignore ? "TRUE" : "FALSE",
+ eap_sm_method_state_txt(ret.methodState),
+ eap_sm_decision_txt(ret.decision),
+ sm->eapRespData);
+
+ sm->ignore = ret.ignore;
+ if (sm->ignore)
+ return;
+ sm->methodState = ret.methodState;
+ sm->decision = ret.decision;
+ sm->allowNotifications = ret.allowNotifications;
+
+ if (sm->m->isKeyAvailable && sm->m->getKey &&
+ sm->m->isKeyAvailable(sm, sm->eap_method_priv)) {
+ struct eap_peer_config *config = eap_get_config(sm);
+
+ eap_sm_free_key(sm);
+ sm->eapKeyData = sm->m->getKey(sm, sm->eap_method_priv,
+ &sm->eapKeyDataLen);
+ os_free(sm->eapSessionId);
+ sm->eapSessionId = NULL;
+ if (sm->m->getSessionId) {
+ sm->eapSessionId = sm->m->getSessionId(
+ sm, sm->eap_method_priv,
+ &sm->eapSessionIdLen);
+ wpa_hexdump(MSG_DEBUG, "EAP: Session-Id",
+ sm->eapSessionId, sm->eapSessionIdLen);
+ }
+ if (config->erp && sm->m->get_emsk && sm->eapSessionId)
+ eap_peer_erp_init(sm);
+ }
+}
+
+
+/*
+ * This state signals the lower layer that a response packet is ready to be
+ * sent.
+ */
+SM_STATE(EAP, SEND_RESPONSE)
+{
+ SM_ENTRY(EAP, SEND_RESPONSE);
+ wpabuf_free(sm->lastRespData);
+ if (sm->eapRespData) {
+ if (sm->workaround)
+ os_memcpy(sm->last_sha1, sm->req_sha1, 20);
+ sm->lastId = sm->reqId;
+ sm->lastRespData = wpabuf_dup(sm->eapRespData);
+ eapol_set_bool(sm, EAPOL_eapResp, TRUE);
+ } else {
+ wpa_printf(MSG_DEBUG, "EAP: No eapRespData available");
+ sm->lastRespData = NULL;
+ }
+ eapol_set_bool(sm, EAPOL_eapReq, FALSE);
+ eapol_set_int(sm, EAPOL_idleWhile, sm->ClientTimeout);
+ sm->reauthInit = FALSE;
+}
+
+
+/*
+ * This state signals the lower layer that the request was discarded, and no
+ * response packet will be sent at this time.
+ */
+SM_STATE(EAP, DISCARD)
+{
+ SM_ENTRY(EAP, DISCARD);
+ eapol_set_bool(sm, EAPOL_eapReq, FALSE);
+ eapol_set_bool(sm, EAPOL_eapNoResp, TRUE);
+}
+
+
+/*
+ * Handles requests for Identity method and builds a response.
+ */
+SM_STATE(EAP, IDENTITY)
+{
+ const struct wpabuf *eapReqData;
+
+ SM_ENTRY(EAP, IDENTITY);
+ eapReqData = eapol_get_eapReqData(sm);
+ if (!eap_hdr_len_valid(eapReqData, 1))
+ return;
+ eap_sm_processIdentity(sm, eapReqData);
+ wpabuf_free(sm->eapRespData);
+ sm->eapRespData = NULL;
+ sm->eapRespData = eap_sm_buildIdentity(sm, sm->reqId, 0);
+}
+
+
+/*
+ * Handles requests for Notification method and builds a response.
+ */
+SM_STATE(EAP, NOTIFICATION)
+{
+ const struct wpabuf *eapReqData;
+
+ SM_ENTRY(EAP, NOTIFICATION);
+ eapReqData = eapol_get_eapReqData(sm);
+ if (!eap_hdr_len_valid(eapReqData, 1))
+ return;
+ eap_sm_processNotify(sm, eapReqData);
+ wpabuf_free(sm->eapRespData);
+ sm->eapRespData = NULL;
+ sm->eapRespData = eap_sm_buildNotify(sm->reqId);
+}
+
+
+/*
+ * This state retransmits the previous response packet.
+ */
+SM_STATE(EAP, RETRANSMIT)
+{
+ SM_ENTRY(EAP, RETRANSMIT);
+ wpabuf_free(sm->eapRespData);
+ if (sm->lastRespData)
+ sm->eapRespData = wpabuf_dup(sm->lastRespData);
+ else
+ sm->eapRespData = NULL;
+}
+
+
+/*
+ * This state is entered in case of a successful completion of authentication
+ * and state machine waits here until port is disabled or EAP authentication is
+ * restarted.
+ */
+SM_STATE(EAP, SUCCESS)
+{
+ SM_ENTRY(EAP, SUCCESS);
+ if (sm->eapKeyData != NULL)
+ sm->eapKeyAvailable = TRUE;
+ eapol_set_bool(sm, EAPOL_eapSuccess, TRUE);
+
+ /*
+ * RFC 4137 does not clear eapReq here, but this seems to be required
+ * to avoid processing the same request twice when state machine is
+ * initialized.
+ */
+ eapol_set_bool(sm, EAPOL_eapReq, FALSE);
+
+ /*
+ * RFC 4137 does not set eapNoResp here, but this seems to be required
+ * to get EAPOL Supplicant backend state machine into SUCCESS state. In
+ * addition, either eapResp or eapNoResp is required to be set after
+ * processing the received EAP frame.
+ */
+ eapol_set_bool(sm, EAPOL_eapNoResp, TRUE);
+
+ wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_SUCCESS
+ "EAP authentication completed successfully");
+}
+
+
+/*
+ * This state is entered in case of a failure and state machine waits here
+ * until port is disabled or EAP authentication is restarted.
+ */
+SM_STATE(EAP, FAILURE)
+{
+ SM_ENTRY(EAP, FAILURE);
+ eapol_set_bool(sm, EAPOL_eapFail, TRUE);
+
+ /*
+ * RFC 4137 does not clear eapReq here, but this seems to be required
+ * to avoid processing the same request twice when state machine is
+ * initialized.
+ */
+ eapol_set_bool(sm, EAPOL_eapReq, FALSE);
+
+ /*
+ * RFC 4137 does not set eapNoResp here. However, either eapResp or
+ * eapNoResp is required to be set after processing the received EAP
+ * frame.
+ */
+ eapol_set_bool(sm, EAPOL_eapNoResp, TRUE);
+
+ wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_FAILURE
+ "EAP authentication failed");
+
+ sm->prev_failure = 1;
+}
+
+
+static int eap_success_workaround(struct eap_sm *sm, int reqId, int lastId)
+{
+ /*
+ * At least Microsoft IAS and Meetinghouse Aegis seem to be sending
+ * EAP-Success/Failure with lastId + 1 even though RFC 3748 and
+ * RFC 4137 require that reqId == lastId. In addition, it looks like
+ * Ringmaster v2.1.2.0 would be using lastId + 2 in EAP-Success.
+ *
+ * Accept this kind of Id if EAP workarounds are enabled. These are
+ * unauthenticated plaintext messages, so this should have minimal
+ * security implications (bit easier to fake EAP-Success/Failure).
+ */
+ if (sm->workaround && (reqId == ((lastId + 1) & 0xff) ||
+ reqId == ((lastId + 2) & 0xff))) {
+ wpa_printf(MSG_DEBUG, "EAP: Workaround for unexpected "
+ "identifier field in EAP Success: "
+ "reqId=%d lastId=%d (these are supposed to be "
+ "same)", reqId, lastId);
+ return 1;
+ }
+ wpa_printf(MSG_DEBUG, "EAP: EAP-Success Id mismatch - reqId=%d "
+ "lastId=%d", reqId, lastId);
+ return 0;
+}
+
+
+/*
+ * RFC 4137 - Appendix A.1: EAP Peer State Machine - State transitions
+ */
+
+static void eap_peer_sm_step_idle(struct eap_sm *sm)
+{
+ /*
+ * The first three transitions are from RFC 4137. The last two are
+ * local additions to handle special cases with LEAP and PEAP server
+ * not sending EAP-Success in some cases.
+ */
+ if (eapol_get_bool(sm, EAPOL_eapReq))
+ SM_ENTER(EAP, RECEIVED);
+ else if ((eapol_get_bool(sm, EAPOL_altAccept) &&
+ sm->decision != DECISION_FAIL) ||
+ (eapol_get_int(sm, EAPOL_idleWhile) == 0 &&
+ sm->decision == DECISION_UNCOND_SUCC))
+ SM_ENTER(EAP, SUCCESS);
+ else if (eapol_get_bool(sm, EAPOL_altReject) ||
+ (eapol_get_int(sm, EAPOL_idleWhile) == 0 &&
+ sm->decision != DECISION_UNCOND_SUCC) ||
+ (eapol_get_bool(sm, EAPOL_altAccept) &&
+ sm->methodState != METHOD_CONT &&
+ sm->decision == DECISION_FAIL))
+ SM_ENTER(EAP, FAILURE);
+ else if (sm->selectedMethod == EAP_TYPE_LEAP &&
+ sm->leap_done && sm->decision != DECISION_FAIL &&
+ sm->methodState == METHOD_DONE)
+ SM_ENTER(EAP, SUCCESS);
+ else if (sm->selectedMethod == EAP_TYPE_PEAP &&
+ sm->peap_done && sm->decision != DECISION_FAIL &&
+ sm->methodState == METHOD_DONE)
+ SM_ENTER(EAP, SUCCESS);
+}
+
+
+static int eap_peer_req_is_duplicate(struct eap_sm *sm)
+{
+ int duplicate;
+
+ duplicate = (sm->reqId == sm->lastId) && sm->rxReq;
+ if (sm->workaround && duplicate &&
+ os_memcmp(sm->req_sha1, sm->last_sha1, 20) != 0) {
+ /*
+ * RFC 4137 uses (reqId == lastId) as the only verification for
+ * duplicate EAP requests. However, this misses cases where the
+ * AS is incorrectly using the same id again; and
+ * unfortunately, such implementations exist. Use SHA1 hash as
+ * an extra verification for the packets being duplicate to
+ * workaround these issues.
+ */
+ wpa_printf(MSG_DEBUG, "EAP: AS used the same Id again, but "
+ "EAP packets were not identical");
+ wpa_printf(MSG_DEBUG, "EAP: workaround - assume this is not a "
+ "duplicate packet");
+ duplicate = 0;
+ }
+
+ return duplicate;
+}
+
+
+static int eap_peer_sm_allow_canned(struct eap_sm *sm)
+{
+ struct eap_peer_config *config = eap_get_config(sm);
+
+ return config && config->phase1 &&
+ os_strstr(config->phase1, "allow_canned_success=1");
+}
+
+
+static void eap_peer_sm_step_received(struct eap_sm *sm)
+{
+ int duplicate = eap_peer_req_is_duplicate(sm);
+
+ /*
+ * Two special cases below for LEAP are local additions to work around
+ * odd LEAP behavior (EAP-Success in the middle of authentication and
+ * then swapped roles). Other transitions are based on RFC 4137.
+ */
+ if (sm->rxSuccess && sm->decision != DECISION_FAIL &&
+ (sm->reqId == sm->lastId ||
+ eap_success_workaround(sm, sm->reqId, sm->lastId)))
+ SM_ENTER(EAP, SUCCESS);
+ else if (sm->workaround && sm->lastId == -1 && sm->rxSuccess &&
+ !sm->rxFailure && !sm->rxReq && eap_peer_sm_allow_canned(sm))
+ SM_ENTER(EAP, SUCCESS); /* EAP-Success prior any EAP method */
+ else if (sm->workaround && sm->lastId == -1 && sm->rxFailure &&
+ !sm->rxReq && sm->methodState != METHOD_CONT &&
+ eap_peer_sm_allow_canned(sm))
+ SM_ENTER(EAP, FAILURE); /* EAP-Failure prior any EAP method */
+ else if (sm->workaround && sm->rxSuccess && !sm->rxFailure &&
+ !sm->rxReq && sm->methodState != METHOD_CONT &&
+ eap_peer_sm_allow_canned(sm))
+ SM_ENTER(EAP, SUCCESS); /* EAP-Success after Identity */
+ else if (sm->methodState != METHOD_CONT &&
+ ((sm->rxFailure &&
+ sm->decision != DECISION_UNCOND_SUCC) ||
+ (sm->rxSuccess && sm->decision == DECISION_FAIL &&
+ (sm->selectedMethod != EAP_TYPE_LEAP ||
+ sm->methodState != METHOD_MAY_CONT))) &&
+ (sm->reqId == sm->lastId ||
+ eap_success_workaround(sm, sm->reqId, sm->lastId)))
+ SM_ENTER(EAP, FAILURE);
+ else if (sm->rxReq && duplicate)
+ SM_ENTER(EAP, RETRANSMIT);
+ else if (sm->rxReq && !duplicate &&
+ sm->reqMethod == EAP_TYPE_NOTIFICATION &&
+ sm->allowNotifications)
+ SM_ENTER(EAP, NOTIFICATION);
+ else if (sm->rxReq && !duplicate &&
+ sm->selectedMethod == EAP_TYPE_NONE &&
+ sm->reqMethod == EAP_TYPE_IDENTITY)
+ SM_ENTER(EAP, IDENTITY);
+ else if (sm->rxReq && !duplicate &&
+ sm->selectedMethod == EAP_TYPE_NONE &&
+ sm->reqMethod != EAP_TYPE_IDENTITY &&
+ sm->reqMethod != EAP_TYPE_NOTIFICATION)
+ SM_ENTER(EAP, GET_METHOD);
+ else if (sm->rxReq && !duplicate &&
+ sm->reqMethod == sm->selectedMethod &&
+ sm->methodState != METHOD_DONE)
+ SM_ENTER(EAP, METHOD);
+ else if (sm->selectedMethod == EAP_TYPE_LEAP &&
+ (sm->rxSuccess || sm->rxResp))
+ SM_ENTER(EAP, METHOD);
+ else if (sm->reauthInit)
+ SM_ENTER(EAP, SEND_RESPONSE);
+ else
+ SM_ENTER(EAP, DISCARD);
+}
+
+
+static void eap_peer_sm_step_local(struct eap_sm *sm)
+{
+ switch (sm->EAP_state) {
+ case EAP_INITIALIZE:
+ SM_ENTER(EAP, IDLE);
+ break;
+ case EAP_DISABLED:
+ if (eapol_get_bool(sm, EAPOL_portEnabled) &&
+ !sm->force_disabled)
+ SM_ENTER(EAP, INITIALIZE);
+ break;
+ case EAP_IDLE:
+ eap_peer_sm_step_idle(sm);
+ break;
+ case EAP_RECEIVED:
+ eap_peer_sm_step_received(sm);
+ break;
+ case EAP_GET_METHOD:
+ if (sm->selectedMethod == sm->reqMethod)
+ SM_ENTER(EAP, METHOD);
+ else
+ SM_ENTER(EAP, SEND_RESPONSE);
+ break;
+ case EAP_METHOD:
+ /*
+ * Note: RFC 4137 uses methodState == DONE && decision == FAIL
+ * as the condition. eapRespData == NULL here is used to allow
+ * final EAP method response to be sent without having to change
+ * all methods to either use methodState MAY_CONT or leaving
+ * decision to something else than FAIL in cases where the only
+ * expected response is EAP-Failure.
+ */
+ if (sm->ignore)
+ SM_ENTER(EAP, DISCARD);
+ else if (sm->methodState == METHOD_DONE &&
+ sm->decision == DECISION_FAIL && !sm->eapRespData)
+ SM_ENTER(EAP, FAILURE);
+ else
+ SM_ENTER(EAP, SEND_RESPONSE);
+ break;
+ case EAP_SEND_RESPONSE:
+ SM_ENTER(EAP, IDLE);
+ break;
+ case EAP_DISCARD:
+ SM_ENTER(EAP, IDLE);
+ break;
+ case EAP_IDENTITY:
+ SM_ENTER(EAP, SEND_RESPONSE);
+ break;
+ case EAP_NOTIFICATION:
+ SM_ENTER(EAP, SEND_RESPONSE);
+ break;
+ case EAP_RETRANSMIT:
+ SM_ENTER(EAP, SEND_RESPONSE);
+ break;
+ case EAP_SUCCESS:
+ break;
+ case EAP_FAILURE:
+ break;
+ }
+}
+
+
+SM_STEP(EAP)
+{
+ /* Global transitions */
+ if (eapol_get_bool(sm, EAPOL_eapRestart) &&
+ eapol_get_bool(sm, EAPOL_portEnabled))
+ SM_ENTER_GLOBAL(EAP, INITIALIZE);
+ else if (!eapol_get_bool(sm, EAPOL_portEnabled) || sm->force_disabled)
+ SM_ENTER_GLOBAL(EAP, DISABLED);
+ else if (sm->num_rounds > EAP_MAX_AUTH_ROUNDS) {
+ /* RFC 4137 does not place any limit on number of EAP messages
+ * in an authentication session. However, some error cases have
+ * ended up in a state were EAP messages were sent between the
+ * peer and server in a loop (e.g., TLS ACK frame in both
+ * direction). Since this is quite undesired outcome, limit the
+ * total number of EAP round-trips and abort authentication if
+ * this limit is exceeded.
+ */
+ if (sm->num_rounds == EAP_MAX_AUTH_ROUNDS + 1) {
+ wpa_msg(sm->msg_ctx, MSG_INFO, "EAP: more than %d "
+ "authentication rounds - abort",
+ EAP_MAX_AUTH_ROUNDS);
+ sm->num_rounds++;
+ SM_ENTER_GLOBAL(EAP, FAILURE);
+ }
+ } else {
+ /* Local transitions */
+ eap_peer_sm_step_local(sm);
+ }
+}
+
+
+static Boolean eap_sm_allowMethod(struct eap_sm *sm, int vendor,
+ EapType method)
+{
+ if (!eap_allowed_method(sm, vendor, method)) {
+ wpa_printf(MSG_DEBUG, "EAP: configuration does not allow: "
+ "vendor %u method %u", vendor, method);
+ return FALSE;
+ }
+ if (eap_peer_get_eap_method(vendor, method))
+ return TRUE;
+ wpa_printf(MSG_DEBUG, "EAP: not included in build: "
+ "vendor %u method %u", vendor, method);
+ return FALSE;
+}
+
+
+static struct wpabuf * eap_sm_build_expanded_nak(
+ struct eap_sm *sm, int id, const struct eap_method *methods,
+ size_t count)
+{
+ struct wpabuf *resp;
+ int found = 0;
+ const struct eap_method *m;
+
+ wpa_printf(MSG_DEBUG, "EAP: Building expanded EAP-Nak");
+
+ /* RFC 3748 - 5.3.2: 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);
+
+ for (m = methods; m; m = m->next) {
+ if (sm->reqVendor == m->vendor &&
+ sm->reqVendorMethod == m->method)
+ continue; /* do not allow the current method again */
+ if (eap_allowed_method(sm, m->vendor, m->method)) {
+ wpa_printf(MSG_DEBUG, "EAP: allowed type: "
+ "vendor=%u method=%u",
+ m->vendor, m->method);
+ wpabuf_put_u8(resp, EAP_TYPE_EXPANDED);
+ wpabuf_put_be24(resp, m->vendor);
+ wpabuf_put_be32(resp, m->method);
+
+ found++;
+ }
+ }
+ if (!found) {
+ wpa_printf(MSG_DEBUG, "EAP: no more allowed methods");
+ wpabuf_put_u8(resp, EAP_TYPE_EXPANDED);
+ wpabuf_put_be24(resp, EAP_VENDOR_IETF);
+ wpabuf_put_be32(resp, EAP_TYPE_NONE);
+ }
+
+ eap_update_len(resp);
+
+ return resp;
+}
+
+
+static struct wpabuf * eap_sm_buildNak(struct eap_sm *sm, int id)
+{
+ struct wpabuf *resp;
+ u8 *start;
+ int found = 0, expanded_found = 0;
+ size_t count;
+ const struct eap_method *methods, *m;
+
+ wpa_printf(MSG_DEBUG, "EAP: Building EAP-Nak (requested type %u "
+ "vendor=%u method=%u not allowed)", sm->reqMethod,
+ sm->reqVendor, sm->reqVendorMethod);
+ methods = eap_peer_get_methods(&count);
+ if (methods == NULL)
+ return NULL;
+ if (sm->reqMethod == EAP_TYPE_EXPANDED)
+ return eap_sm_build_expanded_nak(sm, id, methods, count);
+
+ /* RFC 3748 - 5.3.1: Legacy Nak */
+ 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;
+
+ start = wpabuf_put(resp, 0);
+ for (m = methods; m; m = m->next) {
+ if (m->vendor == EAP_VENDOR_IETF && m->method == sm->reqMethod)
+ continue; /* do not allow the current method again */
+ if (eap_allowed_method(sm, m->vendor, m->method)) {
+ if (m->vendor != EAP_VENDOR_IETF) {
+ if (expanded_found)
+ continue;
+ expanded_found = 1;
+ wpabuf_put_u8(resp, EAP_TYPE_EXPANDED);
+ } else
+ wpabuf_put_u8(resp, m->method);
+ found++;
+ }
+ }
+ if (!found)
+ wpabuf_put_u8(resp, EAP_TYPE_NONE);
+ wpa_hexdump(MSG_DEBUG, "EAP: allowed methods", start, found);
+
+ eap_update_len(resp);
+
+ return resp;
+}
+
+
+static void eap_sm_processIdentity(struct eap_sm *sm, const struct wpabuf *req)
+{
+ const u8 *pos;
+ size_t msg_len;
+
+ wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_STARTED
+ "EAP authentication started");
+ eap_notify_status(sm, "started", "");
+
+ pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_IDENTITY, req,
+ &msg_len);
+ if (pos == NULL)
+ return;
+
+ /*
+ * RFC 3748 - 5.1: Identity
+ * Data field may contain a displayable message in UTF-8. If this
+ * includes NUL-character, only the data before that should be
+ * displayed. Some EAP implementasitons may piggy-back additional
+ * options after the NUL.
+ */
+ /* TODO: could save displayable message so that it can be shown to the
+ * user in case of interaction is required */
+ wpa_hexdump_ascii(MSG_DEBUG, "EAP: EAP-Request Identity data",
+ pos, msg_len);
+}
+
+
+#ifdef PCSC_FUNCS
+
+/*
+ * Rules for figuring out MNC length based on IMSI for SIM cards that do not
+ * include MNC length field.
+ */
+static int mnc_len_from_imsi(const char *imsi)
+{
+ char mcc_str[4];
+ unsigned int mcc;
+
+ os_memcpy(mcc_str, imsi, 3);
+ mcc_str[3] = '\0';
+ mcc = atoi(mcc_str);
+
+ if (mcc == 228)
+ return 2; /* Networks in Switzerland use 2-digit MNC */
+ if (mcc == 244)
+ return 2; /* Networks in Finland use 2-digit MNC */
+
+ return -1;
+}
+
+
+static int eap_sm_append_3gpp_realm(struct eap_sm *sm, char *imsi,
+ size_t max_len, size_t *imsi_len)
+{
+ int mnc_len;
+ char *pos, mnc[4];
+
+ if (*imsi_len + 36 > max_len) {
+ wpa_printf(MSG_WARNING, "No room for realm in IMSI buffer");
+ return -1;
+ }
+
+ /* MNC (2 or 3 digits) */
+ mnc_len = scard_get_mnc_len(sm->scard_ctx);
+ if (mnc_len < 0)
+ mnc_len = mnc_len_from_imsi(imsi);
+ if (mnc_len < 0) {
+ wpa_printf(MSG_INFO, "Failed to get MNC length from (U)SIM "
+ "assuming 3");
+ mnc_len = 3;
+ }
+
+ if (mnc_len == 2) {
+ mnc[0] = '0';
+ mnc[1] = imsi[3];
+ mnc[2] = imsi[4];
+ } else if (mnc_len == 3) {
+ mnc[0] = imsi[3];
+ mnc[1] = imsi[4];
+ mnc[2] = imsi[5];
+ }
+ mnc[3] = '\0';
+
+ pos = imsi + *imsi_len;
+ pos += os_snprintf(pos, imsi + max_len - pos,
+ "@wlan.mnc%s.mcc%c%c%c.3gppnetwork.org",
+ mnc, imsi[0], imsi[1], imsi[2]);
+ *imsi_len = pos - imsi;
+
+ return 0;
+}
+
+
+static int eap_sm_imsi_identity(struct eap_sm *sm,
+ struct eap_peer_config *conf)
+{
+ enum { EAP_SM_SIM, EAP_SM_AKA, EAP_SM_AKA_PRIME } method = EAP_SM_SIM;
+ char imsi[100];
+ size_t imsi_len;
+ struct eap_method_type *m = conf->eap_methods;
+ int i;
+
+ imsi_len = sizeof(imsi);
+ if (scard_get_imsi(sm->scard_ctx, imsi, &imsi_len)) {
+ wpa_printf(MSG_WARNING, "Failed to get IMSI from SIM");
+ return -1;
+ }
+
+ wpa_hexdump_ascii(MSG_DEBUG, "IMSI", (u8 *) imsi, imsi_len);
+
+ if (imsi_len < 7) {
+ wpa_printf(MSG_WARNING, "Too short IMSI for SIM identity");
+ return -1;
+ }
+
+ if (eap_sm_append_3gpp_realm(sm, imsi, sizeof(imsi), &imsi_len) < 0) {
+ wpa_printf(MSG_WARNING, "Could not add realm to SIM identity");
+ return -1;
+ }
+ wpa_hexdump_ascii(MSG_DEBUG, "IMSI + realm", (u8 *) imsi, imsi_len);
+
+ for (i = 0; m && (m[i].vendor != EAP_VENDOR_IETF ||
+ m[i].method != EAP_TYPE_NONE); i++) {
+ if (m[i].vendor == EAP_VENDOR_IETF &&
+ m[i].method == EAP_TYPE_AKA_PRIME) {
+ method = EAP_SM_AKA_PRIME;
+ break;
+ }
+
+ if (m[i].vendor == EAP_VENDOR_IETF &&
+ m[i].method == EAP_TYPE_AKA) {
+ method = EAP_SM_AKA;
+ break;
+ }
+ }
+
+ os_free(conf->identity);
+ conf->identity = os_malloc(1 + imsi_len);
+ if (conf->identity == NULL) {
+ wpa_printf(MSG_WARNING, "Failed to allocate buffer for "
+ "IMSI-based identity");
+ return -1;
+ }
+
+ switch (method) {
+ case EAP_SM_SIM:
+ conf->identity[0] = '1';
+ break;
+ case EAP_SM_AKA:
+ conf->identity[0] = '0';
+ break;
+ case EAP_SM_AKA_PRIME:
+ conf->identity[0] = '6';
+ break;
+ }
+ os_memcpy(conf->identity + 1, imsi, imsi_len);
+ conf->identity_len = 1 + imsi_len;
+
+ return 0;
+}
+
+#endif /* PCSC_FUNCS */
+
+
+static int eap_sm_set_scard_pin(struct eap_sm *sm,
+ struct eap_peer_config *conf)
+{
+#ifdef PCSC_FUNCS
+ if (scard_set_pin(sm->scard_ctx, conf->pin)) {
+ /*
+ * Make sure the same PIN is not tried again in order to avoid
+ * blocking SIM.
+ */
+ os_free(conf->pin);
+ conf->pin = NULL;
+
+ wpa_printf(MSG_WARNING, "PIN validation failed");
+ eap_sm_request_pin(sm);
+ return -1;
+ }
+ return 0;
+#else /* PCSC_FUNCS */
+ return -1;
+#endif /* PCSC_FUNCS */
+}
+
+static int eap_sm_get_scard_identity(struct eap_sm *sm,
+ struct eap_peer_config *conf)
+{
+#ifdef PCSC_FUNCS
+ if (eap_sm_set_scard_pin(sm, conf))
+ return -1;
+
+ return eap_sm_imsi_identity(sm, conf);
+#else /* PCSC_FUNCS */
+ return -1;
+#endif /* PCSC_FUNCS */
+}
+
+
+/**
+ * eap_sm_buildIdentity - Build EAP-Identity/Response for the current network
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @id: EAP identifier for the packet
+ * @encrypted: Whether the packet is for encrypted tunnel (EAP phase 2)
+ * Returns: Pointer to the allocated EAP-Identity/Response packet or %NULL on
+ * failure
+ *
+ * This function allocates and builds an EAP-Identity/Response packet for the
+ * current network. The caller is responsible for freeing the returned data.
+ */
+struct wpabuf * eap_sm_buildIdentity(struct eap_sm *sm, int id, int encrypted)
+{
+ struct eap_peer_config *config = eap_get_config(sm);
+ struct wpabuf *resp;
+ const u8 *identity;
+ size_t identity_len;
+
+ if (config == NULL) {
+ wpa_printf(MSG_WARNING, "EAP: buildIdentity: configuration "
+ "was not available");
+ return NULL;
+ }
+
+ if (sm->m && sm->m->get_identity &&
+ (identity = sm->m->get_identity(sm, sm->eap_method_priv,
+ &identity_len)) != NULL) {
+ wpa_hexdump_ascii(MSG_DEBUG, "EAP: using method re-auth "
+ "identity", identity, identity_len);
+ } else if (!encrypted && config->anonymous_identity) {
+ identity = config->anonymous_identity;
+ identity_len = config->anonymous_identity_len;
+ wpa_hexdump_ascii(MSG_DEBUG, "EAP: using anonymous identity",
+ identity, identity_len);
+ } else {
+ identity = config->identity;
+ identity_len = config->identity_len;
+ wpa_hexdump_ascii(MSG_DEBUG, "EAP: using real identity",
+ identity, identity_len);
+ }
+
+ if (identity == NULL) {
+ wpa_printf(MSG_WARNING, "EAP: buildIdentity: identity "
+ "configuration was not available");
+ if (config->pcsc) {
+ if (eap_sm_get_scard_identity(sm, config) < 0)
+ return NULL;
+ identity = config->identity;
+ identity_len = config->identity_len;
+ wpa_hexdump_ascii(MSG_DEBUG, "permanent identity from "
+ "IMSI", identity, identity_len);
+ } else {
+ eap_sm_request_identity(sm);
+ return NULL;
+ }
+ } else if (config->pcsc) {
+ if (eap_sm_set_scard_pin(sm, config) < 0)
+ return NULL;
+ }
+
+ resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_IDENTITY, identity_len,
+ EAP_CODE_RESPONSE, id);
+ if (resp == NULL)
+ return NULL;
+
+ wpabuf_put_data(resp, identity, identity_len);
+
+ return resp;
+}
+
+
+static void eap_sm_processNotify(struct eap_sm *sm, const struct wpabuf *req)
+{
+ const u8 *pos;
+ char *msg;
+ size_t i, msg_len;
+
+ pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_NOTIFICATION, req,
+ &msg_len);
+ if (pos == NULL)
+ return;
+ wpa_hexdump_ascii(MSG_DEBUG, "EAP: EAP-Request Notification data",
+ pos, msg_len);
+
+ msg = os_malloc(msg_len + 1);
+ if (msg == NULL)
+ return;
+ for (i = 0; i < msg_len; i++)
+ msg[i] = isprint(pos[i]) ? (char) pos[i] : '_';
+ msg[msg_len] = '\0';
+ wpa_msg(sm->msg_ctx, MSG_INFO, "%s%s",
+ WPA_EVENT_EAP_NOTIFICATION, msg);
+ os_free(msg);
+}
+
+
+static struct wpabuf * eap_sm_buildNotify(int id)
+{
+ struct wpabuf *resp;
+
+ wpa_printf(MSG_DEBUG, "EAP: Generating EAP-Response Notification");
+ resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_NOTIFICATION, 0,
+ EAP_CODE_RESPONSE, id);
+ if (resp == NULL)
+ return NULL;
+
+ return resp;
+}
+
+
+static void eap_peer_initiate(struct eap_sm *sm, const struct eap_hdr *hdr,
+ size_t len)
+{
+#ifdef CONFIG_ERP
+ const u8 *pos = (const u8 *) (hdr + 1);
+ const u8 *end = ((const u8 *) hdr) + len;
+ struct erp_tlvs parse;
+
+ if (len < sizeof(*hdr) + 1) {
+ wpa_printf(MSG_DEBUG, "EAP: Ignored too short EAP-Initiate");
+ return;
+ }
+
+ if (*pos != EAP_ERP_TYPE_REAUTH_START) {
+ wpa_printf(MSG_DEBUG,
+ "EAP: Ignored unexpected EAP-Initiate Type=%u",
+ *pos);
+ return;
+ }
+
+ pos++;
+ if (pos >= end) {
+ wpa_printf(MSG_DEBUG,
+ "EAP: Too short EAP-Initiate/Re-auth-Start");
+ return;
+ }
+ pos++; /* Reserved */
+ wpa_hexdump(MSG_DEBUG, "EAP: EAP-Initiate/Re-auth-Start TVs/TLVs",
+ pos, end - pos);
+
+ if (erp_parse_tlvs(pos, end, &parse, 0) < 0)
+ goto invalid;
+
+ if (parse.domain) {
+ wpa_hexdump_ascii(MSG_DEBUG,
+ "EAP: EAP-Initiate/Re-auth-Start - Domain name",
+ parse.domain, parse.domain_len);
+ /* TODO: Derivation of domain specific keys for local ER */
+ }
+
+ if (eap_peer_erp_reauth_start(sm, hdr, len) == 0)
+ return;
+
+invalid:
+#endif /* CONFIG_ERP */
+ wpa_printf(MSG_DEBUG,
+ "EAP: EAP-Initiate/Re-auth-Start - No suitable ERP keys available - try to start full EAP authentication");
+ eapol_set_bool(sm, EAPOL_eapTriggerStart, TRUE);
+}
+
+
+static void eap_peer_finish(struct eap_sm *sm, const struct eap_hdr *hdr,
+ size_t len)
+{
+#ifdef CONFIG_ERP
+ const u8 *pos = (const u8 *) (hdr + 1);
+ const u8 *end = ((const u8 *) hdr) + len;
+ const u8 *start;
+ struct erp_tlvs parse;
+ u8 flags;
+ u16 seq;
+ u8 hash[SHA256_MAC_LEN];
+ size_t hash_len;
+ struct eap_erp_key *erp;
+ int max_len;
+ char nai[254];
+ u8 seed[4];
+ int auth_tag_ok = 0;
+
+ if (len < sizeof(*hdr) + 1) {
+ wpa_printf(MSG_DEBUG, "EAP: Ignored too short EAP-Finish");
+ return;
+ }
+
+ if (*pos != EAP_ERP_TYPE_REAUTH) {
+ wpa_printf(MSG_DEBUG,
+ "EAP: Ignored unexpected EAP-Finish Type=%u", *pos);
+ return;
+ }
+
+ if (len < sizeof(*hdr) + 4) {
+ wpa_printf(MSG_DEBUG,
+ "EAP: Ignored too short EAP-Finish/Re-auth");
+ return;
+ }
+
+ pos++;
+ flags = *pos++;
+ seq = WPA_GET_BE16(pos);
+ pos += 2;
+ wpa_printf(MSG_DEBUG, "EAP: Flags=0x%x SEQ=%u", flags, seq);
+
+ if (seq != sm->erp_seq) {
+ wpa_printf(MSG_DEBUG,
+ "EAP: Unexpected EAP-Finish/Re-auth SEQ=%u", seq);
+ return;
+ }
+
+ /*
+ * Parse TVs/TLVs. Since we do not yet know the length of the
+ * Authentication Tag, stop parsing if an unknown TV/TLV is seen and
+ * just try to find the keyName-NAI first so that we can check the
+ * Authentication Tag.
+ */
+ if (erp_parse_tlvs(pos, end, &parse, 1) < 0)
+ return;
+
+ if (!parse.keyname) {
+ wpa_printf(MSG_DEBUG,
+ "EAP: No keyName-NAI in EAP-Finish/Re-auth Packet");
+ return;
+ }
+
+ wpa_hexdump_ascii(MSG_DEBUG, "EAP: EAP-Finish/Re-auth - keyName-NAI",
+ parse.keyname, parse.keyname_len);
+ if (parse.keyname_len > 253) {
+ wpa_printf(MSG_DEBUG,
+ "EAP: Too long keyName-NAI in EAP-Finish/Re-auth");
+ return;
+ }
+ os_memcpy(nai, parse.keyname, parse.keyname_len);
+ nai[parse.keyname_len] = '\0';
+
+ erp = eap_erp_get_key_nai(sm, nai);
+ if (!erp) {
+ wpa_printf(MSG_DEBUG, "EAP: No matching ERP key found for %s",
+ nai);
+ return;
+ }
+
+ /* Is there enough room for Cryptosuite and Authentication Tag? */
+ start = parse.keyname + parse.keyname_len;
+ max_len = end - start;
+ hash_len = 16;
+ if (max_len < 1 + (int) hash_len) {
+ wpa_printf(MSG_DEBUG,
+ "EAP: Not enough room for Authentication Tag");
+ if (flags & 0x80)
+ goto no_auth_tag;
+ return;
+ }
+ if (end[-17] != EAP_ERP_CS_HMAC_SHA256_128) {
+ wpa_printf(MSG_DEBUG, "EAP: Different Cryptosuite used");
+ if (flags & 0x80)
+ goto no_auth_tag;
+ return;
+ }
+
+ if (hmac_sha256(erp->rIK, erp->rIK_len, (const u8 *) hdr,
+ end - ((const u8 *) hdr) - hash_len, hash) < 0)
+ return;
+ if (os_memcmp(end - hash_len, hash, hash_len) != 0) {
+ wpa_printf(MSG_DEBUG,
+ "EAP: Authentication Tag mismatch");
+ return;
+ }
+ auth_tag_ok = 1;
+ end -= 1 + hash_len;
+
+no_auth_tag:
+ /*
+ * Parse TVs/TLVs again now that we know the exact part of the buffer
+ * that contains them.
+ */
+ wpa_hexdump(MSG_DEBUG, "EAP: EAP-Finish/Re-Auth TVs/TLVs",
+ pos, end - pos);
+ if (erp_parse_tlvs(pos, end, &parse, 0) < 0)
+ return;
+
+ if (flags & 0x80 || !auth_tag_ok) {
+ wpa_printf(MSG_DEBUG,
+ "EAP: EAP-Finish/Re-auth indicated failure");
+ eapol_set_bool(sm, EAPOL_eapFail, TRUE);
+ eapol_set_bool(sm, EAPOL_eapReq, FALSE);
+ eapol_set_bool(sm, EAPOL_eapNoResp, TRUE);
+ wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_FAILURE
+ "EAP authentication failed");
+ sm->prev_failure = 1;
+ wpa_printf(MSG_DEBUG,
+ "EAP: Drop ERP key to try full authentication on next attempt");
+ eap_peer_erp_free_key(erp);
+ return;
+ }
+
+ eap_sm_free_key(sm);
+ sm->eapKeyDataLen = 0;
+ sm->eapKeyData = os_malloc(erp->rRK_len);
+ if (!sm->eapKeyData)
+ return;
+ sm->eapKeyDataLen = erp->rRK_len;
+
+ WPA_PUT_BE16(seed, seq);
+ WPA_PUT_BE16(&seed[2], erp->rRK_len);
+ if (hmac_sha256_kdf(erp->rRK, erp->rRK_len,
+ "Re-authentication Master Session Key@ietf.org",
+ seed, sizeof(seed),
+ sm->eapKeyData, erp->rRK_len) < 0) {
+ wpa_printf(MSG_DEBUG, "EAP: Could not derive rMSK for ERP");
+ eap_sm_free_key(sm);
+ return;
+ }
+ wpa_hexdump_key(MSG_DEBUG, "EAP: ERP rMSK",
+ sm->eapKeyData, sm->eapKeyDataLen);
+ sm->eapKeyAvailable = TRUE;
+ eapol_set_bool(sm, EAPOL_eapSuccess, TRUE);
+ eapol_set_bool(sm, EAPOL_eapReq, FALSE);
+ eapol_set_bool(sm, EAPOL_eapNoResp, TRUE);
+ wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_SUCCESS
+ "EAP re-authentication completed successfully");
+#endif /* CONFIG_ERP */
+}
+
+
+static void eap_sm_parseEapReq(struct eap_sm *sm, const struct wpabuf *req)
+{
+ const struct eap_hdr *hdr;
+ size_t plen;
+ const u8 *pos;
+
+ sm->rxReq = sm->rxResp = sm->rxSuccess = sm->rxFailure = FALSE;
+ sm->reqId = 0;
+ sm->reqMethod = EAP_TYPE_NONE;
+ sm->reqVendor = EAP_VENDOR_IETF;
+ sm->reqVendorMethod = EAP_TYPE_NONE;
+
+ if (req == NULL || wpabuf_len(req) < sizeof(*hdr))
+ return;
+
+ hdr = wpabuf_head(req);
+ plen = be_to_host16(hdr->length);
+ if (plen > wpabuf_len(req)) {
+ wpa_printf(MSG_DEBUG, "EAP: Ignored truncated EAP-Packet "
+ "(len=%lu plen=%lu)",
+ (unsigned long) wpabuf_len(req),
+ (unsigned long) plen);
+ return;
+ }
+
+ sm->reqId = hdr->identifier;
+
+ if (sm->workaround) {
+ const u8 *addr[1];
+ addr[0] = wpabuf_head(req);
+ sha1_vector(1, addr, &plen, sm->req_sha1);
+ }
+
+ switch (hdr->code) {
+ case EAP_CODE_REQUEST:
+ if (plen < sizeof(*hdr) + 1) {
+ wpa_printf(MSG_DEBUG, "EAP: Too short EAP-Request - "
+ "no Type field");
+ return;
+ }
+ sm->rxReq = TRUE;
+ pos = (const u8 *) (hdr + 1);
+ sm->reqMethod = *pos++;
+ if (sm->reqMethod == EAP_TYPE_EXPANDED) {
+ if (plen < sizeof(*hdr) + 8) {
+ wpa_printf(MSG_DEBUG, "EAP: Ignored truncated "
+ "expanded EAP-Packet (plen=%lu)",
+ (unsigned long) plen);
+ return;
+ }
+ sm->reqVendor = WPA_GET_BE24(pos);
+ pos += 3;
+ sm->reqVendorMethod = WPA_GET_BE32(pos);
+ }
+ wpa_printf(MSG_DEBUG, "EAP: Received EAP-Request id=%d "
+ "method=%u vendor=%u vendorMethod=%u",
+ sm->reqId, sm->reqMethod, sm->reqVendor,
+ sm->reqVendorMethod);
+ break;
+ case EAP_CODE_RESPONSE:
+ if (sm->selectedMethod == EAP_TYPE_LEAP) {
+ /*
+ * LEAP differs from RFC 4137 by using reversed roles
+ * for mutual authentication and because of this, we
+ * need to accept EAP-Response frames if LEAP is used.
+ */
+ if (plen < sizeof(*hdr) + 1) {
+ wpa_printf(MSG_DEBUG, "EAP: Too short "
+ "EAP-Response - no Type field");
+ return;
+ }
+ sm->rxResp = TRUE;
+ pos = (const u8 *) (hdr + 1);
+ sm->reqMethod = *pos;
+ wpa_printf(MSG_DEBUG, "EAP: Received EAP-Response for "
+ "LEAP method=%d id=%d",
+ sm->reqMethod, sm->reqId);
+ break;
+ }
+ wpa_printf(MSG_DEBUG, "EAP: Ignored EAP-Response");
+ break;
+ case EAP_CODE_SUCCESS:
+ wpa_printf(MSG_DEBUG, "EAP: Received EAP-Success");
+ eap_notify_status(sm, "completion", "success");
+ sm->rxSuccess = TRUE;
+ break;
+ case EAP_CODE_FAILURE:
+ wpa_printf(MSG_DEBUG, "EAP: Received EAP-Failure");
+ eap_notify_status(sm, "completion", "failure");
+ sm->rxFailure = TRUE;
+ break;
+ case EAP_CODE_INITIATE:
+ eap_peer_initiate(sm, hdr, plen);
+ break;
+ case EAP_CODE_FINISH:
+ eap_peer_finish(sm, hdr, plen);
+ break;
+ default:
+ wpa_printf(MSG_DEBUG, "EAP: Ignored EAP-Packet with unknown "
+ "code %d", hdr->code);
+ break;
+ }
+}
+
+
+static void eap_peer_sm_tls_event(void *ctx, enum tls_event ev,
+ union tls_event_data *data)
+{
+ struct eap_sm *sm = ctx;
+ char *hash_hex = NULL;
+
+ switch (ev) {
+ case TLS_CERT_CHAIN_SUCCESS:
+ eap_notify_status(sm, "remote certificate verification",
+ "success");
+ break;
+ case TLS_CERT_CHAIN_FAILURE:
+ wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_TLS_CERT_ERROR
+ "reason=%d depth=%d subject='%s' err='%s'",
+ data->cert_fail.reason,
+ data->cert_fail.depth,
+ data->cert_fail.subject,
+ data->cert_fail.reason_txt);
+ eap_notify_status(sm, "remote certificate verification",
+ data->cert_fail.reason_txt);
+ break;
+ case TLS_PEER_CERTIFICATE:
+ if (!sm->eapol_cb->notify_cert)
+ break;
+
+ if (data->peer_cert.hash) {
+ size_t len = data->peer_cert.hash_len * 2 + 1;
+ hash_hex = os_malloc(len);
+ if (hash_hex) {
+ wpa_snprintf_hex(hash_hex, len,
+ data->peer_cert.hash,
+ data->peer_cert.hash_len);
+ }
+ }
+
+ sm->eapol_cb->notify_cert(sm->eapol_ctx,
+ data->peer_cert.depth,
+ data->peer_cert.subject,
+ data->peer_cert.altsubject,
+ data->peer_cert.num_altsubject,
+ hash_hex, data->peer_cert.cert);
+ break;
+ case TLS_ALERT:
+ if (data->alert.is_local)
+ eap_notify_status(sm, "local TLS alert",
+ data->alert.description);
+ else
+ eap_notify_status(sm, "remote TLS alert",
+ data->alert.description);
+ break;
+ }
+
+ os_free(hash_hex);
+}
+
+
+/**
+ * eap_peer_sm_init - Allocate and initialize EAP peer state machine
+ * @eapol_ctx: Context data to be used with eapol_cb calls
+ * @eapol_cb: Pointer to EAPOL callback functions
+ * @msg_ctx: Context data for wpa_msg() calls
+ * @conf: EAP configuration
+ * Returns: Pointer to the allocated EAP state machine or %NULL on failure
+ *
+ * This function allocates and initializes an EAP state machine. In addition,
+ * this initializes TLS library for the new EAP state machine. eapol_cb pointer
+ * will be in use until eap_peer_sm_deinit() is used to deinitialize this EAP
+ * state machine. Consequently, the caller must make sure that this data
+ * structure remains alive while the EAP state machine is active.
+ */
+struct eap_sm * eap_peer_sm_init(void *eapol_ctx,
+ const struct eapol_callbacks *eapol_cb,
+ void *msg_ctx, struct eap_config *conf)
+{
+ struct eap_sm *sm;
+ struct tls_config tlsconf;
+
+ sm = os_zalloc(sizeof(*sm));
+ if (sm == NULL)
+ return NULL;
+ sm->eapol_ctx = eapol_ctx;
+ sm->eapol_cb = eapol_cb;
+ sm->msg_ctx = msg_ctx;
+ sm->ClientTimeout = EAP_CLIENT_TIMEOUT_DEFAULT;
+ sm->wps = conf->wps;
+ dl_list_init(&sm->erp_keys);
+
+ os_memset(&tlsconf, 0, sizeof(tlsconf));
+ tlsconf.opensc_engine_path = conf->opensc_engine_path;
+ tlsconf.pkcs11_engine_path = conf->pkcs11_engine_path;
+ tlsconf.pkcs11_module_path = conf->pkcs11_module_path;
+ tlsconf.openssl_ciphers = conf->openssl_ciphers;
+#ifdef CONFIG_FIPS
+ tlsconf.fips_mode = 1;
+#endif /* CONFIG_FIPS */
+ tlsconf.event_cb = eap_peer_sm_tls_event;
+ tlsconf.cb_ctx = sm;
+ tlsconf.cert_in_cb = conf->cert_in_cb;
+ sm->ssl_ctx = tls_init(&tlsconf);
+ if (sm->ssl_ctx == NULL) {
+ wpa_printf(MSG_WARNING, "SSL: Failed to initialize TLS "
+ "context.");
+ os_free(sm);
+ return NULL;
+ }
+
+ sm->ssl_ctx2 = tls_init(&tlsconf);
+ if (sm->ssl_ctx2 == NULL) {
+ wpa_printf(MSG_INFO, "SSL: Failed to initialize TLS "
+ "context (2).");
+ /* Run without separate TLS context within TLS tunnel */
+ }
+
+ return sm;
+}
+
+
+/**
+ * eap_peer_sm_deinit - Deinitialize and free an EAP peer state machine
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ *
+ * This function deinitializes EAP state machine and frees all allocated
+ * resources.
+ */
+void eap_peer_sm_deinit(struct eap_sm *sm)
+{
+ if (sm == NULL)
+ return;
+ eap_deinit_prev_method(sm, "EAP deinit");
+ eap_sm_abort(sm);
+ if (sm->ssl_ctx2)
+ tls_deinit(sm->ssl_ctx2);
+ tls_deinit(sm->ssl_ctx);
+ eap_peer_erp_free_keys(sm);
+ os_free(sm);
+}
+
+
+/**
+ * eap_peer_sm_step - Step EAP peer state machine
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * Returns: 1 if EAP state was changed or 0 if not
+ *
+ * This function advances EAP state machine to a new state to match with the
+ * current variables. This should be called whenever variables used by the EAP
+ * state machine have changed.
+ */
+int eap_peer_sm_step(struct eap_sm *sm)
+{
+ int res = 0;
+ do {
+ sm->changed = FALSE;
+ SM_STEP_RUN(EAP);
+ if (sm->changed)
+ res = 1;
+ } while (sm->changed);
+ return res;
+}
+
+
+/**
+ * eap_sm_abort - Abort EAP authentication
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ *
+ * Release system resources that have been allocated for the authentication
+ * session without fully deinitializing the EAP state machine.
+ */
+void eap_sm_abort(struct eap_sm *sm)
+{
+ wpabuf_free(sm->lastRespData);
+ sm->lastRespData = NULL;
+ wpabuf_free(sm->eapRespData);
+ sm->eapRespData = NULL;
+ eap_sm_free_key(sm);
+ os_free(sm->eapSessionId);
+ sm->eapSessionId = NULL;
+
+ /* This is not clearly specified in the EAP statemachines draft, but
+ * it seems necessary to make sure that some of the EAPOL variables get
+ * cleared for the next authentication. */
+ eapol_set_bool(sm, EAPOL_eapSuccess, FALSE);
+}
+
+
+#ifdef CONFIG_CTRL_IFACE
+static const char * eap_sm_state_txt(int state)
+{
+ switch (state) {
+ case EAP_INITIALIZE:
+ return "INITIALIZE";
+ case EAP_DISABLED:
+ return "DISABLED";
+ case EAP_IDLE:
+ return "IDLE";
+ case EAP_RECEIVED:
+ return "RECEIVED";
+ case EAP_GET_METHOD:
+ return "GET_METHOD";
+ case EAP_METHOD:
+ return "METHOD";
+ case EAP_SEND_RESPONSE:
+ return "SEND_RESPONSE";
+ case EAP_DISCARD:
+ return "DISCARD";
+ case EAP_IDENTITY:
+ return "IDENTITY";
+ case EAP_NOTIFICATION:
+ return "NOTIFICATION";
+ case EAP_RETRANSMIT:
+ return "RETRANSMIT";
+ case EAP_SUCCESS:
+ return "SUCCESS";
+ case EAP_FAILURE:
+ return "FAILURE";
+ default:
+ return "UNKNOWN";
+ }
+}
+#endif /* CONFIG_CTRL_IFACE */
+
+
+#if defined(CONFIG_CTRL_IFACE) || !defined(CONFIG_NO_STDOUT_DEBUG)
+static const char * eap_sm_method_state_txt(EapMethodState state)
+{
+ switch (state) {
+ case METHOD_NONE:
+ return "NONE";
+ case METHOD_INIT:
+ return "INIT";
+ case METHOD_CONT:
+ return "CONT";
+ case METHOD_MAY_CONT:
+ return "MAY_CONT";
+ case METHOD_DONE:
+ return "DONE";
+ default:
+ return "UNKNOWN";
+ }
+}
+
+
+static const char * eap_sm_decision_txt(EapDecision decision)
+{
+ switch (decision) {
+ case DECISION_FAIL:
+ return "FAIL";
+ case DECISION_COND_SUCC:
+ return "COND_SUCC";
+ case DECISION_UNCOND_SUCC:
+ return "UNCOND_SUCC";
+ default:
+ return "UNKNOWN";
+ }
+}
+#endif /* CONFIG_CTRL_IFACE || !CONFIG_NO_STDOUT_DEBUG */
+
+
+#ifdef CONFIG_CTRL_IFACE
+
+/**
+ * eap_sm_get_status - Get EAP state machine status
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @buf: Buffer for status information
+ * @buflen: Maximum buffer length
+ * @verbose: Whether to include verbose status information
+ * Returns: Number of bytes written to buf.
+ *
+ * Query EAP state machine for status information. This function fills in a
+ * text area with current status information from the EAPOL state machine. If
+ * the buffer (buf) is not large enough, status information will be truncated
+ * to fit the buffer.
+ */
+int eap_sm_get_status(struct eap_sm *sm, char *buf, size_t buflen, int verbose)
+{
+ int len, ret;
+
+ if (sm == NULL)
+ return 0;
+
+ len = os_snprintf(buf, buflen,
+ "EAP state=%s\n",
+ eap_sm_state_txt(sm->EAP_state));
+ if (os_snprintf_error(buflen, len))
+ return 0;
+
+ if (sm->selectedMethod != EAP_TYPE_NONE) {
+ const char *name;
+ if (sm->m) {
+ name = sm->m->name;
+ } else {
+ const struct eap_method *m =
+ eap_peer_get_eap_method(EAP_VENDOR_IETF,
+ sm->selectedMethod);
+ if (m)
+ name = m->name;
+ else
+ name = "?";
+ }
+ ret = os_snprintf(buf + len, buflen - len,
+ "selectedMethod=%d (EAP-%s)\n",
+ sm->selectedMethod, name);
+ if (os_snprintf_error(buflen - len, ret))
+ return len;
+ len += ret;
+
+ if (sm->m && sm->m->get_status) {
+ len += sm->m->get_status(sm, sm->eap_method_priv,
+ buf + len, buflen - len,
+ verbose);
+ }
+ }
+
+ if (verbose) {
+ ret = os_snprintf(buf + len, buflen - len,
+ "reqMethod=%d\n"
+ "methodState=%s\n"
+ "decision=%s\n"
+ "ClientTimeout=%d\n",
+ sm->reqMethod,
+ eap_sm_method_state_txt(sm->methodState),
+ eap_sm_decision_txt(sm->decision),
+ sm->ClientTimeout);
+ if (os_snprintf_error(buflen - len, ret))
+ return len;
+ len += ret;
+ }
+
+ return len;
+}
+#endif /* CONFIG_CTRL_IFACE */
+
+
+#if defined(CONFIG_CTRL_IFACE) || !defined(CONFIG_NO_STDOUT_DEBUG)
+static void eap_sm_request(struct eap_sm *sm, enum wpa_ctrl_req_type field,
+ const char *msg, size_t msglen)
+{
+ struct eap_peer_config *config;
+ const char *txt = NULL;
+ char *tmp;
+
+ if (sm == NULL)
+ return;
+ config = eap_get_config(sm);
+ if (config == NULL)
+ return;
+
+ switch (field) {
+ case WPA_CTRL_REQ_EAP_IDENTITY:
+ config->pending_req_identity++;
+ break;
+ case WPA_CTRL_REQ_EAP_PASSWORD:
+ config->pending_req_password++;
+ break;
+ case WPA_CTRL_REQ_EAP_NEW_PASSWORD:
+ config->pending_req_new_password++;
+ break;
+ case WPA_CTRL_REQ_EAP_PIN:
+ config->pending_req_pin++;
+ break;
+ case WPA_CTRL_REQ_EAP_OTP:
+ if (msg) {
+ tmp = os_malloc(msglen + 3);
+ if (tmp == NULL)
+ return;
+ tmp[0] = '[';
+ os_memcpy(tmp + 1, msg, msglen);
+ tmp[msglen + 1] = ']';
+ tmp[msglen + 2] = '\0';
+ txt = tmp;
+ os_free(config->pending_req_otp);
+ config->pending_req_otp = tmp;
+ config->pending_req_otp_len = msglen + 3;
+ } else {
+ if (config->pending_req_otp == NULL)
+ return;
+ txt = config->pending_req_otp;
+ }
+ break;
+ case WPA_CTRL_REQ_EAP_PASSPHRASE:
+ config->pending_req_passphrase++;
+ break;
+ case WPA_CTRL_REQ_SIM:
+ txt = msg;
+ break;
+ default:
+ return;
+ }
+
+ if (sm->eapol_cb->eap_param_needed)
+ sm->eapol_cb->eap_param_needed(sm->eapol_ctx, field, txt);
+}
+#else /* CONFIG_CTRL_IFACE || !CONFIG_NO_STDOUT_DEBUG */
+#define eap_sm_request(sm, type, msg, msglen) do { } while (0)
+#endif /* CONFIG_CTRL_IFACE || !CONFIG_NO_STDOUT_DEBUG */
+
+const char * eap_sm_get_method_name(struct eap_sm *sm)
+{
+ if (sm->m == NULL)
+ return "UNKNOWN";
+ return sm->m->name;
+}
+
+
+/**
+ * eap_sm_request_identity - Request identity from user (ctrl_iface)
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ *
+ * EAP methods can call this function to request identity information for the
+ * current network. This is normally called when the identity is not included
+ * in the network configuration. The request will be sent to monitor programs
+ * through the control interface.
+ */
+void eap_sm_request_identity(struct eap_sm *sm)
+{
+ eap_sm_request(sm, WPA_CTRL_REQ_EAP_IDENTITY, NULL, 0);
+}
+
+
+/**
+ * eap_sm_request_password - Request password from user (ctrl_iface)
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ *
+ * EAP methods can call this function to request password information for the
+ * current network. This is normally called when the password is not included
+ * in the network configuration. The request will be sent to monitor programs
+ * through the control interface.
+ */
+void eap_sm_request_password(struct eap_sm *sm)
+{
+ eap_sm_request(sm, WPA_CTRL_REQ_EAP_PASSWORD, NULL, 0);
+}
+
+
+/**
+ * eap_sm_request_new_password - Request new password from user (ctrl_iface)
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ *
+ * EAP methods can call this function to request new password information for
+ * the current network. This is normally called when the EAP method indicates
+ * that the current password has expired and password change is required. The
+ * request will be sent to monitor programs through the control interface.
+ */
+void eap_sm_request_new_password(struct eap_sm *sm)
+{
+ eap_sm_request(sm, WPA_CTRL_REQ_EAP_NEW_PASSWORD, NULL, 0);
+}
+
+
+/**
+ * eap_sm_request_pin - Request SIM or smart card PIN from user (ctrl_iface)
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ *
+ * EAP methods can call this function to request SIM or smart card PIN
+ * information for the current network. This is normally called when the PIN is
+ * not included in the network configuration. The request will be sent to
+ * monitor programs through the control interface.
+ */
+void eap_sm_request_pin(struct eap_sm *sm)
+{
+ eap_sm_request(sm, WPA_CTRL_REQ_EAP_PIN, NULL, 0);
+}
+
+
+/**
+ * eap_sm_request_otp - Request one time password from user (ctrl_iface)
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @msg: Message to be displayed to the user when asking for OTP
+ * @msg_len: Length of the user displayable message
+ *
+ * EAP methods can call this function to request open time password (OTP) for
+ * the current network. The request will be sent to monitor programs through
+ * the control interface.
+ */
+void eap_sm_request_otp(struct eap_sm *sm, const char *msg, size_t msg_len)
+{
+ eap_sm_request(sm, WPA_CTRL_REQ_EAP_OTP, msg, msg_len);
+}
+
+
+/**
+ * eap_sm_request_passphrase - Request passphrase from user (ctrl_iface)
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ *
+ * EAP methods can call this function to request passphrase for a private key
+ * for the current network. This is normally called when the passphrase is not
+ * included in the network configuration. The request will be sent to monitor
+ * programs through the control interface.
+ */
+void eap_sm_request_passphrase(struct eap_sm *sm)
+{
+ eap_sm_request(sm, WPA_CTRL_REQ_EAP_PASSPHRASE, NULL, 0);
+}
+
+
+/**
+ * eap_sm_request_sim - Request external SIM processing
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @req: EAP method specific request
+ */
+void eap_sm_request_sim(struct eap_sm *sm, const char *req)
+{
+ eap_sm_request(sm, WPA_CTRL_REQ_SIM, req, os_strlen(req));
+}
+
+
+/**
+ * eap_sm_notify_ctrl_attached - Notification of attached monitor
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ *
+ * Notify EAP state machines that a monitor was attached to the control
+ * interface to trigger re-sending of pending requests for user input.
+ */
+void eap_sm_notify_ctrl_attached(struct eap_sm *sm)
+{
+ struct eap_peer_config *config = eap_get_config(sm);
+
+ if (config == NULL)
+ return;
+
+ /* Re-send any pending requests for user data since a new control
+ * interface was added. This handles cases where the EAP authentication
+ * starts immediately after system startup when the user interface is
+ * not yet running. */
+ if (config->pending_req_identity)
+ eap_sm_request_identity(sm);
+ if (config->pending_req_password)
+ eap_sm_request_password(sm);
+ if (config->pending_req_new_password)
+ eap_sm_request_new_password(sm);
+ if (config->pending_req_otp)
+ eap_sm_request_otp(sm, NULL, 0);
+ if (config->pending_req_pin)
+ eap_sm_request_pin(sm);
+ if (config->pending_req_passphrase)
+ eap_sm_request_passphrase(sm);
+}
+
+
+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;
+}
+
+
+/**
+ * eap_get_phase2_type - Get EAP type for the given EAP phase 2 method name
+ * @name: EAP method name, e.g., MD5
+ * @vendor: Buffer for returning EAP Vendor-Id
+ * Returns: EAP method type or %EAP_TYPE_NONE if not found
+ *
+ * This function maps EAP type names into EAP type numbers that are allowed for
+ * Phase 2, i.e., for tunneled authentication. Phase 2 is used, e.g., with
+ * EAP-PEAP, EAP-TTLS, and EAP-FAST.
+ */
+u32 eap_get_phase2_type(const char *name, int *vendor)
+{
+ int v;
+ u32 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;
+}
+
+
+/**
+ * eap_get_phase2_types - Get list of allowed EAP phase 2 types
+ * @config: Pointer to a network configuration
+ * @count: Pointer to a variable to be filled with number of returned EAP types
+ * Returns: Pointer to allocated type list or %NULL on failure
+ *
+ * This function generates an array of allowed EAP phase 2 (tunneled) types for
+ * the given network configuration.
+ */
+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;
+}
+
+
+/**
+ * eap_set_fast_reauth - Update fast_reauth setting
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @enabled: 1 = Fast reauthentication is enabled, 0 = Disabled
+ */
+void eap_set_fast_reauth(struct eap_sm *sm, int enabled)
+{
+ sm->fast_reauth = enabled;
+}
+
+
+/**
+ * eap_set_workaround - Update EAP workarounds setting
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @workaround: 1 = Enable EAP workarounds, 0 = Disable EAP workarounds
+ */
+void eap_set_workaround(struct eap_sm *sm, unsigned int workaround)
+{
+ sm->workaround = workaround;
+}
+
+
+/**
+ * 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->eapol_cb->get_config(sm->eapol_ctx);
+}
+
+
+/**
+ * eap_get_config_identity - Get identity from the network configuration
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @len: Buffer for the length of the identity
+ * Returns: Pointer to the identity or %NULL if not found
+ */
+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;
+}
+
+
+static int eap_get_ext_password(struct eap_sm *sm,
+ struct eap_peer_config *config)
+{
+ char *name;
+
+ if (config->password == NULL)
+ return -1;
+
+ name = os_zalloc(config->password_len + 1);
+ if (name == NULL)
+ return -1;
+ os_memcpy(name, config->password, config->password_len);
+
+ ext_password_free(sm->ext_pw_buf);
+ sm->ext_pw_buf = ext_password_get(sm->ext_pw, name);
+ os_free(name);
+
+ return sm->ext_pw_buf == NULL ? -1 : 0;
+}
+
+
+/**
+ * eap_get_config_password - Get password from the network configuration
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @len: Buffer for the length of the password
+ * Returns: Pointer to the password or %NULL if not found
+ */
+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;
+
+ if (config->flags & EAP_CONFIG_FLAGS_EXT_PASSWORD) {
+ if (eap_get_ext_password(sm, config) < 0)
+ return NULL;
+ *len = wpabuf_len(sm->ext_pw_buf);
+ return wpabuf_head(sm->ext_pw_buf);
+ }
+
+ *len = config->password_len;
+ return config->password;
+}
+
+
+/**
+ * eap_get_config_password2 - Get password from the network configuration
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @len: Buffer for the length of the password
+ * @hash: Buffer for returning whether the password is stored as a
+ * NtPasswordHash instead of plaintext password; can be %NULL if this
+ * information is not needed
+ * Returns: Pointer to the password or %NULL if not found
+ */
+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;
+
+ if (config->flags & EAP_CONFIG_FLAGS_EXT_PASSWORD) {
+ if (eap_get_ext_password(sm, config) < 0)
+ return NULL;
+ if (hash)
+ *hash = 0;
+ *len = wpabuf_len(sm->ext_pw_buf);
+ return wpabuf_head(sm->ext_pw_buf);
+ }
+
+ *len = config->password_len;
+ if (hash)
+ *hash = !!(config->flags & EAP_CONFIG_FLAGS_PASSWORD_NTHASH);
+ return config->password;
+}
+
+
+/**
+ * eap_get_config_new_password - Get new password from network configuration
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @len: Buffer for the length of the new password
+ * Returns: Pointer to the new password or %NULL if not found
+ */
+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_otp - Get one-time password from the network configuration
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @len: Buffer for the length of the one-time password
+ * Returns: Pointer to the one-time password or %NULL if not found
+ */
+const u8 * eap_get_config_otp(struct eap_sm *sm, size_t *len)
+{
+ struct eap_peer_config *config = eap_get_config(sm);
+ if (config == NULL)
+ return NULL;
+ *len = config->otp_len;
+ return config->otp;
+}
+
+
+/**
+ * eap_clear_config_otp - Clear used one-time password
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ *
+ * This function clears a used one-time password (OTP) from the current network
+ * configuration. This should be called when the OTP has been used and is not
+ * needed anymore.
+ */
+void eap_clear_config_otp(struct eap_sm *sm)
+{
+ struct eap_peer_config *config = eap_get_config(sm);
+ if (config == NULL)
+ return;
+ os_memset(config->otp, 0, config->otp_len);
+ os_free(config->otp);
+ config->otp = NULL;
+ config->otp_len = 0;
+}
+
+
+/**
+ * eap_get_config_phase1 - Get phase1 data from the network configuration
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * Returns: Pointer to the phase1 data or %NULL if not found
+ */
+const char * eap_get_config_phase1(struct eap_sm *sm)
+{
+ struct eap_peer_config *config = eap_get_config(sm);
+ if (config == NULL)
+ return NULL;
+ return config->phase1;
+}
+
+
+/**
+ * eap_get_config_phase2 - Get phase2 data from the network configuration
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * Returns: Pointer to the phase1 data or %NULL if not found
+ */
+const char * eap_get_config_phase2(struct eap_sm *sm)
+{
+ struct eap_peer_config *config = eap_get_config(sm);
+ if (config == NULL)
+ return NULL;
+ return config->phase2;
+}
+
+
+int eap_get_config_fragment_size(struct eap_sm *sm)
+{
+ struct eap_peer_config *config = eap_get_config(sm);
+ if (config == NULL)
+ return -1;
+ return config->fragment_size;
+}
+
+
+/**
+ * eap_key_available - Get key availability (eapKeyAvailable variable)
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * Returns: 1 if EAP keying material is available, 0 if not
+ */
+int eap_key_available(struct eap_sm *sm)
+{
+ return sm ? sm->eapKeyAvailable : 0;
+}
+
+
+/**
+ * eap_notify_success - Notify EAP state machine about external success trigger
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ *
+ * This function is called when external event, e.g., successful completion of
+ * WPA-PSK key handshake, is indicating that EAP state machine should move to
+ * success state. This is mainly used with security modes that do not use EAP
+ * state machine (e.g., WPA-PSK).
+ */
+void eap_notify_success(struct eap_sm *sm)
+{
+ if (sm) {
+ sm->decision = DECISION_COND_SUCC;
+ sm->EAP_state = EAP_SUCCESS;
+ }
+}
+
+
+/**
+ * eap_notify_lower_layer_success - Notification of lower layer success
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ *
+ * Notify EAP state machines that a lower layer has detected a successful
+ * authentication. This is used to recover from dropped EAP-Success messages.
+ */
+void eap_notify_lower_layer_success(struct eap_sm *sm)
+{
+ if (sm == NULL)
+ return;
+
+ if (eapol_get_bool(sm, EAPOL_eapSuccess) ||
+ sm->decision == DECISION_FAIL ||
+ (sm->methodState != METHOD_MAY_CONT &&
+ sm->methodState != METHOD_DONE))
+ return;
+
+ if (sm->eapKeyData != NULL)
+ sm->eapKeyAvailable = TRUE;
+ eapol_set_bool(sm, EAPOL_eapSuccess, TRUE);
+ wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_SUCCESS
+ "EAP authentication completed successfully (based on lower "
+ "layer success)");
+}
+
+
+/**
+ * eap_get_eapSessionId - Get Session-Id from EAP state machine
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @len: Pointer to variable that will be set to number of bytes in the session
+ * Returns: Pointer to the EAP Session-Id or %NULL on failure
+ *
+ * Fetch EAP Session-Id from the EAP state machine. The Session-Id is available
+ * only after a successful authentication. EAP state machine continues to manage
+ * the Session-Id and the caller must not change or free the returned data.
+ */
+const u8 * eap_get_eapSessionId(struct eap_sm *sm, size_t *len)
+{
+ if (sm == NULL || sm->eapSessionId == NULL) {
+ *len = 0;
+ return NULL;
+ }
+
+ *len = sm->eapSessionIdLen;
+ return sm->eapSessionId;
+}
+
+
+/**
+ * eap_get_eapKeyData - Get master session key (MSK) from EAP state machine
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @len: Pointer to variable that will be set to number of bytes in the key
+ * Returns: Pointer to the EAP keying data or %NULL on failure
+ *
+ * Fetch EAP keying material (MSK, eapKeyData) from the EAP state machine. The
+ * key is available only after a successful authentication. EAP state machine
+ * continues to manage the key data and the caller must not change or free the
+ * returned data.
+ */
+const u8 * eap_get_eapKeyData(struct eap_sm *sm, size_t *len)
+{
+ if (sm == NULL || sm->eapKeyData == NULL) {
+ *len = 0;
+ return NULL;
+ }
+
+ *len = sm->eapKeyDataLen;
+ return sm->eapKeyData;
+}
+
+
+/**
+ * eap_get_eapKeyData - Get EAP response data
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * Returns: Pointer to the EAP response (eapRespData) or %NULL on failure
+ *
+ * Fetch EAP response (eapRespData) from the EAP state machine. This data is
+ * available when EAP state machine has processed an incoming EAP request. The
+ * EAP state machine does not maintain a reference to the response after this
+ * function is called and the caller is responsible for freeing the data.
+ */
+struct wpabuf * eap_get_eapRespData(struct eap_sm *sm)
+{
+ struct wpabuf *resp;
+
+ if (sm == NULL || sm->eapRespData == NULL)
+ return NULL;
+
+ resp = sm->eapRespData;
+ sm->eapRespData = NULL;
+
+ return resp;
+}
+
+
+/**
+ * eap_sm_register_scard_ctx - Notification of smart card context
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @ctx: Context data for smart card operations
+ *
+ * Notify EAP state machines of context data for smart card operations. This
+ * context data will be used as a parameter for scard_*() functions.
+ */
+void eap_register_scard_ctx(struct eap_sm *sm, void *ctx)
+{
+ if (sm)
+ sm->scard_ctx = ctx;
+}
+
+
+/**
+ * eap_set_config_blob - Set or add a named configuration blob
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @blob: New value for the blob
+ *
+ * Adds a new configuration blob or replaces the current value of an existing
+ * blob.
+ */
+void eap_set_config_blob(struct eap_sm *sm, struct wpa_config_blob *blob)
+{
+#ifndef CONFIG_NO_CONFIG_BLOBS
+ sm->eapol_cb->set_config_blob(sm->eapol_ctx, blob);
+#endif /* CONFIG_NO_CONFIG_BLOBS */
+}
+
+
+/**
+ * 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)
+{
+#ifndef CONFIG_NO_CONFIG_BLOBS
+ return sm->eapol_cb->get_config_blob(sm->eapol_ctx, name);
+#else /* CONFIG_NO_CONFIG_BLOBS */
+ return NULL;
+#endif /* CONFIG_NO_CONFIG_BLOBS */
+}
+
+
+/**
+ * eap_set_force_disabled - Set force_disabled flag
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @disabled: 1 = EAP disabled, 0 = EAP enabled
+ *
+ * This function is used to force EAP state machine to be disabled when it is
+ * not in use (e.g., with WPA-PSK or plaintext connections).
+ */
+void eap_set_force_disabled(struct eap_sm *sm, int disabled)
+{
+ sm->force_disabled = disabled;
+}
+
+
+/**
+ * eap_set_external_sim - Set external_sim flag
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @external_sim: Whether external SIM/USIM processing is used
+ */
+void eap_set_external_sim(struct eap_sm *sm, int external_sim)
+{
+ sm->external_sim = external_sim;
+}
+
+
+ /**
+ * eap_notify_pending - Notify that EAP method is ready to re-process a request
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ *
+ * An EAP method can perform a pending operation (e.g., to get a response from
+ * an external process). Once the response is available, this function can be
+ * used to request EAPOL state machine to retry delivering the previously
+ * received (and still unanswered) EAP request to EAP state machine.
+ */
+void eap_notify_pending(struct eap_sm *sm)
+{
+ sm->eapol_cb->notify_pending(sm->eapol_ctx);
+}
+
+
+/**
+ * eap_invalidate_cached_session - Mark cached session data invalid
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ */
+void eap_invalidate_cached_session(struct eap_sm *sm)
+{
+ if (sm)
+ eap_deinit_prev_method(sm, "invalidate");
+}
+
+
+int eap_is_wps_pbc_enrollee(struct eap_peer_config *conf)
+{
+ if (conf->identity_len != WSC_ID_ENROLLEE_LEN ||
+ os_memcmp(conf->identity, WSC_ID_ENROLLEE, WSC_ID_ENROLLEE_LEN))
+ return 0; /* Not a WPS Enrollee */
+
+ if (conf->phase1 == NULL || os_strstr(conf->phase1, "pbc=1") == NULL)
+ return 0; /* Not using PBC */
+
+ return 1;
+}
+
+
+int eap_is_wps_pin_enrollee(struct eap_peer_config *conf)
+{
+ if (conf->identity_len != WSC_ID_ENROLLEE_LEN ||
+ os_memcmp(conf->identity, WSC_ID_ENROLLEE, WSC_ID_ENROLLEE_LEN))
+ return 0; /* Not a WPS Enrollee */
+
+ if (conf->phase1 == NULL || os_strstr(conf->phase1, "pin=") == NULL)
+ return 0; /* Not using PIN */
+
+ return 1;
+}
+
+
+void eap_sm_set_ext_pw_ctx(struct eap_sm *sm, struct ext_password_data *ext)
+{
+ ext_password_free(sm->ext_pw_buf);
+ sm->ext_pw_buf = NULL;
+ sm->ext_pw = ext;
+}
+
+
+/**
+ * eap_set_anon_id - Set or add anonymous identity
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @id: Anonymous identity (e.g., EAP-SIM pseudonym) or %NULL to clear
+ * @len: Length of anonymous identity in octets
+ */
+void eap_set_anon_id(struct eap_sm *sm, const u8 *id, size_t len)
+{
+ if (sm->eapol_cb->set_anon_id)
+ sm->eapol_cb->set_anon_id(sm->eapol_ctx, id, len);
+}
+
+
+int eap_peer_was_failure_expected(struct eap_sm *sm)
+{
+ return sm->expected_failure;
+}
diff --git a/freebsd/contrib/wpa/src/eap_peer/eap.h b/freebsd/contrib/wpa/src/eap_peer/eap.h
new file mode 100644
index 00000000..1a645af8
--- /dev/null
+++ b/freebsd/contrib/wpa/src/eap_peer/eap.h
@@ -0,0 +1,354 @@
+/*
+ * EAP peer state machine functions (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.
+ */
+
+#ifndef EAP_H
+#define EAP_H
+
+#include "common/defs.h"
+#include "eap_common/eap_defs.h"
+#include "eap_peer/eap_methods.h"
+
+struct eap_sm;
+struct wpa_config_blob;
+struct wpabuf;
+
+struct eap_method_type {
+ int vendor;
+ u32 method;
+};
+
+#ifdef IEEE8021X_EAPOL
+
+/**
+ * enum eapol_bool_var - EAPOL boolean state variables for EAP state machine
+ *
+ * These variables are used in the interface between EAP peer state machine and
+ * lower layer. These are defined in RFC 4137, Sect. 4.1. Lower layer code is
+ * expected to maintain these variables and register a callback functions for
+ * EAP state machine to get and set the variables.
+ */
+enum eapol_bool_var {
+ /**
+ * EAPOL_eapSuccess - EAP SUCCESS state reached
+ *
+ * EAP state machine reads and writes this value.
+ */
+ EAPOL_eapSuccess,
+
+ /**
+ * EAPOL_eapRestart - Lower layer request to restart authentication
+ *
+ * Set to TRUE in lower layer, FALSE in EAP state machine.
+ */
+ EAPOL_eapRestart,
+
+ /**
+ * EAPOL_eapFail - EAP FAILURE state reached
+ *
+ * EAP state machine writes this value.
+ */
+ EAPOL_eapFail,
+
+ /**
+ * EAPOL_eapResp - Response to send
+ *
+ * Set to TRUE in EAP state machine, FALSE in lower layer.
+ */
+ EAPOL_eapResp,
+
+ /**
+ * EAPOL_eapNoResp - Request has been process; no response to send
+ *
+ * Set to TRUE in EAP state machine, FALSE in lower layer.
+ */
+ EAPOL_eapNoResp,
+
+ /**
+ * EAPOL_eapReq - EAP request available from lower layer
+ *
+ * Set to TRUE in lower layer, FALSE in EAP state machine.
+ */
+ EAPOL_eapReq,
+
+ /**
+ * EAPOL_portEnabled - Lower layer is ready for communication
+ *
+ * EAP state machines reads this value.
+ */
+ EAPOL_portEnabled,
+
+ /**
+ * EAPOL_altAccept - Alternate indication of success (RFC3748)
+ *
+ * EAP state machines reads this value.
+ */
+ EAPOL_altAccept,
+
+ /**
+ * EAPOL_altReject - Alternate indication of failure (RFC3748)
+ *
+ * EAP state machines reads this value.
+ */
+ EAPOL_altReject,
+
+ /**
+ * EAPOL_eapTriggerStart - EAP-based trigger to send EAPOL-Start
+ *
+ * EAP state machine writes this value.
+ */
+ EAPOL_eapTriggerStart
+};
+
+/**
+ * enum eapol_int_var - EAPOL integer state variables for EAP state machine
+ *
+ * These variables are used in the interface between EAP peer state machine and
+ * lower layer. These are defined in RFC 4137, Sect. 4.1. Lower layer code is
+ * expected to maintain these variables and register a callback functions for
+ * EAP state machine to get and set the variables.
+ */
+enum eapol_int_var {
+ /**
+ * EAPOL_idleWhile - Outside time for EAP peer timeout
+ *
+ * This integer variable is used to provide an outside timer that the
+ * external (to EAP state machine) code must decrement by one every
+ * second until the value reaches zero. This is used in the same way as
+ * EAPOL state machine timers. EAP state machine reads and writes this
+ * value.
+ */
+ EAPOL_idleWhile
+};
+
+/**
+ * struct eapol_callbacks - Callback functions from EAP to lower layer
+ *
+ * This structure defines the callback functions that EAP state machine
+ * requires from the lower layer (usually EAPOL state machine) for updating
+ * state variables and requesting information. eapol_ctx from
+ * eap_peer_sm_init() call will be used as the ctx parameter for these
+ * callback functions.
+ */
+struct eapol_callbacks {
+ /**
+ * get_config - Get pointer to the current network configuration
+ * @ctx: eapol_ctx from eap_peer_sm_init() call
+ */
+ struct eap_peer_config * (*get_config)(void *ctx);
+
+ /**
+ * get_bool - Get a boolean EAPOL state variable
+ * @variable: EAPOL boolean variable to get
+ * Returns: Value of the EAPOL variable
+ */
+ Boolean (*get_bool)(void *ctx, enum eapol_bool_var variable);
+
+ /**
+ * set_bool - Set a boolean EAPOL state variable
+ * @ctx: eapol_ctx from eap_peer_sm_init() call
+ * @variable: EAPOL boolean variable to set
+ * @value: Value for the EAPOL variable
+ */
+ void (*set_bool)(void *ctx, enum eapol_bool_var variable,
+ Boolean value);
+
+ /**
+ * get_int - Get an integer EAPOL state variable
+ * @ctx: eapol_ctx from eap_peer_sm_init() call
+ * @variable: EAPOL integer variable to get
+ * Returns: Value of the EAPOL variable
+ */
+ unsigned int (*get_int)(void *ctx, enum eapol_int_var variable);
+
+ /**
+ * set_int - Set an integer EAPOL state variable
+ * @ctx: eapol_ctx from eap_peer_sm_init() call
+ * @variable: EAPOL integer variable to set
+ * @value: Value for the EAPOL variable
+ */
+ void (*set_int)(void *ctx, enum eapol_int_var variable,
+ unsigned int value);
+
+ /**
+ * get_eapReqData - Get EAP-Request data
+ * @ctx: eapol_ctx from eap_peer_sm_init() call
+ * @len: Pointer to variable that will be set to eapReqDataLen
+ * Returns: Reference to eapReqData (EAP state machine will not free
+ * this) or %NULL if eapReqData not available.
+ */
+ struct wpabuf * (*get_eapReqData)(void *ctx);
+
+ /**
+ * set_config_blob - Set named configuration blob
+ * @ctx: eapol_ctx from eap_peer_sm_init() call
+ * @blob: New value for the blob
+ *
+ * Adds a new configuration blob or replaces the current value of an
+ * existing blob.
+ */
+ void (*set_config_blob)(void *ctx, struct wpa_config_blob *blob);
+
+ /**
+ * get_config_blob - Get a named configuration blob
+ * @ctx: eapol_ctx from eap_peer_sm_init() call
+ * @name: Name of the blob
+ * Returns: Pointer to blob data or %NULL if not found
+ */
+ const struct wpa_config_blob * (*get_config_blob)(void *ctx,
+ const char *name);
+
+ /**
+ * notify_pending - Notify that a pending request can be retried
+ * @ctx: eapol_ctx from eap_peer_sm_init() call
+ *
+ * An EAP method can perform a pending operation (e.g., to get a
+ * response from an external process). Once the response is available,
+ * this callback function can be used to request EAPOL state machine to
+ * retry delivering the previously received (and still unanswered) EAP
+ * request to EAP state machine.
+ */
+ void (*notify_pending)(void *ctx);
+
+ /**
+ * eap_param_needed - Notify that EAP parameter is needed
+ * @ctx: eapol_ctx from eap_peer_sm_init() call
+ * @field: Field indicator (e.g., WPA_CTRL_REQ_EAP_IDENTITY)
+ * @txt: User readable text describing the required parameter
+ */
+ void (*eap_param_needed)(void *ctx, enum wpa_ctrl_req_type field,
+ const char *txt);
+
+ /**
+ * notify_cert - Notification of a peer certificate
+ * @ctx: eapol_ctx from eap_peer_sm_init() call
+ * @depth: Depth in certificate chain (0 = server)
+ * @subject: Subject of the peer certificate
+ * @altsubject: Select fields from AltSubject of the peer certificate
+ * @num_altsubject: Number of altsubject values
+ * @cert_hash: SHA-256 hash of the certificate
+ * @cert: Peer certificate
+ */
+ void (*notify_cert)(void *ctx, int depth, const char *subject,
+ const char *altsubject[], int num_altsubject,
+ const char *cert_hash, const struct wpabuf *cert);
+
+ /**
+ * notify_status - Notification of the current EAP state
+ * @ctx: eapol_ctx from eap_peer_sm_init() call
+ * @status: Step in the process of EAP authentication
+ * @parameter: Step-specific parameter, e.g., EAP method name
+ */
+ void (*notify_status)(void *ctx, const char *status,
+ const char *parameter);
+
+#ifdef CONFIG_EAP_PROXY
+ /**
+ * eap_proxy_cb - Callback signifying any updates from eap_proxy
+ * @ctx: eapol_ctx from eap_peer_sm_init() call
+ */
+ void (*eap_proxy_cb)(void *ctx);
+#endif /* CONFIG_EAP_PROXY */
+
+ /**
+ * set_anon_id - Set or add anonymous identity
+ * @ctx: eapol_ctx from eap_peer_sm_init() call
+ * @id: Anonymous identity (e.g., EAP-SIM pseudonym) or %NULL to clear
+ * @len: Length of anonymous identity in octets
+ */
+ void (*set_anon_id)(void *ctx, const u8 *id, size_t len);
+};
+
+/**
+ * struct eap_config - Configuration for EAP state machine
+ */
+struct eap_config {
+ /**
+ * opensc_engine_path - OpenSC engine for OpenSSL engine support
+ *
+ * Usually, path to engine_opensc.so.
+ */
+ const char *opensc_engine_path;
+ /**
+ * pkcs11_engine_path - PKCS#11 engine for OpenSSL engine support
+ *
+ * Usually, path to engine_pkcs11.so.
+ */
+ const char *pkcs11_engine_path;
+ /**
+ * pkcs11_module_path - OpenSC PKCS#11 module for OpenSSL engine
+ *
+ * Usually, path to opensc-pkcs11.so.
+ */
+ const char *pkcs11_module_path;
+ /**
+ * openssl_ciphers - OpenSSL cipher string
+ *
+ * This is an OpenSSL specific configuration option for configuring the
+ * default ciphers. If not set, "DEFAULT:!EXP:!LOW" is used as the
+ * default.
+ */
+ const char *openssl_ciphers;
+ /**
+ * wps - WPS context data
+ *
+ * This is only used by EAP-WSC and can be left %NULL if not available.
+ */
+ struct wps_context *wps;
+
+ /**
+ * cert_in_cb - Include server certificates in callback
+ */
+ int cert_in_cb;
+};
+
+struct eap_sm * eap_peer_sm_init(void *eapol_ctx,
+ const struct eapol_callbacks *eapol_cb,
+ void *msg_ctx, struct eap_config *conf);
+void eap_peer_sm_deinit(struct eap_sm *sm);
+int eap_peer_sm_step(struct eap_sm *sm);
+void eap_sm_abort(struct eap_sm *sm);
+int eap_sm_get_status(struct eap_sm *sm, char *buf, size_t buflen,
+ int verbose);
+const char * eap_sm_get_method_name(struct eap_sm *sm);
+struct wpabuf * eap_sm_buildIdentity(struct eap_sm *sm, int id, int encrypted);
+void eap_sm_request_identity(struct eap_sm *sm);
+void eap_sm_request_password(struct eap_sm *sm);
+void eap_sm_request_new_password(struct eap_sm *sm);
+void eap_sm_request_pin(struct eap_sm *sm);
+void eap_sm_request_otp(struct eap_sm *sm, const char *msg, size_t msg_len);
+void eap_sm_request_passphrase(struct eap_sm *sm);
+void eap_sm_request_sim(struct eap_sm *sm, const char *req);
+void eap_sm_notify_ctrl_attached(struct eap_sm *sm);
+u32 eap_get_phase2_type(const char *name, int *vendor);
+struct eap_method_type * eap_get_phase2_types(struct eap_peer_config *config,
+ size_t *count);
+void eap_set_fast_reauth(struct eap_sm *sm, int enabled);
+void eap_set_workaround(struct eap_sm *sm, unsigned int workaround);
+void eap_set_force_disabled(struct eap_sm *sm, int disabled);
+void eap_set_external_sim(struct eap_sm *sm, int external_sim);
+int eap_key_available(struct eap_sm *sm);
+void eap_notify_success(struct eap_sm *sm);
+void eap_notify_lower_layer_success(struct eap_sm *sm);
+const u8 * eap_get_eapSessionId(struct eap_sm *sm, size_t *len);
+const u8 * eap_get_eapKeyData(struct eap_sm *sm, size_t *len);
+struct wpabuf * eap_get_eapRespData(struct eap_sm *sm);
+void eap_register_scard_ctx(struct eap_sm *sm, void *ctx);
+void eap_invalidate_cached_session(struct eap_sm *sm);
+
+int eap_is_wps_pbc_enrollee(struct eap_peer_config *conf);
+int eap_is_wps_pin_enrollee(struct eap_peer_config *conf);
+
+struct ext_password_data;
+void eap_sm_set_ext_pw_ctx(struct eap_sm *sm, struct ext_password_data *ext);
+void eap_set_anon_id(struct eap_sm *sm, const u8 *id, size_t len);
+int eap_peer_was_failure_expected(struct eap_sm *sm);
+void eap_peer_erp_free_keys(struct eap_sm *sm);
+
+#endif /* IEEE8021X_EAPOL */
+
+#endif /* EAP_H */
diff --git a/freebsd/contrib/wpa/src/eap_peer/eap_config.h b/freebsd/contrib/wpa/src/eap_peer/eap_config.h
new file mode 100644
index 00000000..2b1a1d5e
--- /dev/null
+++ b/freebsd/contrib/wpa/src/eap_peer/eap_config.h
@@ -0,0 +1,774 @@
+/*
+ * EAP peer configuration data
+ * Copyright (c) 2003-2013, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef EAP_CONFIG_H
+#define EAP_CONFIG_H
+
+/**
+ * struct eap_peer_config - EAP peer configuration/credentials
+ */
+struct eap_peer_config {
+ /**
+ * identity - EAP Identity
+ *
+ * This field is used to set the real user identity or NAI (for
+ * EAP-PSK/PAX/SAKE/GPSK).
+ */
+ u8 *identity;
+
+ /**
+ * identity_len - EAP Identity length
+ */
+ size_t identity_len;
+
+ /**
+ * anonymous_identity - Anonymous EAP Identity
+ *
+ * This field is used for unencrypted use with EAP types that support
+ * different tunnelled identity, e.g., EAP-TTLS, in order to reveal the
+ * real identity (identity field) only to the authentication server.
+ *
+ * If not set, the identity field will be used for both unencrypted and
+ * protected fields.
+ *
+ * This field can also be used with EAP-SIM/AKA/AKA' to store the
+ * pseudonym identity.
+ */
+ u8 *anonymous_identity;
+
+ /**
+ * anonymous_identity_len - Length of anonymous_identity
+ */
+ size_t anonymous_identity_len;
+
+ /**
+ * password - Password string for EAP
+ *
+ * This field can include either the plaintext password (default
+ * option) or a NtPasswordHash (16-byte MD4 hash of the unicode
+ * presentation of the password) if flags field has
+ * EAP_CONFIG_FLAGS_PASSWORD_NTHASH bit set to 1. NtPasswordHash can
+ * only be used with authentication mechanism that use this hash as the
+ * starting point for operation: MSCHAP and MSCHAPv2 (EAP-MSCHAPv2,
+ * EAP-TTLS/MSCHAPv2, EAP-TTLS/MSCHAP, LEAP).
+ *
+ * In addition, this field is used to configure a pre-shared key for
+ * EAP-PSK/PAX/SAKE/GPSK. The length of the PSK must be 16 for EAP-PSK
+ * and EAP-PAX and 32 for EAP-SAKE. EAP-GPSK can use a variable length
+ * PSK.
+ */
+ u8 *password;
+
+ /**
+ * password_len - Length of password field
+ */
+ size_t password_len;
+
+ /**
+ * ca_cert - File path to CA certificate file (PEM/DER)
+ *
+ * This file can have one or more trusted CA certificates. If ca_cert
+ * and ca_path are not included, server certificate will not be
+ * verified. This is insecure and a trusted CA certificate should
+ * always be configured when using EAP-TLS/TTLS/PEAP. Full path to the
+ * file should be used since working directory may change when
+ * wpa_supplicant is run in the background.
+ *
+ * Alternatively, a named configuration blob can be used by setting
+ * this to blob://blob_name.
+ *
+ * Alternatively, this can be used to only perform matching of the
+ * server certificate (SHA-256 hash of the DER encoded X.509
+ * certificate). In this case, the possible CA certificates in the
+ * server certificate chain are ignored and only the server certificate
+ * is verified. This is configured with the following format:
+ * hash:://server/sha256/cert_hash_in_hex
+ * For example: "hash://server/sha256/
+ * 5a1bc1296205e6fdbe3979728efe3920798885c1c4590b5f90f43222d239ca6a"
+ *
+ * On Windows, trusted CA certificates can be loaded from the system
+ * certificate store by setting this to cert_store://name, e.g.,
+ * ca_cert="cert_store://CA" or ca_cert="cert_store://ROOT".
+ * Note that when running wpa_supplicant as an application, the user
+ * certificate store (My user account) is used, whereas computer store
+ * (Computer account) is used when running wpasvc as a service.
+ */
+ u8 *ca_cert;
+
+ /**
+ * ca_path - Directory path for CA certificate files (PEM)
+ *
+ * This path may contain multiple CA certificates in OpenSSL format.
+ * Common use for this is to point to system trusted CA list which is
+ * often installed into directory like /etc/ssl/certs. If configured,
+ * these certificates are added to the list of trusted CAs. ca_cert
+ * may also be included in that case, but it is not required.
+ */
+ u8 *ca_path;
+
+ /**
+ * client_cert - File path to client certificate file (PEM/DER)
+ *
+ * This field is used with EAP method that use TLS authentication.
+ * Usually, this is only configured for EAP-TLS, even though this could
+ * in theory be used with EAP-TTLS and EAP-PEAP, too. Full path to the
+ * file should be used since working directory may change when
+ * wpa_supplicant is run in the background.
+ *
+ * Alternatively, a named configuration blob can be used by setting
+ * this to blob://blob_name.
+ */
+ u8 *client_cert;
+
+ /**
+ * private_key - File path to client private key file (PEM/DER/PFX)
+ *
+ * When PKCS#12/PFX file (.p12/.pfx) is used, client_cert should be
+ * commented out. Both the private key and certificate will be read
+ * from the PKCS#12 file in this case. Full path to the file should be
+ * used since working directory may change when wpa_supplicant is run
+ * in the background.
+ *
+ * Windows certificate store can be used by leaving client_cert out and
+ * configuring private_key in one of the following formats:
+ *
+ * cert://substring_to_match
+ *
+ * hash://certificate_thumbprint_in_hex
+ *
+ * For example: private_key="hash://63093aa9c47f56ae88334c7b65a4"
+ *
+ * Note that when running wpa_supplicant as an application, the user
+ * certificate store (My user account) is used, whereas computer store
+ * (Computer account) is used when running wpasvc as a service.
+ *
+ * Alternatively, a named configuration blob can be used by setting
+ * this to blob://blob_name.
+ */
+ u8 *private_key;
+
+ /**
+ * private_key_passwd - Password for private key file
+ *
+ * If left out, this will be asked through control interface.
+ */
+ char *private_key_passwd;
+
+ /**
+ * dh_file - File path to DH/DSA parameters file (in PEM format)
+ *
+ * This is an optional configuration file for setting parameters for an
+ * ephemeral DH key exchange. In most cases, the default RSA
+ * authentication does not use this configuration. However, it is
+ * possible setup RSA to use ephemeral DH key exchange. In addition,
+ * ciphers with DSA keys always use ephemeral DH keys. This can be used
+ * to achieve forward secrecy. If the file is in DSA parameters format,
+ * it will be automatically converted into DH params. Full path to the
+ * file should be used since working directory may change when
+ * wpa_supplicant is run in the background.
+ *
+ * Alternatively, a named configuration blob can be used by setting
+ * this to blob://blob_name.
+ */
+ u8 *dh_file;
+
+ /**
+ * subject_match - Constraint for server certificate subject
+ *
+ * This substring is matched against the subject of the authentication
+ * server certificate. If this string is set, the server sertificate is
+ * only accepted if it contains this string in the subject. The subject
+ * string is in following format:
+ *
+ * /C=US/ST=CA/L=San Francisco/CN=Test AS/emailAddress=as@n.example.com
+ *
+ * Note: Since this is a substring match, this cannot be used securily
+ * to do a suffix match against a possible domain name in the CN entry.
+ * For such a use case, domain_suffix_match should be used instead.
+ */
+ u8 *subject_match;
+
+ /**
+ * altsubject_match - Constraint for server certificate alt. subject
+ *
+ * Semicolon separated string of entries to be matched against the
+ * alternative subject name of the authentication server certificate.
+ * If this string is set, the server sertificate is only accepted if it
+ * contains one of the entries in an alternative subject name
+ * extension.
+ *
+ * altSubjectName string is in following format: TYPE:VALUE
+ *
+ * Example: EMAIL:server@example.com
+ * Example: DNS:server.example.com;DNS:server2.example.com
+ *
+ * Following types are supported: EMAIL, DNS, URI
+ */
+ u8 *altsubject_match;
+
+ /**
+ * domain_suffix_match - Constraint for server domain name
+ *
+ * If set, this FQDN is used as a suffix match requirement for the
+ * server certificate in SubjectAltName dNSName element(s). If a
+ * matching dNSName is found, this constraint is met. If no dNSName
+ * values are present, this constraint is matched against SubjectName CN
+ * using same suffix match comparison. Suffix match here means that the
+ * host/domain name is compared one label at a time starting from the
+ * top-level domain and all the labels in domain_suffix_match shall be
+ * included in the certificate. The certificate may include additional
+ * sub-level labels in addition to the required labels.
+ *
+ * For example, domain_suffix_match=example.com would match
+ * test.example.com but would not match test-example.com.
+ */
+ char *domain_suffix_match;
+
+ /**
+ * domain_match - Constraint for server domain name
+ *
+ * If set, this FQDN is used as a full match requirement for the
+ * server certificate in SubjectAltName dNSName element(s). If a
+ * matching dNSName is found, this constraint is met. If no dNSName
+ * values are present, this constraint is matched against SubjectName CN
+ * using same full match comparison. This behavior is similar to
+ * domain_suffix_match, but has the requirement of a full match, i.e.,
+ * no subdomains or wildcard matches are allowed. Case-insensitive
+ * comparison is used, so "Example.com" matches "example.com", but would
+ * not match "test.Example.com".
+ */
+ char *domain_match;
+
+ /**
+ * ca_cert2 - File path to CA certificate file (PEM/DER) (Phase 2)
+ *
+ * This file can have one or more trusted CA certificates. If ca_cert2
+ * and ca_path2 are not included, server certificate will not be
+ * verified. This is insecure and a trusted CA certificate should
+ * always be configured. Full path to the file should be used since
+ * working directory may change when wpa_supplicant is run in the
+ * background.
+ *
+ * This field is like ca_cert, but used for phase 2 (inside
+ * EAP-TTLS/PEAP/FAST tunnel) authentication.
+ *
+ * Alternatively, a named configuration blob can be used by setting
+ * this to blob://blob_name.
+ */
+ u8 *ca_cert2;
+
+ /**
+ * ca_path2 - Directory path for CA certificate files (PEM) (Phase 2)
+ *
+ * This path may contain multiple CA certificates in OpenSSL format.
+ * Common use for this is to point to system trusted CA list which is
+ * often installed into directory like /etc/ssl/certs. If configured,
+ * these certificates are added to the list of trusted CAs. ca_cert
+ * may also be included in that case, but it is not required.
+ *
+ * This field is like ca_path, but used for phase 2 (inside
+ * EAP-TTLS/PEAP/FAST tunnel) authentication.
+ */
+ u8 *ca_path2;
+
+ /**
+ * client_cert2 - File path to client certificate file
+ *
+ * This field is like client_cert, but used for phase 2 (inside
+ * EAP-TTLS/PEAP/FAST tunnel) authentication. Full path to the
+ * file should be used since working directory may change when
+ * wpa_supplicant is run in the background.
+ *
+ * Alternatively, a named configuration blob can be used by setting
+ * this to blob://blob_name.
+ */
+ u8 *client_cert2;
+
+ /**
+ * private_key2 - File path to client private key file
+ *
+ * This field is like private_key, but used for phase 2 (inside
+ * EAP-TTLS/PEAP/FAST tunnel) authentication. Full path to the
+ * file should be used since working directory may change when
+ * wpa_supplicant is run in the background.
+ *
+ * Alternatively, a named configuration blob can be used by setting
+ * this to blob://blob_name.
+ */
+ u8 *private_key2;
+
+ /**
+ * private_key2_passwd - Password for private key file
+ *
+ * This field is like private_key_passwd, but used for phase 2 (inside
+ * EAP-TTLS/PEAP/FAST tunnel) authentication.
+ */
+ char *private_key2_passwd;
+
+ /**
+ * dh_file2 - File path to DH/DSA parameters file (in PEM format)
+ *
+ * This field is like dh_file, but used for phase 2 (inside
+ * EAP-TTLS/PEAP/FAST tunnel) authentication. Full path to the
+ * file should be used since working directory may change when
+ * wpa_supplicant is run in the background.
+ *
+ * Alternatively, a named configuration blob can be used by setting
+ * this to blob://blob_name.
+ */
+ u8 *dh_file2;
+
+ /**
+ * subject_match2 - Constraint for server certificate subject
+ *
+ * This field is like subject_match, but used for phase 2 (inside
+ * EAP-TTLS/PEAP/FAST tunnel) authentication.
+ */
+ u8 *subject_match2;
+
+ /**
+ * altsubject_match2 - Constraint for server certificate alt. subject
+ *
+ * This field is like altsubject_match, but used for phase 2 (inside
+ * EAP-TTLS/PEAP/FAST tunnel) authentication.
+ */
+ u8 *altsubject_match2;
+
+ /**
+ * domain_suffix_match2 - Constraint for server domain name
+ *
+ * This field is like domain_suffix_match, but used for phase 2 (inside
+ * EAP-TTLS/PEAP/FAST tunnel) authentication.
+ */
+ char *domain_suffix_match2;
+
+ /**
+ * domain_match2 - Constraint for server domain name
+ *
+ * This field is like domain_match, but used for phase 2 (inside
+ * EAP-TTLS/PEAP/FAST tunnel) authentication.
+ */
+ char *domain_match2;
+
+ /**
+ * eap_methods - Allowed EAP methods
+ *
+ * (vendor=EAP_VENDOR_IETF,method=EAP_TYPE_NONE) terminated list of
+ * allowed EAP methods or %NULL if all methods are accepted.
+ */
+ struct eap_method_type *eap_methods;
+
+ /**
+ * phase1 - Phase 1 (outer authentication) parameters
+ *
+ * String with field-value pairs, e.g., "peapver=0" or
+ * "peapver=1 peaplabel=1".
+ *
+ * 'peapver' can be used to force which PEAP version (0 or 1) is used.
+ *
+ * 'peaplabel=1' can be used to force new label, "client PEAP
+ * encryption", to be used during key derivation when PEAPv1 or newer.
+ *
+ * Most existing PEAPv1 implementation seem to be using the old label,
+ * "client EAP encryption", and wpa_supplicant is now using that as the
+ * default value.
+ *
+ * Some servers, e.g., Radiator, may require peaplabel=1 configuration
+ * to interoperate with PEAPv1; see eap_testing.txt for more details.
+ *
+ * 'peap_outer_success=0' can be used to terminate PEAP authentication
+ * on tunneled EAP-Success. This is required with some RADIUS servers
+ * that implement draft-josefsson-pppext-eap-tls-eap-05.txt (e.g.,
+ * Lucent NavisRadius v4.4.0 with PEAP in "IETF Draft 5" mode).
+ *
+ * include_tls_length=1 can be used to force wpa_supplicant to include
+ * TLS Message Length field in all TLS messages even if they are not
+ * fragmented.
+ *
+ * sim_min_num_chal=3 can be used to configure EAP-SIM to require three
+ * challenges (by default, it accepts 2 or 3).
+ *
+ * result_ind=1 can be used to enable EAP-SIM and EAP-AKA to use
+ * protected result indication.
+ *
+ * fast_provisioning option can be used to enable in-line provisioning
+ * of EAP-FAST credentials (PAC):
+ * 0 = disabled,
+ * 1 = allow unauthenticated provisioning,
+ * 2 = allow authenticated provisioning,
+ * 3 = allow both unauthenticated and authenticated provisioning
+ *
+ * fast_max_pac_list_len=num option can be used to set the maximum
+ * number of PAC entries to store in a PAC list (default: 10).
+ *
+ * fast_pac_format=binary option can be used to select binary format
+ * for storing PAC entries in order to save some space (the default
+ * text format uses about 2.5 times the size of minimal binary format).
+ *
+ * crypto_binding option can be used to control PEAPv0 cryptobinding
+ * behavior:
+ * 0 = do not use cryptobinding (default)
+ * 1 = use cryptobinding if server supports it
+ * 2 = require cryptobinding
+ *
+ * EAP-WSC (WPS) uses following options: pin=Device_Password and
+ * uuid=Device_UUID
+ *
+ * For wired IEEE 802.1X authentication, "allow_canned_success=1" can be
+ * used to configure a mode that allows EAP-Success (and EAP-Failure)
+ * without going through authentication step. Some switches use such
+ * sequence when forcing the port to be authorized/unauthorized or as a
+ * fallback option if the authentication server is unreachable. By
+ * default, wpa_supplicant discards such frames to protect against
+ * potential attacks by rogue devices, but this option can be used to
+ * disable that protection for cases where the server/authenticator does
+ * not need to be authenticated.
+ */
+ char *phase1;
+
+ /**
+ * phase2 - Phase2 (inner authentication with TLS tunnel) parameters
+ *
+ * String with field-value pairs, e.g., "auth=MSCHAPV2" for EAP-PEAP or
+ * "autheap=MSCHAPV2 autheap=MD5" for EAP-TTLS. "mschapv2_retry=0" can
+ * be used to disable MSCHAPv2 password retry in authentication failure
+ * cases.
+ */
+ char *phase2;
+
+ /**
+ * pcsc - Parameters for PC/SC smartcard interface for USIM and GSM SIM
+ *
+ * This field is used to configure PC/SC smartcard interface.
+ * Currently, the only configuration is whether this field is %NULL (do
+ * not use PC/SC) or non-NULL (e.g., "") to enable PC/SC.
+ *
+ * This field is used for EAP-SIM and EAP-AKA.
+ */
+ char *pcsc;
+
+ /**
+ * pin - PIN for USIM, GSM SIM, and smartcards
+ *
+ * This field is used to configure PIN for SIM and smartcards for
+ * EAP-SIM and EAP-AKA. In addition, this is used with EAP-TLS if a
+ * smartcard is used for private key operations.
+ *
+ * If left out, this will be asked through control interface.
+ */
+ char *pin;
+
+ /**
+ * engine - Enable OpenSSL engine (e.g., for smartcard access)
+ *
+ * This is used if private key operations for EAP-TLS are performed
+ * using a smartcard.
+ */
+ int engine;
+
+ /**
+ * engine_id - Engine ID for OpenSSL engine
+ *
+ * "opensc" to select OpenSC engine or "pkcs11" to select PKCS#11
+ * engine.
+ *
+ * This is used if private key operations for EAP-TLS are performed
+ * using a smartcard.
+ */
+ char *engine_id;
+
+ /**
+ * engine2 - Enable OpenSSL engine (e.g., for smartcard) (Phase 2)
+ *
+ * This is used if private key operations for EAP-TLS are performed
+ * using a smartcard.
+ *
+ * This field is like engine, but used for phase 2 (inside
+ * EAP-TTLS/PEAP/FAST tunnel) authentication.
+ */
+ int engine2;
+
+
+ /**
+ * pin2 - PIN for USIM, GSM SIM, and smartcards (Phase 2)
+ *
+ * This field is used to configure PIN for SIM and smartcards for
+ * EAP-SIM and EAP-AKA. In addition, this is used with EAP-TLS if a
+ * smartcard is used for private key operations.
+ *
+ * This field is like pin2, but used for phase 2 (inside
+ * EAP-TTLS/PEAP/FAST tunnel) authentication.
+ *
+ * If left out, this will be asked through control interface.
+ */
+ char *pin2;
+
+ /**
+ * engine2_id - Engine ID for OpenSSL engine (Phase 2)
+ *
+ * "opensc" to select OpenSC engine or "pkcs11" to select PKCS#11
+ * engine.
+ *
+ * This is used if private key operations for EAP-TLS are performed
+ * using a smartcard.
+ *
+ * This field is like engine_id, but used for phase 2 (inside
+ * EAP-TTLS/PEAP/FAST tunnel) authentication.
+ */
+ char *engine2_id;
+
+
+ /**
+ * key_id - Key ID for OpenSSL engine
+ *
+ * This is used if private key operations for EAP-TLS are performed
+ * using a smartcard.
+ */
+ char *key_id;
+
+ /**
+ * cert_id - Cert ID for OpenSSL engine
+ *
+ * This is used if the certificate operations for EAP-TLS are performed
+ * using a smartcard.
+ */
+ char *cert_id;
+
+ /**
+ * ca_cert_id - CA Cert ID for OpenSSL engine
+ *
+ * This is used if the CA certificate for EAP-TLS is on a smartcard.
+ */
+ char *ca_cert_id;
+
+ /**
+ * key2_id - Key ID for OpenSSL engine (phase2)
+ *
+ * This is used if private key operations for EAP-TLS are performed
+ * using a smartcard.
+ */
+ char *key2_id;
+
+ /**
+ * cert2_id - Cert ID for OpenSSL engine (phase2)
+ *
+ * This is used if the certificate operations for EAP-TLS are performed
+ * using a smartcard.
+ */
+ char *cert2_id;
+
+ /**
+ * ca_cert2_id - CA Cert ID for OpenSSL engine (phase2)
+ *
+ * This is used if the CA certificate for EAP-TLS is on a smartcard.
+ */
+ char *ca_cert2_id;
+
+ /**
+ * otp - One-time-password
+ *
+ * This field should not be set in configuration step. It is only used
+ * internally when OTP is entered through the control interface.
+ */
+ u8 *otp;
+
+ /**
+ * otp_len - Length of the otp field
+ */
+ size_t otp_len;
+
+ /**
+ * pending_req_identity - Whether there is a pending identity request
+ *
+ * This field should not be set in configuration step. It is only used
+ * internally when control interface is used to request needed
+ * information.
+ */
+ int pending_req_identity;
+
+ /**
+ * pending_req_password - Whether there is a pending password request
+ *
+ * This field should not be set in configuration step. It is only used
+ * internally when control interface is used to request needed
+ * information.
+ */
+ int pending_req_password;
+
+ /**
+ * pending_req_pin - Whether there is a pending PIN request
+ *
+ * This field should not be set in configuration step. It is only used
+ * internally when control interface is used to request needed
+ * information.
+ */
+ int pending_req_pin;
+
+ /**
+ * pending_req_new_password - Pending password update request
+ *
+ * This field should not be set in configuration step. It is only used
+ * internally when control interface is used to request needed
+ * information.
+ */
+ int pending_req_new_password;
+
+ /**
+ * pending_req_passphrase - Pending passphrase request
+ *
+ * This field should not be set in configuration step. It is only used
+ * internally when control interface is used to request needed
+ * information.
+ */
+ int pending_req_passphrase;
+
+ /**
+ * pending_req_otp - Whether there is a pending OTP request
+ *
+ * This field should not be set in configuration step. It is only used
+ * internally when control interface is used to request needed
+ * information.
+ */
+ char *pending_req_otp;
+
+ /**
+ * pending_req_otp_len - Length of the pending OTP request
+ */
+ size_t pending_req_otp_len;
+
+ /**
+ * pac_file - File path or blob name for the PAC entries (EAP-FAST)
+ *
+ * wpa_supplicant will need to be able to create this file and write
+ * updates to it when PAC is being provisioned or refreshed. Full path
+ * to the file should be used since working directory may change when
+ * wpa_supplicant is run in the background.
+ * Alternatively, a named configuration blob can be used by setting
+ * this to blob://blob_name.
+ */
+ char *pac_file;
+
+ /**
+ * mschapv2_retry - MSCHAPv2 retry in progress
+ *
+ * This field is used internally by EAP-MSCHAPv2 and should not be set
+ * as part of configuration.
+ */
+ int mschapv2_retry;
+
+ /**
+ * new_password - New password for password update
+ *
+ * This field is used during MSCHAPv2 password update. This is normally
+ * requested from the user through the control interface and not set
+ * from configuration.
+ */
+ u8 *new_password;
+
+ /**
+ * new_password_len - Length of new_password field
+ */
+ size_t new_password_len;
+
+ /**
+ * fragment_size - Maximum EAP fragment size in bytes (default 1398)
+ *
+ * This value limits the fragment size for EAP methods that support
+ * fragmentation (e.g., EAP-TLS and EAP-PEAP). This value should be set
+ * small enough to make the EAP messages fit in MTU of the network
+ * interface used for EAPOL. The default value is suitable for most
+ * cases.
+ */
+ int fragment_size;
+
+#define EAP_CONFIG_FLAGS_PASSWORD_NTHASH BIT(0)
+#define EAP_CONFIG_FLAGS_EXT_PASSWORD BIT(1)
+ /**
+ * flags - Network configuration flags (bitfield)
+ *
+ * This variable is used for internal flags to describe further details
+ * for the network parameters.
+ * bit 0 = password is represented as a 16-byte NtPasswordHash value
+ * instead of plaintext password
+ * bit 1 = password is stored in external storage; the value in the
+ * password field is the name of that external entry
+ */
+ u32 flags;
+
+ /**
+ * ocsp - Whether to use/require OCSP to check server certificate
+ *
+ * 0 = do not use OCSP stapling (TLS certificate status extension)
+ * 1 = try to use OCSP stapling, but not require response
+ * 2 = require valid OCSP stapling response
+ */
+ int ocsp;
+
+ /**
+ * external_sim_resp - Response from external SIM processing
+ *
+ * This field should not be set in configuration step. It is only used
+ * internally when control interface is used to request external
+ * SIM/USIM processing.
+ */
+ char *external_sim_resp;
+
+ /**
+ * sim_num - User selected SIM identifier
+ *
+ * This variable is used for identifying which SIM is used if the system
+ * has more than one.
+ */
+ int sim_num;
+
+ /**
+ * openssl_ciphers - OpenSSL cipher string
+ *
+ * This is an OpenSSL specific configuration option for configuring the
+ * ciphers for this connection. If not set, the default cipher suite
+ * list is used.
+ */
+ char *openssl_ciphers;
+
+ /**
+ * erp - Whether EAP Re-authentication Protocol (ERP) is enabled
+ */
+ int erp;
+};
+
+
+/**
+ * struct wpa_config_blob - Named configuration blob
+ *
+ * This data structure is used to provide storage for binary objects to store
+ * abstract information like certificates and private keys inlined with the
+ * configuration data.
+ */
+struct wpa_config_blob {
+ /**
+ * name - Blob name
+ */
+ char *name;
+
+ /**
+ * data - Pointer to binary data
+ */
+ u8 *data;
+
+ /**
+ * len - Length of binary data
+ */
+ size_t len;
+
+ /**
+ * next - Pointer to next blob in the configuration
+ */
+ struct wpa_config_blob *next;
+};
+
+#endif /* EAP_CONFIG_H */
diff --git a/freebsd/contrib/wpa/src/eap_peer/eap_gtc.c b/freebsd/contrib/wpa/src/eap_peer/eap_gtc.c
new file mode 100644
index 00000000..2ffc142c
--- /dev/null
+++ b/freebsd/contrib/wpa/src/eap_peer/eap_gtc.c
@@ -0,0 +1,147 @@
+#include <machine/rtems-bsd-user-space.h>
+
+/*
+ * EAP peer method: EAP-GTC (RFC 3748)
+ * Copyright (c) 2004-2006, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "eap_i.h"
+
+
+struct eap_gtc_data {
+ int prefix;
+};
+
+
+static void * eap_gtc_init(struct eap_sm *sm)
+{
+ struct eap_gtc_data *data;
+ data = os_zalloc(sizeof(*data));
+ if (data == NULL)
+ return NULL;
+
+ if (sm->m && sm->m->vendor == EAP_VENDOR_IETF &&
+ sm->m->method == EAP_TYPE_FAST) {
+ wpa_printf(MSG_DEBUG, "EAP-GTC: EAP-FAST tunnel - use prefix "
+ "with challenge/response");
+ data->prefix = 1;
+ }
+ return data;
+}
+
+
+static void eap_gtc_deinit(struct eap_sm *sm, void *priv)
+{
+ struct eap_gtc_data *data = priv;
+ os_free(data);
+}
+
+
+static struct wpabuf * eap_gtc_process(struct eap_sm *sm, void *priv,
+ struct eap_method_ret *ret,
+ const struct wpabuf *reqData)
+{
+ struct eap_gtc_data *data = priv;
+ struct wpabuf *resp;
+ const u8 *pos, *password, *identity;
+ size_t password_len, identity_len, len, plen;
+ int otp;
+ u8 id;
+
+ pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_GTC, reqData, &len);
+ if (pos == NULL) {
+ ret->ignore = TRUE;
+ return NULL;
+ }
+ id = eap_get_id(reqData);
+
+ wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-GTC: Request message", pos, len);
+ if (data->prefix &&
+ (len < 10 || os_memcmp(pos, "CHALLENGE=", 10) != 0)) {
+ wpa_printf(MSG_DEBUG, "EAP-GTC: Challenge did not start with "
+ "expected prefix");
+
+ /* Send an empty response in order to allow tunneled
+ * acknowledgement of the failure. This will also cover the
+ * error case which seems to use EAP-MSCHAPv2 like error
+ * reporting with EAP-GTC inside EAP-FAST tunnel. */
+ resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_GTC,
+ 0, EAP_CODE_RESPONSE, id);
+ return resp;
+ }
+
+ password = eap_get_config_otp(sm, &password_len);
+ if (password)
+ otp = 1;
+ else {
+ password = eap_get_config_password(sm, &password_len);
+ otp = 0;
+ }
+
+ if (password == NULL) {
+ wpa_printf(MSG_INFO, "EAP-GTC: Password not configured");
+ eap_sm_request_otp(sm, (const char *) pos, len);
+ ret->ignore = TRUE;
+ return NULL;
+ }
+
+ ret->ignore = FALSE;
+
+ ret->methodState = data->prefix ? METHOD_MAY_CONT : METHOD_DONE;
+ ret->decision = DECISION_COND_SUCC;
+ ret->allowNotifications = FALSE;
+
+ plen = password_len;
+ identity = eap_get_config_identity(sm, &identity_len);
+ if (identity == NULL)
+ return NULL;
+ if (data->prefix)
+ plen += 9 + identity_len + 1;
+ resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_GTC, plen,
+ EAP_CODE_RESPONSE, id);
+ if (resp == NULL)
+ return NULL;
+ if (data->prefix) {
+ wpabuf_put_data(resp, "RESPONSE=", 9);
+ wpabuf_put_data(resp, identity, identity_len);
+ wpabuf_put_u8(resp, '\0');
+ }
+ wpabuf_put_data(resp, password, password_len);
+ wpa_hexdump_ascii_key(MSG_MSGDUMP, "EAP-GTC: Response",
+ wpabuf_head_u8(resp) + sizeof(struct eap_hdr) +
+ 1, plen);
+
+ if (otp) {
+ wpa_printf(MSG_DEBUG, "EAP-GTC: Forgetting used password");
+ eap_clear_config_otp(sm);
+ }
+
+ return resp;
+}
+
+
+int eap_peer_gtc_register(void)
+{
+ struct eap_method *eap;
+ int ret;
+
+ eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION,
+ EAP_VENDOR_IETF, EAP_TYPE_GTC, "GTC");
+ if (eap == NULL)
+ return -1;
+
+ eap->init = eap_gtc_init;
+ eap->deinit = eap_gtc_deinit;
+ eap->process = eap_gtc_process;
+
+ ret = eap_peer_method_register(eap);
+ if (ret)
+ eap_peer_method_free(eap);
+ return ret;
+}
diff --git a/freebsd/contrib/wpa/src/eap_peer/eap_i.h b/freebsd/contrib/wpa/src/eap_peer/eap_i.h
new file mode 100644
index 00000000..99b44dae
--- /dev/null
+++ b/freebsd/contrib/wpa/src/eap_peer/eap_i.h
@@ -0,0 +1,389 @@
+/*
+ * EAP peer state machines internal structures (RFC 4137)
+ * Copyright (c) 2004-2014, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef EAP_I_H
+#define EAP_I_H
+
+#include "wpabuf.h"
+#include "utils/list.h"
+#include "eap_peer/eap.h"
+#include "eap_common/eap_common.h"
+
+/* RFC 4137 - EAP Peer state machine */
+
+typedef enum {
+ DECISION_FAIL, DECISION_COND_SUCC, DECISION_UNCOND_SUCC
+} EapDecision;
+
+typedef enum {
+ METHOD_NONE, METHOD_INIT, METHOD_CONT, METHOD_MAY_CONT, METHOD_DONE
+} EapMethodState;
+
+/**
+ * struct eap_method_ret - EAP return values from struct eap_method::process()
+ *
+ * These structure contains OUT variables for the interface between peer state
+ * machine and methods (RFC 4137, Sect. 4.2). eapRespData will be returned as
+ * the return value of struct eap_method::process() so it is not included in
+ * this structure.
+ */
+struct eap_method_ret {
+ /**
+ * ignore - Whether method decided to drop the current packed (OUT)
+ */
+ Boolean ignore;
+
+ /**
+ * methodState - Method-specific state (IN/OUT)
+ */
+ EapMethodState methodState;
+
+ /**
+ * decision - Authentication decision (OUT)
+ */
+ EapDecision decision;
+
+ /**
+ * allowNotifications - Whether method allows notifications (OUT)
+ */
+ Boolean allowNotifications;
+};
+
+
+/**
+ * struct eap_method - EAP method interface
+ * This structure defines the EAP method interface. Each method will need to
+ * register its own EAP type, EAP name, and set of function pointers for method
+ * specific operations. This interface is based on section 4.4 of RFC 4137.
+ */
+struct eap_method {
+ /**
+ * vendor - EAP Vendor-ID (EAP_VENDOR_*) (0 = IETF)
+ */
+ int vendor;
+
+ /**
+ * method - EAP type number (EAP_TYPE_*)
+ */
+ EapType method;
+
+ /**
+ * name - Name of the method (e.g., "TLS")
+ */
+ const char *name;
+
+ /**
+ * init - Initialize an EAP method
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * Returns: Pointer to allocated private data, or %NULL on failure
+ *
+ * This function is used to initialize the EAP method explicitly
+ * instead of using METHOD_INIT state as specific in RFC 4137. The
+ * method is expected to initialize it method-specific state and return
+ * a pointer that will be used as the priv argument to other calls.
+ */
+ void * (*init)(struct eap_sm *sm);
+
+ /**
+ * deinit - Deinitialize an EAP method
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @priv: Pointer to private EAP method data from eap_method::init()
+ *
+ * Deinitialize the EAP method and free any allocated private data.
+ */
+ void (*deinit)(struct eap_sm *sm, void *priv);
+
+ /**
+ * process - Process an EAP request
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @priv: Pointer to private EAP method data from eap_method::init()
+ * @ret: Return values from EAP request validation and processing
+ * @reqData: EAP request to be processed (eapReqData)
+ * Returns: Pointer to allocated EAP response packet (eapRespData)
+ *
+ * This function is a combination of m.check(), m.process(), and
+ * m.buildResp() procedures defined in section 4.4 of RFC 4137 In other
+ * words, this function validates the incoming request, processes it,
+ * and build a response packet. m.check() and m.process() return values
+ * are returned through struct eap_method_ret *ret variable. Caller is
+ * responsible for freeing the returned EAP response packet.
+ */
+ struct wpabuf * (*process)(struct eap_sm *sm, void *priv,
+ struct eap_method_ret *ret,
+ const struct wpabuf *reqData);
+
+ /**
+ * isKeyAvailable - Find out whether EAP method has keying material
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @priv: Pointer to private EAP method data from eap_method::init()
+ * Returns: %TRUE if key material (eapKeyData) is available
+ */
+ Boolean (*isKeyAvailable)(struct eap_sm *sm, void *priv);
+
+ /**
+ * getKey - Get EAP method specific keying material (eapKeyData)
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @priv: Pointer to private EAP method data from eap_method::init()
+ * @len: Pointer to variable to store key length (eapKeyDataLen)
+ * Returns: Keying material (eapKeyData) or %NULL if not available
+ *
+ * This function can be used to get the keying material from the EAP
+ * method. The key may already be stored in the method-specific private
+ * data or this function may derive the key.
+ */
+ u8 * (*getKey)(struct eap_sm *sm, void *priv, size_t *len);
+
+ /**
+ * get_status - Get EAP method status
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @priv: Pointer to private EAP method data from eap_method::init()
+ * @buf: Buffer for status information
+ * @buflen: Maximum buffer length
+ * @verbose: Whether to include verbose status information
+ * Returns: Number of bytes written to buf
+ *
+ * Query EAP method for status information. This function fills in a
+ * text area with current status information from the EAP method. If
+ * the buffer (buf) is not large enough, status information will be
+ * truncated to fit the buffer.
+ */
+ int (*get_status)(struct eap_sm *sm, void *priv, char *buf,
+ size_t buflen, int verbose);
+
+ /**
+ * has_reauth_data - Whether method is ready for fast reauthentication
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @priv: Pointer to private EAP method data from eap_method::init()
+ * Returns: %TRUE or %FALSE based on whether fast reauthentication is
+ * possible
+ *
+ * This function is an optional handler that only EAP methods
+ * supporting fast re-authentication need to implement.
+ */
+ Boolean (*has_reauth_data)(struct eap_sm *sm, void *priv);
+
+ /**
+ * deinit_for_reauth - Release data that is not needed for fast re-auth
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @priv: Pointer to private EAP method data from eap_method::init()
+ *
+ * This function is an optional handler that only EAP methods
+ * supporting fast re-authentication need to implement. This is called
+ * when authentication has been completed and EAP state machine is
+ * requesting that enough state information is maintained for fast
+ * re-authentication
+ */
+ void (*deinit_for_reauth)(struct eap_sm *sm, void *priv);
+
+ /**
+ * init_for_reauth - Prepare for start of fast re-authentication
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @priv: Pointer to private EAP method data from eap_method::init()
+ *
+ * This function is an optional handler that only EAP methods
+ * supporting fast re-authentication need to implement. This is called
+ * when EAP authentication is started and EAP state machine is
+ * requesting fast re-authentication to be used.
+ */
+ void * (*init_for_reauth)(struct eap_sm *sm, void *priv);
+
+ /**
+ * get_identity - Get method specific identity for re-authentication
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @priv: Pointer to private EAP method data from eap_method::init()
+ * @len: Length of the returned identity
+ * Returns: Pointer to the method specific identity or %NULL if default
+ * identity is to be used
+ *
+ * This function is an optional handler that only EAP methods
+ * that use method specific identity need to implement.
+ */
+ const u8 * (*get_identity)(struct eap_sm *sm, void *priv, size_t *len);
+
+ /**
+ * free - Free EAP method data
+ * @method: Pointer to the method data registered with
+ * eap_peer_method_register().
+ *
+ * This function will be called when the EAP method is being
+ * unregistered. If the EAP method allocated resources during
+ * registration (e.g., allocated struct eap_method), they should be
+ * freed in this function. No other method functions will be called
+ * after this call. If this function is not defined (i.e., function
+ * pointer is %NULL), a default handler is used to release the method
+ * data with free(method). This is suitable for most cases.
+ */
+ void (*free)(struct eap_method *method);
+
+#define EAP_PEER_METHOD_INTERFACE_VERSION 1
+ /**
+ * version - Version of the EAP peer method interface
+ *
+ * The EAP peer method implementation should set this variable to
+ * EAP_PEER_METHOD_INTERFACE_VERSION. This is used to verify that the
+ * EAP method is using supported API version when using dynamically
+ * loadable EAP methods.
+ */
+ int version;
+
+ /**
+ * next - Pointer to the next EAP method
+ *
+ * This variable is used internally in the EAP method registration code
+ * to create a linked list of registered EAP methods.
+ */
+ struct eap_method *next;
+
+#ifdef CONFIG_DYNAMIC_EAP_METHODS
+ /**
+ * dl_handle - Handle for the dynamic library
+ *
+ * This variable is used internally in the EAP method registration code
+ * to store a handle for the dynamic library. If the method is linked
+ * in statically, this is %NULL.
+ */
+ void *dl_handle;
+#endif /* CONFIG_DYNAMIC_EAP_METHODS */
+
+ /**
+ * get_emsk - Get EAP method specific keying extended material (EMSK)
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @priv: Pointer to private EAP method data from eap_method::init()
+ * @len: Pointer to a variable to store EMSK length
+ * Returns: EMSK or %NULL if not available
+ *
+ * This function can be used to get the extended keying material from
+ * the EAP method. The key may already be stored in the method-specific
+ * private data or this function may derive the key.
+ */
+ u8 * (*get_emsk)(struct eap_sm *sm, void *priv, size_t *len);
+
+ /**
+ * getSessionId - Get EAP method specific Session-Id
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @priv: Pointer to private EAP method data from eap_method::init()
+ * @len: Pointer to a variable to store Session-Id length
+ * Returns: Session-Id or %NULL if not available
+ *
+ * This function can be used to get the Session-Id from the EAP method.
+ * The Session-Id may already be stored in the method-specific private
+ * data or this function may derive the Session-Id.
+ */
+ u8 * (*getSessionId)(struct eap_sm *sm, void *priv, size_t *len);
+};
+
+
+struct eap_erp_key {
+ struct dl_list list;
+ size_t rRK_len;
+ size_t rIK_len;
+ u8 rRK[ERP_MAX_KEY_LEN];
+ u8 rIK[ERP_MAX_KEY_LEN];
+ u32 next_seq;
+ char keyname_nai[];
+};
+
+/**
+ * struct eap_sm - EAP state machine data
+ */
+struct eap_sm {
+ enum {
+ EAP_INITIALIZE, EAP_DISABLED, EAP_IDLE, EAP_RECEIVED,
+ EAP_GET_METHOD, EAP_METHOD, EAP_SEND_RESPONSE, EAP_DISCARD,
+ EAP_IDENTITY, EAP_NOTIFICATION, EAP_RETRANSMIT, EAP_SUCCESS,
+ EAP_FAILURE
+ } EAP_state;
+ /* Long-term local variables */
+ EapType selectedMethod;
+ EapMethodState methodState;
+ int lastId;
+ struct wpabuf *lastRespData;
+ EapDecision decision;
+ /* Short-term local variables */
+ Boolean rxReq;
+ Boolean rxSuccess;
+ Boolean rxFailure;
+ int reqId;
+ EapType reqMethod;
+ int reqVendor;
+ u32 reqVendorMethod;
+ Boolean ignore;
+ /* Constants */
+ int ClientTimeout;
+
+ /* Miscellaneous variables */
+ Boolean allowNotifications; /* peer state machine <-> methods */
+ struct wpabuf *eapRespData; /* peer to lower layer */
+ Boolean eapKeyAvailable; /* peer to lower layer */
+ u8 *eapKeyData; /* peer to lower layer */
+ size_t eapKeyDataLen; /* peer to lower layer */
+ u8 *eapSessionId; /* peer to lower layer */
+ size_t eapSessionIdLen; /* peer to lower layer */
+ const struct eap_method *m; /* selected EAP method */
+ /* not defined in RFC 4137 */
+ Boolean changed;
+ void *eapol_ctx;
+ const struct eapol_callbacks *eapol_cb;
+ void *eap_method_priv;
+ int init_phase2;
+ int fast_reauth;
+ Boolean reauthInit; /* send EAP-Identity/Re-auth */
+ u32 erp_seq;
+
+ Boolean rxResp /* LEAP only */;
+ Boolean leap_done;
+ Boolean peap_done;
+ u8 req_sha1[20]; /* SHA1() of the current EAP packet */
+ u8 last_sha1[20]; /* SHA1() of the previously received EAP packet; used
+ * in duplicate request detection. */
+
+ void *msg_ctx;
+ void *scard_ctx;
+ void *ssl_ctx;
+ void *ssl_ctx2;
+
+ unsigned int workaround;
+
+ /* Optional challenges generated in Phase 1 (EAP-FAST) */
+ u8 *peer_challenge, *auth_challenge;
+
+ int num_rounds;
+ int force_disabled;
+
+ struct wps_context *wps;
+
+ int prev_failure;
+ struct eap_peer_config *last_config;
+
+ struct ext_password_data *ext_pw;
+ struct wpabuf *ext_pw_buf;
+
+ int external_sim;
+
+ unsigned int expected_failure:1;
+
+ struct dl_list erp_keys; /* struct eap_erp_key */
+};
+
+const u8 * eap_get_config_identity(struct eap_sm *sm, size_t *len);
+const u8 * eap_get_config_password(struct eap_sm *sm, size_t *len);
+const u8 * eap_get_config_password2(struct eap_sm *sm, size_t *len, int *hash);
+const u8 * eap_get_config_new_password(struct eap_sm *sm, size_t *len);
+const u8 * eap_get_config_otp(struct eap_sm *sm, size_t *len);
+void eap_clear_config_otp(struct eap_sm *sm);
+const char * eap_get_config_phase1(struct eap_sm *sm);
+const char * eap_get_config_phase2(struct eap_sm *sm);
+int eap_get_config_fragment_size(struct eap_sm *sm);
+struct eap_peer_config * eap_get_config(struct eap_sm *sm);
+void eap_set_config_blob(struct eap_sm *sm, struct wpa_config_blob *blob);
+const struct wpa_config_blob *
+eap_get_config_blob(struct eap_sm *sm, const char *name);
+void eap_notify_pending(struct eap_sm *sm);
+int eap_allowed_method(struct eap_sm *sm, int vendor, u32 method);
+
+#endif /* EAP_I_H */
diff --git a/freebsd/contrib/wpa/src/eap_peer/eap_leap.c b/freebsd/contrib/wpa/src/eap_peer/eap_leap.c
new file mode 100644
index 00000000..e765af14
--- /dev/null
+++ b/freebsd/contrib/wpa/src/eap_peer/eap_leap.c
@@ -0,0 +1,415 @@
+#include <machine/rtems-bsd-user-space.h>
+
+/*
+ * EAP peer method: LEAP
+ * Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "crypto/ms_funcs.h"
+#include "crypto/crypto.h"
+#include "crypto/random.h"
+#include "eap_i.h"
+
+#define LEAP_VERSION 1
+#define LEAP_CHALLENGE_LEN 8
+#define LEAP_RESPONSE_LEN 24
+#define LEAP_KEY_LEN 16
+
+
+struct eap_leap_data {
+ enum {
+ LEAP_WAIT_CHALLENGE,
+ LEAP_WAIT_SUCCESS,
+ LEAP_WAIT_RESPONSE,
+ LEAP_DONE
+ } state;
+
+ u8 peer_challenge[LEAP_CHALLENGE_LEN];
+ u8 peer_response[LEAP_RESPONSE_LEN];
+
+ u8 ap_challenge[LEAP_CHALLENGE_LEN];
+ u8 ap_response[LEAP_RESPONSE_LEN];
+};
+
+
+static void * eap_leap_init(struct eap_sm *sm)
+{
+ struct eap_leap_data *data;
+
+ data = os_zalloc(sizeof(*data));
+ if (data == NULL)
+ return NULL;
+ data->state = LEAP_WAIT_CHALLENGE;
+
+ sm->leap_done = FALSE;
+ return data;
+}
+
+
+static void eap_leap_deinit(struct eap_sm *sm, void *priv)
+{
+ os_free(priv);
+}
+
+
+static struct wpabuf * eap_leap_process_request(struct eap_sm *sm, void *priv,
+ struct eap_method_ret *ret,
+ const struct wpabuf *reqData)
+{
+ struct eap_leap_data *data = priv;
+ struct wpabuf *resp;
+ const u8 *pos, *challenge, *identity, *password;
+ u8 challenge_len, *rpos;
+ size_t identity_len, password_len, len;
+ int pwhash;
+
+ wpa_printf(MSG_DEBUG, "EAP-LEAP: Processing EAP-Request");
+
+ identity = eap_get_config_identity(sm, &identity_len);
+ password = eap_get_config_password2(sm, &password_len, &pwhash);
+ if (identity == NULL || password == NULL)
+ return NULL;
+
+ pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_LEAP, reqData, &len);
+ if (pos == NULL || len < 3) {
+ wpa_printf(MSG_INFO, "EAP-LEAP: Invalid EAP-Request frame");
+ ret->ignore = TRUE;
+ return NULL;
+ }
+
+ if (*pos != LEAP_VERSION) {
+ wpa_printf(MSG_WARNING, "EAP-LEAP: Unsupported LEAP version "
+ "%d", *pos);
+ ret->ignore = TRUE;
+ return NULL;
+ }
+ pos++;
+
+ pos++; /* skip unused byte */
+
+ challenge_len = *pos++;
+ if (challenge_len != LEAP_CHALLENGE_LEN || challenge_len > len - 3) {
+ wpa_printf(MSG_INFO, "EAP-LEAP: Invalid challenge "
+ "(challenge_len=%d reqDataLen=%lu)",
+ challenge_len, (unsigned long) wpabuf_len(reqData));
+ ret->ignore = TRUE;
+ return NULL;
+ }
+ challenge = pos;
+ os_memcpy(data->peer_challenge, challenge, LEAP_CHALLENGE_LEN);
+ wpa_hexdump(MSG_MSGDUMP, "EAP-LEAP: Challenge from AP",
+ challenge, LEAP_CHALLENGE_LEN);
+
+ wpa_printf(MSG_DEBUG, "EAP-LEAP: Generating Challenge Response");
+
+ resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_LEAP,
+ 3 + LEAP_RESPONSE_LEN + identity_len,
+ EAP_CODE_RESPONSE, eap_get_id(reqData));
+ if (resp == NULL)
+ return NULL;
+ wpabuf_put_u8(resp, LEAP_VERSION);
+ wpabuf_put_u8(resp, 0); /* unused */
+ wpabuf_put_u8(resp, LEAP_RESPONSE_LEN);
+ rpos = wpabuf_put(resp, LEAP_RESPONSE_LEN);
+ if (pwhash)
+ challenge_response(challenge, password, rpos);
+ else
+ nt_challenge_response(challenge, password, password_len, rpos);
+ os_memcpy(data->peer_response, rpos, LEAP_RESPONSE_LEN);
+ wpa_hexdump(MSG_MSGDUMP, "EAP-LEAP: Response",
+ rpos, LEAP_RESPONSE_LEN);
+ wpabuf_put_data(resp, identity, identity_len);
+
+ data->state = LEAP_WAIT_SUCCESS;
+
+ return resp;
+}
+
+
+static struct wpabuf * eap_leap_process_success(struct eap_sm *sm, void *priv,
+ struct eap_method_ret *ret,
+ const struct wpabuf *reqData)
+{
+ struct eap_leap_data *data = priv;
+ struct wpabuf *resp;
+ u8 *pos;
+ const u8 *identity;
+ size_t identity_len;
+
+ wpa_printf(MSG_DEBUG, "EAP-LEAP: Processing EAP-Success");
+
+ identity = eap_get_config_identity(sm, &identity_len);
+ if (identity == NULL)
+ return NULL;
+
+ if (data->state != LEAP_WAIT_SUCCESS) {
+ wpa_printf(MSG_INFO, "EAP-LEAP: EAP-Success received in "
+ "unexpected state (%d) - ignored", data->state);
+ ret->ignore = TRUE;
+ return NULL;
+ }
+
+ resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_LEAP,
+ 3 + LEAP_CHALLENGE_LEN + identity_len,
+ EAP_CODE_REQUEST, eap_get_id(reqData));
+ if (resp == NULL)
+ return NULL;
+ wpabuf_put_u8(resp, LEAP_VERSION);
+ wpabuf_put_u8(resp, 0); /* unused */
+ wpabuf_put_u8(resp, LEAP_CHALLENGE_LEN);
+ pos = wpabuf_put(resp, LEAP_CHALLENGE_LEN);
+ if (random_get_bytes(pos, LEAP_CHALLENGE_LEN)) {
+ wpa_printf(MSG_WARNING, "EAP-LEAP: Failed to read random data "
+ "for challenge");
+ wpabuf_free(resp);
+ ret->ignore = TRUE;
+ return NULL;
+ }
+ os_memcpy(data->ap_challenge, pos, LEAP_CHALLENGE_LEN);
+ wpa_hexdump(MSG_MSGDUMP, "EAP-LEAP: Challenge to AP/AS", pos,
+ LEAP_CHALLENGE_LEN);
+ wpabuf_put_data(resp, identity, identity_len);
+
+ data->state = LEAP_WAIT_RESPONSE;
+
+ return resp;
+}
+
+
+static struct wpabuf * eap_leap_process_response(struct eap_sm *sm, void *priv,
+ struct eap_method_ret *ret,
+ const struct wpabuf *reqData)
+{
+ struct eap_leap_data *data = priv;
+ const u8 *pos, *password;
+ u8 response_len, pw_hash[16], pw_hash_hash[16],
+ expected[LEAP_RESPONSE_LEN];
+ size_t password_len, len;
+ int pwhash;
+
+ wpa_printf(MSG_DEBUG, "EAP-LEAP: Processing EAP-Response");
+
+ password = eap_get_config_password2(sm, &password_len, &pwhash);
+ if (password == NULL)
+ return NULL;
+
+ pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_LEAP, reqData, &len);
+ if (pos == NULL || len < 3) {
+ wpa_printf(MSG_INFO, "EAP-LEAP: Invalid EAP-Response frame");
+ ret->ignore = TRUE;
+ return NULL;
+ }
+
+ if (*pos != LEAP_VERSION) {
+ wpa_printf(MSG_WARNING, "EAP-LEAP: Unsupported LEAP version "
+ "%d", *pos);
+ ret->ignore = TRUE;
+ return NULL;
+ }
+ pos++;
+
+ pos++; /* skip unused byte */
+
+ response_len = *pos++;
+ if (response_len != LEAP_RESPONSE_LEN || response_len > len - 3) {
+ wpa_printf(MSG_INFO, "EAP-LEAP: Invalid response "
+ "(response_len=%d reqDataLen=%lu)",
+ response_len, (unsigned long) wpabuf_len(reqData));
+ ret->ignore = TRUE;
+ return NULL;
+ }
+
+ wpa_hexdump(MSG_DEBUG, "EAP-LEAP: Response from AP",
+ pos, LEAP_RESPONSE_LEN);
+ os_memcpy(data->ap_response, pos, LEAP_RESPONSE_LEN);
+
+ if (pwhash) {
+ if (hash_nt_password_hash(password, pw_hash_hash)) {
+ ret->ignore = TRUE;
+ return NULL;
+ }
+ } else {
+ if (nt_password_hash(password, password_len, pw_hash) ||
+ hash_nt_password_hash(pw_hash, pw_hash_hash)) {
+ ret->ignore = TRUE;
+ return NULL;
+ }
+ }
+ challenge_response(data->ap_challenge, pw_hash_hash, expected);
+
+ ret->methodState = METHOD_DONE;
+ ret->allowNotifications = FALSE;
+
+ if (os_memcmp_const(pos, expected, LEAP_RESPONSE_LEN) != 0) {
+ wpa_printf(MSG_WARNING, "EAP-LEAP: AP sent an invalid "
+ "response - authentication failed");
+ wpa_hexdump(MSG_DEBUG, "EAP-LEAP: Expected response from AP",
+ expected, LEAP_RESPONSE_LEN);
+ ret->decision = DECISION_FAIL;
+ return NULL;
+ }
+
+ ret->decision = DECISION_UNCOND_SUCC;
+
+ /* LEAP is somewhat odd method since it sends EAP-Success in the middle
+ * of the authentication. Use special variable to transit EAP state
+ * machine to SUCCESS state. */
+ sm->leap_done = TRUE;
+ data->state = LEAP_DONE;
+
+ /* No more authentication messages expected; AP will send EAPOL-Key
+ * frames if encryption is enabled. */
+ return NULL;
+}
+
+
+static struct wpabuf * eap_leap_process(struct eap_sm *sm, void *priv,
+ struct eap_method_ret *ret,
+ const struct wpabuf *reqData)
+{
+ const struct eap_hdr *eap;
+ size_t password_len;
+ const u8 *password;
+
+ password = eap_get_config_password(sm, &password_len);
+ if (password == NULL) {
+ wpa_printf(MSG_INFO, "EAP-LEAP: Password not configured");
+ eap_sm_request_password(sm);
+ ret->ignore = TRUE;
+ return NULL;
+ }
+
+ /*
+ * LEAP needs to be able to handle EAP-Success frame which does not
+ * include Type field. Consequently, eap_hdr_validate() cannot be used
+ * here. This validation will be done separately for EAP-Request and
+ * EAP-Response frames.
+ */
+ eap = wpabuf_head(reqData);
+ if (wpabuf_len(reqData) < sizeof(*eap) ||
+ be_to_host16(eap->length) > wpabuf_len(reqData)) {
+ wpa_printf(MSG_INFO, "EAP-LEAP: Invalid frame");
+ ret->ignore = TRUE;
+ return NULL;
+ }
+
+ ret->ignore = FALSE;
+ ret->allowNotifications = TRUE;
+ ret->methodState = METHOD_MAY_CONT;
+ ret->decision = DECISION_FAIL;
+
+ sm->leap_done = FALSE;
+
+ switch (eap->code) {
+ case EAP_CODE_REQUEST:
+ return eap_leap_process_request(sm, priv, ret, reqData);
+ case EAP_CODE_SUCCESS:
+ return eap_leap_process_success(sm, priv, ret, reqData);
+ case EAP_CODE_RESPONSE:
+ return eap_leap_process_response(sm, priv, ret, reqData);
+ default:
+ wpa_printf(MSG_INFO, "EAP-LEAP: Unexpected EAP code (%d) - "
+ "ignored", eap->code);
+ ret->ignore = TRUE;
+ return NULL;
+ }
+}
+
+
+static Boolean eap_leap_isKeyAvailable(struct eap_sm *sm, void *priv)
+{
+ struct eap_leap_data *data = priv;
+ return data->state == LEAP_DONE;
+}
+
+
+static u8 * eap_leap_getKey(struct eap_sm *sm, void *priv, size_t *len)
+{
+ struct eap_leap_data *data = priv;
+ u8 *key, pw_hash_hash[16], pw_hash[16];
+ const u8 *addr[5], *password;
+ size_t elen[5], password_len;
+ int pwhash;
+
+ if (data->state != LEAP_DONE)
+ return NULL;
+
+ password = eap_get_config_password2(sm, &password_len, &pwhash);
+ if (password == NULL)
+ return NULL;
+
+ key = os_malloc(LEAP_KEY_LEN);
+ if (key == NULL)
+ return NULL;
+
+ if (pwhash) {
+ if (hash_nt_password_hash(password, pw_hash_hash)) {
+ os_free(key);
+ return NULL;
+ }
+ } else {
+ if (nt_password_hash(password, password_len, pw_hash) ||
+ hash_nt_password_hash(pw_hash, pw_hash_hash)) {
+ os_free(key);
+ return NULL;
+ }
+ }
+ wpa_hexdump_key(MSG_DEBUG, "EAP-LEAP: pw_hash_hash",
+ pw_hash_hash, 16);
+ wpa_hexdump(MSG_DEBUG, "EAP-LEAP: peer_challenge",
+ data->peer_challenge, LEAP_CHALLENGE_LEN);
+ wpa_hexdump(MSG_DEBUG, "EAP-LEAP: peer_response",
+ data->peer_response, LEAP_RESPONSE_LEN);
+ wpa_hexdump(MSG_DEBUG, "EAP-LEAP: ap_challenge",
+ data->ap_challenge, LEAP_CHALLENGE_LEN);
+ wpa_hexdump(MSG_DEBUG, "EAP-LEAP: ap_response",
+ data->ap_response, LEAP_RESPONSE_LEN);
+
+ addr[0] = pw_hash_hash;
+ elen[0] = 16;
+ addr[1] = data->ap_challenge;
+ elen[1] = LEAP_CHALLENGE_LEN;
+ addr[2] = data->ap_response;
+ elen[2] = LEAP_RESPONSE_LEN;
+ addr[3] = data->peer_challenge;
+ elen[3] = LEAP_CHALLENGE_LEN;
+ addr[4] = data->peer_response;
+ elen[4] = LEAP_RESPONSE_LEN;
+ md5_vector(5, addr, elen, key);
+ wpa_hexdump_key(MSG_DEBUG, "EAP-LEAP: master key", key, LEAP_KEY_LEN);
+ *len = LEAP_KEY_LEN;
+
+ os_memset(pw_hash, 0, sizeof(pw_hash));
+ os_memset(pw_hash_hash, 0, sizeof(pw_hash_hash));
+
+ return key;
+}
+
+
+int eap_peer_leap_register(void)
+{
+ struct eap_method *eap;
+ int ret;
+
+ eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION,
+ EAP_VENDOR_IETF, EAP_TYPE_LEAP, "LEAP");
+ if (eap == NULL)
+ return -1;
+
+ eap->init = eap_leap_init;
+ eap->deinit = eap_leap_deinit;
+ eap->process = eap_leap_process;
+ eap->isKeyAvailable = eap_leap_isKeyAvailable;
+ eap->getKey = eap_leap_getKey;
+
+ ret = eap_peer_method_register(eap);
+ if (ret)
+ eap_peer_method_free(eap);
+ return ret;
+}
diff --git a/freebsd/contrib/wpa/src/eap_peer/eap_md5.c b/freebsd/contrib/wpa/src/eap_peer/eap_md5.c
new file mode 100644
index 00000000..145bfa8f
--- /dev/null
+++ b/freebsd/contrib/wpa/src/eap_peer/eap_md5.c
@@ -0,0 +1,122 @@
+#include <machine/rtems-bsd-user-space.h>
+
+/*
+ * EAP peer method: EAP-MD5 (RFC 3748 and RFC 1994)
+ * Copyright (c) 2004-2012, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "eap_i.h"
+#include "eap_common/chap.h"
+
+
+static void * eap_md5_init(struct eap_sm *sm)
+{
+ /* No need for private data. However, must return non-NULL to indicate
+ * success. */
+ return (void *) 1;
+}
+
+
+static void eap_md5_deinit(struct eap_sm *sm, void *priv)
+{
+}
+
+
+static struct wpabuf * eap_md5_process(struct eap_sm *sm, void *priv,
+ struct eap_method_ret *ret,
+ const struct wpabuf *reqData)
+{
+ struct wpabuf *resp;
+ const u8 *pos, *challenge, *password;
+ u8 *rpos, id;
+ size_t len, challenge_len, password_len;
+
+ password = eap_get_config_password(sm, &password_len);
+ if (password == NULL) {
+ wpa_printf(MSG_INFO, "EAP-MD5: Password not configured");
+ eap_sm_request_password(sm);
+ ret->ignore = TRUE;
+ return NULL;
+ }
+
+ pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_MD5, reqData, &len);
+ if (pos == NULL || len == 0) {
+ wpa_printf(MSG_INFO, "EAP-MD5: Invalid frame (pos=%p len=%lu)",
+ pos, (unsigned long) len);
+ ret->ignore = TRUE;
+ return NULL;
+ }
+
+ /*
+ * CHAP Challenge:
+ * Value-Size (1 octet) | Value(Challenge) | Name(optional)
+ */
+ challenge_len = *pos++;
+ if (challenge_len == 0 || challenge_len > len - 1) {
+ wpa_printf(MSG_INFO, "EAP-MD5: Invalid challenge "
+ "(challenge_len=%lu len=%lu)",
+ (unsigned long) challenge_len, (unsigned long) len);
+ ret->ignore = TRUE;
+ return NULL;
+ }
+ ret->ignore = FALSE;
+ challenge = pos;
+ wpa_hexdump(MSG_MSGDUMP, "EAP-MD5: Challenge",
+ challenge, challenge_len);
+
+ wpa_printf(MSG_DEBUG, "EAP-MD5: Generating Challenge Response");
+ ret->methodState = METHOD_DONE;
+ ret->decision = DECISION_COND_SUCC;
+ ret->allowNotifications = TRUE;
+
+ resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_MD5, 1 + CHAP_MD5_LEN,
+ EAP_CODE_RESPONSE, eap_get_id(reqData));
+ if (resp == NULL)
+ return NULL;
+
+ /*
+ * CHAP Response:
+ * Value-Size (1 octet) | Value(Response) | Name(optional)
+ */
+ wpabuf_put_u8(resp, CHAP_MD5_LEN);
+
+ id = eap_get_id(resp);
+ rpos = wpabuf_put(resp, CHAP_MD5_LEN);
+ if (chap_md5(id, password, password_len, challenge, challenge_len,
+ rpos)) {
+ wpa_printf(MSG_INFO, "EAP-MD5: CHAP MD5 operation failed");
+ ret->ignore = TRUE;
+ wpabuf_free(resp);
+ return NULL;
+ }
+ wpa_hexdump(MSG_MSGDUMP, "EAP-MD5: Response", rpos, CHAP_MD5_LEN);
+
+ return resp;
+}
+
+
+int eap_peer_md5_register(void)
+{
+ struct eap_method *eap;
+ int ret;
+
+ eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION,
+ EAP_VENDOR_IETF, EAP_TYPE_MD5, "MD5");
+ if (eap == NULL)
+ return -1;
+
+ eap->init = eap_md5_init;
+ eap->deinit = eap_md5_deinit;
+ eap->process = eap_md5_process;
+
+ ret = eap_peer_method_register(eap);
+ if (ret)
+ eap_peer_method_free(eap);
+ return ret;
+}
diff --git a/freebsd/contrib/wpa/src/eap_peer/eap_methods.c b/freebsd/contrib/wpa/src/eap_peer/eap_methods.c
new file mode 100644
index 00000000..7af5a535
--- /dev/null
+++ b/freebsd/contrib/wpa/src/eap_peer/eap_methods.c
@@ -0,0 +1,371 @@
+#include <machine/rtems-bsd-user-space.h>
+
+/*
+ * 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.
+ */
+
+#include "includes.h"
+#ifdef CONFIG_DYNAMIC_EAP_METHODS
+#include <dlfcn.h>
+#endif /* CONFIG_DYNAMIC_EAP_METHODS */
+
+#include "common.h"
+#include "eap_i.h"
+#include "eap_methods.h"
+
+
+static struct eap_method *eap_methods = NULL;
+
+
+/**
+ * eap_peer_get_eap_method - Get EAP method based on type number
+ * @vendor: EAP Vendor-Id (0 = IETF)
+ * @method: EAP type number
+ * Returns: Pointer to EAP method or %NULL if not found
+ */
+const struct eap_method * eap_peer_get_eap_method(int vendor, EapType method)
+{
+ struct eap_method *m;
+ for (m = eap_methods; m; m = m->next) {
+ if (m->vendor == vendor && m->method == method)
+ return m;
+ }
+ return NULL;
+}
+
+
+/**
+ * eap_peer_get_type - Get EAP type for the given EAP method name
+ * @name: EAP method name, e.g., TLS
+ * @vendor: Buffer for returning EAP Vendor-Id
+ * Returns: EAP method type or %EAP_TYPE_NONE if not found
+ *
+ * This function maps EAP type names into EAP type numbers based on the list of
+ * EAP methods included in the build.
+ */
+EapType eap_peer_get_type(const char *name, int *vendor)
+{
+ struct eap_method *m;
+ for (m = eap_methods; m; m = m->next) {
+ if (os_strcmp(m->name, name) == 0) {
+ *vendor = m->vendor;
+ return m->method;
+ }
+ }
+ *vendor = EAP_VENDOR_IETF;
+ return EAP_TYPE_NONE;
+}
+
+
+/**
+ * eap_get_name - Get EAP method name for the given EAP type
+ * @vendor: EAP Vendor-Id (0 = IETF)
+ * @type: EAP method type
+ * Returns: EAP method name, e.g., TLS, or %NULL if not found
+ *
+ * This function maps EAP type numbers into EAP type names based on the list of
+ * EAP methods included in the build.
+ */
+const char * eap_get_name(int vendor, EapType type)
+{
+ struct eap_method *m;
+ if (vendor == EAP_VENDOR_IETF && type == EAP_TYPE_EXPANDED)
+ return "expanded";
+ for (m = eap_methods; m; m = m->next) {
+ if (m->vendor == vendor && m->method == type)
+ return m->name;
+ }
+ return NULL;
+}
+
+
+/**
+ * eap_get_names - Get space separated list of names for supported EAP methods
+ * @buf: Buffer for names
+ * @buflen: Buffer length
+ * Returns: Number of characters written into buf (not including nul
+ * termination)
+ */
+size_t eap_get_names(char *buf, size_t buflen)
+{
+ char *pos, *end;
+ struct eap_method *m;
+ int ret;
+
+ if (buflen == 0)
+ return 0;
+
+ pos = buf;
+ end = pos + buflen;
+
+ for (m = eap_methods; m; m = m->next) {
+ ret = os_snprintf(pos, end - pos, "%s%s",
+ m == eap_methods ? "" : " ", m->name);
+ if (os_snprintf_error(end - pos, ret))
+ break;
+ pos += ret;
+ }
+ buf[buflen - 1] = '\0';
+
+ return pos - buf;
+}
+
+
+/**
+ * eap_get_names_as_string_array - Get supported EAP methods as string array
+ * @num: Buffer for returning the number of items in array, not including %NULL
+ * terminator. This parameter can be %NULL if the length is not needed.
+ * Returns: A %NULL-terminated array of strings, or %NULL on error.
+ *
+ * This function returns the list of names for all supported EAP methods as an
+ * array of strings. The caller must free the returned array items and the
+ * array.
+ */
+char ** eap_get_names_as_string_array(size_t *num)
+{
+ struct eap_method *m;
+ size_t array_len = 0;
+ char **array;
+ int i = 0, j;
+
+ for (m = eap_methods; m; m = m->next)
+ array_len++;
+
+ array = os_calloc(array_len + 1, sizeof(char *));
+ if (array == NULL)
+ return NULL;
+
+ for (m = eap_methods; m; m = m->next) {
+ array[i++] = os_strdup(m->name);
+ if (array[i - 1] == NULL) {
+ for (j = 0; j < i; j++)
+ os_free(array[j]);
+ os_free(array);
+ return NULL;
+ }
+ }
+ array[i] = NULL;
+
+ if (num)
+ *num = array_len;
+
+ return array;
+}
+
+
+/**
+ * eap_peer_get_methods - Get a list of enabled EAP peer methods
+ * @count: Set to number of available methods
+ * Returns: List of enabled EAP peer methods
+ */
+const struct eap_method * eap_peer_get_methods(size_t *count)
+{
+ int c = 0;
+ struct eap_method *m;
+
+ for (m = eap_methods; m; m = m->next)
+ c++;
+
+ *count = c;
+ return eap_methods;
+}
+
+
+#ifdef CONFIG_DYNAMIC_EAP_METHODS
+/**
+ * eap_peer_method_load - Load a dynamic EAP method library (shared object)
+ * @so: File path for the shared object file to load
+ * Returns: 0 on success, -1 on failure
+ */
+int eap_peer_method_load(const char *so)
+{
+ void *handle;
+ int (*dyn_init)(void);
+ int ret;
+
+ handle = dlopen(so, RTLD_LAZY);
+ if (handle == NULL) {
+ wpa_printf(MSG_ERROR, "EAP: Failed to open dynamic EAP method "
+ "'%s': %s", so, dlerror());
+ return -1;
+ }
+
+ dyn_init = dlsym(handle, "eap_peer_method_dynamic_init");
+ if (dyn_init == NULL) {
+ dlclose(handle);
+ wpa_printf(MSG_ERROR, "EAP: Invalid EAP method '%s' - no "
+ "eap_peer_method_dynamic_init()", so);
+ return -1;
+ }
+
+ ret = dyn_init();
+ if (ret) {
+ dlclose(handle);
+ wpa_printf(MSG_ERROR, "EAP: Failed to add EAP method '%s' - "
+ "ret %d", so, ret);
+ return ret;
+ }
+
+ /* Store the handle for this shared object. It will be freed with
+ * dlclose() when the EAP method is unregistered. */
+ eap_methods->dl_handle = handle;
+
+ wpa_printf(MSG_DEBUG, "EAP: Loaded dynamic EAP method: '%s'", so);
+
+ return 0;
+}
+
+
+/**
+ * eap_peer_method_unload - Unload a dynamic EAP method library (shared object)
+ * @method: Pointer to the dynamically loaded EAP method
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function can be used to unload EAP methods that have been previously
+ * loaded with eap_peer_method_load(). Before unloading the method, all
+ * references to the method must be removed to make sure that no dereferences
+ * of freed memory will occur after unloading.
+ */
+int eap_peer_method_unload(struct eap_method *method)
+{
+ struct eap_method *m, *prev;
+ void *handle;
+
+ m = eap_methods;
+ prev = NULL;
+ while (m) {
+ if (m == method)
+ break;
+ prev = m;
+ m = m->next;
+ }
+
+ if (m == NULL || m->dl_handle == NULL)
+ return -1;
+
+ if (prev)
+ prev->next = m->next;
+ else
+ eap_methods = m->next;
+
+ handle = m->dl_handle;
+
+ if (m->free)
+ m->free(m);
+ else
+ eap_peer_method_free(m);
+
+ dlclose(handle);
+
+ return 0;
+}
+#endif /* CONFIG_DYNAMIC_EAP_METHODS */
+
+
+/**
+ * eap_peer_method_alloc - Allocate EAP peer method structure
+ * @version: Version of the EAP peer method interface (set to
+ * EAP_PEER_METHOD_INTERFACE_VERSION)
+ * @vendor: EAP Vendor-ID (EAP_VENDOR_*) (0 = IETF)
+ * @method: EAP type number (EAP_TYPE_*)
+ * @name: Name of the method (e.g., "TLS")
+ * Returns: Allocated EAP method structure or %NULL on failure
+ *
+ * The returned structure should be freed with eap_peer_method_free() when it
+ * is not needed anymore.
+ */
+struct eap_method * eap_peer_method_alloc(int version, int vendor,
+ EapType method, const char *name)
+{
+ struct eap_method *eap;
+ eap = os_zalloc(sizeof(*eap));
+ if (eap == NULL)
+ return NULL;
+ eap->version = version;
+ eap->vendor = vendor;
+ eap->method = method;
+ eap->name = name;
+ return eap;
+}
+
+
+/**
+ * eap_peer_method_free - Free EAP peer method structure
+ * @method: Method structure allocated with eap_peer_method_alloc()
+ */
+void eap_peer_method_free(struct eap_method *method)
+{
+ os_free(method);
+}
+
+
+/**
+ * eap_peer_method_register - Register an EAP peer method
+ * @method: EAP method to register
+ * Returns: 0 on success, -1 on invalid method, or -2 if a matching EAP method
+ * has already been registered
+ *
+ * Each EAP peer method needs to call this function to register itself as a
+ * supported EAP method.
+ */
+int eap_peer_method_register(struct eap_method *method)
+{
+ struct eap_method *m, *last = NULL;
+
+ if (method == NULL || method->name == NULL ||
+ method->version != EAP_PEER_METHOD_INTERFACE_VERSION)
+ return -1;
+
+ for (m = eap_methods; m; m = m->next) {
+ if ((m->vendor == method->vendor &&
+ m->method == method->method) ||
+ os_strcmp(m->name, method->name) == 0)
+ return -2;
+ last = m;
+ }
+
+ if (last)
+ last->next = method;
+ else
+ eap_methods = method;
+
+ return 0;
+}
+
+
+/**
+ * eap_peer_unregister_methods - Unregister EAP peer methods
+ *
+ * This function is called at program termination to unregister all EAP peer
+ * methods.
+ */
+void eap_peer_unregister_methods(void)
+{
+ struct eap_method *m;
+#ifdef CONFIG_DYNAMIC_EAP_METHODS
+ void *handle;
+#endif /* CONFIG_DYNAMIC_EAP_METHODS */
+
+ while (eap_methods) {
+ m = eap_methods;
+ eap_methods = eap_methods->next;
+
+#ifdef CONFIG_DYNAMIC_EAP_METHODS
+ handle = m->dl_handle;
+#endif /* CONFIG_DYNAMIC_EAP_METHODS */
+
+ if (m->free)
+ m->free(m);
+ else
+ eap_peer_method_free(m);
+
+#ifdef CONFIG_DYNAMIC_EAP_METHODS
+ if (handle)
+ dlclose(handle);
+#endif /* CONFIG_DYNAMIC_EAP_METHODS */
+ }
+}
diff --git a/freebsd/contrib/wpa/src/eap_peer/eap_methods.h b/freebsd/contrib/wpa/src/eap_peer/eap_methods.h
new file mode 100644
index 00000000..e35c919a
--- /dev/null
+++ b/freebsd/contrib/wpa/src/eap_peer/eap_methods.h
@@ -0,0 +1,111 @@
+/*
+ * 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_common/eap_defs.h"
+
+const struct eap_method * eap_peer_get_eap_method(int vendor, EapType method);
+const struct eap_method * eap_peer_get_methods(size_t *count);
+
+struct eap_method * eap_peer_method_alloc(int version, int vendor,
+ EapType method, const char *name);
+void eap_peer_method_free(struct eap_method *method);
+int eap_peer_method_register(struct eap_method *method);
+
+
+#ifdef IEEE8021X_EAPOL
+
+EapType eap_peer_get_type(const char *name, int *vendor);
+const char * eap_get_name(int vendor, EapType type);
+size_t eap_get_names(char *buf, size_t buflen);
+char ** eap_get_names_as_string_array(size_t *num);
+void eap_peer_unregister_methods(void);
+
+#else /* IEEE8021X_EAPOL */
+
+static inline EapType eap_peer_get_type(const char *name, int *vendor)
+{
+ *vendor = EAP_VENDOR_IETF;
+ return EAP_TYPE_NONE;
+}
+
+static inline const char * eap_get_name(int vendor, EapType type)
+{
+ return NULL;
+}
+
+static inline size_t eap_get_names(char *buf, size_t buflen)
+{
+ return 0;
+}
+
+static inline int eap_peer_register_methods(void)
+{
+ return 0;
+}
+
+static inline void eap_peer_unregister_methods(void)
+{
+}
+
+static inline char ** eap_get_names_as_string_array(size_t *num)
+{
+ return NULL;
+}
+
+#endif /* IEEE8021X_EAPOL */
+
+
+#ifdef CONFIG_DYNAMIC_EAP_METHODS
+
+int eap_peer_method_load(const char *so);
+int eap_peer_method_unload(struct eap_method *method);
+
+#else /* CONFIG_DYNAMIC_EAP_METHODS */
+
+static inline int eap_peer_method_load(const char *so)
+{
+ return 0;
+}
+
+static inline int eap_peer_method_unload(struct eap_method *method)
+{
+ return 0;
+}
+
+#endif /* CONFIG_DYNAMIC_EAP_METHODS */
+
+/* EAP peer method registration calls for statically linked in methods */
+int eap_peer_md5_register(void);
+int eap_peer_tls_register(void);
+int eap_peer_unauth_tls_register(void);
+int eap_peer_wfa_unauth_tls_register(void);
+int eap_peer_mschapv2_register(void);
+int eap_peer_peap_register(void);
+int eap_peer_ttls_register(void);
+int eap_peer_gtc_register(void);
+int eap_peer_otp_register(void);
+int eap_peer_sim_register(void);
+int eap_peer_leap_register(void);
+int eap_peer_psk_register(void);
+int eap_peer_aka_register(void);
+int eap_peer_aka_prime_register(void);
+int eap_peer_fast_register(void);
+int eap_peer_pax_register(void);
+int eap_peer_sake_register(void);
+int eap_peer_gpsk_register(void);
+int eap_peer_wsc_register(void);
+int eap_peer_ikev2_register(void);
+int eap_peer_vendor_test_register(void);
+int eap_peer_tnc_register(void);
+int eap_peer_pwd_register(void);
+int eap_peer_eke_register(void);
+
+#endif /* EAP_METHODS_H */
diff --git a/freebsd/contrib/wpa/src/eap_peer/eap_mschapv2.c b/freebsd/contrib/wpa/src/eap_peer/eap_mschapv2.c
new file mode 100644
index 00000000..18bb04c2
--- /dev/null
+++ b/freebsd/contrib/wpa/src/eap_peer/eap_mschapv2.c
@@ -0,0 +1,903 @@
+#include <machine/rtems-bsd-user-space.h>
+
+/*
+ * EAP peer method: EAP-MSCHAPV2 (draft-kamath-pppext-eap-mschapv2-00.txt)
+ * Copyright (c) 2004-2008, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ *
+ * This file implements EAP peer part of EAP-MSCHAPV2 method (EAP type 26).
+ * draft-kamath-pppext-eap-mschapv2-00.txt defines the Microsoft EAP CHAP
+ * Extensions Protocol, Version 2, for mutual authentication and key
+ * derivation. This encapsulates MS-CHAP-v2 protocol which is defined in
+ * RFC 2759. Use of EAP-MSCHAPV2 derived keys with MPPE cipher is described in
+ * RFC 3079.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "crypto/ms_funcs.h"
+#include "crypto/random.h"
+#include "common/wpa_ctrl.h"
+#include "mschapv2.h"
+#include "eap_i.h"
+#include "eap_config.h"
+
+
+#ifdef _MSC_VER
+#pragma pack(push, 1)
+#endif /* _MSC_VER */
+
+struct eap_mschapv2_hdr {
+ u8 op_code; /* MSCHAPV2_OP_* */
+ u8 mschapv2_id; /* usually same as EAP identifier; must be changed
+ * for challenges, but not for success/failure */
+ u8 ms_length[2]; /* Note: misaligned; length - 5 */
+ /* followed by data */
+} STRUCT_PACKED;
+
+/* Response Data field */
+struct ms_response {
+ u8 peer_challenge[MSCHAPV2_CHAL_LEN];
+ u8 reserved[8];
+ u8 nt_response[MSCHAPV2_NT_RESPONSE_LEN];
+ u8 flags;
+} STRUCT_PACKED;
+
+/* Change-Password Data field */
+struct ms_change_password {
+ u8 encr_password[516];
+ u8 encr_hash[16];
+ u8 peer_challenge[MSCHAPV2_CHAL_LEN];
+ u8 reserved[8];
+ u8 nt_response[MSCHAPV2_NT_RESPONSE_LEN];
+ u8 flags[2];
+} STRUCT_PACKED;
+
+#ifdef _MSC_VER
+#pragma pack(pop)
+#endif /* _MSC_VER */
+
+#define MSCHAPV2_OP_CHALLENGE 1
+#define MSCHAPV2_OP_RESPONSE 2
+#define MSCHAPV2_OP_SUCCESS 3
+#define MSCHAPV2_OP_FAILURE 4
+#define MSCHAPV2_OP_CHANGE_PASSWORD 7
+
+#define ERROR_RESTRICTED_LOGON_HOURS 646
+#define ERROR_ACCT_DISABLED 647
+#define ERROR_PASSWD_EXPIRED 648
+#define ERROR_NO_DIALIN_PERMISSION 649
+#define ERROR_AUTHENTICATION_FAILURE 691
+#define ERROR_CHANGING_PASSWORD 709
+
+#define PASSWD_CHANGE_CHAL_LEN 16
+#define MSCHAPV2_KEY_LEN 16
+
+
+struct eap_mschapv2_data {
+ u8 auth_response[MSCHAPV2_AUTH_RESPONSE_LEN];
+ int auth_response_valid;
+
+ int prev_error;
+ u8 passwd_change_challenge[PASSWD_CHANGE_CHAL_LEN];
+ int passwd_change_challenge_valid;
+ int passwd_change_version;
+
+ /* Optional challenge values generated in EAP-FAST Phase 1 negotiation
+ */
+ u8 *peer_challenge;
+ u8 *auth_challenge;
+
+ int phase2;
+ u8 master_key[MSCHAPV2_MASTER_KEY_LEN];
+ int master_key_valid;
+ int success;
+
+ struct wpabuf *prev_challenge;
+};
+
+
+static void eap_mschapv2_deinit(struct eap_sm *sm, void *priv);
+
+
+static void * eap_mschapv2_init(struct eap_sm *sm)
+{
+ struct eap_mschapv2_data *data;
+ data = os_zalloc(sizeof(*data));
+ if (data == NULL)
+ return NULL;
+
+ if (sm->peer_challenge) {
+ data->peer_challenge = os_malloc(MSCHAPV2_CHAL_LEN);
+ if (data->peer_challenge == NULL) {
+ eap_mschapv2_deinit(sm, data);
+ return NULL;
+ }
+ os_memcpy(data->peer_challenge, sm->peer_challenge,
+ MSCHAPV2_CHAL_LEN);
+ }
+
+ if (sm->auth_challenge) {
+ data->auth_challenge = os_malloc(MSCHAPV2_CHAL_LEN);
+ if (data->auth_challenge == NULL) {
+ eap_mschapv2_deinit(sm, data);
+ return NULL;
+ }
+ os_memcpy(data->auth_challenge, sm->auth_challenge,
+ MSCHAPV2_CHAL_LEN);
+ }
+
+ data->phase2 = sm->init_phase2;
+
+ return data;
+}
+
+
+static void eap_mschapv2_deinit(struct eap_sm *sm, void *priv)
+{
+ struct eap_mschapv2_data *data = priv;
+ os_free(data->peer_challenge);
+ os_free(data->auth_challenge);
+ wpabuf_free(data->prev_challenge);
+ bin_clear_free(data, sizeof(*data));
+}
+
+
+static struct wpabuf * eap_mschapv2_challenge_reply(
+ struct eap_sm *sm, struct eap_mschapv2_data *data, u8 id,
+ u8 mschapv2_id, const u8 *auth_challenge)
+{
+ struct wpabuf *resp;
+ struct eap_mschapv2_hdr *ms;
+ u8 *peer_challenge;
+ int ms_len;
+ struct ms_response *r;
+ size_t identity_len, password_len;
+ const u8 *identity, *password;
+ int pwhash;
+
+ wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Generating Challenge Response");
+
+ identity = eap_get_config_identity(sm, &identity_len);
+ password = eap_get_config_password2(sm, &password_len, &pwhash);
+ if (identity == NULL || password == NULL)
+ return NULL;
+
+ ms_len = sizeof(*ms) + 1 + sizeof(*r) + identity_len;
+ resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, ms_len,
+ EAP_CODE_RESPONSE, id);
+ if (resp == NULL)
+ return NULL;
+
+ ms = wpabuf_put(resp, sizeof(*ms));
+ ms->op_code = MSCHAPV2_OP_RESPONSE;
+ ms->mschapv2_id = mschapv2_id;
+ if (data->prev_error) {
+ /*
+ * TODO: this does not seem to be enough when processing two
+ * or more failure messages. IAS did not increment mschapv2_id
+ * in its own packets, but it seemed to expect the peer to
+ * increment this for all packets(?).
+ */
+ ms->mschapv2_id++;
+ }
+ WPA_PUT_BE16(ms->ms_length, ms_len);
+
+ wpabuf_put_u8(resp, sizeof(*r)); /* Value-Size */
+
+ /* Response */
+ r = wpabuf_put(resp, sizeof(*r));
+ peer_challenge = r->peer_challenge;
+ if (data->peer_challenge) {
+ wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: peer_challenge generated "
+ "in Phase 1");
+ peer_challenge = data->peer_challenge;
+ os_memset(r->peer_challenge, 0, MSCHAPV2_CHAL_LEN);
+ } else if (random_get_bytes(peer_challenge, MSCHAPV2_CHAL_LEN)) {
+ wpabuf_free(resp);
+ return NULL;
+ }
+ os_memset(r->reserved, 0, 8);
+ if (data->auth_challenge) {
+ wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: auth_challenge generated "
+ "in Phase 1");
+ auth_challenge = data->auth_challenge;
+ }
+ if (mschapv2_derive_response(identity, identity_len, password,
+ password_len, pwhash, auth_challenge,
+ peer_challenge, r->nt_response,
+ data->auth_response, data->master_key)) {
+ wpa_printf(MSG_ERROR, "EAP-MSCHAPV2: Failed to derive "
+ "response");
+ wpabuf_free(resp);
+ return NULL;
+ }
+ data->auth_response_valid = 1;
+ data->master_key_valid = 1;
+
+ r->flags = 0; /* reserved, must be zero */
+
+ wpabuf_put_data(resp, identity, identity_len);
+ wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: TX identifier %d mschapv2_id %d "
+ "(response)", id, ms->mschapv2_id);
+ return resp;
+}
+
+
+/**
+ * eap_mschapv2_process - Process an EAP-MSCHAPv2 challenge message
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @data: Pointer to private EAP method data from eap_mschapv2_init()
+ * @ret: Return values from EAP request validation and processing
+ * @req: Pointer to EAP-MSCHAPv2 header from the request
+ * @req_len: Length of the EAP-MSCHAPv2 data
+ * @id: EAP identifier used in the request
+ * Returns: Pointer to allocated EAP response packet (eapRespData) or %NULL if
+ * no reply available
+ */
+static struct wpabuf * eap_mschapv2_challenge(
+ struct eap_sm *sm, struct eap_mschapv2_data *data,
+ struct eap_method_ret *ret, const struct eap_mschapv2_hdr *req,
+ size_t req_len, u8 id)
+{
+ size_t len, challenge_len;
+ const u8 *pos, *challenge;
+
+ if (eap_get_config_identity(sm, &len) == NULL ||
+ eap_get_config_password(sm, &len) == NULL)
+ return NULL;
+
+ wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Received challenge");
+ if (req_len < sizeof(*req) + 1) {
+ wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Too short challenge data "
+ "(len %lu)", (unsigned long) req_len);
+ ret->ignore = TRUE;
+ return NULL;
+ }
+ pos = (const u8 *) (req + 1);
+ challenge_len = *pos++;
+ len = req_len - sizeof(*req) - 1;
+ if (challenge_len != MSCHAPV2_CHAL_LEN) {
+ wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Invalid challenge length "
+ "%lu", (unsigned long) challenge_len);
+ ret->ignore = TRUE;
+ return NULL;
+ }
+
+ if (len < challenge_len) {
+ wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Too short challenge"
+ " packet: len=%lu challenge_len=%lu",
+ (unsigned long) len, (unsigned long) challenge_len);
+ ret->ignore = TRUE;
+ return NULL;
+ }
+
+ if (data->passwd_change_challenge_valid) {
+ wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Using challenge from the "
+ "failure message");
+ challenge = data->passwd_change_challenge;
+ } else
+ challenge = pos;
+ pos += challenge_len;
+ len -= challenge_len;
+ wpa_hexdump_ascii(MSG_DEBUG, "EAP-MSCHAPV2: Authentication Servername",
+ pos, len);
+
+ ret->ignore = FALSE;
+ ret->methodState = METHOD_MAY_CONT;
+ ret->decision = DECISION_FAIL;
+ ret->allowNotifications = TRUE;
+
+ return eap_mschapv2_challenge_reply(sm, data, id, req->mschapv2_id,
+ challenge);
+}
+
+
+static void eap_mschapv2_password_changed(struct eap_sm *sm,
+ struct eap_mschapv2_data *data)
+{
+ struct eap_peer_config *config = eap_get_config(sm);
+ if (config && config->new_password) {
+ wpa_msg(sm->msg_ctx, MSG_INFO,
+ WPA_EVENT_PASSWORD_CHANGED
+ "EAP-MSCHAPV2: Password changed successfully");
+ data->prev_error = 0;
+ bin_clear_free(config->password, config->password_len);
+ if (config->flags & EAP_CONFIG_FLAGS_EXT_PASSWORD) {
+ /* TODO: update external storage */
+ } 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)) {
+ bin_clear_free(config->password,
+ config->password_len);
+ config->password = NULL;
+ config->password_len = 0;
+ }
+ bin_clear_free(config->new_password,
+ config->new_password_len);
+ } else {
+ config->password = config->new_password;
+ config->password_len = config->new_password_len;
+ }
+ config->new_password = NULL;
+ config->new_password_len = 0;
+ }
+}
+
+
+/**
+ * eap_mschapv2_process - Process an EAP-MSCHAPv2 success message
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @data: Pointer to private EAP method data from eap_mschapv2_init()
+ * @ret: Return values from EAP request validation and processing
+ * @req: Pointer to EAP-MSCHAPv2 header from the request
+ * @req_len: Length of the EAP-MSCHAPv2 data
+ * @id: EAP identifier used in th erequest
+ * Returns: Pointer to allocated EAP response packet (eapRespData) or %NULL if
+ * no reply available
+ */
+static struct wpabuf * eap_mschapv2_success(struct eap_sm *sm,
+ struct eap_mschapv2_data *data,
+ struct eap_method_ret *ret,
+ const struct eap_mschapv2_hdr *req,
+ size_t req_len, u8 id)
+{
+ struct wpabuf *resp;
+ const u8 *pos;
+ size_t len;
+
+ wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Received success");
+ len = req_len - sizeof(*req);
+ pos = (const u8 *) (req + 1);
+ if (!data->auth_response_valid ||
+ mschapv2_verify_auth_response(data->auth_response, pos, len)) {
+ wpa_printf(MSG_WARNING, "EAP-MSCHAPV2: Invalid authenticator "
+ "response in success request");
+ ret->methodState = METHOD_DONE;
+ ret->decision = DECISION_FAIL;
+ return NULL;
+ }
+ pos += 2 + 2 * MSCHAPV2_AUTH_RESPONSE_LEN;
+ len -= 2 + 2 * MSCHAPV2_AUTH_RESPONSE_LEN;
+ while (len > 0 && *pos == ' ') {
+ pos++;
+ len--;
+ }
+ wpa_hexdump_ascii(MSG_DEBUG, "EAP-MSCHAPV2: Success message",
+ pos, len);
+ wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Authentication succeeded");
+
+ /* Note: Only op_code of the EAP-MSCHAPV2 header is included in success
+ * message. */
+ resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, 1,
+ EAP_CODE_RESPONSE, id);
+ if (resp == NULL) {
+ wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Failed to allocate "
+ "buffer for success response");
+ ret->ignore = TRUE;
+ return NULL;
+ }
+
+ wpabuf_put_u8(resp, MSCHAPV2_OP_SUCCESS); /* op_code */
+
+ ret->methodState = METHOD_DONE;
+ ret->decision = DECISION_UNCOND_SUCC;
+ ret->allowNotifications = FALSE;
+ data->success = 1;
+
+ if (data->prev_error == ERROR_PASSWD_EXPIRED)
+ eap_mschapv2_password_changed(sm, data);
+
+ return resp;
+}
+
+
+static int eap_mschapv2_failure_txt(struct eap_sm *sm,
+ struct eap_mschapv2_data *data, char *txt)
+{
+ char *pos, *msg = "";
+ int retry = 1;
+ struct eap_peer_config *config = eap_get_config(sm);
+
+ /* For example:
+ * E=691 R=1 C=<32 octets hex challenge> V=3 M=Authentication Failure
+ */
+
+ pos = txt;
+
+ if (pos && os_strncmp(pos, "E=", 2) == 0) {
+ pos += 2;
+ data->prev_error = atoi(pos);
+ wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: error %d",
+ data->prev_error);
+ pos = os_strchr(pos, ' ');
+ if (pos)
+ pos++;
+ }
+
+ if (pos && os_strncmp(pos, "R=", 2) == 0) {
+ pos += 2;
+ retry = atoi(pos);
+ wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: retry is %sallowed",
+ retry == 1 ? "" : "not ");
+ pos = os_strchr(pos, ' ');
+ if (pos)
+ pos++;
+ }
+
+ if (pos && os_strncmp(pos, "C=", 2) == 0) {
+ int hex_len;
+ pos += 2;
+ hex_len = os_strchr(pos, ' ') - (char *) pos;
+ if (hex_len == PASSWD_CHANGE_CHAL_LEN * 2) {
+ if (hexstr2bin(pos, data->passwd_change_challenge,
+ PASSWD_CHANGE_CHAL_LEN)) {
+ wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: invalid "
+ "failure challenge");
+ } else {
+ wpa_hexdump(MSG_DEBUG, "EAP-MSCHAPV2: failure "
+ "challenge",
+ data->passwd_change_challenge,
+ PASSWD_CHANGE_CHAL_LEN);
+ data->passwd_change_challenge_valid = 1;
+ }
+ } else {
+ wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: invalid failure "
+ "challenge len %d", hex_len);
+ }
+ pos = os_strchr(pos, ' ');
+ if (pos)
+ pos++;
+ } else {
+ wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: required challenge field "
+ "was not present in failure message");
+ }
+
+ if (pos && os_strncmp(pos, "V=", 2) == 0) {
+ pos += 2;
+ data->passwd_change_version = atoi(pos);
+ wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: password changing "
+ "protocol version %d", data->passwd_change_version);
+ pos = os_strchr(pos, ' ');
+ if (pos)
+ pos++;
+ }
+
+ if (pos && os_strncmp(pos, "M=", 2) == 0) {
+ pos += 2;
+ msg = pos;
+ }
+ if (data->prev_error == ERROR_AUTHENTICATION_FAILURE && retry &&
+ config && config->phase2 &&
+ os_strstr(config->phase2, "mschapv2_retry=0")) {
+ wpa_printf(MSG_DEBUG,
+ "EAP-MSCHAPV2: mark password retry disabled based on local configuration");
+ retry = 0;
+ }
+ wpa_msg(sm->msg_ctx, MSG_WARNING,
+ "EAP-MSCHAPV2: failure message: '%s' (retry %sallowed, error "
+ "%d)",
+ msg, retry == 1 ? "" : "not ", data->prev_error);
+ if (data->prev_error == ERROR_PASSWD_EXPIRED &&
+ data->passwd_change_version == 3 && config) {
+ if (config->new_password == NULL) {
+ wpa_msg(sm->msg_ctx, MSG_INFO,
+ "EAP-MSCHAPV2: Password expired - password "
+ "change required");
+ eap_sm_request_new_password(sm);
+ }
+ } else if (retry == 1 && config) {
+ /* TODO: could prevent the current password from being used
+ * again at least for some period of time */
+ if (!config->mschapv2_retry)
+ eap_sm_request_identity(sm);
+ eap_sm_request_password(sm);
+ config->mschapv2_retry = 1;
+ } else if (config) {
+ /* TODO: prevent retries using same username/password */
+ config->mschapv2_retry = 0;
+ }
+
+ return retry == 1;
+}
+
+
+static struct wpabuf * eap_mschapv2_change_password(
+ struct eap_sm *sm, struct eap_mschapv2_data *data,
+ struct eap_method_ret *ret, const struct eap_mschapv2_hdr *req, u8 id)
+{
+#ifdef CONFIG_NO_RC4
+ wpa_printf(MSG_ERROR,
+ "EAP-MSCHAPV2: RC4 not support in the build - cannot change password");
+ return NULL;
+#else /* CONFIG_NO_RC4 */
+ struct wpabuf *resp;
+ int ms_len;
+ const u8 *username, *password, *new_password;
+ size_t username_len, password_len, new_password_len;
+ struct eap_mschapv2_hdr *ms;
+ struct ms_change_password *cp;
+ u8 password_hash[16], password_hash_hash[16];
+ int pwhash;
+
+ username = eap_get_config_identity(sm, &username_len);
+ password = eap_get_config_password2(sm, &password_len, &pwhash);
+ new_password = eap_get_config_new_password(sm, &new_password_len);
+ if (username == NULL || password == NULL || new_password == NULL)
+ return NULL;
+
+ username = mschapv2_remove_domain(username, &username_len);
+
+ ret->ignore = FALSE;
+ ret->methodState = METHOD_MAY_CONT;
+ ret->decision = DECISION_COND_SUCC;
+ ret->allowNotifications = TRUE;
+
+ ms_len = sizeof(*ms) + sizeof(*cp);
+ resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, ms_len,
+ EAP_CODE_RESPONSE, id);
+ if (resp == NULL)
+ return NULL;
+
+ ms = wpabuf_put(resp, sizeof(*ms));
+ ms->op_code = MSCHAPV2_OP_CHANGE_PASSWORD;
+ ms->mschapv2_id = req->mschapv2_id + 1;
+ WPA_PUT_BE16(ms->ms_length, ms_len);
+ cp = wpabuf_put(resp, sizeof(*cp));
+
+ /* Encrypted-Password */
+ if (pwhash) {
+ if (encrypt_pw_block_with_password_hash(
+ new_password, new_password_len,
+ password, cp->encr_password))
+ goto fail;
+ } else {
+ if (new_password_encrypted_with_old_nt_password_hash(
+ new_password, new_password_len,
+ password, password_len, cp->encr_password))
+ goto fail;
+ }
+
+ /* Encrypted-Hash */
+ if (pwhash) {
+ u8 new_password_hash[16];
+ if (nt_password_hash(new_password, new_password_len,
+ new_password_hash))
+ goto fail;
+ nt_password_hash_encrypted_with_block(password,
+ new_password_hash,
+ cp->encr_hash);
+ } else {
+ if (old_nt_password_hash_encrypted_with_new_nt_password_hash(
+ new_password, new_password_len,
+ password, password_len, cp->encr_hash))
+ goto fail;
+ }
+
+ /* Peer-Challenge */
+ if (random_get_bytes(cp->peer_challenge, MSCHAPV2_CHAL_LEN))
+ goto fail;
+
+ /* Reserved, must be zero */
+ os_memset(cp->reserved, 0, 8);
+
+ /* NT-Response */
+ wpa_hexdump(MSG_DEBUG, "EAP-MSCHAPV2: auth_challenge",
+ data->passwd_change_challenge, PASSWD_CHANGE_CHAL_LEN);
+ wpa_hexdump(MSG_DEBUG, "EAP-MSCHAPV2: peer_challenge",
+ cp->peer_challenge, MSCHAPV2_CHAL_LEN);
+ wpa_hexdump_ascii(MSG_DEBUG, "EAP-MSCHAPV2: username",
+ username, username_len);
+ wpa_hexdump_ascii_key(MSG_DEBUG, "EAP-MSCHAPV2: new password",
+ new_password, new_password_len);
+ generate_nt_response(data->passwd_change_challenge, cp->peer_challenge,
+ username, username_len,
+ new_password, new_password_len,
+ cp->nt_response);
+ wpa_hexdump(MSG_DEBUG, "EAP-MSCHAPV2: NT-Response",
+ cp->nt_response, MSCHAPV2_NT_RESPONSE_LEN);
+
+ /* Authenticator response is not really needed yet, but calculate it
+ * here so that challenges need not be saved. */
+ generate_authenticator_response(new_password, new_password_len,
+ cp->peer_challenge,
+ data->passwd_change_challenge,
+ username, username_len,
+ cp->nt_response, data->auth_response);
+ data->auth_response_valid = 1;
+
+ /* Likewise, generate master_key here since we have the needed data
+ * available. */
+ if (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->auth_response_valid = 0;
+ goto fail;
+ }
+ data->master_key_valid = 1;
+
+ /* Flags */
+ os_memset(cp->flags, 0, 2);
+
+ wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: TX identifier %d mschapv2_id %d "
+ "(change pw)", id, ms->mschapv2_id);
+
+ return resp;
+
+fail:
+ wpabuf_free(resp);
+ return NULL;
+#endif /* CONFIG_NO_RC4 */
+}
+
+
+/**
+ * eap_mschapv2_process - Process an EAP-MSCHAPv2 failure message
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @data: Pointer to private EAP method data from eap_mschapv2_init()
+ * @ret: Return values from EAP request validation and processing
+ * @req: Pointer to EAP-MSCHAPv2 header from the request
+ * @req_len: Length of the EAP-MSCHAPv2 data
+ * @id: EAP identifier used in th erequest
+ * Returns: Pointer to allocated EAP response packet (eapRespData) or %NULL if
+ * no reply available
+ */
+static struct wpabuf * eap_mschapv2_failure(struct eap_sm *sm,
+ struct eap_mschapv2_data *data,
+ struct eap_method_ret *ret,
+ const struct eap_mschapv2_hdr *req,
+ size_t req_len, u8 id)
+{
+ struct wpabuf *resp;
+ const u8 *msdata = (const u8 *) (req + 1);
+ char *buf;
+ size_t len = req_len - sizeof(*req);
+ int retry = 0;
+
+ wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Received failure");
+ wpa_hexdump_ascii(MSG_DEBUG, "EAP-MSCHAPV2: Failure data",
+ msdata, len);
+ /*
+ * eap_mschapv2_failure_txt() expects a nul terminated string, so we
+ * must allocate a large enough temporary buffer to create that since
+ * the received message does not include nul termination.
+ */
+ buf = 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) {
+ /* TODO: could try to retry authentication, e.g, after having
+ * changed the username/password. In this case, EAP MS-CHAP-v2
+ * Failure Response would not be sent here. */
+ return NULL;
+ }
+
+ /* Note: Only op_code of the EAP-MSCHAPV2 header is included in failure
+ * message. */
+ resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, 1,
+ EAP_CODE_RESPONSE, id);
+ if (resp == NULL)
+ return NULL;
+
+ wpabuf_put_u8(resp, MSCHAPV2_OP_FAILURE); /* op_code */
+
+ return resp;
+}
+
+
+static int eap_mschapv2_check_config(struct eap_sm *sm)
+{
+ size_t len;
+
+ if (eap_get_config_identity(sm, &len) == NULL) {
+ wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Identity not configured");
+ eap_sm_request_identity(sm);
+ return -1;
+ }
+
+ if (eap_get_config_password(sm, &len) == NULL) {
+ wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Password not configured");
+ eap_sm_request_password(sm);
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static int eap_mschapv2_check_mslen(struct eap_sm *sm, size_t len,
+ const struct eap_mschapv2_hdr *ms)
+{
+ size_t ms_len = WPA_GET_BE16(ms->ms_length);
+
+ if (ms_len == len)
+ return 0;
+
+ wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Invalid header: len=%lu "
+ "ms_len=%lu", (unsigned long) len, (unsigned long) ms_len);
+ if (sm->workaround) {
+ /* Some authentication servers use invalid ms_len,
+ * ignore it for interoperability. */
+ wpa_printf(MSG_INFO, "EAP-MSCHAPV2: workaround, ignore"
+ " invalid ms_len %lu (len %lu)",
+ (unsigned long) ms_len,
+ (unsigned long) len);
+ return 0;
+ }
+
+ return -1;
+}
+
+
+static void eap_mschapv2_copy_challenge(struct eap_mschapv2_data *data,
+ const struct wpabuf *reqData)
+{
+ /*
+ * Store a copy of the challenge message, so that it can be processed
+ * again in case retry is allowed after a possible failure.
+ */
+ wpabuf_free(data->prev_challenge);
+ data->prev_challenge = wpabuf_dup(reqData);
+}
+
+
+/**
+ * eap_mschapv2_process - Process an EAP-MSCHAPv2 request
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @priv: Pointer to private EAP method data from eap_mschapv2_init()
+ * @ret: Return values from EAP request validation and processing
+ * @reqData: EAP request to be processed (eapReqData)
+ * Returns: Pointer to allocated EAP response packet (eapRespData) or %NULL if
+ * no reply available
+ */
+static struct wpabuf * eap_mschapv2_process(struct eap_sm *sm, void *priv,
+ struct eap_method_ret *ret,
+ const struct wpabuf *reqData)
+{
+ struct eap_mschapv2_data *data = priv;
+ struct eap_peer_config *config = eap_get_config(sm);
+ const struct eap_mschapv2_hdr *ms;
+ int using_prev_challenge = 0;
+ const u8 *pos;
+ size_t len;
+ u8 id;
+
+ if (eap_mschapv2_check_config(sm)) {
+ ret->ignore = TRUE;
+ return NULL;
+ }
+
+ if (config->mschapv2_retry && data->prev_challenge &&
+ data->prev_error == ERROR_AUTHENTICATION_FAILURE) {
+ wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Replacing pending packet "
+ "with the previous challenge");
+
+ reqData = data->prev_challenge;
+ using_prev_challenge = 1;
+ config->mschapv2_retry = 0;
+ }
+
+ pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, reqData,
+ &len);
+ if (pos == NULL || len < sizeof(*ms) + 1) {
+ ret->ignore = TRUE;
+ return NULL;
+ }
+
+ ms = (const struct eap_mschapv2_hdr *) pos;
+ if (eap_mschapv2_check_mslen(sm, len, ms)) {
+ ret->ignore = TRUE;
+ return NULL;
+ }
+
+ id = eap_get_id(reqData);
+ wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: RX identifier %d mschapv2_id %d",
+ id, ms->mschapv2_id);
+
+ switch (ms->op_code) {
+ case MSCHAPV2_OP_CHALLENGE:
+ if (!using_prev_challenge)
+ eap_mschapv2_copy_challenge(data, reqData);
+ return eap_mschapv2_challenge(sm, data, ret, ms, len, id);
+ case MSCHAPV2_OP_SUCCESS:
+ return eap_mschapv2_success(sm, data, ret, ms, len, id);
+ case MSCHAPV2_OP_FAILURE:
+ return eap_mschapv2_failure(sm, data, ret, ms, len, id);
+ default:
+ wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Unknown op %d - ignored",
+ ms->op_code);
+ ret->ignore = TRUE;
+ return NULL;
+ }
+}
+
+
+static Boolean eap_mschapv2_isKeyAvailable(struct eap_sm *sm, void *priv)
+{
+ struct eap_mschapv2_data *data = priv;
+ return data->success && data->master_key_valid;
+}
+
+
+static u8 * eap_mschapv2_getKey(struct eap_sm *sm, void *priv, size_t *len)
+{
+ struct eap_mschapv2_data *data = priv;
+ u8 *key;
+ int key_len;
+
+ if (!data->master_key_valid || !data->success)
+ return NULL;
+
+ key_len = 2 * MSCHAPV2_KEY_LEN;
+
+ key = os_malloc(key_len);
+ if (key == NULL)
+ return NULL;
+
+ /* MSK = server MS-MPPE-Recv-Key | MS-MPPE-Send-Key, i.e.,
+ * peer MS-MPPE-Send-Key | MS-MPPE-Recv-Key */
+ get_asymetric_start_key(data->master_key, key, MSCHAPV2_KEY_LEN, 1, 0);
+ get_asymetric_start_key(data->master_key, key + MSCHAPV2_KEY_LEN,
+ MSCHAPV2_KEY_LEN, 0, 0);
+
+ wpa_hexdump_key(MSG_DEBUG, "EAP-MSCHAPV2: Derived key",
+ key, key_len);
+
+ *len = key_len;
+ return key;
+}
+
+
+/**
+ * eap_peer_mschapv2_register - Register EAP-MSCHAPv2 peer method
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function is used to register EAP-MSCHAPv2 peer method into the EAP
+ * method list.
+ */
+int eap_peer_mschapv2_register(void)
+{
+ struct eap_method *eap;
+ int ret;
+
+ eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION,
+ EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2,
+ "MSCHAPV2");
+ if (eap == NULL)
+ return -1;
+
+ eap->init = eap_mschapv2_init;
+ eap->deinit = eap_mschapv2_deinit;
+ eap->process = eap_mschapv2_process;
+ eap->isKeyAvailable = eap_mschapv2_isKeyAvailable;
+ eap->getKey = eap_mschapv2_getKey;
+
+ ret = eap_peer_method_register(eap);
+ if (ret)
+ eap_peer_method_free(eap);
+ return ret;
+}
diff --git a/freebsd/contrib/wpa/src/eap_peer/eap_otp.c b/freebsd/contrib/wpa/src/eap_peer/eap_otp.c
new file mode 100644
index 00000000..d4d8bc1c
--- /dev/null
+++ b/freebsd/contrib/wpa/src/eap_peer/eap_otp.c
@@ -0,0 +1,103 @@
+#include <machine/rtems-bsd-user-space.h>
+
+/*
+ * EAP peer method: EAP-OTP (RFC 3748)
+ * Copyright (c) 2004-2006, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "eap_i.h"
+
+
+static void * eap_otp_init(struct eap_sm *sm)
+{
+ /* No need for private data. However, must return non-NULL to indicate
+ * success. */
+ return (void *) 1;
+}
+
+
+static void eap_otp_deinit(struct eap_sm *sm, void *priv)
+{
+}
+
+
+static struct wpabuf * eap_otp_process(struct eap_sm *sm, void *priv,
+ struct eap_method_ret *ret,
+ const struct wpabuf *reqData)
+{
+ struct wpabuf *resp;
+ const u8 *pos, *password;
+ size_t password_len, len;
+ int otp;
+
+ pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_OTP, reqData, &len);
+ if (pos == NULL) {
+ ret->ignore = TRUE;
+ return NULL;
+ }
+ wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-OTP: Request message",
+ pos, len);
+
+ password = eap_get_config_otp(sm, &password_len);
+ if (password)
+ otp = 1;
+ else {
+ password = eap_get_config_password(sm, &password_len);
+ otp = 0;
+ }
+
+ if (password == NULL) {
+ wpa_printf(MSG_INFO, "EAP-OTP: Password not configured");
+ eap_sm_request_otp(sm, (const char *) pos, len);
+ ret->ignore = TRUE;
+ return NULL;
+ }
+
+ ret->ignore = FALSE;
+
+ ret->methodState = METHOD_DONE;
+ ret->decision = DECISION_COND_SUCC;
+ ret->allowNotifications = FALSE;
+
+ resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_OTP, password_len,
+ EAP_CODE_RESPONSE, eap_get_id(reqData));
+ if (resp == NULL)
+ return NULL;
+ wpabuf_put_data(resp, password, password_len);
+ wpa_hexdump_ascii_key(MSG_MSGDUMP, "EAP-OTP: Response",
+ password, password_len);
+
+ if (otp) {
+ wpa_printf(MSG_DEBUG, "EAP-OTP: Forgetting used password");
+ eap_clear_config_otp(sm);
+ }
+
+ return resp;
+}
+
+
+int eap_peer_otp_register(void)
+{
+ struct eap_method *eap;
+ int ret;
+
+ eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION,
+ EAP_VENDOR_IETF, EAP_TYPE_OTP, "OTP");
+ if (eap == NULL)
+ return -1;
+
+ eap->init = eap_otp_init;
+ eap->deinit = eap_otp_deinit;
+ eap->process = eap_otp_process;
+
+ ret = eap_peer_method_register(eap);
+ if (ret)
+ eap_peer_method_free(eap);
+ return ret;
+}
diff --git a/freebsd/contrib/wpa/src/eap_peer/eap_peap.c b/freebsd/contrib/wpa/src/eap_peer/eap_peap.c
new file mode 100644
index 00000000..19afa1cf
--- /dev/null
+++ b/freebsd/contrib/wpa/src/eap_peer/eap_peap.c
@@ -0,0 +1,1264 @@
+#include <machine/rtems-bsd-user-space.h>
+
+/*
+ * EAP peer method: EAP-PEAP (draft-josefsson-pppext-eap-tls-eap-10.txt)
+ * Copyright (c) 2004-2008, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "crypto/sha1.h"
+#include "crypto/tls.h"
+#include "eap_common/eap_tlv_common.h"
+#include "eap_common/eap_peap_common.h"
+#include "eap_i.h"
+#include "eap_tls_common.h"
+#include "eap_config.h"
+#include "tncc.h"
+
+
+/* Maximum supported PEAP version
+ * 0 = Microsoft's PEAP version 0; draft-kamath-pppext-peapv0-00.txt
+ * 1 = draft-josefsson-ppext-eap-tls-eap-05.txt
+ */
+#define EAP_PEAP_VERSION 1
+
+
+static void eap_peap_deinit(struct eap_sm *sm, void *priv);
+
+
+struct eap_peap_data {
+ struct eap_ssl_data ssl;
+
+ int peap_version, force_peap_version, force_new_label;
+
+ const struct eap_method *phase2_method;
+ void *phase2_priv;
+ int phase2_success;
+ int phase2_eap_success;
+ int phase2_eap_started;
+
+ struct eap_method_type phase2_type;
+ struct eap_method_type *phase2_types;
+ size_t num_phase2_types;
+
+ int peap_outer_success; /* 0 = PEAP terminated on Phase 2 inner
+ * EAP-Success
+ * 1 = reply with tunneled EAP-Success to inner
+ * EAP-Success and expect AS to send outer
+ * (unencrypted) EAP-Success after this
+ * 2 = reply with PEAP/TLS ACK to inner
+ * EAP-Success and expect AS to send outer
+ * (unencrypted) EAP-Success after this */
+ int resuming; /* starting a resumed session */
+ int reauth; /* reauthentication */
+ u8 *key_data;
+ u8 *session_id;
+ size_t id_len;
+
+ struct wpabuf *pending_phase2_req;
+ enum { NO_BINDING, OPTIONAL_BINDING, REQUIRE_BINDING } crypto_binding;
+ int crypto_binding_used;
+ u8 binding_nonce[32];
+ u8 ipmk[40];
+ u8 cmk[20];
+ int soh; /* Whether IF-TNCCS-SOH (Statement of Health; Microsoft NAP)
+ * is enabled. */
+};
+
+
+static int eap_peap_parse_phase1(struct eap_peap_data *data,
+ const char *phase1)
+{
+ const char *pos;
+
+ pos = os_strstr(phase1, "peapver=");
+ if (pos) {
+ data->force_peap_version = atoi(pos + 8);
+ data->peap_version = data->force_peap_version;
+ wpa_printf(MSG_DEBUG, "EAP-PEAP: Forced PEAP version %d",
+ data->force_peap_version);
+ }
+
+ if (os_strstr(phase1, "peaplabel=1")) {
+ data->force_new_label = 1;
+ wpa_printf(MSG_DEBUG, "EAP-PEAP: Force new label for key "
+ "derivation");
+ }
+
+ if (os_strstr(phase1, "peap_outer_success=0")) {
+ data->peap_outer_success = 0;
+ wpa_printf(MSG_DEBUG, "EAP-PEAP: terminate authentication on "
+ "tunneled EAP-Success");
+ } else if (os_strstr(phase1, "peap_outer_success=1")) {
+ data->peap_outer_success = 1;
+ wpa_printf(MSG_DEBUG, "EAP-PEAP: send tunneled EAP-Success "
+ "after receiving tunneled EAP-Success");
+ } else if (os_strstr(phase1, "peap_outer_success=2")) {
+ data->peap_outer_success = 2;
+ wpa_printf(MSG_DEBUG, "EAP-PEAP: send PEAP/TLS ACK after "
+ "receiving tunneled EAP-Success");
+ }
+
+ if (os_strstr(phase1, "crypto_binding=0")) {
+ data->crypto_binding = NO_BINDING;
+ wpa_printf(MSG_DEBUG, "EAP-PEAP: Do not use cryptobinding");
+ } else if (os_strstr(phase1, "crypto_binding=1")) {
+ data->crypto_binding = OPTIONAL_BINDING;
+ wpa_printf(MSG_DEBUG, "EAP-PEAP: Optional cryptobinding");
+ } else if (os_strstr(phase1, "crypto_binding=2")) {
+ data->crypto_binding = REQUIRE_BINDING;
+ wpa_printf(MSG_DEBUG, "EAP-PEAP: Require cryptobinding");
+ }
+
+#ifdef EAP_TNC
+ if (os_strstr(phase1, "tnc=soh2")) {
+ data->soh = 2;
+ wpa_printf(MSG_DEBUG, "EAP-PEAP: SoH version 2 enabled");
+ } else if (os_strstr(phase1, "tnc=soh1")) {
+ data->soh = 1;
+ wpa_printf(MSG_DEBUG, "EAP-PEAP: SoH version 1 enabled");
+ } else if (os_strstr(phase1, "tnc=soh")) {
+ data->soh = 2;
+ wpa_printf(MSG_DEBUG, "EAP-PEAP: SoH version 2 enabled");
+ }
+#endif /* EAP_TNC */
+
+ return 0;
+}
+
+
+static void * eap_peap_init(struct eap_sm *sm)
+{
+ struct eap_peap_data *data;
+ struct eap_peer_config *config = eap_get_config(sm);
+
+ data = os_zalloc(sizeof(*data));
+ if (data == NULL)
+ return NULL;
+ sm->peap_done = FALSE;
+ data->peap_version = EAP_PEAP_VERSION;
+ data->force_peap_version = -1;
+ data->peap_outer_success = 2;
+ data->crypto_binding = OPTIONAL_BINDING;
+
+ if (config && config->phase1 &&
+ eap_peap_parse_phase1(data, config->phase1) < 0) {
+ eap_peap_deinit(sm, data);
+ return NULL;
+ }
+
+ if (eap_peer_select_phase2_methods(config, "auth=",
+ &data->phase2_types,
+ &data->num_phase2_types) < 0) {
+ eap_peap_deinit(sm, data);
+ return NULL;
+ }
+
+ data->phase2_type.vendor = EAP_VENDOR_IETF;
+ data->phase2_type.method = EAP_TYPE_NONE;
+
+ if (eap_peer_tls_ssl_init(sm, &data->ssl, config, EAP_TYPE_PEAP)) {
+ wpa_printf(MSG_INFO, "EAP-PEAP: Failed to initialize SSL.");
+ eap_peap_deinit(sm, data);
+ return NULL;
+ }
+
+ return data;
+}
+
+
+static void eap_peap_free_key(struct eap_peap_data *data)
+{
+ if (data->key_data) {
+ bin_clear_free(data->key_data, EAP_TLS_KEY_LEN);
+ data->key_data = NULL;
+ }
+}
+
+
+static void eap_peap_deinit(struct eap_sm *sm, void *priv)
+{
+ struct eap_peap_data *data = priv;
+ if (data == NULL)
+ return;
+ if (data->phase2_priv && data->phase2_method)
+ data->phase2_method->deinit(sm, data->phase2_priv);
+ os_free(data->phase2_types);
+ eap_peer_tls_ssl_deinit(sm, &data->ssl);
+ eap_peap_free_key(data);
+ os_free(data->session_id);
+ wpabuf_free(data->pending_phase2_req);
+ os_free(data);
+}
+
+
+/**
+ * eap_tlv_build_nak - Build EAP-TLV NAK message
+ * @id: EAP identifier for the header
+ * @nak_type: TLV type (EAP_TLV_*)
+ * Returns: Buffer to the allocated EAP-TLV NAK message or %NULL on failure
+ *
+ * This function builds an EAP-TLV NAK message. The caller is responsible for
+ * freeing the returned buffer.
+ */
+static struct wpabuf * eap_tlv_build_nak(int id, u16 nak_type)
+{
+ struct wpabuf *msg;
+
+ msg = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_TLV, 10,
+ EAP_CODE_RESPONSE, id);
+ if (msg == NULL)
+ return NULL;
+
+ wpabuf_put_u8(msg, 0x80); /* Mandatory */
+ wpabuf_put_u8(msg, EAP_TLV_NAK_TLV);
+ wpabuf_put_be16(msg, 6); /* Length */
+ wpabuf_put_be32(msg, 0); /* Vendor-Id */
+ wpabuf_put_be16(msg, nak_type); /* NAK-Type */
+
+ return msg;
+}
+
+
+static int eap_peap_get_isk(struct eap_sm *sm, struct eap_peap_data *data,
+ u8 *isk, size_t isk_len)
+{
+ u8 *key;
+ size_t key_len;
+
+ os_memset(isk, 0, isk_len);
+ if (data->phase2_method == NULL || data->phase2_priv == NULL ||
+ data->phase2_method->isKeyAvailable == NULL ||
+ data->phase2_method->getKey == NULL)
+ return 0;
+
+ if (!data->phase2_method->isKeyAvailable(sm, data->phase2_priv) ||
+ (key = data->phase2_method->getKey(sm, data->phase2_priv,
+ &key_len)) == NULL) {
+ wpa_printf(MSG_DEBUG, "EAP-PEAP: Could not get key material "
+ "from Phase 2");
+ return -1;
+ }
+
+ if (key_len > isk_len)
+ key_len = isk_len;
+ os_memcpy(isk, key, key_len);
+ os_free(key);
+
+ return 0;
+}
+
+
+static int eap_peap_derive_cmk(struct eap_sm *sm, struct eap_peap_data *data)
+{
+ u8 *tk;
+ u8 isk[32], imck[60];
+
+ /*
+ * Tunnel key (TK) is the first 60 octets of the key generated by
+ * phase 1 of PEAP (based on TLS).
+ */
+ tk = data->key_data;
+ if (tk == NULL)
+ return -1;
+ wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: TK", tk, 60);
+
+ if (data->reauth &&
+ tls_connection_resumed(sm->ssl_ctx, data->ssl.conn)) {
+ /* Fast-connect: IPMK|CMK = TK */
+ os_memcpy(data->ipmk, tk, 40);
+ wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: IPMK from TK",
+ data->ipmk, 40);
+ os_memcpy(data->cmk, tk + 40, 20);
+ wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: CMK from TK",
+ data->cmk, 20);
+ return 0;
+ }
+
+ if (eap_peap_get_isk(sm, data, isk, sizeof(isk)) < 0)
+ return -1;
+ wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: ISK", isk, sizeof(isk));
+
+ /*
+ * IPMK Seed = "Inner Methods Compound Keys" | ISK
+ * TempKey = First 40 octets of TK
+ * IPMK|CMK = PRF+(TempKey, IPMK Seed, 60)
+ * (note: draft-josefsson-pppext-eap-tls-eap-10.txt includes a space
+ * in the end of the label just before ISK; is that just a typo?)
+ */
+ wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: TempKey", tk, 40);
+ if (peap_prfplus(data->peap_version, tk, 40,
+ "Inner Methods Compound Keys",
+ isk, sizeof(isk), imck, sizeof(imck)) < 0)
+ return -1;
+ wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: IMCK (IPMKj)",
+ imck, sizeof(imck));
+
+ os_memcpy(data->ipmk, imck, 40);
+ wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: IPMK (S-IPMKj)", data->ipmk, 40);
+ os_memcpy(data->cmk, imck + 40, 20);
+ wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: CMK (CMKj)", data->cmk, 20);
+
+ return 0;
+}
+
+
+static int eap_tlv_add_cryptobinding(struct eap_sm *sm,
+ struct eap_peap_data *data,
+ struct wpabuf *buf)
+{
+ u8 *mac;
+ u8 eap_type = EAP_TYPE_PEAP;
+ const u8 *addr[2];
+ size_t len[2];
+ u16 tlv_type;
+
+ /* Compound_MAC: HMAC-SHA1-160(cryptobinding TLV | EAP type) */
+ addr[0] = wpabuf_put(buf, 0);
+ len[0] = 60;
+ addr[1] = &eap_type;
+ len[1] = 1;
+
+ tlv_type = EAP_TLV_CRYPTO_BINDING_TLV;
+ wpabuf_put_be16(buf, tlv_type);
+ wpabuf_put_be16(buf, 56);
+
+ wpabuf_put_u8(buf, 0); /* Reserved */
+ wpabuf_put_u8(buf, data->peap_version); /* Version */
+ wpabuf_put_u8(buf, data->peap_version); /* RecvVersion */
+ wpabuf_put_u8(buf, 1); /* SubType: 0 = Request, 1 = Response */
+ wpabuf_put_data(buf, data->binding_nonce, 32); /* Nonce */
+ mac = wpabuf_put(buf, 20); /* Compound_MAC */
+ wpa_hexdump(MSG_MSGDUMP, "EAP-PEAP: Compound_MAC CMK", data->cmk, 20);
+ wpa_hexdump(MSG_MSGDUMP, "EAP-PEAP: Compound_MAC data 1",
+ addr[0], len[0]);
+ wpa_hexdump(MSG_MSGDUMP, "EAP-PEAP: Compound_MAC data 2",
+ addr[1], len[1]);
+ hmac_sha1_vector(data->cmk, 20, 2, addr, len, mac);
+ wpa_hexdump(MSG_MSGDUMP, "EAP-PEAP: Compound_MAC", mac, SHA1_MAC_LEN);
+ data->crypto_binding_used = 1;
+
+ return 0;
+}
+
+
+/**
+ * eap_tlv_build_result - Build EAP-TLV Result message
+ * @id: EAP identifier for the header
+ * @status: Status (EAP_TLV_RESULT_SUCCESS or EAP_TLV_RESULT_FAILURE)
+ * Returns: Buffer to the allocated EAP-TLV Result message or %NULL on failure
+ *
+ * This function builds an EAP-TLV Result message. The caller is responsible
+ * for freeing the returned buffer.
+ */
+static struct wpabuf * eap_tlv_build_result(struct eap_sm *sm,
+ struct eap_peap_data *data,
+ int crypto_tlv_used,
+ int id, u16 status)
+{
+ struct wpabuf *msg;
+ size_t len;
+
+ if (data->crypto_binding == NO_BINDING)
+ crypto_tlv_used = 0;
+
+ len = 6;
+ if (crypto_tlv_used)
+ len += 60; /* Cryptobinding TLV */
+ msg = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_TLV, len,
+ EAP_CODE_RESPONSE, id);
+ if (msg == NULL)
+ return NULL;
+
+ wpabuf_put_u8(msg, 0x80); /* Mandatory */
+ wpabuf_put_u8(msg, EAP_TLV_RESULT_TLV);
+ wpabuf_put_be16(msg, 2); /* Length */
+ wpabuf_put_be16(msg, status); /* Status */
+
+ if (crypto_tlv_used && eap_tlv_add_cryptobinding(sm, data, msg)) {
+ wpabuf_free(msg);
+ return NULL;
+ }
+
+ return msg;
+}
+
+
+static int eap_tlv_validate_cryptobinding(struct eap_sm *sm,
+ struct eap_peap_data *data,
+ const u8 *crypto_tlv,
+ size_t crypto_tlv_len)
+{
+ u8 buf[61], mac[SHA1_MAC_LEN];
+ const u8 *pos;
+
+ if (eap_peap_derive_cmk(sm, data) < 0) {
+ wpa_printf(MSG_DEBUG, "EAP-PEAP: Could not derive CMK");
+ return -1;
+ }
+
+ if (crypto_tlv_len != 4 + 56) {
+ wpa_printf(MSG_DEBUG, "EAP-PEAP: Invalid cryptobinding TLV "
+ "length %d", (int) crypto_tlv_len);
+ return -1;
+ }
+
+ pos = crypto_tlv;
+ pos += 4; /* TLV header */
+ if (pos[1] != data->peap_version) {
+ wpa_printf(MSG_DEBUG, "EAP-PEAP: Cryptobinding TLV Version "
+ "mismatch (was %d; expected %d)",
+ pos[1], data->peap_version);
+ return -1;
+ }
+
+ if (pos[3] != 0) {
+ wpa_printf(MSG_DEBUG, "EAP-PEAP: Unexpected Cryptobinding TLV "
+ "SubType %d", pos[3]);
+ return -1;
+ }
+ pos += 4;
+ os_memcpy(data->binding_nonce, pos, 32);
+ pos += 32; /* Nonce */
+
+ /* Compound_MAC: HMAC-SHA1-160(cryptobinding TLV | EAP type) */
+ os_memcpy(buf, crypto_tlv, 60);
+ os_memset(buf + 4 + 4 + 32, 0, 20); /* Compound_MAC */
+ buf[60] = EAP_TYPE_PEAP;
+ wpa_hexdump(MSG_DEBUG, "EAP-PEAP: Compound_MAC data",
+ buf, sizeof(buf));
+ hmac_sha1(data->cmk, 20, buf, sizeof(buf), mac);
+
+ if (os_memcmp_const(mac, pos, SHA1_MAC_LEN) != 0) {
+ wpa_printf(MSG_DEBUG, "EAP-PEAP: Invalid Compound_MAC in "
+ "cryptobinding TLV");
+ wpa_hexdump(MSG_DEBUG, "EAP-PEAP: Received MAC",
+ pos, SHA1_MAC_LEN);
+ wpa_hexdump(MSG_DEBUG, "EAP-PEAP: Expected MAC",
+ mac, SHA1_MAC_LEN);
+ return -1;
+ }
+
+ wpa_printf(MSG_DEBUG, "EAP-PEAP: Valid cryptobinding TLV received");
+
+ return 0;
+}
+
+
+/**
+ * eap_tlv_process - Process a received EAP-TLV message and generate a response
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @ret: Return values from EAP request validation and processing
+ * @req: EAP-TLV request to be processed. The caller must have validated that
+ * the buffer is large enough to contain full request (hdr->length bytes) and
+ * that the EAP type is EAP_TYPE_TLV.
+ * @resp: Buffer to return a pointer to the allocated response message. This
+ * field should be initialized to %NULL before the call. The value will be
+ * updated if a response message is generated. The caller is responsible for
+ * freeing the allocated message.
+ * @force_failure: Force negotiation to fail
+ * Returns: 0 on success, -1 on failure
+ */
+static int eap_tlv_process(struct eap_sm *sm, struct eap_peap_data *data,
+ struct eap_method_ret *ret,
+ const struct wpabuf *req, struct wpabuf **resp,
+ int force_failure)
+{
+ size_t left, tlv_len;
+ const u8 *pos;
+ const u8 *result_tlv = NULL, *crypto_tlv = NULL;
+ size_t result_tlv_len = 0, crypto_tlv_len = 0;
+ int tlv_type, mandatory;
+
+ /* Parse TLVs */
+ pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_TLV, req, &left);
+ if (pos == NULL)
+ return -1;
+ wpa_hexdump(MSG_DEBUG, "EAP-TLV: Received TLVs", pos, left);
+ while (left >= 4) {
+ mandatory = !!(pos[0] & 0x80);
+ tlv_type = WPA_GET_BE16(pos) & 0x3fff;
+ pos += 2;
+ tlv_len = WPA_GET_BE16(pos);
+ pos += 2;
+ left -= 4;
+ if (tlv_len > left) {
+ wpa_printf(MSG_DEBUG, "EAP-TLV: TLV underrun "
+ "(tlv_len=%lu left=%lu)",
+ (unsigned long) tlv_len,
+ (unsigned long) left);
+ return -1;
+ }
+ switch (tlv_type) {
+ case EAP_TLV_RESULT_TLV:
+ result_tlv = pos;
+ result_tlv_len = tlv_len;
+ break;
+ case EAP_TLV_CRYPTO_BINDING_TLV:
+ crypto_tlv = pos;
+ crypto_tlv_len = tlv_len;
+ break;
+ default:
+ wpa_printf(MSG_DEBUG, "EAP-TLV: Unsupported TLV Type "
+ "%d%s", tlv_type,
+ mandatory ? " (mandatory)" : "");
+ if (mandatory) {
+ /* NAK TLV and ignore all TLVs in this packet.
+ */
+ *resp = eap_tlv_build_nak(eap_get_id(req),
+ tlv_type);
+ return *resp == NULL ? -1 : 0;
+ }
+ /* Ignore this TLV, but process other TLVs */
+ break;
+ }
+
+ pos += tlv_len;
+ left -= tlv_len;
+ }
+ if (left) {
+ wpa_printf(MSG_DEBUG, "EAP-TLV: Last TLV too short in "
+ "Request (left=%lu)", (unsigned long) left);
+ return -1;
+ }
+
+ /* Process supported TLVs */
+ if (crypto_tlv && data->crypto_binding != NO_BINDING) {
+ wpa_hexdump(MSG_DEBUG, "EAP-PEAP: Cryptobinding TLV",
+ crypto_tlv, crypto_tlv_len);
+ if (eap_tlv_validate_cryptobinding(sm, data, crypto_tlv - 4,
+ crypto_tlv_len + 4) < 0) {
+ if (result_tlv == NULL)
+ return -1;
+ force_failure = 1;
+ crypto_tlv = NULL; /* do not include Cryptobinding TLV
+ * in response, if the received
+ * cryptobinding was invalid. */
+ }
+ } else if (!crypto_tlv && data->crypto_binding == REQUIRE_BINDING) {
+ wpa_printf(MSG_DEBUG, "EAP-PEAP: No cryptobinding TLV");
+ return -1;
+ }
+
+ if (result_tlv) {
+ int status, resp_status;
+ wpa_hexdump(MSG_DEBUG, "EAP-TLV: Result TLV",
+ result_tlv, result_tlv_len);
+ if (result_tlv_len < 2) {
+ wpa_printf(MSG_INFO, "EAP-TLV: Too short Result TLV "
+ "(len=%lu)",
+ (unsigned long) result_tlv_len);
+ return -1;
+ }
+ status = WPA_GET_BE16(result_tlv);
+ if (status == EAP_TLV_RESULT_SUCCESS) {
+ wpa_printf(MSG_INFO, "EAP-TLV: TLV Result - Success "
+ "- EAP-TLV/Phase2 Completed");
+ if (force_failure) {
+ wpa_printf(MSG_INFO, "EAP-TLV: Earlier failure"
+ " - force failed Phase 2");
+ resp_status = EAP_TLV_RESULT_FAILURE;
+ ret->decision = DECISION_FAIL;
+ } else {
+ resp_status = EAP_TLV_RESULT_SUCCESS;
+ ret->decision = DECISION_UNCOND_SUCC;
+ }
+ } else if (status == EAP_TLV_RESULT_FAILURE) {
+ wpa_printf(MSG_INFO, "EAP-TLV: TLV Result - Failure");
+ resp_status = EAP_TLV_RESULT_FAILURE;
+ ret->decision = DECISION_FAIL;
+ } else {
+ wpa_printf(MSG_INFO, "EAP-TLV: Unknown TLV Result "
+ "Status %d", status);
+ resp_status = EAP_TLV_RESULT_FAILURE;
+ ret->decision = DECISION_FAIL;
+ }
+ ret->methodState = METHOD_DONE;
+
+ *resp = eap_tlv_build_result(sm, data, crypto_tlv != NULL,
+ eap_get_id(req), resp_status);
+ }
+
+ return 0;
+}
+
+
+static int eap_peap_phase2_request(struct eap_sm *sm,
+ struct eap_peap_data *data,
+ struct eap_method_ret *ret,
+ struct wpabuf *req,
+ struct wpabuf **resp)
+{
+ struct eap_hdr *hdr = wpabuf_mhead(req);
+ size_t len = be_to_host16(hdr->length);
+ u8 *pos;
+ struct eap_method_ret iret;
+ struct eap_peer_config *config = eap_get_config(sm);
+
+ if (len <= sizeof(struct eap_hdr)) {
+ wpa_printf(MSG_INFO, "EAP-PEAP: too short "
+ "Phase 2 request (len=%lu)", (unsigned long) len);
+ return -1;
+ }
+ pos = (u8 *) (hdr + 1);
+ wpa_printf(MSG_DEBUG, "EAP-PEAP: Phase 2 Request: type=%d", *pos);
+ switch (*pos) {
+ case EAP_TYPE_IDENTITY:
+ *resp = eap_sm_buildIdentity(sm, hdr->identifier, 1);
+ break;
+ case EAP_TYPE_TLV:
+ os_memset(&iret, 0, sizeof(iret));
+ if (eap_tlv_process(sm, data, &iret, req, resp,
+ data->phase2_eap_started &&
+ !data->phase2_eap_success)) {
+ ret->methodState = METHOD_DONE;
+ ret->decision = DECISION_FAIL;
+ return -1;
+ }
+ if (iret.methodState == METHOD_DONE ||
+ iret.methodState == METHOD_MAY_CONT) {
+ ret->methodState = iret.methodState;
+ ret->decision = iret.decision;
+ data->phase2_success = 1;
+ }
+ break;
+ case EAP_TYPE_EXPANDED:
+#ifdef EAP_TNC
+ if (data->soh) {
+ const u8 *epos;
+ size_t eleft;
+
+ epos = eap_hdr_validate(EAP_VENDOR_MICROSOFT, 0x21,
+ req, &eleft);
+ if (epos) {
+ struct wpabuf *buf;
+ wpa_printf(MSG_DEBUG,
+ "EAP-PEAP: SoH EAP Extensions");
+ buf = tncc_process_soh_request(data->soh,
+ epos, eleft);
+ if (buf) {
+ *resp = eap_msg_alloc(
+ EAP_VENDOR_MICROSOFT, 0x21,
+ wpabuf_len(buf),
+ EAP_CODE_RESPONSE,
+ hdr->identifier);
+ if (*resp == NULL) {
+ ret->methodState = METHOD_DONE;
+ ret->decision = DECISION_FAIL;
+ return -1;
+ }
+ wpabuf_put_buf(*resp, buf);
+ wpabuf_free(buf);
+ break;
+ }
+ }
+ }
+#endif /* EAP_TNC */
+ /* fall through */
+ default:
+ if (data->phase2_type.vendor == EAP_VENDOR_IETF &&
+ data->phase2_type.method == EAP_TYPE_NONE) {
+ size_t i;
+ for (i = 0; i < data->num_phase2_types; i++) {
+ if (data->phase2_types[i].vendor !=
+ EAP_VENDOR_IETF ||
+ data->phase2_types[i].method != *pos)
+ continue;
+
+ data->phase2_type.vendor =
+ data->phase2_types[i].vendor;
+ data->phase2_type.method =
+ data->phase2_types[i].method;
+ wpa_printf(MSG_DEBUG, "EAP-PEAP: Selected "
+ "Phase 2 EAP vendor %d method %d",
+ data->phase2_type.vendor,
+ data->phase2_type.method);
+ break;
+ }
+ }
+ if (*pos != data->phase2_type.method ||
+ *pos == EAP_TYPE_NONE) {
+ if (eap_peer_tls_phase2_nak(data->phase2_types,
+ data->num_phase2_types,
+ hdr, resp))
+ return -1;
+ return 0;
+ }
+
+ if (data->phase2_priv == NULL) {
+ data->phase2_method = eap_peer_get_eap_method(
+ data->phase2_type.vendor,
+ data->phase2_type.method);
+ if (data->phase2_method) {
+ sm->init_phase2 = 1;
+ data->phase2_priv =
+ data->phase2_method->init(sm);
+ sm->init_phase2 = 0;
+ }
+ }
+ if (data->phase2_priv == NULL || data->phase2_method == NULL) {
+ wpa_printf(MSG_INFO, "EAP-PEAP: failed to initialize "
+ "Phase 2 EAP method %d", *pos);
+ ret->methodState = METHOD_DONE;
+ ret->decision = DECISION_FAIL;
+ return -1;
+ }
+ data->phase2_eap_started = 1;
+ os_memset(&iret, 0, sizeof(iret));
+ *resp = data->phase2_method->process(sm, data->phase2_priv,
+ &iret, req);
+ if ((iret.methodState == METHOD_DONE ||
+ iret.methodState == METHOD_MAY_CONT) &&
+ (iret.decision == DECISION_UNCOND_SUCC ||
+ iret.decision == DECISION_COND_SUCC)) {
+ data->phase2_eap_success = 1;
+ data->phase2_success = 1;
+ }
+ break;
+ }
+
+ if (*resp == NULL &&
+ (config->pending_req_identity || config->pending_req_password ||
+ config->pending_req_otp || config->pending_req_new_password)) {
+ wpabuf_free(data->pending_phase2_req);
+ data->pending_phase2_req = wpabuf_alloc_copy(hdr, len);
+ }
+
+ return 0;
+}
+
+
+static int eap_peap_decrypt(struct eap_sm *sm, struct eap_peap_data *data,
+ struct eap_method_ret *ret,
+ const struct eap_hdr *req,
+ const struct wpabuf *in_data,
+ struct wpabuf **out_data)
+{
+ struct wpabuf *in_decrypted = NULL;
+ int res, skip_change = 0;
+ struct eap_hdr *hdr, *rhdr;
+ struct wpabuf *resp = NULL;
+ size_t len;
+
+ wpa_printf(MSG_DEBUG, "EAP-PEAP: received %lu bytes encrypted data for"
+ " Phase 2", (unsigned long) wpabuf_len(in_data));
+
+ if (data->pending_phase2_req) {
+ wpa_printf(MSG_DEBUG, "EAP-PEAP: Pending Phase 2 request - "
+ "skip decryption and use old data");
+ /* Clear TLS reassembly state. */
+ eap_peer_tls_reset_input(&data->ssl);
+ in_decrypted = data->pending_phase2_req;
+ data->pending_phase2_req = NULL;
+ skip_change = 1;
+ goto continue_req;
+ }
+
+ if (wpabuf_len(in_data) == 0 && sm->workaround &&
+ data->phase2_success) {
+ /*
+ * Cisco ACS seems to be using TLS ACK to terminate
+ * EAP-PEAPv0/GTC. Try to reply with TLS ACK.
+ */
+ wpa_printf(MSG_DEBUG, "EAP-PEAP: Received TLS ACK, but "
+ "expected data - acknowledge with TLS ACK since "
+ "Phase 2 has been completed");
+ ret->decision = DECISION_COND_SUCC;
+ ret->methodState = METHOD_DONE;
+ return 1;
+ } else if (wpabuf_len(in_data) == 0) {
+ /* Received TLS ACK - requesting more fragments */
+ return eap_peer_tls_encrypt(sm, &data->ssl, EAP_TYPE_PEAP,
+ data->peap_version,
+ req->identifier, NULL, out_data);
+ }
+
+ res = eap_peer_tls_decrypt(sm, &data->ssl, in_data, &in_decrypted);
+ if (res)
+ return res;
+
+continue_req:
+ wpa_hexdump_buf(MSG_DEBUG, "EAP-PEAP: Decrypted Phase 2 EAP",
+ in_decrypted);
+
+ hdr = wpabuf_mhead(in_decrypted);
+ if (wpabuf_len(in_decrypted) == 5 && hdr->code == EAP_CODE_REQUEST &&
+ be_to_host16(hdr->length) == 5 &&
+ eap_get_type(in_decrypted) == EAP_TYPE_IDENTITY) {
+ /* At least FreeRADIUS seems to send full EAP header with
+ * EAP Request Identity */
+ skip_change = 1;
+ }
+ if (wpabuf_len(in_decrypted) >= 5 && hdr->code == EAP_CODE_REQUEST &&
+ eap_get_type(in_decrypted) == EAP_TYPE_TLV) {
+ skip_change = 1;
+ }
+
+ if (data->peap_version == 0 && !skip_change) {
+ struct eap_hdr *nhdr;
+ struct wpabuf *nmsg = wpabuf_alloc(sizeof(struct eap_hdr) +
+ wpabuf_len(in_decrypted));
+ if (nmsg == NULL) {
+ wpabuf_free(in_decrypted);
+ return 0;
+ }
+ nhdr = wpabuf_put(nmsg, sizeof(*nhdr));
+ wpabuf_put_buf(nmsg, in_decrypted);
+ nhdr->code = req->code;
+ nhdr->identifier = req->identifier;
+ nhdr->length = host_to_be16(sizeof(struct eap_hdr) +
+ wpabuf_len(in_decrypted));
+
+ wpabuf_free(in_decrypted);
+ in_decrypted = nmsg;
+ }
+
+ hdr = wpabuf_mhead(in_decrypted);
+ if (wpabuf_len(in_decrypted) < sizeof(*hdr)) {
+ wpa_printf(MSG_INFO, "EAP-PEAP: Too short Phase 2 "
+ "EAP frame (len=%lu)",
+ (unsigned long) wpabuf_len(in_decrypted));
+ wpabuf_free(in_decrypted);
+ return 0;
+ }
+ len = be_to_host16(hdr->length);
+ if (len > wpabuf_len(in_decrypted)) {
+ wpa_printf(MSG_INFO, "EAP-PEAP: Length mismatch in "
+ "Phase 2 EAP frame (len=%lu hdr->length=%lu)",
+ (unsigned long) wpabuf_len(in_decrypted),
+ (unsigned long) len);
+ wpabuf_free(in_decrypted);
+ return 0;
+ }
+ if (len < wpabuf_len(in_decrypted)) {
+ wpa_printf(MSG_INFO, "EAP-PEAP: Odd.. Phase 2 EAP header has "
+ "shorter length than full decrypted data "
+ "(%lu < %lu)",
+ (unsigned long) len,
+ (unsigned long) wpabuf_len(in_decrypted));
+ }
+ wpa_printf(MSG_DEBUG, "EAP-PEAP: received Phase 2: code=%d "
+ "identifier=%d length=%lu", hdr->code, hdr->identifier,
+ (unsigned long) len);
+ switch (hdr->code) {
+ case EAP_CODE_REQUEST:
+ if (eap_peap_phase2_request(sm, data, ret, in_decrypted,
+ &resp)) {
+ wpabuf_free(in_decrypted);
+ wpa_printf(MSG_INFO, "EAP-PEAP: Phase2 Request "
+ "processing failed");
+ return 0;
+ }
+ break;
+ case EAP_CODE_SUCCESS:
+ wpa_printf(MSG_DEBUG, "EAP-PEAP: Phase 2 Success");
+ if (data->peap_version == 1) {
+ /* EAP-Success within TLS tunnel is used to indicate
+ * shutdown of the TLS channel. The authentication has
+ * been completed. */
+ if (data->phase2_eap_started &&
+ !data->phase2_eap_success) {
+ wpa_printf(MSG_DEBUG, "EAP-PEAP: Phase 2 "
+ "Success used to indicate success, "
+ "but Phase 2 EAP was not yet "
+ "completed successfully");
+ ret->methodState = METHOD_DONE;
+ ret->decision = DECISION_FAIL;
+ wpabuf_free(in_decrypted);
+ return 0;
+ }
+ wpa_printf(MSG_DEBUG, "EAP-PEAP: Version 1 - "
+ "EAP-Success within TLS tunnel - "
+ "authentication completed");
+ ret->decision = DECISION_UNCOND_SUCC;
+ ret->methodState = METHOD_DONE;
+ data->phase2_success = 1;
+ if (data->peap_outer_success == 2) {
+ wpabuf_free(in_decrypted);
+ wpa_printf(MSG_DEBUG, "EAP-PEAP: Use TLS ACK "
+ "to finish authentication");
+ return 1;
+ } else if (data->peap_outer_success == 1) {
+ /* Reply with EAP-Success within the TLS
+ * channel to complete the authentication. */
+ resp = wpabuf_alloc(sizeof(struct eap_hdr));
+ if (resp) {
+ rhdr = wpabuf_put(resp, sizeof(*rhdr));
+ rhdr->code = EAP_CODE_SUCCESS;
+ rhdr->identifier = hdr->identifier;
+ rhdr->length =
+ host_to_be16(sizeof(*rhdr));
+ }
+ } else {
+ /* No EAP-Success expected for Phase 1 (outer,
+ * unencrypted auth), so force EAP state
+ * machine to SUCCESS state. */
+ sm->peap_done = TRUE;
+ }
+ } else {
+ /* FIX: ? */
+ }
+ break;
+ case EAP_CODE_FAILURE:
+ wpa_printf(MSG_DEBUG, "EAP-PEAP: Phase 2 Failure");
+ ret->decision = DECISION_FAIL;
+ ret->methodState = METHOD_MAY_CONT;
+ ret->allowNotifications = FALSE;
+ /* Reply with EAP-Failure within the TLS channel to complete
+ * failure reporting. */
+ resp = wpabuf_alloc(sizeof(struct eap_hdr));
+ if (resp) {
+ rhdr = wpabuf_put(resp, sizeof(*rhdr));
+ rhdr->code = EAP_CODE_FAILURE;
+ rhdr->identifier = hdr->identifier;
+ rhdr->length = host_to_be16(sizeof(*rhdr));
+ }
+ break;
+ default:
+ wpa_printf(MSG_INFO, "EAP-PEAP: Unexpected code=%d in "
+ "Phase 2 EAP header", hdr->code);
+ break;
+ }
+
+ wpabuf_free(in_decrypted);
+
+ if (resp) {
+ int skip_change2 = 0;
+ struct wpabuf *rmsg, buf;
+
+ wpa_hexdump_buf_key(MSG_DEBUG,
+ "EAP-PEAP: Encrypting Phase 2 data", resp);
+ /* PEAP version changes */
+ if (wpabuf_len(resp) >= 5 &&
+ wpabuf_head_u8(resp)[0] == EAP_CODE_RESPONSE &&
+ eap_get_type(resp) == EAP_TYPE_TLV)
+ skip_change2 = 1;
+ rmsg = resp;
+ if (data->peap_version == 0 && !skip_change2) {
+ wpabuf_set(&buf, wpabuf_head_u8(resp) +
+ sizeof(struct eap_hdr),
+ wpabuf_len(resp) - sizeof(struct eap_hdr));
+ rmsg = &buf;
+ }
+
+ if (eap_peer_tls_encrypt(sm, &data->ssl, EAP_TYPE_PEAP,
+ data->peap_version, req->identifier,
+ rmsg, out_data)) {
+ wpa_printf(MSG_INFO, "EAP-PEAP: Failed to encrypt "
+ "a Phase 2 frame");
+ }
+ wpabuf_free(resp);
+ }
+
+ return 0;
+}
+
+
+static struct wpabuf * eap_peap_process(struct eap_sm *sm, void *priv,
+ struct eap_method_ret *ret,
+ const struct wpabuf *reqData)
+{
+ const struct eap_hdr *req;
+ size_t left;
+ int res;
+ u8 flags, id;
+ struct wpabuf *resp;
+ const u8 *pos;
+ struct eap_peap_data *data = priv;
+ struct wpabuf msg;
+
+ pos = eap_peer_tls_process_init(sm, &data->ssl, EAP_TYPE_PEAP, ret,
+ reqData, &left, &flags);
+ if (pos == NULL)
+ return NULL;
+ req = wpabuf_head(reqData);
+ id = req->identifier;
+
+ if (flags & EAP_TLS_FLAGS_START) {
+ wpa_printf(MSG_DEBUG, "EAP-PEAP: Start (server ver=%d, own "
+ "ver=%d)", flags & EAP_TLS_VERSION_MASK,
+ data->peap_version);
+ if ((flags & EAP_TLS_VERSION_MASK) < data->peap_version)
+ data->peap_version = flags & EAP_TLS_VERSION_MASK;
+ if (data->force_peap_version >= 0 &&
+ data->force_peap_version != data->peap_version) {
+ wpa_printf(MSG_WARNING, "EAP-PEAP: Failed to select "
+ "forced PEAP version %d",
+ data->force_peap_version);
+ ret->methodState = METHOD_DONE;
+ ret->decision = DECISION_FAIL;
+ ret->allowNotifications = FALSE;
+ return NULL;
+ }
+ wpa_printf(MSG_DEBUG, "EAP-PEAP: Using PEAP version %d",
+ data->peap_version);
+ left = 0; /* make sure that this frame is empty, even though it
+ * should always be, anyway */
+ }
+
+ wpabuf_set(&msg, pos, left);
+
+ resp = NULL;
+ if (tls_connection_established(sm->ssl_ctx, data->ssl.conn) &&
+ !data->resuming) {
+ res = eap_peap_decrypt(sm, data, ret, req, &msg, &resp);
+ } else {
+ res = eap_peer_tls_process_helper(sm, &data->ssl,
+ EAP_TYPE_PEAP,
+ data->peap_version, id, &msg,
+ &resp);
+
+ if (res < 0) {
+ wpa_printf(MSG_DEBUG,
+ "EAP-PEAP: TLS processing failed");
+ ret->methodState = METHOD_DONE;
+ ret->decision = DECISION_FAIL;
+ return resp;
+ }
+ if (tls_connection_established(sm->ssl_ctx, data->ssl.conn)) {
+ char *label;
+ wpa_printf(MSG_DEBUG,
+ "EAP-PEAP: TLS done, proceed to Phase 2");
+ eap_peap_free_key(data);
+ /* draft-josefsson-ppext-eap-tls-eap-05.txt
+ * specifies that PEAPv1 would use "client PEAP
+ * encryption" as the label. However, most existing
+ * PEAPv1 implementations seem to be using the old
+ * label, "client EAP encryption", instead. Use the old
+ * label by default, but allow it to be configured with
+ * phase1 parameter peaplabel=1. */
+ if (data->force_new_label)
+ label = "client PEAP encryption";
+ else
+ label = "client EAP encryption";
+ wpa_printf(MSG_DEBUG, "EAP-PEAP: using label '%s' in "
+ "key derivation", label);
+ data->key_data =
+ eap_peer_tls_derive_key(sm, &data->ssl, label,
+ EAP_TLS_KEY_LEN);
+ if (data->key_data) {
+ wpa_hexdump_key(MSG_DEBUG,
+ "EAP-PEAP: Derived key",
+ data->key_data,
+ EAP_TLS_KEY_LEN);
+ } else {
+ wpa_printf(MSG_DEBUG, "EAP-PEAP: Failed to "
+ "derive key");
+ }
+
+ os_free(data->session_id);
+ data->session_id =
+ eap_peer_tls_derive_session_id(sm, &data->ssl,
+ EAP_TYPE_PEAP,
+ &data->id_len);
+ if (data->session_id) {
+ wpa_hexdump(MSG_DEBUG,
+ "EAP-PEAP: Derived Session-Id",
+ data->session_id, data->id_len);
+ } else {
+ wpa_printf(MSG_ERROR, "EAP-PEAP: Failed to "
+ "derive Session-Id");
+ }
+
+ if (sm->workaround && data->resuming) {
+ /*
+ * At least few RADIUS servers (Aegis v1.1.6;
+ * but not v1.1.4; and Cisco ACS) seem to be
+ * terminating PEAPv1 (Aegis) or PEAPv0 (Cisco
+ * ACS) session resumption with outer
+ * EAP-Success. This does not seem to follow
+ * draft-josefsson-pppext-eap-tls-eap-05.txt
+ * section 4.2, so only allow this if EAP
+ * workarounds are enabled.
+ */
+ wpa_printf(MSG_DEBUG, "EAP-PEAP: Workaround - "
+ "allow outer EAP-Success to "
+ "terminate PEAP resumption");
+ ret->decision = DECISION_COND_SUCC;
+ data->phase2_success = 1;
+ }
+
+ data->resuming = 0;
+ }
+
+ if (res == 2) {
+ /*
+ * Application data included in the handshake message.
+ */
+ wpabuf_free(data->pending_phase2_req);
+ data->pending_phase2_req = resp;
+ resp = NULL;
+ res = eap_peap_decrypt(sm, data, ret, req, &msg,
+ &resp);
+ }
+ }
+
+ if (ret->methodState == METHOD_DONE) {
+ ret->allowNotifications = FALSE;
+ }
+
+ if (res == 1) {
+ wpabuf_free(resp);
+ return eap_peer_tls_build_ack(id, EAP_TYPE_PEAP,
+ data->peap_version);
+ }
+
+ return resp;
+}
+
+
+static Boolean eap_peap_has_reauth_data(struct eap_sm *sm, void *priv)
+{
+ struct eap_peap_data *data = priv;
+ return tls_connection_established(sm->ssl_ctx, data->ssl.conn) &&
+ data->phase2_success;
+}
+
+
+static void eap_peap_deinit_for_reauth(struct eap_sm *sm, void *priv)
+{
+ struct eap_peap_data *data = priv;
+ wpabuf_free(data->pending_phase2_req);
+ data->pending_phase2_req = NULL;
+ data->crypto_binding_used = 0;
+}
+
+
+static void * eap_peap_init_for_reauth(struct eap_sm *sm, void *priv)
+{
+ struct eap_peap_data *data = priv;
+ eap_peap_free_key(data);
+ os_free(data->session_id);
+ data->session_id = NULL;
+ if (eap_peer_tls_reauth_init(sm, &data->ssl)) {
+ os_free(data);
+ return NULL;
+ }
+ if (data->phase2_priv && data->phase2_method &&
+ data->phase2_method->init_for_reauth)
+ data->phase2_method->init_for_reauth(sm, data->phase2_priv);
+ data->phase2_success = 0;
+ data->phase2_eap_success = 0;
+ data->phase2_eap_started = 0;
+ data->resuming = 1;
+ data->reauth = 1;
+ sm->peap_done = FALSE;
+ return priv;
+}
+
+
+static int eap_peap_get_status(struct eap_sm *sm, void *priv, char *buf,
+ size_t buflen, int verbose)
+{
+ struct eap_peap_data *data = priv;
+ int len, ret;
+
+ len = eap_peer_tls_status(sm, &data->ssl, buf, buflen, verbose);
+ if (data->phase2_method) {
+ ret = os_snprintf(buf + len, buflen - len,
+ "EAP-PEAPv%d Phase2 method=%s\n",
+ data->peap_version,
+ data->phase2_method->name);
+ if (os_snprintf_error(buflen - len, ret))
+ return len;
+ len += ret;
+ }
+ return len;
+}
+
+
+static Boolean eap_peap_isKeyAvailable(struct eap_sm *sm, void *priv)
+{
+ struct eap_peap_data *data = priv;
+ return data->key_data != NULL && data->phase2_success;
+}
+
+
+static u8 * eap_peap_getKey(struct eap_sm *sm, void *priv, size_t *len)
+{
+ struct eap_peap_data *data = priv;
+ u8 *key;
+
+ if (data->key_data == NULL || !data->phase2_success)
+ return NULL;
+
+ key = os_malloc(EAP_TLS_KEY_LEN);
+ if (key == NULL)
+ return NULL;
+
+ *len = EAP_TLS_KEY_LEN;
+
+ if (data->crypto_binding_used) {
+ u8 csk[128];
+ /*
+ * Note: It looks like Microsoft implementation requires null
+ * termination for this label while the one used for deriving
+ * IPMK|CMK did not use null termination.
+ */
+ if (peap_prfplus(data->peap_version, data->ipmk, 40,
+ "Session Key Generating Function",
+ (u8 *) "\00", 1, csk, sizeof(csk)) < 0) {
+ os_free(key);
+ return NULL;
+ }
+ wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: CSK", csk, sizeof(csk));
+ os_memcpy(key, csk, EAP_TLS_KEY_LEN);
+ wpa_hexdump(MSG_DEBUG, "EAP-PEAP: Derived key",
+ key, EAP_TLS_KEY_LEN);
+ } else
+ os_memcpy(key, data->key_data, EAP_TLS_KEY_LEN);
+
+ return key;
+}
+
+
+static u8 * eap_peap_get_session_id(struct eap_sm *sm, void *priv, size_t *len)
+{
+ struct eap_peap_data *data = priv;
+ u8 *id;
+
+ if (data->session_id == NULL || !data->phase2_success)
+ return NULL;
+
+ id = os_malloc(data->id_len);
+ if (id == NULL)
+ return NULL;
+
+ *len = data->id_len;
+ os_memcpy(id, data->session_id, data->id_len);
+
+ return id;
+}
+
+
+int eap_peer_peap_register(void)
+{
+ struct eap_method *eap;
+ int ret;
+
+ eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION,
+ EAP_VENDOR_IETF, EAP_TYPE_PEAP, "PEAP");
+ if (eap == NULL)
+ return -1;
+
+ eap->init = eap_peap_init;
+ eap->deinit = eap_peap_deinit;
+ eap->process = eap_peap_process;
+ eap->isKeyAvailable = eap_peap_isKeyAvailable;
+ eap->getKey = eap_peap_getKey;
+ eap->get_status = eap_peap_get_status;
+ eap->has_reauth_data = eap_peap_has_reauth_data;
+ eap->deinit_for_reauth = eap_peap_deinit_for_reauth;
+ eap->init_for_reauth = eap_peap_init_for_reauth;
+ eap->getSessionId = eap_peap_get_session_id;
+
+ ret = eap_peer_method_register(eap);
+ if (ret)
+ eap_peer_method_free(eap);
+ return ret;
+}
diff --git a/freebsd/contrib/wpa/src/eap_peer/eap_proxy.h b/freebsd/contrib/wpa/src/eap_peer/eap_proxy.h
new file mode 100644
index 00000000..23cdbe69
--- /dev/null
+++ b/freebsd/contrib/wpa/src/eap_peer/eap_proxy.h
@@ -0,0 +1,49 @@
+/*
+ * EAP proxy definitions
+ * Copyright (c) 2011-2013 Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef EAP_PROXY_H
+#define EAP_PROXY_H
+
+struct eap_proxy_sm;
+struct eapol_callbacks;
+struct eap_sm;
+struct eap_peer_config;
+
+enum eap_proxy_status {
+ EAP_PROXY_FAILURE = 0x00,
+ EAP_PROXY_SUCCESS
+};
+
+struct eap_proxy_sm *
+eap_proxy_init(void *eapol_ctx, struct eapol_callbacks *eapol_cb,
+ void *msg_ctx);
+
+void eap_proxy_deinit(struct eap_proxy_sm *eap_proxy);
+
+int eap_proxy_key_available(struct eap_proxy_sm *sm);
+
+const u8 * eap_proxy_get_eapKeyData(struct eap_proxy_sm *sm, size_t *len);
+
+struct wpabuf * eap_proxy_get_eapRespData(struct eap_proxy_sm *sm);
+
+int eap_proxy_sm_step(struct eap_proxy_sm *sm, struct eap_sm *eap_sm);
+
+enum eap_proxy_status
+eap_proxy_packet_update(struct eap_proxy_sm *eap_proxy, u8 *eapReqData,
+ int eapReqDataLen);
+
+int eap_proxy_sm_get_status(struct eap_proxy_sm *sm, char *buf, size_t buflen,
+ int verbose);
+
+int eap_proxy_get_imsi(struct eap_proxy_sm *eap_proxy, char *imsi_buf,
+ size_t *imsi_len);
+
+int eap_proxy_notify_config(struct eap_proxy_sm *sm,
+ struct eap_peer_config *config);
+
+#endif /* EAP_PROXY_H */
diff --git a/freebsd/contrib/wpa/src/eap_peer/eap_psk.c b/freebsd/contrib/wpa/src/eap_peer/eap_psk.c
new file mode 100644
index 00000000..f91c9b39
--- /dev/null
+++ b/freebsd/contrib/wpa/src/eap_peer/eap_psk.c
@@ -0,0 +1,504 @@
+#include <machine/rtems-bsd-user-space.h>
+
+/*
+ * EAP peer method: EAP-PSK (RFC 4764)
+ * Copyright (c) 2004-2008, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ *
+ * Note: EAP-PSK is an EAP authentication method and as such, completely
+ * different from WPA-PSK. This file is not needed for WPA-PSK functionality.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "crypto/aes_wrap.h"
+#include "crypto/random.h"
+#include "eap_common/eap_psk_common.h"
+#include "eap_i.h"
+
+
+struct eap_psk_data {
+ enum { PSK_INIT, PSK_MAC_SENT, PSK_DONE } state;
+ u8 rand_p[EAP_PSK_RAND_LEN];
+ u8 rand_s[EAP_PSK_RAND_LEN];
+ u8 ak[EAP_PSK_AK_LEN], kdk[EAP_PSK_KDK_LEN], tek[EAP_PSK_TEK_LEN];
+ u8 *id_s, *id_p;
+ size_t id_s_len, id_p_len;
+ u8 msk[EAP_MSK_LEN];
+ u8 emsk[EAP_EMSK_LEN];
+};
+
+
+static void * eap_psk_init(struct eap_sm *sm)
+{
+ struct eap_psk_data *data;
+ const u8 *identity, *password;
+ size_t identity_len, password_len;
+
+ password = eap_get_config_password(sm, &password_len);
+ if (!password || password_len != 16) {
+ wpa_printf(MSG_INFO, "EAP-PSK: 16-octet pre-shared key not "
+ "configured");
+ return NULL;
+ }
+
+ data = os_zalloc(sizeof(*data));
+ if (data == NULL)
+ return NULL;
+ if (eap_psk_key_setup(password, data->ak, data->kdk)) {
+ os_free(data);
+ return NULL;
+ }
+ wpa_hexdump_key(MSG_DEBUG, "EAP-PSK: AK", data->ak, EAP_PSK_AK_LEN);
+ wpa_hexdump_key(MSG_DEBUG, "EAP-PSK: KDK", data->kdk, EAP_PSK_KDK_LEN);
+ data->state = PSK_INIT;
+
+ identity = eap_get_config_identity(sm, &identity_len);
+ if (identity) {
+ data->id_p = os_malloc(identity_len);
+ if (data->id_p)
+ os_memcpy(data->id_p, identity, identity_len);
+ data->id_p_len = identity_len;
+ }
+ if (data->id_p == NULL) {
+ wpa_printf(MSG_INFO, "EAP-PSK: could not get own identity");
+ os_free(data);
+ return NULL;
+ }
+
+ return data;
+}
+
+
+static void eap_psk_deinit(struct eap_sm *sm, void *priv)
+{
+ struct eap_psk_data *data = priv;
+ os_free(data->id_s);
+ os_free(data->id_p);
+ bin_clear_free(data, sizeof(*data));
+}
+
+
+static struct wpabuf * eap_psk_process_1(struct eap_psk_data *data,
+ struct eap_method_ret *ret,
+ const struct wpabuf *reqData)
+{
+ const struct eap_psk_hdr_1 *hdr1;
+ struct eap_psk_hdr_2 *hdr2;
+ struct wpabuf *resp;
+ u8 *buf, *pos;
+ size_t buflen, len;
+ const u8 *cpos;
+
+ wpa_printf(MSG_DEBUG, "EAP-PSK: in INIT state");
+
+ cpos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_PSK, reqData, &len);
+ hdr1 = (const struct eap_psk_hdr_1 *) cpos;
+ if (cpos == NULL || len < sizeof(*hdr1)) {
+ wpa_printf(MSG_INFO, "EAP-PSK: Invalid first message "
+ "length (%lu; expected %lu or more)",
+ (unsigned long) len,
+ (unsigned long) sizeof(*hdr1));
+ ret->ignore = TRUE;
+ return NULL;
+ }
+ wpa_printf(MSG_DEBUG, "EAP-PSK: Flags=0x%x", hdr1->flags);
+ if (EAP_PSK_FLAGS_GET_T(hdr1->flags) != 0) {
+ wpa_printf(MSG_INFO, "EAP-PSK: Unexpected T=%d (expected 0)",
+ EAP_PSK_FLAGS_GET_T(hdr1->flags));
+ ret->methodState = METHOD_DONE;
+ ret->decision = DECISION_FAIL;
+ return NULL;
+ }
+ wpa_hexdump(MSG_DEBUG, "EAP-PSK: RAND_S", hdr1->rand_s,
+ EAP_PSK_RAND_LEN);
+ os_memcpy(data->rand_s, hdr1->rand_s, EAP_PSK_RAND_LEN);
+ os_free(data->id_s);
+ data->id_s_len = len - sizeof(*hdr1);
+ data->id_s = os_malloc(data->id_s_len);
+ if (data->id_s == NULL) {
+ wpa_printf(MSG_ERROR, "EAP-PSK: Failed to allocate memory for "
+ "ID_S (len=%lu)", (unsigned long) data->id_s_len);
+ ret->ignore = TRUE;
+ return NULL;
+ }
+ os_memcpy(data->id_s, (u8 *) (hdr1 + 1), data->id_s_len);
+ wpa_hexdump_ascii(MSG_DEBUG, "EAP-PSK: ID_S",
+ data->id_s, data->id_s_len);
+
+ if (random_get_bytes(data->rand_p, EAP_PSK_RAND_LEN)) {
+ wpa_printf(MSG_ERROR, "EAP-PSK: Failed to get random data");
+ ret->ignore = TRUE;
+ return NULL;
+ }
+
+ resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_PSK,
+ sizeof(*hdr2) + data->id_p_len, EAP_CODE_RESPONSE,
+ eap_get_id(reqData));
+ if (resp == NULL)
+ return NULL;
+ hdr2 = wpabuf_put(resp, sizeof(*hdr2));
+ hdr2->flags = EAP_PSK_FLAGS_SET_T(1); /* T=1 */
+ os_memcpy(hdr2->rand_s, hdr1->rand_s, EAP_PSK_RAND_LEN);
+ os_memcpy(hdr2->rand_p, data->rand_p, EAP_PSK_RAND_LEN);
+ wpabuf_put_data(resp, data->id_p, data->id_p_len);
+ /* MAC_P = OMAC1-AES-128(AK, ID_P||ID_S||RAND_S||RAND_P) */
+ buflen = data->id_p_len + data->id_s_len + 2 * EAP_PSK_RAND_LEN;
+ buf = os_malloc(buflen);
+ if (buf == NULL) {
+ wpabuf_free(resp);
+ return NULL;
+ }
+ os_memcpy(buf, data->id_p, data->id_p_len);
+ pos = buf + data->id_p_len;
+ os_memcpy(pos, data->id_s, data->id_s_len);
+ pos += data->id_s_len;
+ os_memcpy(pos, hdr1->rand_s, EAP_PSK_RAND_LEN);
+ pos += EAP_PSK_RAND_LEN;
+ os_memcpy(pos, data->rand_p, EAP_PSK_RAND_LEN);
+ if (omac1_aes_128(data->ak, buf, buflen, hdr2->mac_p)) {
+ os_free(buf);
+ wpabuf_free(resp);
+ return NULL;
+ }
+ os_free(buf);
+ wpa_hexdump(MSG_DEBUG, "EAP-PSK: RAND_P", hdr2->rand_p,
+ EAP_PSK_RAND_LEN);
+ wpa_hexdump(MSG_DEBUG, "EAP-PSK: MAC_P", hdr2->mac_p, EAP_PSK_MAC_LEN);
+ wpa_hexdump_ascii(MSG_DEBUG, "EAP-PSK: ID_P",
+ data->id_p, data->id_p_len);
+
+ data->state = PSK_MAC_SENT;
+
+ return resp;
+}
+
+
+static struct wpabuf * eap_psk_process_3(struct eap_psk_data *data,
+ struct eap_method_ret *ret,
+ const struct wpabuf *reqData)
+{
+ const struct eap_psk_hdr_3 *hdr3;
+ struct eap_psk_hdr_4 *hdr4;
+ struct wpabuf *resp;
+ u8 *buf, *rpchannel, nonce[16], *decrypted;
+ const u8 *pchannel, *tag, *msg;
+ u8 mac[EAP_PSK_MAC_LEN];
+ size_t buflen, left, data_len, len, plen;
+ int failed = 0;
+ const u8 *pos;
+
+ wpa_printf(MSG_DEBUG, "EAP-PSK: in MAC_SENT state");
+
+ pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_PSK,
+ reqData, &len);
+ hdr3 = (const struct eap_psk_hdr_3 *) pos;
+ if (pos == NULL || len < sizeof(*hdr3)) {
+ wpa_printf(MSG_INFO, "EAP-PSK: Invalid third message "
+ "length (%lu; expected %lu or more)",
+ (unsigned long) len,
+ (unsigned long) sizeof(*hdr3));
+ ret->ignore = TRUE;
+ return NULL;
+ }
+ left = len - sizeof(*hdr3);
+ pchannel = (const u8 *) (hdr3 + 1);
+ wpa_printf(MSG_DEBUG, "EAP-PSK: Flags=0x%x", hdr3->flags);
+ if (EAP_PSK_FLAGS_GET_T(hdr3->flags) != 2) {
+ wpa_printf(MSG_INFO, "EAP-PSK: Unexpected T=%d (expected 2)",
+ EAP_PSK_FLAGS_GET_T(hdr3->flags));
+ ret->methodState = METHOD_DONE;
+ ret->decision = DECISION_FAIL;
+ return NULL;
+ }
+ wpa_hexdump(MSG_DEBUG, "EAP-PSK: RAND_S", hdr3->rand_s,
+ EAP_PSK_RAND_LEN);
+ wpa_hexdump(MSG_DEBUG, "EAP-PSK: MAC_S", hdr3->mac_s, EAP_PSK_MAC_LEN);
+ wpa_hexdump(MSG_DEBUG, "EAP-PSK: PCHANNEL", pchannel, left);
+
+ if (left < 4 + 16 + 1) {
+ wpa_printf(MSG_INFO, "EAP-PSK: Too short PCHANNEL data in "
+ "third message (len=%lu, expected 21)",
+ (unsigned long) left);
+ ret->ignore = TRUE;
+ return NULL;
+ }
+
+ /* MAC_S = OMAC1-AES-128(AK, ID_S||RAND_P) */
+ buflen = data->id_s_len + EAP_PSK_RAND_LEN;
+ buf = os_malloc(buflen);
+ if (buf == NULL)
+ return NULL;
+ os_memcpy(buf, data->id_s, data->id_s_len);
+ os_memcpy(buf + data->id_s_len, data->rand_p, EAP_PSK_RAND_LEN);
+ if (omac1_aes_128(data->ak, buf, buflen, mac)) {
+ os_free(buf);
+ return NULL;
+ }
+ os_free(buf);
+ if (os_memcmp_const(mac, hdr3->mac_s, EAP_PSK_MAC_LEN) != 0) {
+ wpa_printf(MSG_WARNING, "EAP-PSK: Invalid MAC_S in third "
+ "message");
+ ret->methodState = METHOD_DONE;
+ ret->decision = DECISION_FAIL;
+ return NULL;
+ }
+ wpa_printf(MSG_DEBUG, "EAP-PSK: MAC_S verified successfully");
+
+ if (eap_psk_derive_keys(data->kdk, data->rand_p, data->tek,
+ data->msk, data->emsk)) {
+ ret->methodState = METHOD_DONE;
+ ret->decision = DECISION_FAIL;
+ return NULL;
+ }
+ wpa_hexdump_key(MSG_DEBUG, "EAP-PSK: TEK", data->tek, EAP_PSK_TEK_LEN);
+ wpa_hexdump_key(MSG_DEBUG, "EAP-PSK: MSK", data->msk, EAP_MSK_LEN);
+ wpa_hexdump_key(MSG_DEBUG, "EAP-PSK: EMSK", data->emsk, EAP_EMSK_LEN);
+
+ os_memset(nonce, 0, 12);
+ os_memcpy(nonce + 12, pchannel, 4);
+ pchannel += 4;
+ left -= 4;
+
+ tag = pchannel;
+ pchannel += 16;
+ left -= 16;
+
+ msg = pchannel;
+
+ wpa_hexdump(MSG_MSGDUMP, "EAP-PSK: PCHANNEL - nonce",
+ nonce, sizeof(nonce));
+ wpa_hexdump(MSG_MSGDUMP, "EAP-PSK: PCHANNEL - hdr",
+ wpabuf_head(reqData), 5);
+ wpa_hexdump(MSG_MSGDUMP, "EAP-PSK: PCHANNEL - cipher msg", msg, left);
+
+ decrypted = os_malloc(left);
+ if (decrypted == NULL) {
+ ret->methodState = METHOD_DONE;
+ ret->decision = DECISION_FAIL;
+ return NULL;
+ }
+ os_memcpy(decrypted, msg, left);
+
+ if (aes_128_eax_decrypt(data->tek, nonce, sizeof(nonce),
+ wpabuf_head(reqData),
+ sizeof(struct eap_hdr) + 1 +
+ sizeof(*hdr3) - EAP_PSK_MAC_LEN, decrypted,
+ left, tag)) {
+ wpa_printf(MSG_WARNING, "EAP-PSK: PCHANNEL decryption failed");
+ os_free(decrypted);
+ return NULL;
+ }
+ wpa_hexdump(MSG_DEBUG, "EAP-PSK: Decrypted PCHANNEL message",
+ decrypted, left);
+
+ /* Verify R flag */
+ switch (decrypted[0] >> 6) {
+ case EAP_PSK_R_FLAG_CONT:
+ wpa_printf(MSG_DEBUG, "EAP-PSK: R flag - CONT - unsupported");
+ failed = 1;
+ break;
+ case EAP_PSK_R_FLAG_DONE_SUCCESS:
+ wpa_printf(MSG_DEBUG, "EAP-PSK: R flag - DONE_SUCCESS");
+ break;
+ case EAP_PSK_R_FLAG_DONE_FAILURE:
+ wpa_printf(MSG_DEBUG, "EAP-PSK: R flag - DONE_FAILURE");
+ wpa_printf(MSG_INFO, "EAP-PSK: Authentication server rejected "
+ "authentication");
+ failed = 1;
+ break;
+ }
+
+ data_len = 1;
+ if ((decrypted[0] & EAP_PSK_E_FLAG) && left > 1)
+ data_len++;
+ plen = sizeof(*hdr4) + 4 + 16 + data_len;
+ resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_PSK, plen,
+ EAP_CODE_RESPONSE, eap_get_id(reqData));
+ if (resp == NULL) {
+ os_free(decrypted);
+ return NULL;
+ }
+ hdr4 = wpabuf_put(resp, sizeof(*hdr4));
+ hdr4->flags = EAP_PSK_FLAGS_SET_T(3); /* T=3 */
+ os_memcpy(hdr4->rand_s, hdr3->rand_s, EAP_PSK_RAND_LEN);
+ rpchannel = wpabuf_put(resp, 4 + 16 + data_len);
+
+ /* nonce++ */
+ inc_byte_array(nonce, sizeof(nonce));
+ os_memcpy(rpchannel, nonce + 12, 4);
+
+ if (decrypted[0] & EAP_PSK_E_FLAG) {
+ wpa_printf(MSG_DEBUG, "EAP-PSK: Unsupported E (Ext) flag");
+ failed = 1;
+ rpchannel[4 + 16] = (EAP_PSK_R_FLAG_DONE_FAILURE << 6) |
+ EAP_PSK_E_FLAG;
+ if (left > 1) {
+ /* Add empty EXT_Payload with same EXT_Type */
+ rpchannel[4 + 16 + 1] = decrypted[1];
+ }
+ } else if (failed)
+ rpchannel[4 + 16] = EAP_PSK_R_FLAG_DONE_FAILURE << 6;
+ else
+ rpchannel[4 + 16] = EAP_PSK_R_FLAG_DONE_SUCCESS << 6;
+
+ wpa_hexdump(MSG_DEBUG, "EAP-PSK: reply message (plaintext)",
+ rpchannel + 4 + 16, data_len);
+ if (aes_128_eax_encrypt(data->tek, nonce, sizeof(nonce),
+ wpabuf_head(resp),
+ sizeof(struct eap_hdr) + 1 + sizeof(*hdr4),
+ rpchannel + 4 + 16, data_len, rpchannel + 4)) {
+ os_free(decrypted);
+ wpabuf_free(resp);
+ return NULL;
+ }
+ wpa_hexdump(MSG_DEBUG, "EAP-PSK: reply message (PCHANNEL)",
+ rpchannel, 4 + 16 + data_len);
+
+ wpa_printf(MSG_DEBUG, "EAP-PSK: Completed %ssuccessfully",
+ failed ? "un" : "");
+ data->state = PSK_DONE;
+ ret->methodState = METHOD_DONE;
+ ret->decision = failed ? DECISION_FAIL : DECISION_UNCOND_SUCC;
+
+ os_free(decrypted);
+
+ return resp;
+}
+
+
+static struct wpabuf * eap_psk_process(struct eap_sm *sm, void *priv,
+ struct eap_method_ret *ret,
+ const struct wpabuf *reqData)
+{
+ struct eap_psk_data *data = priv;
+ const u8 *pos;
+ struct wpabuf *resp = NULL;
+ size_t len;
+
+ pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_PSK, reqData, &len);
+ if (pos == NULL) {
+ ret->ignore = TRUE;
+ return NULL;
+ }
+
+ ret->ignore = FALSE;
+ ret->methodState = METHOD_MAY_CONT;
+ ret->decision = DECISION_FAIL;
+ ret->allowNotifications = TRUE;
+
+ switch (data->state) {
+ case PSK_INIT:
+ resp = eap_psk_process_1(data, ret, reqData);
+ break;
+ case PSK_MAC_SENT:
+ resp = eap_psk_process_3(data, ret, reqData);
+ break;
+ case PSK_DONE:
+ wpa_printf(MSG_DEBUG, "EAP-PSK: in DONE state - ignore "
+ "unexpected message");
+ ret->ignore = TRUE;
+ return NULL;
+ }
+
+ if (ret->methodState == METHOD_DONE) {
+ ret->allowNotifications = FALSE;
+ }
+
+ return resp;
+}
+
+
+static Boolean eap_psk_isKeyAvailable(struct eap_sm *sm, void *priv)
+{
+ struct eap_psk_data *data = priv;
+ return data->state == PSK_DONE;
+}
+
+
+static u8 * eap_psk_getKey(struct eap_sm *sm, void *priv, size_t *len)
+{
+ struct eap_psk_data *data = priv;
+ u8 *key;
+
+ if (data->state != PSK_DONE)
+ return NULL;
+
+ key = os_malloc(EAP_MSK_LEN);
+ if (key == NULL)
+ return NULL;
+
+ *len = EAP_MSK_LEN;
+ os_memcpy(key, data->msk, EAP_MSK_LEN);
+
+ return key;
+}
+
+
+static u8 * eap_psk_get_session_id(struct eap_sm *sm, void *priv, size_t *len)
+{
+ struct eap_psk_data *data = priv;
+ u8 *id;
+
+ if (data->state != PSK_DONE)
+ return NULL;
+
+ *len = 1 + 2 * EAP_PSK_RAND_LEN;
+ id = os_malloc(*len);
+ if (id == NULL)
+ return NULL;
+
+ id[0] = EAP_TYPE_PSK;
+ os_memcpy(id + 1, data->rand_p, EAP_PSK_RAND_LEN);
+ os_memcpy(id + 1 + EAP_PSK_RAND_LEN, data->rand_s, EAP_PSK_RAND_LEN);
+ wpa_hexdump(MSG_DEBUG, "EAP-PSK: Derived Session-Id", id, *len);
+
+ return id;
+}
+
+
+static u8 * eap_psk_get_emsk(struct eap_sm *sm, void *priv, size_t *len)
+{
+ struct eap_psk_data *data = priv;
+ u8 *key;
+
+ if (data->state != PSK_DONE)
+ return NULL;
+
+ key = os_malloc(EAP_EMSK_LEN);
+ if (key == NULL)
+ return NULL;
+
+ *len = EAP_EMSK_LEN;
+ os_memcpy(key, data->emsk, EAP_EMSK_LEN);
+
+ return key;
+}
+
+
+int eap_peer_psk_register(void)
+{
+ struct eap_method *eap;
+ int ret;
+
+ eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION,
+ EAP_VENDOR_IETF, EAP_TYPE_PSK, "PSK");
+ if (eap == NULL)
+ return -1;
+
+ eap->init = eap_psk_init;
+ eap->deinit = eap_psk_deinit;
+ eap->process = eap_psk_process;
+ eap->isKeyAvailable = eap_psk_isKeyAvailable;
+ eap->getKey = eap_psk_getKey;
+ eap->getSessionId = eap_psk_get_session_id;
+ eap->get_emsk = eap_psk_get_emsk;
+
+ ret = eap_peer_method_register(eap);
+ if (ret)
+ eap_peer_method_free(eap);
+ return ret;
+}
diff --git a/freebsd/contrib/wpa/src/eap_peer/eap_tls.c b/freebsd/contrib/wpa/src/eap_peer/eap_tls.c
new file mode 100644
index 00000000..8ca6db9c
--- /dev/null
+++ b/freebsd/contrib/wpa/src/eap_peer/eap_tls.c
@@ -0,0 +1,441 @@
+#include <machine/rtems-bsd-user-space.h>
+
+/*
+ * 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.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "crypto/tls.h"
+#include "eap_i.h"
+#include "eap_tls_common.h"
+#include "eap_config.h"
+
+
+static void eap_tls_deinit(struct eap_sm *sm, void *priv);
+
+
+struct eap_tls_data {
+ struct eap_ssl_data ssl;
+ u8 *key_data;
+ u8 *session_id;
+ size_t id_len;
+ void *ssl_ctx;
+ u8 eap_type;
+};
+
+
+static void * eap_tls_init(struct eap_sm *sm)
+{
+ struct eap_tls_data *data;
+ struct eap_peer_config *config = eap_get_config(sm);
+ if (config == NULL ||
+ ((sm->init_phase2 ? config->private_key2 : config->private_key)
+ == NULL &&
+ (sm->init_phase2 ? config->engine2 : config->engine) == 0)) {
+ wpa_printf(MSG_INFO, "EAP-TLS: Private key not configured");
+ return NULL;
+ }
+
+ data = os_zalloc(sizeof(*data));
+ if (data == NULL)
+ return NULL;
+
+ data->ssl_ctx = sm->init_phase2 && sm->ssl_ctx2 ? sm->ssl_ctx2 :
+ 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);
+ if (config->engine) {
+ wpa_printf(MSG_DEBUG, "EAP-TLS: Requesting Smartcard "
+ "PIN");
+ eap_sm_request_pin(sm);
+ sm->ignore = TRUE;
+ } else if (config->private_key && !config->private_key_passwd)
+ {
+ wpa_printf(MSG_DEBUG, "EAP-TLS: Requesting private "
+ "key passphrase");
+ eap_sm_request_passphrase(sm);
+ sm->ignore = TRUE;
+ }
+ return NULL;
+ }
+
+ data->eap_type = EAP_TYPE_TLS;
+
+ return data;
+}
+
+
+#ifdef EAP_UNAUTH_TLS
+static void * eap_unauth_tls_init(struct eap_sm *sm)
+{
+ struct eap_tls_data *data;
+ struct eap_peer_config *config = eap_get_config(sm);
+
+ data = os_zalloc(sizeof(*data));
+ if (data == NULL)
+ return NULL;
+
+ data->ssl_ctx = sm->init_phase2 && sm->ssl_ctx2 ? sm->ssl_ctx2 :
+ sm->ssl_ctx;
+
+ if (eap_peer_tls_ssl_init(sm, &data->ssl, config,
+ EAP_UNAUTH_TLS_TYPE)) {
+ wpa_printf(MSG_INFO, "EAP-TLS: Failed to initialize SSL.");
+ eap_tls_deinit(sm, data);
+ return NULL;
+ }
+
+ data->eap_type = EAP_UNAUTH_TLS_TYPE;
+
+ return data;
+}
+#endif /* EAP_UNAUTH_TLS */
+
+
+#ifdef CONFIG_HS20
+static void * eap_wfa_unauth_tls_init(struct eap_sm *sm)
+{
+ struct eap_tls_data *data;
+ struct eap_peer_config *config = eap_get_config(sm);
+
+ data = os_zalloc(sizeof(*data));
+ if (data == NULL)
+ return NULL;
+
+ data->ssl_ctx = sm->init_phase2 && sm->ssl_ctx2 ? sm->ssl_ctx2 :
+ sm->ssl_ctx;
+
+ if (eap_peer_tls_ssl_init(sm, &data->ssl, config,
+ EAP_WFA_UNAUTH_TLS_TYPE)) {
+ wpa_printf(MSG_INFO, "EAP-TLS: Failed to initialize SSL.");
+ eap_tls_deinit(sm, data);
+ return NULL;
+ }
+
+ data->eap_type = EAP_WFA_UNAUTH_TLS_TYPE;
+
+ return data;
+}
+#endif /* CONFIG_HS20 */
+
+
+static void eap_tls_free_key(struct eap_tls_data *data)
+{
+ if (data->key_data) {
+ bin_clear_free(data->key_data, EAP_TLS_KEY_LEN + EAP_EMSK_LEN);
+ data->key_data = NULL;
+ }
+}
+
+
+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);
+ eap_tls_free_key(data);
+ os_free(data->session_id);
+ os_free(data);
+}
+
+
+static struct wpabuf * eap_tls_failure(struct eap_sm *sm,
+ struct eap_tls_data *data,
+ struct eap_method_ret *ret, int res,
+ struct wpabuf *resp, u8 id)
+{
+ wpa_printf(MSG_DEBUG, "EAP-TLS: TLS processing failed");
+
+ ret->methodState = METHOD_DONE;
+ ret->decision = DECISION_FAIL;
+
+ if (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;
+
+ eap_tls_free_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;
+ struct wpabuf msg;
+
+ 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;
+ wpabuf_set(&msg, pos, left);
+ res = eap_peer_tls_process_helper(sm, &data->ssl, data->eap_type, 0,
+ id, &msg, &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 Boolean eap_tls_has_reauth_data(struct eap_sm *sm, void *priv)
+{
+ struct eap_tls_data *data = priv;
+ return tls_connection_established(data->ssl_ctx, data->ssl.conn);
+}
+
+
+static void eap_tls_deinit_for_reauth(struct eap_sm *sm, void *priv)
+{
+}
+
+
+static void * eap_tls_init_for_reauth(struct eap_sm *sm, void *priv)
+{
+ struct eap_tls_data *data = priv;
+ eap_tls_free_key(data);
+ os_free(data->session_id);
+ data->session_id = NULL;
+ if (eap_peer_tls_reauth_init(sm, &data->ssl)) {
+ os_free(data);
+ return NULL;
+ }
+ return priv;
+}
+
+
+static int eap_tls_get_status(struct eap_sm *sm, void *priv, char *buf,
+ size_t buflen, int verbose)
+{
+ struct eap_tls_data *data = priv;
+ return eap_peer_tls_status(sm, &data->ssl, buf, buflen, verbose);
+}
+
+
+static Boolean eap_tls_isKeyAvailable(struct eap_sm *sm, void *priv)
+{
+ struct eap_tls_data *data = priv;
+ return data->key_data != NULL;
+}
+
+
+static u8 * eap_tls_getKey(struct eap_sm *sm, void *priv, size_t *len)
+{
+ struct eap_tls_data *data = priv;
+ u8 *key;
+
+ if (data->key_data == NULL)
+ return NULL;
+
+ key = os_malloc(EAP_TLS_KEY_LEN);
+ if (key == NULL)
+ return NULL;
+
+ *len = EAP_TLS_KEY_LEN;
+ os_memcpy(key, data->key_data, EAP_TLS_KEY_LEN);
+
+ return key;
+}
+
+
+static u8 * eap_tls_get_emsk(struct eap_sm *sm, void *priv, size_t *len)
+{
+ struct eap_tls_data *data = priv;
+ u8 *key;
+
+ if (data->key_data == NULL)
+ return NULL;
+
+ key = os_malloc(EAP_EMSK_LEN);
+ if (key == NULL)
+ return NULL;
+
+ *len = EAP_EMSK_LEN;
+ os_memcpy(key, data->key_data + EAP_TLS_KEY_LEN, EAP_EMSK_LEN);
+
+ return key;
+}
+
+
+static u8 * eap_tls_get_session_id(struct eap_sm *sm, void *priv, size_t *len)
+{
+ struct eap_tls_data *data = priv;
+ u8 *id;
+
+ if (data->session_id == NULL)
+ return NULL;
+
+ id = os_malloc(data->id_len);
+ if (id == NULL)
+ return NULL;
+
+ *len = data->id_len;
+ os_memcpy(id, data->session_id, data->id_len);
+
+ return id;
+}
+
+
+int eap_peer_tls_register(void)
+{
+ struct eap_method *eap;
+ int ret;
+
+ eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION,
+ EAP_VENDOR_IETF, EAP_TYPE_TLS, "TLS");
+ if (eap == NULL)
+ return -1;
+
+ eap->init = eap_tls_init;
+ eap->deinit = eap_tls_deinit;
+ eap->process = eap_tls_process;
+ eap->isKeyAvailable = eap_tls_isKeyAvailable;
+ eap->getKey = eap_tls_getKey;
+ eap->getSessionId = eap_tls_get_session_id;
+ eap->get_status = eap_tls_get_status;
+ eap->has_reauth_data = eap_tls_has_reauth_data;
+ eap->deinit_for_reauth = eap_tls_deinit_for_reauth;
+ eap->init_for_reauth = eap_tls_init_for_reauth;
+ eap->get_emsk = eap_tls_get_emsk;
+
+ ret = eap_peer_method_register(eap);
+ if (ret)
+ eap_peer_method_free(eap);
+ return ret;
+}
+
+
+#ifdef EAP_UNAUTH_TLS
+int eap_peer_unauth_tls_register(void)
+{
+ struct eap_method *eap;
+ int ret;
+
+ eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION,
+ EAP_VENDOR_UNAUTH_TLS,
+ EAP_VENDOR_TYPE_UNAUTH_TLS, "UNAUTH-TLS");
+ if (eap == NULL)
+ return -1;
+
+ eap->init = eap_unauth_tls_init;
+ eap->deinit = eap_tls_deinit;
+ eap->process = eap_tls_process;
+ eap->isKeyAvailable = eap_tls_isKeyAvailable;
+ eap->getKey = eap_tls_getKey;
+ eap->get_status = eap_tls_get_status;
+ eap->has_reauth_data = eap_tls_has_reauth_data;
+ eap->deinit_for_reauth = eap_tls_deinit_for_reauth;
+ eap->init_for_reauth = eap_tls_init_for_reauth;
+ eap->get_emsk = eap_tls_get_emsk;
+
+ ret = eap_peer_method_register(eap);
+ if (ret)
+ eap_peer_method_free(eap);
+ return ret;
+}
+#endif /* EAP_UNAUTH_TLS */
+
+
+#ifdef CONFIG_HS20
+int eap_peer_wfa_unauth_tls_register(void)
+{
+ struct eap_method *eap;
+ int ret;
+
+ eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION,
+ EAP_VENDOR_WFA_NEW,
+ EAP_VENDOR_WFA_UNAUTH_TLS,
+ "WFA-UNAUTH-TLS");
+ if (eap == NULL)
+ return -1;
+
+ eap->init = eap_wfa_unauth_tls_init;
+ eap->deinit = eap_tls_deinit;
+ eap->process = eap_tls_process;
+ eap->isKeyAvailable = eap_tls_isKeyAvailable;
+ eap->getKey = eap_tls_getKey;
+ eap->get_status = eap_tls_get_status;
+ eap->has_reauth_data = eap_tls_has_reauth_data;
+ eap->deinit_for_reauth = eap_tls_deinit_for_reauth;
+ eap->init_for_reauth = eap_tls_init_for_reauth;
+ eap->get_emsk = eap_tls_get_emsk;
+
+ ret = eap_peer_method_register(eap);
+ if (ret)
+ eap_peer_method_free(eap);
+ return ret;
+}
+#endif /* CONFIG_HS20 */
diff --git a/freebsd/contrib/wpa/src/eap_peer/eap_tls_common.c b/freebsd/contrib/wpa/src/eap_peer/eap_tls_common.c
new file mode 100644
index 00000000..ff6c937c
--- /dev/null
+++ b/freebsd/contrib/wpa/src/eap_peer/eap_tls_common.c
@@ -0,0 +1,1110 @@
+#include <machine/rtems-bsd-user-space.h>
+
+/*
+ * 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 "includes.h"
+
+#include "common.h"
+#include "crypto/sha1.h"
+#include "crypto/tls.h"
+#include "eap_i.h"
+#include "eap_tls_common.h"
+#include "eap_config.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);
+ if (type == EAP_WFA_UNAUTH_TLS_TYPE)
+ return eap_msg_alloc(EAP_VENDOR_WFA_NEW,
+ EAP_VENDOR_WFA_UNAUTH_TLS, payload_len,
+ code, identifier);
+ return eap_msg_alloc(EAP_VENDOR_IETF, type, payload_len, code,
+ identifier);
+}
+
+
+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;
+ if (os_strstr(txt, "tls_disable_tlsv1_0=1"))
+ params->flags |= TLS_CONN_DISABLE_TLSv1_0;
+ if (os_strstr(txt, "tls_disable_tlsv1_0=0"))
+ params->flags &= ~TLS_CONN_DISABLE_TLSv1_0;
+ if (os_strstr(txt, "tls_disable_tlsv1_1=1"))
+ params->flags |= TLS_CONN_DISABLE_TLSv1_1;
+ if (os_strstr(txt, "tls_disable_tlsv1_1=0"))
+ params->flags &= ~TLS_CONN_DISABLE_TLSv1_1;
+ if (os_strstr(txt, "tls_disable_tlsv1_2=1"))
+ params->flags |= TLS_CONN_DISABLE_TLSv1_2;
+ if (os_strstr(txt, "tls_disable_tlsv1_2=0"))
+ params->flags &= ~TLS_CONN_DISABLE_TLSv1_2;
+}
+
+
+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;
+ params->dh_file = (char *) config->dh_file;
+ params->subject_match = (char *) config->subject_match;
+ params->altsubject_match = (char *) config->altsubject_match;
+ params->suffix_match = config->domain_suffix_match;
+ params->domain_match = config->domain_match;
+ params->engine = config->engine;
+ params->engine_id = config->engine_id;
+ params->pin = config->pin;
+ params->key_id = config->key_id;
+ params->cert_id = config->cert_id;
+ params->ca_cert_id = config->ca_cert_id;
+ eap_tls_params_flags(params, config->phase1);
+}
+
+
+static void eap_tls_params_from_conf2(struct tls_connection_params *params,
+ struct eap_peer_config *config)
+{
+ params->ca_cert = (char *) config->ca_cert2;
+ params->ca_path = (char *) config->ca_path2;
+ params->client_cert = (char *) config->client_cert2;
+ params->private_key = (char *) config->private_key2;
+ params->private_key_passwd = (char *) config->private_key2_passwd;
+ params->dh_file = (char *) config->dh_file2;
+ params->subject_match = (char *) config->subject_match2;
+ params->altsubject_match = (char *) config->altsubject_match2;
+ params->suffix_match = config->domain_suffix_match2;
+ params->domain_match = config->domain_match2;
+ params->engine = config->engine2;
+ params->engine_id = config->engine2_id;
+ params->pin = config->pin2;
+ params->key_id = config->key2_id;
+ params->cert_id = config->cert2_id;
+ params->ca_cert_id = config->ca_cert2_id;
+ eap_tls_params_flags(params, config->phase2);
+}
+
+
+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, int phase2)
+{
+ 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;
+ }
+ if (phase2) {
+ wpa_printf(MSG_DEBUG, "TLS: using phase2 config options");
+ eap_tls_params_from_conf2(params, config);
+ } else {
+ wpa_printf(MSG_DEBUG, "TLS: using phase1 config options");
+ eap_tls_params_from_conf1(params, config);
+ if (data->eap_type == EAP_TYPE_FAST)
+ params->flags |= TLS_CONN_EAP_FAST;
+ }
+
+ /*
+ * Use blob data, if available. Otherwise, leave reference to external
+ * file as-is.
+ */
+ if (eap_tls_check_blob(sm, &params->ca_cert, &params->ca_cert_blob,
+ &params->ca_cert_blob_len) ||
+ eap_tls_check_blob(sm, &params->client_cert,
+ &params->client_cert_blob,
+ &params->client_cert_blob_len) ||
+ eap_tls_check_blob(sm, &params->private_key,
+ &params->private_key_blob,
+ &params->private_key_blob_len) ||
+ eap_tls_check_blob(sm, &params->dh_file, &params->dh_blob,
+ &params->dh_blob_len)) {
+ wpa_printf(MSG_INFO, "SSL: Failed to get configuration blobs");
+ return -1;
+ }
+
+ params->openssl_ciphers = config->openssl_ciphers;
+
+ 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_BAD_PIN) {
+ /*
+ * At this point with the pkcs11 engine the PIN is 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.
+ */
+ wpa_printf(MSG_INFO,
+ "TLS: Bad PIN provided, requesting a new one");
+ os_free(config->pin);
+ config->pin = NULL;
+ eap_sm_request_pin(sm);
+ sm->ignore = TRUE;
+ } else if (res == TLS_SET_PARAMS_ENGINE_PRV_INIT_FAILED) {
+ wpa_printf(MSG_INFO, "TLS: Failed to initialize engine");
+ } else if (res == TLS_SET_PARAMS_ENGINE_PRV_VERIFY_FAILED) {
+ wpa_printf(MSG_INFO, "TLS: Failed to load private key");
+ sm->ignore = TRUE;
+ }
+ 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->phase2 = sm->init_phase2;
+ data->ssl_ctx = sm->init_phase2 && sm->ssl_ctx2 ? sm->ssl_ctx2 :
+ sm->ssl_ctx;
+ if (eap_tls_params_from_conf(sm, data, &params, config, data->phase2) <
+ 0)
+ return -1;
+
+ if (eap_tls_init_connection(sm, data, config, &params) < 0)
+ return -1;
+
+ data->tls_out_limit = config->fragment_size;
+ if (data->phase2) {
+ /* Limit the fragment size in the inner TLS authentication
+ * since the outer authentication with EAP-PEAP does not yet
+ * support fragmentation */
+ if (data->tls_out_limit > 100)
+ data->tls_out_limit -= 100;
+ }
+
+ if (config->phase1 &&
+ os_strstr(config->phase1, "include_tls_length=1")) {
+ wpa_printf(MSG_DEBUG, "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)
+{
+ u8 *out;
+
+ out = os_malloc(len);
+ if (out == NULL)
+ return NULL;
+
+ if (tls_connection_prf(data->ssl_ctx, data->conn, label, 0, 0,
+ out, len)) {
+ os_free(out);
+ return NULL;
+ }
+
+ return out;
+}
+
+
+/**
+ * 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_random keys;
+ u8 *out;
+
+ if (tls_connection_get_random(sm->ssl_ctx, data->conn, &keys))
+ return NULL;
+
+ if (keys.client_random == NULL || keys.server_random == NULL)
+ return NULL;
+
+ *len = 1 + keys.client_random_len + keys.server_random_len;
+ out = os_malloc(*len);
+ if (out == NULL)
+ return NULL;
+
+ /* Session-Id = EAP type || client.random || server.random */
+ out[0] = eap_type;
+ os_memcpy(out + 1, keys.client_random, keys.client_random_len);
+ os_memcpy(out + 1 + keys.client_random_len, keys.server_random,
+ keys.server_random_len);
+
+ return out;
+}
+
+
+/**
+ * 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_DEBUG, "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
+ * @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 struct wpabuf *in_data,
+ struct wpabuf **out_data)
+{
+ const struct wpabuf *msg;
+ int need_more_input;
+ struct wpabuf *appl_data;
+
+ msg = eap_peer_tls_data_reassemble(data, in_data, &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)
+ 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
+ * @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 struct wpabuf *in_data,
+ struct wpabuf **out_data)
+{
+ int ret = 0;
+
+ *out_data = NULL;
+
+ if (data->tls_out && wpabuf_len(data->tls_out) > 0 &&
+ wpabuf_len(in_data) > 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, 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 (len=%u)",
+ (unsigned int) wpabuf_len(data->tls_out));
+ ret = -1;
+ /* TODO: clean pin if engine used? */
+ if (wpabuf_len(data->tls_out) == 0) {
+ wpabuf_free(data->tls_out);
+ data->tls_out = NULL;
+ return -1;
+ }
+ }
+
+ if (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)",
+ (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 version[20], name[128];
+ int len = 0, ret;
+
+ if (tls_get_version(data->ssl_ctx, data->conn, version,
+ sizeof(version)) < 0)
+ version[0] = '\0';
+ if (tls_get_cipher(data->ssl_ctx, data->conn, name, sizeof(name)) < 0)
+ name[0] = '\0';
+
+ ret = os_snprintf(buf + len, buflen - len,
+ "eap_tls_version=%s\n"
+ "EAP TLS cipher=%s\n"
+ "tls_session_reused=%d\n",
+ version, name,
+ tls_connection_resumed(data->ssl_ctx, data->conn));
+ if (os_snprintf_error(buflen - len, ret))
+ 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 if (eap_type == EAP_WFA_UNAUTH_TLS_TYPE)
+ pos = eap_hdr_validate(EAP_VENDOR_WFA_NEW,
+ EAP_VENDOR_WFA_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;
+ u32 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 = 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_ERROR, "TLS: Unsupported Phase2 EAP "
+ "method '%s'", start);
+ } else {
+ num_methods++;
+ _methods = os_realloc_array(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 Phase2 EAP methods available");
+ 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 2 Request: Nak type=%d", *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;
+}
diff --git a/freebsd/contrib/wpa/src/eap_peer/eap_tls_common.h b/freebsd/contrib/wpa/src/eap_peer/eap_tls_common.h
new file mode 100644
index 00000000..acd2b783
--- /dev/null
+++ b/freebsd/contrib/wpa/src/eap_peer/eap_tls_common.h
@@ -0,0 +1,132 @@
+/*
+ * EAP peer: EAP-TLS/PEAP/TTLS/FAST common functions
+ * Copyright (c) 2004-2009, 2012, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef EAP_TLS_COMMON_H
+#define EAP_TLS_COMMON_H
+
+/**
+ * struct eap_ssl_data - TLS data for EAP methods
+ */
+struct eap_ssl_data {
+ /**
+ * conn - TLS connection context data from tls_connection_init()
+ */
+ struct tls_connection *conn;
+
+ /**
+ * tls_out - TLS message to be sent out in fragments
+ */
+ struct wpabuf *tls_out;
+
+ /**
+ * tls_out_pos - The current position in the outgoing TLS message
+ */
+ size_t tls_out_pos;
+
+ /**
+ * tls_out_limit - Maximum fragment size for outgoing TLS messages
+ */
+ size_t tls_out_limit;
+
+ /**
+ * tls_in - Received TLS message buffer for re-assembly
+ */
+ struct wpabuf *tls_in;
+
+ /**
+ * tls_in_left - Number of remaining bytes in the incoming TLS message
+ */
+ size_t tls_in_left;
+
+ /**
+ * tls_in_total - Total number of bytes in the incoming TLS message
+ */
+ size_t tls_in_total;
+
+ /**
+ * phase2 - Whether this TLS connection is used in EAP phase 2 (tunnel)
+ */
+ int phase2;
+
+ /**
+ * include_tls_length - Whether the TLS length field is included even
+ * if the TLS data is not fragmented
+ */
+ int include_tls_length;
+
+ /**
+ * eap - EAP state machine allocated with eap_peer_sm_init()
+ */
+ struct eap_sm *eap;
+
+ /**
+ * ssl_ctx - TLS library context to use for the connection
+ */
+ void *ssl_ctx;
+
+ /**
+ * eap_type - EAP method used in Phase 1 (EAP_TYPE_TLS/PEAP/TTLS/FAST)
+ */
+ u8 eap_type;
+};
+
+
+/* EAP TLS Flags */
+#define EAP_TLS_FLAGS_LENGTH_INCLUDED 0x80
+#define EAP_TLS_FLAGS_MORE_FRAGMENTS 0x40
+#define EAP_TLS_FLAGS_START 0x20
+#define EAP_TLS_VERSION_MASK 0x07
+
+ /* could be up to 128 bytes, but only the first 64 bytes are used */
+#define EAP_TLS_KEY_LEN 64
+
+/* dummy type used as a flag for UNAUTH-TLS */
+#define EAP_UNAUTH_TLS_TYPE 255
+#define EAP_WFA_UNAUTH_TLS_TYPE 254
+
+
+int eap_peer_tls_ssl_init(struct eap_sm *sm, struct eap_ssl_data *data,
+ struct eap_peer_config *config, u8 eap_type);
+void eap_peer_tls_ssl_deinit(struct eap_sm *sm, struct eap_ssl_data *data);
+u8 * eap_peer_tls_derive_key(struct eap_sm *sm, struct eap_ssl_data *data,
+ const char *label, size_t len);
+u8 * eap_peer_tls_derive_session_id(struct eap_sm *sm,
+ struct eap_ssl_data *data, u8 eap_type,
+ size_t *len);
+int eap_peer_tls_process_helper(struct eap_sm *sm, struct eap_ssl_data *data,
+ EapType eap_type, int peap_version,
+ u8 id, const struct wpabuf *in_data,
+ struct wpabuf **out_data);
+struct wpabuf * eap_peer_tls_build_ack(u8 id, EapType eap_type,
+ int peap_version);
+int eap_peer_tls_reauth_init(struct eap_sm *sm, struct eap_ssl_data *data);
+int eap_peer_tls_status(struct eap_sm *sm, struct eap_ssl_data *data,
+ char *buf, size_t buflen, int verbose);
+const u8 * eap_peer_tls_process_init(struct eap_sm *sm,
+ struct eap_ssl_data *data,
+ EapType eap_type,
+ struct eap_method_ret *ret,
+ const struct wpabuf *reqData,
+ size_t *len, u8 *flags);
+void eap_peer_tls_reset_input(struct eap_ssl_data *data);
+void eap_peer_tls_reset_output(struct eap_ssl_data *data);
+int eap_peer_tls_decrypt(struct eap_sm *sm, struct eap_ssl_data *data,
+ const struct wpabuf *in_data,
+ struct wpabuf **in_decrypted);
+int eap_peer_tls_encrypt(struct eap_sm *sm, struct eap_ssl_data *data,
+ EapType eap_type, int peap_version, u8 id,
+ const struct wpabuf *in_data,
+ struct wpabuf **out_data);
+int eap_peer_select_phase2_methods(struct eap_peer_config *config,
+ const char *prefix,
+ struct eap_method_type **types,
+ size_t *num_types);
+int eap_peer_tls_phase2_nak(struct eap_method_type *types, size_t num_types,
+ struct eap_hdr *hdr, struct wpabuf **resp);
+
+#endif /* EAP_TLS_COMMON_H */
diff --git a/freebsd/contrib/wpa/src/eap_peer/eap_ttls.c b/freebsd/contrib/wpa/src/eap_peer/eap_ttls.c
new file mode 100644
index 00000000..55788774
--- /dev/null
+++ b/freebsd/contrib/wpa/src/eap_peer/eap_ttls.c
@@ -0,0 +1,1723 @@
+#include <machine/rtems-bsd-user-space.h>
+
+/*
+ * EAP peer method: EAP-TTLS (RFC 5281)
+ * 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.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "crypto/ms_funcs.h"
+#include "crypto/sha1.h"
+#include "crypto/tls.h"
+#include "eap_common/chap.h"
+#include "eap_common/eap_ttls.h"
+#include "mschapv2.h"
+#include "eap_i.h"
+#include "eap_tls_common.h"
+#include "eap_config.h"
+
+
+#define EAP_TTLS_VERSION 0
+
+
+static void eap_ttls_deinit(struct eap_sm *sm, void *priv);
+
+
+struct eap_ttls_data {
+ struct eap_ssl_data ssl;
+
+ int ttls_version;
+
+ const struct eap_method *phase2_method;
+ void *phase2_priv;
+ int phase2_success;
+ int phase2_start;
+
+ enum phase2_types {
+ EAP_TTLS_PHASE2_EAP,
+ EAP_TTLS_PHASE2_MSCHAPV2,
+ EAP_TTLS_PHASE2_MSCHAP,
+ EAP_TTLS_PHASE2_PAP,
+ EAP_TTLS_PHASE2_CHAP
+ } phase2_type;
+ struct eap_method_type phase2_eap_type;
+ struct eap_method_type *phase2_eap_types;
+ size_t num_phase2_eap_types;
+
+ u8 auth_response[MSCHAPV2_AUTH_RESPONSE_LEN];
+ int auth_response_valid;
+ u8 master_key[MSCHAPV2_MASTER_KEY_LEN]; /* MSCHAPv2 master key */
+ u8 ident;
+ int resuming; /* starting a resumed session */
+ int reauth; /* reauthentication */
+ u8 *key_data;
+ u8 *session_id;
+ size_t id_len;
+
+ struct wpabuf *pending_phase2_req;
+
+#ifdef EAP_TNC
+ int ready_for_tnc;
+ int tnc_started;
+#endif /* EAP_TNC */
+};
+
+
+static void * eap_ttls_init(struct eap_sm *sm)
+{
+ struct eap_ttls_data *data;
+ struct eap_peer_config *config = eap_get_config(sm);
+ char *selected;
+
+ data = os_zalloc(sizeof(*data));
+ if (data == NULL)
+ return NULL;
+ data->ttls_version = EAP_TTLS_VERSION;
+ selected = "EAP";
+ data->phase2_type = EAP_TTLS_PHASE2_EAP;
+
+ if (config && config->phase2) {
+ if (os_strstr(config->phase2, "autheap=")) {
+ selected = "EAP";
+ data->phase2_type = EAP_TTLS_PHASE2_EAP;
+ } else if (os_strstr(config->phase2, "auth=MSCHAPV2")) {
+ selected = "MSCHAPV2";
+ data->phase2_type = EAP_TTLS_PHASE2_MSCHAPV2;
+ } else if (os_strstr(config->phase2, "auth=MSCHAP")) {
+ selected = "MSCHAP";
+ data->phase2_type = EAP_TTLS_PHASE2_MSCHAP;
+ } else if (os_strstr(config->phase2, "auth=PAP")) {
+ selected = "PAP";
+ data->phase2_type = EAP_TTLS_PHASE2_PAP;
+ } else if (os_strstr(config->phase2, "auth=CHAP")) {
+ selected = "CHAP";
+ data->phase2_type = EAP_TTLS_PHASE2_CHAP;
+ }
+ }
+ wpa_printf(MSG_DEBUG, "EAP-TTLS: Phase2 type: %s", selected);
+
+ if (data->phase2_type == EAP_TTLS_PHASE2_EAP) {
+ if (eap_peer_select_phase2_methods(config, "autheap=",
+ &data->phase2_eap_types,
+ &data->num_phase2_eap_types)
+ < 0) {
+ eap_ttls_deinit(sm, data);
+ return NULL;
+ }
+
+ data->phase2_eap_type.vendor = EAP_VENDOR_IETF;
+ data->phase2_eap_type.method = EAP_TYPE_NONE;
+ }
+
+ if (eap_peer_tls_ssl_init(sm, &data->ssl, config, EAP_TYPE_TTLS)) {
+ wpa_printf(MSG_INFO, "EAP-TTLS: Failed to initialize SSL.");
+ eap_ttls_deinit(sm, data);
+ return NULL;
+ }
+
+ return data;
+}
+
+
+static void eap_ttls_phase2_eap_deinit(struct eap_sm *sm,
+ struct eap_ttls_data *data)
+{
+ if (data->phase2_priv && data->phase2_method) {
+ data->phase2_method->deinit(sm, data->phase2_priv);
+ data->phase2_method = NULL;
+ data->phase2_priv = NULL;
+ }
+}
+
+
+static void eap_ttls_free_key(struct eap_ttls_data *data)
+{
+ if (data->key_data) {
+ bin_clear_free(data->key_data, EAP_TLS_KEY_LEN + EAP_EMSK_LEN);
+ data->key_data = NULL;
+ }
+}
+
+
+static void eap_ttls_deinit(struct eap_sm *sm, void *priv)
+{
+ struct eap_ttls_data *data = priv;
+ if (data == NULL)
+ return;
+ eap_ttls_phase2_eap_deinit(sm, data);
+ os_free(data->phase2_eap_types);
+ eap_peer_tls_ssl_deinit(sm, &data->ssl);
+ eap_ttls_free_key(data);
+ os_free(data->session_id);
+ wpabuf_free(data->pending_phase2_req);
+ os_free(data);
+}
+
+
+static u8 * eap_ttls_avp_hdr(u8 *avphdr, u32 avp_code, u32 vendor_id,
+ int mandatory, size_t len)
+{
+ struct ttls_avp_vendor *avp;
+ u8 flags;
+ size_t hdrlen;
+
+ avp = (struct ttls_avp_vendor *) avphdr;
+ flags = mandatory ? AVP_FLAGS_MANDATORY : 0;
+ if (vendor_id) {
+ flags |= AVP_FLAGS_VENDOR;
+ hdrlen = sizeof(*avp);
+ avp->vendor_id = host_to_be32(vendor_id);
+ } else {
+ hdrlen = sizeof(struct ttls_avp);
+ }
+
+ avp->avp_code = host_to_be32(avp_code);
+ avp->avp_length = host_to_be32(((u32) flags << 24) |
+ (u32) (hdrlen + len));
+
+ return avphdr + hdrlen;
+}
+
+
+static u8 * eap_ttls_avp_add(u8 *start, u8 *avphdr, u32 avp_code,
+ u32 vendor_id, int mandatory,
+ const u8 *data, size_t len)
+{
+ u8 *pos;
+ pos = eap_ttls_avp_hdr(avphdr, avp_code, vendor_id, mandatory, len);
+ os_memcpy(pos, data, len);
+ pos += len;
+ AVP_PAD(start, pos);
+ return pos;
+}
+
+
+static int eap_ttls_avp_encapsulate(struct wpabuf **resp, u32 avp_code,
+ int mandatory)
+{
+ struct wpabuf *msg;
+ u8 *avp, *pos;
+
+ msg = wpabuf_alloc(sizeof(struct ttls_avp) + wpabuf_len(*resp) + 4);
+ if (msg == NULL) {
+ wpabuf_free(*resp);
+ *resp = NULL;
+ return -1;
+ }
+
+ avp = wpabuf_mhead(msg);
+ pos = eap_ttls_avp_hdr(avp, avp_code, 0, mandatory, wpabuf_len(*resp));
+ os_memcpy(pos, wpabuf_head(*resp), wpabuf_len(*resp));
+ pos += wpabuf_len(*resp);
+ AVP_PAD(avp, pos);
+ wpabuf_free(*resp);
+ wpabuf_put(msg, pos - avp);
+ *resp = msg;
+ return 0;
+}
+
+
+static int eap_ttls_v0_derive_key(struct eap_sm *sm,
+ struct eap_ttls_data *data)
+{
+ eap_ttls_free_key(data);
+ data->key_data = eap_peer_tls_derive_key(sm, &data->ssl,
+ "ttls keying material",
+ EAP_TLS_KEY_LEN +
+ EAP_EMSK_LEN);
+ if (!data->key_data) {
+ wpa_printf(MSG_INFO, "EAP-TTLS: Failed to derive key");
+ return -1;
+ }
+
+ wpa_hexdump_key(MSG_DEBUG, "EAP-TTLS: Derived key",
+ data->key_data, EAP_TLS_KEY_LEN);
+ wpa_hexdump_key(MSG_DEBUG, "EAP-TTLS: Derived EMSK",
+ data->key_data + EAP_TLS_KEY_LEN,
+ EAP_EMSK_LEN);
+
+ os_free(data->session_id);
+ data->session_id = eap_peer_tls_derive_session_id(sm, &data->ssl,
+ EAP_TYPE_TTLS,
+ &data->id_len);
+ if (data->session_id) {
+ wpa_hexdump(MSG_DEBUG, "EAP-TTLS: Derived Session-Id",
+ data->session_id, data->id_len);
+ } else {
+ wpa_printf(MSG_ERROR, "EAP-TTLS: Failed to derive Session-Id");
+ }
+
+ return 0;
+}
+
+
+#ifndef CONFIG_FIPS
+static u8 * eap_ttls_implicit_challenge(struct eap_sm *sm,
+ struct eap_ttls_data *data, size_t len)
+{
+ return eap_peer_tls_derive_key(sm, &data->ssl, "ttls challenge", len);
+}
+#endif /* CONFIG_FIPS */
+
+
+static void eap_ttls_phase2_select_eap_method(struct eap_ttls_data *data,
+ u8 method)
+{
+ size_t i;
+ for (i = 0; i < data->num_phase2_eap_types; i++) {
+ if (data->phase2_eap_types[i].vendor != EAP_VENDOR_IETF ||
+ data->phase2_eap_types[i].method != method)
+ continue;
+
+ data->phase2_eap_type.vendor =
+ data->phase2_eap_types[i].vendor;
+ data->phase2_eap_type.method =
+ data->phase2_eap_types[i].method;
+ wpa_printf(MSG_DEBUG, "EAP-TTLS: Selected "
+ "Phase 2 EAP vendor %d method %d",
+ data->phase2_eap_type.vendor,
+ data->phase2_eap_type.method);
+ break;
+ }
+}
+
+
+static int eap_ttls_phase2_eap_process(struct eap_sm *sm,
+ struct eap_ttls_data *data,
+ struct eap_method_ret *ret,
+ struct eap_hdr *hdr, size_t len,
+ struct wpabuf **resp)
+{
+ struct wpabuf msg;
+ struct eap_method_ret iret;
+
+ os_memset(&iret, 0, sizeof(iret));
+ wpabuf_set(&msg, hdr, len);
+ *resp = data->phase2_method->process(sm, data->phase2_priv, &iret,
+ &msg);
+ if ((iret.methodState == METHOD_DONE ||
+ iret.methodState == METHOD_MAY_CONT) &&
+ (iret.decision == DECISION_UNCOND_SUCC ||
+ iret.decision == DECISION_COND_SUCC ||
+ iret.decision == DECISION_FAIL)) {
+ ret->methodState = iret.methodState;
+ ret->decision = iret.decision;
+ }
+
+ return 0;
+}
+
+
+static int eap_ttls_phase2_request_eap_method(struct eap_sm *sm,
+ struct eap_ttls_data *data,
+ struct eap_method_ret *ret,
+ struct eap_hdr *hdr, size_t len,
+ u8 method, struct wpabuf **resp)
+{
+#ifdef EAP_TNC
+ if (data->tnc_started && data->phase2_method &&
+ data->phase2_priv && method == EAP_TYPE_TNC &&
+ data->phase2_eap_type.method == EAP_TYPE_TNC)
+ return eap_ttls_phase2_eap_process(sm, data, ret, hdr, len,
+ resp);
+
+ if (data->ready_for_tnc && !data->tnc_started &&
+ method == EAP_TYPE_TNC) {
+ wpa_printf(MSG_DEBUG, "EAP-TTLS: Start TNC after completed "
+ "EAP method");
+ data->tnc_started = 1;
+ }
+
+ if (data->tnc_started) {
+ if (data->phase2_eap_type.vendor != EAP_VENDOR_IETF ||
+ data->phase2_eap_type.method == EAP_TYPE_TNC) {
+ wpa_printf(MSG_DEBUG, "EAP-TTLS: Unexpected EAP "
+ "type %d for TNC", method);
+ return -1;
+ }
+
+ data->phase2_eap_type.vendor = EAP_VENDOR_IETF;
+ data->phase2_eap_type.method = method;
+ wpa_printf(MSG_DEBUG, "EAP-TTLS: Selected "
+ "Phase 2 EAP vendor %d method %d (TNC)",
+ data->phase2_eap_type.vendor,
+ data->phase2_eap_type.method);
+
+ if (data->phase2_type == EAP_TTLS_PHASE2_EAP)
+ eap_ttls_phase2_eap_deinit(sm, data);
+ }
+#endif /* EAP_TNC */
+
+ if (data->phase2_eap_type.vendor == EAP_VENDOR_IETF &&
+ data->phase2_eap_type.method == EAP_TYPE_NONE)
+ eap_ttls_phase2_select_eap_method(data, method);
+
+ if (method != data->phase2_eap_type.method || method == EAP_TYPE_NONE)
+ {
+ if (eap_peer_tls_phase2_nak(data->phase2_eap_types,
+ data->num_phase2_eap_types,
+ hdr, resp))
+ return -1;
+ return 0;
+ }
+
+ if (data->phase2_priv == NULL) {
+ data->phase2_method = eap_peer_get_eap_method(
+ EAP_VENDOR_IETF, method);
+ if (data->phase2_method) {
+ sm->init_phase2 = 1;
+ data->phase2_priv = data->phase2_method->init(sm);
+ sm->init_phase2 = 0;
+ }
+ }
+ if (data->phase2_priv == NULL || data->phase2_method == NULL) {
+ wpa_printf(MSG_INFO, "EAP-TTLS: failed to initialize "
+ "Phase 2 EAP method %d", method);
+ return -1;
+ }
+
+ return eap_ttls_phase2_eap_process(sm, data, ret, hdr, len, resp);
+}
+
+
+static int eap_ttls_phase2_request_eap(struct eap_sm *sm,
+ struct eap_ttls_data *data,
+ struct eap_method_ret *ret,
+ struct eap_hdr *hdr,
+ struct wpabuf **resp)
+{
+ size_t len = be_to_host16(hdr->length);
+ u8 *pos;
+ struct eap_peer_config *config = eap_get_config(sm);
+
+ if (len <= sizeof(struct eap_hdr)) {
+ wpa_printf(MSG_INFO, "EAP-TTLS: too short "
+ "Phase 2 request (len=%lu)", (unsigned long) len);
+ return -1;
+ }
+ pos = (u8 *) (hdr + 1);
+ wpa_printf(MSG_DEBUG, "EAP-TTLS: Phase 2 EAP Request: type=%d", *pos);
+ switch (*pos) {
+ case EAP_TYPE_IDENTITY:
+ *resp = eap_sm_buildIdentity(sm, hdr->identifier, 1);
+ break;
+ default:
+ if (eap_ttls_phase2_request_eap_method(sm, data, ret, hdr, len,
+ *pos, resp) < 0)
+ return -1;
+ break;
+ }
+
+ if (*resp == NULL &&
+ (config->pending_req_identity || config->pending_req_password ||
+ config->pending_req_otp)) {
+ return 0;
+ }
+
+ if (*resp == NULL)
+ return -1;
+
+ wpa_hexdump_buf(MSG_DEBUG, "EAP-TTLS: AVP encapsulate EAP Response",
+ *resp);
+ return eap_ttls_avp_encapsulate(resp, RADIUS_ATTR_EAP_MESSAGE, 1);
+}
+
+
+static int eap_ttls_phase2_request_mschapv2(struct eap_sm *sm,
+ struct eap_ttls_data *data,
+ struct eap_method_ret *ret,
+ struct wpabuf **resp)
+{
+#ifdef CONFIG_FIPS
+ wpa_printf(MSG_ERROR, "EAP-TTLS: MSCHAPV2 not supported in FIPS build");
+ return -1;
+#else /* CONFIG_FIPS */
+#ifdef EAP_MSCHAPv2
+ struct wpabuf *msg;
+ u8 *buf, *pos, *challenge, *peer_challenge;
+ const u8 *identity, *password;
+ size_t identity_len, password_len;
+ int pwhash;
+
+ wpa_printf(MSG_DEBUG, "EAP-TTLS: Phase 2 MSCHAPV2 Request");
+
+ identity = eap_get_config_identity(sm, &identity_len);
+ password = eap_get_config_password2(sm, &password_len, &pwhash);
+ if (identity == NULL || password == NULL)
+ return -1;
+
+ msg = wpabuf_alloc(identity_len + 1000);
+ if (msg == NULL) {
+ wpa_printf(MSG_ERROR,
+ "EAP-TTLS/MSCHAPV2: Failed to allocate memory");
+ return -1;
+ }
+ pos = buf = wpabuf_mhead(msg);
+
+ /* User-Name */
+ pos = eap_ttls_avp_add(buf, pos, RADIUS_ATTR_USER_NAME, 0, 1,
+ identity, identity_len);
+
+ /* MS-CHAP-Challenge */
+ challenge = eap_ttls_implicit_challenge(
+ sm, data, EAP_TTLS_MSCHAPV2_CHALLENGE_LEN + 1);
+ if (challenge == NULL) {
+ wpabuf_free(msg);
+ wpa_printf(MSG_ERROR, "EAP-TTLS/MSCHAPV2: Failed to derive "
+ "implicit challenge");
+ return -1;
+ }
+
+ pos = eap_ttls_avp_add(buf, pos, RADIUS_ATTR_MS_CHAP_CHALLENGE,
+ RADIUS_VENDOR_ID_MICROSOFT, 1,
+ challenge, EAP_TTLS_MSCHAPV2_CHALLENGE_LEN);
+
+ /* MS-CHAP2-Response */
+ pos = eap_ttls_avp_hdr(pos, RADIUS_ATTR_MS_CHAP2_RESPONSE,
+ RADIUS_VENDOR_ID_MICROSOFT, 1,
+ EAP_TTLS_MSCHAPV2_RESPONSE_LEN);
+ data->ident = challenge[EAP_TTLS_MSCHAPV2_CHALLENGE_LEN];
+ *pos++ = data->ident;
+ *pos++ = 0; /* Flags */
+ if (os_get_random(pos, EAP_TTLS_MSCHAPV2_CHALLENGE_LEN) < 0) {
+ os_free(challenge);
+ wpabuf_free(msg);
+ wpa_printf(MSG_ERROR, "EAP-TTLS/MSCHAPV2: Failed to get "
+ "random data for peer challenge");
+ return -1;
+ }
+ peer_challenge = pos;
+ pos += EAP_TTLS_MSCHAPV2_CHALLENGE_LEN;
+ os_memset(pos, 0, 8); /* Reserved, must be zero */
+ pos += 8;
+ if (mschapv2_derive_response(identity, identity_len, password,
+ password_len, pwhash, challenge,
+ peer_challenge, pos, data->auth_response,
+ data->master_key)) {
+ os_free(challenge);
+ wpabuf_free(msg);
+ wpa_printf(MSG_ERROR, "EAP-TTLS/MSCHAPV2: Failed to derive "
+ "response");
+ return -1;
+ }
+ data->auth_response_valid = 1;
+
+ pos += 24;
+ os_free(challenge);
+ AVP_PAD(buf, pos);
+
+ wpabuf_put(msg, pos - buf);
+ *resp = msg;
+
+ return 0;
+#else /* EAP_MSCHAPv2 */
+ wpa_printf(MSG_ERROR, "EAP-TTLS: MSCHAPv2 not included in the build");
+ return -1;
+#endif /* EAP_MSCHAPv2 */
+#endif /* CONFIG_FIPS */
+}
+
+
+static int eap_ttls_phase2_request_mschap(struct eap_sm *sm,
+ struct eap_ttls_data *data,
+ struct eap_method_ret *ret,
+ struct wpabuf **resp)
+{
+#ifdef CONFIG_FIPS
+ wpa_printf(MSG_ERROR, "EAP-TTLS: MSCHAP not supported in FIPS build");
+ return -1;
+#else /* CONFIG_FIPS */
+ struct wpabuf *msg;
+ u8 *buf, *pos, *challenge;
+ const u8 *identity, *password;
+ size_t identity_len, password_len;
+ int pwhash;
+
+ wpa_printf(MSG_DEBUG, "EAP-TTLS: Phase 2 MSCHAP Request");
+
+ identity = eap_get_config_identity(sm, &identity_len);
+ password = eap_get_config_password2(sm, &password_len, &pwhash);
+ if (identity == NULL || password == NULL)
+ return -1;
+
+ msg = wpabuf_alloc(identity_len + 1000);
+ if (msg == NULL) {
+ wpa_printf(MSG_ERROR,
+ "EAP-TTLS/MSCHAP: Failed to allocate memory");
+ return -1;
+ }
+ pos = buf = wpabuf_mhead(msg);
+
+ /* User-Name */
+ pos = eap_ttls_avp_add(buf, pos, RADIUS_ATTR_USER_NAME, 0, 1,
+ identity, identity_len);
+
+ /* MS-CHAP-Challenge */
+ challenge = eap_ttls_implicit_challenge(
+ sm, data, EAP_TTLS_MSCHAP_CHALLENGE_LEN + 1);
+ if (challenge == NULL) {
+ wpabuf_free(msg);
+ wpa_printf(MSG_ERROR, "EAP-TTLS/MSCHAP: Failed to derive "
+ "implicit challenge");
+ return -1;
+ }
+
+ pos = eap_ttls_avp_add(buf, pos, RADIUS_ATTR_MS_CHAP_CHALLENGE,
+ RADIUS_VENDOR_ID_MICROSOFT, 1,
+ challenge, EAP_TTLS_MSCHAP_CHALLENGE_LEN);
+
+ /* MS-CHAP-Response */
+ pos = eap_ttls_avp_hdr(pos, RADIUS_ATTR_MS_CHAP_RESPONSE,
+ RADIUS_VENDOR_ID_MICROSOFT, 1,
+ EAP_TTLS_MSCHAP_RESPONSE_LEN);
+ data->ident = challenge[EAP_TTLS_MSCHAP_CHALLENGE_LEN];
+ *pos++ = data->ident;
+ *pos++ = 1; /* Flags: Use NT style passwords */
+ os_memset(pos, 0, 24); /* LM-Response */
+ pos += 24;
+ if (pwhash) {
+ challenge_response(challenge, password, pos); /* NT-Response */
+ wpa_hexdump_key(MSG_DEBUG, "EAP-TTLS: MSCHAP password hash",
+ password, 16);
+ } else {
+ nt_challenge_response(challenge, password, password_len,
+ pos); /* NT-Response */
+ wpa_hexdump_ascii_key(MSG_DEBUG, "EAP-TTLS: MSCHAP password",
+ password, password_len);
+ }
+ wpa_hexdump(MSG_DEBUG, "EAP-TTLS: MSCHAP implicit challenge",
+ challenge, EAP_TTLS_MSCHAP_CHALLENGE_LEN);
+ wpa_hexdump(MSG_DEBUG, "EAP-TTLS: MSCHAP response", pos, 24);
+ pos += 24;
+ os_free(challenge);
+ AVP_PAD(buf, pos);
+
+ wpabuf_put(msg, pos - buf);
+ *resp = msg;
+
+ /* EAP-TTLS/MSCHAP does not provide tunneled success
+ * notification, so assume that Phase2 succeeds. */
+ ret->methodState = METHOD_DONE;
+ ret->decision = DECISION_COND_SUCC;
+
+ return 0;
+#endif /* CONFIG_FIPS */
+}
+
+
+static int eap_ttls_phase2_request_pap(struct eap_sm *sm,
+ struct eap_ttls_data *data,
+ struct eap_method_ret *ret,
+ struct wpabuf **resp)
+{
+ struct wpabuf *msg;
+ u8 *buf, *pos;
+ size_t pad;
+ const u8 *identity, *password;
+ size_t identity_len, password_len;
+
+ wpa_printf(MSG_DEBUG, "EAP-TTLS: Phase 2 PAP Request");
+
+ identity = eap_get_config_identity(sm, &identity_len);
+ password = eap_get_config_password(sm, &password_len);
+ if (identity == NULL || password == NULL)
+ return -1;
+
+ msg = wpabuf_alloc(identity_len + password_len + 100);
+ if (msg == NULL) {
+ wpa_printf(MSG_ERROR,
+ "EAP-TTLS/PAP: Failed to allocate memory");
+ return -1;
+ }
+ pos = buf = wpabuf_mhead(msg);
+
+ /* User-Name */
+ pos = eap_ttls_avp_add(buf, pos, RADIUS_ATTR_USER_NAME, 0, 1,
+ identity, identity_len);
+
+ /* User-Password; in RADIUS, this is encrypted, but EAP-TTLS encrypts
+ * the data, so no separate encryption is used in the AVP itself.
+ * However, the password is padded to obfuscate its length. */
+ pad = password_len == 0 ? 16 : (16 - (password_len & 15)) & 15;
+ pos = eap_ttls_avp_hdr(pos, RADIUS_ATTR_USER_PASSWORD, 0, 1,
+ password_len + pad);
+ os_memcpy(pos, password, password_len);
+ pos += password_len;
+ os_memset(pos, 0, pad);
+ pos += pad;
+ AVP_PAD(buf, pos);
+
+ wpabuf_put(msg, pos - buf);
+ *resp = msg;
+
+ /* EAP-TTLS/PAP does not provide tunneled success notification,
+ * so assume that Phase2 succeeds. */
+ ret->methodState = METHOD_DONE;
+ ret->decision = DECISION_COND_SUCC;
+
+ return 0;
+}
+
+
+static int eap_ttls_phase2_request_chap(struct eap_sm *sm,
+ struct eap_ttls_data *data,
+ struct eap_method_ret *ret,
+ struct wpabuf **resp)
+{
+#ifdef CONFIG_FIPS
+ wpa_printf(MSG_ERROR, "EAP-TTLS: CHAP not supported in FIPS build");
+ return -1;
+#else /* CONFIG_FIPS */
+ struct wpabuf *msg;
+ u8 *buf, *pos, *challenge;
+ const u8 *identity, *password;
+ size_t identity_len, password_len;
+
+ wpa_printf(MSG_DEBUG, "EAP-TTLS: Phase 2 CHAP Request");
+
+ identity = eap_get_config_identity(sm, &identity_len);
+ password = eap_get_config_password(sm, &password_len);
+ if (identity == NULL || password == NULL)
+ return -1;
+
+ msg = wpabuf_alloc(identity_len + 1000);
+ if (msg == NULL) {
+ wpa_printf(MSG_ERROR,
+ "EAP-TTLS/CHAP: Failed to allocate memory");
+ return -1;
+ }
+ pos = buf = wpabuf_mhead(msg);
+
+ /* User-Name */
+ pos = eap_ttls_avp_add(buf, pos, RADIUS_ATTR_USER_NAME, 0, 1,
+ identity, identity_len);
+
+ /* CHAP-Challenge */
+ challenge = eap_ttls_implicit_challenge(
+ sm, data, EAP_TTLS_CHAP_CHALLENGE_LEN + 1);
+ if (challenge == NULL) {
+ wpabuf_free(msg);
+ wpa_printf(MSG_ERROR, "EAP-TTLS/CHAP: Failed to derive "
+ "implicit challenge");
+ return -1;
+ }
+
+ pos = eap_ttls_avp_add(buf, pos, RADIUS_ATTR_CHAP_CHALLENGE, 0, 1,
+ challenge, EAP_TTLS_CHAP_CHALLENGE_LEN);
+
+ /* CHAP-Password */
+ pos = eap_ttls_avp_hdr(pos, RADIUS_ATTR_CHAP_PASSWORD, 0, 1,
+ 1 + EAP_TTLS_CHAP_PASSWORD_LEN);
+ data->ident = challenge[EAP_TTLS_CHAP_CHALLENGE_LEN];
+ *pos++ = data->ident;
+
+ /* MD5(Ident + Password + Challenge) */
+ chap_md5(data->ident, password, password_len, challenge,
+ EAP_TTLS_CHAP_CHALLENGE_LEN, pos);
+
+ wpa_hexdump_ascii(MSG_DEBUG, "EAP-TTLS: CHAP username",
+ identity, identity_len);
+ wpa_hexdump_ascii_key(MSG_DEBUG, "EAP-TTLS: CHAP password",
+ password, password_len);
+ wpa_hexdump(MSG_DEBUG, "EAP-TTLS: CHAP implicit challenge",
+ challenge, EAP_TTLS_CHAP_CHALLENGE_LEN);
+ wpa_hexdump(MSG_DEBUG, "EAP-TTLS: CHAP password",
+ pos, EAP_TTLS_CHAP_PASSWORD_LEN);
+ pos += EAP_TTLS_CHAP_PASSWORD_LEN;
+ os_free(challenge);
+ AVP_PAD(buf, pos);
+
+ wpabuf_put(msg, pos - buf);
+ *resp = msg;
+
+ /* EAP-TTLS/CHAP does not provide tunneled success
+ * notification, so assume that Phase2 succeeds. */
+ ret->methodState = METHOD_DONE;
+ ret->decision = DECISION_COND_SUCC;
+
+ return 0;
+#endif /* CONFIG_FIPS */
+}
+
+
+static int eap_ttls_phase2_request(struct eap_sm *sm,
+ struct eap_ttls_data *data,
+ struct eap_method_ret *ret,
+ struct eap_hdr *hdr,
+ struct wpabuf **resp)
+{
+ int res = 0;
+ size_t len;
+ enum phase2_types phase2_type = data->phase2_type;
+
+#ifdef EAP_TNC
+ if (data->tnc_started) {
+ wpa_printf(MSG_DEBUG, "EAP-TTLS: Processing TNC");
+ phase2_type = EAP_TTLS_PHASE2_EAP;
+ }
+#endif /* EAP_TNC */
+
+ if (phase2_type == EAP_TTLS_PHASE2_MSCHAPV2 ||
+ phase2_type == EAP_TTLS_PHASE2_MSCHAP ||
+ phase2_type == EAP_TTLS_PHASE2_PAP ||
+ phase2_type == EAP_TTLS_PHASE2_CHAP) {
+ if (eap_get_config_identity(sm, &len) == NULL) {
+ wpa_printf(MSG_INFO,
+ "EAP-TTLS: Identity not configured");
+ eap_sm_request_identity(sm);
+ if (eap_get_config_password(sm, &len) == NULL)
+ eap_sm_request_password(sm);
+ return 0;
+ }
+
+ if (eap_get_config_password(sm, &len) == NULL) {
+ wpa_printf(MSG_INFO,
+ "EAP-TTLS: Password not configured");
+ eap_sm_request_password(sm);
+ return 0;
+ }
+ }
+
+ switch (phase2_type) {
+ case EAP_TTLS_PHASE2_EAP:
+ res = eap_ttls_phase2_request_eap(sm, data, ret, hdr, resp);
+ break;
+ case EAP_TTLS_PHASE2_MSCHAPV2:
+ res = eap_ttls_phase2_request_mschapv2(sm, data, ret, resp);
+ break;
+ case EAP_TTLS_PHASE2_MSCHAP:
+ res = eap_ttls_phase2_request_mschap(sm, data, ret, resp);
+ break;
+ case EAP_TTLS_PHASE2_PAP:
+ res = eap_ttls_phase2_request_pap(sm, data, ret, resp);
+ break;
+ case EAP_TTLS_PHASE2_CHAP:
+ res = eap_ttls_phase2_request_chap(sm, data, ret, resp);
+ break;
+ default:
+ wpa_printf(MSG_ERROR, "EAP-TTLS: Phase 2 - Unknown");
+ res = -1;
+ break;
+ }
+
+ if (res < 0) {
+ ret->methodState = METHOD_DONE;
+ ret->decision = DECISION_FAIL;
+ }
+
+ return res;
+}
+
+
+struct ttls_parse_avp {
+ u8 *mschapv2;
+ u8 *eapdata;
+ size_t eap_len;
+ int mschapv2_error;
+};
+
+
+static int eap_ttls_parse_attr_eap(const u8 *dpos, size_t dlen,
+ struct ttls_parse_avp *parse)
+{
+ wpa_printf(MSG_DEBUG, "EAP-TTLS: AVP - EAP Message");
+ if (parse->eapdata == NULL) {
+ parse->eapdata = os_malloc(dlen);
+ if (parse->eapdata == NULL) {
+ wpa_printf(MSG_WARNING, "EAP-TTLS: Failed to allocate "
+ "memory for Phase 2 EAP data");
+ return -1;
+ }
+ os_memcpy(parse->eapdata, dpos, dlen);
+ parse->eap_len = dlen;
+ } else {
+ u8 *neweap = os_realloc(parse->eapdata, parse->eap_len + dlen);
+ if (neweap == NULL) {
+ wpa_printf(MSG_WARNING, "EAP-TTLS: Failed to allocate "
+ "memory for Phase 2 EAP data");
+ return -1;
+ }
+ os_memcpy(neweap + parse->eap_len, dpos, dlen);
+ parse->eapdata = neweap;
+ parse->eap_len += dlen;
+ }
+
+ return 0;
+}
+
+
+static int eap_ttls_parse_avp(u8 *pos, size_t left,
+ struct ttls_parse_avp *parse)
+{
+ struct ttls_avp *avp;
+ u32 avp_code, avp_length, vendor_id = 0;
+ u8 avp_flags, *dpos;
+ size_t dlen;
+
+ avp = (struct ttls_avp *) pos;
+ avp_code = be_to_host32(avp->avp_code);
+ avp_length = be_to_host32(avp->avp_length);
+ avp_flags = (avp_length >> 24) & 0xff;
+ avp_length &= 0xffffff;
+ wpa_printf(MSG_DEBUG, "EAP-TTLS: AVP: code=%d flags=0x%02x "
+ "length=%d", (int) avp_code, avp_flags,
+ (int) avp_length);
+
+ if (avp_length > left) {
+ wpa_printf(MSG_WARNING, "EAP-TTLS: AVP overflow "
+ "(len=%d, left=%lu) - dropped",
+ (int) avp_length, (unsigned long) left);
+ return -1;
+ }
+
+ if (avp_length < sizeof(*avp)) {
+ wpa_printf(MSG_WARNING, "EAP-TTLS: Invalid AVP length %d",
+ avp_length);
+ return -1;
+ }
+
+ dpos = (u8 *) (avp + 1);
+ dlen = avp_length - sizeof(*avp);
+ if (avp_flags & AVP_FLAGS_VENDOR) {
+ if (dlen < 4) {
+ wpa_printf(MSG_WARNING, "EAP-TTLS: Vendor AVP "
+ "underflow");
+ return -1;
+ }
+ vendor_id = WPA_GET_BE32(dpos);
+ wpa_printf(MSG_DEBUG, "EAP-TTLS: AVP vendor_id %d",
+ (int) vendor_id);
+ dpos += 4;
+ dlen -= 4;
+ }
+
+ wpa_hexdump(MSG_DEBUG, "EAP-TTLS: AVP data", dpos, dlen);
+
+ if (vendor_id == 0 && avp_code == RADIUS_ATTR_EAP_MESSAGE) {
+ if (eap_ttls_parse_attr_eap(dpos, dlen, parse) < 0)
+ return -1;
+ } else if (vendor_id == 0 && avp_code == RADIUS_ATTR_REPLY_MESSAGE) {
+ /* This is an optional message that can be displayed to
+ * the user. */
+ wpa_hexdump_ascii(MSG_DEBUG, "EAP-TTLS: AVP - Reply-Message",
+ dpos, dlen);
+ } else if (vendor_id == RADIUS_VENDOR_ID_MICROSOFT &&
+ avp_code == RADIUS_ATTR_MS_CHAP2_SUCCESS) {
+ wpa_hexdump_ascii(MSG_DEBUG, "EAP-TTLS: MS-CHAP2-Success",
+ dpos, dlen);
+ if (dlen != 43) {
+ wpa_printf(MSG_WARNING, "EAP-TTLS: Unexpected "
+ "MS-CHAP2-Success length "
+ "(len=%lu, expected 43)",
+ (unsigned long) dlen);
+ return -1;
+ }
+ parse->mschapv2 = dpos;
+ } else if (vendor_id == RADIUS_VENDOR_ID_MICROSOFT &&
+ avp_code == RADIUS_ATTR_MS_CHAP_ERROR) {
+ wpa_hexdump_ascii(MSG_DEBUG, "EAP-TTLS: MS-CHAP-Error",
+ dpos, dlen);
+ parse->mschapv2_error = 1;
+ } else if (avp_flags & AVP_FLAGS_MANDATORY) {
+ wpa_printf(MSG_WARNING, "EAP-TTLS: Unsupported mandatory AVP "
+ "code %d vendor_id %d - dropped",
+ (int) avp_code, (int) vendor_id);
+ return -1;
+ } else {
+ wpa_printf(MSG_DEBUG, "EAP-TTLS: Ignoring unsupported AVP "
+ "code %d vendor_id %d",
+ (int) avp_code, (int) vendor_id);
+ }
+
+ return avp_length;
+}
+
+
+static int eap_ttls_parse_avps(struct wpabuf *in_decrypted,
+ struct ttls_parse_avp *parse)
+{
+ u8 *pos;
+ size_t left, pad;
+ int avp_length;
+
+ pos = wpabuf_mhead(in_decrypted);
+ left = wpabuf_len(in_decrypted);
+ wpa_hexdump(MSG_DEBUG, "EAP-TTLS: Decrypted Phase 2 AVPs", pos, left);
+ if (left < sizeof(struct ttls_avp)) {
+ wpa_printf(MSG_WARNING, "EAP-TTLS: Too short Phase 2 AVP frame"
+ " len=%lu expected %lu or more - dropped",
+ (unsigned long) left,
+ (unsigned long) sizeof(struct ttls_avp));
+ return -1;
+ }
+
+ /* Parse AVPs */
+ os_memset(parse, 0, sizeof(*parse));
+
+ while (left > 0) {
+ avp_length = eap_ttls_parse_avp(pos, left, parse);
+ if (avp_length < 0)
+ return -1;
+
+ pad = (4 - (avp_length & 3)) & 3;
+ pos += avp_length + pad;
+ if (left < avp_length + pad)
+ left = 0;
+ else
+ left -= avp_length + pad;
+ }
+
+ return 0;
+}
+
+
+static u8 * eap_ttls_fake_identity_request(void)
+{
+ struct eap_hdr *hdr;
+ u8 *buf;
+
+ wpa_printf(MSG_DEBUG, "EAP-TTLS: empty data in beginning of "
+ "Phase 2 - use fake EAP-Request Identity");
+ buf = os_malloc(sizeof(*hdr) + 1);
+ if (buf == NULL) {
+ wpa_printf(MSG_WARNING, "EAP-TTLS: failed to allocate "
+ "memory for fake EAP-Identity Request");
+ return NULL;
+ }
+
+ hdr = (struct eap_hdr *) buf;
+ hdr->code = EAP_CODE_REQUEST;
+ hdr->identifier = 0;
+ hdr->length = host_to_be16(sizeof(*hdr) + 1);
+ buf[sizeof(*hdr)] = EAP_TYPE_IDENTITY;
+
+ return buf;
+}
+
+
+static int eap_ttls_encrypt_response(struct eap_sm *sm,
+ struct eap_ttls_data *data,
+ struct wpabuf *resp, u8 identifier,
+ struct wpabuf **out_data)
+{
+ if (resp == NULL)
+ return 0;
+
+ wpa_hexdump_buf_key(MSG_DEBUG, "EAP-TTLS: Encrypting Phase 2 data",
+ resp);
+ if (eap_peer_tls_encrypt(sm, &data->ssl, EAP_TYPE_TTLS,
+ data->ttls_version, identifier,
+ resp, out_data)) {
+ wpa_printf(MSG_INFO, "EAP-TTLS: Failed to encrypt a Phase 2 "
+ "frame");
+ wpabuf_free(resp);
+ return -1;
+ }
+ wpabuf_free(resp);
+
+ return 0;
+}
+
+
+static int eap_ttls_process_phase2_eap(struct eap_sm *sm,
+ struct eap_ttls_data *data,
+ struct eap_method_ret *ret,
+ struct ttls_parse_avp *parse,
+ struct wpabuf **resp)
+{
+ struct eap_hdr *hdr;
+ size_t len;
+
+ if (parse->eapdata == NULL) {
+ wpa_printf(MSG_WARNING, "EAP-TTLS: No EAP Message in the "
+ "packet - dropped");
+ return -1;
+ }
+
+ wpa_hexdump(MSG_DEBUG, "EAP-TTLS: Phase 2 EAP",
+ parse->eapdata, parse->eap_len);
+ hdr = (struct eap_hdr *) parse->eapdata;
+
+ if (parse->eap_len < sizeof(*hdr)) {
+ wpa_printf(MSG_WARNING, "EAP-TTLS: Too short Phase 2 EAP "
+ "frame (len=%lu, expected %lu or more) - dropped",
+ (unsigned long) parse->eap_len,
+ (unsigned long) sizeof(*hdr));
+ return -1;
+ }
+ len = be_to_host16(hdr->length);
+ if (len > parse->eap_len) {
+ wpa_printf(MSG_INFO, "EAP-TTLS: Length mismatch in Phase 2 "
+ "EAP frame (EAP hdr len=%lu, EAP data len in "
+ "AVP=%lu)",
+ (unsigned long) len,
+ (unsigned long) parse->eap_len);
+ return -1;
+ }
+ wpa_printf(MSG_DEBUG, "EAP-TTLS: received Phase 2: code=%d "
+ "identifier=%d length=%lu",
+ hdr->code, hdr->identifier, (unsigned long) len);
+ switch (hdr->code) {
+ case EAP_CODE_REQUEST:
+ if (eap_ttls_phase2_request(sm, data, ret, hdr, resp)) {
+ wpa_printf(MSG_INFO, "EAP-TTLS: Phase2 Request "
+ "processing failed");
+ return -1;
+ }
+ break;
+ default:
+ wpa_printf(MSG_INFO, "EAP-TTLS: Unexpected code=%d in "
+ "Phase 2 EAP header", hdr->code);
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static int eap_ttls_process_phase2_mschapv2(struct eap_sm *sm,
+ struct eap_ttls_data *data,
+ struct eap_method_ret *ret,
+ struct ttls_parse_avp *parse)
+{
+#ifdef EAP_MSCHAPv2
+ if (parse->mschapv2_error) {
+ wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAPV2: Received "
+ "MS-CHAP-Error - failed");
+ ret->methodState = METHOD_DONE;
+ ret->decision = DECISION_FAIL;
+ /* Reply with empty data to ACK error */
+ return 1;
+ }
+
+ if (parse->mschapv2 == NULL) {
+#ifdef EAP_TNC
+ if (data->phase2_success && parse->eapdata) {
+ /*
+ * Allow EAP-TNC to be started after successfully
+ * completed MSCHAPV2.
+ */
+ return 1;
+ }
+#endif /* EAP_TNC */
+ wpa_printf(MSG_WARNING, "EAP-TTLS: no MS-CHAP2-Success AVP "
+ "received for Phase2 MSCHAPV2");
+ return -1;
+ }
+ if (parse->mschapv2[0] != data->ident) {
+ wpa_printf(MSG_WARNING, "EAP-TTLS: Ident mismatch for Phase 2 "
+ "MSCHAPV2 (received Ident 0x%02x, expected 0x%02x)",
+ parse->mschapv2[0], data->ident);
+ return -1;
+ }
+ if (!data->auth_response_valid ||
+ mschapv2_verify_auth_response(data->auth_response,
+ parse->mschapv2 + 1, 42)) {
+ wpa_printf(MSG_WARNING, "EAP-TTLS: Invalid authenticator "
+ "response in Phase 2 MSCHAPV2 success request");
+ return -1;
+ }
+
+ wpa_printf(MSG_INFO, "EAP-TTLS: Phase 2 MSCHAPV2 "
+ "authentication succeeded");
+ ret->methodState = METHOD_DONE;
+ ret->decision = DECISION_UNCOND_SUCC;
+ data->phase2_success = 1;
+
+ /*
+ * Reply with empty data; authentication server will reply
+ * with EAP-Success after this.
+ */
+ return 1;
+#else /* EAP_MSCHAPv2 */
+ wpa_printf(MSG_ERROR, "EAP-TTLS: MSCHAPv2 not included in the build");
+ return -1;
+#endif /* EAP_MSCHAPv2 */
+}
+
+
+#ifdef EAP_TNC
+static int eap_ttls_process_tnc_start(struct eap_sm *sm,
+ struct eap_ttls_data *data,
+ struct eap_method_ret *ret,
+ struct ttls_parse_avp *parse,
+ struct wpabuf **resp)
+{
+ /* TNC uses inner EAP method after non-EAP TTLS phase 2. */
+ if (parse->eapdata == NULL) {
+ wpa_printf(MSG_INFO, "EAP-TTLS: Phase 2 received "
+ "unexpected tunneled data (no EAP)");
+ return -1;
+ }
+
+ if (!data->ready_for_tnc) {
+ wpa_printf(MSG_INFO, "EAP-TTLS: Phase 2 received "
+ "EAP after non-EAP, but not ready for TNC");
+ return -1;
+ }
+
+ wpa_printf(MSG_DEBUG, "EAP-TTLS: Start TNC after completed "
+ "non-EAP method");
+ data->tnc_started = 1;
+
+ if (eap_ttls_process_phase2_eap(sm, data, ret, parse, resp) < 0)
+ return -1;
+
+ return 0;
+}
+#endif /* EAP_TNC */
+
+
+static int eap_ttls_process_decrypted(struct eap_sm *sm,
+ struct eap_ttls_data *data,
+ struct eap_method_ret *ret,
+ u8 identifier,
+ struct ttls_parse_avp *parse,
+ struct wpabuf *in_decrypted,
+ struct wpabuf **out_data)
+{
+ struct wpabuf *resp = NULL;
+ struct eap_peer_config *config = eap_get_config(sm);
+ int res;
+ enum phase2_types phase2_type = data->phase2_type;
+
+#ifdef EAP_TNC
+ if (data->tnc_started)
+ phase2_type = EAP_TTLS_PHASE2_EAP;
+#endif /* EAP_TNC */
+
+ switch (phase2_type) {
+ case EAP_TTLS_PHASE2_EAP:
+ if (eap_ttls_process_phase2_eap(sm, data, ret, parse, &resp) <
+ 0)
+ return -1;
+ break;
+ case EAP_TTLS_PHASE2_MSCHAPV2:
+ res = eap_ttls_process_phase2_mschapv2(sm, data, ret, parse);
+#ifdef EAP_TNC
+ if (res == 1 && parse->eapdata && data->phase2_success) {
+ /*
+ * TNC may be required as the next
+ * authentication method within the tunnel.
+ */
+ ret->methodState = METHOD_MAY_CONT;
+ data->ready_for_tnc = 1;
+ if (eap_ttls_process_tnc_start(sm, data, ret, parse,
+ &resp) == 0)
+ break;
+ }
+#endif /* EAP_TNC */
+ return res;
+ case EAP_TTLS_PHASE2_MSCHAP:
+ case EAP_TTLS_PHASE2_PAP:
+ case EAP_TTLS_PHASE2_CHAP:
+#ifdef EAP_TNC
+ if (eap_ttls_process_tnc_start(sm, data, ret, parse, &resp) <
+ 0)
+ return -1;
+ break;
+#else /* EAP_TNC */
+ /* EAP-TTLS/{MSCHAP,PAP,CHAP} should not send any TLS tunneled
+ * requests to the supplicant */
+ wpa_printf(MSG_INFO, "EAP-TTLS: Phase 2 received unexpected "
+ "tunneled data");
+ return -1;
+#endif /* EAP_TNC */
+ }
+
+ if (resp) {
+ if (eap_ttls_encrypt_response(sm, data, resp, identifier,
+ out_data) < 0)
+ return -1;
+ } else if (config->pending_req_identity ||
+ config->pending_req_password ||
+ config->pending_req_otp ||
+ config->pending_req_new_password) {
+ wpabuf_free(data->pending_phase2_req);
+ data->pending_phase2_req = wpabuf_dup(in_decrypted);
+ }
+
+ return 0;
+}
+
+
+static int eap_ttls_implicit_identity_request(struct eap_sm *sm,
+ struct eap_ttls_data *data,
+ struct eap_method_ret *ret,
+ u8 identifier,
+ struct wpabuf **out_data)
+{
+ int retval = 0;
+ struct eap_hdr *hdr;
+ struct wpabuf *resp;
+
+ hdr = (struct eap_hdr *) eap_ttls_fake_identity_request();
+ if (hdr == NULL) {
+ ret->methodState = METHOD_DONE;
+ ret->decision = DECISION_FAIL;
+ return -1;
+ }
+
+ resp = NULL;
+ if (eap_ttls_phase2_request(sm, data, ret, hdr, &resp)) {
+ wpa_printf(MSG_INFO, "EAP-TTLS: Phase2 Request "
+ "processing failed");
+ retval = -1;
+ } else {
+ struct eap_peer_config *config = eap_get_config(sm);
+ if (resp == NULL &&
+ (config->pending_req_identity ||
+ config->pending_req_password ||
+ config->pending_req_otp ||
+ config->pending_req_new_password)) {
+ /*
+ * Use empty buffer to force implicit request
+ * processing when EAP request is re-processed after
+ * user input.
+ */
+ wpabuf_free(data->pending_phase2_req);
+ data->pending_phase2_req = wpabuf_alloc(0);
+ }
+
+ retval = eap_ttls_encrypt_response(sm, data, resp, identifier,
+ out_data);
+ }
+
+ os_free(hdr);
+
+ if (retval < 0) {
+ ret->methodState = METHOD_DONE;
+ ret->decision = DECISION_FAIL;
+ }
+
+ return retval;
+}
+
+
+static int eap_ttls_phase2_start(struct eap_sm *sm, struct eap_ttls_data *data,
+ struct eap_method_ret *ret, u8 identifier,
+ struct wpabuf **out_data)
+{
+ data->phase2_start = 0;
+
+ /*
+ * EAP-TTLS does not use Phase2 on fast re-auth; this must be done only
+ * if TLS part was indeed resuming a previous session. Most
+ * Authentication Servers terminate EAP-TTLS before reaching this
+ * point, but some do not. Make wpa_supplicant stop phase 2 here, if
+ * needed.
+ */
+ if (data->reauth &&
+ tls_connection_resumed(sm->ssl_ctx, data->ssl.conn)) {
+ wpa_printf(MSG_DEBUG, "EAP-TTLS: Session resumption - "
+ "skip phase 2");
+ *out_data = eap_peer_tls_build_ack(identifier, EAP_TYPE_TTLS,
+ data->ttls_version);
+ ret->methodState = METHOD_DONE;
+ ret->decision = DECISION_UNCOND_SUCC;
+ data->phase2_success = 1;
+ return 0;
+ }
+
+ return eap_ttls_implicit_identity_request(sm, data, ret, identifier,
+ out_data);
+}
+
+
+static int eap_ttls_decrypt(struct eap_sm *sm, struct eap_ttls_data *data,
+ struct eap_method_ret *ret, u8 identifier,
+ const struct wpabuf *in_data,
+ struct wpabuf **out_data)
+{
+ struct wpabuf *in_decrypted = NULL;
+ int retval = 0;
+ struct ttls_parse_avp parse;
+
+ os_memset(&parse, 0, sizeof(parse));
+
+ wpa_printf(MSG_DEBUG, "EAP-TTLS: received %lu bytes encrypted data for"
+ " Phase 2",
+ in_data ? (unsigned long) wpabuf_len(in_data) : 0);
+
+ if (data->pending_phase2_req) {
+ wpa_printf(MSG_DEBUG, "EAP-TTLS: Pending Phase 2 request - "
+ "skip decryption and use old data");
+ /* Clear TLS reassembly state. */
+ eap_peer_tls_reset_input(&data->ssl);
+
+ in_decrypted = data->pending_phase2_req;
+ data->pending_phase2_req = NULL;
+ if (wpabuf_len(in_decrypted) == 0) {
+ wpabuf_free(in_decrypted);
+ return eap_ttls_implicit_identity_request(
+ sm, data, ret, identifier, out_data);
+ }
+ goto continue_req;
+ }
+
+ if ((in_data == NULL || wpabuf_len(in_data) == 0) &&
+ data->phase2_start) {
+ return eap_ttls_phase2_start(sm, data, ret, identifier,
+ out_data);
+ }
+
+ if (in_data == NULL || wpabuf_len(in_data) == 0) {
+ /* Received TLS ACK - requesting more fragments */
+ return eap_peer_tls_encrypt(sm, &data->ssl, EAP_TYPE_TTLS,
+ data->ttls_version,
+ identifier, NULL, out_data);
+ }
+
+ retval = eap_peer_tls_decrypt(sm, &data->ssl, in_data, &in_decrypted);
+ if (retval)
+ goto done;
+
+continue_req:
+ data->phase2_start = 0;
+
+ if (eap_ttls_parse_avps(in_decrypted, &parse) < 0) {
+ retval = -1;
+ goto done;
+ }
+
+ retval = eap_ttls_process_decrypted(sm, data, ret, identifier,
+ &parse, in_decrypted, out_data);
+
+done:
+ wpabuf_free(in_decrypted);
+ os_free(parse.eapdata);
+
+ if (retval < 0) {
+ ret->methodState = METHOD_DONE;
+ ret->decision = DECISION_FAIL;
+ }
+
+ return retval;
+}
+
+
+static int eap_ttls_process_handshake(struct eap_sm *sm,
+ struct eap_ttls_data *data,
+ struct eap_method_ret *ret,
+ u8 identifier,
+ const struct wpabuf *in_data,
+ struct wpabuf **out_data)
+{
+ int res;
+
+ res = eap_peer_tls_process_helper(sm, &data->ssl, EAP_TYPE_TTLS,
+ data->ttls_version, identifier,
+ in_data, out_data);
+ if (res < 0) {
+ wpa_printf(MSG_DEBUG, "EAP-TTLS: TLS processing failed");
+ ret->methodState = METHOD_DONE;
+ ret->decision = DECISION_FAIL;
+ return -1;
+ }
+
+ if (tls_connection_established(sm->ssl_ctx, data->ssl.conn)) {
+ wpa_printf(MSG_DEBUG, "EAP-TTLS: TLS done, proceed to "
+ "Phase 2");
+ if (data->resuming) {
+ wpa_printf(MSG_DEBUG, "EAP-TTLS: fast reauth - may "
+ "skip Phase 2");
+ ret->decision = DECISION_COND_SUCC;
+ ret->methodState = METHOD_MAY_CONT;
+ }
+ data->phase2_start = 1;
+ eap_ttls_v0_derive_key(sm, data);
+
+ if (*out_data == NULL || wpabuf_len(*out_data) == 0) {
+ if (eap_ttls_decrypt(sm, data, ret, identifier,
+ NULL, out_data)) {
+ wpa_printf(MSG_WARNING, "EAP-TTLS: "
+ "failed to process early "
+ "start for Phase 2");
+ }
+ res = 0;
+ }
+ data->resuming = 0;
+ }
+
+ if (res == 2) {
+ /*
+ * Application data included in the handshake message.
+ */
+ wpabuf_free(data->pending_phase2_req);
+ data->pending_phase2_req = *out_data;
+ *out_data = NULL;
+ res = eap_ttls_decrypt(sm, data, ret, identifier, in_data,
+ out_data);
+ }
+
+ return res;
+}
+
+
+static void eap_ttls_check_auth_status(struct eap_sm *sm,
+ struct eap_ttls_data *data,
+ struct eap_method_ret *ret)
+{
+ if (ret->methodState == METHOD_DONE) {
+ ret->allowNotifications = FALSE;
+ if (ret->decision == DECISION_UNCOND_SUCC ||
+ ret->decision == DECISION_COND_SUCC) {
+ wpa_printf(MSG_DEBUG, "EAP-TTLS: Authentication "
+ "completed successfully");
+ data->phase2_success = 1;
+#ifdef EAP_TNC
+ if (!data->ready_for_tnc && !data->tnc_started) {
+ /*
+ * TNC may be required as the next
+ * authentication method within the tunnel.
+ */
+ ret->methodState = METHOD_MAY_CONT;
+ data->ready_for_tnc = 1;
+ }
+#endif /* EAP_TNC */
+ }
+ } else if (ret->methodState == METHOD_MAY_CONT &&
+ (ret->decision == DECISION_UNCOND_SUCC ||
+ ret->decision == DECISION_COND_SUCC)) {
+ wpa_printf(MSG_DEBUG, "EAP-TTLS: Authentication "
+ "completed successfully (MAY_CONT)");
+ data->phase2_success = 1;
+ }
+}
+
+
+static struct wpabuf * eap_ttls_process(struct eap_sm *sm, void *priv,
+ struct eap_method_ret *ret,
+ const struct wpabuf *reqData)
+{
+ size_t left;
+ int res;
+ u8 flags, id;
+ struct wpabuf *resp;
+ const u8 *pos;
+ struct eap_ttls_data *data = priv;
+ struct wpabuf msg;
+
+ pos = eap_peer_tls_process_init(sm, &data->ssl, EAP_TYPE_TTLS, 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-TTLS: Start (server ver=%d, own "
+ "ver=%d)", flags & EAP_TLS_VERSION_MASK,
+ data->ttls_version);
+
+ /* RFC 5281, Ch. 9.2:
+ * "This packet MAY contain additional information in the form
+ * of AVPs, which may provide useful hints to the client"
+ * For now, ignore any potential extra data.
+ */
+ left = 0;
+ }
+
+ wpabuf_set(&msg, pos, left);
+
+ resp = NULL;
+ if (tls_connection_established(sm->ssl_ctx, data->ssl.conn) &&
+ !data->resuming) {
+ res = eap_ttls_decrypt(sm, data, ret, id, &msg, &resp);
+ } else {
+ res = eap_ttls_process_handshake(sm, data, ret, id,
+ &msg, &resp);
+ }
+
+ eap_ttls_check_auth_status(sm, data, ret);
+
+ /* FIX: what about res == -1? Could just move all error processing into
+ * the other functions and get rid of this res==1 case here. */
+ if (res == 1) {
+ wpabuf_free(resp);
+ return eap_peer_tls_build_ack(id, EAP_TYPE_TTLS,
+ data->ttls_version);
+ }
+ return resp;
+}
+
+
+static Boolean eap_ttls_has_reauth_data(struct eap_sm *sm, void *priv)
+{
+ struct eap_ttls_data *data = priv;
+ return tls_connection_established(sm->ssl_ctx, data->ssl.conn) &&
+ data->phase2_success;
+}
+
+
+static void eap_ttls_deinit_for_reauth(struct eap_sm *sm, void *priv)
+{
+ struct eap_ttls_data *data = priv;
+ wpabuf_free(data->pending_phase2_req);
+ data->pending_phase2_req = NULL;
+#ifdef EAP_TNC
+ data->ready_for_tnc = 0;
+ data->tnc_started = 0;
+#endif /* EAP_TNC */
+}
+
+
+static void * eap_ttls_init_for_reauth(struct eap_sm *sm, void *priv)
+{
+ struct eap_ttls_data *data = priv;
+ eap_ttls_free_key(data);
+ os_free(data->session_id);
+ data->session_id = NULL;
+ if (eap_peer_tls_reauth_init(sm, &data->ssl)) {
+ os_free(data);
+ return NULL;
+ }
+ if (data->phase2_priv && data->phase2_method &&
+ data->phase2_method->init_for_reauth)
+ data->phase2_method->init_for_reauth(sm, data->phase2_priv);
+ data->phase2_start = 0;
+ data->phase2_success = 0;
+ data->resuming = 1;
+ data->reauth = 1;
+ return priv;
+}
+
+
+static int eap_ttls_get_status(struct eap_sm *sm, void *priv, char *buf,
+ size_t buflen, int verbose)
+{
+ struct eap_ttls_data *data = priv;
+ int len, ret;
+
+ len = eap_peer_tls_status(sm, &data->ssl, buf, buflen, verbose);
+ ret = os_snprintf(buf + len, buflen - len,
+ "EAP-TTLSv%d Phase2 method=",
+ data->ttls_version);
+ if (os_snprintf_error(buflen - len, ret))
+ return len;
+ len += ret;
+ switch (data->phase2_type) {
+ case EAP_TTLS_PHASE2_EAP:
+ ret = os_snprintf(buf + len, buflen - len, "EAP-%s\n",
+ data->phase2_method ?
+ data->phase2_method->name : "?");
+ break;
+ case EAP_TTLS_PHASE2_MSCHAPV2:
+ ret = os_snprintf(buf + len, buflen - len, "MSCHAPV2\n");
+ break;
+ case EAP_TTLS_PHASE2_MSCHAP:
+ ret = os_snprintf(buf + len, buflen - len, "MSCHAP\n");
+ break;
+ case EAP_TTLS_PHASE2_PAP:
+ ret = os_snprintf(buf + len, buflen - len, "PAP\n");
+ break;
+ case EAP_TTLS_PHASE2_CHAP:
+ ret = os_snprintf(buf + len, buflen - len, "CHAP\n");
+ break;
+ default:
+ ret = 0;
+ break;
+ }
+ if (os_snprintf_error(buflen - len, ret))
+ return len;
+ len += ret;
+
+ return len;
+}
+
+
+static Boolean eap_ttls_isKeyAvailable(struct eap_sm *sm, void *priv)
+{
+ struct eap_ttls_data *data = priv;
+ return data->key_data != NULL && data->phase2_success;
+}
+
+
+static u8 * eap_ttls_getKey(struct eap_sm *sm, void *priv, size_t *len)
+{
+ struct eap_ttls_data *data = priv;
+ u8 *key;
+
+ if (data->key_data == NULL || !data->phase2_success)
+ return NULL;
+
+ key = os_malloc(EAP_TLS_KEY_LEN);
+ if (key == NULL)
+ return NULL;
+
+ *len = EAP_TLS_KEY_LEN;
+ os_memcpy(key, data->key_data, EAP_TLS_KEY_LEN);
+
+ return key;
+}
+
+
+static u8 * eap_ttls_get_session_id(struct eap_sm *sm, void *priv, size_t *len)
+{
+ struct eap_ttls_data *data = priv;
+ u8 *id;
+
+ if (data->session_id == NULL || !data->phase2_success)
+ return NULL;
+
+ id = os_malloc(data->id_len);
+ if (id == NULL)
+ return NULL;
+
+ *len = data->id_len;
+ os_memcpy(id, data->session_id, data->id_len);
+
+ return id;
+}
+
+
+static u8 * eap_ttls_get_emsk(struct eap_sm *sm, void *priv, size_t *len)
+{
+ struct eap_ttls_data *data = priv;
+ u8 *key;
+
+ if (data->key_data == NULL)
+ return NULL;
+
+ key = os_malloc(EAP_EMSK_LEN);
+ if (key == NULL)
+ return NULL;
+
+ *len = EAP_EMSK_LEN;
+ os_memcpy(key, data->key_data + EAP_TLS_KEY_LEN, EAP_EMSK_LEN);
+
+ return key;
+}
+
+
+int eap_peer_ttls_register(void)
+{
+ struct eap_method *eap;
+ int ret;
+
+ eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION,
+ EAP_VENDOR_IETF, EAP_TYPE_TTLS, "TTLS");
+ if (eap == NULL)
+ return -1;
+
+ eap->init = eap_ttls_init;
+ eap->deinit = eap_ttls_deinit;
+ eap->process = eap_ttls_process;
+ eap->isKeyAvailable = eap_ttls_isKeyAvailable;
+ eap->getKey = eap_ttls_getKey;
+ eap->getSessionId = eap_ttls_get_session_id;
+ eap->get_status = eap_ttls_get_status;
+ eap->has_reauth_data = eap_ttls_has_reauth_data;
+ eap->deinit_for_reauth = eap_ttls_deinit_for_reauth;
+ eap->init_for_reauth = eap_ttls_init_for_reauth;
+ eap->get_emsk = eap_ttls_get_emsk;
+
+ ret = eap_peer_method_register(eap);
+ if (ret)
+ eap_peer_method_free(eap);
+ return ret;
+}
diff --git a/freebsd/contrib/wpa/src/eap_peer/mschapv2.c b/freebsd/contrib/wpa/src/eap_peer/mschapv2.c
new file mode 100644
index 00000000..ba5d763c
--- /dev/null
+++ b/freebsd/contrib/wpa/src/eap_peer/mschapv2.c
@@ -0,0 +1,126 @@
+#include <machine/rtems-bsd-user-space.h>
+
+/*
+ * MSCHAPV2 (RFC 2759)
+ * Copyright (c) 2004-2008, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "crypto/ms_funcs.h"
+#include "mschapv2.h"
+
+const u8 * mschapv2_remove_domain(const u8 *username, size_t *len)
+{
+ size_t i;
+
+ /*
+ * MSCHAPv2 does not include optional domain name in the
+ * challenge-response calculation, so remove domain prefix
+ * (if present).
+ */
+
+ for (i = 0; i < *len; i++) {
+ if (username[i] == '\\') {
+ *len -= i + 1;
+ return username + i + 1;
+ }
+ }
+
+ return username;
+}
+
+
+int mschapv2_derive_response(const u8 *identity, size_t identity_len,
+ const u8 *password, size_t password_len,
+ int pwhash,
+ const u8 *auth_challenge,
+ const u8 *peer_challenge,
+ u8 *nt_response, u8 *auth_response,
+ u8 *master_key)
+{
+ const u8 *username;
+ size_t username_len;
+ u8 password_hash[16], password_hash_hash[16];
+
+ wpa_hexdump_ascii(MSG_DEBUG, "MSCHAPV2: Identity",
+ identity, identity_len);
+ username_len = identity_len;
+ username = mschapv2_remove_domain(identity, &username_len);
+ wpa_hexdump_ascii(MSG_DEBUG, "MSCHAPV2: Username",
+ username, username_len);
+
+ wpa_hexdump(MSG_DEBUG, "MSCHAPV2: auth_challenge",
+ auth_challenge, MSCHAPV2_CHAL_LEN);
+ wpa_hexdump(MSG_DEBUG, "MSCHAPV2: peer_challenge",
+ peer_challenge, MSCHAPV2_CHAL_LEN);
+ wpa_hexdump_ascii(MSG_DEBUG, "MSCHAPV2: username",
+ username, username_len);
+ /* Authenticator response is not really needed yet, but calculate it
+ * here so that challenges need not be saved. */
+ if (pwhash) {
+ wpa_hexdump_key(MSG_DEBUG, "MSCHAPV2: password hash",
+ password, password_len);
+ 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 {
+ wpa_hexdump_ascii_key(MSG_DEBUG, "MSCHAPV2: password",
+ password, password_len);
+ 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;
+ }
+ wpa_hexdump(MSG_DEBUG, "MSCHAPV2: NT Response",
+ nt_response, MSCHAPV2_NT_RESPONSE_LEN);
+ wpa_hexdump(MSG_DEBUG, "MSCHAPV2: Auth Response",
+ auth_response, MSCHAPV2_AUTH_RESPONSE_LEN);
+
+ /* Generate master_key here since we have the needed data available. */
+ if (pwhash) {
+ if (hash_nt_password_hash(password, password_hash_hash))
+ return -1;
+ } else {
+ if (nt_password_hash(password, password_len, password_hash) ||
+ hash_nt_password_hash(password_hash, password_hash_hash))
+ return -1;
+ }
+ if (get_master_key(password_hash_hash, nt_response, master_key))
+ return -1;
+ wpa_hexdump_key(MSG_DEBUG, "MSCHAPV2: Master Key",
+ master_key, MSCHAPV2_MASTER_KEY_LEN);
+
+ return 0;
+}
+
+
+int mschapv2_verify_auth_response(const u8 *auth_response,
+ const u8 *buf, size_t buf_len)
+{
+ u8 recv_response[MSCHAPV2_AUTH_RESPONSE_LEN];
+ if (buf_len < 2 + 2 * MSCHAPV2_AUTH_RESPONSE_LEN ||
+ buf[0] != 'S' || buf[1] != '=' ||
+ hexstr2bin((char *) (buf + 2), recv_response,
+ MSCHAPV2_AUTH_RESPONSE_LEN) ||
+ os_memcmp_const(auth_response, recv_response,
+ MSCHAPV2_AUTH_RESPONSE_LEN) != 0)
+ return -1;
+ return 0;
+}
diff --git a/freebsd/contrib/wpa/src/eap_peer/mschapv2.h b/freebsd/contrib/wpa/src/eap_peer/mschapv2.h
new file mode 100644
index 00000000..edd458b4
--- /dev/null
+++ b/freebsd/contrib/wpa/src/eap_peer/mschapv2.h
@@ -0,0 +1,28 @@
+/*
+ * MSCHAPV2 (RFC 2759)
+ * Copyright (c) 2004-2008, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#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 */
diff --git a/freebsd/contrib/wpa/src/eap_peer/tncc.h b/freebsd/contrib/wpa/src/eap_peer/tncc.h
new file mode 100644
index 00000000..df2a2870
--- /dev/null
+++ b/freebsd/contrib/wpa/src/eap_peer/tncc.h
@@ -0,0 +1,36 @@
+/*
+ * EAP-TNC - TNCC (IF-IMC and IF-TNCCS)
+ * Copyright (c) 2007, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef TNCC_H
+#define TNCC_H
+
+struct tncc_data;
+
+struct tncc_data * tncc_init(void);
+void tncc_deinit(struct tncc_data *tncc);
+void tncc_init_connection(struct tncc_data *tncc);
+size_t tncc_total_send_len(struct tncc_data *tncc);
+u8 * tncc_copy_send_buf(struct tncc_data *tncc, u8 *pos);
+char * tncc_if_tnccs_start(struct tncc_data *tncc);
+char * tncc_if_tnccs_end(void);
+
+enum tncc_process_res {
+ TNCCS_PROCESS_ERROR = -1,
+ TNCCS_PROCESS_OK_NO_RECOMMENDATION = 0,
+ TNCCS_RECOMMENDATION_ERROR,
+ TNCCS_RECOMMENDATION_ALLOW,
+ TNCCS_RECOMMENDATION_NONE,
+ TNCCS_RECOMMENDATION_ISOLATE
+};
+
+enum tncc_process_res tncc_process_if_tnccs(struct tncc_data *tncc,
+ const u8 *msg, size_t len);
+
+struct wpabuf * tncc_process_soh_request(int ver, const u8 *data, size_t len);
+
+#endif /* TNCC_H */
diff --git a/freebsd/contrib/wpa/src/eap_server/eap_methods.h b/freebsd/contrib/wpa/src/eap_server/eap_methods.h
new file mode 100644
index 00000000..0baa3279
--- /dev/null
+++ b/freebsd/contrib/wpa/src/eap_server/eap_methods.h
@@ -0,0 +1,51 @@
+/*
+ * EAP server method registration
+ * Copyright (c) 2004-2009, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef EAP_SERVER_METHODS_H
+#define EAP_SERVER_METHODS_H
+
+#include "eap_common/eap_defs.h"
+
+const struct eap_method * eap_server_get_eap_method(int vendor,
+ EapType method);
+struct eap_method * eap_server_method_alloc(int version, int vendor,
+ EapType method, const char *name);
+void eap_server_method_free(struct eap_method *method);
+int eap_server_method_register(struct eap_method *method);
+
+EapType eap_server_get_type(const char *name, int *vendor);
+void eap_server_unregister_methods(void);
+const char * eap_server_get_name(int vendor, EapType type);
+
+/* EAP server method registration calls for statically linked in methods */
+int eap_server_identity_register(void);
+int eap_server_md5_register(void);
+int eap_server_tls_register(void);
+int eap_server_unauth_tls_register(void);
+int eap_server_wfa_unauth_tls_register(void);
+int eap_server_mschapv2_register(void);
+int eap_server_peap_register(void);
+int eap_server_tlv_register(void);
+int eap_server_gtc_register(void);
+int eap_server_ttls_register(void);
+int eap_server_sim_register(void);
+int eap_server_aka_register(void);
+int eap_server_aka_prime_register(void);
+int eap_server_pax_register(void);
+int eap_server_psk_register(void);
+int eap_server_sake_register(void);
+int eap_server_gpsk_register(void);
+int eap_server_vendor_test_register(void);
+int eap_server_fast_register(void);
+int eap_server_wsc_register(void);
+int eap_server_ikev2_register(void);
+int eap_server_tnc_register(void);
+int eap_server_pwd_register(void);
+int eap_server_eke_register(void);
+
+#endif /* EAP_SERVER_METHODS_H */
diff --git a/freebsd/contrib/wpa/src/eapol_supp/eapol_supp_sm.c b/freebsd/contrib/wpa/src/eapol_supp/eapol_supp_sm.c
new file mode 100644
index 00000000..e633d7c1
--- /dev/null
+++ b/freebsd/contrib/wpa/src/eapol_supp/eapol_supp_sm.c
@@ -0,0 +1,2152 @@
+#include <machine/rtems-bsd-user-space.h>
+
+/*
+ * EAPOL supplicant state machines
+ * Copyright (c) 2004-2012, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "state_machine.h"
+#include "wpabuf.h"
+#include "eloop.h"
+#include "crypto/crypto.h"
+#include "crypto/md5.h"
+#include "common/eapol_common.h"
+#include "eap_peer/eap.h"
+#include "eap_peer/eap_proxy.h"
+#include "eapol_supp_sm.h"
+
+#define STATE_MACHINE_DATA struct eapol_sm
+#define STATE_MACHINE_DEBUG_PREFIX "EAPOL"
+
+
+/* IEEE 802.1X-2004 - Supplicant - EAPOL state machines */
+
+/**
+ * struct eapol_sm - Internal data for EAPOL state machines
+ */
+struct eapol_sm {
+ /* Timers */
+ unsigned int authWhile;
+ unsigned int heldWhile;
+ unsigned int startWhen;
+ unsigned int idleWhile; /* for EAP state machine */
+ int timer_tick_enabled;
+
+ /* Global variables */
+ Boolean eapFail;
+ Boolean eapolEap;
+ Boolean eapSuccess;
+ Boolean initialize;
+ Boolean keyDone;
+ Boolean keyRun;
+ PortControl portControl;
+ Boolean portEnabled;
+ PortStatus suppPortStatus; /* dot1xSuppControlledPortStatus */
+ Boolean portValid;
+ Boolean suppAbort;
+ Boolean suppFail;
+ Boolean suppStart;
+ Boolean suppSuccess;
+ Boolean suppTimeout;
+
+ /* Supplicant PAE state machine */
+ enum {
+ SUPP_PAE_UNKNOWN = 0,
+ SUPP_PAE_DISCONNECTED = 1,
+ SUPP_PAE_LOGOFF = 2,
+ SUPP_PAE_CONNECTING = 3,
+ SUPP_PAE_AUTHENTICATING = 4,
+ SUPP_PAE_AUTHENTICATED = 5,
+ /* unused(6) */
+ SUPP_PAE_HELD = 7,
+ SUPP_PAE_RESTART = 8,
+ SUPP_PAE_S_FORCE_AUTH = 9,
+ SUPP_PAE_S_FORCE_UNAUTH = 10
+ } SUPP_PAE_state; /* dot1xSuppPaeState */
+ /* Variables */
+ Boolean userLogoff;
+ Boolean logoffSent;
+ unsigned int startCount;
+ Boolean eapRestart;
+ PortControl sPortMode;
+ /* Constants */
+ unsigned int heldPeriod; /* dot1xSuppHeldPeriod */
+ unsigned int startPeriod; /* dot1xSuppStartPeriod */
+ unsigned int maxStart; /* dot1xSuppMaxStart */
+
+ /* Key Receive state machine */
+ enum {
+ KEY_RX_UNKNOWN = 0,
+ KEY_RX_NO_KEY_RECEIVE, KEY_RX_KEY_RECEIVE
+ } KEY_RX_state;
+ /* Variables */
+ Boolean rxKey;
+
+ /* Supplicant Backend state machine */
+ enum {
+ SUPP_BE_UNKNOWN = 0,
+ SUPP_BE_INITIALIZE = 1,
+ SUPP_BE_IDLE = 2,
+ SUPP_BE_REQUEST = 3,
+ SUPP_BE_RECEIVE = 4,
+ SUPP_BE_RESPONSE = 5,
+ SUPP_BE_FAIL = 6,
+ SUPP_BE_TIMEOUT = 7,
+ SUPP_BE_SUCCESS = 8
+ } SUPP_BE_state; /* dot1xSuppBackendPaeState */
+ /* Variables */
+ Boolean eapNoResp;
+ Boolean eapReq;
+ Boolean eapResp;
+ /* Constants */
+ unsigned int authPeriod; /* dot1xSuppAuthPeriod */
+
+ /* Statistics */
+ unsigned int dot1xSuppEapolFramesRx;
+ unsigned int dot1xSuppEapolFramesTx;
+ unsigned int dot1xSuppEapolStartFramesTx;
+ unsigned int dot1xSuppEapolLogoffFramesTx;
+ unsigned int dot1xSuppEapolRespFramesTx;
+ unsigned int dot1xSuppEapolReqIdFramesRx;
+ unsigned int dot1xSuppEapolReqFramesRx;
+ unsigned int dot1xSuppInvalidEapolFramesRx;
+ unsigned int dot1xSuppEapLengthErrorFramesRx;
+ unsigned int dot1xSuppLastEapolFrameVersion;
+ unsigned char dot1xSuppLastEapolFrameSource[6];
+
+ /* Miscellaneous variables (not defined in IEEE 802.1X-2004) */
+ Boolean changed;
+ struct eap_sm *eap;
+ struct eap_peer_config *config;
+ Boolean initial_req;
+ u8 *last_rx_key;
+ size_t last_rx_key_len;
+ struct wpabuf *eapReqData; /* for EAP */
+ Boolean altAccept; /* for EAP */
+ Boolean altReject; /* for EAP */
+ Boolean eapTriggerStart;
+ Boolean replay_counter_valid;
+ u8 last_replay_counter[16];
+ struct eapol_config conf;
+ struct eapol_ctx *ctx;
+ enum { EAPOL_CB_IN_PROGRESS = 0, EAPOL_CB_SUCCESS, EAPOL_CB_FAILURE }
+ cb_status;
+ Boolean cached_pmk;
+
+ Boolean unicast_key_received, broadcast_key_received;
+
+ Boolean force_authorized_update;
+
+#ifdef CONFIG_EAP_PROXY
+ Boolean use_eap_proxy;
+ struct eap_proxy_sm *eap_proxy;
+#endif /* CONFIG_EAP_PROXY */
+};
+
+
+static void eapol_sm_txLogoff(struct eapol_sm *sm);
+static void eapol_sm_txStart(struct eapol_sm *sm);
+static void eapol_sm_processKey(struct eapol_sm *sm);
+static void eapol_sm_getSuppRsp(struct eapol_sm *sm);
+static void eapol_sm_txSuppRsp(struct eapol_sm *sm);
+static void eapol_sm_abortSupp(struct eapol_sm *sm);
+static void eapol_sm_abort_cached(struct eapol_sm *sm);
+static void eapol_sm_step_timeout(void *eloop_ctx, void *timeout_ctx);
+static void eapol_sm_set_port_authorized(struct eapol_sm *sm);
+static void eapol_sm_set_port_unauthorized(struct eapol_sm *sm);
+
+
+/* Port Timers state machine - implemented as a function that will be called
+ * once a second as a registered event loop timeout */
+static void eapol_port_timers_tick(void *eloop_ctx, void *timeout_ctx)
+{
+ struct eapol_sm *sm = timeout_ctx;
+
+ if (sm->authWhile > 0) {
+ sm->authWhile--;
+ if (sm->authWhile == 0)
+ wpa_printf(MSG_DEBUG, "EAPOL: authWhile --> 0");
+ }
+ if (sm->heldWhile > 0) {
+ sm->heldWhile--;
+ if (sm->heldWhile == 0)
+ wpa_printf(MSG_DEBUG, "EAPOL: heldWhile --> 0");
+ }
+ if (sm->startWhen > 0) {
+ sm->startWhen--;
+ if (sm->startWhen == 0)
+ wpa_printf(MSG_DEBUG, "EAPOL: startWhen --> 0");
+ }
+ if (sm->idleWhile > 0) {
+ sm->idleWhile--;
+ if (sm->idleWhile == 0)
+ wpa_printf(MSG_DEBUG, "EAPOL: idleWhile --> 0");
+ }
+
+ if (sm->authWhile | sm->heldWhile | sm->startWhen | sm->idleWhile) {
+ eloop_register_timeout(1, 0, eapol_port_timers_tick, eloop_ctx,
+ sm);
+ } else {
+ wpa_printf(MSG_DEBUG, "EAPOL: disable timer tick");
+ sm->timer_tick_enabled = 0;
+ }
+ eapol_sm_step(sm);
+}
+
+
+static void eapol_enable_timer_tick(struct eapol_sm *sm)
+{
+ if (sm->timer_tick_enabled)
+ return;
+ wpa_printf(MSG_DEBUG, "EAPOL: enable timer tick");
+ sm->timer_tick_enabled = 1;
+ eloop_cancel_timeout(eapol_port_timers_tick, NULL, sm);
+ eloop_register_timeout(1, 0, eapol_port_timers_tick, NULL, sm);
+}
+
+
+SM_STATE(SUPP_PAE, LOGOFF)
+{
+ SM_ENTRY(SUPP_PAE, LOGOFF);
+ eapol_sm_txLogoff(sm);
+ sm->logoffSent = TRUE;
+ eapol_sm_set_port_unauthorized(sm);
+}
+
+
+SM_STATE(SUPP_PAE, DISCONNECTED)
+{
+ SM_ENTRY(SUPP_PAE, DISCONNECTED);
+ sm->sPortMode = Auto;
+ sm->startCount = 0;
+ sm->eapTriggerStart = FALSE;
+ sm->logoffSent = FALSE;
+ eapol_sm_set_port_unauthorized(sm);
+ sm->suppAbort = TRUE;
+
+ sm->unicast_key_received = FALSE;
+ sm->broadcast_key_received = FALSE;
+
+ /*
+ * IEEE Std 802.1X-2004 does not clear heldWhile here, but doing so
+ * allows the timer tick to be stopped more quickly when the port is
+ * not enabled. Since this variable is used only within HELD state,
+ * clearing it on initialization does not change actual state machine
+ * behavior.
+ */
+ sm->heldWhile = 0;
+}
+
+
+SM_STATE(SUPP_PAE, CONNECTING)
+{
+ int send_start = sm->SUPP_PAE_state == SUPP_PAE_CONNECTING ||
+ sm->SUPP_PAE_state == SUPP_PAE_HELD;
+ SM_ENTRY(SUPP_PAE, CONNECTING);
+
+ if (sm->eapTriggerStart)
+ send_start = 1;
+ sm->eapTriggerStart = FALSE;
+
+ if (send_start) {
+ sm->startWhen = sm->startPeriod;
+ sm->startCount++;
+ } else {
+ /*
+ * Do not send EAPOL-Start immediately since in most cases,
+ * Authenticator is going to start authentication immediately
+ * after association and an extra EAPOL-Start is just going to
+ * delay authentication. Use a short timeout to send the first
+ * EAPOL-Start if Authenticator does not start authentication.
+ */
+ if (sm->conf.wps && !(sm->conf.wps & EAPOL_PEER_IS_WPS20_AP)) {
+ /* Reduce latency on starting WPS negotiation. */
+ wpa_printf(MSG_DEBUG,
+ "EAPOL: Using shorter startWhen for WPS");
+ sm->startWhen = 1;
+ } else {
+ sm->startWhen = 2;
+ }
+ }
+ eapol_enable_timer_tick(sm);
+ sm->eapolEap = FALSE;
+ if (send_start)
+ eapol_sm_txStart(sm);
+}
+
+
+SM_STATE(SUPP_PAE, AUTHENTICATING)
+{
+ SM_ENTRY(SUPP_PAE, AUTHENTICATING);
+ sm->startCount = 0;
+ sm->suppSuccess = FALSE;
+ sm->suppFail = FALSE;
+ sm->suppTimeout = FALSE;
+ sm->keyRun = FALSE;
+ sm->keyDone = FALSE;
+ sm->suppStart = TRUE;
+}
+
+
+SM_STATE(SUPP_PAE, HELD)
+{
+ SM_ENTRY(SUPP_PAE, HELD);
+ sm->heldWhile = sm->heldPeriod;
+ eapol_enable_timer_tick(sm);
+ eapol_sm_set_port_unauthorized(sm);
+ sm->cb_status = EAPOL_CB_FAILURE;
+}
+
+
+SM_STATE(SUPP_PAE, AUTHENTICATED)
+{
+ SM_ENTRY(SUPP_PAE, AUTHENTICATED);
+ eapol_sm_set_port_authorized(sm);
+ sm->cb_status = EAPOL_CB_SUCCESS;
+}
+
+
+SM_STATE(SUPP_PAE, RESTART)
+{
+ SM_ENTRY(SUPP_PAE, RESTART);
+ sm->eapRestart = TRUE;
+}
+
+
+SM_STATE(SUPP_PAE, S_FORCE_AUTH)
+{
+ SM_ENTRY(SUPP_PAE, S_FORCE_AUTH);
+ eapol_sm_set_port_authorized(sm);
+ sm->sPortMode = ForceAuthorized;
+}
+
+
+SM_STATE(SUPP_PAE, S_FORCE_UNAUTH)
+{
+ SM_ENTRY(SUPP_PAE, S_FORCE_UNAUTH);
+ eapol_sm_set_port_unauthorized(sm);
+ sm->sPortMode = ForceUnauthorized;
+ eapol_sm_txLogoff(sm);
+}
+
+
+SM_STEP(SUPP_PAE)
+{
+ if ((sm->userLogoff && !sm->logoffSent) &&
+ !(sm->initialize || !sm->portEnabled))
+ SM_ENTER_GLOBAL(SUPP_PAE, LOGOFF);
+ else if (((sm->portControl == Auto) &&
+ (sm->sPortMode != sm->portControl)) ||
+ sm->initialize || !sm->portEnabled)
+ SM_ENTER_GLOBAL(SUPP_PAE, DISCONNECTED);
+ else if ((sm->portControl == ForceAuthorized) &&
+ (sm->sPortMode != sm->portControl) &&
+ !(sm->initialize || !sm->portEnabled))
+ SM_ENTER_GLOBAL(SUPP_PAE, S_FORCE_AUTH);
+ else if ((sm->portControl == ForceUnauthorized) &&
+ (sm->sPortMode != sm->portControl) &&
+ !(sm->initialize || !sm->portEnabled))
+ SM_ENTER_GLOBAL(SUPP_PAE, S_FORCE_UNAUTH);
+ else switch (sm->SUPP_PAE_state) {
+ case SUPP_PAE_UNKNOWN:
+ break;
+ case SUPP_PAE_LOGOFF:
+ if (!sm->userLogoff)
+ SM_ENTER(SUPP_PAE, DISCONNECTED);
+ break;
+ case SUPP_PAE_DISCONNECTED:
+ SM_ENTER(SUPP_PAE, CONNECTING);
+ break;
+ case SUPP_PAE_CONNECTING:
+ if (sm->startWhen == 0 && sm->startCount < sm->maxStart)
+ SM_ENTER(SUPP_PAE, CONNECTING);
+ else if (sm->startWhen == 0 &&
+ sm->startCount >= sm->maxStart &&
+ sm->portValid)
+ SM_ENTER(SUPP_PAE, AUTHENTICATED);
+ else if (sm->eapSuccess || sm->eapFail)
+ SM_ENTER(SUPP_PAE, AUTHENTICATING);
+ else if (sm->eapolEap)
+ SM_ENTER(SUPP_PAE, RESTART);
+ else if (sm->startWhen == 0 &&
+ sm->startCount >= sm->maxStart &&
+ !sm->portValid)
+ SM_ENTER(SUPP_PAE, HELD);
+ break;
+ case SUPP_PAE_AUTHENTICATING:
+ if (sm->eapSuccess && !sm->portValid &&
+ sm->conf.accept_802_1x_keys &&
+ sm->conf.required_keys == 0) {
+ wpa_printf(MSG_DEBUG, "EAPOL: IEEE 802.1X for "
+ "plaintext connection; no EAPOL-Key frames "
+ "required");
+ sm->portValid = TRUE;
+ if (sm->ctx->eapol_done_cb)
+ sm->ctx->eapol_done_cb(sm->ctx->ctx);
+ }
+ if (sm->eapSuccess && sm->portValid)
+ SM_ENTER(SUPP_PAE, AUTHENTICATED);
+ else if (sm->eapFail || (sm->keyDone && !sm->portValid))
+ SM_ENTER(SUPP_PAE, HELD);
+ else if (sm->suppTimeout)
+ SM_ENTER(SUPP_PAE, CONNECTING);
+ else if (sm->eapTriggerStart)
+ SM_ENTER(SUPP_PAE, CONNECTING);
+ break;
+ case SUPP_PAE_HELD:
+ if (sm->heldWhile == 0)
+ SM_ENTER(SUPP_PAE, CONNECTING);
+ else if (sm->eapolEap)
+ SM_ENTER(SUPP_PAE, RESTART);
+ break;
+ case SUPP_PAE_AUTHENTICATED:
+ if (sm->eapolEap && sm->portValid)
+ SM_ENTER(SUPP_PAE, RESTART);
+ else if (!sm->portValid)
+ SM_ENTER(SUPP_PAE, DISCONNECTED);
+ break;
+ case SUPP_PAE_RESTART:
+ if (!sm->eapRestart)
+ SM_ENTER(SUPP_PAE, AUTHENTICATING);
+ break;
+ case SUPP_PAE_S_FORCE_AUTH:
+ break;
+ case SUPP_PAE_S_FORCE_UNAUTH:
+ break;
+ }
+}
+
+
+SM_STATE(KEY_RX, NO_KEY_RECEIVE)
+{
+ SM_ENTRY(KEY_RX, NO_KEY_RECEIVE);
+}
+
+
+SM_STATE(KEY_RX, KEY_RECEIVE)
+{
+ SM_ENTRY(KEY_RX, KEY_RECEIVE);
+ eapol_sm_processKey(sm);
+ sm->rxKey = FALSE;
+}
+
+
+SM_STEP(KEY_RX)
+{
+ if (sm->initialize || !sm->portEnabled)
+ SM_ENTER_GLOBAL(KEY_RX, NO_KEY_RECEIVE);
+ switch (sm->KEY_RX_state) {
+ case KEY_RX_UNKNOWN:
+ break;
+ case KEY_RX_NO_KEY_RECEIVE:
+ if (sm->rxKey)
+ SM_ENTER(KEY_RX, KEY_RECEIVE);
+ break;
+ case KEY_RX_KEY_RECEIVE:
+ if (sm->rxKey)
+ SM_ENTER(KEY_RX, KEY_RECEIVE);
+ break;
+ }
+}
+
+
+SM_STATE(SUPP_BE, REQUEST)
+{
+ SM_ENTRY(SUPP_BE, REQUEST);
+ sm->authWhile = 0;
+ sm->eapReq = TRUE;
+ eapol_sm_getSuppRsp(sm);
+}
+
+
+SM_STATE(SUPP_BE, RESPONSE)
+{
+ SM_ENTRY(SUPP_BE, RESPONSE);
+ eapol_sm_txSuppRsp(sm);
+ sm->eapResp = FALSE;
+}
+
+
+SM_STATE(SUPP_BE, SUCCESS)
+{
+ SM_ENTRY(SUPP_BE, SUCCESS);
+ sm->keyRun = TRUE;
+ sm->suppSuccess = TRUE;
+
+#ifdef CONFIG_EAP_PROXY
+ if (sm->use_eap_proxy) {
+ if (eap_proxy_key_available(sm->eap_proxy)) {
+ /* New key received - clear IEEE 802.1X EAPOL-Key replay
+ * counter */
+ sm->replay_counter_valid = FALSE;
+ }
+ return;
+ }
+#endif /* CONFIG_EAP_PROXY */
+
+ if (eap_key_available(sm->eap)) {
+ /* New key received - clear IEEE 802.1X EAPOL-Key replay
+ * counter */
+ sm->replay_counter_valid = FALSE;
+ }
+}
+
+
+SM_STATE(SUPP_BE, FAIL)
+{
+ SM_ENTRY(SUPP_BE, FAIL);
+ sm->suppFail = TRUE;
+}
+
+
+SM_STATE(SUPP_BE, TIMEOUT)
+{
+ SM_ENTRY(SUPP_BE, TIMEOUT);
+ sm->suppTimeout = TRUE;
+}
+
+
+SM_STATE(SUPP_BE, IDLE)
+{
+ SM_ENTRY(SUPP_BE, IDLE);
+ sm->suppStart = FALSE;
+ sm->initial_req = TRUE;
+}
+
+
+SM_STATE(SUPP_BE, INITIALIZE)
+{
+ SM_ENTRY(SUPP_BE, INITIALIZE);
+ eapol_sm_abortSupp(sm);
+ sm->suppAbort = FALSE;
+
+ /*
+ * IEEE Std 802.1X-2004 does not clear authWhile here, but doing so
+ * allows the timer tick to be stopped more quickly when the port is
+ * not enabled. Since this variable is used only within RECEIVE state,
+ * clearing it on initialization does not change actual state machine
+ * behavior.
+ */
+ sm->authWhile = 0;
+}
+
+
+SM_STATE(SUPP_BE, RECEIVE)
+{
+ SM_ENTRY(SUPP_BE, RECEIVE);
+ sm->authWhile = sm->authPeriod;
+ eapol_enable_timer_tick(sm);
+ sm->eapolEap = FALSE;
+ sm->eapNoResp = FALSE;
+ sm->initial_req = FALSE;
+}
+
+
+SM_STEP(SUPP_BE)
+{
+ if (sm->initialize || sm->suppAbort)
+ SM_ENTER_GLOBAL(SUPP_BE, INITIALIZE);
+ else switch (sm->SUPP_BE_state) {
+ case SUPP_BE_UNKNOWN:
+ break;
+ case SUPP_BE_REQUEST:
+ /*
+ * IEEE Std 802.1X-2004 has transitions from REQUEST to FAIL
+ * and SUCCESS based on eapFail and eapSuccess, respectively.
+ * However, IEEE Std 802.1X-2004 is also specifying that
+ * eapNoResp should be set in conjunction with eapSuccess and
+ * eapFail which would mean that more than one of the
+ * transitions here would be activated at the same time.
+ * Skipping RESPONSE and/or RECEIVE states in these cases can
+ * cause problems and the direct transitions to do not seem
+ * correct. Because of this, the conditions for these
+ * transitions are verified only after eapNoResp. They are
+ * unlikely to be used since eapNoResp should always be set if
+ * either of eapSuccess or eapFail is set.
+ */
+ if (sm->eapResp && sm->eapNoResp) {
+ wpa_printf(MSG_DEBUG, "EAPOL: SUPP_BE REQUEST: both "
+ "eapResp and eapNoResp set?!");
+ }
+ if (sm->eapResp)
+ SM_ENTER(SUPP_BE, RESPONSE);
+ else if (sm->eapNoResp)
+ SM_ENTER(SUPP_BE, RECEIVE);
+ else if (sm->eapFail)
+ SM_ENTER(SUPP_BE, FAIL);
+ else if (sm->eapSuccess)
+ SM_ENTER(SUPP_BE, SUCCESS);
+ break;
+ case SUPP_BE_RESPONSE:
+ SM_ENTER(SUPP_BE, RECEIVE);
+ break;
+ case SUPP_BE_SUCCESS:
+ SM_ENTER(SUPP_BE, IDLE);
+ break;
+ case SUPP_BE_FAIL:
+ SM_ENTER(SUPP_BE, IDLE);
+ break;
+ case SUPP_BE_TIMEOUT:
+ SM_ENTER(SUPP_BE, IDLE);
+ break;
+ case SUPP_BE_IDLE:
+ if (sm->eapFail && sm->suppStart)
+ SM_ENTER(SUPP_BE, FAIL);
+ else if (sm->eapolEap && sm->suppStart)
+ SM_ENTER(SUPP_BE, REQUEST);
+ else if (sm->eapSuccess && sm->suppStart)
+ SM_ENTER(SUPP_BE, SUCCESS);
+ break;
+ case SUPP_BE_INITIALIZE:
+ SM_ENTER(SUPP_BE, IDLE);
+ break;
+ case SUPP_BE_RECEIVE:
+ if (sm->eapolEap)
+ SM_ENTER(SUPP_BE, REQUEST);
+ else if (sm->eapFail)
+ SM_ENTER(SUPP_BE, FAIL);
+ else if (sm->authWhile == 0)
+ SM_ENTER(SUPP_BE, TIMEOUT);
+ else if (sm->eapSuccess)
+ SM_ENTER(SUPP_BE, SUCCESS);
+ break;
+ }
+}
+
+
+static void eapol_sm_txLogoff(struct eapol_sm *sm)
+{
+ wpa_printf(MSG_DEBUG, "EAPOL: txLogoff");
+ sm->ctx->eapol_send(sm->ctx->eapol_send_ctx,
+ IEEE802_1X_TYPE_EAPOL_LOGOFF, (u8 *) "", 0);
+ sm->dot1xSuppEapolLogoffFramesTx++;
+ sm->dot1xSuppEapolFramesTx++;
+}
+
+
+static void eapol_sm_txStart(struct eapol_sm *sm)
+{
+ wpa_printf(MSG_DEBUG, "EAPOL: txStart");
+ sm->ctx->eapol_send(sm->ctx->eapol_send_ctx,
+ IEEE802_1X_TYPE_EAPOL_START, (u8 *) "", 0);
+ sm->dot1xSuppEapolStartFramesTx++;
+ sm->dot1xSuppEapolFramesTx++;
+}
+
+
+#define IEEE8021X_ENCR_KEY_LEN 32
+#define IEEE8021X_SIGN_KEY_LEN 32
+
+struct eap_key_data {
+ u8 encr_key[IEEE8021X_ENCR_KEY_LEN];
+ u8 sign_key[IEEE8021X_SIGN_KEY_LEN];
+};
+
+
+static void eapol_sm_processKey(struct eapol_sm *sm)
+{
+#ifndef CONFIG_FIPS
+ struct ieee802_1x_hdr *hdr;
+ struct ieee802_1x_eapol_key *key;
+ struct eap_key_data keydata;
+ u8 orig_key_sign[IEEE8021X_KEY_SIGN_LEN], datakey[32];
+#ifndef CONFIG_NO_RC4
+ u8 ekey[IEEE8021X_KEY_IV_LEN + IEEE8021X_ENCR_KEY_LEN];
+#endif /* CONFIG_NO_RC4 */
+ int key_len, res, sign_key_len, encr_key_len;
+ u16 rx_key_length;
+ size_t plen;
+
+ wpa_printf(MSG_DEBUG, "EAPOL: processKey");
+ if (sm->last_rx_key == NULL)
+ return;
+
+ if (!sm->conf.accept_802_1x_keys) {
+ wpa_printf(MSG_WARNING, "EAPOL: Received IEEE 802.1X EAPOL-Key"
+ " even though this was not accepted - "
+ "ignoring this packet");
+ return;
+ }
+
+ if (sm->last_rx_key_len < sizeof(*hdr) + sizeof(*key))
+ return;
+ hdr = (struct ieee802_1x_hdr *) sm->last_rx_key;
+ key = (struct ieee802_1x_eapol_key *) (hdr + 1);
+ plen = be_to_host16(hdr->length);
+ if (sizeof(*hdr) + plen > sm->last_rx_key_len || plen < sizeof(*key)) {
+ wpa_printf(MSG_WARNING, "EAPOL: Too short EAPOL-Key frame");
+ return;
+ }
+ rx_key_length = WPA_GET_BE16(key->key_length);
+ wpa_printf(MSG_DEBUG, "EAPOL: RX IEEE 802.1X ver=%d type=%d len=%d "
+ "EAPOL-Key: type=%d key_length=%d key_index=0x%x",
+ hdr->version, hdr->type, be_to_host16(hdr->length),
+ key->type, rx_key_length, key->key_index);
+
+ eapol_sm_notify_lower_layer_success(sm, 1);
+ sign_key_len = IEEE8021X_SIGN_KEY_LEN;
+ encr_key_len = IEEE8021X_ENCR_KEY_LEN;
+ res = eapol_sm_get_key(sm, (u8 *) &keydata, sizeof(keydata));
+ if (res < 0) {
+ wpa_printf(MSG_DEBUG, "EAPOL: Could not get master key for "
+ "decrypting EAPOL-Key keys");
+ return;
+ }
+ if (res == 16) {
+ /* LEAP derives only 16 bytes of keying material. */
+ res = eapol_sm_get_key(sm, (u8 *) &keydata, 16);
+ if (res) {
+ wpa_printf(MSG_DEBUG, "EAPOL: Could not get LEAP "
+ "master key for decrypting EAPOL-Key keys");
+ return;
+ }
+ sign_key_len = 16;
+ encr_key_len = 16;
+ os_memcpy(keydata.sign_key, keydata.encr_key, 16);
+ } else if (res) {
+ wpa_printf(MSG_DEBUG, "EAPOL: Could not get enough master key "
+ "data for decrypting EAPOL-Key keys (res=%d)", res);
+ return;
+ }
+
+ /* The key replay_counter must increase when same master key */
+ if (sm->replay_counter_valid &&
+ os_memcmp(sm->last_replay_counter, key->replay_counter,
+ IEEE8021X_REPLAY_COUNTER_LEN) >= 0) {
+ wpa_printf(MSG_WARNING, "EAPOL: EAPOL-Key replay counter did "
+ "not increase - ignoring key");
+ wpa_hexdump(MSG_DEBUG, "EAPOL: last replay counter",
+ sm->last_replay_counter,
+ IEEE8021X_REPLAY_COUNTER_LEN);
+ wpa_hexdump(MSG_DEBUG, "EAPOL: received replay counter",
+ key->replay_counter, IEEE8021X_REPLAY_COUNTER_LEN);
+ return;
+ }
+
+ /* Verify key signature (HMAC-MD5) */
+ os_memcpy(orig_key_sign, key->key_signature, IEEE8021X_KEY_SIGN_LEN);
+ os_memset(key->key_signature, 0, IEEE8021X_KEY_SIGN_LEN);
+ hmac_md5(keydata.sign_key, sign_key_len,
+ sm->last_rx_key, sizeof(*hdr) + be_to_host16(hdr->length),
+ key->key_signature);
+ if (os_memcmp_const(orig_key_sign, key->key_signature,
+ IEEE8021X_KEY_SIGN_LEN) != 0) {
+ wpa_printf(MSG_DEBUG, "EAPOL: Invalid key signature in "
+ "EAPOL-Key packet");
+ os_memcpy(key->key_signature, orig_key_sign,
+ IEEE8021X_KEY_SIGN_LEN);
+ return;
+ }
+ wpa_printf(MSG_DEBUG, "EAPOL: EAPOL-Key key signature verified");
+
+ key_len = plen - sizeof(*key);
+ if (key_len > 32 || rx_key_length > 32) {
+ wpa_printf(MSG_WARNING, "EAPOL: Too long key data length %d",
+ key_len ? key_len : rx_key_length);
+ return;
+ }
+ if (key_len == rx_key_length) {
+#ifdef CONFIG_NO_RC4
+ if (encr_key_len) {
+ /* otherwise unused */
+ }
+ wpa_printf(MSG_ERROR, "EAPOL: RC4 not supported in the build");
+ return;
+#else /* CONFIG_NO_RC4 */
+ os_memcpy(ekey, key->key_iv, IEEE8021X_KEY_IV_LEN);
+ os_memcpy(ekey + IEEE8021X_KEY_IV_LEN, keydata.encr_key,
+ encr_key_len);
+ os_memcpy(datakey, key + 1, key_len);
+ rc4_skip(ekey, IEEE8021X_KEY_IV_LEN + encr_key_len, 0,
+ datakey, key_len);
+ wpa_hexdump_key(MSG_DEBUG, "EAPOL: Decrypted(RC4) key",
+ datakey, key_len);
+#endif /* CONFIG_NO_RC4 */
+ } else if (key_len == 0) {
+ /*
+ * IEEE 802.1X-2004 specifies that least significant Key Length
+ * octets from MS-MPPE-Send-Key are used as the key if the key
+ * data is not present. This seems to be meaning the beginning
+ * of the MS-MPPE-Send-Key. In addition, MS-MPPE-Send-Key in
+ * Supplicant corresponds to MS-MPPE-Recv-Key in Authenticator.
+ * Anyway, taking the beginning of the keying material from EAP
+ * seems to interoperate with Authenticators.
+ */
+ key_len = rx_key_length;
+ os_memcpy(datakey, keydata.encr_key, key_len);
+ wpa_hexdump_key(MSG_DEBUG, "EAPOL: using part of EAP keying "
+ "material data encryption key",
+ datakey, key_len);
+ } else {
+ wpa_printf(MSG_DEBUG, "EAPOL: Invalid key data length %d "
+ "(key_length=%d)", key_len, rx_key_length);
+ return;
+ }
+
+ sm->replay_counter_valid = TRUE;
+ os_memcpy(sm->last_replay_counter, key->replay_counter,
+ IEEE8021X_REPLAY_COUNTER_LEN);
+
+ wpa_printf(MSG_DEBUG, "EAPOL: Setting dynamic WEP key: %s keyidx %d "
+ "len %d",
+ key->key_index & IEEE8021X_KEY_INDEX_FLAG ?
+ "unicast" : "broadcast",
+ key->key_index & IEEE8021X_KEY_INDEX_MASK, key_len);
+
+ if (sm->ctx->set_wep_key &&
+ sm->ctx->set_wep_key(sm->ctx->ctx,
+ key->key_index & IEEE8021X_KEY_INDEX_FLAG,
+ key->key_index & IEEE8021X_KEY_INDEX_MASK,
+ datakey, key_len) < 0) {
+ wpa_printf(MSG_WARNING, "EAPOL: Failed to set WEP key to the "
+ " driver.");
+ } else {
+ if (key->key_index & IEEE8021X_KEY_INDEX_FLAG)
+ sm->unicast_key_received = TRUE;
+ else
+ sm->broadcast_key_received = TRUE;
+
+ if ((sm->unicast_key_received ||
+ !(sm->conf.required_keys & EAPOL_REQUIRE_KEY_UNICAST)) &&
+ (sm->broadcast_key_received ||
+ !(sm->conf.required_keys & EAPOL_REQUIRE_KEY_BROADCAST)))
+ {
+ wpa_printf(MSG_DEBUG, "EAPOL: all required EAPOL-Key "
+ "frames received");
+ sm->portValid = TRUE;
+ if (sm->ctx->eapol_done_cb)
+ sm->ctx->eapol_done_cb(sm->ctx->ctx);
+ }
+ }
+#endif /* CONFIG_FIPS */
+}
+
+
+static void eapol_sm_getSuppRsp(struct eapol_sm *sm)
+{
+ wpa_printf(MSG_DEBUG, "EAPOL: getSuppRsp");
+ /* EAP layer processing; no special code is needed, since Supplicant
+ * Backend state machine is waiting for eapNoResp or eapResp to be set
+ * and these are only set in the EAP state machine when the processing
+ * has finished. */
+}
+
+
+static void eapol_sm_txSuppRsp(struct eapol_sm *sm)
+{
+ struct wpabuf *resp;
+
+ wpa_printf(MSG_DEBUG, "EAPOL: txSuppRsp");
+
+#ifdef CONFIG_EAP_PROXY
+ if (sm->use_eap_proxy) {
+ /* Get EAP Response from EAP Proxy */
+ resp = eap_proxy_get_eapRespData(sm->eap_proxy);
+ if (resp == NULL) {
+ wpa_printf(MSG_WARNING, "EAPOL: txSuppRsp - EAP Proxy "
+ "response data not available");
+ return;
+ }
+ } else
+#endif /* CONFIG_EAP_PROXY */
+
+ resp = eap_get_eapRespData(sm->eap);
+ if (resp == NULL) {
+ wpa_printf(MSG_WARNING, "EAPOL: txSuppRsp - EAP response data "
+ "not available");
+ return;
+ }
+
+ /* Send EAP-Packet from the EAP layer to the Authenticator */
+ sm->ctx->eapol_send(sm->ctx->eapol_send_ctx,
+ IEEE802_1X_TYPE_EAP_PACKET, wpabuf_head(resp),
+ wpabuf_len(resp));
+
+ /* eapRespData is not used anymore, so free it here */
+ wpabuf_free(resp);
+
+ if (sm->initial_req)
+ sm->dot1xSuppEapolReqIdFramesRx++;
+ else
+ sm->dot1xSuppEapolReqFramesRx++;
+ sm->dot1xSuppEapolRespFramesTx++;
+ sm->dot1xSuppEapolFramesTx++;
+}
+
+
+static void eapol_sm_abortSupp(struct eapol_sm *sm)
+{
+ /* release system resources that may have been allocated for the
+ * authentication session */
+ os_free(sm->last_rx_key);
+ sm->last_rx_key = NULL;
+ wpabuf_free(sm->eapReqData);
+ sm->eapReqData = NULL;
+ eap_sm_abort(sm->eap);
+}
+
+
+static void eapol_sm_step_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+ eapol_sm_step(timeout_ctx);
+}
+
+
+static void eapol_sm_set_port_authorized(struct eapol_sm *sm)
+{
+ int cb;
+
+ cb = sm->suppPortStatus != Authorized || sm->force_authorized_update;
+ sm->force_authorized_update = FALSE;
+ sm->suppPortStatus = Authorized;
+ if (cb && sm->ctx->port_cb)
+ sm->ctx->port_cb(sm->ctx->ctx, 1);
+}
+
+
+static void eapol_sm_set_port_unauthorized(struct eapol_sm *sm)
+{
+ int cb;
+
+ cb = sm->suppPortStatus != Unauthorized || sm->force_authorized_update;
+ sm->force_authorized_update = FALSE;
+ sm->suppPortStatus = Unauthorized;
+ if (cb && sm->ctx->port_cb)
+ sm->ctx->port_cb(sm->ctx->ctx, 0);
+}
+
+
+/**
+ * eapol_sm_step - EAPOL state machine step function
+ * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init()
+ *
+ * This function is called to notify the state machine about changed external
+ * variables. It will step through the EAPOL state machines in loop to process
+ * all triggered state changes.
+ */
+void eapol_sm_step(struct eapol_sm *sm)
+{
+ int i;
+
+ /* In theory, it should be ok to run this in loop until !changed.
+ * However, it is better to use a limit on number of iterations to
+ * allow events (e.g., SIGTERM) to stop the program cleanly if the
+ * state machine were to generate a busy loop. */
+ for (i = 0; i < 100; i++) {
+ sm->changed = FALSE;
+ SM_STEP_RUN(SUPP_PAE);
+ SM_STEP_RUN(KEY_RX);
+ SM_STEP_RUN(SUPP_BE);
+#ifdef CONFIG_EAP_PROXY
+ if (sm->use_eap_proxy) {
+ /* Drive the EAP proxy state machine */
+ if (eap_proxy_sm_step(sm->eap_proxy, sm->eap))
+ sm->changed = TRUE;
+ } else
+#endif /* CONFIG_EAP_PROXY */
+ if (eap_peer_sm_step(sm->eap))
+ sm->changed = TRUE;
+ if (!sm->changed)
+ break;
+ }
+
+ if (sm->changed) {
+ /* restart EAPOL state machine step from timeout call in order
+ * to allow other events to be processed. */
+ eloop_cancel_timeout(eapol_sm_step_timeout, NULL, sm);
+ eloop_register_timeout(0, 0, eapol_sm_step_timeout, NULL, sm);
+ }
+
+ if (sm->ctx->cb && sm->cb_status != EAPOL_CB_IN_PROGRESS) {
+ enum eapol_supp_result result;
+ if (sm->cb_status == EAPOL_CB_SUCCESS)
+ result = EAPOL_SUPP_RESULT_SUCCESS;
+ else if (eap_peer_was_failure_expected(sm->eap))
+ result = EAPOL_SUPP_RESULT_EXPECTED_FAILURE;
+ else
+ result = EAPOL_SUPP_RESULT_FAILURE;
+ sm->cb_status = EAPOL_CB_IN_PROGRESS;
+ sm->ctx->cb(sm, result, sm->ctx->cb_ctx);
+ }
+}
+
+
+#ifdef CONFIG_CTRL_IFACE
+static const char *eapol_supp_pae_state(int state)
+{
+ switch (state) {
+ case SUPP_PAE_LOGOFF:
+ return "LOGOFF";
+ case SUPP_PAE_DISCONNECTED:
+ return "DISCONNECTED";
+ case SUPP_PAE_CONNECTING:
+ return "CONNECTING";
+ case SUPP_PAE_AUTHENTICATING:
+ return "AUTHENTICATING";
+ case SUPP_PAE_HELD:
+ return "HELD";
+ case SUPP_PAE_AUTHENTICATED:
+ return "AUTHENTICATED";
+ case SUPP_PAE_RESTART:
+ return "RESTART";
+ default:
+ return "UNKNOWN";
+ }
+}
+
+
+static const char *eapol_supp_be_state(int state)
+{
+ switch (state) {
+ case SUPP_BE_REQUEST:
+ return "REQUEST";
+ case SUPP_BE_RESPONSE:
+ return "RESPONSE";
+ case SUPP_BE_SUCCESS:
+ return "SUCCESS";
+ case SUPP_BE_FAIL:
+ return "FAIL";
+ case SUPP_BE_TIMEOUT:
+ return "TIMEOUT";
+ case SUPP_BE_IDLE:
+ return "IDLE";
+ case SUPP_BE_INITIALIZE:
+ return "INITIALIZE";
+ case SUPP_BE_RECEIVE:
+ return "RECEIVE";
+ default:
+ return "UNKNOWN";
+ }
+}
+
+
+static const char * eapol_port_status(PortStatus status)
+{
+ if (status == Authorized)
+ return "Authorized";
+ else
+ return "Unauthorized";
+}
+#endif /* CONFIG_CTRL_IFACE */
+
+
+#if defined(CONFIG_CTRL_IFACE) || !defined(CONFIG_NO_STDOUT_DEBUG)
+static const char * eapol_port_control(PortControl ctrl)
+{
+ switch (ctrl) {
+ case Auto:
+ return "Auto";
+ case ForceUnauthorized:
+ return "ForceUnauthorized";
+ case ForceAuthorized:
+ return "ForceAuthorized";
+ default:
+ return "Unknown";
+ }
+}
+#endif /* CONFIG_CTRL_IFACE || !CONFIG_NO_STDOUT_DEBUG */
+
+
+/**
+ * eapol_sm_configure - Set EAPOL variables
+ * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init()
+ * @heldPeriod: dot1xSuppHeldPeriod
+ * @authPeriod: dot1xSuppAuthPeriod
+ * @startPeriod: dot1xSuppStartPeriod
+ * @maxStart: dot1xSuppMaxStart
+ *
+ * Set configurable EAPOL state machine variables. Each variable can be set to
+ * the given value or ignored if set to -1 (to set only some of the variables).
+ */
+void eapol_sm_configure(struct eapol_sm *sm, int heldPeriod, int authPeriod,
+ int startPeriod, int maxStart)
+{
+ if (sm == NULL)
+ return;
+ if (heldPeriod >= 0)
+ sm->heldPeriod = heldPeriod;
+ if (authPeriod >= 0)
+ sm->authPeriod = authPeriod;
+ if (startPeriod >= 0)
+ sm->startPeriod = startPeriod;
+ if (maxStart >= 0)
+ sm->maxStart = maxStart;
+}
+
+
+/**
+ * eapol_sm_get_method_name - Get EAPOL method name
+ * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init()
+ * Returns: Static string containing name of current eap method or NULL
+ */
+const char * eapol_sm_get_method_name(struct eapol_sm *sm)
+{
+ if (sm->SUPP_PAE_state != SUPP_PAE_AUTHENTICATED ||
+ sm->suppPortStatus != Authorized)
+ return NULL;
+
+ return eap_sm_get_method_name(sm->eap);
+}
+
+
+#ifdef CONFIG_CTRL_IFACE
+/**
+ * eapol_sm_get_status - Get EAPOL state machine status
+ * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init()
+ * @buf: Buffer for status information
+ * @buflen: Maximum buffer length
+ * @verbose: Whether to include verbose status information
+ * Returns: Number of bytes written to buf.
+ *
+ * Query EAPOL state machine for status information. This function fills in a
+ * text area with current status information from the EAPOL state machine. If
+ * the buffer (buf) is not large enough, status information will be truncated
+ * to fit the buffer.
+ */
+int eapol_sm_get_status(struct eapol_sm *sm, char *buf, size_t buflen,
+ int verbose)
+{
+ int len, ret;
+ if (sm == NULL)
+ return 0;
+
+ len = os_snprintf(buf, buflen,
+ "Supplicant PAE state=%s\n"
+ "suppPortStatus=%s\n",
+ eapol_supp_pae_state(sm->SUPP_PAE_state),
+ eapol_port_status(sm->suppPortStatus));
+ if (os_snprintf_error(buflen, len))
+ return 0;
+
+ if (verbose) {
+ ret = os_snprintf(buf + len, buflen - len,
+ "heldPeriod=%u\n"
+ "authPeriod=%u\n"
+ "startPeriod=%u\n"
+ "maxStart=%u\n"
+ "portControl=%s\n"
+ "Supplicant Backend state=%s\n",
+ sm->heldPeriod,
+ sm->authPeriod,
+ sm->startPeriod,
+ sm->maxStart,
+ eapol_port_control(sm->portControl),
+ eapol_supp_be_state(sm->SUPP_BE_state));
+ if (os_snprintf_error(buflen - len, ret))
+ return len;
+ len += ret;
+ }
+
+#ifdef CONFIG_EAP_PROXY
+ if (sm->use_eap_proxy)
+ len += eap_proxy_sm_get_status(sm->eap_proxy,
+ buf + len, buflen - len,
+ verbose);
+ else
+#endif /* CONFIG_EAP_PROXY */
+ len += eap_sm_get_status(sm->eap, buf + len, buflen - len, verbose);
+
+ return len;
+}
+
+
+/**
+ * eapol_sm_get_mib - Get EAPOL state machine MIBs
+ * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init()
+ * @buf: Buffer for MIB information
+ * @buflen: Maximum buffer length
+ * Returns: Number of bytes written to buf.
+ *
+ * Query EAPOL state machine for MIB information. This function fills in a
+ * text area with current MIB information from the EAPOL state machine. If
+ * the buffer (buf) is not large enough, MIB information will be truncated to
+ * fit the buffer.
+ */
+int eapol_sm_get_mib(struct eapol_sm *sm, char *buf, size_t buflen)
+{
+ size_t len;
+ int ret;
+
+ if (sm == NULL)
+ return 0;
+ ret = os_snprintf(buf, buflen,
+ "dot1xSuppPaeState=%d\n"
+ "dot1xSuppHeldPeriod=%u\n"
+ "dot1xSuppAuthPeriod=%u\n"
+ "dot1xSuppStartPeriod=%u\n"
+ "dot1xSuppMaxStart=%u\n"
+ "dot1xSuppSuppControlledPortStatus=%s\n"
+ "dot1xSuppBackendPaeState=%d\n",
+ sm->SUPP_PAE_state,
+ sm->heldPeriod,
+ sm->authPeriod,
+ sm->startPeriod,
+ sm->maxStart,
+ sm->suppPortStatus == Authorized ?
+ "Authorized" : "Unauthorized",
+ sm->SUPP_BE_state);
+
+ if (os_snprintf_error(buflen, ret))
+ return 0;
+ len = ret;
+
+ ret = os_snprintf(buf + len, buflen - len,
+ "dot1xSuppEapolFramesRx=%u\n"
+ "dot1xSuppEapolFramesTx=%u\n"
+ "dot1xSuppEapolStartFramesTx=%u\n"
+ "dot1xSuppEapolLogoffFramesTx=%u\n"
+ "dot1xSuppEapolRespFramesTx=%u\n"
+ "dot1xSuppEapolReqIdFramesRx=%u\n"
+ "dot1xSuppEapolReqFramesRx=%u\n"
+ "dot1xSuppInvalidEapolFramesRx=%u\n"
+ "dot1xSuppEapLengthErrorFramesRx=%u\n"
+ "dot1xSuppLastEapolFrameVersion=%u\n"
+ "dot1xSuppLastEapolFrameSource=" MACSTR "\n",
+ sm->dot1xSuppEapolFramesRx,
+ sm->dot1xSuppEapolFramesTx,
+ sm->dot1xSuppEapolStartFramesTx,
+ sm->dot1xSuppEapolLogoffFramesTx,
+ sm->dot1xSuppEapolRespFramesTx,
+ sm->dot1xSuppEapolReqIdFramesRx,
+ sm->dot1xSuppEapolReqFramesRx,
+ sm->dot1xSuppInvalidEapolFramesRx,
+ sm->dot1xSuppEapLengthErrorFramesRx,
+ sm->dot1xSuppLastEapolFrameVersion,
+ MAC2STR(sm->dot1xSuppLastEapolFrameSource));
+
+ if (os_snprintf_error(buflen - len, ret))
+ return len;
+ len += ret;
+
+ return len;
+}
+#endif /* CONFIG_CTRL_IFACE */
+
+
+/**
+ * eapol_sm_rx_eapol - Process received EAPOL frames
+ * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init()
+ * @src: Source MAC address of the EAPOL packet
+ * @buf: Pointer to the beginning of the EAPOL data (EAPOL header)
+ * @len: Length of the EAPOL frame
+ * Returns: 1 = EAPOL frame processed, 0 = not for EAPOL state machine,
+ * -1 failure
+ */
+int eapol_sm_rx_eapol(struct eapol_sm *sm, const u8 *src, const u8 *buf,
+ size_t len)
+{
+ const struct ieee802_1x_hdr *hdr;
+ const struct ieee802_1x_eapol_key *key;
+ int data_len;
+ int res = 1;
+ size_t plen;
+
+ if (sm == NULL)
+ return 0;
+ sm->dot1xSuppEapolFramesRx++;
+ if (len < sizeof(*hdr)) {
+ sm->dot1xSuppInvalidEapolFramesRx++;
+ return 0;
+ }
+ hdr = (const struct ieee802_1x_hdr *) buf;
+ sm->dot1xSuppLastEapolFrameVersion = hdr->version;
+ os_memcpy(sm->dot1xSuppLastEapolFrameSource, src, ETH_ALEN);
+ if (hdr->version < EAPOL_VERSION) {
+ /* TODO: backwards compatibility */
+ }
+ plen = be_to_host16(hdr->length);
+ if (plen > len - sizeof(*hdr)) {
+ sm->dot1xSuppEapLengthErrorFramesRx++;
+ return 0;
+ }
+#ifdef CONFIG_WPS
+ if (sm->conf.wps && sm->conf.workaround &&
+ plen < len - sizeof(*hdr) &&
+ hdr->type == IEEE802_1X_TYPE_EAP_PACKET &&
+ len - sizeof(*hdr) > sizeof(struct eap_hdr)) {
+ const struct eap_hdr *ehdr =
+ (const struct eap_hdr *) (hdr + 1);
+ u16 elen;
+
+ elen = be_to_host16(ehdr->length);
+ if (elen > plen && elen <= len - sizeof(*hdr)) {
+ /*
+ * Buffalo WHR-G125 Ver.1.47 seems to send EAP-WPS
+ * packets with too short EAPOL header length field
+ * (14 octets). This is fixed in firmware Ver.1.49.
+ * As a workaround, fix the EAPOL header based on the
+ * correct length in the EAP packet.
+ */
+ wpa_printf(MSG_DEBUG, "EAPOL: Workaround - fix EAPOL "
+ "payload length based on EAP header: "
+ "%d -> %d", (int) plen, elen);
+ plen = elen;
+ }
+ }
+#endif /* CONFIG_WPS */
+ data_len = plen + sizeof(*hdr);
+
+ switch (hdr->type) {
+ case IEEE802_1X_TYPE_EAP_PACKET:
+ if (sm->conf.workaround) {
+ /*
+ * An AP has been reported to send out EAP message with
+ * undocumented code 10 at some point near the
+ * completion of EAP authentication. This can result in
+ * issues with the unexpected EAP message triggering
+ * restart of EAPOL authentication. Avoid this by
+ * skipping the message without advancing the state
+ * machine.
+ */
+ const struct eap_hdr *ehdr =
+ (const struct eap_hdr *) (hdr + 1);
+ if (plen >= sizeof(*ehdr) && ehdr->code == 10) {
+ wpa_printf(MSG_DEBUG, "EAPOL: Ignore EAP packet with unknown code 10");
+ break;
+ }
+ }
+
+ if (sm->cached_pmk) {
+ /* Trying to use PMKSA caching, but Authenticator did
+ * not seem to have a matching entry. Need to restart
+ * EAPOL state machines.
+ */
+ eapol_sm_abort_cached(sm);
+ }
+ wpabuf_free(sm->eapReqData);
+ sm->eapReqData = wpabuf_alloc_copy(hdr + 1, plen);
+ if (sm->eapReqData) {
+ wpa_printf(MSG_DEBUG, "EAPOL: Received EAP-Packet "
+ "frame");
+ sm->eapolEap = TRUE;
+#ifdef CONFIG_EAP_PROXY
+ if (sm->use_eap_proxy) {
+ eap_proxy_packet_update(
+ sm->eap_proxy,
+ wpabuf_mhead_u8(sm->eapReqData),
+ wpabuf_len(sm->eapReqData));
+ wpa_printf(MSG_DEBUG, "EAPOL: eap_proxy "
+ "EAP Req updated");
+ }
+#endif /* CONFIG_EAP_PROXY */
+ eapol_sm_step(sm);
+ }
+ break;
+ case IEEE802_1X_TYPE_EAPOL_KEY:
+ if (plen < sizeof(*key)) {
+ wpa_printf(MSG_DEBUG, "EAPOL: Too short EAPOL-Key "
+ "frame received");
+ break;
+ }
+ key = (const struct ieee802_1x_eapol_key *) (hdr + 1);
+ if (key->type == EAPOL_KEY_TYPE_WPA ||
+ key->type == EAPOL_KEY_TYPE_RSN) {
+ /* WPA Supplicant takes care of this frame. */
+ wpa_printf(MSG_DEBUG, "EAPOL: Ignoring WPA EAPOL-Key "
+ "frame in EAPOL state machines");
+ res = 0;
+ break;
+ }
+ if (key->type != EAPOL_KEY_TYPE_RC4) {
+ wpa_printf(MSG_DEBUG, "EAPOL: Ignored unknown "
+ "EAPOL-Key type %d", key->type);
+ break;
+ }
+ os_free(sm->last_rx_key);
+ sm->last_rx_key = os_malloc(data_len);
+ if (sm->last_rx_key) {
+ wpa_printf(MSG_DEBUG, "EAPOL: Received EAPOL-Key "
+ "frame");
+ os_memcpy(sm->last_rx_key, buf, data_len);
+ sm->last_rx_key_len = data_len;
+ sm->rxKey = TRUE;
+ eapol_sm_step(sm);
+ }
+ break;
+#ifdef CONFIG_MACSEC
+ case IEEE802_1X_TYPE_EAPOL_MKA:
+ wpa_printf(MSG_EXCESSIVE,
+ "EAPOL type %d will be handled by MKA",
+ hdr->type);
+ break;
+#endif /* CONFIG_MACSEC */
+ default:
+ wpa_printf(MSG_DEBUG, "EAPOL: Received unknown EAPOL type %d",
+ hdr->type);
+ sm->dot1xSuppInvalidEapolFramesRx++;
+ break;
+ }
+
+ return res;
+}
+
+
+/**
+ * eapol_sm_notify_tx_eapol_key - Notification about transmitted EAPOL packet
+ * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init()
+ *
+ * Notify EAPOL state machine about transmitted EAPOL packet from an external
+ * component, e.g., WPA. This will update the statistics.
+ */
+void eapol_sm_notify_tx_eapol_key(struct eapol_sm *sm)
+{
+ if (sm)
+ sm->dot1xSuppEapolFramesTx++;
+}
+
+
+/**
+ * eapol_sm_notify_portEnabled - Notification about portEnabled change
+ * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init()
+ * @enabled: New portEnabled value
+ *
+ * Notify EAPOL state machine about new portEnabled value.
+ */
+void eapol_sm_notify_portEnabled(struct eapol_sm *sm, Boolean enabled)
+{
+ if (sm == NULL)
+ return;
+ wpa_printf(MSG_DEBUG, "EAPOL: External notification - "
+ "portEnabled=%d", enabled);
+ if (sm->portEnabled != enabled)
+ sm->force_authorized_update = TRUE;
+ sm->portEnabled = enabled;
+ eapol_sm_step(sm);
+}
+
+
+/**
+ * eapol_sm_notify_portValid - Notification about portValid change
+ * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init()
+ * @valid: New portValid value
+ *
+ * Notify EAPOL state machine about new portValid value.
+ */
+void eapol_sm_notify_portValid(struct eapol_sm *sm, Boolean valid)
+{
+ if (sm == NULL)
+ return;
+ wpa_printf(MSG_DEBUG, "EAPOL: External notification - "
+ "portValid=%d", valid);
+ sm->portValid = valid;
+ eapol_sm_step(sm);
+}
+
+
+/**
+ * eapol_sm_notify_eap_success - Notification of external EAP success trigger
+ * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init()
+ * @success: %TRUE = set success, %FALSE = clear success
+ *
+ * Notify the EAPOL state machine that external event has forced EAP state to
+ * success (success = %TRUE). This can be cleared by setting success = %FALSE.
+ *
+ * This function is called to update EAP state when WPA-PSK key handshake has
+ * been completed successfully since WPA-PSK does not use EAP state machine.
+ */
+void eapol_sm_notify_eap_success(struct eapol_sm *sm, Boolean success)
+{
+ if (sm == NULL)
+ return;
+ wpa_printf(MSG_DEBUG, "EAPOL: External notification - "
+ "EAP success=%d", success);
+ sm->eapSuccess = success;
+ sm->altAccept = success;
+ if (success)
+ eap_notify_success(sm->eap);
+ eapol_sm_step(sm);
+}
+
+
+/**
+ * eapol_sm_notify_eap_fail - Notification of external EAP failure trigger
+ * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init()
+ * @fail: %TRUE = set failure, %FALSE = clear failure
+ *
+ * Notify EAPOL state machine that external event has forced EAP state to
+ * failure (fail = %TRUE). This can be cleared by setting fail = %FALSE.
+ */
+void eapol_sm_notify_eap_fail(struct eapol_sm *sm, Boolean fail)
+{
+ if (sm == NULL)
+ return;
+ wpa_printf(MSG_DEBUG, "EAPOL: External notification - "
+ "EAP fail=%d", fail);
+ sm->eapFail = fail;
+ sm->altReject = fail;
+ eapol_sm_step(sm);
+}
+
+
+/**
+ * eapol_sm_notify_config - Notification of EAPOL configuration change
+ * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init()
+ * @config: Pointer to current network EAP configuration
+ * @conf: Pointer to EAPOL configuration data
+ *
+ * Notify EAPOL state machine that configuration has changed. config will be
+ * stored as a backpointer to network configuration. This can be %NULL to clear
+ * the stored pointed. conf will be copied to local EAPOL/EAP configuration
+ * data. If conf is %NULL, this part of the configuration change will be
+ * skipped.
+ */
+void eapol_sm_notify_config(struct eapol_sm *sm,
+ struct eap_peer_config *config,
+ const struct eapol_config *conf)
+{
+ if (sm == NULL)
+ return;
+
+ sm->config = config;
+#ifdef CONFIG_EAP_PROXY
+ sm->use_eap_proxy = eap_proxy_notify_config(sm->eap_proxy, config) > 0;
+#endif /* CONFIG_EAP_PROXY */
+
+ if (conf == NULL)
+ return;
+
+ sm->conf.accept_802_1x_keys = conf->accept_802_1x_keys;
+ sm->conf.required_keys = conf->required_keys;
+ sm->conf.fast_reauth = conf->fast_reauth;
+ sm->conf.workaround = conf->workaround;
+ sm->conf.wps = conf->wps;
+#ifdef CONFIG_EAP_PROXY
+ if (sm->use_eap_proxy) {
+ /* Using EAP Proxy, so skip EAP state machine update */
+ return;
+ }
+#endif /* CONFIG_EAP_PROXY */
+ if (sm->eap) {
+ eap_set_fast_reauth(sm->eap, conf->fast_reauth);
+ eap_set_workaround(sm->eap, conf->workaround);
+ eap_set_force_disabled(sm->eap, conf->eap_disabled);
+ eap_set_external_sim(sm->eap, conf->external_sim);
+ }
+}
+
+
+/**
+ * eapol_sm_get_key - Get master session key (MSK) from EAP
+ * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init()
+ * @key: Pointer for key buffer
+ * @len: Number of bytes to copy to key
+ * Returns: 0 on success (len of key available), maximum available key len
+ * (>0) if key is available but it is shorter than len, or -1 on failure.
+ *
+ * Fetch EAP keying material (MSK, eapKeyData) from EAP state machine. The key
+ * is available only after a successful authentication.
+ */
+int eapol_sm_get_key(struct eapol_sm *sm, u8 *key, size_t len)
+{
+ const u8 *eap_key;
+ size_t eap_len;
+
+#ifdef CONFIG_EAP_PROXY
+ if (sm && sm->use_eap_proxy) {
+ /* Get key from EAP proxy */
+ if (sm == NULL || !eap_proxy_key_available(sm->eap_proxy)) {
+ wpa_printf(MSG_DEBUG, "EAPOL: EAP key not available");
+ return -1;
+ }
+ eap_key = eap_proxy_get_eapKeyData(sm->eap_proxy, &eap_len);
+ if (eap_key == NULL) {
+ wpa_printf(MSG_DEBUG, "EAPOL: Failed to get "
+ "eapKeyData");
+ return -1;
+ }
+ goto key_fetched;
+ }
+#endif /* CONFIG_EAP_PROXY */
+ if (sm == NULL || !eap_key_available(sm->eap)) {
+ wpa_printf(MSG_DEBUG, "EAPOL: EAP key not available");
+ return -1;
+ }
+ eap_key = eap_get_eapKeyData(sm->eap, &eap_len);
+ if (eap_key == NULL) {
+ wpa_printf(MSG_DEBUG, "EAPOL: Failed to get eapKeyData");
+ return -1;
+ }
+#ifdef CONFIG_EAP_PROXY
+key_fetched:
+#endif /* CONFIG_EAP_PROXY */
+ if (len > eap_len) {
+ wpa_printf(MSG_DEBUG, "EAPOL: Requested key length (%lu) not "
+ "available (len=%lu)",
+ (unsigned long) len, (unsigned long) eap_len);
+ return eap_len;
+ }
+ os_memcpy(key, eap_key, len);
+ wpa_printf(MSG_DEBUG, "EAPOL: Successfully fetched key (len=%lu)",
+ (unsigned long) len);
+ return 0;
+}
+
+
+/**
+ * eapol_sm_get_session_id - Get EAP Session-Id
+ * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init()
+ * @len: Pointer to variable that will be set to number of bytes in the session
+ * Returns: Pointer to the EAP Session-Id or %NULL on failure
+ *
+ * The Session-Id is available only after a successful authentication.
+ */
+const u8 * eapol_sm_get_session_id(struct eapol_sm *sm, size_t *len)
+{
+ if (sm == NULL || !eap_key_available(sm->eap)) {
+ wpa_printf(MSG_DEBUG, "EAPOL: EAP Session-Id not available");
+ return NULL;
+ }
+ return eap_get_eapSessionId(sm->eap, len);
+}
+
+
+/**
+ * eapol_sm_notify_logoff - Notification of logon/logoff commands
+ * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init()
+ * @logoff: Whether command was logoff
+ *
+ * Notify EAPOL state machines that user requested logon/logoff.
+ */
+void eapol_sm_notify_logoff(struct eapol_sm *sm, Boolean logoff)
+{
+ if (sm) {
+ sm->userLogoff = logoff;
+ if (!logoff) {
+ /* If there is a delayed txStart queued, start now. */
+ sm->startWhen = 0;
+ }
+ eapol_sm_step(sm);
+ }
+}
+
+
+/**
+ * eapol_sm_notify_pmkid_attempt - Notification of successful PMKSA caching
+ * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init()
+ *
+ * Notify EAPOL state machines that PMKSA caching was successful. This is used
+ * to move EAPOL and EAP state machines into authenticated/successful state.
+ */
+void eapol_sm_notify_cached(struct eapol_sm *sm)
+{
+ if (sm == NULL)
+ return;
+ wpa_printf(MSG_DEBUG, "EAPOL: PMKSA caching was used - skip EAPOL");
+ sm->eapSuccess = TRUE;
+ eap_notify_success(sm->eap);
+ eapol_sm_step(sm);
+}
+
+
+/**
+ * eapol_sm_notify_pmkid_attempt - Notification of PMKSA caching
+ * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init()
+ *
+ * Notify EAPOL state machines if PMKSA caching is used.
+ */
+void eapol_sm_notify_pmkid_attempt(struct eapol_sm *sm)
+{
+ if (sm == NULL)
+ return;
+ wpa_printf(MSG_DEBUG, "RSN: Trying to use cached PMKSA");
+ sm->cached_pmk = TRUE;
+}
+
+
+static void eapol_sm_abort_cached(struct eapol_sm *sm)
+{
+ wpa_printf(MSG_DEBUG, "RSN: Authenticator did not accept PMKID, "
+ "doing full EAP authentication");
+ if (sm == NULL)
+ return;
+ sm->cached_pmk = FALSE;
+ sm->SUPP_PAE_state = SUPP_PAE_CONNECTING;
+ eapol_sm_set_port_unauthorized(sm);
+
+ /* Make sure we do not start sending EAPOL-Start frames first, but
+ * instead move to RESTART state to start EAPOL authentication. */
+ sm->startWhen = 3;
+ eapol_enable_timer_tick(sm);
+
+ if (sm->ctx->aborted_cached)
+ sm->ctx->aborted_cached(sm->ctx->ctx);
+}
+
+
+/**
+ * eapol_sm_register_scard_ctx - Notification of smart card context
+ * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init()
+ * @ctx: Context data for smart card operations
+ *
+ * Notify EAPOL state machines of context data for smart card operations. This
+ * context data will be used as a parameter for scard_*() functions.
+ */
+void eapol_sm_register_scard_ctx(struct eapol_sm *sm, void *ctx)
+{
+ if (sm) {
+ sm->ctx->scard_ctx = ctx;
+ eap_register_scard_ctx(sm->eap, ctx);
+ }
+}
+
+
+/**
+ * eapol_sm_notify_portControl - Notification of portControl changes
+ * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init()
+ * @portControl: New value for portControl variable
+ *
+ * Notify EAPOL state machines that portControl variable has changed.
+ */
+void eapol_sm_notify_portControl(struct eapol_sm *sm, PortControl portControl)
+{
+ if (sm == NULL)
+ return;
+ wpa_printf(MSG_DEBUG, "EAPOL: External notification - "
+ "portControl=%s", eapol_port_control(portControl));
+ sm->portControl = portControl;
+ eapol_sm_step(sm);
+}
+
+
+/**
+ * eapol_sm_notify_ctrl_attached - Notification of attached monitor
+ * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init()
+ *
+ * Notify EAPOL state machines that a monitor was attached to the control
+ * interface to trigger re-sending of pending requests for user input.
+ */
+void eapol_sm_notify_ctrl_attached(struct eapol_sm *sm)
+{
+ if (sm == NULL)
+ return;
+ eap_sm_notify_ctrl_attached(sm->eap);
+}
+
+
+/**
+ * eapol_sm_notify_ctrl_response - Notification of received user input
+ * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init()
+ *
+ * Notify EAPOL state machines that a control response, i.e., user
+ * input, was received in order to trigger retrying of a pending EAP request.
+ */
+void eapol_sm_notify_ctrl_response(struct eapol_sm *sm)
+{
+ if (sm == NULL)
+ return;
+ if (sm->eapReqData && !sm->eapReq) {
+ wpa_printf(MSG_DEBUG, "EAPOL: received control response (user "
+ "input) notification - retrying pending EAP "
+ "Request");
+ sm->eapolEap = TRUE;
+ sm->eapReq = TRUE;
+ eapol_sm_step(sm);
+ }
+}
+
+
+/**
+ * eapol_sm_request_reauth - Request reauthentication
+ * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init()
+ *
+ * This function can be used to request EAPOL reauthentication, e.g., when the
+ * current PMKSA entry is nearing expiration.
+ */
+void eapol_sm_request_reauth(struct eapol_sm *sm)
+{
+ if (sm == NULL || sm->SUPP_PAE_state != SUPP_PAE_AUTHENTICATED)
+ return;
+ eapol_sm_txStart(sm);
+}
+
+
+/**
+ * eapol_sm_notify_lower_layer_success - Notification of lower layer success
+ * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init()
+ * @in_eapol_sm: Whether the caller is already running inside EAPOL state
+ * machine loop (eapol_sm_step())
+ *
+ * Notify EAPOL (and EAP) state machines that a lower layer has detected a
+ * successful authentication. This is used to recover from dropped EAP-Success
+ * messages.
+ */
+void eapol_sm_notify_lower_layer_success(struct eapol_sm *sm, int in_eapol_sm)
+{
+ if (sm == NULL)
+ return;
+ eap_notify_lower_layer_success(sm->eap);
+ if (!in_eapol_sm)
+ eapol_sm_step(sm);
+}
+
+
+/**
+ * eapol_sm_invalidate_cached_session - Mark cached EAP session data invalid
+ * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init()
+ */
+void eapol_sm_invalidate_cached_session(struct eapol_sm *sm)
+{
+ if (sm)
+ eap_invalidate_cached_session(sm->eap);
+}
+
+
+static struct eap_peer_config * eapol_sm_get_config(void *ctx)
+{
+ struct eapol_sm *sm = ctx;
+ return sm ? sm->config : NULL;
+}
+
+
+static struct wpabuf * eapol_sm_get_eapReqData(void *ctx)
+{
+ struct eapol_sm *sm = ctx;
+ if (sm == NULL || sm->eapReqData == NULL)
+ return NULL;
+
+ return sm->eapReqData;
+}
+
+
+static Boolean eapol_sm_get_bool(void *ctx, enum eapol_bool_var variable)
+{
+ struct eapol_sm *sm = ctx;
+ if (sm == NULL)
+ return FALSE;
+ switch (variable) {
+ case EAPOL_eapSuccess:
+ return sm->eapSuccess;
+ case EAPOL_eapRestart:
+ return sm->eapRestart;
+ case EAPOL_eapFail:
+ return sm->eapFail;
+ case EAPOL_eapResp:
+ return sm->eapResp;
+ case EAPOL_eapNoResp:
+ return sm->eapNoResp;
+ case EAPOL_eapReq:
+ return sm->eapReq;
+ case EAPOL_portEnabled:
+ return sm->portEnabled;
+ case EAPOL_altAccept:
+ return sm->altAccept;
+ case EAPOL_altReject:
+ return sm->altReject;
+ case EAPOL_eapTriggerStart:
+ return sm->eapTriggerStart;
+ }
+ return FALSE;
+}
+
+
+static void eapol_sm_set_bool(void *ctx, enum eapol_bool_var variable,
+ Boolean value)
+{
+ struct eapol_sm *sm = ctx;
+ if (sm == NULL)
+ return;
+ switch (variable) {
+ case EAPOL_eapSuccess:
+ sm->eapSuccess = value;
+ break;
+ case EAPOL_eapRestart:
+ sm->eapRestart = value;
+ break;
+ case EAPOL_eapFail:
+ sm->eapFail = value;
+ break;
+ case EAPOL_eapResp:
+ sm->eapResp = value;
+ break;
+ case EAPOL_eapNoResp:
+ sm->eapNoResp = value;
+ break;
+ case EAPOL_eapReq:
+ sm->eapReq = value;
+ break;
+ case EAPOL_portEnabled:
+ sm->portEnabled = value;
+ break;
+ case EAPOL_altAccept:
+ sm->altAccept = value;
+ break;
+ case EAPOL_altReject:
+ sm->altReject = value;
+ break;
+ case EAPOL_eapTriggerStart:
+ sm->eapTriggerStart = value;
+ break;
+ }
+}
+
+
+static unsigned int eapol_sm_get_int(void *ctx, enum eapol_int_var variable)
+{
+ struct eapol_sm *sm = ctx;
+ if (sm == NULL)
+ return 0;
+ switch (variable) {
+ case EAPOL_idleWhile:
+ return sm->idleWhile;
+ }
+ return 0;
+}
+
+
+static void eapol_sm_set_int(void *ctx, enum eapol_int_var variable,
+ unsigned int value)
+{
+ struct eapol_sm *sm = ctx;
+ if (sm == NULL)
+ return;
+ switch (variable) {
+ case EAPOL_idleWhile:
+ sm->idleWhile = value;
+ if (sm->idleWhile > 0)
+ eapol_enable_timer_tick(sm);
+ break;
+ }
+}
+
+
+static void eapol_sm_set_config_blob(void *ctx, struct wpa_config_blob *blob)
+{
+#ifndef CONFIG_NO_CONFIG_BLOBS
+ struct eapol_sm *sm = ctx;
+ if (sm && sm->ctx && sm->ctx->set_config_blob)
+ sm->ctx->set_config_blob(sm->ctx->ctx, blob);
+#endif /* CONFIG_NO_CONFIG_BLOBS */
+}
+
+
+static const struct wpa_config_blob *
+eapol_sm_get_config_blob(void *ctx, const char *name)
+{
+#ifndef CONFIG_NO_CONFIG_BLOBS
+ struct eapol_sm *sm = ctx;
+ if (sm && sm->ctx && sm->ctx->get_config_blob)
+ return sm->ctx->get_config_blob(sm->ctx->ctx, name);
+ else
+ return NULL;
+#else /* CONFIG_NO_CONFIG_BLOBS */
+ return NULL;
+#endif /* CONFIG_NO_CONFIG_BLOBS */
+}
+
+
+static void eapol_sm_notify_pending(void *ctx)
+{
+ struct eapol_sm *sm = ctx;
+ if (sm == NULL)
+ return;
+ if (sm->eapReqData && !sm->eapReq) {
+ wpa_printf(MSG_DEBUG, "EAPOL: received notification from EAP "
+ "state machine - retrying pending EAP Request");
+ sm->eapolEap = TRUE;
+ sm->eapReq = TRUE;
+ eapol_sm_step(sm);
+ }
+}
+
+
+#if defined(CONFIG_CTRL_IFACE) || !defined(CONFIG_NO_STDOUT_DEBUG)
+static void eapol_sm_eap_param_needed(void *ctx, enum wpa_ctrl_req_type field,
+ const char *txt)
+{
+ struct eapol_sm *sm = ctx;
+ wpa_printf(MSG_DEBUG, "EAPOL: EAP parameter needed");
+ if (sm->ctx->eap_param_needed)
+ sm->ctx->eap_param_needed(sm->ctx->ctx, field, txt);
+}
+#else /* CONFIG_CTRL_IFACE || !CONFIG_NO_STDOUT_DEBUG */
+#define eapol_sm_eap_param_needed NULL
+#endif /* CONFIG_CTRL_IFACE || !CONFIG_NO_STDOUT_DEBUG */
+
+static void eapol_sm_notify_cert(void *ctx, int depth, const char *subject,
+ const char *altsubject[],
+ int num_altsubject, const char *cert_hash,
+ const struct wpabuf *cert)
+{
+ struct eapol_sm *sm = ctx;
+ if (sm->ctx->cert_cb)
+ sm->ctx->cert_cb(sm->ctx->ctx, depth, subject, altsubject,
+ num_altsubject, cert_hash, cert);
+}
+
+
+static void eapol_sm_notify_status(void *ctx, const char *status,
+ const char *parameter)
+{
+ struct eapol_sm *sm = ctx;
+
+ if (sm->ctx->status_cb)
+ sm->ctx->status_cb(sm->ctx->ctx, status, parameter);
+}
+
+
+#ifdef CONFIG_EAP_PROXY
+static void eapol_sm_eap_proxy_cb(void *ctx)
+{
+ struct eapol_sm *sm = ctx;
+
+ if (sm->ctx->eap_proxy_cb)
+ sm->ctx->eap_proxy_cb(sm->ctx->ctx);
+}
+#endif /* CONFIG_EAP_PROXY */
+
+
+static void eapol_sm_set_anon_id(void *ctx, const u8 *id, size_t len)
+{
+ struct eapol_sm *sm = ctx;
+
+ if (sm->ctx->set_anon_id)
+ sm->ctx->set_anon_id(sm->ctx->ctx, id, len);
+}
+
+
+static const struct eapol_callbacks eapol_cb =
+{
+ eapol_sm_get_config,
+ eapol_sm_get_bool,
+ eapol_sm_set_bool,
+ eapol_sm_get_int,
+ eapol_sm_set_int,
+ eapol_sm_get_eapReqData,
+ eapol_sm_set_config_blob,
+ eapol_sm_get_config_blob,
+ eapol_sm_notify_pending,
+ eapol_sm_eap_param_needed,
+ eapol_sm_notify_cert,
+ eapol_sm_notify_status,
+#ifdef CONFIG_EAP_PROXY
+ eapol_sm_eap_proxy_cb,
+#endif /* CONFIG_EAP_PROXY */
+ eapol_sm_set_anon_id
+};
+
+
+/**
+ * eapol_sm_init - Initialize EAPOL state machine
+ * @ctx: Pointer to EAPOL context data; this needs to be an allocated buffer
+ * and EAPOL state machine will free it in eapol_sm_deinit()
+ * Returns: Pointer to the allocated EAPOL state machine or %NULL on failure
+ *
+ * Allocate and initialize an EAPOL state machine.
+ */
+struct eapol_sm *eapol_sm_init(struct eapol_ctx *ctx)
+{
+ struct eapol_sm *sm;
+ struct eap_config conf;
+ sm = os_zalloc(sizeof(*sm));
+ if (sm == NULL)
+ return NULL;
+ sm->ctx = ctx;
+
+ sm->portControl = Auto;
+
+ /* Supplicant PAE state machine */
+ sm->heldPeriod = 60;
+ sm->startPeriod = 30;
+ sm->maxStart = 3;
+
+ /* Supplicant Backend state machine */
+ sm->authPeriod = 30;
+
+ os_memset(&conf, 0, sizeof(conf));
+ conf.opensc_engine_path = ctx->opensc_engine_path;
+ conf.pkcs11_engine_path = ctx->pkcs11_engine_path;
+ conf.pkcs11_module_path = ctx->pkcs11_module_path;
+ conf.openssl_ciphers = ctx->openssl_ciphers;
+ conf.wps = ctx->wps;
+ conf.cert_in_cb = ctx->cert_in_cb;
+
+ sm->eap = eap_peer_sm_init(sm, &eapol_cb, sm->ctx->msg_ctx, &conf);
+ if (sm->eap == NULL) {
+ os_free(sm);
+ return NULL;
+ }
+
+#ifdef CONFIG_EAP_PROXY
+ sm->use_eap_proxy = FALSE;
+ sm->eap_proxy = eap_proxy_init(sm, &eapol_cb, sm->ctx->msg_ctx);
+ if (sm->eap_proxy == NULL) {
+ wpa_printf(MSG_ERROR, "Unable to initialize EAP Proxy");
+ }
+#endif /* CONFIG_EAP_PROXY */
+
+ /* Initialize EAPOL state machines */
+ sm->force_authorized_update = TRUE;
+ sm->initialize = TRUE;
+ eapol_sm_step(sm);
+ sm->initialize = FALSE;
+ eapol_sm_step(sm);
+
+ sm->timer_tick_enabled = 1;
+ eloop_register_timeout(1, 0, eapol_port_timers_tick, NULL, sm);
+
+ return sm;
+}
+
+
+/**
+ * eapol_sm_deinit - Deinitialize EAPOL state machine
+ * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init()
+ *
+ * Deinitialize and free EAPOL state machine.
+ */
+void eapol_sm_deinit(struct eapol_sm *sm)
+{
+ if (sm == NULL)
+ return;
+ eloop_cancel_timeout(eapol_sm_step_timeout, NULL, sm);
+ eloop_cancel_timeout(eapol_port_timers_tick, NULL, sm);
+ eap_peer_sm_deinit(sm->eap);
+#ifdef CONFIG_EAP_PROXY
+ eap_proxy_deinit(sm->eap_proxy);
+#endif /* CONFIG_EAP_PROXY */
+ os_free(sm->last_rx_key);
+ wpabuf_free(sm->eapReqData);
+ os_free(sm->ctx);
+ os_free(sm);
+}
+
+
+void eapol_sm_set_ext_pw_ctx(struct eapol_sm *sm,
+ struct ext_password_data *ext)
+{
+ if (sm && sm->eap)
+ eap_sm_set_ext_pw_ctx(sm->eap, ext);
+}
+
+
+int eapol_sm_failed(struct eapol_sm *sm)
+{
+ if (sm == NULL)
+ return 0;
+ return !sm->eapSuccess && sm->eapFail;
+}
+
+
+int eapol_sm_get_eap_proxy_imsi(struct eapol_sm *sm, char *imsi, size_t *len)
+{
+#ifdef CONFIG_EAP_PROXY
+ if (sm->eap_proxy == NULL)
+ return -1;
+ return eap_proxy_get_imsi(sm->eap_proxy, imsi, len);
+#else /* CONFIG_EAP_PROXY */
+ return -1;
+#endif /* CONFIG_EAP_PROXY */
+}
+
+
+void eapol_sm_erp_flush(struct eapol_sm *sm)
+{
+ if (sm)
+ eap_peer_erp_free_keys(sm->eap);
+}
diff --git a/freebsd/contrib/wpa/src/eapol_supp/eapol_supp_sm.h b/freebsd/contrib/wpa/src/eapol_supp/eapol_supp_sm.h
new file mode 100644
index 00000000..1309ff75
--- /dev/null
+++ b/freebsd/contrib/wpa/src/eapol_supp/eapol_supp_sm.h
@@ -0,0 +1,443 @@
+/*
+ * EAPOL supplicant state machines
+ * 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.
+ */
+
+#ifndef EAPOL_SUPP_SM_H
+#define EAPOL_SUPP_SM_H
+
+#include "common/defs.h"
+
+typedef enum { Unauthorized, Authorized } PortStatus;
+typedef enum { Auto, ForceUnauthorized, ForceAuthorized } PortControl;
+
+/**
+ * struct eapol_config - Per network configuration for EAPOL state machines
+ */
+struct eapol_config {
+ /**
+ * accept_802_1x_keys - Accept IEEE 802.1X (non-WPA) EAPOL-Key frames
+ *
+ * This variable should be set to 1 when using EAPOL state machines
+ * with non-WPA security policy to generate dynamic WEP keys. When
+ * using WPA, this should be set to 0 so that WPA state machine can
+ * process the EAPOL-Key frames.
+ */
+ int accept_802_1x_keys;
+
+#define EAPOL_REQUIRE_KEY_UNICAST BIT(0)
+#define EAPOL_REQUIRE_KEY_BROADCAST BIT(1)
+ /**
+ * required_keys - Which EAPOL-Key packets are required
+ *
+ * This variable determines which EAPOL-Key packets are required before
+ * marking connection authenticated. This is a bit field of
+ * EAPOL_REQUIRE_KEY_UNICAST and EAPOL_REQUIRE_KEY_BROADCAST flags.
+ */
+ int required_keys;
+
+ /**
+ * fast_reauth - Whether fast EAP reauthentication is enabled
+ */
+ int fast_reauth;
+
+ /**
+ * workaround - Whether EAP workarounds are enabled
+ */
+ unsigned int workaround;
+
+ /**
+ * eap_disabled - Whether EAP is disabled
+ */
+ int eap_disabled;
+
+ /**
+ * external_sim - Use external processing for SIM/USIM operations
+ */
+ int external_sim;
+
+#define EAPOL_LOCAL_WPS_IN_USE BIT(0)
+#define EAPOL_PEER_IS_WPS20_AP BIT(1)
+ /**
+ * wps - Whether this connection is used for WPS
+ */
+ int wps;
+};
+
+struct eapol_sm;
+struct wpa_config_blob;
+
+enum eapol_supp_result {
+ EAPOL_SUPP_RESULT_FAILURE,
+ EAPOL_SUPP_RESULT_SUCCESS,
+ EAPOL_SUPP_RESULT_EXPECTED_FAILURE
+};
+
+/**
+ * struct eapol_ctx - Global (for all networks) EAPOL state machine context
+ */
+struct eapol_ctx {
+ /**
+ * ctx - Pointer to arbitrary upper level context
+ */
+ void *ctx;
+
+ /**
+ * preauth - IEEE 802.11i/RSN pre-authentication
+ *
+ * This EAPOL state machine is used for IEEE 802.11i/RSN
+ * pre-authentication
+ */
+ int preauth;
+
+ /**
+ * cb - Function to be called when EAPOL negotiation has been completed
+ * @eapol: Pointer to EAPOL state machine data
+ * @result: Whether the authentication was completed successfully
+ * @ctx: Pointer to context data (cb_ctx)
+ *
+ * This optional callback function will be called when the EAPOL
+ * authentication has been completed. This allows the owner of the
+ * EAPOL state machine to process the key and terminate the EAPOL state
+ * machine. Currently, this is used only in RSN pre-authentication.
+ */
+ void (*cb)(struct eapol_sm *eapol, enum eapol_supp_result result,
+ void *ctx);
+
+ /**
+ * cb_ctx - Callback context for cb()
+ */
+ void *cb_ctx;
+
+ /**
+ * msg_ctx - Callback context for wpa_msg() calls
+ */
+ void *msg_ctx;
+
+ /**
+ * scard_ctx - Callback context for PC/SC scard_*() function calls
+ *
+ * This context can be updated with eapol_sm_register_scard_ctx().
+ */
+ void *scard_ctx;
+
+ /**
+ * eapol_send_ctx - Callback context for eapol_send() calls
+ */
+ void *eapol_send_ctx;
+
+ /**
+ * eapol_done_cb - Function to be called at successful completion
+ * @ctx: Callback context (ctx)
+ *
+ * This function is called at the successful completion of EAPOL
+ * authentication. If dynamic WEP keys are used, this is called only
+ * after all the expected keys have been received.
+ */
+ void (*eapol_done_cb)(void *ctx);
+
+ /**
+ * eapol_send - Send EAPOL packets
+ * @ctx: Callback context (eapol_send_ctx)
+ * @type: EAPOL type (IEEE802_1X_TYPE_*)
+ * @buf: Pointer to EAPOL payload
+ * @len: Length of the EAPOL payload
+ * Returns: 0 on success, -1 on failure
+ */
+ int (*eapol_send)(void *ctx, int type, const u8 *buf, size_t len);
+
+ /**
+ * set_wep_key - Configure WEP keys
+ * @ctx: Callback context (ctx)
+ * @unicast: Non-zero = unicast, 0 = multicast/broadcast key
+ * @keyidx: Key index (0..3)
+ * @key: WEP key
+ * @keylen: Length of the WEP key
+ * Returns: 0 on success, -1 on failure
+ */
+ int (*set_wep_key)(void *ctx, int unicast, int keyidx,
+ const u8 *key, size_t keylen);
+
+ /**
+ * set_config_blob - Set or add a named configuration blob
+ * @ctx: Callback context (ctx)
+ * @blob: New value for the blob
+ *
+ * Adds a new configuration blob or replaces the current value of an
+ * existing blob.
+ */
+ void (*set_config_blob)(void *ctx, struct wpa_config_blob *blob);
+
+ /**
+ * get_config_blob - Get a named configuration blob
+ * @ctx: Callback context (ctx)
+ * @name: Name of the blob
+ * Returns: Pointer to blob data or %NULL if not found
+ */
+ const struct wpa_config_blob * (*get_config_blob)(void *ctx,
+ const char *name);
+
+ /**
+ * aborted_cached - Notify that cached PMK attempt was aborted
+ * @ctx: Callback context (ctx)
+ */
+ void (*aborted_cached)(void *ctx);
+
+ /**
+ * opensc_engine_path - Path to the OpenSSL engine for opensc
+ *
+ * This is an OpenSSL specific configuration option for loading OpenSC
+ * engine (engine_opensc.so); if %NULL, this engine is not loaded.
+ */
+ const char *opensc_engine_path;
+
+ /**
+ * pkcs11_engine_path - Path to the OpenSSL engine for PKCS#11
+ *
+ * This is an OpenSSL specific configuration option for loading PKCS#11
+ * engine (engine_pkcs11.so); if %NULL, this engine is not loaded.
+ */
+ const char *pkcs11_engine_path;
+
+ /**
+ * pkcs11_module_path - Path to the OpenSSL OpenSC/PKCS#11 module
+ *
+ * This is an OpenSSL specific configuration option for configuring
+ * path to OpenSC/PKCS#11 engine (opensc-pkcs11.so); if %NULL, this
+ * module is not loaded.
+ */
+ const char *pkcs11_module_path;
+
+ /**
+ * openssl_ciphers - OpenSSL cipher string
+ *
+ * This is an OpenSSL specific configuration option for configuring the
+ * default ciphers. If not set, "DEFAULT:!EXP:!LOW" is used as the
+ * default.
+ */
+ const char *openssl_ciphers;
+
+ /**
+ * wps - WPS context data
+ *
+ * This is only used by EAP-WSC and can be left %NULL if not available.
+ */
+ struct wps_context *wps;
+
+ /**
+ * eap_param_needed - Notify that EAP parameter is needed
+ * @ctx: Callback context (ctx)
+ * @field: Field indicator (e.g., WPA_CTRL_REQ_EAP_IDENTITY)
+ * @txt: User readable text describing the required parameter
+ */
+ void (*eap_param_needed)(void *ctx, enum wpa_ctrl_req_type field,
+ const char *txt);
+
+ /**
+ * port_cb - Set port authorized/unauthorized callback (optional)
+ * @ctx: Callback context (ctx)
+ * @authorized: Whether the supplicant port is now in authorized state
+ */
+ void (*port_cb)(void *ctx, int authorized);
+
+ /**
+ * cert_cb - Notification of a peer certificate
+ * @ctx: Callback context (ctx)
+ * @depth: Depth in certificate chain (0 = server)
+ * @subject: Subject of the peer certificate
+ * @altsubject: Select fields from AltSubject of the peer certificate
+ * @num_altsubject: Number of altsubject values
+ * @cert_hash: SHA-256 hash of the certificate
+ * @cert: Peer certificate
+ */
+ void (*cert_cb)(void *ctx, int depth, const char *subject,
+ const char *altsubject[], int num_altsubject,
+ const char *cert_hash, const struct wpabuf *cert);
+
+ /**
+ * cert_in_cb - Include server certificates in callback
+ */
+ int cert_in_cb;
+
+ /**
+ * status_cb - Notification of a change in EAP status
+ * @ctx: Callback context (ctx)
+ * @status: Step in the process of EAP authentication
+ * @parameter: Step-specific parameter, e.g., EAP method name
+ */
+ void (*status_cb)(void *ctx, const char *status,
+ const char *parameter);
+
+#ifdef CONFIG_EAP_PROXY
+ /**
+ * eap_proxy_cb - Callback signifying any updates from eap_proxy
+ * @ctx: eapol_ctx from eap_peer_sm_init() call
+ */
+ void (*eap_proxy_cb)(void *ctx);
+#endif /* CONFIG_EAP_PROXY */
+
+ /**
+ * set_anon_id - Set or add anonymous identity
+ * @ctx: eapol_ctx from eap_peer_sm_init() call
+ * @id: Anonymous identity (e.g., EAP-SIM pseudonym)
+ * @len: Length of anonymous identity in octets
+ */
+ void (*set_anon_id)(void *ctx, const u8 *id, size_t len);
+};
+
+
+struct eap_peer_config;
+struct ext_password_data;
+
+#ifdef IEEE8021X_EAPOL
+struct eapol_sm *eapol_sm_init(struct eapol_ctx *ctx);
+void eapol_sm_deinit(struct eapol_sm *sm);
+void eapol_sm_step(struct eapol_sm *sm);
+int eapol_sm_get_status(struct eapol_sm *sm, char *buf, size_t buflen,
+ int verbose);
+int eapol_sm_get_mib(struct eapol_sm *sm, char *buf, size_t buflen);
+void eapol_sm_configure(struct eapol_sm *sm, int heldPeriod, int authPeriod,
+ int startPeriod, int maxStart);
+int eapol_sm_rx_eapol(struct eapol_sm *sm, const u8 *src, const u8 *buf,
+ size_t len);
+void eapol_sm_notify_tx_eapol_key(struct eapol_sm *sm);
+void eapol_sm_notify_portEnabled(struct eapol_sm *sm, Boolean enabled);
+void eapol_sm_notify_portValid(struct eapol_sm *sm, Boolean valid);
+void eapol_sm_notify_eap_success(struct eapol_sm *sm, Boolean success);
+void eapol_sm_notify_eap_fail(struct eapol_sm *sm, Boolean fail);
+void eapol_sm_notify_config(struct eapol_sm *sm,
+ struct eap_peer_config *config,
+ const struct eapol_config *conf);
+int eapol_sm_get_key(struct eapol_sm *sm, u8 *key, size_t len);
+const u8 * eapol_sm_get_session_id(struct eapol_sm *sm, size_t *len);
+void eapol_sm_notify_logoff(struct eapol_sm *sm, Boolean logoff);
+void eapol_sm_notify_cached(struct eapol_sm *sm);
+void eapol_sm_notify_pmkid_attempt(struct eapol_sm *sm);
+void eapol_sm_register_scard_ctx(struct eapol_sm *sm, void *ctx);
+void eapol_sm_notify_portControl(struct eapol_sm *sm, PortControl portControl);
+void eapol_sm_notify_ctrl_attached(struct eapol_sm *sm);
+void eapol_sm_notify_ctrl_response(struct eapol_sm *sm);
+void eapol_sm_request_reauth(struct eapol_sm *sm);
+void eapol_sm_notify_lower_layer_success(struct eapol_sm *sm, int in_eapol_sm);
+void eapol_sm_invalidate_cached_session(struct eapol_sm *sm);
+const char * eapol_sm_get_method_name(struct eapol_sm *sm);
+void eapol_sm_set_ext_pw_ctx(struct eapol_sm *sm,
+ struct ext_password_data *ext);
+int eapol_sm_failed(struct eapol_sm *sm);
+void eapol_sm_erp_flush(struct eapol_sm *sm);
+int eapol_sm_get_eap_proxy_imsi(struct eapol_sm *sm, char *imsi, size_t *len);
+#else /* IEEE8021X_EAPOL */
+static inline struct eapol_sm *eapol_sm_init(struct eapol_ctx *ctx)
+{
+ free(ctx);
+ return (struct eapol_sm *) 1;
+}
+static inline void eapol_sm_deinit(struct eapol_sm *sm)
+{
+}
+static inline void eapol_sm_step(struct eapol_sm *sm)
+{
+}
+static inline int eapol_sm_get_status(struct eapol_sm *sm, char *buf,
+ size_t buflen, int verbose)
+{
+ return 0;
+}
+static inline int eapol_sm_get_mib(struct eapol_sm *sm, char *buf,
+ size_t buflen)
+{
+ return 0;
+}
+static inline void eapol_sm_configure(struct eapol_sm *sm, int heldPeriod,
+ int authPeriod, int startPeriod,
+ int maxStart)
+{
+}
+static inline int eapol_sm_rx_eapol(struct eapol_sm *sm, const u8 *src,
+ const u8 *buf, size_t len)
+{
+ return 0;
+}
+static inline void eapol_sm_notify_tx_eapol_key(struct eapol_sm *sm)
+{
+}
+static inline void eapol_sm_notify_portEnabled(struct eapol_sm *sm,
+ Boolean enabled)
+{
+}
+static inline void eapol_sm_notify_portValid(struct eapol_sm *sm,
+ Boolean valid)
+{
+}
+static inline void eapol_sm_notify_eap_success(struct eapol_sm *sm,
+ Boolean success)
+{
+}
+static inline void eapol_sm_notify_eap_fail(struct eapol_sm *sm, Boolean fail)
+{
+}
+static inline void eapol_sm_notify_config(struct eapol_sm *sm,
+ struct eap_peer_config *config,
+ struct eapol_config *conf)
+{
+}
+static inline int eapol_sm_get_key(struct eapol_sm *sm, u8 *key, size_t len)
+{
+ return -1;
+}
+static inline const u8 *
+eapol_sm_get_session_id(struct eapol_sm *sm, size_t *len)
+{
+ return NULL;
+}
+static inline void eapol_sm_notify_logoff(struct eapol_sm *sm, Boolean logoff)
+{
+}
+static inline void eapol_sm_notify_cached(struct eapol_sm *sm)
+{
+}
+static inline void eapol_sm_notify_pmkid_attempt(struct eapol_sm *sm)
+{
+}
+#define eapol_sm_register_scard_ctx(sm, ctx) do { } while (0)
+static inline void eapol_sm_notify_portControl(struct eapol_sm *sm,
+ PortControl portControl)
+{
+}
+static inline void eapol_sm_notify_ctrl_attached(struct eapol_sm *sm)
+{
+}
+static inline void eapol_sm_notify_ctrl_response(struct eapol_sm *sm)
+{
+}
+static inline void eapol_sm_request_reauth(struct eapol_sm *sm)
+{
+}
+static inline void eapol_sm_notify_lower_layer_success(struct eapol_sm *sm,
+ int in_eapol_sm)
+{
+}
+static inline void eapol_sm_invalidate_cached_session(struct eapol_sm *sm)
+{
+}
+static inline const char * eapol_sm_get_method_name(struct eapol_sm *sm)
+{
+ return NULL;
+}
+static inline void eapol_sm_set_ext_pw_ctx(struct eapol_sm *sm,
+ struct ext_password_data *ext)
+{
+}
+static inline int eapol_sm_failed(struct eapol_sm *sm)
+{
+ return 0;
+}
+static inline void eapol_sm_erp_flush(struct eapol_sm *sm)
+{
+}
+#endif /* IEEE8021X_EAPOL */
+
+#endif /* EAPOL_SUPP_SM_H */
diff --git a/freebsd/contrib/wpa/src/fst/fst.h b/freebsd/contrib/wpa/src/fst/fst.h
new file mode 100644
index 00000000..0c0e435b
--- /dev/null
+++ b/freebsd/contrib/wpa/src/fst/fst.h
@@ -0,0 +1,296 @@
+/*
+ * FST module - interface definitions
+ * Copyright (c) 2014, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef FST_H
+#define FST_H
+
+#ifdef CONFIG_FST
+
+#include "common/defs.h"
+#include "fst/fst_ctrl_iface.h"
+
+/* FST module hostap integration API */
+
+#define US_IN_MS 1000
+#define LLT_UNIT_US 32 /* See 10.32.2.2 Transitioning between states */
+
+#define FST_LLT_MS_TO_VAL(m) (((u32) (m)) * US_IN_MS / LLT_UNIT_US)
+#define FST_LLT_VAL_TO_MS(v) (((u32) (v)) * LLT_UNIT_US / US_IN_MS)
+
+#define FST_MAX_LLT_MS FST_LLT_VAL_TO_MS(-1)
+#define FST_MAX_PRIO_VALUE ((u8) -1)
+#define FST_MAX_GROUP_ID_LEN IFNAMSIZ
+
+#define FST_DEFAULT_LLT_CFG_VALUE 50
+
+struct hostapd_hw_modes;
+struct ieee80211_mgmt;
+struct fst_iface;
+struct fst_group;
+struct fst_session;
+struct fst_get_peer_ctx;
+struct fst_ctrl_handle;
+
+struct fst_wpa_obj {
+ void *ctx;
+
+ /**
+ * get_bssid - Get BSSID of the interface
+ * @ctx: User context %ctx
+ * Returns: BSSID for success, %NULL for failure.
+ *
+ * NOTE: For AP it returns the own BSSID, while for STA - the BSSID of
+ * the associated AP.
+ */
+ const u8 * (*get_bssid)(void *ctx);
+
+ /**
+ * get_channel_info - Get current channel info
+ * @ctx: User context %ctx
+ * @hw_mode: OUT, current HW mode
+ * @channel: OUT, current channel
+ */
+ void (*get_channel_info)(void *ctx, enum hostapd_hw_mode *hw_mode,
+ u8 *channel);
+
+ /**
+ * get_hw_modes - Get hardware modes
+ * @ctx: User context %ctx
+ * @modes: OUT, pointer on array of hw modes
+ *
+ * Returns: Number of hw modes available.
+ */
+ int (*get_hw_modes)(void *ctx, struct hostapd_hw_modes **modes);
+
+ /**
+ * set_ies - Set interface's MB IE
+ * @ctx: User context %ctx
+ * @fst_ies: MB IE buffer (owned by FST module)
+ */
+ void (*set_ies)(void *ctx, const struct wpabuf *fst_ies);
+
+ /**
+ * send_action - Send FST Action frame via the interface
+ * @ctx: User context %ctx
+ * @addr: Address of the destination STA
+ * @data: Action frame buffer
+ * Returns: 0 for success, negative error code for failure.
+ */
+ int (*send_action)(void *ctx, const u8 *addr, struct wpabuf *data);
+
+ /**
+ * get_mb_ie - Get last MB IE received from STA
+ * @ctx: User context %ctx
+ * @addr: Address of the STA
+ * Returns: MB IE buffer, %NULL if no MB IE received from the STA
+ */
+ const struct wpabuf * (*get_mb_ie)(void *ctx, const u8 *addr);
+
+ /**
+ * update_mb_ie - Update last MB IE received from STA
+ * @ctx: User context %ctx
+ * @addr: Address of the STA
+ * @buf: Buffer that contains the MB IEs data
+ * @size: Size of data in %buf
+ */
+ void (*update_mb_ie)(void *ctx, const u8 *addr,
+ const u8 *buf, size_t size);
+
+ /**
+ * get_peer_first - Get MAC address of the 1st connected STA
+ * @ctx: User context %ctx
+ * @get_ctx: Context to be used for %get_peer_next call
+ * @mb_only: %TRUE if only multi-band capable peer should be reported
+ * Returns: Address of the 1st connected STA, %NULL if no STAs connected
+ */
+ const u8 * (*get_peer_first)(void *ctx,
+ struct fst_get_peer_ctx **get_ctx,
+ Boolean mb_only);
+ /**
+ * get_peer_next - Get MAC address of the next connected STA
+ * @ctx: User context %ctx
+ * @get_ctx: Context received from %get_peer_first or previous
+ * %get_peer_next call
+ * @mb_only: %TRUE if only multi-band capable peer should be reported
+ * Returns: Address of the next connected STA, %NULL if no more STAs
+ * connected
+ */
+ const u8 * (*get_peer_next)(void *ctx,
+ struct fst_get_peer_ctx **get_ctx,
+ Boolean mb_only);
+};
+
+/**
+ * fst_global_init - Global FST module initiator
+ * Returns: 0 for success, negative error code for failure.
+ * Note: The purpose of this function is to allocate and initiate global
+ * FST module data structures (linked lists, static data etc.)
+ * This function should be called prior to the 1st %fst_attach call.
+ */
+int fst_global_init(void);
+
+/**
+ * fst_global_deinit - Global FST module de-initiator
+ * Note: The purpose of this function is to deallocate and de-initiate global
+ * FST module data structures (linked lists, static data etc.)
+ */
+void fst_global_deinit(void);
+
+/**
+ * struct fst_ctrl - Notification interface for FST module
+ */
+struct fst_ctrl {
+ /**
+ * init - Initialize the notification interface
+ * Returns: 0 for success, negative error code for failure.
+ */
+ int (*init)(void);
+
+ /**
+ * deinit - Deinitialize the notification interface
+ */
+ void (*deinit)(void);
+
+ /**
+ * on_group_created - Notify about FST group creation
+ * Returns: 0 for success, negative error code for failure.
+ */
+ int (*on_group_created)(struct fst_group *g);
+
+ /**
+ * on_group_deleted - Notify about FST group deletion
+ */
+ void (*on_group_deleted)(struct fst_group *g);
+
+ /**
+ * on_iface_added - Notify about interface addition
+ * Returns: 0 for success, negative error code for failure.
+ */
+ int (*on_iface_added)(struct fst_iface *i);
+
+ /**
+ * on_iface_removed - Notify about interface removal
+ */
+ void (*on_iface_removed)(struct fst_iface *i);
+
+ /**
+ * on_session_added - Notify about FST session addition
+ * Returns: 0 for success, negative error code for failure.
+ */
+ int (*on_session_added)(struct fst_session *s);
+
+ /**
+ * on_session_removed - Notify about FST session removal
+ */
+ void (*on_session_removed)(struct fst_session *s);
+
+ /**
+ * on_event - Notify about FST event
+ * @event_type: Event type
+ * @i: Interface object that relates to the event or NULL
+ * @g: Group object that relates to the event or NULL
+ * @extra - Event specific data (see fst_ctrl_iface.h for more info)
+ */
+ void (*on_event)(enum fst_event_type event_type, struct fst_iface *i,
+ struct fst_session *s,
+ const union fst_event_extra *extra);
+};
+
+struct fst_ctrl_handle * fst_global_add_ctrl(const struct fst_ctrl *ctrl);
+void fst_global_del_ctrl(struct fst_ctrl_handle *h);
+
+/**
+ * NOTE: These values have to be read from configuration file
+ */
+struct fst_iface_cfg {
+ char group_id[FST_MAX_GROUP_ID_LEN + 1];
+ u8 priority;
+ u32 llt;
+};
+
+/**
+ * fst_attach - Attach interface to an FST group according to configuration read
+ * @ifname: Interface name
+ * @own_addr: Own interface MAC address
+ * @iface_obj: Callbacks to be used by FST module to communicate with
+ * hostapd/wpa_supplicant
+ * @cfg: FST-related interface configuration read from the configuration file
+ * Returns: FST interface object for success, %NULL for failure.
+ */
+struct fst_iface * fst_attach(const char *ifname,
+ const u8 *own_addr,
+ const struct fst_wpa_obj *iface_obj,
+ const struct fst_iface_cfg *cfg);
+
+/**
+ * fst_detach - Detach an interface
+ * @iface: FST interface object
+ */
+void fst_detach(struct fst_iface *iface);
+
+/* FST module inputs */
+/**
+ * fst_rx_action - FST Action frames handler
+ * @iface: FST interface object
+ * @mgmt: Action frame arrived
+ * @len: Action frame length
+ */
+void fst_rx_action(struct fst_iface *iface, const struct ieee80211_mgmt *mgmt,
+ size_t len);
+
+/**
+ * fst_notify_peer_connected - FST STA connect handler
+ * @iface: FST interface object
+ * @addr: Address of the connected STA
+ */
+void fst_notify_peer_connected(struct fst_iface *iface, const u8 *addr);
+
+/**
+ * fst_notify_peer_disconnected - FST STA disconnect handler
+ * @iface: FST interface object
+ * @addr: Address of the disconnected STA
+ */
+void fst_notify_peer_disconnected(struct fst_iface *iface, const u8 *addr);
+
+/* FST module auxiliary routines */
+
+/**
+ * fst_are_ifaces_aggregated - Determines whether 2 interfaces belong to the
+ * same FST group
+ * @iface1: 1st FST interface object
+ * @iface1: 2nd FST interface object
+ *
+ * Returns: %TRUE if the interfaces belong to the same FST group,
+ * %FALSE otherwise
+ */
+Boolean fst_are_ifaces_aggregated(struct fst_iface *iface1,
+ struct fst_iface *iface2);
+
+#else /* CONFIG_FST */
+
+static inline int fst_global_init(void)
+{
+ return 0;
+}
+
+static inline int fst_global_start(void)
+{
+ return 0;
+}
+
+static inline void fst_global_stop(void)
+{
+}
+
+static inline void fst_global_deinit(void)
+{
+}
+
+#endif /* CONFIG_FST */
+
+#endif /* FST_H */
diff --git a/freebsd/contrib/wpa/src/fst/fst_ctrl_aux.h b/freebsd/contrib/wpa/src/fst/fst_ctrl_aux.h
new file mode 100644
index 00000000..e2133f50
--- /dev/null
+++ b/freebsd/contrib/wpa/src/fst/fst_ctrl_aux.h
@@ -0,0 +1,91 @@
+/*
+ * FST module - miscellaneous definitions
+ * Copyright (c) 2014, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef FST_CTRL_AUX_H
+#define FST_CTRL_AUX_H
+
+#include "common/defs.h"
+
+/* FST module control interface API */
+#define FST_INVALID_SESSION_ID ((u32) -1)
+#define FST_MAX_GROUP_ID_SIZE 32
+#define FST_MAX_INTERFACE_SIZE 32
+
+enum fst_session_state {
+ FST_SESSION_STATE_INITIAL,
+ FST_SESSION_STATE_SETUP_COMPLETION,
+ FST_SESSION_STATE_TRANSITION_DONE,
+ FST_SESSION_STATE_TRANSITION_CONFIRMED,
+ FST_SESSION_STATE_LAST
+};
+
+enum fst_event_type {
+ EVENT_FST_IFACE_STATE_CHANGED, /* An interface has been either attached
+ * to or detached from an FST group */
+ EVENT_FST_ESTABLISHED, /* FST Session has been established */
+ EVENT_FST_SETUP, /* FST Session request received */
+ EVENT_FST_SESSION_STATE_CHANGED,/* FST Session state has been changed */
+ EVENT_PEER_STATE_CHANGED /* FST related generic event occurred,
+ * see struct fst_hostap_event_data for
+ * more info */
+};
+
+enum fst_initiator {
+ FST_INITIATOR_UNDEFINED,
+ FST_INITIATOR_LOCAL,
+ FST_INITIATOR_REMOTE,
+};
+
+union fst_event_extra {
+ struct fst_event_extra_iface_state {
+ Boolean attached;
+ char ifname[FST_MAX_INTERFACE_SIZE];
+ char group_id[FST_MAX_GROUP_ID_SIZE];
+ } iface_state; /* for EVENT_FST_IFACE_STATE_CHANGED */
+ struct fst_event_extra_peer_state {
+ Boolean connected;
+ char ifname[FST_MAX_INTERFACE_SIZE];
+ u8 addr[ETH_ALEN];
+ } peer_state; /* for EVENT_PEER_STATE_CHANGED */
+ struct fst_event_extra_session_state {
+ enum fst_session_state old_state;
+ enum fst_session_state new_state;
+ union fst_session_state_switch_extra {
+ struct {
+ enum fst_reason {
+ REASON_TEARDOWN,
+ REASON_SETUP,
+ REASON_SWITCH,
+ REASON_STT,
+ REASON_REJECT,
+ REASON_ERROR_PARAMS,
+ REASON_RESET,
+ REASON_DETACH_IFACE,
+ } reason;
+ u8 reject_code; /* REASON_REJECT */
+ /* REASON_SWITCH,
+ * REASON_TEARDOWN,
+ * REASON_REJECT
+ */
+ enum fst_initiator initiator;
+ } to_initial;
+ } extra;
+ } session_state; /* for EVENT_FST_SESSION_STATE_CHANGED */
+};
+
+/* helpers - prints enum in string form */
+#define FST_NAME_UNKNOWN "UNKNOWN"
+
+const char * fst_get_str_name(unsigned index, const char *names[],
+ size_t names_size);
+
+const char * fst_session_event_type_name(enum fst_event_type);
+const char * fst_reason_name(enum fst_reason reason);
+const char * fst_session_state_name(enum fst_session_state state);
+
+#endif /* FST_CTRL_AUX_H */
diff --git a/freebsd/contrib/wpa/src/fst/fst_ctrl_iface.h b/freebsd/contrib/wpa/src/fst/fst_ctrl_iface.h
new file mode 100644
index 00000000..4d0cd9fc
--- /dev/null
+++ b/freebsd/contrib/wpa/src/fst/fst_ctrl_iface.h
@@ -0,0 +1,45 @@
+/*
+ * FST module - internal Control interface definitions
+ * Copyright (c) 2014, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef FST_CTRL_IFACE_H
+#define FST_CTRL_IFACE_H
+
+#include "fst/fst_ctrl_aux.h"
+
+#ifdef CONFIG_FST
+
+/* receiver */
+int fst_ctrl_iface_mb_info(const u8 *addr, char *buf, size_t buflen);
+
+int fst_ctrl_iface_receive(const char *txtaddr, char *buf, size_t buflen);
+
+extern const struct fst_ctrl *fst_ctrl_cli;
+
+#else /* CONFIG_FST */
+
+static inline int
+fst_ctrl_iface_mb_info(const u8 *addr, char *buf, size_t buflen)
+{
+ return 0;
+}
+
+#endif /* CONFIG_FST */
+
+int fst_read_next_int_param(const char *params, Boolean *valid, char **endp);
+int fst_read_next_text_param(const char *params, char *buf, size_t buflen,
+ char **endp);
+int fst_read_peer_addr(const char *mac, u8 *peer_addr);
+
+struct fst_iface_cfg;
+
+int fst_parse_attach_command(const char *cmd, char *ifname, size_t ifname_size,
+ struct fst_iface_cfg *cfg);
+int fst_parse_detach_command(const char *cmd, char *ifname, size_t ifname_size);
+int fst_iface_detach(const char *ifname);
+
+#endif /* CTRL_IFACE_FST_H */
diff --git a/freebsd/contrib/wpa/src/l2_packet/l2_packet.h b/freebsd/contrib/wpa/src/l2_packet/l2_packet.h
new file mode 100644
index 00000000..2a452458
--- /dev/null
+++ b/freebsd/contrib/wpa/src/l2_packet/l2_packet.h
@@ -0,0 +1,154 @@
+/*
+ * WPA Supplicant - Layer2 packet interface definition
+ * Copyright (c) 2003-2005, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ *
+ * This file defines an interface for layer 2 (link layer) packet sending and
+ * receiving. l2_packet_linux.c is one implementation for such a layer 2
+ * implementation using Linux packet sockets and l2_packet_pcap.c another one
+ * using libpcap and libdnet. When porting %wpa_supplicant to other operating
+ * systems, a new l2_packet implementation may need to be added.
+ */
+
+#ifndef L2_PACKET_H
+#define L2_PACKET_H
+
+/**
+ * struct l2_packet_data - Internal l2_packet data structure
+ *
+ * This structure is used by the l2_packet implementation to store its private
+ * data. Other files use a pointer to this data when calling the l2_packet
+ * functions, but the contents of this structure should not be used directly
+ * outside l2_packet implementation.
+ */
+struct l2_packet_data;
+
+#ifdef _MSC_VER
+#pragma pack(push, 1)
+#endif /* _MSC_VER */
+
+struct l2_ethhdr {
+ u8 h_dest[ETH_ALEN];
+ u8 h_source[ETH_ALEN];
+ be16 h_proto;
+} STRUCT_PACKED;
+
+#ifdef _MSC_VER
+#pragma pack(pop)
+#endif /* _MSC_VER */
+
+enum l2_packet_filter_type {
+ L2_PACKET_FILTER_DHCP,
+ L2_PACKET_FILTER_NDISC,
+};
+
+/**
+ * l2_packet_init - Initialize l2_packet interface
+ * @ifname: Interface name
+ * @own_addr: Optional own MAC address if available from driver interface or
+ * %NULL if not available
+ * @protocol: Ethernet protocol number in host byte order
+ * @rx_callback: Callback function that will be called for each received packet
+ * @rx_callback_ctx: Callback data (ctx) for calls to rx_callback()
+ * @l2_hdr: 1 = include layer 2 header, 0 = do not include header
+ * Returns: Pointer to internal data or %NULL on failure
+ *
+ * rx_callback function will be called with src_addr pointing to the source
+ * address (MAC address) of the the packet. If l2_hdr is set to 0, buf
+ * points to len bytes of the payload after the layer 2 header and similarly,
+ * TX buffers start with payload. This behavior can be changed by setting
+ * l2_hdr=1 to include the layer 2 header in the data buffer.
+ */
+struct l2_packet_data * l2_packet_init(
+ const char *ifname, const u8 *own_addr, unsigned short protocol,
+ void (*rx_callback)(void *ctx, const u8 *src_addr,
+ const u8 *buf, size_t len),
+ void *rx_callback_ctx, int l2_hdr);
+
+/**
+ * l2_packet_init_bridge - Like l2_packet_init() but with bridge workaround
+ *
+ * This version of l2_packet_init() can be used to enable a workaround for Linux
+ * packet socket in case of a station interface in a bridge.
+ */
+struct l2_packet_data * l2_packet_init_bridge(
+ const char *br_ifname, const char *ifname, const u8 *own_addr,
+ unsigned short protocol,
+ void (*rx_callback)(void *ctx, const u8 *src_addr,
+ const u8 *buf, size_t len),
+ void *rx_callback_ctx, int l2_hdr);
+
+/**
+ * l2_packet_deinit - Deinitialize l2_packet interface
+ * @l2: Pointer to internal l2_packet data from l2_packet_init()
+ */
+void l2_packet_deinit(struct l2_packet_data *l2);
+
+/**
+ * l2_packet_get_own_addr - Get own layer 2 address
+ * @l2: Pointer to internal l2_packet data from l2_packet_init()
+ * @addr: Buffer for the own address (6 bytes)
+ * Returns: 0 on success, -1 on failure
+ */
+int l2_packet_get_own_addr(struct l2_packet_data *l2, u8 *addr);
+
+/**
+ * l2_packet_send - Send a packet
+ * @l2: Pointer to internal l2_packet data from l2_packet_init()
+ * @dst_addr: Destination address for the packet (only used if l2_hdr == 0)
+ * @proto: Protocol/ethertype for the packet in host byte order (only used if
+ * l2_hdr == 0)
+ * @buf: Packet contents to be sent; including layer 2 header if l2_hdr was
+ * set to 1 in l2_packet_init() call. Otherwise, only the payload of the packet
+ * is included.
+ * @len: Length of the buffer (including l2 header only if l2_hdr == 1)
+ * Returns: >=0 on success, <0 on failure
+ */
+int l2_packet_send(struct l2_packet_data *l2, const u8 *dst_addr, u16 proto,
+ const u8 *buf, size_t len);
+
+/**
+ * l2_packet_get_ip_addr - Get the current IP address from the interface
+ * @l2: Pointer to internal l2_packet data from l2_packet_init()
+ * @buf: Buffer for the IP address in text format
+ * @len: Maximum buffer length
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function can be used to get the current IP address from the interface
+ * bound to the l2_packet. This is mainly for status information and the IP
+ * address will be stored as an ASCII string. This function is not essential
+ * for %wpa_supplicant operation, so full implementation is not required.
+ * l2_packet implementation will need to define the function, but it can return
+ * -1 if the IP address information is not available.
+ */
+int l2_packet_get_ip_addr(struct l2_packet_data *l2, char *buf, size_t len);
+
+
+/**
+ * l2_packet_notify_auth_start - Notify l2_packet about start of authentication
+ * @l2: Pointer to internal l2_packet data from l2_packet_init()
+ *
+ * This function is called when authentication is expected to start, e.g., when
+ * association has been completed, in order to prepare l2_packet implementation
+ * for EAPOL frames. This function is used mainly if the l2_packet code needs
+ * to do polling in which case it can increasing polling frequency. This can
+ * also be an empty function if the l2_packet implementation does not benefit
+ * from knowing about the starting authentication.
+ */
+void l2_packet_notify_auth_start(struct l2_packet_data *l2);
+
+/**
+ * l2_packet_set_packet_filter - Set socket filter for l2_packet
+ * @l2: Pointer to internal l2_packet data from l2_packet_init()
+ * @type: enum l2_packet_filter_type, type of filter
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function is used to set the socket filter for l2_packet socket.
+ *
+ */
+int l2_packet_set_packet_filter(struct l2_packet_data *l2,
+ enum l2_packet_filter_type type);
+
+#endif /* L2_PACKET_H */
diff --git a/freebsd/contrib/wpa/src/l2_packet/l2_packet_freebsd.c b/freebsd/contrib/wpa/src/l2_packet/l2_packet_freebsd.c
new file mode 100644
index 00000000..555abc5e
--- /dev/null
+++ b/freebsd/contrib/wpa/src/l2_packet/l2_packet_freebsd.c
@@ -0,0 +1,331 @@
+#include <machine/rtems-bsd-user-space.h>
+
+/*
+ * WPA Supplicant - Layer2 packet handling with FreeBSD
+ * Copyright (c) 2003-2005, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2005, Sam Leffler <sam@errno.com>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+#if defined(__APPLE__) || defined(__GLIBC__)
+#include <net/bpf.h>
+#endif /* __APPLE__ */
+#include <pcap.h>
+
+#include <sys/ioctl.h>
+#ifdef __sun__
+#include <libdlpi.h>
+#else /* __sun__ */
+#include <sys/sysctl.h>
+#endif /* __sun__ */
+
+#include <net/if.h>
+#include <net/if_dl.h>
+#include <net/route.h>
+#include <netinet/in.h>
+
+#include "common.h"
+#include "eloop.h"
+#include "l2_packet.h"
+
+
+static const u8 pae_group_addr[ETH_ALEN] =
+{ 0x01, 0x80, 0xc2, 0x00, 0x00, 0x03 };
+
+struct l2_packet_data {
+ pcap_t *pcap;
+ char ifname[100];
+ u8 own_addr[ETH_ALEN];
+ void (*rx_callback)(void *ctx, const u8 *src_addr,
+ const u8 *buf, size_t len);
+ void *rx_callback_ctx;
+ int l2_hdr; /* whether to include layer 2 (Ethernet) header data
+ * buffers */
+};
+
+
+int l2_packet_get_own_addr(struct l2_packet_data *l2, u8 *addr)
+{
+ os_memcpy(addr, l2->own_addr, ETH_ALEN);
+ return 0;
+}
+
+
+int l2_packet_send(struct l2_packet_data *l2, const u8 *dst_addr, u16 proto,
+ const u8 *buf, size_t len)
+{
+ if (!l2->l2_hdr) {
+ int ret;
+ struct l2_ethhdr *eth = os_malloc(sizeof(*eth) + len);
+ if (eth == NULL)
+ return -1;
+ os_memcpy(eth->h_dest, dst_addr, ETH_ALEN);
+ os_memcpy(eth->h_source, l2->own_addr, ETH_ALEN);
+ eth->h_proto = htons(proto);
+ os_memcpy(eth + 1, buf, len);
+ ret = pcap_inject(l2->pcap, (u8 *) eth, len + sizeof(*eth));
+ os_free(eth);
+ return ret;
+ } else
+ return pcap_inject(l2->pcap, buf, len);
+}
+
+
+static void l2_packet_receive(int sock, void *eloop_ctx, void *sock_ctx)
+{
+ struct l2_packet_data *l2 = eloop_ctx;
+ pcap_t *pcap = sock_ctx;
+ struct pcap_pkthdr hdr;
+ const u_char *packet;
+ struct l2_ethhdr *ethhdr;
+ unsigned char *buf;
+ size_t len;
+
+ packet = pcap_next(pcap, &hdr);
+
+ if (packet == NULL || hdr.caplen < sizeof(*ethhdr))
+ return;
+
+ ethhdr = (struct l2_ethhdr *) packet;
+ if (l2->l2_hdr) {
+ buf = (unsigned char *) ethhdr;
+ len = hdr.caplen;
+ } else {
+ buf = (unsigned char *) (ethhdr + 1);
+ len = hdr.caplen - sizeof(*ethhdr);
+ }
+ l2->rx_callback(l2->rx_callback_ctx, ethhdr->h_source, buf, len);
+}
+
+
+static int l2_packet_init_libpcap(struct l2_packet_data *l2,
+ unsigned short protocol)
+{
+ bpf_u_int32 pcap_maskp, pcap_netp;
+ char pcap_filter[200], pcap_err[PCAP_ERRBUF_SIZE];
+ struct bpf_program pcap_fp;
+
+ pcap_lookupnet(l2->ifname, &pcap_netp, &pcap_maskp, pcap_err);
+ l2->pcap = pcap_open_live(l2->ifname, 2500, 0, 10, pcap_err);
+ if (l2->pcap == NULL) {
+ fprintf(stderr, "pcap_open_live: %s\n", pcap_err);
+ fprintf(stderr, "ifname='%s'\n", l2->ifname);
+ return -1;
+ }
+ if (pcap_datalink(l2->pcap) != DLT_EN10MB &&
+ pcap_set_datalink(l2->pcap, DLT_EN10MB) < 0) {
+ fprintf(stderr, "pcap_set_datalink(DLT_EN10MB): %s\n",
+ pcap_geterr(l2->pcap));
+ return -1;
+ }
+ os_snprintf(pcap_filter, sizeof(pcap_filter),
+ "not ether src " MACSTR " and "
+ "( ether dst " MACSTR " or ether dst " MACSTR " ) and "
+ "ether proto 0x%x",
+ MAC2STR(l2->own_addr), /* do not receive own packets */
+ MAC2STR(l2->own_addr), MAC2STR(pae_group_addr),
+ protocol);
+ if (pcap_compile(l2->pcap, &pcap_fp, pcap_filter, 1, pcap_netp) < 0) {
+ fprintf(stderr, "pcap_compile: %s\n", pcap_geterr(l2->pcap));
+ return -1;
+ }
+
+ if (pcap_setfilter(l2->pcap, &pcap_fp) < 0) {
+ fprintf(stderr, "pcap_setfilter: %s\n", pcap_geterr(l2->pcap));
+ return -1;
+ }
+
+ pcap_freecode(&pcap_fp);
+#ifndef __sun__
+ /*
+ * When libpcap uses BPF we must enable "immediate mode" to
+ * receive frames right away; otherwise the system may
+ * buffer them for us.
+ */
+ {
+ unsigned int on = 1;
+ if (ioctl(pcap_fileno(l2->pcap), BIOCIMMEDIATE, &on) < 0) {
+ fprintf(stderr, "%s: cannot enable immediate mode on "
+ "interface %s: %s\n",
+ __func__, l2->ifname, strerror(errno));
+ /* XXX should we fail? */
+ }
+ }
+#endif /* __sun__ */
+
+ eloop_register_read_sock(pcap_get_selectable_fd(l2->pcap),
+ l2_packet_receive, l2, l2->pcap);
+
+ return 0;
+}
+
+
+static int eth_get(const char *device, u8 ea[ETH_ALEN])
+{
+#ifdef __sun__
+ dlpi_handle_t dh;
+ u32 physaddrlen = DLPI_PHYSADDR_MAX;
+ u8 physaddr[DLPI_PHYSADDR_MAX];
+ int retval;
+
+ retval = dlpi_open(device, &dh, 0);
+ if (retval != DLPI_SUCCESS) {
+ wpa_printf(MSG_ERROR, "dlpi_open error: %s",
+ dlpi_strerror(retval));
+ return -1;
+ }
+
+ retval = dlpi_get_physaddr(dh, DL_CURR_PHYS_ADDR, physaddr,
+ &physaddrlen);
+ if (retval != DLPI_SUCCESS) {
+ wpa_printf(MSG_ERROR, "dlpi_get_physaddr error: %s",
+ dlpi_strerror(retval));
+ dlpi_close(dh);
+ return -1;
+ }
+ os_memcpy(ea, physaddr, ETH_ALEN);
+ dlpi_close(dh);
+#else /* __sun__ */
+ struct if_msghdr *ifm;
+ struct sockaddr_dl *sdl;
+ u_char *p, *buf;
+ size_t len;
+ int mib[] = { CTL_NET, AF_ROUTE, 0, AF_LINK, NET_RT_IFLIST, 0 };
+
+ if (sysctl(mib, 6, NULL, &len, NULL, 0) < 0)
+ return -1;
+ if ((buf = os_malloc(len)) == NULL)
+ return -1;
+ if (sysctl(mib, 6, buf, &len, NULL, 0) < 0) {
+ os_free(buf);
+ return -1;
+ }
+ for (p = buf; p < buf + len; p += ifm->ifm_msglen) {
+ ifm = (struct if_msghdr *)p;
+ sdl = (struct sockaddr_dl *)(ifm + 1);
+ if (ifm->ifm_type != RTM_IFINFO ||
+ (ifm->ifm_addrs & RTA_IFP) == 0)
+ continue;
+ if (sdl->sdl_family != AF_LINK || sdl->sdl_nlen == 0 ||
+ os_memcmp(sdl->sdl_data, device, sdl->sdl_nlen) != 0)
+ continue;
+ os_memcpy(ea, LLADDR(sdl), sdl->sdl_alen);
+ break;
+ }
+ os_free(buf);
+
+ if (p >= buf + len) {
+ errno = ESRCH;
+ return -1;
+ }
+#endif /* __sun__ */
+ return 0;
+}
+
+
+struct l2_packet_data * l2_packet_init(
+ const char *ifname, const u8 *own_addr, unsigned short protocol,
+ void (*rx_callback)(void *ctx, const u8 *src_addr,
+ const u8 *buf, size_t len),
+ void *rx_callback_ctx, int l2_hdr)
+{
+ struct l2_packet_data *l2;
+
+ l2 = os_zalloc(sizeof(struct l2_packet_data));
+ if (l2 == NULL)
+ return NULL;
+ os_strlcpy(l2->ifname, ifname, sizeof(l2->ifname));
+ l2->rx_callback = rx_callback;
+ l2->rx_callback_ctx = rx_callback_ctx;
+ l2->l2_hdr = l2_hdr;
+
+ if (eth_get(l2->ifname, l2->own_addr) < 0) {
+ fprintf(stderr, "Failed to get link-level address for "
+ "interface '%s'.\n", l2->ifname);
+ os_free(l2);
+ return NULL;
+ }
+
+ if (l2_packet_init_libpcap(l2, protocol)) {
+ os_free(l2);
+ return NULL;
+ }
+
+ return l2;
+}
+
+
+struct l2_packet_data * l2_packet_init_bridge(
+ const char *br_ifname, const char *ifname, const u8 *own_addr,
+ unsigned short protocol,
+ void (*rx_callback)(void *ctx, const u8 *src_addr,
+ const u8 *buf, size_t len),
+ void *rx_callback_ctx, int l2_hdr)
+{
+ return l2_packet_init(br_ifname, own_addr, protocol, rx_callback,
+ rx_callback_ctx, l2_hdr);
+}
+
+
+void l2_packet_deinit(struct l2_packet_data *l2)
+{
+ if (l2 != NULL) {
+ if (l2->pcap) {
+ eloop_unregister_read_sock(
+ pcap_get_selectable_fd(l2->pcap));
+ pcap_close(l2->pcap);
+ }
+ os_free(l2);
+ }
+}
+
+
+int l2_packet_get_ip_addr(struct l2_packet_data *l2, char *buf, size_t len)
+{
+ pcap_if_t *devs, *dev;
+ struct pcap_addr *addr;
+ struct sockaddr_in *saddr;
+ int found = 0;
+ char err[PCAP_ERRBUF_SIZE + 1];
+
+ if (pcap_findalldevs(&devs, err) < 0) {
+ wpa_printf(MSG_DEBUG, "pcap_findalldevs: %s\n", err);
+ return -1;
+ }
+
+ for (dev = devs; dev && !found; dev = dev->next) {
+ if (os_strcmp(dev->name, l2->ifname) != 0)
+ continue;
+
+ addr = dev->addresses;
+ while (addr) {
+ saddr = (struct sockaddr_in *) addr->addr;
+ if (saddr && saddr->sin_family == AF_INET) {
+ os_strlcpy(buf, inet_ntoa(saddr->sin_addr),
+ len);
+ found = 1;
+ break;
+ }
+ addr = addr->next;
+ }
+ }
+
+ pcap_freealldevs(devs);
+
+ return found ? 0 : -1;
+}
+
+
+void l2_packet_notify_auth_start(struct l2_packet_data *l2)
+{
+}
+
+
+int l2_packet_set_packet_filter(struct l2_packet_data *l2,
+ enum l2_packet_filter_type type)
+{
+ return -1;
+}
diff --git a/freebsd/contrib/wpa/src/p2p/p2p.h b/freebsd/contrib/wpa/src/p2p/p2p.h
new file mode 100644
index 00000000..b4060be4
--- /dev/null
+++ b/freebsd/contrib/wpa/src/p2p/p2p.h
@@ -0,0 +1,2343 @@
+/*
+ * Wi-Fi Direct - P2P module
+ * Copyright (c) 2009-2010, Atheros Communications
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef P2P_H
+#define P2P_H
+
+#include "common/ieee802_11_defs.h"
+#include "wps/wps.h"
+
+/* P2P ASP Setup Capability */
+#define P2PS_SETUP_NONE 0
+#define P2PS_SETUP_NEW BIT(0)
+#define P2PS_SETUP_CLIENT BIT(1)
+#define P2PS_SETUP_GROUP_OWNER BIT(2)
+
+#define P2PS_WILD_HASH_STR "org.wi-fi.wfds"
+#define P2PS_HASH_LEN 6
+#define P2P_MAX_QUERY_HASH 6
+#define P2PS_FEATURE_CAPAB_CPT_MAX 2
+
+/**
+ * P2P_MAX_PREF_CHANNELS - Maximum number of preferred channels
+ */
+#define P2P_MAX_PREF_CHANNELS 100
+
+/**
+ * P2P_MAX_REG_CLASSES - Maximum number of regulatory classes
+ */
+#define P2P_MAX_REG_CLASSES 10
+
+/**
+ * P2P_MAX_REG_CLASS_CHANNELS - Maximum number of channels per regulatory class
+ */
+#define P2P_MAX_REG_CLASS_CHANNELS 20
+
+/**
+ * struct p2p_channels - List of supported channels
+ */
+struct p2p_channels {
+ /**
+ * struct p2p_reg_class - Supported regulatory class
+ */
+ struct p2p_reg_class {
+ /**
+ * reg_class - Regulatory class (IEEE 802.11-2007, Annex J)
+ */
+ u8 reg_class;
+
+ /**
+ * channel - Supported channels
+ */
+ u8 channel[P2P_MAX_REG_CLASS_CHANNELS];
+
+ /**
+ * channels - Number of channel entries in use
+ */
+ size_t channels;
+ } reg_class[P2P_MAX_REG_CLASSES];
+
+ /**
+ * reg_classes - Number of reg_class entries in use
+ */
+ size_t reg_classes;
+};
+
+enum p2p_wps_method {
+ WPS_NOT_READY, WPS_PIN_DISPLAY, WPS_PIN_KEYPAD, WPS_PBC, WPS_NFC,
+ WPS_P2PS
+};
+
+/**
+ * struct p2p_go_neg_results - P2P Group Owner Negotiation results
+ */
+struct p2p_go_neg_results {
+ /**
+ * status - Negotiation result (Status Code)
+ *
+ * 0 (P2P_SC_SUCCESS) indicates success. Non-zero values indicate
+ * failed negotiation.
+ */
+ int status;
+
+ /**
+ * role_go - Whether local end is Group Owner
+ */
+ int role_go;
+
+ /**
+ * freq - Frequency of the group operational channel in MHz
+ */
+ int freq;
+
+ int ht40;
+
+ int vht;
+
+ /**
+ * ssid - SSID of the group
+ */
+ u8 ssid[SSID_MAX_LEN];
+
+ /**
+ * ssid_len - Length of SSID in octets
+ */
+ size_t ssid_len;
+
+ /**
+ * psk - WPA pre-shared key (256 bits) (GO only)
+ */
+ u8 psk[32];
+
+ /**
+ * psk_set - Whether PSK field is configured (GO only)
+ */
+ int psk_set;
+
+ /**
+ * passphrase - WPA2-Personal passphrase for the group (GO only)
+ */
+ char passphrase[64];
+
+ /**
+ * peer_device_addr - P2P Device Address of the peer
+ */
+ u8 peer_device_addr[ETH_ALEN];
+
+ /**
+ * peer_interface_addr - P2P Interface Address of the peer
+ */
+ u8 peer_interface_addr[ETH_ALEN];
+
+ /**
+ * wps_method - WPS method to be used during provisioning
+ */
+ enum p2p_wps_method wps_method;
+
+#define P2P_MAX_CHANNELS 50
+
+ /**
+ * freq_list - Zero-terminated list of possible operational channels
+ */
+ int freq_list[P2P_MAX_CHANNELS];
+
+ /**
+ * persistent_group - Whether the group should be made persistent
+ * 0 = not persistent
+ * 1 = persistent group without persistent reconnect
+ * 2 = persistent group with persistent reconnect
+ */
+ int persistent_group;
+
+ /**
+ * peer_config_timeout - Peer configuration timeout (in 10 msec units)
+ */
+ unsigned int peer_config_timeout;
+};
+
+struct p2ps_provision {
+ /**
+ * pd_seeker - P2PS provision discovery seeker role
+ */
+ unsigned int pd_seeker:1;
+
+ /**
+ * status - Remote returned provisioning status code
+ */
+ int status;
+
+ /**
+ * adv_id - P2PS Advertisement ID
+ */
+ u32 adv_id;
+
+ /**
+ * session_id - P2PS Session ID
+ */
+ u32 session_id;
+
+ /**
+ * method - WPS Method (to be) used to establish session
+ */
+ u16 method;
+
+ /**
+ * conncap - Connection Capabilities negotiated between P2P peers
+ */
+ u8 conncap;
+
+ /**
+ * role - Info about the roles to be used for this connection
+ */
+ u8 role;
+
+ /**
+ * session_mac - MAC address of the peer that started the session
+ */
+ u8 session_mac[ETH_ALEN];
+
+ /**
+ * adv_mac - MAC address of the peer advertised the service
+ */
+ u8 adv_mac[ETH_ALEN];
+
+ /**
+ * cpt_mask - Supported Coordination Protocol Transport mask
+ *
+ * A bitwise mask of supported ASP Coordination Protocol Transports.
+ * This property is set together and corresponds with cpt_priority.
+ */
+ u8 cpt_mask;
+
+ /**
+ * cpt_priority - Coordination Protocol Transport priority list
+ *
+ * Priorities of supported ASP Coordination Protocol Transports.
+ * This property is set together and corresponds with cpt_mask.
+ * The CPT priority list is 0 terminated.
+ */
+ u8 cpt_priority[P2PS_FEATURE_CAPAB_CPT_MAX + 1];
+
+ /**
+ * info - Vendor defined extra Provisioning information
+ */
+ char info[0];
+};
+
+struct p2ps_advertisement {
+ struct p2ps_advertisement *next;
+
+ /**
+ * svc_info - Pointer to (internal) Service defined information
+ */
+ char *svc_info;
+
+ /**
+ * id - P2PS Advertisement ID
+ */
+ u32 id;
+
+ /**
+ * config_methods - WPS Methods which are allowed for this service
+ */
+ u16 config_methods;
+
+ /**
+ * state - Current state of the service: 0 - Out Of Service, 1-255 Vendor defined
+ */
+ u8 state;
+
+ /**
+ * auto_accept - Automatically Accept provisioning request if possible.
+ */
+ u8 auto_accept;
+
+ /**
+ * hash - 6 octet Service Name has to match against incoming Probe Requests
+ */
+ u8 hash[P2PS_HASH_LEN];
+
+ /**
+ * cpt_mask - supported Coordination Protocol Transport mask
+ *
+ * A bitwise mask of supported ASP Coordination Protocol Transports.
+ * This property is set together and corresponds with cpt_priority.
+ */
+ u8 cpt_mask;
+
+ /**
+ * cpt_priority - Coordination Protocol Transport priority list
+ *
+ * Priorities of supported ASP Coordinatin Protocol Transports.
+ * This property is set together and corresponds with cpt_mask.
+ * The CPT priority list is 0 terminated.
+ */
+ u8 cpt_priority[P2PS_FEATURE_CAPAB_CPT_MAX + 1];
+
+ /**
+ * svc_name - NULL Terminated UTF-8 Service Name, and svc_info storage
+ */
+ char svc_name[0];
+};
+
+
+struct p2p_data;
+
+enum p2p_scan_type {
+ P2P_SCAN_SOCIAL,
+ P2P_SCAN_FULL,
+ P2P_SCAN_SPECIFIC,
+ P2P_SCAN_SOCIAL_PLUS_ONE
+};
+
+#define P2P_MAX_WPS_VENDOR_EXT 10
+
+/**
+ * struct p2p_peer_info - P2P peer information
+ */
+struct p2p_peer_info {
+ /**
+ * p2p_device_addr - P2P Device Address of the peer
+ */
+ u8 p2p_device_addr[ETH_ALEN];
+
+ /**
+ * pri_dev_type - Primary Device Type
+ */
+ u8 pri_dev_type[8];
+
+ /**
+ * device_name - Device Name (0..32 octets encoded in UTF-8)
+ */
+ char device_name[WPS_DEV_NAME_MAX_LEN + 1];
+
+ /**
+ * manufacturer - Manufacturer (0..64 octets encoded in UTF-8)
+ */
+ char manufacturer[WPS_MANUFACTURER_MAX_LEN + 1];
+
+ /**
+ * model_name - Model Name (0..32 octets encoded in UTF-8)
+ */
+ char model_name[WPS_MODEL_NAME_MAX_LEN + 1];
+
+ /**
+ * model_number - Model Number (0..32 octets encoded in UTF-8)
+ */
+ char model_number[WPS_MODEL_NUMBER_MAX_LEN + 1];
+
+ /**
+ * serial_number - Serial Number (0..32 octets encoded in UTF-8)
+ */
+ char serial_number[WPS_SERIAL_NUMBER_MAX_LEN + 1];
+
+ /**
+ * level - Signal level
+ */
+ int level;
+
+ /**
+ * config_methods - WPS Configuration Methods
+ */
+ u16 config_methods;
+
+ /**
+ * dev_capab - Device Capabilities
+ */
+ u8 dev_capab;
+
+ /**
+ * group_capab - Group Capabilities
+ */
+ u8 group_capab;
+
+ /**
+ * wps_sec_dev_type_list - WPS secondary device type list
+ *
+ * This list includes from 0 to 16 Secondary Device Types as indicated
+ * by wps_sec_dev_type_list_len (8 * number of types).
+ */
+ u8 wps_sec_dev_type_list[WPS_SEC_DEV_TYPE_MAX_LEN];
+
+ /**
+ * wps_sec_dev_type_list_len - Length of secondary device type list
+ */
+ size_t wps_sec_dev_type_list_len;
+
+ struct wpabuf *wps_vendor_ext[P2P_MAX_WPS_VENDOR_EXT];
+
+ /**
+ * wfd_subelems - Wi-Fi Display subelements from WFD IE(s)
+ */
+ struct wpabuf *wfd_subelems;
+
+ /**
+ * vendor_elems - Unrecognized vendor elements
+ *
+ * This buffer includes any other vendor element than P2P, WPS, and WFD
+ * IE(s) from the frame that was used to discover the peer.
+ */
+ struct wpabuf *vendor_elems;
+
+ /**
+ * p2ps_instance - P2PS Application Service Info
+ */
+ struct wpabuf *p2ps_instance;
+};
+
+enum p2p_prov_disc_status {
+ P2P_PROV_DISC_SUCCESS,
+ P2P_PROV_DISC_TIMEOUT,
+ P2P_PROV_DISC_REJECTED,
+ P2P_PROV_DISC_TIMEOUT_JOIN,
+ P2P_PROV_DISC_INFO_UNAVAILABLE,
+};
+
+struct p2p_channel {
+ u8 op_class;
+ u8 chan;
+};
+
+/**
+ * struct p2p_config - P2P configuration
+ *
+ * This configuration is provided to the P2P module during initialization with
+ * p2p_init().
+ */
+struct p2p_config {
+ /**
+ * country - Country code to use in P2P operations
+ */
+ char country[3];
+
+ /**
+ * reg_class - Regulatory class for own listen channel
+ */
+ u8 reg_class;
+
+ /**
+ * channel - Own listen channel
+ */
+ u8 channel;
+
+ /**
+ * channel_forced - the listen channel was forced by configuration
+ * or by control interface and cannot be overridden
+ */
+ u8 channel_forced;
+
+ /**
+ * Regulatory class for own operational channel
+ */
+ u8 op_reg_class;
+
+ /**
+ * op_channel - Own operational channel
+ */
+ u8 op_channel;
+
+ /**
+ * cfg_op_channel - Whether op_channel is hardcoded in configuration
+ */
+ u8 cfg_op_channel;
+
+ /**
+ * channels - Own supported regulatory classes and channels
+ *
+ * List of supposerted channels per regulatory class. The regulatory
+ * classes are defined in IEEE Std 802.11-2007 Annex J and the
+ * numbering of the clases depends on the configured country code.
+ */
+ struct p2p_channels channels;
+
+ /**
+ * cli_channels - Additional client channels
+ *
+ * This list of channels (if any) will be used when advertising local
+ * channels during GO Negotiation or Invitation for the cases where the
+ * local end may become the client. This may allow the peer to become a
+ * GO on additional channels if it supports these options. The main use
+ * case for this is to include passive-scan channels on devices that may
+ * not know their current location and have configured most channels to
+ * not allow initiation of radition (i.e., another device needs to take
+ * master responsibilities).
+ */
+ struct p2p_channels cli_channels;
+
+ /**
+ * num_pref_chan - Number of pref_chan entries
+ */
+ unsigned int num_pref_chan;
+
+ /**
+ * pref_chan - Preferred channels for GO Negotiation
+ */
+ struct p2p_channel *pref_chan;
+
+ /**
+ * pri_dev_type - Primary Device Type (see WPS)
+ */
+ u8 pri_dev_type[8];
+
+ /**
+ * P2P_SEC_DEVICE_TYPES - Maximum number of secondary device types
+ */
+#define P2P_SEC_DEVICE_TYPES 5
+
+ /**
+ * sec_dev_type - Optional secondary device types
+ */
+ u8 sec_dev_type[P2P_SEC_DEVICE_TYPES][8];
+
+ /**
+ * num_sec_dev_types - Number of sec_dev_type entries
+ */
+ size_t num_sec_dev_types;
+
+ /**
+ * dev_addr - P2P Device Address
+ */
+ u8 dev_addr[ETH_ALEN];
+
+ /**
+ * dev_name - Device Name
+ */
+ char *dev_name;
+
+ char *manufacturer;
+ char *model_name;
+ char *model_number;
+ char *serial_number;
+
+ u8 uuid[16];
+ u16 config_methods;
+
+ /**
+ * concurrent_operations - Whether concurrent operations are supported
+ */
+ int concurrent_operations;
+
+ /**
+ * max_peers - Maximum number of discovered peers to remember
+ *
+ * If more peers are discovered, older entries will be removed to make
+ * room for the new ones.
+ */
+ size_t max_peers;
+
+ /**
+ * p2p_intra_bss - Intra BSS communication is supported
+ */
+ int p2p_intra_bss;
+
+ /**
+ * ssid_postfix - Postfix data to add to the SSID
+ *
+ * This data will be added to the end of the SSID after the
+ * DIRECT-<random two octets> prefix.
+ */
+ u8 ssid_postfix[SSID_MAX_LEN - 9];
+
+ /**
+ * ssid_postfix_len - Length of the ssid_postfix data
+ */
+ size_t ssid_postfix_len;
+
+ /**
+ * max_listen - Maximum listen duration in ms
+ */
+ unsigned int max_listen;
+
+ /**
+ * passphrase_len - Passphrase length (8..63)
+ *
+ * This parameter controls the length of the random passphrase that is
+ * generated at the GO.
+ */
+ unsigned int passphrase_len;
+
+ /**
+ * cb_ctx - Context to use with callback functions
+ */
+ void *cb_ctx;
+
+ /**
+ * debug_print - Debug print
+ * @ctx: Callback context from cb_ctx
+ * @level: Debug verbosity level (MSG_*)
+ * @msg: Debug message
+ */
+ void (*debug_print)(void *ctx, int level, const char *msg);
+
+
+ /* Callbacks to request lower layer driver operations */
+
+ /**
+ * p2p_scan - Request a P2P scan/search
+ * @ctx: Callback context from cb_ctx
+ * @type: Scan type
+ * @freq: Specific frequency (MHz) to scan or 0 for no restriction
+ * @num_req_dev_types: Number of requested device types
+ * @req_dev_types: Array containing requested device types
+ * @dev_id: Device ID to search for or %NULL to find all devices
+ * @pw_id: Device Password ID
+ * Returns: 0 on success, -1 on failure
+ *
+ * This callback function is used to request a P2P scan or search
+ * operation to be completed. Type type argument specifies which type
+ * of scan is to be done. @P2P_SCAN_SOCIAL indicates that only the
+ * social channels (1, 6, 11) should be scanned. @P2P_SCAN_FULL
+ * indicates that all channels are to be scanned. @P2P_SCAN_SPECIFIC
+ * request a scan of a single channel specified by freq.
+ * @P2P_SCAN_SOCIAL_PLUS_ONE request scan of all the social channels
+ * plus one extra channel specified by freq.
+ *
+ * The full scan is used for the initial scan to find group owners from
+ * all. The other types are used during search phase scan of the social
+ * channels (with potential variation if the Listen channel of the
+ * target peer is known or if other channels are scanned in steps).
+ *
+ * The scan results are returned after this call by calling
+ * p2p_scan_res_handler() for each scan result that has a P2P IE and
+ * then calling p2p_scan_res_handled() to indicate that all scan
+ * results have been indicated.
+ */
+ int (*p2p_scan)(void *ctx, enum p2p_scan_type type, int freq,
+ unsigned int num_req_dev_types,
+ const u8 *req_dev_types, const u8 *dev_id, u16 pw_id);
+
+ /**
+ * send_probe_resp - Transmit a Probe Response frame
+ * @ctx: Callback context from cb_ctx
+ * @buf: Probe Response frame (including the header and body)
+ * @freq: Forced frequency (in MHz) to use or 0.
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function is used to reply to Probe Request frames that were
+ * indicated with a call to p2p_probe_req_rx(). The response is to be
+ * sent on the same channel, unless otherwise specified, or to be
+ * dropped if the driver is not listening to Probe Request frames
+ * anymore.
+ *
+ * Alternatively, the responsibility for building the Probe Response
+ * frames in Listen state may be in another system component in which
+ * case this function need to be implemented (i.e., the function
+ * pointer can be %NULL). The WPS and P2P IEs to be added for Probe
+ * Response frames in such a case are available from the
+ * start_listen() callback. It should be noted that the received Probe
+ * Request frames must be indicated by calling p2p_probe_req_rx() even
+ * if this send_probe_resp() is not used.
+ */
+ int (*send_probe_resp)(void *ctx, const struct wpabuf *buf,
+ unsigned int freq);
+
+ /**
+ * send_action - Transmit an Action frame
+ * @ctx: Callback context from cb_ctx
+ * @freq: Frequency in MHz for the channel on which to transmit
+ * @dst: Destination MAC address (Address 1)
+ * @src: Source MAC address (Address 2)
+ * @bssid: BSSID (Address 3)
+ * @buf: Frame body (starting from Category field)
+ * @len: Length of buf in octets
+ * @wait_time: How many msec to wait for a response frame
+ * Returns: 0 on success, -1 on failure
+ *
+ * The Action frame may not be transmitted immediately and the status
+ * of the transmission must be reported by calling
+ * p2p_send_action_cb() once the frame has either been transmitted or
+ * it has been dropped due to excessive retries or other failure to
+ * transmit.
+ */
+ int (*send_action)(void *ctx, unsigned int freq, const u8 *dst,
+ const u8 *src, const u8 *bssid, const u8 *buf,
+ size_t len, unsigned int wait_time);
+
+ /**
+ * send_action_done - Notify that Action frame sequence was completed
+ * @ctx: Callback context from cb_ctx
+ *
+ * This function is called when the Action frame sequence that was
+ * started with send_action() has been completed, i.e., when there is
+ * no need to wait for a response from the destination peer anymore.
+ */
+ void (*send_action_done)(void *ctx);
+
+ /**
+ * start_listen - Start Listen state
+ * @ctx: Callback context from cb_ctx
+ * @freq: Frequency of the listen channel in MHz
+ * @duration: Duration for the Listen state in milliseconds
+ * @probe_resp_ie: IE(s) to be added to Probe Response frames
+ * Returns: 0 on success, -1 on failure
+ *
+ * This Listen state may not start immediately since the driver may
+ * have other pending operations to complete first. Once the Listen
+ * state has started, p2p_listen_cb() must be called to notify the P2P
+ * module. Once the Listen state is stopped, p2p_listen_end() must be
+ * called to notify the P2P module that the driver is not in the Listen
+ * state anymore.
+ *
+ * If the send_probe_resp() is not used for generating the response,
+ * the IEs from probe_resp_ie need to be added to the end of the Probe
+ * Response frame body. If send_probe_resp() is used, the probe_resp_ie
+ * information can be ignored.
+ */
+ int (*start_listen)(void *ctx, unsigned int freq,
+ unsigned int duration,
+ const struct wpabuf *probe_resp_ie);
+ /**
+ * stop_listen - Stop Listen state
+ * @ctx: Callback context from cb_ctx
+ *
+ * This callback can be used to stop a Listen state operation that was
+ * previously requested with start_listen().
+ */
+ void (*stop_listen)(void *ctx);
+
+ /**
+ * get_noa - Get current Notice of Absence attribute payload
+ * @ctx: Callback context from cb_ctx
+ * @interface_addr: P2P Interface Address of the GO
+ * @buf: Buffer for returning NoA
+ * @buf_len: Buffer length in octets
+ * Returns: Number of octets used in buf, 0 to indicate no NoA is being
+ * advertized, or -1 on failure
+ *
+ * This function is used to fetch the current Notice of Absence
+ * attribute value from GO.
+ */
+ int (*get_noa)(void *ctx, const u8 *interface_addr, u8 *buf,
+ size_t buf_len);
+
+ /* Callbacks to notify events to upper layer management entity */
+
+ /**
+ * dev_found - Notification of a found P2P Device
+ * @ctx: Callback context from cb_ctx
+ * @addr: Source address of the message triggering this notification
+ * @info: P2P peer information
+ * @new_device: Inform if the peer is newly found
+ *
+ * This callback is used to notify that a new P2P Device has been
+ * found. This may happen, e.g., during Search state based on scan
+ * results or during Listen state based on receive Probe Request and
+ * Group Owner Negotiation Request.
+ */
+ void (*dev_found)(void *ctx, const u8 *addr,
+ const struct p2p_peer_info *info,
+ int new_device);
+
+ /**
+ * dev_lost - Notification of a lost P2P Device
+ * @ctx: Callback context from cb_ctx
+ * @dev_addr: P2P Device Address of the lost P2P Device
+ *
+ * This callback is used to notify that a P2P Device has been deleted.
+ */
+ void (*dev_lost)(void *ctx, const u8 *dev_addr);
+
+ /**
+ * find_stopped - Notification of a p2p_find operation stopping
+ * @ctx: Callback context from cb_ctx
+ */
+ void (*find_stopped)(void *ctx);
+
+ /**
+ * go_neg_req_rx - Notification of a receive GO Negotiation Request
+ * @ctx: Callback context from cb_ctx
+ * @src: Source address of the message triggering this notification
+ * @dev_passwd_id: WPS Device Password ID
+ * @go_intent: Peer's GO Intent
+ *
+ * This callback is used to notify that a P2P Device is requesting
+ * group owner negotiation with us, but we do not have all the
+ * necessary information to start GO Negotiation. This indicates that
+ * the local user has not authorized the connection yet by providing a
+ * PIN or PBC button press. This information can be provided with a
+ * call to p2p_connect().
+ */
+ void (*go_neg_req_rx)(void *ctx, const u8 *src, u16 dev_passwd_id,
+ u8 go_intent);
+
+ /**
+ * go_neg_completed - Notification of GO Negotiation results
+ * @ctx: Callback context from cb_ctx
+ * @res: GO Negotiation results
+ *
+ * This callback is used to notify that Group Owner Negotiation has
+ * been completed. Non-zero struct p2p_go_neg_results::status indicates
+ * failed negotiation. In case of success, this function is responsible
+ * for creating a new group interface (or using the existing interface
+ * depending on driver features), setting up the group interface in
+ * proper mode based on struct p2p_go_neg_results::role_go and
+ * initializing WPS provisioning either as a Registrar (if GO) or as an
+ * Enrollee. Successful WPS provisioning must be indicated by calling
+ * p2p_wps_success_cb(). The callee is responsible for timing out group
+ * formation if WPS provisioning cannot be completed successfully
+ * within 15 seconds.
+ */
+ void (*go_neg_completed)(void *ctx, struct p2p_go_neg_results *res);
+
+ /**
+ * sd_request - Callback on Service Discovery Request
+ * @ctx: Callback context from cb_ctx
+ * @freq: Frequency (in MHz) of the channel
+ * @sa: Source address of the request
+ * @dialog_token: Dialog token
+ * @update_indic: Service Update Indicator from the source of request
+ * @tlvs: P2P Service Request TLV(s)
+ * @tlvs_len: Length of tlvs buffer in octets
+ *
+ * This callback is used to indicate reception of a service discovery
+ * request. Response to the query must be indicated by calling
+ * p2p_sd_response() with the context information from the arguments to
+ * this callback function.
+ *
+ * This callback handler can be set to %NULL to indicate that service
+ * discovery is not supported.
+ */
+ void (*sd_request)(void *ctx, int freq, const u8 *sa, u8 dialog_token,
+ u16 update_indic, const u8 *tlvs, size_t tlvs_len);
+
+ /**
+ * sd_response - Callback on Service Discovery Response
+ * @ctx: Callback context from cb_ctx
+ * @sa: Source address of the request
+ * @update_indic: Service Update Indicator from the source of response
+ * @tlvs: P2P Service Response TLV(s)
+ * @tlvs_len: Length of tlvs buffer in octets
+ *
+ * This callback is used to indicate reception of a service discovery
+ * response. This callback handler can be set to %NULL if no service
+ * discovery requests are used. The information provided with this call
+ * is replies to the queries scheduled with p2p_sd_request().
+ */
+ void (*sd_response)(void *ctx, const u8 *sa, u16 update_indic,
+ const u8 *tlvs, size_t tlvs_len);
+
+ /**
+ * prov_disc_req - Callback on Provisiong Discovery Request
+ * @ctx: Callback context from cb_ctx
+ * @peer: Source address of the request
+ * @config_methods: Requested WPS Config Method
+ * @dev_addr: P2P Device Address of the found P2P Device
+ * @pri_dev_type: Primary Device Type
+ * @dev_name: Device Name
+ * @supp_config_methods: Supported configuration Methods
+ * @dev_capab: Device Capabilities
+ * @group_capab: Group Capabilities
+ * @group_id: P2P Group ID (or %NULL if not included)
+ * @group_id_len: Length of P2P Group ID
+ *
+ * This callback is used to indicate reception of a Provision Discovery
+ * Request frame that the P2P module accepted.
+ */
+ void (*prov_disc_req)(void *ctx, const u8 *peer, u16 config_methods,
+ const u8 *dev_addr, const u8 *pri_dev_type,
+ const char *dev_name, u16 supp_config_methods,
+ u8 dev_capab, u8 group_capab,
+ const u8 *group_id, size_t group_id_len);
+
+ /**
+ * prov_disc_resp - Callback on Provisiong Discovery Response
+ * @ctx: Callback context from cb_ctx
+ * @peer: Source address of the response
+ * @config_methods: Value from p2p_prov_disc_req() or 0 on failure
+ *
+ * This callback is used to indicate reception of a Provision Discovery
+ * Response frame for a pending request scheduled with
+ * p2p_prov_disc_req(). This callback handler can be set to %NULL if
+ * provision discovery is not used.
+ */
+ void (*prov_disc_resp)(void *ctx, const u8 *peer, u16 config_methods);
+
+ /**
+ * prov_disc_fail - Callback on Provision Discovery failure
+ * @ctx: Callback context from cb_ctx
+ * @peer: Source address of the response
+ * @status: Cause of failure, will not be %P2P_PROV_DISC_SUCCESS
+ * @adv_id: If non-zero, then the adv_id of the PD Request
+ * @adv_mac: P2P Device Address of the advertizer
+ * @deferred_session_resp: Deferred session response sent by advertizer
+ *
+ * This callback is used to indicate either a failure or no response
+ * to an earlier provision discovery request.
+ *
+ * This callback handler can be set to %NULL if provision discovery
+ * is not used or failures do not need to be indicated.
+ */
+ void (*prov_disc_fail)(void *ctx, const u8 *peer,
+ enum p2p_prov_disc_status status,
+ u32 adv_id, const u8 *adv_mac,
+ const char *deferred_session_resp);
+
+ /**
+ * invitation_process - Optional callback for processing Invitations
+ * @ctx: Callback context from cb_ctx
+ * @sa: Source address of the Invitation Request
+ * @bssid: P2P Group BSSID from the request or %NULL if not included
+ * @go_dev_addr: GO Device Address from P2P Group ID
+ * @ssid: SSID from P2P Group ID
+ * @ssid_len: Length of ssid buffer in octets
+ * @go: Variable for returning whether the local end is GO in the group
+ * @group_bssid: Buffer for returning P2P Group BSSID (if local end GO)
+ * @force_freq: Variable for returning forced frequency for the group
+ * @persistent_group: Whether this is an invitation to reinvoke a
+ * persistent group (instead of invitation to join an active
+ * group)
+ * @channels: Available operating channels for the group
+ * @dev_pw_id: Device Password ID for NFC static handover or -1 if not
+ * used
+ * Returns: Status code (P2P_SC_*)
+ *
+ * This optional callback can be used to implement persistent reconnect
+ * by allowing automatic restarting of persistent groups without user
+ * interaction. If this callback is not implemented (i.e., is %NULL),
+ * the received Invitation Request frames are replied with
+ * %P2P_SC_REQ_RECEIVED status and indicated to upper layer with the
+ * invitation_result() callback.
+ *
+ * If the requested parameters are acceptable and the group is known,
+ * %P2P_SC_SUCCESS may be returned. If the requested group is unknown,
+ * %P2P_SC_FAIL_UNKNOWN_GROUP should be returned. %P2P_SC_REQ_RECEIVED
+ * can be returned if there is not enough data to provide immediate
+ * response, i.e., if some sort of user interaction is needed. The
+ * invitation_received() callback will be called in that case
+ * immediately after this call.
+ */
+ u8 (*invitation_process)(void *ctx, const u8 *sa, const u8 *bssid,
+ const u8 *go_dev_addr, const u8 *ssid,
+ size_t ssid_len, int *go, u8 *group_bssid,
+ int *force_freq, int persistent_group,
+ const struct p2p_channels *channels,
+ int dev_pw_id);
+
+ /**
+ * invitation_received - Callback on Invitation Request RX
+ * @ctx: Callback context from cb_ctx
+ * @sa: Source address of the Invitation Request
+ * @bssid: P2P Group BSSID or %NULL if not received
+ * @ssid: SSID of the group
+ * @ssid_len: Length of ssid in octets
+ * @go_dev_addr: GO Device Address
+ * @status: Response Status
+ * @op_freq: Operational frequency for the group
+ *
+ * This callback is used to indicate sending of an Invitation Response
+ * for a received Invitation Request. If status == 0 (success), the
+ * upper layer code is responsible for starting the group. status == 1
+ * indicates need to get user authorization for the group. Other status
+ * values indicate that the invitation request was rejected.
+ */
+ void (*invitation_received)(void *ctx, const u8 *sa, const u8 *bssid,
+ const u8 *ssid, size_t ssid_len,
+ const u8 *go_dev_addr, u8 status,
+ int op_freq);
+
+ /**
+ * invitation_result - Callback on Invitation result
+ * @ctx: Callback context from cb_ctx
+ * @status: Negotiation result (Status Code)
+ * @bssid: P2P Group BSSID or %NULL if not received
+ * @channels: Available operating channels for the group
+ * @addr: Peer address
+ * @freq: Frequency (in MHz) indicated during invitation or 0
+ * @peer_oper_freq: Operating frequency (in MHz) advertized by the peer
+ * during invitation or 0
+ *
+ * This callback is used to indicate result of an Invitation procedure
+ * started with a call to p2p_invite(). The indicated status code is
+ * the value received from the peer in Invitation Response with 0
+ * (P2P_SC_SUCCESS) indicating success or -1 to indicate a timeout or a
+ * local failure in transmitting the Invitation Request.
+ */
+ void (*invitation_result)(void *ctx, int status, const u8 *bssid,
+ const struct p2p_channels *channels,
+ const u8 *addr, int freq, int peer_oper_freq);
+
+ /**
+ * go_connected - Check whether we are connected to a GO
+ * @ctx: Callback context from cb_ctx
+ * @dev_addr: P2P Device Address of a GO
+ * Returns: 1 if we are connected as a P2P client to the specified GO
+ * or 0 if not.
+ */
+ int (*go_connected)(void *ctx, const u8 *dev_addr);
+
+ /**
+ * presence_resp - Callback on Presence Response
+ * @ctx: Callback context from cb_ctx
+ * @src: Source address (GO's P2P Interface Address)
+ * @status: Result of the request (P2P_SC_*)
+ * @noa: Returned NoA value
+ * @noa_len: Length of the NoA buffer in octets
+ */
+ void (*presence_resp)(void *ctx, const u8 *src, u8 status,
+ const u8 *noa, size_t noa_len);
+
+ /**
+ * is_concurrent_session_active - Check whether concurrent session is
+ * active on other virtual interfaces
+ * @ctx: Callback context from cb_ctx
+ * Returns: 1 if concurrent session is active on other virtual interface
+ * or 0 if not.
+ */
+ int (*is_concurrent_session_active)(void *ctx);
+
+ /**
+ * is_p2p_in_progress - Check whether P2P operation is in progress
+ * @ctx: Callback context from cb_ctx
+ * Returns: 1 if P2P operation (e.g., group formation) is in progress
+ * or 0 if not.
+ */
+ int (*is_p2p_in_progress)(void *ctx);
+
+ /**
+ * Determine if we have a persistent group we share with remote peer
+ * and allocate interface for this group if needed
+ * @ctx: Callback context from cb_ctx
+ * @addr: Peer device address to search for
+ * @ssid: Persistent group SSID or %NULL if any
+ * @ssid_len: Length of @ssid
+ * @go_dev_addr: Buffer for returning GO P2P Device Address
+ * @ret_ssid: Buffer for returning group SSID
+ * @ret_ssid_len: Buffer for returning length of @ssid
+ * @intended_iface_addr: Buffer for returning intended iface address
+ * Returns: 1 if a matching persistent group was found, 0 otherwise
+ */
+ int (*get_persistent_group)(void *ctx, const u8 *addr, const u8 *ssid,
+ size_t ssid_len, u8 *go_dev_addr,
+ u8 *ret_ssid, size_t *ret_ssid_len,
+ u8 *intended_iface_addr);
+
+ /**
+ * Get information about a possible local GO role
+ * @ctx: Callback context from cb_ctx
+ * @intended_addr: Buffer for returning intended GO interface address
+ * @ssid: Buffer for returning group SSID
+ * @ssid_len: Buffer for returning length of @ssid
+ * @group_iface: Buffer for returning whether a separate group interface
+ * would be used
+ * Returns: 1 if GO info found, 0 otherwise
+ *
+ * This is used to compose New Group settings (SSID, and intended
+ * address) during P2PS provisioning if results of provisioning *might*
+ * result in our being an autonomous GO.
+ */
+ int (*get_go_info)(void *ctx, u8 *intended_addr,
+ u8 *ssid, size_t *ssid_len, int *group_iface);
+
+ /**
+ * remove_stale_groups - Remove stale P2PS groups
+ *
+ * Because P2PS stages *potential* GOs, and remote devices can remove
+ * credentials unilaterally, we need to make sure we don't let stale
+ * unusable groups build up.
+ */
+ int (*remove_stale_groups)(void *ctx, const u8 *peer, const u8 *go,
+ const u8 *ssid, size_t ssid_len);
+
+ /**
+ * p2ps_prov_complete - P2PS provisioning complete
+ *
+ * When P2PS provisioning completes (successfully or not) we must
+ * transmit all of the results to the upper layers.
+ */
+ void (*p2ps_prov_complete)(void *ctx, u8 status, const u8 *dev,
+ const u8 *adv_mac, const u8 *ses_mac,
+ const u8 *grp_mac, u32 adv_id, u32 ses_id,
+ u8 conncap, int passwd_id,
+ const u8 *persist_ssid,
+ size_t persist_ssid_size, int response_done,
+ int prov_start, const char *session_info,
+ const u8 *feat_cap, size_t feat_cap_len);
+
+ /**
+ * prov_disc_resp_cb - Callback for indicating completion of PD Response
+ * @ctx: Callback context from cb_ctx
+ * Returns: 1 if operation was started, 0 otherwise
+ *
+ * This callback can be used to perform any pending actions after
+ * provisioning. It is mainly used for P2PS pending group creation.
+ */
+ int (*prov_disc_resp_cb)(void *ctx);
+
+ /**
+ * p2ps_group_capability - Determine group capability
+ *
+ * This function can be used to determine group capability based on
+ * information from P2PS PD exchange and the current state of ongoing
+ * groups and driver capabilities.
+ *
+ * P2PS_SETUP_* bitmap is used as the parameters and return value.
+ */
+ u8 (*p2ps_group_capability)(void *ctx, u8 incoming, u8 role);
+
+ /**
+ * get_pref_freq_list - Get preferred frequency list for an interface
+ * @ctx: Callback context from cb_ctx
+ * @go: Whether the use if for GO role
+ * @len: Length of freq_list in entries (both IN and OUT)
+ * @freq_list: Buffer for returning the preferred frequencies (MHz)
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function can be used to query the preferred frequency list from
+ * the driver specific to a particular interface type.
+ */
+ int (*get_pref_freq_list)(void *ctx, int go,
+ unsigned int *len, unsigned int *freq_list);
+};
+
+
+/* P2P module initialization/deinitialization */
+
+/**
+ * p2p_init - Initialize P2P module
+ * @cfg: P2P module configuration
+ * Returns: Pointer to private data or %NULL on failure
+ *
+ * This function is used to initialize global P2P module context (one per
+ * device). The P2P module will keep a copy of the configuration data, so the
+ * caller does not need to maintain this structure. However, the callback
+ * functions and the context parameters to them must be kept available until
+ * the P2P module is deinitialized with p2p_deinit().
+ */
+struct p2p_data * p2p_init(const struct p2p_config *cfg);
+
+/**
+ * p2p_deinit - Deinitialize P2P module
+ * @p2p: P2P module context from p2p_init()
+ */
+void p2p_deinit(struct p2p_data *p2p);
+
+/**
+ * p2p_flush - Flush P2P module state
+ * @p2p: P2P module context from p2p_init()
+ *
+ * This command removes the P2P module state like peer device entries.
+ */
+void p2p_flush(struct p2p_data *p2p);
+
+/**
+ * p2p_unauthorize - Unauthorize the specified peer device
+ * @p2p: P2P module context from p2p_init()
+ * @addr: P2P peer entry to be unauthorized
+ * Returns: 0 on success, -1 on failure
+ *
+ * This command removes any connection authorization from the specified P2P
+ * peer device address. This can be used, e.g., to cancel effect of a previous
+ * p2p_authorize() or p2p_connect() call that has not yet resulted in completed
+ * GO Negotiation.
+ */
+int p2p_unauthorize(struct p2p_data *p2p, const u8 *addr);
+
+/**
+ * p2p_set_dev_name - Set device name
+ * @p2p: P2P module context from p2p_init()
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function can be used to update the P2P module configuration with
+ * information that was not available at the time of the p2p_init() call.
+ */
+int p2p_set_dev_name(struct p2p_data *p2p, const char *dev_name);
+
+int p2p_set_manufacturer(struct p2p_data *p2p, const char *manufacturer);
+int p2p_set_model_name(struct p2p_data *p2p, const char *model_name);
+int p2p_set_model_number(struct p2p_data *p2p, const char *model_number);
+int p2p_set_serial_number(struct p2p_data *p2p, const char *serial_number);
+
+void p2p_set_config_methods(struct p2p_data *p2p, u16 config_methods);
+void p2p_set_uuid(struct p2p_data *p2p, const u8 *uuid);
+
+/**
+ * p2p_set_pri_dev_type - Set primary device type
+ * @p2p: P2P module context from p2p_init()
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function can be used to update the P2P module configuration with
+ * information that was not available at the time of the p2p_init() call.
+ */
+int p2p_set_pri_dev_type(struct p2p_data *p2p, const u8 *pri_dev_type);
+
+/**
+ * p2p_set_sec_dev_types - Set secondary device types
+ * @p2p: P2P module context from p2p_init()
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function can be used to update the P2P module configuration with
+ * information that was not available at the time of the p2p_init() call.
+ */
+int p2p_set_sec_dev_types(struct p2p_data *p2p, const u8 dev_types[][8],
+ size_t num_dev_types);
+
+int p2p_set_country(struct p2p_data *p2p, const char *country);
+
+
+/* Commands from upper layer management entity */
+
+enum p2p_discovery_type {
+ P2P_FIND_START_WITH_FULL,
+ P2P_FIND_ONLY_SOCIAL,
+ P2P_FIND_PROGRESSIVE
+};
+
+/**
+ * p2p_find - Start P2P Find (Device Discovery)
+ * @p2p: P2P module context from p2p_init()
+ * @timeout: Timeout for find operation in seconds or 0 for no timeout
+ * @type: Device Discovery type
+ * @num_req_dev_types: Number of requested device types
+ * @req_dev_types: Requested device types array, must be an array
+ * containing num_req_dev_types * WPS_DEV_TYPE_LEN bytes; %NULL if no
+ * requested device types.
+ * @dev_id: Device ID to search for or %NULL to find all devices
+ * @search_delay: Extra delay in milliseconds between search iterations
+ * @seek_count: Number of ASP Service Strings in the seek_string array
+ * @seek_string: ASP Service Strings to query for in Probe Requests
+ * @freq: Requested first scan frequency (in MHz) to modify type ==
+ * P2P_FIND_START_WITH_FULL behavior. 0 = Use normal full scan.
+ * If p2p_find is already in progress, this parameter is ignored and full
+ * scan will be executed.
+ * Returns: 0 on success, -1 on failure
+ */
+int p2p_find(struct p2p_data *p2p, unsigned int timeout,
+ enum p2p_discovery_type type,
+ unsigned int num_req_dev_types, const u8 *req_dev_types,
+ const u8 *dev_id, unsigned int search_delay,
+ u8 seek_count, const char **seek_string, int freq);
+
+/**
+ * p2p_notify_scan_trigger_status - Indicate scan trigger status
+ * @p2p: P2P module context from p2p_init()
+ * @status: 0 on success, -1 on failure
+ */
+void p2p_notify_scan_trigger_status(struct p2p_data *p2p, int status);
+
+/**
+ * p2p_stop_find - Stop P2P Find (Device Discovery)
+ * @p2p: P2P module context from p2p_init()
+ */
+void p2p_stop_find(struct p2p_data *p2p);
+
+/**
+ * p2p_stop_find_for_freq - Stop P2P Find for next oper on specific freq
+ * @p2p: P2P module context from p2p_init()
+ * @freq: Frequency in MHz for next operation
+ *
+ * This is like p2p_stop_find(), but Listen state is not stopped if we are
+ * already on the same frequency.
+ */
+void p2p_stop_find_for_freq(struct p2p_data *p2p, int freq);
+
+/**
+ * p2p_listen - Start P2P Listen state for specified duration
+ * @p2p: P2P module context from p2p_init()
+ * @timeout: Listen state duration in milliseconds
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function can be used to request the P2P module to keep the device
+ * discoverable on the listen channel for an extended set of time. At least in
+ * its current form, this is mainly used for testing purposes and may not be of
+ * much use for normal P2P operations.
+ */
+int p2p_listen(struct p2p_data *p2p, unsigned int timeout);
+
+/**
+ * p2p_stop_listen - Stop P2P Listen
+ * @p2p: P2P module context from p2p_init()
+ */
+void p2p_stop_listen(struct p2p_data *p2p);
+
+/**
+ * p2p_connect - Start P2P group formation (GO negotiation)
+ * @p2p: P2P module context from p2p_init()
+ * @peer_addr: MAC address of the peer P2P client
+ * @wps_method: WPS method to be used in provisioning
+ * @go_intent: Local GO intent value (1..15)
+ * @own_interface_addr: Intended interface address to use with the group
+ * @force_freq: The only allowed channel frequency in MHz or 0
+ * @persistent_group: Whether to create a persistent group (0 = no, 1 =
+ * persistent group without persistent reconnect, 2 = persistent group with
+ * persistent reconnect)
+ * @force_ssid: Forced SSID for the group if we become GO or %NULL to generate
+ * a new SSID
+ * @force_ssid_len: Length of $force_ssid buffer
+ * @pd_before_go_neg: Whether to send Provision Discovery prior to GO
+ * Negotiation as an interoperability workaround when initiating group
+ * formation
+ * @pref_freq: Preferred operating frequency in MHz or 0 (this is only used if
+ * force_freq == 0)
+ * Returns: 0 on success, -1 on failure
+ */
+int p2p_connect(struct p2p_data *p2p, const u8 *peer_addr,
+ enum p2p_wps_method wps_method,
+ int go_intent, const u8 *own_interface_addr,
+ unsigned int force_freq, int persistent_group,
+ const u8 *force_ssid, size_t force_ssid_len,
+ int pd_before_go_neg, unsigned int pref_freq, u16 oob_pw_id);
+
+/**
+ * p2p_authorize - Authorize P2P group formation (GO negotiation)
+ * @p2p: P2P module context from p2p_init()
+ * @peer_addr: MAC address of the peer P2P client
+ * @wps_method: WPS method to be used in provisioning
+ * @go_intent: Local GO intent value (1..15)
+ * @own_interface_addr: Intended interface address to use with the group
+ * @force_freq: The only allowed channel frequency in MHz or 0
+ * @persistent_group: Whether to create a persistent group (0 = no, 1 =
+ * persistent group without persistent reconnect, 2 = persistent group with
+ * persistent reconnect)
+ * @force_ssid: Forced SSID for the group if we become GO or %NULL to generate
+ * a new SSID
+ * @force_ssid_len: Length of $force_ssid buffer
+ * @pref_freq: Preferred operating frequency in MHz or 0 (this is only used if
+ * force_freq == 0)
+ * Returns: 0 on success, -1 on failure
+ *
+ * This is like p2p_connect(), but the actual group negotiation is not
+ * initiated automatically, i.e., the other end is expected to do that.
+ */
+int p2p_authorize(struct p2p_data *p2p, const u8 *peer_addr,
+ enum p2p_wps_method wps_method,
+ int go_intent, const u8 *own_interface_addr,
+ unsigned int force_freq, int persistent_group,
+ const u8 *force_ssid, size_t force_ssid_len,
+ unsigned int pref_freq, u16 oob_pw_id);
+
+/**
+ * p2p_reject - Reject peer device (explicitly block connection attempts)
+ * @p2p: P2P module context from p2p_init()
+ * @peer_addr: MAC address of the peer P2P client
+ * Returns: 0 on success, -1 on failure
+ */
+int p2p_reject(struct p2p_data *p2p, const u8 *peer_addr);
+
+/**
+ * p2p_prov_disc_req - Send Provision Discovery Request
+ * @p2p: P2P module context from p2p_init()
+ * @peer_addr: MAC address of the peer P2P client
+ * @p2ps_prov: Provisioning info for P2PS
+ * @config_methods: WPS Config Methods value (only one bit set)
+ * @join: Whether this is used by a client joining an active group
+ * @force_freq: Forced TX frequency for the frame (mainly for the join case)
+ * @user_initiated_pd: Flag to indicate if initiated by user or not
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function can be used to request a discovered P2P peer to display a PIN
+ * (config_methods = WPS_CONFIG_DISPLAY) or be prepared to enter a PIN from us
+ * (config_methods = WPS_CONFIG_KEYPAD). The Provision Discovery Request frame
+ * is transmitted once immediately and if no response is received, the frame
+ * will be sent again whenever the target device is discovered during device
+ * dsicovery (start with a p2p_find() call). Response from the peer is
+ * indicated with the p2p_config::prov_disc_resp() callback.
+ */
+int p2p_prov_disc_req(struct p2p_data *p2p, const u8 *peer_addr,
+ struct p2ps_provision *p2ps_prov, u16 config_methods,
+ int join, int force_freq,
+ int user_initiated_pd);
+
+/**
+ * p2p_sd_request - Schedule a service discovery query
+ * @p2p: P2P module context from p2p_init()
+ * @dst: Destination peer or %NULL to apply for all peers
+ * @tlvs: P2P Service Query TLV(s)
+ * Returns: Reference to the query or %NULL on failure
+ *
+ * Response to the query is indicated with the p2p_config::sd_response()
+ * callback.
+ */
+void * p2p_sd_request(struct p2p_data *p2p, const u8 *dst,
+ const struct wpabuf *tlvs);
+
+#ifdef CONFIG_WIFI_DISPLAY
+void * p2p_sd_request_wfd(struct p2p_data *p2p, const u8 *dst,
+ const struct wpabuf *tlvs);
+#endif /* CONFIG_WIFI_DISPLAY */
+
+/**
+ * p2p_sd_cancel_request - Cancel a pending service discovery query
+ * @p2p: P2P module context from p2p_init()
+ * @req: Query reference from p2p_sd_request()
+ * Returns: 0 if request for cancelled; -1 if not found
+ */
+int p2p_sd_cancel_request(struct p2p_data *p2p, void *req);
+
+/**
+ * p2p_sd_response - Send response to a service discovery query
+ * @p2p: P2P module context from p2p_init()
+ * @freq: Frequency from p2p_config::sd_request() callback
+ * @dst: Destination address from p2p_config::sd_request() callback
+ * @dialog_token: Dialog token from p2p_config::sd_request() callback
+ * @resp_tlvs: P2P Service Response TLV(s)
+ *
+ * This function is called as a response to the request indicated with
+ * p2p_config::sd_request() callback.
+ */
+void p2p_sd_response(struct p2p_data *p2p, int freq, const u8 *dst,
+ u8 dialog_token, const struct wpabuf *resp_tlvs);
+
+/**
+ * p2p_sd_service_update - Indicate a change in local services
+ * @p2p: P2P module context from p2p_init()
+ *
+ * This function needs to be called whenever there is a change in availability
+ * of the local services. This will increment the Service Update Indicator
+ * value which will be used in SD Request and Response frames.
+ */
+void p2p_sd_service_update(struct p2p_data *p2p);
+
+
+enum p2p_invite_role {
+ P2P_INVITE_ROLE_GO,
+ P2P_INVITE_ROLE_ACTIVE_GO,
+ P2P_INVITE_ROLE_CLIENT
+};
+
+/**
+ * p2p_invite - Invite a P2P Device into a group
+ * @p2p: P2P module context from p2p_init()
+ * @peer: Device Address of the peer P2P Device
+ * @role: Local role in the group
+ * @bssid: Group BSSID or %NULL if not known
+ * @ssid: Group SSID
+ * @ssid_len: Length of ssid in octets
+ * @force_freq: The only allowed channel frequency in MHz or 0
+ * @go_dev_addr: Forced GO Device Address or %NULL if none
+ * @persistent_group: Whether this is to reinvoke a persistent group
+ * @pref_freq: Preferred operating frequency in MHz or 0 (this is only used if
+ * force_freq == 0)
+ * @dev_pw_id: Device Password ID from OOB Device Password (NFC) static handover
+ * case or -1 if not used
+ * Returns: 0 on success, -1 on failure
+ */
+int p2p_invite(struct p2p_data *p2p, const u8 *peer, enum p2p_invite_role role,
+ const u8 *bssid, const u8 *ssid, size_t ssid_len,
+ unsigned int force_freq, const u8 *go_dev_addr,
+ int persistent_group, unsigned int pref_freq, int dev_pw_id);
+
+/**
+ * p2p_presence_req - Request GO presence
+ * @p2p: P2P module context from p2p_init()
+ * @go_interface_addr: GO P2P Interface Address
+ * @own_interface_addr: Own P2P Interface Address for this group
+ * @freq: Group operating frequence (in MHz)
+ * @duration1: Preferred presence duration in microseconds
+ * @interval1: Preferred presence interval in microseconds
+ * @duration2: Acceptable presence duration in microseconds
+ * @interval2: Acceptable presence interval in microseconds
+ * Returns: 0 on success, -1 on failure
+ *
+ * If both duration and interval values are zero, the parameter pair is not
+ * specified (i.e., to remove Presence Request, use duration1 = interval1 = 0).
+ */
+int p2p_presence_req(struct p2p_data *p2p, const u8 *go_interface_addr,
+ const u8 *own_interface_addr, unsigned int freq,
+ u32 duration1, u32 interval1, u32 duration2,
+ u32 interval2);
+
+/**
+ * p2p_ext_listen - Set Extended Listen Timing
+ * @p2p: P2P module context from p2p_init()
+ * @freq: Group operating frequence (in MHz)
+ * @period: Availability period in milliseconds (1-65535; 0 to disable)
+ * @interval: Availability interval in milliseconds (1-65535; 0 to disable)
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function can be used to enable or disable (period = interval = 0)
+ * Extended Listen Timing. When enabled, the P2P Device will become
+ * discoverable (go into Listen State) every @interval milliseconds for at
+ * least @period milliseconds.
+ */
+int p2p_ext_listen(struct p2p_data *p2p, unsigned int period,
+ unsigned int interval);
+
+/* Event notifications from upper layer management operations */
+
+/**
+ * p2p_wps_success_cb - Report successfully completed WPS provisioning
+ * @p2p: P2P module context from p2p_init()
+ * @mac_addr: Peer address
+ *
+ * This function is used to report successfully completed WPS provisioning
+ * during group formation in both GO/Registrar and client/Enrollee roles.
+ */
+void p2p_wps_success_cb(struct p2p_data *p2p, const u8 *mac_addr);
+
+/**
+ * p2p_group_formation_failed - Report failed WPS provisioning
+ * @p2p: P2P module context from p2p_init()
+ *
+ * This function is used to report failed group formation. This can happen
+ * either due to failed WPS provisioning or due to 15 second timeout during
+ * the provisioning phase.
+ */
+void p2p_group_formation_failed(struct p2p_data *p2p);
+
+/**
+ * p2p_get_provisioning_info - Get any stored provisioning info
+ * @p2p: P2P module context from p2p_init()
+ * @addr: Peer P2P Device Address
+ * Returns: WPS provisioning information (WPS config method) or 0 if no
+ * information is available
+ *
+ * This function is used to retrieve stored WPS provisioning info for the given
+ * peer.
+ */
+u16 p2p_get_provisioning_info(struct p2p_data *p2p, const u8 *addr);
+
+/**
+ * p2p_clear_provisioning_info - Clear any stored provisioning info
+ * @p2p: P2P module context from p2p_init()
+ * @iface_addr: Peer P2P Device Address
+ *
+ * This function is used to clear stored WPS provisioning info for the given
+ * peer.
+ */
+void p2p_clear_provisioning_info(struct p2p_data *p2p, const u8 *addr);
+
+
+/* Event notifications from lower layer driver operations */
+
+/**
+ * enum p2p_probe_req_status
+ *
+ * @P2P_PREQ_MALFORMED: frame was not well-formed
+ * @P2P_PREQ_NOT_LISTEN: device isn't in listen state, frame ignored
+ * @P2P_PREQ_NOT_P2P: frame was not a P2P probe request
+ * @P2P_PREQ_P2P_NOT_PROCESSED: frame was P2P but wasn't processed
+ * @P2P_PREQ_P2P_PROCESSED: frame has been processed by P2P
+ */
+enum p2p_probe_req_status {
+ P2P_PREQ_MALFORMED,
+ P2P_PREQ_NOT_LISTEN,
+ P2P_PREQ_NOT_P2P,
+ P2P_PREQ_NOT_PROCESSED,
+ P2P_PREQ_PROCESSED
+};
+
+/**
+ * p2p_probe_req_rx - Report reception of a Probe Request frame
+ * @p2p: P2P module context from p2p_init()
+ * @addr: Source MAC address
+ * @dst: Destination MAC address if available or %NULL
+ * @bssid: BSSID if available or %NULL
+ * @ie: Information elements from the Probe Request frame body
+ * @ie_len: Length of ie buffer in octets
+ * @rx_freq: Probe Request frame RX frequency
+ * Returns: value indicating the type and status of the probe request
+ */
+enum p2p_probe_req_status
+p2p_probe_req_rx(struct p2p_data *p2p, const u8 *addr, const u8 *dst,
+ const u8 *bssid, const u8 *ie, size_t ie_len,
+ unsigned int rx_freq);
+
+/**
+ * p2p_rx_action - Report received Action frame
+ * @p2p: P2P module context from p2p_init()
+ * @da: Destination address of the received Action frame
+ * @sa: Source address of the received Action frame
+ * @bssid: Address 3 of the received Action frame
+ * @category: Category of the received Action frame
+ * @data: Action frame body after the Category field
+ * @len: Length of the data buffer in octets
+ * @freq: Frequency (in MHz) on which the frame was received
+ */
+void p2p_rx_action(struct p2p_data *p2p, const u8 *da, const u8 *sa,
+ const u8 *bssid, u8 category,
+ const u8 *data, size_t len, int freq);
+
+/**
+ * p2p_scan_res_handler - Indicate a P2P scan results
+ * @p2p: P2P module context from p2p_init()
+ * @bssid: BSSID of the scan result
+ * @freq: Frequency of the channel on which the device was found in MHz
+ * @rx_time: Time when the result was received
+ * @level: Signal level (signal strength of the received Beacon/Probe Response
+ * frame)
+ * @ies: Pointer to IEs from the scan result
+ * @ies_len: Length of the ies buffer
+ * Returns: 0 to continue or 1 to stop scan result indication
+ *
+ * This function is called to indicate a scan result entry with P2P IE from a
+ * scan requested with struct p2p_config::p2p_scan(). This can be called during
+ * the actual scan process (i.e., whenever a new device is found) or as a
+ * sequence of calls after the full scan has been completed. The former option
+ * can result in optimized operations, but may not be supported by all
+ * driver/firmware designs. The ies buffer need to include at least the P2P IE,
+ * but it is recommended to include all IEs received from the device. The
+ * caller does not need to check that the IEs contain a P2P IE before calling
+ * this function since frames will be filtered internally if needed.
+ *
+ * This function will return 1 if it wants to stop scan result iteration (and
+ * scan in general if it is still in progress). This is used to allow faster
+ * start of a pending operation, e.g., to start a pending GO negotiation.
+ */
+int p2p_scan_res_handler(struct p2p_data *p2p, const u8 *bssid, int freq,
+ struct os_reltime *rx_time, int level, const u8 *ies,
+ size_t ies_len);
+
+/**
+ * p2p_scan_res_handled - Indicate end of scan results
+ * @p2p: P2P module context from p2p_init()
+ *
+ * This function is called to indicate that all P2P scan results from a scan
+ * have been reported with zero or more calls to p2p_scan_res_handler(). This
+ * function must be called as a response to successful
+ * struct p2p_config::p2p_scan() call if none of the p2p_scan_res_handler()
+ * calls stopped iteration.
+ */
+void p2p_scan_res_handled(struct p2p_data *p2p);
+
+enum p2p_send_action_result {
+ P2P_SEND_ACTION_SUCCESS /* Frame was send and acknowledged */,
+ P2P_SEND_ACTION_NO_ACK /* Frame was sent, but not acknowledged */,
+ P2P_SEND_ACTION_FAILED /* Frame was not sent due to a failure */
+};
+
+/**
+ * p2p_send_action_cb - Notify TX status of an Action frame
+ * @p2p: P2P module context from p2p_init()
+ * @freq: Channel frequency in MHz
+ * @dst: Destination MAC address (Address 1)
+ * @src: Source MAC address (Address 2)
+ * @bssid: BSSID (Address 3)
+ * @result: Result of the transmission attempt
+ *
+ * This function is used to indicate the result of an Action frame transmission
+ * that was requested with struct p2p_config::send_action() callback.
+ */
+void p2p_send_action_cb(struct p2p_data *p2p, unsigned int freq, const u8 *dst,
+ const u8 *src, const u8 *bssid,
+ enum p2p_send_action_result result);
+
+/**
+ * p2p_listen_cb - Indicate the start of a requested Listen state
+ * @p2p: P2P module context from p2p_init()
+ * @freq: Listen channel frequency in MHz
+ * @duration: Duration for the Listen state in milliseconds
+ *
+ * This function is used to indicate that a Listen state requested with
+ * struct p2p_config::start_listen() callback has started.
+ */
+void p2p_listen_cb(struct p2p_data *p2p, unsigned int freq,
+ unsigned int duration);
+
+/**
+ * p2p_listen_end - Indicate the end of a requested Listen state
+ * @p2p: P2P module context from p2p_init()
+ * @freq: Listen channel frequency in MHz
+ * Returns: 0 if no operations were started, 1 if an operation was started
+ *
+ * This function is used to indicate that a Listen state requested with
+ * struct p2p_config::start_listen() callback has ended.
+ */
+int p2p_listen_end(struct p2p_data *p2p, unsigned int freq);
+
+void p2p_deauth_notif(struct p2p_data *p2p, const u8 *bssid, u16 reason_code,
+ const u8 *ie, size_t ie_len);
+
+void p2p_disassoc_notif(struct p2p_data *p2p, const u8 *bssid, u16 reason_code,
+ const u8 *ie, size_t ie_len);
+
+
+/* Per-group P2P state for GO */
+
+struct p2p_group;
+
+/**
+ * struct p2p_group_config - P2P group configuration
+ *
+ * This configuration is provided to the P2P module during initialization of
+ * the per-group information with p2p_group_init().
+ */
+struct p2p_group_config {
+ /**
+ * persistent_group - Whether the group is persistent
+ * 0 = not a persistent group
+ * 1 = persistent group without persistent reconnect
+ * 2 = persistent group with persistent reconnect
+ */
+ int persistent_group;
+
+ /**
+ * interface_addr - P2P Interface Address of the group
+ */
+ u8 interface_addr[ETH_ALEN];
+
+ /**
+ * max_clients - Maximum number of clients in the group
+ */
+ unsigned int max_clients;
+
+ /**
+ * ssid - Group SSID
+ */
+ u8 ssid[SSID_MAX_LEN];
+
+ /**
+ * ssid_len - Length of SSID
+ */
+ size_t ssid_len;
+
+ /**
+ * freq - Operating channel of the group
+ */
+ int freq;
+
+ /**
+ * cb_ctx - Context to use with callback functions
+ */
+ void *cb_ctx;
+
+ /**
+ * ie_update - Notification of IE update
+ * @ctx: Callback context from cb_ctx
+ * @beacon_ies: P2P IE for Beacon frames or %NULL if no change
+ * @proberesp_ies: P2P Ie for Probe Response frames
+ *
+ * P2P module uses this callback function to notify whenever the P2P IE
+ * in Beacon or Probe Response frames should be updated based on group
+ * events.
+ *
+ * The callee is responsible for freeing the returned buffer(s) with
+ * wpabuf_free().
+ */
+ void (*ie_update)(void *ctx, struct wpabuf *beacon_ies,
+ struct wpabuf *proberesp_ies);
+
+ /**
+ * idle_update - Notification of changes in group idle state
+ * @ctx: Callback context from cb_ctx
+ * @idle: Whether the group is idle (no associated stations)
+ */
+ void (*idle_update)(void *ctx, int idle);
+};
+
+/**
+ * p2p_group_init - Initialize P2P group
+ * @p2p: P2P module context from p2p_init()
+ * @config: P2P group configuration (will be freed by p2p_group_deinit())
+ * Returns: Pointer to private data or %NULL on failure
+ *
+ * This function is used to initialize per-group P2P module context. Currently,
+ * this is only used to manage GO functionality and P2P clients do not need to
+ * create an instance of this per-group information.
+ */
+struct p2p_group * p2p_group_init(struct p2p_data *p2p,
+ struct p2p_group_config *config);
+
+/**
+ * p2p_group_deinit - Deinitialize P2P group
+ * @group: P2P group context from p2p_group_init()
+ */
+void p2p_group_deinit(struct p2p_group *group);
+
+/**
+ * p2p_group_notif_assoc - Notification of P2P client association with GO
+ * @group: P2P group context from p2p_group_init()
+ * @addr: Interface address of the P2P client
+ * @ie: IEs from the (Re)association Request frame
+ * @len: Length of the ie buffer in octets
+ * Returns: 0 on success, -1 on failure
+ */
+int p2p_group_notif_assoc(struct p2p_group *group, const u8 *addr,
+ const u8 *ie, size_t len);
+
+/**
+ * p2p_group_assoc_resp_ie - Build P2P IE for (re)association response
+ * @group: P2P group context from p2p_group_init()
+ * @status: Status value (P2P_SC_SUCCESS if association succeeded)
+ * Returns: P2P IE for (Re)association Response or %NULL on failure
+ *
+ * The caller is responsible for freeing the returned buffer with
+ * wpabuf_free().
+ */
+struct wpabuf * p2p_group_assoc_resp_ie(struct p2p_group *group, u8 status);
+
+/**
+ * p2p_group_notif_disassoc - Notification of P2P client disassociation from GO
+ * @group: P2P group context from p2p_group_init()
+ * @addr: Interface address of the P2P client
+ */
+void p2p_group_notif_disassoc(struct p2p_group *group, const u8 *addr);
+
+/**
+ * p2p_group_notif_formation_done - Notification of completed group formation
+ * @group: P2P group context from p2p_group_init()
+ */
+void p2p_group_notif_formation_done(struct p2p_group *group);
+
+/**
+ * p2p_group_notif_noa - Notification of NoA change
+ * @group: P2P group context from p2p_group_init()
+ * @noa: Notice of Absence attribute payload, %NULL if none
+ * @noa_len: Length of noa buffer in octets
+ * Returns: 0 on success, -1 on failure
+ *
+ * Notify the P2P group management about a new NoA contents. This will be
+ * inserted into the P2P IEs in Beacon and Probe Response frames with rest of
+ * the group information.
+ */
+int p2p_group_notif_noa(struct p2p_group *group, const u8 *noa,
+ size_t noa_len);
+
+/**
+ * p2p_group_match_dev_type - Match device types in group with requested type
+ * @group: P2P group context from p2p_group_init()
+ * @wps: WPS TLVs from Probe Request frame (concatenated WPS IEs)
+ * Returns: 1 on match, 0 on mismatch
+ *
+ * This function can be used to match the Requested Device Type attribute in
+ * WPS IE with the device types of a group member for deciding whether a GO
+ * should reply to a Probe Request frame. Match will be reported if the WPS IE
+ * is not requested any specific device type.
+ */
+int p2p_group_match_dev_type(struct p2p_group *group, struct wpabuf *wps);
+
+/**
+ * p2p_group_match_dev_id - Match P2P Device Address in group with requested device id
+ */
+int p2p_group_match_dev_id(struct p2p_group *group, struct wpabuf *p2p);
+
+/**
+ * p2p_group_go_discover - Send GO Discoverability Request to a group client
+ * @group: P2P group context from p2p_group_init()
+ * Returns: 0 on success (frame scheduled); -1 if client was not found
+ */
+int p2p_group_go_discover(struct p2p_group *group, const u8 *dev_id,
+ const u8 *searching_dev, int rx_freq);
+
+
+/* Generic helper functions */
+
+/**
+ * p2p_ie_text - Build text format description of P2P IE
+ * @p2p_ie: P2P IE
+ * @buf: Buffer for returning text
+ * @end: Pointer to the end of the buf area
+ * Returns: Number of octets written to the buffer or -1 on failure
+ *
+ * This function can be used to parse P2P IE contents into text format
+ * field=value lines.
+ */
+int p2p_ie_text(struct wpabuf *p2p_ie, char *buf, char *end);
+
+/**
+ * p2p_scan_result_text - Build text format description of P2P IE
+ * @ies: Information elements from scan results
+ * @ies_len: ies buffer length in octets
+ * @buf: Buffer for returning text
+ * @end: Pointer to the end of the buf area
+ * Returns: Number of octets written to the buffer or -1 on failure
+ *
+ * This function can be used to parse P2P IE contents into text format
+ * field=value lines.
+ */
+int p2p_scan_result_text(const u8 *ies, size_t ies_len, char *buf, char *end);
+
+/**
+ * p2p_parse_dev_addr_in_p2p_ie - Parse P2P Device Address from a concatenated
+ * P2P IE
+ * @p2p_ie: P2P IE
+ * @dev_addr: Buffer for returning P2P Device Address
+ * Returns: 0 on success or -1 if P2P Device Address could not be parsed
+ */
+int p2p_parse_dev_addr_in_p2p_ie(struct wpabuf *p2p_ie, u8 *dev_addr);
+
+/**
+ * p2p_parse_dev_addr - Parse P2P Device Address from P2P IE(s)
+ * @ies: Information elements from scan results
+ * @ies_len: ies buffer length in octets
+ * @dev_addr: Buffer for returning P2P Device Address
+ * Returns: 0 on success or -1 if P2P Device Address could not be parsed
+ */
+int p2p_parse_dev_addr(const u8 *ies, size_t ies_len, u8 *dev_addr);
+
+/**
+ * p2p_assoc_req_ie - Build P2P IE for (Re)Association Request frame
+ * @p2p: P2P module context from p2p_init()
+ * @bssid: BSSID
+ * @buf: Buffer for writing the P2P IE
+ * @len: Maximum buf length in octets
+ * @p2p_group: Whether this is for association with a P2P GO
+ * @p2p_ie: Reassembled P2P IE data from scan results or %NULL if none
+ * Returns: Number of octets written into buf or -1 on failure
+ */
+int p2p_assoc_req_ie(struct p2p_data *p2p, const u8 *bssid, u8 *buf,
+ size_t len, int p2p_group, struct wpabuf *p2p_ie);
+
+/**
+ * p2p_scan_ie - Build P2P IE for Probe Request
+ * @p2p: P2P module context from p2p_init()
+ * @ies: Buffer for writing P2P IE
+ * @dev_id: Device ID to search for or %NULL for any
+ */
+void p2p_scan_ie(struct p2p_data *p2p, struct wpabuf *ies, const u8 *dev_id);
+
+/**
+ * p2p_scan_ie_buf_len - Get maximum buffer length needed for p2p_scan_ie
+ * @p2p: P2P module context from p2p_init()
+ * Returns: Number of octets that p2p_scan_ie() may add to the buffer
+ */
+size_t p2p_scan_ie_buf_len(struct p2p_data *p2p);
+
+/**
+ * p2p_go_params - Generate random P2P group parameters
+ * @p2p: P2P module context from p2p_init()
+ * @params: Buffer for parameters
+ * Returns: 0 on success, -1 on failure
+ */
+int p2p_go_params(struct p2p_data *p2p, struct p2p_go_neg_results *params);
+
+/**
+ * p2p_get_group_capab - Get Group Capability from P2P IE data
+ * @p2p_ie: P2P IE(s) contents
+ * Returns: Group Capability
+ */
+u8 p2p_get_group_capab(const struct wpabuf *p2p_ie);
+
+/**
+ * p2p_get_cross_connect_disallowed - Does WLAN AP disallows cross connection
+ * @p2p_ie: P2P IE(s) contents
+ * Returns: 0 if cross connection is allow, 1 if not
+ */
+int p2p_get_cross_connect_disallowed(const struct wpabuf *p2p_ie);
+
+/**
+ * p2p_get_go_dev_addr - Get P2P Device Address from P2P IE data
+ * @p2p_ie: P2P IE(s) contents
+ * Returns: Pointer to P2P Device Address or %NULL if not included
+ */
+const u8 * p2p_get_go_dev_addr(const struct wpabuf *p2p_ie);
+
+/**
+ * p2p_get_peer_info - Get P2P peer information
+ * @p2p: P2P module context from p2p_init()
+ * @addr: P2P Device Address of the peer or %NULL to indicate the first peer
+ * @next: Whether to select the peer entry following the one indicated by addr
+ * Returns: Pointer to peer info or %NULL if not found
+ */
+const struct p2p_peer_info * p2p_get_peer_info(struct p2p_data *p2p,
+ const u8 *addr, int next);
+
+/**
+ * p2p_get_peer_info_txt - Get internal P2P peer information in text format
+ * @info: Pointer to P2P peer info from p2p_get_peer_info()
+ * @buf: Buffer for returning text
+ * @buflen: Maximum buffer length
+ * Returns: Number of octets written to the buffer or -1 on failure
+ *
+ * Note: This information is internal to the P2P module and subject to change.
+ * As such, this should not really be used by external programs for purposes
+ * other than debugging.
+ */
+int p2p_get_peer_info_txt(const struct p2p_peer_info *info,
+ char *buf, size_t buflen);
+
+/**
+ * p2p_peer_known - Check whether P2P peer is known
+ * @p2p: P2P module context from p2p_init()
+ * @addr: P2P Device Address of the peer
+ * Returns: 1 if the specified device is in the P2P peer table or 0 if not
+ */
+int p2p_peer_known(struct p2p_data *p2p, const u8 *addr);
+
+/**
+ * p2p_set_client_discoverability - Set client discoverability capability
+ * @p2p: P2P module context from p2p_init()
+ * @enabled: Whether client discoverability will be enabled
+ *
+ * This function can be used to disable (and re-enable) client discoverability.
+ * This capability is enabled by default and should not be disabled in normal
+ * use cases, i.e., this is mainly for testing purposes.
+ */
+void p2p_set_client_discoverability(struct p2p_data *p2p, int enabled);
+
+/**
+ * p2p_set_managed_oper - Set managed P2P Device operations capability
+ * @p2p: P2P module context from p2p_init()
+ * @enabled: Whether managed P2P Device operations will be enabled
+ */
+void p2p_set_managed_oper(struct p2p_data *p2p, int enabled);
+
+/**
+ * p2p_config_get_random_social - Return a random social channel
+ * @p2p: P2P config
+ * @op_class: Selected operating class
+ * @op_channel: Selected social channel
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function is used before p2p_init is called. A random social channel
+ * from supports bands 2.4 GHz (channels 1,6,11) and 60 GHz (channel 2) is
+ * returned on success.
+ */
+int p2p_config_get_random_social(struct p2p_config *p2p, u8 *op_class,
+ u8 *op_channel);
+
+int p2p_set_listen_channel(struct p2p_data *p2p, u8 reg_class, u8 channel,
+ u8 forced);
+
+u8 p2p_get_listen_channel(struct p2p_data *p2p);
+
+int p2p_set_ssid_postfix(struct p2p_data *p2p, const u8 *postfix, size_t len);
+
+int p2p_get_interface_addr(struct p2p_data *p2p, const u8 *dev_addr,
+ u8 *iface_addr);
+int p2p_get_dev_addr(struct p2p_data *p2p, const u8 *iface_addr,
+ u8 *dev_addr);
+
+void p2p_set_peer_filter(struct p2p_data *p2p, const u8 *addr);
+
+/**
+ * p2p_set_cross_connect - Set cross connection capability
+ * @p2p: P2P module context from p2p_init()
+ * @enabled: Whether cross connection will be enabled
+ */
+void p2p_set_cross_connect(struct p2p_data *p2p, int enabled);
+
+int p2p_get_oper_freq(struct p2p_data *p2p, const u8 *iface_addr);
+
+/**
+ * p2p_set_intra_bss_dist - Set intra BSS distribution
+ * @p2p: P2P module context from p2p_init()
+ * @enabled: Whether intra BSS distribution will be enabled
+ */
+void p2p_set_intra_bss_dist(struct p2p_data *p2p, int enabled);
+
+int p2p_channels_includes_freq(const struct p2p_channels *channels,
+ unsigned int freq);
+
+int p2p_channels_to_freqs(const struct p2p_channels *channels,
+ int *freq_list, unsigned int max_len);
+
+/**
+ * p2p_supported_freq - Check whether channel is supported for P2P
+ * @p2p: P2P module context from p2p_init()
+ * @freq: Channel frequency in MHz
+ * Returns: 0 if channel not usable for P2P, 1 if usable for P2P
+ */
+int p2p_supported_freq(struct p2p_data *p2p, unsigned int freq);
+
+/**
+ * p2p_supported_freq_go - Check whether channel is supported for P2P GO operation
+ * @p2p: P2P module context from p2p_init()
+ * @freq: Channel frequency in MHz
+ * Returns: 0 if channel not usable for P2P, 1 if usable for P2P
+ */
+int p2p_supported_freq_go(struct p2p_data *p2p, unsigned int freq);
+
+/**
+ * p2p_supported_freq_cli - Check whether channel is supported for P2P client operation
+ * @p2p: P2P module context from p2p_init()
+ * @freq: Channel frequency in MHz
+ * Returns: 0 if channel not usable for P2P, 1 if usable for P2P
+ */
+int p2p_supported_freq_cli(struct p2p_data *p2p, unsigned int freq);
+
+/**
+ * p2p_get_pref_freq - Get channel from preferred channel list
+ * @p2p: P2P module context from p2p_init()
+ * @channels: List of channels
+ * Returns: Preferred channel
+ */
+unsigned int p2p_get_pref_freq(struct p2p_data *p2p,
+ const struct p2p_channels *channels);
+
+void p2p_update_channel_list(struct p2p_data *p2p,
+ const struct p2p_channels *chan,
+ const struct p2p_channels *cli_chan);
+
+/**
+ * p2p_set_best_channels - Update best channel information
+ * @p2p: P2P module context from p2p_init()
+ * @freq_24: Frequency (MHz) of best channel in 2.4 GHz band
+ * @freq_5: Frequency (MHz) of best channel in 5 GHz band
+ * @freq_overall: Frequency (MHz) of best channel overall
+ */
+void p2p_set_best_channels(struct p2p_data *p2p, int freq_24, int freq_5,
+ int freq_overall);
+
+/**
+ * p2p_set_own_freq_preference - Set own preference for channel
+ * @p2p: P2P module context from p2p_init()
+ * @freq: Frequency (MHz) of the preferred channel or 0 if no preference
+ *
+ * This function can be used to set a preference on the operating channel based
+ * on frequencies used on the other virtual interfaces that share the same
+ * radio. If non-zero, this is used to try to avoid multi-channel concurrency.
+ */
+void p2p_set_own_freq_preference(struct p2p_data *p2p, int freq);
+
+const u8 * p2p_get_go_neg_peer(struct p2p_data *p2p);
+
+/**
+ * p2p_get_group_num_members - Get number of members in group
+ * @group: P2P group context from p2p_group_init()
+ * Returns: Number of members in the group
+ */
+unsigned int p2p_get_group_num_members(struct p2p_group *group);
+
+/**
+ * p2p_client_limit_reached - Check if client limit is reached
+ * @group: P2P group context from p2p_group_init()
+ * Returns: 1 if no of clients limit reached
+ */
+int p2p_client_limit_reached(struct p2p_group *group);
+
+/**
+ * p2p_iterate_group_members - Iterate group members
+ * @group: P2P group context from p2p_group_init()
+ * @next: iteration pointer, must be a pointer to a void * that is set to %NULL
+ * on the first call and not modified later
+ * Returns: A P2P Device Address for each call and %NULL for no more members
+ */
+const u8 * p2p_iterate_group_members(struct p2p_group *group, void **next);
+
+/**
+ * p2p_group_get_dev_addr - Get a P2P Device Address of a client in a group
+ * @group: P2P group context from p2p_group_init()
+ * @addr: P2P Interface Address of the client
+ * Returns: P2P Device Address of the client if found or %NULL if no match
+ * found
+ */
+const u8 * p2p_group_get_dev_addr(struct p2p_group *group, const u8 *addr);
+
+/**
+ * p2p_group_is_client_connected - Check whether a specific client is connected
+ * @group: P2P group context from p2p_group_init()
+ * @addr: P2P Device Address of the client
+ * Returns: 1 if client is connected or 0 if not
+ */
+int p2p_group_is_client_connected(struct p2p_group *group, const u8 *dev_addr);
+
+/**
+ * p2p_group_get_config - Get the group configuration
+ * @group: P2P group context from p2p_group_init()
+ * Returns: The group configuration pointer
+ */
+const struct p2p_group_config * p2p_group_get_config(struct p2p_group *group);
+
+/**
+ * p2p_loop_on_all_groups - Run the given callback on all groups
+ * @p2p: P2P module context from p2p_init()
+ * @group_callback: The callback function pointer
+ * @user_data: Some user data pointer which can be %NULL
+ *
+ * The group_callback function can stop the iteration by returning 0.
+ */
+void p2p_loop_on_all_groups(struct p2p_data *p2p,
+ int (*group_callback)(struct p2p_group *group,
+ void *user_data),
+ void *user_data);
+
+/**
+ * p2p_get_peer_found - Get P2P peer info structure of a found peer
+ * @p2p: P2P module context from p2p_init()
+ * @addr: P2P Device Address of the peer or %NULL to indicate the first peer
+ * @next: Whether to select the peer entry following the one indicated by addr
+ * Returns: The first P2P peer info available or %NULL if no such peer exists
+ */
+const struct p2p_peer_info *
+p2p_get_peer_found(struct p2p_data *p2p, const u8 *addr, int next);
+
+/**
+ * p2p_remove_wps_vendor_extensions - Remove WPS vendor extensions
+ * @p2p: P2P module context from p2p_init()
+ */
+void p2p_remove_wps_vendor_extensions(struct p2p_data *p2p);
+
+/**
+ * p2p_add_wps_vendor_extension - Add a WPS vendor extension
+ * @p2p: P2P module context from p2p_init()
+ * @vendor_ext: The vendor extensions to add
+ * Returns: 0 on success, -1 on failure
+ *
+ * The wpabuf structures in the array are owned by the P2P
+ * module after this call.
+ */
+int p2p_add_wps_vendor_extension(struct p2p_data *p2p,
+ const struct wpabuf *vendor_ext);
+
+/**
+ * p2p_set_oper_channel - Set the P2P operating channel
+ * @p2p: P2P module context from p2p_init()
+ * @op_reg_class: Operating regulatory class to set
+ * @op_channel: operating channel to set
+ * @cfg_op_channel : Whether op_channel is hardcoded in configuration
+ * Returns: 0 on success, -1 on failure
+ */
+int p2p_set_oper_channel(struct p2p_data *p2p, u8 op_reg_class, u8 op_channel,
+ int cfg_op_channel);
+
+/**
+ * p2p_set_pref_chan - Set P2P preferred channel list
+ * @p2p: P2P module context from p2p_init()
+ * @num_pref_chan: Number of entries in pref_chan list
+ * @pref_chan: Preferred channels or %NULL to remove preferences
+ * Returns: 0 on success, -1 on failure
+ */
+int p2p_set_pref_chan(struct p2p_data *p2p, unsigned int num_pref_chan,
+ const struct p2p_channel *pref_chan);
+
+/**
+ * p2p_set_no_go_freq - Set no GO channel ranges
+ * @p2p: P2P module context from p2p_init()
+ * @list: Channel ranges or %NULL to remove restriction
+ * Returns: 0 on success, -1 on failure
+ */
+int p2p_set_no_go_freq(struct p2p_data *p2p,
+ const struct wpa_freq_range_list *list);
+
+/**
+ * p2p_in_progress - Check whether a P2P operation is progress
+ * @p2p: P2P module context from p2p_init()
+ * Returns: 0 if P2P module is idle, 1 if an operation is in progress but not
+ * in search state, or 2 if search state operation is in progress
+ */
+int p2p_in_progress(struct p2p_data *p2p);
+
+const char * p2p_wps_method_text(enum p2p_wps_method method);
+
+/**
+ * p2p_set_config_timeout - Set local config timeouts
+ * @p2p: P2P module context from p2p_init()
+ * @go_timeout: Time in 10 ms units it takes to start the GO mode
+ * @client_timeout: Time in 10 ms units it takes to start the client mode
+ */
+void p2p_set_config_timeout(struct p2p_data *p2p, u8 go_timeout,
+ u8 client_timeout);
+
+int p2p_set_wfd_ie_beacon(struct p2p_data *p2p, struct wpabuf *ie);
+int p2p_set_wfd_ie_probe_req(struct p2p_data *p2p, struct wpabuf *ie);
+int p2p_set_wfd_ie_probe_resp(struct p2p_data *p2p, struct wpabuf *ie);
+int p2p_set_wfd_ie_assoc_req(struct p2p_data *p2p, struct wpabuf *ie);
+int p2p_set_wfd_ie_invitation(struct p2p_data *p2p, struct wpabuf *ie);
+int p2p_set_wfd_ie_prov_disc_req(struct p2p_data *p2p, struct wpabuf *ie);
+int p2p_set_wfd_ie_prov_disc_resp(struct p2p_data *p2p, struct wpabuf *ie);
+int p2p_set_wfd_ie_go_neg(struct p2p_data *p2p, struct wpabuf *ie);
+int p2p_set_wfd_dev_info(struct p2p_data *p2p, const struct wpabuf *elem);
+int p2p_set_wfd_assoc_bssid(struct p2p_data *p2p, const struct wpabuf *elem);
+int p2p_set_wfd_coupled_sink_info(struct p2p_data *p2p,
+ const struct wpabuf *elem);
+struct wpabuf * wifi_display_encaps(struct wpabuf *subelems);
+
+/**
+ * p2p_set_disc_int - Set min/max discoverable interval for p2p_find
+ * @p2p: P2P module context from p2p_init()
+ * @min_disc_int: minDiscoverableInterval (in units of 100 TU); default 1
+ * @max_disc_int: maxDiscoverableInterval (in units of 100 TU); default 3
+ * @max_disc_tu: Maximum number of TUs (1.024 ms) for discoverable interval; or
+ * -1 not to limit
+ * Returns: 0 on success, or -1 on failure
+ *
+ * This function can be used to configure minDiscoverableInterval and
+ * maxDiscoverableInterval parameters for the Listen state during device
+ * discovery (p2p_find). A random number of 100 TU units is picked for each
+ * Listen state iteration from [min_disc_int,max_disc_int] range.
+ *
+ * max_disc_tu can be used to futher limit the discoverable duration. However,
+ * it should be noted that use of this parameter is not recommended since it
+ * would not be compliant with the P2P specification.
+ */
+int p2p_set_disc_int(struct p2p_data *p2p, int min_disc_int, int max_disc_int,
+ int max_disc_tu);
+
+/**
+ * p2p_get_state_txt - Get current P2P state for debug purposes
+ * @p2p: P2P module context from p2p_init()
+ * Returns: Name of the current P2P module state
+ *
+ * It should be noted that the P2P module state names are internal information
+ * and subject to change at any point, i.e., this information should be used
+ * mainly for debugging purposes.
+ */
+const char * p2p_get_state_txt(struct p2p_data *p2p);
+
+struct wpabuf * p2p_build_nfc_handover_req(struct p2p_data *p2p,
+ int client_freq,
+ const u8 *go_dev_addr,
+ const u8 *ssid, size_t ssid_len);
+struct wpabuf * p2p_build_nfc_handover_sel(struct p2p_data *p2p,
+ int client_freq,
+ const u8 *go_dev_addr,
+ const u8 *ssid, size_t ssid_len);
+
+struct p2p_nfc_params {
+ int sel;
+ const u8 *wsc_attr;
+ size_t wsc_len;
+ const u8 *p2p_attr;
+ size_t p2p_len;
+
+ enum {
+ NO_ACTION, JOIN_GROUP, AUTH_JOIN, INIT_GO_NEG, RESP_GO_NEG,
+ BOTH_GO, PEER_CLIENT
+ } next_step;
+ struct p2p_peer_info *peer;
+ u8 oob_dev_pw[WPS_OOB_PUBKEY_HASH_LEN + 2 +
+ WPS_OOB_DEVICE_PASSWORD_LEN];
+ size_t oob_dev_pw_len;
+ int go_freq;
+ u8 go_dev_addr[ETH_ALEN];
+ u8 go_ssid[SSID_MAX_LEN];
+ size_t go_ssid_len;
+};
+
+int p2p_process_nfc_connection_handover(struct p2p_data *p2p,
+ struct p2p_nfc_params *params);
+
+void p2p_set_authorized_oob_dev_pw_id(struct p2p_data *p2p, u16 dev_pw_id,
+ int go_intent,
+ const u8 *own_interface_addr);
+
+int p2p_set_passphrase_len(struct p2p_data *p2p, unsigned int len);
+
+void p2p_loop_on_known_peers(struct p2p_data *p2p,
+ void (*peer_callback)(struct p2p_peer_info *peer,
+ void *user_data),
+ void *user_data);
+
+void p2p_set_vendor_elems(struct p2p_data *p2p, struct wpabuf **vendor_elem);
+
+void p2p_set_intended_addr(struct p2p_data *p2p, const u8 *intended_addr);
+
+struct p2ps_advertisement *
+p2p_service_p2ps_id(struct p2p_data *p2p, u32 adv_id);
+int p2p_service_add_asp(struct p2p_data *p2p, int auto_accept, u32 adv_id,
+ const char *adv_str, u8 svc_state,
+ u16 config_methods, const char *svc_info,
+ const u8 *cpt_priority);
+int p2p_service_del_asp(struct p2p_data *p2p, u32 adv_id);
+void p2p_service_flush_asp(struct p2p_data *p2p);
+struct p2ps_advertisement * p2p_get_p2ps_adv_list(struct p2p_data *p2p);
+
+/**
+ * p2p_expire_peers - Periodic cleanup function to expire peers
+ * @p2p: P2P module context from p2p_init()
+ *
+ * This is a cleanup function that the entity calling p2p_init() is
+ * expected to call periodically to clean up expired peer entries.
+ */
+void p2p_expire_peers(struct p2p_data *p2p);
+
+void p2p_set_own_pref_freq_list(struct p2p_data *p2p,
+ const unsigned int *pref_freq_list,
+ unsigned int size);
+
+/**
+ * p2p_group_get_common_freqs - Get the group common frequencies
+ * @group: P2P group context from p2p_group_init()
+ * @common_freqs: On return will hold the group common frequencies
+ * @num: On return will hold the number of group common frequencies
+ * Returns: 0 on success, -1 otherwise
+ */
+int p2p_group_get_common_freqs(struct p2p_group *group, int *common_freqs,
+ unsigned int *num);
+
+#endif /* P2P_H */
diff --git a/freebsd/contrib/wpa/src/rsn_supp/peerkey.c b/freebsd/contrib/wpa/src/rsn_supp/peerkey.c
new file mode 100644
index 00000000..87263f86
--- /dev/null
+++ b/freebsd/contrib/wpa/src/rsn_supp/peerkey.c
@@ -0,0 +1,1157 @@
+#include <machine/rtems-bsd-user-space.h>
+
+/*
+ * WPA Supplicant - PeerKey for Direct Link Setup (DLS)
+ * Copyright (c) 2006-2015, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#ifdef CONFIG_PEERKEY
+
+#include "common.h"
+#include "eloop.h"
+#include "crypto/sha1.h"
+#include "crypto/sha256.h"
+#include "crypto/random.h"
+#include "common/ieee802_11_defs.h"
+#include "wpa.h"
+#include "wpa_i.h"
+#include "wpa_ie.h"
+#include "peerkey.h"
+
+
+static u8 * wpa_add_ie(u8 *pos, const u8 *ie, size_t ie_len)
+{
+ os_memcpy(pos, ie, ie_len);
+ return pos + ie_len;
+}
+
+
+static u8 * wpa_add_kde(u8 *pos, u32 kde, const u8 *data, size_t data_len)
+{
+ *pos++ = WLAN_EID_VENDOR_SPECIFIC;
+ *pos++ = RSN_SELECTOR_LEN + data_len;
+ RSN_SELECTOR_PUT(pos, kde);
+ pos += RSN_SELECTOR_LEN;
+ os_memcpy(pos, data, data_len);
+ pos += data_len;
+ return pos;
+}
+
+
+static void wpa_supplicant_smk_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+#if 0
+ struct wpa_sm *sm = eloop_ctx;
+ struct wpa_peerkey *peerkey = timeout_ctx;
+#endif
+ /* TODO: time out SMK and any STK that was generated using this SMK */
+}
+
+
+static void wpa_supplicant_peerkey_free(struct wpa_sm *sm,
+ struct wpa_peerkey *peerkey)
+{
+ eloop_cancel_timeout(wpa_supplicant_smk_timeout, sm, peerkey);
+ os_free(peerkey);
+}
+
+
+static int wpa_supplicant_send_smk_error(struct wpa_sm *sm, const u8 *dst,
+ const u8 *peer,
+ u16 mui, u16 error_type, int ver)
+{
+ size_t rlen;
+ struct wpa_eapol_key *err;
+ struct wpa_eapol_key_192 *err192;
+ struct rsn_error_kde error;
+ u8 *rbuf, *pos;
+ size_t kde_len;
+ u16 key_info;
+
+ kde_len = 2 + RSN_SELECTOR_LEN + sizeof(error);
+ if (peer)
+ kde_len += 2 + RSN_SELECTOR_LEN + ETH_ALEN;
+
+ rbuf = wpa_sm_alloc_eapol(sm, IEEE802_1X_TYPE_EAPOL_KEY,
+ NULL, sizeof(*err) + kde_len, &rlen,
+ (void *) &err);
+ if (rbuf == NULL)
+ return -1;
+ err192 = (struct wpa_eapol_key_192 *) err;
+
+ err->type = EAPOL_KEY_TYPE_RSN;
+ key_info = ver | WPA_KEY_INFO_SMK_MESSAGE | WPA_KEY_INFO_MIC |
+ WPA_KEY_INFO_SECURE | WPA_KEY_INFO_ERROR |
+ WPA_KEY_INFO_REQUEST;
+ WPA_PUT_BE16(err->key_info, key_info);
+ WPA_PUT_BE16(err->key_length, 0);
+ os_memcpy(err->replay_counter, sm->request_counter,
+ WPA_REPLAY_COUNTER_LEN);
+ inc_byte_array(sm->request_counter, WPA_REPLAY_COUNTER_LEN);
+
+ WPA_PUT_BE16(err->key_data_length, (u16) kde_len);
+ pos = (u8 *) (err + 1);
+
+ if (peer) {
+ /* Peer MAC Address KDE */
+ pos = wpa_add_kde(pos, RSN_KEY_DATA_MAC_ADDR, peer, ETH_ALEN);
+ }
+
+ /* Error KDE */
+ error.mui = host_to_be16(mui);
+ error.error_type = host_to_be16(error_type);
+ wpa_add_kde(pos, RSN_KEY_DATA_ERROR, (u8 *) &error, sizeof(error));
+
+ if (peer) {
+ wpa_printf(MSG_DEBUG, "RSN: Sending EAPOL-Key SMK Error (peer "
+ MACSTR " mui %d error_type %d)",
+ MAC2STR(peer), mui, error_type);
+ } else {
+ wpa_printf(MSG_DEBUG, "RSN: Sending EAPOL-Key SMK Error "
+ "(mui %d error_type %d)", mui, error_type);
+ }
+
+ wpa_eapol_key_send(sm, sm->ptk.kck, sm->ptk.kck_len, ver, dst,
+ ETH_P_EAPOL, rbuf, rlen, err192->key_mic);
+
+ return 0;
+}
+
+
+static int wpa_supplicant_send_smk_m3(struct wpa_sm *sm,
+ const unsigned char *src_addr,
+ const struct wpa_eapol_key *key,
+ int ver, struct wpa_peerkey *peerkey)
+{
+ size_t rlen;
+ struct wpa_eapol_key *reply;
+ struct wpa_eapol_key_192 *reply192;
+ u8 *rbuf, *pos;
+ size_t kde_len;
+ u16 key_info;
+
+ /* KDEs: Peer RSN IE, Initiator MAC Address, Initiator Nonce */
+ kde_len = peerkey->rsnie_p_len +
+ 2 + RSN_SELECTOR_LEN + ETH_ALEN +
+ 2 + RSN_SELECTOR_LEN + WPA_NONCE_LEN;
+
+ rbuf = wpa_sm_alloc_eapol(sm, IEEE802_1X_TYPE_EAPOL_KEY,
+ NULL, sizeof(*reply) + kde_len, &rlen,
+ (void *) &reply);
+ if (rbuf == NULL)
+ return -1;
+ reply192 = (struct wpa_eapol_key_192 *) reply;
+
+ reply->type = EAPOL_KEY_TYPE_RSN;
+ key_info = ver | WPA_KEY_INFO_SMK_MESSAGE | WPA_KEY_INFO_MIC |
+ WPA_KEY_INFO_SECURE;
+ WPA_PUT_BE16(reply->key_info, key_info);
+ WPA_PUT_BE16(reply->key_length, 0);
+ os_memcpy(reply->replay_counter, key->replay_counter,
+ WPA_REPLAY_COUNTER_LEN);
+
+ os_memcpy(reply->key_nonce, peerkey->pnonce, WPA_NONCE_LEN);
+
+ WPA_PUT_BE16(reply->key_data_length, (u16) kde_len);
+ pos = (u8 *) (reply + 1);
+
+ /* Peer RSN IE */
+ pos = wpa_add_ie(pos, peerkey->rsnie_p, peerkey->rsnie_p_len);
+
+ /* Initiator MAC Address KDE */
+ pos = wpa_add_kde(pos, RSN_KEY_DATA_MAC_ADDR, peerkey->addr, ETH_ALEN);
+
+ /* Initiator Nonce */
+ wpa_add_kde(pos, RSN_KEY_DATA_NONCE, peerkey->inonce, WPA_NONCE_LEN);
+
+ wpa_printf(MSG_DEBUG, "RSN: Sending EAPOL-Key SMK M3");
+ wpa_eapol_key_send(sm, sm->ptk.kck, sm->ptk.kck_len, ver, src_addr,
+ ETH_P_EAPOL, rbuf, rlen, reply192->key_mic);
+
+ return 0;
+}
+
+
+static int wpa_supplicant_process_smk_m2(
+ struct wpa_sm *sm, const unsigned char *src_addr,
+ const struct wpa_eapol_key *key, size_t extra_len, int ver)
+{
+ struct wpa_peerkey *peerkey;
+ struct wpa_eapol_ie_parse kde;
+ struct wpa_ie_data ie;
+ int cipher;
+ struct rsn_ie_hdr *hdr;
+ u8 *pos;
+
+ wpa_printf(MSG_DEBUG, "RSN: Received SMK M2");
+
+ if (!sm->peerkey_enabled || sm->proto != WPA_PROTO_RSN) {
+ wpa_printf(MSG_INFO, "RSN: SMK handshake not allowed for "
+ "the current network");
+ return -1;
+ }
+
+ if (wpa_supplicant_parse_ies((const u8 *) (key + 1), extra_len, &kde) <
+ 0) {
+ wpa_printf(MSG_INFO, "RSN: Failed to parse KDEs in SMK M2");
+ return -1;
+ }
+
+ if (kde.rsn_ie == NULL || kde.mac_addr == NULL ||
+ kde.mac_addr_len < ETH_ALEN) {
+ wpa_printf(MSG_INFO, "RSN: No RSN IE or MAC address KDE in "
+ "SMK M2");
+ return -1;
+ }
+
+ wpa_printf(MSG_DEBUG, "RSN: SMK M2 - SMK initiator " MACSTR,
+ MAC2STR(kde.mac_addr));
+
+ if (kde.rsn_ie_len > PEERKEY_MAX_IE_LEN) {
+ wpa_printf(MSG_INFO, "RSN: Too long Initiator RSN IE in SMK "
+ "M2");
+ return -1;
+ }
+
+ if (wpa_parse_wpa_ie_rsn(kde.rsn_ie, kde.rsn_ie_len, &ie) < 0) {
+ wpa_printf(MSG_INFO, "RSN: Failed to parse RSN IE in SMK M2");
+ return -1;
+ }
+
+ cipher = wpa_pick_pairwise_cipher(ie.pairwise_cipher &
+ sm->allowed_pairwise_cipher, 0);
+ if (cipher < 0) {
+ wpa_printf(MSG_INFO, "RSN: No acceptable cipher in SMK M2");
+ wpa_supplicant_send_smk_error(sm, src_addr, kde.mac_addr,
+ STK_MUI_SMK, STK_ERR_CPHR_NS,
+ ver);
+ return -1;
+ }
+ wpa_printf(MSG_DEBUG, "RSN: Using %s for PeerKey",
+ wpa_cipher_txt(cipher));
+
+ /* TODO: find existing entry and if found, use that instead of adding
+ * a new one; how to handle the case where both ends initiate at the
+ * same time? */
+ peerkey = os_zalloc(sizeof(*peerkey));
+ if (peerkey == NULL)
+ return -1;
+ os_memcpy(peerkey->addr, kde.mac_addr, ETH_ALEN);
+ os_memcpy(peerkey->inonce, key->key_nonce, WPA_NONCE_LEN);
+ os_memcpy(peerkey->rsnie_i, kde.rsn_ie, kde.rsn_ie_len);
+ peerkey->rsnie_i_len = kde.rsn_ie_len;
+ peerkey->cipher = cipher;
+ peerkey->akmp = ie.key_mgmt;
+
+ if (random_get_bytes(peerkey->pnonce, WPA_NONCE_LEN)) {
+ wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+ "WPA: Failed to get random data for PNonce");
+ wpa_supplicant_peerkey_free(sm, peerkey);
+ return -1;
+ }
+
+ hdr = (struct rsn_ie_hdr *) peerkey->rsnie_p;
+ hdr->elem_id = WLAN_EID_RSN;
+ WPA_PUT_LE16(hdr->version, RSN_VERSION);
+ pos = (u8 *) (hdr + 1);
+ /* Group Suite can be anything for SMK RSN IE; receiver will just
+ * ignore it. */
+ RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_CCMP);
+ pos += RSN_SELECTOR_LEN;
+ /* Include only the selected cipher in pairwise cipher suite */
+ WPA_PUT_LE16(pos, 1);
+ pos += 2;
+ RSN_SELECTOR_PUT(pos, wpa_cipher_to_suite(WPA_PROTO_RSN, cipher));
+ pos += RSN_SELECTOR_LEN;
+
+ hdr->len = (pos - peerkey->rsnie_p) - 2;
+ peerkey->rsnie_p_len = pos - peerkey->rsnie_p;
+ wpa_hexdump(MSG_DEBUG, "WPA: RSN IE for SMK handshake",
+ peerkey->rsnie_p, peerkey->rsnie_p_len);
+
+ wpa_supplicant_send_smk_m3(sm, src_addr, key, ver, peerkey);
+
+ peerkey->next = sm->peerkey;
+ sm->peerkey = peerkey;
+
+ return 0;
+}
+
+
+/**
+ * rsn_smkid - Derive SMK identifier
+ * @smk: Station master key (32 bytes)
+ * @pnonce: Peer Nonce
+ * @mac_p: Peer MAC address
+ * @inonce: Initiator Nonce
+ * @mac_i: Initiator MAC address
+ * @akmp: Negotiated AKM
+ *
+ * 8.5.1.4 Station to station (STK) key hierarchy
+ * SMKID = HMAC-SHA1-128(SMK, "SMK Name" || PNonce || MAC_P || INonce || MAC_I)
+ */
+static void rsn_smkid(const u8 *smk, const u8 *pnonce, const u8 *mac_p,
+ const u8 *inonce, const u8 *mac_i, u8 *smkid,
+ int akmp)
+{
+ char *title = "SMK Name";
+ const u8 *addr[5];
+ const size_t len[5] = { 8, WPA_NONCE_LEN, ETH_ALEN, WPA_NONCE_LEN,
+ ETH_ALEN };
+ unsigned char hash[SHA256_MAC_LEN];
+
+ addr[0] = (u8 *) title;
+ addr[1] = pnonce;
+ addr[2] = mac_p;
+ addr[3] = inonce;
+ addr[4] = mac_i;
+
+#ifdef CONFIG_IEEE80211W
+ if (wpa_key_mgmt_sha256(akmp))
+ hmac_sha256_vector(smk, PMK_LEN, 5, addr, len, hash);
+ else
+#endif /* CONFIG_IEEE80211W */
+ hmac_sha1_vector(smk, PMK_LEN, 5, addr, len, hash);
+ os_memcpy(smkid, hash, PMKID_LEN);
+}
+
+
+static void wpa_supplicant_send_stk_1_of_4(struct wpa_sm *sm,
+ struct wpa_peerkey *peerkey)
+{
+ size_t mlen;
+ struct wpa_eapol_key *msg;
+ u8 *mbuf;
+ size_t kde_len;
+ u16 key_info, ver;
+
+ kde_len = 2 + RSN_SELECTOR_LEN + PMKID_LEN;
+
+ mbuf = wpa_sm_alloc_eapol(sm, IEEE802_1X_TYPE_EAPOL_KEY, NULL,
+ sizeof(*msg) + kde_len, &mlen,
+ (void *) &msg);
+ if (mbuf == NULL)
+ return;
+
+ msg->type = EAPOL_KEY_TYPE_RSN;
+
+ if (peerkey->cipher != WPA_CIPHER_TKIP)
+ ver = WPA_KEY_INFO_TYPE_HMAC_SHA1_AES;
+ else
+ ver = WPA_KEY_INFO_TYPE_HMAC_MD5_RC4;
+
+ key_info = ver | WPA_KEY_INFO_KEY_TYPE | WPA_KEY_INFO_ACK;
+ WPA_PUT_BE16(msg->key_info, key_info);
+
+ if (peerkey->cipher != WPA_CIPHER_TKIP)
+ WPA_PUT_BE16(msg->key_length, 16);
+ else
+ WPA_PUT_BE16(msg->key_length, 32);
+
+ os_memcpy(msg->replay_counter, peerkey->replay_counter,
+ WPA_REPLAY_COUNTER_LEN);
+ inc_byte_array(peerkey->replay_counter, WPA_REPLAY_COUNTER_LEN);
+
+ WPA_PUT_BE16(msg->key_data_length, kde_len);
+ wpa_add_kde((u8 *) (msg + 1), RSN_KEY_DATA_PMKID,
+ peerkey->smkid, PMKID_LEN);
+
+ if (random_get_bytes(peerkey->inonce, WPA_NONCE_LEN)) {
+ wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+ "RSN: Failed to get random data for INonce (STK)");
+ os_free(mbuf);
+ return;
+ }
+ wpa_hexdump(MSG_DEBUG, "RSN: INonce for STK 4-Way Handshake",
+ peerkey->inonce, WPA_NONCE_LEN);
+ os_memcpy(msg->key_nonce, peerkey->inonce, WPA_NONCE_LEN);
+
+ wpa_printf(MSG_DEBUG, "RSN: Sending EAPOL-Key STK 1/4 to " MACSTR,
+ MAC2STR(peerkey->addr));
+ wpa_eapol_key_send(sm, NULL, 0, ver, peerkey->addr, ETH_P_EAPOL,
+ mbuf, mlen, NULL);
+}
+
+
+static void wpa_supplicant_send_stk_3_of_4(struct wpa_sm *sm,
+ struct wpa_peerkey *peerkey)
+{
+ size_t mlen;
+ struct wpa_eapol_key *msg;
+ u8 *mbuf, *pos;
+ size_t kde_len;
+ u16 key_info, ver;
+ be32 lifetime;
+
+ kde_len = peerkey->rsnie_i_len +
+ 2 + RSN_SELECTOR_LEN + sizeof(lifetime);
+
+ mbuf = wpa_sm_alloc_eapol(sm, IEEE802_1X_TYPE_EAPOL_KEY, NULL,
+ sizeof(*msg) + kde_len, &mlen,
+ (void *) &msg);
+ if (mbuf == NULL)
+ return;
+
+ msg->type = EAPOL_KEY_TYPE_RSN;
+
+ if (peerkey->cipher != WPA_CIPHER_TKIP)
+ ver = WPA_KEY_INFO_TYPE_HMAC_SHA1_AES;
+ else
+ ver = WPA_KEY_INFO_TYPE_HMAC_MD5_RC4;
+
+ key_info = ver | WPA_KEY_INFO_KEY_TYPE | WPA_KEY_INFO_ACK |
+ WPA_KEY_INFO_MIC | WPA_KEY_INFO_SECURE;
+ WPA_PUT_BE16(msg->key_info, key_info);
+
+ if (peerkey->cipher != WPA_CIPHER_TKIP)
+ WPA_PUT_BE16(msg->key_length, 16);
+ else
+ WPA_PUT_BE16(msg->key_length, 32);
+
+ os_memcpy(msg->replay_counter, peerkey->replay_counter,
+ WPA_REPLAY_COUNTER_LEN);
+ inc_byte_array(peerkey->replay_counter, WPA_REPLAY_COUNTER_LEN);
+
+ WPA_PUT_BE16(msg->key_data_length, kde_len);
+ pos = (u8 *) (msg + 1);
+ pos = wpa_add_ie(pos, peerkey->rsnie_i, peerkey->rsnie_i_len);
+ lifetime = host_to_be32(peerkey->lifetime);
+ wpa_add_kde(pos, RSN_KEY_DATA_LIFETIME,
+ (u8 *) &lifetime, sizeof(lifetime));
+
+ os_memcpy(msg->key_nonce, peerkey->inonce, WPA_NONCE_LEN);
+
+ wpa_printf(MSG_DEBUG, "RSN: Sending EAPOL-Key STK 3/4 to " MACSTR,
+ MAC2STR(peerkey->addr));
+ wpa_eapol_key_send(sm, peerkey->stk.kck, peerkey->stk.kck_len, ver,
+ peerkey->addr, ETH_P_EAPOL, mbuf, mlen,
+ msg->key_mic);
+}
+
+
+static int wpa_supplicant_process_smk_m4(struct wpa_peerkey *peerkey,
+ struct wpa_eapol_ie_parse *kde)
+{
+ wpa_printf(MSG_DEBUG, "RSN: Received SMK M4 (Initiator " MACSTR ")",
+ MAC2STR(kde->mac_addr));
+
+ if (os_memcmp(kde->smk + PMK_LEN, peerkey->pnonce, WPA_NONCE_LEN) != 0)
+ {
+ wpa_printf(MSG_INFO, "RSN: PNonce in SMK KDE does not "
+ "match with the one used in SMK M3");
+ return -1;
+ }
+
+ if (os_memcmp(kde->nonce, peerkey->inonce, WPA_NONCE_LEN) != 0) {
+ wpa_printf(MSG_INFO, "RSN: INonce in SMK M4 did not "
+ "match with the one received in SMK M2");
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static int wpa_supplicant_process_smk_m5(struct wpa_sm *sm,
+ const unsigned char *src_addr,
+ const struct wpa_eapol_key *key,
+ int ver,
+ struct wpa_peerkey *peerkey,
+ struct wpa_eapol_ie_parse *kde)
+{
+ int cipher;
+ struct wpa_ie_data ie;
+
+ wpa_printf(MSG_DEBUG, "RSN: Received SMK M5 (Peer " MACSTR ")",
+ MAC2STR(kde->mac_addr));
+ if (kde->rsn_ie == NULL || kde->rsn_ie_len > PEERKEY_MAX_IE_LEN ||
+ wpa_parse_wpa_ie_rsn(kde->rsn_ie, kde->rsn_ie_len, &ie) < 0) {
+ wpa_printf(MSG_INFO, "RSN: No RSN IE in SMK M5");
+ /* TODO: abort negotiation */
+ return -1;
+ }
+
+ if (os_memcmp(key->key_nonce, peerkey->inonce, WPA_NONCE_LEN) != 0) {
+ wpa_printf(MSG_INFO, "RSN: Key Nonce in SMK M5 does "
+ "not match with INonce used in SMK M1");
+ return -1;
+ }
+
+ if (os_memcmp(kde->smk + PMK_LEN, peerkey->inonce, WPA_NONCE_LEN) != 0)
+ {
+ wpa_printf(MSG_INFO, "RSN: INonce in SMK KDE does not "
+ "match with the one used in SMK M1");
+ return -1;
+ }
+
+ os_memcpy(peerkey->rsnie_p, kde->rsn_ie, kde->rsn_ie_len);
+ peerkey->rsnie_p_len = kde->rsn_ie_len;
+ os_memcpy(peerkey->pnonce, kde->nonce, WPA_NONCE_LEN);
+
+ cipher = wpa_pick_pairwise_cipher(ie.pairwise_cipher &
+ sm->allowed_pairwise_cipher, 0);
+ if (cipher < 0) {
+ wpa_printf(MSG_INFO, "RSN: SMK Peer STA " MACSTR " selected "
+ "unacceptable cipher", MAC2STR(kde->mac_addr));
+ wpa_supplicant_send_smk_error(sm, src_addr, kde->mac_addr,
+ STK_MUI_SMK, STK_ERR_CPHR_NS,
+ ver);
+ /* TODO: abort negotiation */
+ return -1;
+ }
+ wpa_printf(MSG_DEBUG, "RSN: Using %s for PeerKey",
+ wpa_cipher_txt(cipher));
+ peerkey->cipher = cipher;
+
+ return 0;
+}
+
+
+static int wpa_supplicant_process_smk_m45(
+ struct wpa_sm *sm, const unsigned char *src_addr,
+ const struct wpa_eapol_key *key, size_t extra_len, int ver)
+{
+ struct wpa_peerkey *peerkey;
+ struct wpa_eapol_ie_parse kde;
+ u32 lifetime;
+
+ if (!sm->peerkey_enabled || sm->proto != WPA_PROTO_RSN) {
+ wpa_printf(MSG_DEBUG, "RSN: SMK handshake not allowed for "
+ "the current network");
+ return -1;
+ }
+
+ if (wpa_supplicant_parse_ies((const u8 *) (key + 1), extra_len, &kde) <
+ 0) {
+ wpa_printf(MSG_INFO, "RSN: Failed to parse KDEs in SMK M4/M5");
+ return -1;
+ }
+
+ if (kde.mac_addr == NULL || kde.mac_addr_len < ETH_ALEN ||
+ kde.nonce == NULL || kde.nonce_len < WPA_NONCE_LEN ||
+ kde.smk == NULL || kde.smk_len < PMK_LEN + WPA_NONCE_LEN ||
+ kde.lifetime == NULL || kde.lifetime_len < 4) {
+ wpa_printf(MSG_INFO, "RSN: No MAC Address, Nonce, SMK, or "
+ "Lifetime KDE in SMK M4/M5");
+ return -1;
+ }
+
+ for (peerkey = sm->peerkey; peerkey; peerkey = peerkey->next) {
+ if (os_memcmp(peerkey->addr, kde.mac_addr, ETH_ALEN) == 0 &&
+ os_memcmp(peerkey->initiator ? peerkey->inonce :
+ peerkey->pnonce,
+ key->key_nonce, WPA_NONCE_LEN) == 0)
+ break;
+ }
+ if (peerkey == NULL) {
+ wpa_printf(MSG_INFO, "RSN: No matching SMK handshake found "
+ "for SMK M4/M5: peer " MACSTR,
+ MAC2STR(kde.mac_addr));
+ return -1;
+ }
+
+ if (peerkey->initiator) {
+ if (wpa_supplicant_process_smk_m5(sm, src_addr, key, ver,
+ peerkey, &kde) < 0)
+ return -1;
+ } else {
+ if (wpa_supplicant_process_smk_m4(peerkey, &kde) < 0)
+ return -1;
+ }
+
+ os_memcpy(peerkey->smk, kde.smk, PMK_LEN);
+ peerkey->smk_complete = 1;
+ wpa_hexdump_key(MSG_DEBUG, "RSN: SMK", peerkey->smk, PMK_LEN);
+ lifetime = WPA_GET_BE32(kde.lifetime);
+ wpa_printf(MSG_DEBUG, "RSN: SMK lifetime %u seconds", lifetime);
+ if (lifetime > 1000000000)
+ lifetime = 1000000000; /* avoid overflowing eloop time */
+ peerkey->lifetime = lifetime;
+ eloop_register_timeout(lifetime, 0, wpa_supplicant_smk_timeout,
+ sm, peerkey);
+
+ if (peerkey->initiator) {
+ rsn_smkid(peerkey->smk, peerkey->pnonce, peerkey->addr,
+ peerkey->inonce, sm->own_addr, peerkey->smkid,
+ peerkey->akmp);
+ wpa_supplicant_send_stk_1_of_4(sm, peerkey);
+ } else {
+ rsn_smkid(peerkey->smk, peerkey->pnonce, sm->own_addr,
+ peerkey->inonce, peerkey->addr, peerkey->smkid,
+ peerkey->akmp);
+ }
+ wpa_hexdump(MSG_DEBUG, "RSN: SMKID", peerkey->smkid, PMKID_LEN);
+
+ return 0;
+}
+
+
+static int wpa_supplicant_process_smk_error(
+ struct wpa_sm *sm, const unsigned char *src_addr,
+ const struct wpa_eapol_key *key, size_t extra_len)
+{
+ struct wpa_eapol_ie_parse kde;
+ struct rsn_error_kde error;
+ u8 peer[ETH_ALEN];
+ u16 error_type;
+
+ wpa_printf(MSG_DEBUG, "RSN: Received SMK Error");
+
+ if (!sm->peerkey_enabled || sm->proto != WPA_PROTO_RSN) {
+ wpa_printf(MSG_DEBUG, "RSN: SMK handshake not allowed for "
+ "the current network");
+ return -1;
+ }
+
+ if (wpa_supplicant_parse_ies((const u8 *) (key + 1), extra_len, &kde) <
+ 0) {
+ wpa_printf(MSG_INFO, "RSN: Failed to parse KDEs in SMK Error");
+ return -1;
+ }
+
+ if (kde.error == NULL || kde.error_len < sizeof(error)) {
+ wpa_printf(MSG_INFO, "RSN: No Error KDE in SMK Error");
+ return -1;
+ }
+
+ if (kde.mac_addr && kde.mac_addr_len >= ETH_ALEN)
+ os_memcpy(peer, kde.mac_addr, ETH_ALEN);
+ else
+ os_memset(peer, 0, ETH_ALEN);
+ os_memcpy(&error, kde.error, sizeof(error));
+ error_type = be_to_host16(error.error_type);
+ wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
+ "RSN: SMK Error KDE received: MUI %d error_type %d peer "
+ MACSTR,
+ be_to_host16(error.mui), error_type,
+ MAC2STR(peer));
+
+ if (kde.mac_addr &&
+ (error_type == STK_ERR_STA_NR || error_type == STK_ERR_STA_NRSN ||
+ error_type == STK_ERR_CPHR_NS)) {
+ struct wpa_peerkey *peerkey;
+
+ for (peerkey = sm->peerkey; peerkey; peerkey = peerkey->next) {
+ if (os_memcmp(peerkey->addr, kde.mac_addr, ETH_ALEN) ==
+ 0)
+ break;
+ }
+ if (peerkey == NULL) {
+ wpa_printf(MSG_DEBUG, "RSN: No matching SMK handshake "
+ "found for SMK Error");
+ return -1;
+ }
+ /* TODO: abort SMK/STK handshake and remove all related keys */
+ }
+
+ return 0;
+}
+
+
+static void wpa_supplicant_process_stk_1_of_4(struct wpa_sm *sm,
+ struct wpa_peerkey *peerkey,
+ const struct wpa_eapol_key *key,
+ u16 ver, const u8 *key_data,
+ size_t key_data_len)
+{
+ struct wpa_eapol_ie_parse ie;
+ size_t kde_buf_len;
+ struct wpa_ptk *stk;
+ u8 buf[8], *kde_buf, *pos;
+ be32 lifetime;
+
+ wpa_printf(MSG_DEBUG, "RSN: RX message 1 of STK 4-Way Handshake from "
+ MACSTR " (ver=%d)", MAC2STR(peerkey->addr), ver);
+
+ os_memset(&ie, 0, sizeof(ie));
+
+ /* RSN: msg 1/4 should contain SMKID for the selected SMK */
+ wpa_hexdump(MSG_DEBUG, "RSN: msg 1/4 key data", key_data, key_data_len);
+ if (wpa_supplicant_parse_ies(key_data, key_data_len, &ie) < 0 ||
+ ie.pmkid == NULL) {
+ wpa_printf(MSG_DEBUG, "RSN: No SMKID in STK 1/4");
+ return;
+ }
+ if (os_memcmp_const(ie.pmkid, peerkey->smkid, PMKID_LEN) != 0) {
+ wpa_hexdump(MSG_DEBUG, "RSN: Unknown SMKID in STK 1/4",
+ ie.pmkid, PMKID_LEN);
+ return;
+ }
+
+ if (random_get_bytes(peerkey->pnonce, WPA_NONCE_LEN)) {
+ wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+ "RSN: Failed to get random data for PNonce");
+ return;
+ }
+ wpa_hexdump(MSG_DEBUG, "WPA: Renewed PNonce",
+ peerkey->pnonce, WPA_NONCE_LEN);
+
+ /* Calculate STK which will be stored as a temporary STK until it has
+ * been verified when processing message 3/4. */
+ stk = &peerkey->tstk;
+ wpa_pmk_to_ptk(peerkey->smk, PMK_LEN, "Peer key expansion",
+ sm->own_addr, peerkey->addr,
+ peerkey->pnonce, key->key_nonce,
+ stk, peerkey->akmp, peerkey->cipher);
+ /* Supplicant: swap tx/rx Mic keys */
+ os_memcpy(buf, &stk->tk[16], 8);
+ os_memcpy(&stk->tk[16], &stk->tk[24], 8);
+ os_memcpy(&stk->tk[24], buf, 8);
+ peerkey->tstk_set = 1;
+
+ kde_buf_len = peerkey->rsnie_p_len +
+ 2 + RSN_SELECTOR_LEN + sizeof(lifetime) +
+ 2 + RSN_SELECTOR_LEN + PMKID_LEN;
+ kde_buf = os_malloc(kde_buf_len);
+ if (kde_buf == NULL)
+ return;
+ pos = kde_buf;
+ pos = wpa_add_ie(pos, peerkey->rsnie_p, peerkey->rsnie_p_len);
+ lifetime = host_to_be32(peerkey->lifetime);
+ pos = wpa_add_kde(pos, RSN_KEY_DATA_LIFETIME,
+ (u8 *) &lifetime, sizeof(lifetime));
+ wpa_add_kde(pos, RSN_KEY_DATA_PMKID, peerkey->smkid, PMKID_LEN);
+
+ if (wpa_supplicant_send_2_of_4(sm, peerkey->addr, key, ver,
+ peerkey->pnonce, kde_buf, kde_buf_len,
+ stk)) {
+ os_free(kde_buf);
+ return;
+ }
+ os_free(kde_buf);
+
+ os_memcpy(peerkey->inonce, key->key_nonce, WPA_NONCE_LEN);
+}
+
+
+static void wpa_supplicant_update_smk_lifetime(struct wpa_sm *sm,
+ struct wpa_peerkey *peerkey,
+ struct wpa_eapol_ie_parse *kde)
+{
+ u32 lifetime;
+
+ if (kde->lifetime == NULL || kde->lifetime_len < sizeof(lifetime))
+ return;
+
+ lifetime = WPA_GET_BE32(kde->lifetime);
+
+ if (lifetime >= peerkey->lifetime) {
+ wpa_printf(MSG_DEBUG, "RSN: Peer used SMK lifetime %u seconds "
+ "which is larger than or equal to own value %u "
+ "seconds - ignored", lifetime, peerkey->lifetime);
+ return;
+ }
+
+ wpa_printf(MSG_DEBUG, "RSN: Peer used shorter SMK lifetime %u seconds "
+ "(own was %u seconds) - updated",
+ lifetime, peerkey->lifetime);
+ peerkey->lifetime = lifetime;
+
+ eloop_cancel_timeout(wpa_supplicant_smk_timeout, sm, peerkey);
+ eloop_register_timeout(lifetime, 0, wpa_supplicant_smk_timeout,
+ sm, peerkey);
+}
+
+
+static void wpa_supplicant_process_stk_2_of_4(struct wpa_sm *sm,
+ struct wpa_peerkey *peerkey,
+ const struct wpa_eapol_key *key,
+ u16 ver, const u8 *key_data,
+ size_t key_data_len)
+{
+ struct wpa_eapol_ie_parse kde;
+
+ wpa_printf(MSG_DEBUG, "RSN: RX message 2 of STK 4-Way Handshake from "
+ MACSTR " (ver=%d)", MAC2STR(peerkey->addr), ver);
+
+ os_memset(&kde, 0, sizeof(kde));
+
+ /* RSN: msg 2/4 should contain SMKID for the selected SMK and RSN IE
+ * from the peer. It may also include Lifetime KDE. */
+ wpa_hexdump(MSG_DEBUG, "RSN: msg 2/4 key data", key_data, key_data_len);
+ if (wpa_supplicant_parse_ies(key_data, key_data_len, &kde) < 0 ||
+ kde.pmkid == NULL || kde.rsn_ie == NULL) {
+ wpa_printf(MSG_DEBUG, "RSN: No SMKID or RSN IE in STK 2/4");
+ return;
+ }
+
+ if (os_memcmp_const(kde.pmkid, peerkey->smkid, PMKID_LEN) != 0) {
+ wpa_hexdump(MSG_DEBUG, "RSN: Unknown SMKID in STK 2/4",
+ kde.pmkid, PMKID_LEN);
+ return;
+ }
+
+ if (kde.rsn_ie_len != peerkey->rsnie_p_len ||
+ os_memcmp(kde.rsn_ie, peerkey->rsnie_p, kde.rsn_ie_len) != 0) {
+ wpa_printf(MSG_INFO, "RSN: Peer RSN IE in SMK and STK "
+ "handshakes did not match");
+ wpa_hexdump(MSG_DEBUG, "RSN: Peer RSN IE in SMK handshake",
+ peerkey->rsnie_p, peerkey->rsnie_p_len);
+ wpa_hexdump(MSG_DEBUG, "RSN: Peer RSN IE in STK handshake",
+ kde.rsn_ie, kde.rsn_ie_len);
+ return;
+ }
+
+ wpa_supplicant_update_smk_lifetime(sm, peerkey, &kde);
+
+ wpa_supplicant_send_stk_3_of_4(sm, peerkey);
+ os_memcpy(peerkey->pnonce, key->key_nonce, WPA_NONCE_LEN);
+}
+
+
+static void wpa_supplicant_process_stk_3_of_4(struct wpa_sm *sm,
+ struct wpa_peerkey *peerkey,
+ const struct wpa_eapol_key *key,
+ u16 ver, const u8 *key_data,
+ size_t key_data_len)
+{
+ struct wpa_eapol_ie_parse kde;
+ size_t key_len;
+ const u8 *_key;
+ u8 key_buf[32], rsc[6];
+
+ wpa_printf(MSG_DEBUG, "RSN: RX message 3 of STK 4-Way Handshake from "
+ MACSTR " (ver=%d)", MAC2STR(peerkey->addr), ver);
+
+ os_memset(&kde, 0, sizeof(kde));
+
+ /* RSN: msg 3/4 should contain Initiator RSN IE. It may also include
+ * Lifetime KDE. */
+ wpa_hexdump(MSG_DEBUG, "RSN: msg 3/4 key data", key_data, key_data_len);
+ if (wpa_supplicant_parse_ies(key_data, key_data_len, &kde) < 0) {
+ wpa_printf(MSG_DEBUG, "RSN: Failed to parse key data in "
+ "STK 3/4");
+ return;
+ }
+
+ if (kde.rsn_ie_len != peerkey->rsnie_i_len ||
+ os_memcmp(kde.rsn_ie, peerkey->rsnie_i, kde.rsn_ie_len) != 0) {
+ wpa_printf(MSG_INFO, "RSN: Initiator RSN IE in SMK and STK "
+ "handshakes did not match");
+ wpa_hexdump(MSG_DEBUG, "RSN: Initiator RSN IE in SMK "
+ "handshake",
+ peerkey->rsnie_i, peerkey->rsnie_i_len);
+ wpa_hexdump(MSG_DEBUG, "RSN: Initiator RSN IE in STK "
+ "handshake",
+ kde.rsn_ie, kde.rsn_ie_len);
+ return;
+ }
+
+ if (os_memcmp(peerkey->inonce, key->key_nonce, WPA_NONCE_LEN) != 0) {
+ wpa_printf(MSG_WARNING, "RSN: INonce from message 1 of STK "
+ "4-Way Handshake differs from 3 of STK 4-Way "
+ "Handshake - drop packet (src=" MACSTR ")",
+ MAC2STR(peerkey->addr));
+ return;
+ }
+
+ wpa_supplicant_update_smk_lifetime(sm, peerkey, &kde);
+
+ if (wpa_supplicant_send_4_of_4(sm, peerkey->addr, key, ver,
+ WPA_GET_BE16(key->key_info),
+ &peerkey->stk))
+ return;
+
+ _key = peerkey->stk.tk;
+ if (peerkey->cipher == WPA_CIPHER_TKIP) {
+ /* Swap Tx/Rx keys for Michael MIC */
+ os_memcpy(key_buf, _key, 16);
+ os_memcpy(key_buf + 16, _key + 24, 8);
+ os_memcpy(key_buf + 24, _key + 16, 8);
+ _key = key_buf;
+ key_len = 32;
+ } else
+ key_len = 16;
+
+ os_memset(rsc, 0, 6);
+ if (wpa_sm_set_key(sm, peerkey->cipher, peerkey->addr, 0, 1,
+ rsc, sizeof(rsc), _key, key_len) < 0) {
+ os_memset(key_buf, 0, sizeof(key_buf));
+ wpa_printf(MSG_WARNING, "RSN: Failed to set STK to the "
+ "driver.");
+ return;
+ }
+ os_memset(key_buf, 0, sizeof(key_buf));
+}
+
+
+static void wpa_supplicant_process_stk_4_of_4(struct wpa_sm *sm,
+ struct wpa_peerkey *peerkey,
+ const struct wpa_eapol_key *key,
+ u16 ver)
+{
+ u8 rsc[6];
+
+ wpa_printf(MSG_DEBUG, "RSN: RX message 4 of STK 4-Way Handshake from "
+ MACSTR " (ver=%d)", MAC2STR(peerkey->addr), ver);
+
+ os_memset(rsc, 0, 6);
+ if (wpa_sm_set_key(sm, peerkey->cipher, peerkey->addr, 0, 1,
+ rsc, sizeof(rsc), peerkey->stk.tk,
+ peerkey->cipher == WPA_CIPHER_TKIP ? 32 : 16) < 0) {
+ wpa_printf(MSG_WARNING, "RSN: Failed to set STK to the "
+ "driver.");
+ return;
+ }
+}
+
+
+/**
+ * peerkey_verify_eapol_key_mic - Verify PeerKey MIC
+ * @sm: Pointer to WPA state machine data from wpa_sm_init()
+ * @peerkey: Pointer to the PeerKey data for the peer
+ * @key: Pointer to the EAPOL-Key frame header
+ * @ver: Version bits from EAPOL-Key Key Info
+ * @buf: Pointer to the beginning of EAPOL-Key frame
+ * @len: Length of the EAPOL-Key frame
+ * Returns: 0 on success, -1 on failure
+ */
+int peerkey_verify_eapol_key_mic(struct wpa_sm *sm,
+ struct wpa_peerkey *peerkey,
+ struct wpa_eapol_key_192 *key, u16 ver,
+ const u8 *buf, size_t len)
+{
+ u8 mic[WPA_EAPOL_KEY_MIC_MAX_LEN];
+ size_t mic_len = 16;
+ int ok = 0;
+
+ if (peerkey->initiator && !peerkey->stk_set) {
+ wpa_pmk_to_ptk(peerkey->smk, PMK_LEN, "Peer key expansion",
+ sm->own_addr, peerkey->addr,
+ peerkey->inonce, key->key_nonce,
+ &peerkey->stk, peerkey->akmp, peerkey->cipher);
+ peerkey->stk_set = 1;
+ }
+
+ os_memcpy(mic, key->key_mic, mic_len);
+ if (peerkey->tstk_set) {
+ os_memset(key->key_mic, 0, mic_len);
+ wpa_eapol_key_mic(peerkey->tstk.kck, peerkey->tstk.kck_len,
+ sm->key_mgmt, ver, buf, len, key->key_mic);
+ if (os_memcmp_const(mic, key->key_mic, mic_len) != 0) {
+ wpa_printf(MSG_WARNING, "RSN: Invalid EAPOL-Key MIC "
+ "when using TSTK - ignoring TSTK");
+ } else {
+ ok = 1;
+ peerkey->tstk_set = 0;
+ peerkey->stk_set = 1;
+ os_memcpy(&peerkey->stk, &peerkey->tstk,
+ sizeof(peerkey->stk));
+ os_memset(&peerkey->tstk, 0, sizeof(peerkey->tstk));
+ }
+ }
+
+ if (!ok && peerkey->stk_set) {
+ os_memset(key->key_mic, 0, mic_len);
+ wpa_eapol_key_mic(peerkey->stk.kck, peerkey->stk.kck_len,
+ sm->key_mgmt, ver, buf, len, key->key_mic);
+ if (os_memcmp_const(mic, key->key_mic, mic_len) != 0) {
+ wpa_printf(MSG_WARNING, "RSN: Invalid EAPOL-Key MIC "
+ "- dropping packet");
+ return -1;
+ }
+ ok = 1;
+ }
+
+ if (!ok) {
+ wpa_printf(MSG_WARNING, "RSN: Could not verify EAPOL-Key MIC "
+ "- dropping packet");
+ return -1;
+ }
+
+ os_memcpy(peerkey->replay_counter, key->replay_counter,
+ WPA_REPLAY_COUNTER_LEN);
+ peerkey->replay_counter_set = 1;
+ return 0;
+}
+
+
+/**
+ * wpa_sm_stkstart - Send EAPOL-Key Request for STK handshake (STK M1)
+ * @sm: Pointer to WPA state machine data from wpa_sm_init()
+ * @peer: MAC address of the peer STA
+ * Returns: 0 on success, or -1 on failure
+ *
+ * Send an EAPOL-Key Request to the current authenticator to start STK
+ * handshake with the peer.
+ */
+int wpa_sm_stkstart(struct wpa_sm *sm, const u8 *peer)
+{
+ size_t rlen, kde_len;
+ struct wpa_eapol_key *req;
+ int key_info, ver;
+ u8 bssid[ETH_ALEN], *rbuf, *pos, *count_pos;
+ u16 count;
+ struct rsn_ie_hdr *hdr;
+ struct wpa_peerkey *peerkey;
+ struct wpa_ie_data ie;
+
+ if (sm->proto != WPA_PROTO_RSN || !sm->ptk_set || !sm->peerkey_enabled)
+ return -1;
+
+ if (sm->ap_rsn_ie &&
+ wpa_parse_wpa_ie_rsn(sm->ap_rsn_ie, sm->ap_rsn_ie_len, &ie) == 0 &&
+ !(ie.capabilities & WPA_CAPABILITY_PEERKEY_ENABLED)) {
+ wpa_printf(MSG_DEBUG, "RSN: Current AP does not support STK");
+ return -1;
+ }
+
+ if (sm->pairwise_cipher != WPA_CIPHER_TKIP)
+ ver = WPA_KEY_INFO_TYPE_HMAC_SHA1_AES;
+ else
+ ver = WPA_KEY_INFO_TYPE_HMAC_MD5_RC4;
+
+ if (wpa_sm_get_bssid(sm, bssid) < 0) {
+ wpa_printf(MSG_WARNING, "Failed to read BSSID for EAPOL-Key "
+ "SMK M1");
+ return -1;
+ }
+
+ /* TODO: find existing entry and if found, use that instead of adding
+ * a new one */
+ peerkey = os_zalloc(sizeof(*peerkey));
+ if (peerkey == NULL)
+ return -1;
+ peerkey->initiator = 1;
+ os_memcpy(peerkey->addr, peer, ETH_ALEN);
+ peerkey->akmp = sm->key_mgmt;
+
+ /* SMK M1:
+ * EAPOL-Key(S=1, M=1, A=0, I=0, K=0, SM=1, KeyRSC=0, Nonce=INonce,
+ * MIC=MIC, DataKDs=(RSNIE_I, MAC_P KDE))
+ */
+
+ hdr = (struct rsn_ie_hdr *) peerkey->rsnie_i;
+ hdr->elem_id = WLAN_EID_RSN;
+ WPA_PUT_LE16(hdr->version, RSN_VERSION);
+ pos = (u8 *) (hdr + 1);
+ /* Group Suite can be anything for SMK RSN IE; receiver will just
+ * ignore it. */
+ RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_CCMP);
+ pos += RSN_SELECTOR_LEN;
+ count_pos = pos;
+ pos += 2;
+
+ count = rsn_cipher_put_suites(pos, sm->allowed_pairwise_cipher);
+ pos += count * RSN_SELECTOR_LEN;
+ WPA_PUT_LE16(count_pos, count);
+
+ hdr->len = (pos - peerkey->rsnie_i) - 2;
+ peerkey->rsnie_i_len = pos - peerkey->rsnie_i;
+ wpa_hexdump(MSG_DEBUG, "WPA: RSN IE for SMK handshake",
+ peerkey->rsnie_i, peerkey->rsnie_i_len);
+
+ kde_len = peerkey->rsnie_i_len + 2 + RSN_SELECTOR_LEN + ETH_ALEN;
+
+ rbuf = wpa_sm_alloc_eapol(sm, IEEE802_1X_TYPE_EAPOL_KEY, NULL,
+ sizeof(*req) + kde_len, &rlen,
+ (void *) &req);
+ if (rbuf == NULL) {
+ wpa_supplicant_peerkey_free(sm, peerkey);
+ return -1;
+ }
+
+ req->type = EAPOL_KEY_TYPE_RSN;
+ key_info = WPA_KEY_INFO_SMK_MESSAGE | WPA_KEY_INFO_MIC |
+ WPA_KEY_INFO_SECURE | WPA_KEY_INFO_REQUEST | ver;
+ WPA_PUT_BE16(req->key_info, key_info);
+ WPA_PUT_BE16(req->key_length, 0);
+ os_memcpy(req->replay_counter, sm->request_counter,
+ WPA_REPLAY_COUNTER_LEN);
+ inc_byte_array(sm->request_counter, WPA_REPLAY_COUNTER_LEN);
+
+ if (random_get_bytes(peerkey->inonce, WPA_NONCE_LEN)) {
+ wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+ "WPA: Failed to get random data for INonce");
+ os_free(rbuf);
+ wpa_supplicant_peerkey_free(sm, peerkey);
+ return -1;
+ }
+ os_memcpy(req->key_nonce, peerkey->inonce, WPA_NONCE_LEN);
+ wpa_hexdump(MSG_DEBUG, "WPA: INonce for SMK handshake",
+ req->key_nonce, WPA_NONCE_LEN);
+
+ WPA_PUT_BE16(req->key_data_length, (u16) kde_len);
+ pos = (u8 *) (req + 1);
+
+ /* Initiator RSN IE */
+ pos = wpa_add_ie(pos, peerkey->rsnie_i, peerkey->rsnie_i_len);
+ /* Peer MAC address KDE */
+ wpa_add_kde(pos, RSN_KEY_DATA_MAC_ADDR, peer, ETH_ALEN);
+
+ wpa_printf(MSG_INFO, "RSN: Sending EAPOL-Key SMK M1 Request (peer "
+ MACSTR ")", MAC2STR(peer));
+ wpa_eapol_key_send(sm, sm->ptk.kck, sm->ptk.kck_len, ver, bssid,
+ ETH_P_EAPOL, rbuf, rlen, req->key_mic);
+
+ peerkey->next = sm->peerkey;
+ sm->peerkey = peerkey;
+
+ return 0;
+}
+
+
+/**
+ * peerkey_deinit - Free PeerKey values
+ * @sm: Pointer to WPA state machine data from wpa_sm_init()
+ */
+void peerkey_deinit(struct wpa_sm *sm)
+{
+ struct wpa_peerkey *prev, *peerkey = sm->peerkey;
+ while (peerkey) {
+ prev = peerkey;
+ peerkey = peerkey->next;
+ wpa_supplicant_peerkey_free(sm, prev);
+ }
+ sm->peerkey = NULL;
+}
+
+
+void peerkey_rx_eapol_4way(struct wpa_sm *sm, struct wpa_peerkey *peerkey,
+ struct wpa_eapol_key *key, u16 key_info, u16 ver,
+ const u8 *key_data, size_t key_data_len)
+{
+ if ((key_info & (WPA_KEY_INFO_MIC | WPA_KEY_INFO_ACK)) ==
+ (WPA_KEY_INFO_MIC | WPA_KEY_INFO_ACK)) {
+ /* 3/4 STK 4-Way Handshake */
+ wpa_supplicant_process_stk_3_of_4(sm, peerkey, key, ver,
+ key_data, key_data_len);
+ } else if (key_info & WPA_KEY_INFO_ACK) {
+ /* 1/4 STK 4-Way Handshake */
+ wpa_supplicant_process_stk_1_of_4(sm, peerkey, key, ver,
+ key_data, key_data_len);
+ } else if (key_info & WPA_KEY_INFO_SECURE) {
+ /* 4/4 STK 4-Way Handshake */
+ wpa_supplicant_process_stk_4_of_4(sm, peerkey, key, ver);
+ } else {
+ /* 2/4 STK 4-Way Handshake */
+ wpa_supplicant_process_stk_2_of_4(sm, peerkey, key, ver,
+ key_data, key_data_len);
+ }
+}
+
+
+void peerkey_rx_eapol_smk(struct wpa_sm *sm, const u8 *src_addr,
+ struct wpa_eapol_key *key, size_t extra_len,
+ u16 key_info, u16 ver)
+{
+ if (key_info & WPA_KEY_INFO_ERROR) {
+ /* SMK Error */
+ wpa_supplicant_process_smk_error(sm, src_addr, key, extra_len);
+ } else if (key_info & WPA_KEY_INFO_ACK) {
+ /* SMK M2 */
+ wpa_supplicant_process_smk_m2(sm, src_addr, key, extra_len,
+ ver);
+ } else {
+ /* SMK M4 or M5 */
+ wpa_supplicant_process_smk_m45(sm, src_addr, key, extra_len,
+ ver);
+ }
+}
+
+#endif /* CONFIG_PEERKEY */
diff --git a/freebsd/contrib/wpa/src/rsn_supp/peerkey.h b/freebsd/contrib/wpa/src/rsn_supp/peerkey.h
new file mode 100644
index 00000000..6ccd948b
--- /dev/null
+++ b/freebsd/contrib/wpa/src/rsn_supp/peerkey.h
@@ -0,0 +1,82 @@
+/*
+ * WPA Supplicant - PeerKey for Direct Link Setup (DLS)
+ * Copyright (c) 2006-2015, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef PEERKEY_H
+#define PEERKEY_H
+
+#define PEERKEY_MAX_IE_LEN 80
+struct wpa_peerkey {
+ struct wpa_peerkey *next;
+ int initiator; /* whether this end was initator for SMK handshake */
+ u8 addr[ETH_ALEN]; /* other end MAC address */
+ u8 inonce[WPA_NONCE_LEN]; /* Initiator Nonce */
+ u8 pnonce[WPA_NONCE_LEN]; /* Peer Nonce */
+ u8 rsnie_i[PEERKEY_MAX_IE_LEN]; /* Initiator RSN IE */
+ size_t rsnie_i_len;
+ u8 rsnie_p[PEERKEY_MAX_IE_LEN]; /* Peer RSN IE */
+ size_t rsnie_p_len;
+ u8 smk[PMK_LEN];
+ int smk_complete;
+ u8 smkid[PMKID_LEN];
+ u32 lifetime;
+ int cipher; /* Selected cipher (WPA_CIPHER_*) */
+ u8 replay_counter[WPA_REPLAY_COUNTER_LEN];
+ int replay_counter_set;
+ int akmp;
+
+ struct wpa_ptk stk, tstk;
+ int stk_set, tstk_set;
+};
+
+
+#ifdef CONFIG_PEERKEY
+
+int peerkey_verify_eapol_key_mic(struct wpa_sm *sm,
+ struct wpa_peerkey *peerkey,
+ struct wpa_eapol_key_192 *key, u16 ver,
+ const u8 *buf, size_t len);
+void peerkey_rx_eapol_4way(struct wpa_sm *sm, struct wpa_peerkey *peerkey,
+ struct wpa_eapol_key *key, u16 key_info, u16 ver,
+ const u8 *key_data, size_t key_data_len);
+void peerkey_rx_eapol_smk(struct wpa_sm *sm, const u8 *src_addr,
+ struct wpa_eapol_key *key, size_t extra_len,
+ u16 key_info, u16 ver);
+void peerkey_deinit(struct wpa_sm *sm);
+
+#else /* CONFIG_PEERKEY */
+
+static inline int
+peerkey_verify_eapol_key_mic(struct wpa_sm *sm,
+ struct wpa_peerkey *peerkey,
+ struct wpa_eapol_key *key, u16 ver,
+ const u8 *buf, size_t len)
+{
+ return -1;
+}
+
+static inline void
+peerkey_rx_eapol_4way(struct wpa_sm *sm, struct wpa_peerkey *peerkey,
+ struct wpa_eapol_key *key, u16 key_info, u16 ver,
+ const u8 *key_data, size_t key_data_len)
+{
+}
+
+static inline void
+peerkey_rx_eapol_smk(struct wpa_sm *sm, const u8 *src_addr,
+ struct wpa_eapol_key *key, size_t extra_len,
+ u16 key_info, u16 ver)
+{
+}
+
+static inline void peerkey_deinit(struct wpa_sm *sm)
+{
+}
+
+#endif /* CONFIG_PEERKEY */
+
+#endif /* PEERKEY_H */
diff --git a/freebsd/contrib/wpa/src/rsn_supp/pmksa_cache.c b/freebsd/contrib/wpa/src/rsn_supp/pmksa_cache.c
new file mode 100644
index 00000000..e9bf319a
--- /dev/null
+++ b/freebsd/contrib/wpa/src/rsn_supp/pmksa_cache.c
@@ -0,0 +1,539 @@
+#include <machine/rtems-bsd-user-space.h>
+
+/*
+ * WPA Supplicant - RSN PMKSA cache
+ * Copyright (c) 2004-2009, 2011-2015, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "eloop.h"
+#include "eapol_supp/eapol_supp_sm.h"
+#include "wpa.h"
+#include "wpa_i.h"
+#include "pmksa_cache.h"
+
+#ifdef IEEE8021X_EAPOL
+
+static const int pmksa_cache_max_entries = 32;
+
+struct rsn_pmksa_cache {
+ struct rsn_pmksa_cache_entry *pmksa; /* PMKSA cache */
+ int pmksa_count; /* number of entries in PMKSA cache */
+ struct wpa_sm *sm; /* TODO: get rid of this reference(?) */
+
+ void (*free_cb)(struct rsn_pmksa_cache_entry *entry, void *ctx,
+ enum pmksa_free_reason reason);
+ void *ctx;
+};
+
+
+static void pmksa_cache_set_expiration(struct rsn_pmksa_cache *pmksa);
+
+
+static void _pmksa_cache_free_entry(struct rsn_pmksa_cache_entry *entry)
+{
+ bin_clear_free(entry, sizeof(*entry));
+}
+
+
+static void pmksa_cache_free_entry(struct rsn_pmksa_cache *pmksa,
+ struct rsn_pmksa_cache_entry *entry,
+ enum pmksa_free_reason reason)
+{
+ wpa_sm_remove_pmkid(pmksa->sm, entry->aa, entry->pmkid);
+ pmksa->pmksa_count--;
+ pmksa->free_cb(entry, pmksa->ctx, reason);
+ _pmksa_cache_free_entry(entry);
+}
+
+
+static void pmksa_cache_expire(void *eloop_ctx, void *timeout_ctx)
+{
+ struct rsn_pmksa_cache *pmksa = eloop_ctx;
+ struct os_reltime now;
+
+ os_get_reltime(&now);
+ while (pmksa->pmksa && pmksa->pmksa->expiration <= now.sec) {
+ struct rsn_pmksa_cache_entry *entry = pmksa->pmksa;
+ pmksa->pmksa = entry->next;
+ wpa_printf(MSG_DEBUG, "RSN: expired PMKSA cache entry for "
+ MACSTR, MAC2STR(entry->aa));
+ pmksa_cache_free_entry(pmksa, entry, PMKSA_EXPIRE);
+ }
+
+ pmksa_cache_set_expiration(pmksa);
+}
+
+
+static void pmksa_cache_reauth(void *eloop_ctx, void *timeout_ctx)
+{
+ struct rsn_pmksa_cache *pmksa = eloop_ctx;
+ pmksa->sm->cur_pmksa = NULL;
+ eapol_sm_request_reauth(pmksa->sm->eapol);
+}
+
+
+static void pmksa_cache_set_expiration(struct rsn_pmksa_cache *pmksa)
+{
+ int sec;
+ struct rsn_pmksa_cache_entry *entry;
+ struct os_reltime now;
+
+ eloop_cancel_timeout(pmksa_cache_expire, pmksa, NULL);
+ eloop_cancel_timeout(pmksa_cache_reauth, pmksa, NULL);
+ if (pmksa->pmksa == NULL)
+ return;
+ os_get_reltime(&now);
+ sec = pmksa->pmksa->expiration - now.sec;
+ if (sec < 0)
+ sec = 0;
+ eloop_register_timeout(sec + 1, 0, pmksa_cache_expire, pmksa, NULL);
+
+ entry = pmksa->sm->cur_pmksa ? pmksa->sm->cur_pmksa :
+ pmksa_cache_get(pmksa, pmksa->sm->bssid, NULL, NULL);
+ if (entry) {
+ sec = pmksa->pmksa->reauth_time - now.sec;
+ if (sec < 0)
+ sec = 0;
+ eloop_register_timeout(sec, 0, pmksa_cache_reauth, pmksa,
+ NULL);
+ }
+}
+
+
+/**
+ * pmksa_cache_add - Add a PMKSA cache entry
+ * @pmksa: Pointer to PMKSA cache data from pmksa_cache_init()
+ * @pmk: The new pairwise master key
+ * @pmk_len: PMK length in bytes, usually PMK_LEN (32)
+ * @kck: Key confirmation key or %NULL if not yet derived
+ * @kck_len: KCK length in bytes
+ * @aa: Authenticator address
+ * @spa: Supplicant address
+ * @network_ctx: Network configuration context for this PMK
+ * @akmp: WPA_KEY_MGMT_* used in key derivation
+ * Returns: Pointer to the added PMKSA cache entry or %NULL on error
+ *
+ * This function create a PMKSA entry for a new PMK and adds it to the PMKSA
+ * cache. If an old entry is already in the cache for the same Authenticator,
+ * this entry will be replaced with the new entry. PMKID will be calculated
+ * based on the PMK and the driver interface is notified of the new PMKID.
+ */
+struct rsn_pmksa_cache_entry *
+pmksa_cache_add(struct rsn_pmksa_cache *pmksa, const u8 *pmk, size_t pmk_len,
+ const u8 *kck, size_t kck_len,
+ const u8 *aa, const u8 *spa, void *network_ctx, int akmp)
+{
+ struct rsn_pmksa_cache_entry *entry, *pos, *prev;
+ struct os_reltime now;
+
+ if (pmk_len > PMK_LEN)
+ return NULL;
+
+ if (wpa_key_mgmt_suite_b(akmp) && !kck)
+ return NULL;
+
+ entry = os_zalloc(sizeof(*entry));
+ if (entry == NULL)
+ return NULL;
+ os_memcpy(entry->pmk, pmk, pmk_len);
+ entry->pmk_len = pmk_len;
+ if (akmp == WPA_KEY_MGMT_IEEE8021X_SUITE_B_192)
+ rsn_pmkid_suite_b_192(kck, kck_len, aa, spa, entry->pmkid);
+ else if (wpa_key_mgmt_suite_b(akmp))
+ rsn_pmkid_suite_b(kck, kck_len, aa, spa, entry->pmkid);
+ else
+ rsn_pmkid(pmk, pmk_len, aa, spa, entry->pmkid,
+ wpa_key_mgmt_sha256(akmp));
+ os_get_reltime(&now);
+ entry->expiration = now.sec + pmksa->sm->dot11RSNAConfigPMKLifetime;
+ entry->reauth_time = now.sec + pmksa->sm->dot11RSNAConfigPMKLifetime *
+ pmksa->sm->dot11RSNAConfigPMKReauthThreshold / 100;
+ entry->akmp = akmp;
+ os_memcpy(entry->aa, aa, ETH_ALEN);
+ entry->network_ctx = network_ctx;
+
+ /* Replace an old entry for the same Authenticator (if found) with the
+ * new entry */
+ pos = pmksa->pmksa;
+ prev = NULL;
+ while (pos) {
+ if (os_memcmp(aa, pos->aa, ETH_ALEN) == 0) {
+ if (pos->pmk_len == pmk_len &&
+ os_memcmp_const(pos->pmk, pmk, pmk_len) == 0 &&
+ os_memcmp_const(pos->pmkid, entry->pmkid,
+ PMKID_LEN) == 0) {
+ wpa_printf(MSG_DEBUG, "WPA: reusing previous "
+ "PMKSA entry");
+ os_free(entry);
+ return pos;
+ }
+ if (prev == NULL)
+ pmksa->pmksa = pos->next;
+ else
+ prev->next = pos->next;
+
+ /*
+ * If OKC is used, there may be other PMKSA cache
+ * entries based on the same PMK. These needs to be
+ * flushed so that a new entry can be created based on
+ * the new PMK. Only clear other entries if they have a
+ * matching PMK and this PMK has been used successfully
+ * with the current AP, i.e., if opportunistic flag has
+ * been cleared in wpa_supplicant_key_neg_complete().
+ */
+ wpa_printf(MSG_DEBUG, "RSN: Replace PMKSA entry for "
+ "the current AP and any PMKSA cache entry "
+ "that was based on the old PMK");
+ if (!pos->opportunistic)
+ pmksa_cache_flush(pmksa, network_ctx, pos->pmk,
+ pos->pmk_len);
+ pmksa_cache_free_entry(pmksa, pos, PMKSA_REPLACE);
+ break;
+ }
+ prev = pos;
+ pos = pos->next;
+ }
+
+ if (pmksa->pmksa_count >= pmksa_cache_max_entries && pmksa->pmksa) {
+ /* Remove the oldest entry to make room for the new entry */
+ pos = pmksa->pmksa;
+
+ if (pos == pmksa->sm->cur_pmksa) {
+ /*
+ * Never remove the current PMKSA cache entry, since
+ * it's in use, and removing it triggers a needless
+ * deauthentication.
+ */
+ pos = pos->next;
+ pmksa->pmksa->next = pos ? pos->next : NULL;
+ } else
+ pmksa->pmksa = pos->next;
+
+ if (pos) {
+ wpa_printf(MSG_DEBUG, "RSN: removed the oldest idle "
+ "PMKSA cache entry (for " MACSTR ") to "
+ "make room for new one",
+ MAC2STR(pos->aa));
+ pmksa_cache_free_entry(pmksa, pos, PMKSA_FREE);
+ }
+ }
+
+ /* Add the new entry; order by expiration time */
+ pos = pmksa->pmksa;
+ prev = NULL;
+ while (pos) {
+ if (pos->expiration > entry->expiration)
+ break;
+ prev = pos;
+ pos = pos->next;
+ }
+ if (prev == NULL) {
+ entry->next = pmksa->pmksa;
+ pmksa->pmksa = entry;
+ pmksa_cache_set_expiration(pmksa);
+ } else {
+ entry->next = prev->next;
+ prev->next = entry;
+ }
+ pmksa->pmksa_count++;
+ wpa_printf(MSG_DEBUG, "RSN: Added PMKSA cache entry for " MACSTR
+ " network_ctx=%p", MAC2STR(entry->aa), network_ctx);
+ wpa_sm_add_pmkid(pmksa->sm, entry->aa, entry->pmkid);
+
+ return entry;
+}
+
+
+/**
+ * pmksa_cache_flush - Flush PMKSA cache entries for a specific network
+ * @pmksa: Pointer to PMKSA cache data from pmksa_cache_init()
+ * @network_ctx: Network configuration context or %NULL to flush all entries
+ * @pmk: PMK to match for or %NYLL to match all PMKs
+ * @pmk_len: PMK length
+ */
+void pmksa_cache_flush(struct rsn_pmksa_cache *pmksa, void *network_ctx,
+ const u8 *pmk, size_t pmk_len)
+{
+ struct rsn_pmksa_cache_entry *entry, *prev = NULL, *tmp;
+ int removed = 0;
+
+ entry = pmksa->pmksa;
+ while (entry) {
+ if ((entry->network_ctx == network_ctx ||
+ network_ctx == NULL) &&
+ (pmk == NULL ||
+ (pmk_len == entry->pmk_len &&
+ os_memcmp(pmk, entry->pmk, pmk_len) == 0))) {
+ wpa_printf(MSG_DEBUG, "RSN: Flush PMKSA cache entry "
+ "for " MACSTR, MAC2STR(entry->aa));
+ if (prev)
+ prev->next = entry->next;
+ else
+ pmksa->pmksa = entry->next;
+ tmp = entry;
+ entry = entry->next;
+ pmksa_cache_free_entry(pmksa, tmp, PMKSA_FREE);
+ removed++;
+ } else {
+ prev = entry;
+ entry = entry->next;
+ }
+ }
+ if (removed)
+ pmksa_cache_set_expiration(pmksa);
+}
+
+
+/**
+ * pmksa_cache_deinit - Free all entries in PMKSA cache
+ * @pmksa: Pointer to PMKSA cache data from pmksa_cache_init()
+ */
+void pmksa_cache_deinit(struct rsn_pmksa_cache *pmksa)
+{
+ struct rsn_pmksa_cache_entry *entry, *prev;
+
+ if (pmksa == NULL)
+ return;
+
+ entry = pmksa->pmksa;
+ pmksa->pmksa = NULL;
+ while (entry) {
+ prev = entry;
+ entry = entry->next;
+ os_free(prev);
+ }
+ pmksa_cache_set_expiration(pmksa);
+ os_free(pmksa);
+}
+
+
+/**
+ * pmksa_cache_get - Fetch a PMKSA cache entry
+ * @pmksa: Pointer to PMKSA cache data from pmksa_cache_init()
+ * @aa: Authenticator address or %NULL to match any
+ * @pmkid: PMKID or %NULL to match any
+ * @network_ctx: Network context or %NULL to match any
+ * Returns: Pointer to PMKSA cache entry or %NULL if no match was found
+ */
+struct rsn_pmksa_cache_entry * pmksa_cache_get(struct rsn_pmksa_cache *pmksa,
+ const u8 *aa, const u8 *pmkid,
+ const void *network_ctx)
+{
+ struct rsn_pmksa_cache_entry *entry = pmksa->pmksa;
+ while (entry) {
+ if ((aa == NULL || os_memcmp(entry->aa, aa, ETH_ALEN) == 0) &&
+ (pmkid == NULL ||
+ os_memcmp(entry->pmkid, pmkid, PMKID_LEN) == 0) &&
+ (network_ctx == NULL || network_ctx == entry->network_ctx))
+ return entry;
+ entry = entry->next;
+ }
+ return NULL;
+}
+
+
+static struct rsn_pmksa_cache_entry *
+pmksa_cache_clone_entry(struct rsn_pmksa_cache *pmksa,
+ const struct rsn_pmksa_cache_entry *old_entry,
+ const u8 *aa)
+{
+ struct rsn_pmksa_cache_entry *new_entry;
+
+ new_entry = pmksa_cache_add(pmksa, old_entry->pmk, old_entry->pmk_len,
+ NULL, 0,
+ aa, pmksa->sm->own_addr,
+ old_entry->network_ctx, old_entry->akmp);
+ if (new_entry == NULL)
+ return NULL;
+
+ /* TODO: reorder entries based on expiration time? */
+ new_entry->expiration = old_entry->expiration;
+ new_entry->opportunistic = 1;
+
+ return new_entry;
+}
+
+
+/**
+ * pmksa_cache_get_opportunistic - Try to get an opportunistic PMKSA entry
+ * @pmksa: Pointer to PMKSA cache data from pmksa_cache_init()
+ * @network_ctx: Network configuration context
+ * @aa: Authenticator address for the new AP
+ * Returns: Pointer to a new PMKSA cache entry or %NULL if not available
+ *
+ * Try to create a new PMKSA cache entry opportunistically by guessing that the
+ * new AP is sharing the same PMK as another AP that has the same SSID and has
+ * already an entry in PMKSA cache.
+ */
+struct rsn_pmksa_cache_entry *
+pmksa_cache_get_opportunistic(struct rsn_pmksa_cache *pmksa, void *network_ctx,
+ const u8 *aa)
+{
+ struct rsn_pmksa_cache_entry *entry = pmksa->pmksa;
+
+ wpa_printf(MSG_DEBUG, "RSN: Consider " MACSTR " for OKC", MAC2STR(aa));
+ if (network_ctx == NULL)
+ return NULL;
+ while (entry) {
+ if (entry->network_ctx == network_ctx) {
+ entry = pmksa_cache_clone_entry(pmksa, entry, aa);
+ if (entry) {
+ wpa_printf(MSG_DEBUG, "RSN: added "
+ "opportunistic PMKSA cache entry "
+ "for " MACSTR, MAC2STR(aa));
+ }
+ return entry;
+ }
+ entry = entry->next;
+ }
+ return NULL;
+}
+
+
+/**
+ * pmksa_cache_get_current - Get the current used PMKSA entry
+ * @sm: Pointer to WPA state machine data from wpa_sm_init()
+ * Returns: Pointer to the current PMKSA cache entry or %NULL if not available
+ */
+struct rsn_pmksa_cache_entry * pmksa_cache_get_current(struct wpa_sm *sm)
+{
+ if (sm == NULL)
+ return NULL;
+ return sm->cur_pmksa;
+}
+
+
+/**
+ * pmksa_cache_clear_current - Clear the current PMKSA entry selection
+ * @sm: Pointer to WPA state machine data from wpa_sm_init()
+ */
+void pmksa_cache_clear_current(struct wpa_sm *sm)
+{
+ if (sm == NULL)
+ return;
+ sm->cur_pmksa = NULL;
+}
+
+
+/**
+ * pmksa_cache_set_current - Set the current PMKSA entry selection
+ * @sm: Pointer to WPA state machine data from wpa_sm_init()
+ * @pmkid: PMKID for selecting PMKSA or %NULL if not used
+ * @bssid: BSSID for PMKSA or %NULL if not used
+ * @network_ctx: Network configuration context
+ * @try_opportunistic: Whether to allow opportunistic PMKSA caching
+ * Returns: 0 if PMKSA was found or -1 if no matching entry was found
+ */
+int pmksa_cache_set_current(struct wpa_sm *sm, const u8 *pmkid,
+ const u8 *bssid, void *network_ctx,
+ int try_opportunistic)
+{
+ struct rsn_pmksa_cache *pmksa = sm->pmksa;
+ wpa_printf(MSG_DEBUG, "RSN: PMKSA cache search - network_ctx=%p "
+ "try_opportunistic=%d", network_ctx, try_opportunistic);
+ if (pmkid)
+ wpa_hexdump(MSG_DEBUG, "RSN: Search for PMKID",
+ pmkid, PMKID_LEN);
+ if (bssid)
+ wpa_printf(MSG_DEBUG, "RSN: Search for BSSID " MACSTR,
+ MAC2STR(bssid));
+
+ sm->cur_pmksa = NULL;
+ if (pmkid)
+ sm->cur_pmksa = pmksa_cache_get(pmksa, NULL, pmkid,
+ network_ctx);
+ if (sm->cur_pmksa == NULL && bssid)
+ sm->cur_pmksa = pmksa_cache_get(pmksa, bssid, NULL,
+ network_ctx);
+ if (sm->cur_pmksa == NULL && try_opportunistic && bssid)
+ sm->cur_pmksa = pmksa_cache_get_opportunistic(pmksa,
+ network_ctx,
+ bssid);
+ if (sm->cur_pmksa) {
+ wpa_hexdump(MSG_DEBUG, "RSN: PMKSA cache entry found - PMKID",
+ sm->cur_pmksa->pmkid, PMKID_LEN);
+ return 0;
+ }
+ wpa_printf(MSG_DEBUG, "RSN: No PMKSA cache entry found");
+ return -1;
+}
+
+
+/**
+ * pmksa_cache_list - Dump text list of entries in PMKSA cache
+ * @pmksa: Pointer to PMKSA cache data from pmksa_cache_init()
+ * @buf: Buffer for the list
+ * @len: Length of the buffer
+ * Returns: number of bytes written to buffer
+ *
+ * This function is used to generate a text format representation of the
+ * current PMKSA cache contents for the ctrl_iface PMKSA command.
+ */
+int pmksa_cache_list(struct rsn_pmksa_cache *pmksa, char *buf, size_t len)
+{
+ int i, ret;
+ char *pos = buf;
+ struct rsn_pmksa_cache_entry *entry;
+ struct os_reltime now;
+
+ os_get_reltime(&now);
+ ret = os_snprintf(pos, buf + len - pos,
+ "Index / AA / PMKID / expiration (in seconds) / "
+ "opportunistic\n");
+ if (os_snprintf_error(buf + len - pos, ret))
+ return pos - buf;
+ pos += ret;
+ i = 0;
+ entry = pmksa->pmksa;
+ while (entry) {
+ i++;
+ ret = os_snprintf(pos, buf + len - pos, "%d " MACSTR " ",
+ i, MAC2STR(entry->aa));
+ if (os_snprintf_error(buf + len - pos, ret))
+ return pos - buf;
+ pos += ret;
+ pos += wpa_snprintf_hex(pos, buf + len - pos, entry->pmkid,
+ PMKID_LEN);
+ ret = os_snprintf(pos, buf + len - pos, " %d %d\n",
+ (int) (entry->expiration - now.sec),
+ entry->opportunistic);
+ if (os_snprintf_error(buf + len - pos, ret))
+ return pos - buf;
+ pos += ret;
+ entry = entry->next;
+ }
+ return pos - buf;
+}
+
+
+/**
+ * pmksa_cache_init - Initialize PMKSA cache
+ * @free_cb: Callback function to be called when a PMKSA cache entry is freed
+ * @ctx: Context pointer for free_cb function
+ * @sm: Pointer to WPA state machine data from wpa_sm_init()
+ * Returns: Pointer to PMKSA cache data or %NULL on failure
+ */
+struct rsn_pmksa_cache *
+pmksa_cache_init(void (*free_cb)(struct rsn_pmksa_cache_entry *entry,
+ void *ctx, enum pmksa_free_reason reason),
+ void *ctx, struct wpa_sm *sm)
+{
+ struct rsn_pmksa_cache *pmksa;
+
+ pmksa = os_zalloc(sizeof(*pmksa));
+ if (pmksa) {
+ pmksa->free_cb = free_cb;
+ pmksa->ctx = ctx;
+ pmksa->sm = sm;
+ }
+
+ return pmksa;
+}
+
+#endif /* IEEE8021X_EAPOL */
diff --git a/freebsd/contrib/wpa/src/rsn_supp/pmksa_cache.h b/freebsd/contrib/wpa/src/rsn_supp/pmksa_cache.h
new file mode 100644
index 00000000..f8e040e0
--- /dev/null
+++ b/freebsd/contrib/wpa/src/rsn_supp/pmksa_cache.h
@@ -0,0 +1,134 @@
+/*
+ * wpa_supplicant - WPA2/RSN PMKSA cache functions
+ * Copyright (c) 2003-2009, 2011-2012, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef PMKSA_CACHE_H
+#define PMKSA_CACHE_H
+
+/**
+ * struct rsn_pmksa_cache_entry - PMKSA cache entry
+ */
+struct rsn_pmksa_cache_entry {
+ struct rsn_pmksa_cache_entry *next;
+ u8 pmkid[PMKID_LEN];
+ u8 pmk[PMK_LEN];
+ size_t pmk_len;
+ os_time_t expiration;
+ int akmp; /* WPA_KEY_MGMT_* */
+ u8 aa[ETH_ALEN];
+
+ os_time_t reauth_time;
+
+ /**
+ * network_ctx - Network configuration context
+ *
+ * This field is only used to match PMKSA cache entries to a specific
+ * network configuration (e.g., a specific SSID and security policy).
+ * This can be a pointer to the configuration entry, but PMKSA caching
+ * code does not dereference the value and this could be any kind of
+ * identifier.
+ */
+ void *network_ctx;
+ int opportunistic;
+};
+
+struct rsn_pmksa_cache;
+
+enum pmksa_free_reason {
+ PMKSA_FREE,
+ PMKSA_REPLACE,
+ PMKSA_EXPIRE,
+};
+
+#ifdef IEEE8021X_EAPOL
+
+struct rsn_pmksa_cache *
+pmksa_cache_init(void (*free_cb)(struct rsn_pmksa_cache_entry *entry,
+ void *ctx, enum pmksa_free_reason reason),
+ void *ctx, struct wpa_sm *sm);
+void pmksa_cache_deinit(struct rsn_pmksa_cache *pmksa);
+struct rsn_pmksa_cache_entry * pmksa_cache_get(struct rsn_pmksa_cache *pmksa,
+ const u8 *aa, const u8 *pmkid,
+ const void *network_ctx);
+int pmksa_cache_list(struct rsn_pmksa_cache *pmksa, char *buf, size_t len);
+struct rsn_pmksa_cache_entry *
+pmksa_cache_add(struct rsn_pmksa_cache *pmksa, const u8 *pmk, size_t pmk_len,
+ const u8 *kck, size_t kck_len,
+ const u8 *aa, const u8 *spa, void *network_ctx, int akmp);
+struct rsn_pmksa_cache_entry * pmksa_cache_get_current(struct wpa_sm *sm);
+void pmksa_cache_clear_current(struct wpa_sm *sm);
+int pmksa_cache_set_current(struct wpa_sm *sm, const u8 *pmkid,
+ const u8 *bssid, void *network_ctx,
+ int try_opportunistic);
+struct rsn_pmksa_cache_entry *
+pmksa_cache_get_opportunistic(struct rsn_pmksa_cache *pmksa,
+ void *network_ctx, const u8 *aa);
+void pmksa_cache_flush(struct rsn_pmksa_cache *pmksa, void *network_ctx,
+ const u8 *pmk, size_t pmk_len);
+
+#else /* IEEE8021X_EAPOL */
+
+static inline struct rsn_pmksa_cache *
+pmksa_cache_init(void (*free_cb)(struct rsn_pmksa_cache_entry *entry,
+ void *ctx, enum pmksa_free_reason reason),
+ void *ctx, struct wpa_sm *sm)
+{
+ return (void *) -1;
+}
+
+static inline void pmksa_cache_deinit(struct rsn_pmksa_cache *pmksa)
+{
+}
+
+static inline struct rsn_pmksa_cache_entry *
+pmksa_cache_get(struct rsn_pmksa_cache *pmksa, const u8 *aa, const u8 *pmkid,
+ const void *network_ctx)
+{
+ return NULL;
+}
+
+static inline struct rsn_pmksa_cache_entry *
+pmksa_cache_get_current(struct wpa_sm *sm)
+{
+ return NULL;
+}
+
+static inline int pmksa_cache_list(struct rsn_pmksa_cache *pmksa, char *buf,
+ size_t len)
+{
+ return -1;
+}
+
+static inline struct rsn_pmksa_cache_entry *
+pmksa_cache_add(struct rsn_pmksa_cache *pmksa, const u8 *pmk, size_t pmk_len,
+ const u8 *kck, size_t kck_len,
+ const u8 *aa, const u8 *spa, void *network_ctx, int akmp)
+{
+ return NULL;
+}
+
+static inline void pmksa_cache_clear_current(struct wpa_sm *sm)
+{
+}
+
+static inline int pmksa_cache_set_current(struct wpa_sm *sm, const u8 *pmkid,
+ const u8 *bssid,
+ void *network_ctx,
+ int try_opportunistic)
+{
+ return -1;
+}
+
+static inline void pmksa_cache_flush(struct rsn_pmksa_cache *pmksa,
+ void *network_ctx,
+ const u8 *pmk, size_t pmk_len)
+{
+}
+
+#endif /* IEEE8021X_EAPOL */
+
+#endif /* PMKSA_CACHE_H */
diff --git a/freebsd/contrib/wpa/src/rsn_supp/preauth.c b/freebsd/contrib/wpa/src/rsn_supp/preauth.c
new file mode 100644
index 00000000..f6b3cf5c
--- /dev/null
+++ b/freebsd/contrib/wpa/src/rsn_supp/preauth.c
@@ -0,0 +1,543 @@
+#include <machine/rtems-bsd-user-space.h>
+
+/*
+ * RSN pre-authentication (supplicant)
+ * Copyright (c) 2003-2015, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "wpa.h"
+#include "eloop.h"
+#include "l2_packet/l2_packet.h"
+#include "eapol_supp/eapol_supp_sm.h"
+#include "preauth.h"
+#include "pmksa_cache.h"
+#include "wpa_i.h"
+
+
+#ifdef IEEE8021X_EAPOL
+
+#define PMKID_CANDIDATE_PRIO_SCAN 1000
+
+
+struct rsn_pmksa_candidate {
+ struct dl_list list;
+ u8 bssid[ETH_ALEN];
+ int priority;
+};
+
+
+/**
+ * pmksa_candidate_free - Free all entries in PMKSA candidate list
+ * @sm: Pointer to WPA state machine data from wpa_sm_init()
+ */
+void pmksa_candidate_free(struct wpa_sm *sm)
+{
+ struct rsn_pmksa_candidate *entry, *n;
+
+ if (sm == NULL)
+ return;
+
+ dl_list_for_each_safe(entry, n, &sm->pmksa_candidates,
+ struct rsn_pmksa_candidate, list) {
+ dl_list_del(&entry->list);
+ os_free(entry);
+ }
+}
+
+
+static void rsn_preauth_receive(void *ctx, const u8 *src_addr,
+ const u8 *buf, size_t len)
+{
+ struct wpa_sm *sm = ctx;
+
+ wpa_printf(MSG_DEBUG, "RX pre-auth from " MACSTR, MAC2STR(src_addr));
+ wpa_hexdump(MSG_MSGDUMP, "RX pre-auth", buf, len);
+
+ if (sm->preauth_eapol == NULL ||
+ is_zero_ether_addr(sm->preauth_bssid) ||
+ os_memcmp(sm->preauth_bssid, src_addr, ETH_ALEN) != 0) {
+ wpa_printf(MSG_WARNING, "RSN pre-auth frame received from "
+ "unexpected source " MACSTR " - dropped",
+ MAC2STR(src_addr));
+ return;
+ }
+
+ eapol_sm_rx_eapol(sm->preauth_eapol, src_addr, buf, len);
+}
+
+
+static void rsn_preauth_eapol_cb(struct eapol_sm *eapol,
+ enum eapol_supp_result result,
+ void *ctx)
+{
+ struct wpa_sm *sm = ctx;
+ u8 pmk[PMK_LEN];
+
+ if (result == EAPOL_SUPP_RESULT_SUCCESS) {
+ int res, pmk_len;
+ pmk_len = PMK_LEN;
+ res = eapol_sm_get_key(eapol, pmk, PMK_LEN);
+ if (res) {
+ /*
+ * EAP-LEAP is an exception from other EAP methods: it
+ * uses only 16-byte PMK.
+ */
+ res = eapol_sm_get_key(eapol, pmk, 16);
+ pmk_len = 16;
+ }
+ if (res == 0) {
+ wpa_hexdump_key(MSG_DEBUG, "RSN: PMK from pre-auth",
+ pmk, pmk_len);
+ sm->pmk_len = pmk_len;
+ pmksa_cache_add(sm->pmksa, pmk, pmk_len,
+ NULL, 0,
+ sm->preauth_bssid, sm->own_addr,
+ sm->network_ctx,
+ WPA_KEY_MGMT_IEEE8021X);
+ } else {
+ wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
+ "RSN: failed to get master session key from "
+ "pre-auth EAPOL state machines");
+ result = EAPOL_SUPP_RESULT_FAILURE;
+ }
+ }
+
+ wpa_msg(sm->ctx->msg_ctx, MSG_INFO, "RSN: pre-authentication with "
+ MACSTR " %s", MAC2STR(sm->preauth_bssid),
+ result == EAPOL_SUPP_RESULT_SUCCESS ? "completed successfully" :
+ "failed");
+
+ rsn_preauth_deinit(sm);
+ rsn_preauth_candidate_process(sm);
+}
+
+
+static void rsn_preauth_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+ struct wpa_sm *sm = eloop_ctx;
+
+ wpa_msg(sm->ctx->msg_ctx, MSG_INFO, "RSN: pre-authentication with "
+ MACSTR " timed out", MAC2STR(sm->preauth_bssid));
+ rsn_preauth_deinit(sm);
+ rsn_preauth_candidate_process(sm);
+}
+
+
+static int rsn_preauth_eapol_send(void *ctx, int type, const u8 *buf,
+ size_t len)
+{
+ struct wpa_sm *sm = ctx;
+ u8 *msg;
+ size_t msglen;
+ int res;
+
+ /* TODO: could add l2_packet_sendmsg that allows fragments to avoid
+ * extra copy here */
+
+ if (sm->l2_preauth == NULL)
+ return -1;
+
+ msg = wpa_sm_alloc_eapol(sm, type, buf, len, &msglen, NULL);
+ if (msg == NULL)
+ return -1;
+
+ wpa_hexdump(MSG_MSGDUMP, "TX EAPOL (preauth)", msg, msglen);
+ res = l2_packet_send(sm->l2_preauth, sm->preauth_bssid,
+ ETH_P_RSN_PREAUTH, msg, msglen);
+ os_free(msg);
+ return res;
+}
+
+
+/**
+ * rsn_preauth_init - Start new RSN pre-authentication
+ * @sm: Pointer to WPA state machine data from wpa_sm_init()
+ * @dst: Authenticator address (BSSID) with which to preauthenticate
+ * @eap_conf: Current EAP configuration
+ * Returns: 0 on success, -1 on another pre-authentication is in progress,
+ * -2 on layer 2 packet initialization failure, -3 on EAPOL state machine
+ * initialization failure, -4 on memory allocation failure
+ *
+ * This function request an RSN pre-authentication with a given destination
+ * address. This is usually called for PMKSA candidates found from scan results
+ * or from driver reports. In addition, ctrl_iface PREAUTH command can trigger
+ * pre-authentication.
+ */
+int rsn_preauth_init(struct wpa_sm *sm, const u8 *dst,
+ struct eap_peer_config *eap_conf)
+{
+ struct eapol_config eapol_conf;
+ struct eapol_ctx *ctx;
+ int ret;
+
+ if (sm->preauth_eapol)
+ return -1;
+
+ wpa_msg(sm->ctx->msg_ctx, MSG_DEBUG,
+ "RSN: starting pre-authentication with " MACSTR, MAC2STR(dst));
+
+ sm->l2_preauth = l2_packet_init(sm->ifname, sm->own_addr,
+ ETH_P_RSN_PREAUTH,
+ rsn_preauth_receive, sm, 0);
+ if (sm->l2_preauth == NULL) {
+ wpa_printf(MSG_WARNING, "RSN: Failed to initialize L2 packet "
+ "processing for pre-authentication");
+ return -2;
+ }
+
+ if (sm->bridge_ifname) {
+ sm->l2_preauth_br = l2_packet_init(sm->bridge_ifname,
+ sm->own_addr,
+ ETH_P_RSN_PREAUTH,
+ rsn_preauth_receive, sm, 0);
+ if (sm->l2_preauth_br == NULL) {
+ wpa_printf(MSG_WARNING, "RSN: Failed to initialize L2 "
+ "packet processing (bridge) for "
+ "pre-authentication");
+ ret = -2;
+ goto fail;
+ }
+ }
+
+ ctx = os_zalloc(sizeof(*ctx));
+ if (ctx == NULL) {
+ wpa_printf(MSG_WARNING, "Failed to allocate EAPOL context.");
+ ret = -4;
+ goto fail;
+ }
+ ctx->ctx = sm->ctx->ctx;
+ ctx->msg_ctx = sm->ctx->ctx;
+ ctx->preauth = 1;
+ ctx->cb = rsn_preauth_eapol_cb;
+ ctx->cb_ctx = sm;
+ ctx->scard_ctx = sm->scard_ctx;
+ ctx->eapol_send = rsn_preauth_eapol_send;
+ ctx->eapol_send_ctx = sm;
+ ctx->set_config_blob = sm->ctx->set_config_blob;
+ ctx->get_config_blob = sm->ctx->get_config_blob;
+
+ sm->preauth_eapol = eapol_sm_init(ctx);
+ if (sm->preauth_eapol == NULL) {
+ os_free(ctx);
+ wpa_printf(MSG_WARNING, "RSN: Failed to initialize EAPOL "
+ "state machines for pre-authentication");
+ ret = -3;
+ goto fail;
+ }
+ os_memset(&eapol_conf, 0, sizeof(eapol_conf));
+ eapol_conf.accept_802_1x_keys = 0;
+ eapol_conf.required_keys = 0;
+ eapol_conf.fast_reauth = sm->fast_reauth;
+ eapol_conf.workaround = sm->eap_workaround;
+ eapol_sm_notify_config(sm->preauth_eapol, eap_conf, &eapol_conf);
+ /*
+ * Use a shorter startPeriod with preauthentication since the first
+ * preauth EAPOL-Start frame may end up being dropped due to race
+ * condition in the AP between the data receive and key configuration
+ * after the 4-Way Handshake.
+ */
+ eapol_sm_configure(sm->preauth_eapol, -1, -1, 5, 6);
+ os_memcpy(sm->preauth_bssid, dst, ETH_ALEN);
+
+ eapol_sm_notify_portValid(sm->preauth_eapol, TRUE);
+ /* 802.1X::portControl = Auto */
+ eapol_sm_notify_portEnabled(sm->preauth_eapol, TRUE);
+
+ eloop_register_timeout(sm->dot11RSNAConfigSATimeout, 0,
+ rsn_preauth_timeout, sm, NULL);
+
+ return 0;
+
+fail:
+ if (sm->l2_preauth_br) {
+ l2_packet_deinit(sm->l2_preauth_br);
+ sm->l2_preauth_br = NULL;
+ }
+ l2_packet_deinit(sm->l2_preauth);
+ sm->l2_preauth = NULL;
+ return ret;
+}
+
+
+/**
+ * rsn_preauth_deinit - Abort RSN pre-authentication
+ * @sm: Pointer to WPA state machine data from wpa_sm_init()
+ *
+ * This function aborts the current RSN pre-authentication (if one is started)
+ * and frees resources allocated for it.
+ */
+void rsn_preauth_deinit(struct wpa_sm *sm)
+{
+ if (sm == NULL || !sm->preauth_eapol)
+ return;
+
+ eloop_cancel_timeout(rsn_preauth_timeout, sm, NULL);
+ eapol_sm_deinit(sm->preauth_eapol);
+ sm->preauth_eapol = NULL;
+ os_memset(sm->preauth_bssid, 0, ETH_ALEN);
+
+ l2_packet_deinit(sm->l2_preauth);
+ sm->l2_preauth = NULL;
+ if (sm->l2_preauth_br) {
+ l2_packet_deinit(sm->l2_preauth_br);
+ sm->l2_preauth_br = NULL;
+ }
+}
+
+
+/**
+ * rsn_preauth_candidate_process - Process PMKSA candidates
+ * @sm: Pointer to WPA state machine data from wpa_sm_init()
+ *
+ * Go through the PMKSA candidates and start pre-authentication if a candidate
+ * without an existing PMKSA cache entry is found. Processed candidates will be
+ * removed from the list.
+ */
+void rsn_preauth_candidate_process(struct wpa_sm *sm)
+{
+ struct rsn_pmksa_candidate *candidate, *n;
+
+ if (dl_list_empty(&sm->pmksa_candidates))
+ return;
+
+ /* TODO: drop priority for old candidate entries */
+
+ wpa_msg(sm->ctx->msg_ctx, MSG_DEBUG, "RSN: processing PMKSA candidate "
+ "list");
+ if (sm->preauth_eapol ||
+ sm->proto != WPA_PROTO_RSN ||
+ wpa_sm_get_state(sm) != WPA_COMPLETED ||
+ (sm->key_mgmt != WPA_KEY_MGMT_IEEE8021X &&
+ sm->key_mgmt != WPA_KEY_MGMT_IEEE8021X_SHA256 &&
+ sm->key_mgmt != WPA_KEY_MGMT_IEEE8021X_SUITE_B &&
+ sm->key_mgmt != WPA_KEY_MGMT_IEEE8021X_SUITE_B_192)) {
+ wpa_msg(sm->ctx->msg_ctx, MSG_DEBUG, "RSN: not in suitable "
+ "state for new pre-authentication");
+ return; /* invalid state for new pre-auth */
+ }
+
+ dl_list_for_each_safe(candidate, n, &sm->pmksa_candidates,
+ struct rsn_pmksa_candidate, list) {
+ struct rsn_pmksa_cache_entry *p = NULL;
+ p = pmksa_cache_get(sm->pmksa, candidate->bssid, NULL, NULL);
+ if (os_memcmp(sm->bssid, candidate->bssid, ETH_ALEN) != 0 &&
+ (p == NULL || p->opportunistic)) {
+ wpa_msg(sm->ctx->msg_ctx, MSG_DEBUG, "RSN: PMKSA "
+ "candidate " MACSTR
+ " selected for pre-authentication",
+ MAC2STR(candidate->bssid));
+ dl_list_del(&candidate->list);
+ rsn_preauth_init(sm, candidate->bssid,
+ sm->eap_conf_ctx);
+ os_free(candidate);
+ return;
+ }
+ wpa_msg(sm->ctx->msg_ctx, MSG_DEBUG, "RSN: PMKSA candidate "
+ MACSTR " does not need pre-authentication anymore",
+ MAC2STR(candidate->bssid));
+ /* Some drivers (e.g., NDIS) expect to get notified about the
+ * PMKIDs again, so report the existing data now. */
+ if (p) {
+ wpa_sm_add_pmkid(sm, candidate->bssid, p->pmkid);
+ }
+
+ dl_list_del(&candidate->list);
+ os_free(candidate);
+ }
+ wpa_msg(sm->ctx->msg_ctx, MSG_DEBUG, "RSN: no more pending PMKSA "
+ "candidates");
+}
+
+
+/**
+ * pmksa_candidate_add - Add a new PMKSA candidate
+ * @sm: Pointer to WPA state machine data from wpa_sm_init()
+ * @bssid: BSSID (authenticator address) of the candidate
+ * @prio: Priority (the smaller number, the higher priority)
+ * @preauth: Whether the candidate AP advertises support for pre-authentication
+ *
+ * This function is used to add PMKSA candidates for RSN pre-authentication. It
+ * is called from scan result processing and from driver events for PMKSA
+ * candidates, i.e., EVENT_PMKID_CANDIDATE events to wpa_supplicant_event().
+ */
+void pmksa_candidate_add(struct wpa_sm *sm, const u8 *bssid,
+ int prio, int preauth)
+{
+ struct rsn_pmksa_candidate *cand, *pos;
+
+ if (sm->network_ctx && sm->proactive_key_caching)
+ pmksa_cache_get_opportunistic(sm->pmksa, sm->network_ctx,
+ bssid);
+
+ if (!preauth) {
+ wpa_printf(MSG_DEBUG, "RSN: Ignored PMKID candidate without "
+ "preauth flag");
+ return;
+ }
+
+ /* If BSSID already on candidate list, update the priority of the old
+ * entry. Do not override priority based on normal scan results. */
+ cand = NULL;
+ dl_list_for_each(pos, &sm->pmksa_candidates,
+ struct rsn_pmksa_candidate, list) {
+ if (os_memcmp(pos->bssid, bssid, ETH_ALEN) == 0) {
+ cand = pos;
+ break;
+ }
+ }
+
+ if (cand) {
+ dl_list_del(&cand->list);
+ if (prio < PMKID_CANDIDATE_PRIO_SCAN)
+ cand->priority = prio;
+ } else {
+ cand = os_zalloc(sizeof(*cand));
+ if (cand == NULL)
+ return;
+ os_memcpy(cand->bssid, bssid, ETH_ALEN);
+ cand->priority = prio;
+ }
+
+ /* Add candidate to the list; order by increasing priority value. i.e.,
+ * highest priority (smallest value) first. */
+ dl_list_for_each(pos, &sm->pmksa_candidates,
+ struct rsn_pmksa_candidate, list) {
+ if (cand->priority <= pos->priority) {
+ if (!pos->list.prev) {
+ /*
+ * This cannot really happen in pracrice since
+ * pos was fetched from the list and the prev
+ * pointer must be set. It looks like clang
+ * static analyzer gets confused with the
+ * dl_list_del(&cand->list) call above and ends
+ * up assuming pos->list.prev could be NULL.
+ */
+ os_free(cand);
+ return;
+ }
+ dl_list_add(pos->list.prev, &cand->list);
+ cand = NULL;
+ break;
+ }
+ }
+ if (cand)
+ dl_list_add_tail(&sm->pmksa_candidates, &cand->list);
+
+ wpa_msg(sm->ctx->msg_ctx, MSG_DEBUG, "RSN: added PMKSA cache "
+ "candidate " MACSTR " prio %d", MAC2STR(bssid), prio);
+ rsn_preauth_candidate_process(sm);
+}
+
+
+/* TODO: schedule periodic scans if current AP supports preauth */
+
+/**
+ * rsn_preauth_scan_results - Start processing scan results for canditates
+ * @sm: Pointer to WPA state machine data from wpa_sm_init()
+ * Returns: 0 if ready to process results or -1 to skip processing
+ *
+ * This functions is used to notify RSN code about start of new scan results
+ * processing. The actual scan results will be provided by calling
+ * rsn_preauth_scan_result() for each BSS if this function returned 0.
+ */
+int rsn_preauth_scan_results(struct wpa_sm *sm)
+{
+ if (sm->ssid_len == 0)
+ return -1;
+
+ /*
+ * TODO: is it ok to free all candidates? What about the entries
+ * received from EVENT_PMKID_CANDIDATE?
+ */
+ pmksa_candidate_free(sm);
+
+ return 0;
+}
+
+
+/**
+ * rsn_preauth_scan_result - Processing scan result for PMKSA canditates
+ * @sm: Pointer to WPA state machine data from wpa_sm_init()
+ *
+ * Add all suitable APs (Authenticators) from scan results into PMKSA
+ * candidate list.
+ */
+void rsn_preauth_scan_result(struct wpa_sm *sm, const u8 *bssid,
+ const u8 *ssid, const u8 *rsn)
+{
+ struct wpa_ie_data ie;
+ struct rsn_pmksa_cache_entry *pmksa;
+
+ if (ssid[1] != sm->ssid_len ||
+ os_memcmp(ssid + 2, sm->ssid, sm->ssid_len) != 0)
+ return; /* Not for the current SSID */
+
+ if (os_memcmp(bssid, sm->bssid, ETH_ALEN) == 0)
+ return; /* Ignore current AP */
+
+ if (wpa_parse_wpa_ie(rsn, 2 + rsn[1], &ie))
+ return;
+
+ pmksa = pmksa_cache_get(sm->pmksa, bssid, NULL, NULL);
+ if (pmksa && (!pmksa->opportunistic ||
+ !(ie.capabilities & WPA_CAPABILITY_PREAUTH)))
+ return;
+
+ /* Give less priority to candidates found from normal scan results. */
+ pmksa_candidate_add(sm, bssid, PMKID_CANDIDATE_PRIO_SCAN,
+ ie.capabilities & WPA_CAPABILITY_PREAUTH);
+}
+
+
+#ifdef CONFIG_CTRL_IFACE
+/**
+ * rsn_preauth_get_status - Get pre-authentication status
+ * @sm: Pointer to WPA state machine data from wpa_sm_init()
+ * @buf: Buffer for status information
+ * @buflen: Maximum buffer length
+ * @verbose: Whether to include verbose status information
+ * Returns: Number of bytes written to buf.
+ *
+ * Query WPA2 pre-authentication for status information. This function fills in
+ * a text area with current status information. If the buffer (buf) is not
+ * large enough, status information will be truncated to fit the buffer.
+ */
+int rsn_preauth_get_status(struct wpa_sm *sm, char *buf, size_t buflen,
+ int verbose)
+{
+ char *pos = buf, *end = buf + buflen;
+ int res, ret;
+
+ if (sm->preauth_eapol) {
+ ret = os_snprintf(pos, end - pos, "Pre-authentication "
+ "EAPOL state machines:\n");
+ if (os_snprintf_error(end - pos, ret))
+ return pos - buf;
+ pos += ret;
+ res = eapol_sm_get_status(sm->preauth_eapol,
+ pos, end - pos, verbose);
+ if (res >= 0)
+ pos += res;
+ }
+
+ return pos - buf;
+}
+#endif /* CONFIG_CTRL_IFACE */
+
+
+/**
+ * rsn_preauth_in_progress - Verify whether pre-authentication is in progress
+ * @sm: Pointer to WPA state machine data from wpa_sm_init()
+ */
+int rsn_preauth_in_progress(struct wpa_sm *sm)
+{
+ return sm->preauth_eapol != NULL;
+}
+
+#endif /* IEEE8021X_EAPOL */
diff --git a/freebsd/contrib/wpa/src/rsn_supp/preauth.h b/freebsd/contrib/wpa/src/rsn_supp/preauth.h
new file mode 100644
index 00000000..277f0663
--- /dev/null
+++ b/freebsd/contrib/wpa/src/rsn_supp/preauth.h
@@ -0,0 +1,79 @@
+/*
+ * wpa_supplicant - WPA2/RSN pre-authentication functions
+ * Copyright (c) 2003-2009, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef PREAUTH_H
+#define PREAUTH_H
+
+struct wpa_scan_results;
+
+#ifdef IEEE8021X_EAPOL
+
+void pmksa_candidate_free(struct wpa_sm *sm);
+int rsn_preauth_init(struct wpa_sm *sm, const u8 *dst,
+ struct eap_peer_config *eap_conf);
+void rsn_preauth_deinit(struct wpa_sm *sm);
+int rsn_preauth_scan_results(struct wpa_sm *sm);
+void rsn_preauth_scan_result(struct wpa_sm *sm, const u8 *bssid,
+ const u8 *ssid, const u8 *rsn);
+void pmksa_candidate_add(struct wpa_sm *sm, const u8 *bssid,
+ int prio, int preauth);
+void rsn_preauth_candidate_process(struct wpa_sm *sm);
+int rsn_preauth_get_status(struct wpa_sm *sm, char *buf, size_t buflen,
+ int verbose);
+int rsn_preauth_in_progress(struct wpa_sm *sm);
+
+#else /* IEEE8021X_EAPOL */
+
+static inline void pmksa_candidate_free(struct wpa_sm *sm)
+{
+}
+
+static inline void rsn_preauth_candidate_process(struct wpa_sm *sm)
+{
+}
+
+static inline int rsn_preauth_init(struct wpa_sm *sm, const u8 *dst,
+ struct eap_peer_config *eap_conf)
+{
+ return -1;
+}
+
+static inline void rsn_preauth_deinit(struct wpa_sm *sm)
+{
+}
+
+static inline int rsn_preauth_scan_results(struct wpa_sm *sm)
+{
+ return -1;
+}
+
+static inline void rsn_preauth_scan_result(struct wpa_sm *sm, const u8 *bssid,
+ const u8 *ssid, const u8 *rsn)
+{
+}
+
+static inline void pmksa_candidate_add(struct wpa_sm *sm,
+ const u8 *bssid,
+ int prio, int preauth)
+{
+}
+
+static inline int rsn_preauth_get_status(struct wpa_sm *sm, char *buf,
+ size_t buflen, int verbose)
+{
+ return 0;
+}
+
+static inline int rsn_preauth_in_progress(struct wpa_sm *sm)
+{
+ return 0;
+}
+
+#endif /* IEEE8021X_EAPOL */
+
+#endif /* PREAUTH_H */
diff --git a/freebsd/contrib/wpa/src/rsn_supp/wpa.c b/freebsd/contrib/wpa/src/rsn_supp/wpa.c
new file mode 100644
index 00000000..a738c986
--- /dev/null
+++ b/freebsd/contrib/wpa/src/rsn_supp/wpa.c
@@ -0,0 +1,2979 @@
+#include <machine/rtems-bsd-user-space.h>
+
+/*
+ * WPA Supplicant - WPA state machine and EAPOL-Key processing
+ * Copyright (c) 2003-2015, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "crypto/aes_wrap.h"
+#include "crypto/crypto.h"
+#include "crypto/random.h"
+#include "common/ieee802_11_defs.h"
+#include "eapol_supp/eapol_supp_sm.h"
+#include "wpa.h"
+#include "eloop.h"
+#include "preauth.h"
+#include "pmksa_cache.h"
+#include "wpa_i.h"
+#include "wpa_ie.h"
+#include "peerkey.h"
+
+
+/**
+ * wpa_eapol_key_send - Send WPA/RSN EAPOL-Key message
+ * @sm: Pointer to WPA state machine data from wpa_sm_init()
+ * @kck: Key Confirmation Key (KCK, part of PTK)
+ * @kck_len: KCK length in octets
+ * @ver: Version field from Key Info
+ * @dest: Destination address for the frame
+ * @proto: Ethertype (usually ETH_P_EAPOL)
+ * @msg: EAPOL-Key message
+ * @msg_len: Length of message
+ * @key_mic: Pointer to the buffer to which the EAPOL-Key MIC is written
+ */
+void wpa_eapol_key_send(struct wpa_sm *sm, const u8 *kck, size_t kck_len,
+ int ver, const u8 *dest, u16 proto,
+ u8 *msg, size_t msg_len, u8 *key_mic)
+{
+ size_t mic_len = wpa_mic_len(sm->key_mgmt);
+
+ if (is_zero_ether_addr(dest) && is_zero_ether_addr(sm->bssid)) {
+ /*
+ * Association event was not yet received; try to fetch
+ * BSSID from the driver.
+ */
+ if (wpa_sm_get_bssid(sm, sm->bssid) < 0) {
+ wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
+ "WPA: Failed to read BSSID for "
+ "EAPOL-Key destination address");
+ } else {
+ dest = sm->bssid;
+ wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
+ "WPA: Use BSSID (" MACSTR
+ ") as the destination for EAPOL-Key",
+ MAC2STR(dest));
+ }
+ }
+ if (key_mic &&
+ wpa_eapol_key_mic(kck, kck_len, sm->key_mgmt, ver, msg, msg_len,
+ key_mic)) {
+ wpa_msg(sm->ctx->msg_ctx, MSG_ERROR,
+ "WPA: Failed to generate EAPOL-Key version %d key_mgmt 0x%x MIC",
+ ver, sm->key_mgmt);
+ goto out;
+ }
+ wpa_hexdump_key(MSG_DEBUG, "WPA: KCK", kck, kck_len);
+ wpa_hexdump(MSG_DEBUG, "WPA: Derived Key MIC", key_mic, mic_len);
+ wpa_hexdump(MSG_MSGDUMP, "WPA: TX EAPOL-Key", msg, msg_len);
+ wpa_sm_ether_send(sm, dest, proto, msg, msg_len);
+ eapol_sm_notify_tx_eapol_key(sm->eapol);
+out:
+ os_free(msg);
+}
+
+
+/**
+ * wpa_sm_key_request - Send EAPOL-Key Request
+ * @sm: Pointer to WPA state machine data from wpa_sm_init()
+ * @error: Indicate whether this is an Michael MIC error report
+ * @pairwise: 1 = error report for pairwise packet, 0 = for group packet
+ *
+ * Send an EAPOL-Key Request to the current authenticator. This function is
+ * used to request rekeying and it is usually called when a local Michael MIC
+ * failure is detected.
+ */
+void wpa_sm_key_request(struct wpa_sm *sm, int error, int pairwise)
+{
+ size_t mic_len, hdrlen, rlen;
+ struct wpa_eapol_key *reply;
+ struct wpa_eapol_key_192 *reply192;
+ int key_info, ver;
+ u8 bssid[ETH_ALEN], *rbuf, *key_mic;
+
+ if (sm->key_mgmt == WPA_KEY_MGMT_OSEN ||
+ wpa_key_mgmt_suite_b(sm->key_mgmt))
+ ver = WPA_KEY_INFO_TYPE_AKM_DEFINED;
+ else if (wpa_key_mgmt_ft(sm->key_mgmt) ||
+ wpa_key_mgmt_sha256(sm->key_mgmt))
+ ver = WPA_KEY_INFO_TYPE_AES_128_CMAC;
+ else if (sm->pairwise_cipher != WPA_CIPHER_TKIP)
+ ver = WPA_KEY_INFO_TYPE_HMAC_SHA1_AES;
+ else
+ ver = WPA_KEY_INFO_TYPE_HMAC_MD5_RC4;
+
+ if (wpa_sm_get_bssid(sm, bssid) < 0) {
+ wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+ "Failed to read BSSID for EAPOL-Key request");
+ return;
+ }
+
+ mic_len = wpa_mic_len(sm->key_mgmt);
+ hdrlen = mic_len == 24 ? sizeof(*reply192) : sizeof(*reply);
+ rbuf = wpa_sm_alloc_eapol(sm, IEEE802_1X_TYPE_EAPOL_KEY, NULL,
+ hdrlen, &rlen, (void *) &reply);
+ if (rbuf == NULL)
+ return;
+ reply192 = (struct wpa_eapol_key_192 *) reply;
+
+ reply->type = (sm->proto == WPA_PROTO_RSN ||
+ sm->proto == WPA_PROTO_OSEN) ?
+ EAPOL_KEY_TYPE_RSN : EAPOL_KEY_TYPE_WPA;
+ key_info = WPA_KEY_INFO_REQUEST | ver;
+ if (sm->ptk_set)
+ key_info |= WPA_KEY_INFO_MIC;
+ if (error)
+ key_info |= WPA_KEY_INFO_ERROR;
+ if (pairwise)
+ key_info |= WPA_KEY_INFO_KEY_TYPE;
+ WPA_PUT_BE16(reply->key_info, key_info);
+ WPA_PUT_BE16(reply->key_length, 0);
+ os_memcpy(reply->replay_counter, sm->request_counter,
+ WPA_REPLAY_COUNTER_LEN);
+ inc_byte_array(sm->request_counter, WPA_REPLAY_COUNTER_LEN);
+
+ if (mic_len == 24)
+ WPA_PUT_BE16(reply192->key_data_length, 0);
+ else
+ WPA_PUT_BE16(reply->key_data_length, 0);
+ if (!(key_info & WPA_KEY_INFO_MIC))
+ key_mic = NULL;
+ else
+ key_mic = reply192->key_mic; /* same offset in reply */
+
+ wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
+ "WPA: Sending EAPOL-Key Request (error=%d "
+ "pairwise=%d ptk_set=%d len=%lu)",
+ error, pairwise, sm->ptk_set, (unsigned long) rlen);
+ wpa_eapol_key_send(sm, sm->ptk.kck, sm->ptk.kck_len, ver, bssid,
+ ETH_P_EAPOL, rbuf, rlen, key_mic);
+}
+
+
+static void wpa_supplicant_key_mgmt_set_pmk(struct wpa_sm *sm)
+{
+#ifdef CONFIG_IEEE80211R
+ if (sm->key_mgmt == WPA_KEY_MGMT_FT_IEEE8021X) {
+ if (wpa_sm_key_mgmt_set_pmk(sm, sm->xxkey, sm->xxkey_len))
+ wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
+ "RSN: Cannot set low order 256 bits of MSK for key management offload");
+ } else {
+#endif /* CONFIG_IEEE80211R */
+ if (wpa_sm_key_mgmt_set_pmk(sm, sm->pmk, sm->pmk_len))
+ wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
+ "RSN: Cannot set PMK for key management offload");
+#ifdef CONFIG_IEEE80211R
+ }
+#endif /* CONFIG_IEEE80211R */
+}
+
+
+static int wpa_supplicant_get_pmk(struct wpa_sm *sm,
+ const unsigned char *src_addr,
+ const u8 *pmkid)
+{
+ int abort_cached = 0;
+
+ if (pmkid && !sm->cur_pmksa) {
+ /* When using drivers that generate RSN IE, wpa_supplicant may
+ * not have enough time to get the association information
+ * event before receiving this 1/4 message, so try to find a
+ * matching PMKSA cache entry here. */
+ sm->cur_pmksa = pmksa_cache_get(sm->pmksa, src_addr, pmkid,
+ NULL);
+ if (sm->cur_pmksa) {
+ wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
+ "RSN: found matching PMKID from PMKSA cache");
+ } else {
+ wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
+ "RSN: no matching PMKID found");
+ abort_cached = 1;
+ }
+ }
+
+ if (pmkid && sm->cur_pmksa &&
+ os_memcmp_const(pmkid, sm->cur_pmksa->pmkid, PMKID_LEN) == 0) {
+ wpa_hexdump(MSG_DEBUG, "RSN: matched PMKID", pmkid, PMKID_LEN);
+ wpa_sm_set_pmk_from_pmksa(sm);
+ wpa_hexdump_key(MSG_DEBUG, "RSN: PMK from PMKSA cache",
+ sm->pmk, sm->pmk_len);
+ eapol_sm_notify_cached(sm->eapol);
+#ifdef CONFIG_IEEE80211R
+ sm->xxkey_len = 0;
+#endif /* CONFIG_IEEE80211R */
+ } else if (wpa_key_mgmt_wpa_ieee8021x(sm->key_mgmt) && sm->eapol) {
+ int res, pmk_len;
+ pmk_len = PMK_LEN;
+ res = eapol_sm_get_key(sm->eapol, sm->pmk, PMK_LEN);
+ if (res) {
+ /*
+ * EAP-LEAP is an exception from other EAP methods: it
+ * uses only 16-byte PMK.
+ */
+ res = eapol_sm_get_key(sm->eapol, sm->pmk, 16);
+ pmk_len = 16;
+ } else {
+#ifdef CONFIG_IEEE80211R
+ u8 buf[2 * PMK_LEN];
+ if (eapol_sm_get_key(sm->eapol, buf, 2 * PMK_LEN) == 0)
+ {
+ os_memcpy(sm->xxkey, buf + PMK_LEN, PMK_LEN);
+ sm->xxkey_len = PMK_LEN;
+ os_memset(buf, 0, sizeof(buf));
+ }
+#endif /* CONFIG_IEEE80211R */
+ }
+ if (res == 0) {
+ struct rsn_pmksa_cache_entry *sa = NULL;
+ wpa_hexdump_key(MSG_DEBUG, "WPA: PMK from EAPOL state "
+ "machines", sm->pmk, pmk_len);
+ sm->pmk_len = pmk_len;
+ wpa_supplicant_key_mgmt_set_pmk(sm);
+ if (sm->proto == WPA_PROTO_RSN &&
+ !wpa_key_mgmt_suite_b(sm->key_mgmt) &&
+ !wpa_key_mgmt_ft(sm->key_mgmt)) {
+ sa = pmksa_cache_add(sm->pmksa,
+ sm->pmk, pmk_len,
+ NULL, 0,
+ src_addr, sm->own_addr,
+ sm->network_ctx,
+ sm->key_mgmt);
+ }
+ if (!sm->cur_pmksa && pmkid &&
+ pmksa_cache_get(sm->pmksa, src_addr, pmkid, NULL))
+ {
+ wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
+ "RSN: the new PMK matches with the "
+ "PMKID");
+ abort_cached = 0;
+ } else if (sa && !sm->cur_pmksa && pmkid) {
+ /*
+ * It looks like the authentication server
+ * derived mismatching MSK. This should not
+ * really happen, but bugs happen.. There is not
+ * much we can do here without knowing what
+ * exactly caused the server to misbehave.
+ */
+ wpa_dbg(sm->ctx->msg_ctx, MSG_INFO,
+ "RSN: PMKID mismatch - authentication server may have derived different MSK?!");
+ return -1;
+ }
+
+ if (!sm->cur_pmksa)
+ sm->cur_pmksa = sa;
+ } else {
+ wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+ "WPA: Failed to get master session key from "
+ "EAPOL state machines - key handshake "
+ "aborted");
+ if (sm->cur_pmksa) {
+ wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
+ "RSN: Cancelled PMKSA caching "
+ "attempt");
+ sm->cur_pmksa = NULL;
+ abort_cached = 1;
+ } else if (!abort_cached) {
+ return -1;
+ }
+ }
+ }
+
+ if (abort_cached && wpa_key_mgmt_wpa_ieee8021x(sm->key_mgmt) &&
+ !wpa_key_mgmt_suite_b(sm->key_mgmt) &&
+ !wpa_key_mgmt_ft(sm->key_mgmt) && sm->key_mgmt != WPA_KEY_MGMT_OSEN)
+ {
+ /* Send EAPOL-Start to trigger full EAP authentication. */
+ u8 *buf;
+ size_t buflen;
+
+ wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
+ "RSN: no PMKSA entry found - trigger "
+ "full EAP authentication");
+ buf = wpa_sm_alloc_eapol(sm, IEEE802_1X_TYPE_EAPOL_START,
+ NULL, 0, &buflen, NULL);
+ if (buf) {
+ wpa_sm_ether_send(sm, sm->bssid, ETH_P_EAPOL,
+ buf, buflen);
+ os_free(buf);
+ return -2;
+ }
+
+ return -1;
+ }
+
+ return 0;
+}
+
+
+/**
+ * wpa_supplicant_send_2_of_4 - Send message 2 of WPA/RSN 4-Way Handshake
+ * @sm: Pointer to WPA state machine data from wpa_sm_init()
+ * @dst: Destination address for the frame
+ * @key: Pointer to the EAPOL-Key frame header
+ * @ver: Version bits from EAPOL-Key Key Info
+ * @nonce: Nonce value for the EAPOL-Key frame
+ * @wpa_ie: WPA/RSN IE
+ * @wpa_ie_len: Length of the WPA/RSN IE
+ * @ptk: PTK to use for keyed hash and encryption
+ * Returns: 0 on success, -1 on failure
+ */
+int wpa_supplicant_send_2_of_4(struct wpa_sm *sm, const unsigned char *dst,
+ const struct wpa_eapol_key *key,
+ int ver, const u8 *nonce,
+ const u8 *wpa_ie, size_t wpa_ie_len,
+ struct wpa_ptk *ptk)
+{
+ size_t mic_len, hdrlen, rlen;
+ struct wpa_eapol_key *reply;
+ struct wpa_eapol_key_192 *reply192;
+ u8 *rbuf, *key_mic;
+ u8 *rsn_ie_buf = NULL;
+
+ if (wpa_ie == NULL) {
+ wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, "WPA: No wpa_ie set - "
+ "cannot generate msg 2/4");
+ return -1;
+ }
+
+#ifdef CONFIG_IEEE80211R
+ if (wpa_key_mgmt_ft(sm->key_mgmt)) {
+ int res;
+
+ /*
+ * Add PMKR1Name into RSN IE (PMKID-List) and add MDIE and
+ * FTIE from (Re)Association Response.
+ */
+ rsn_ie_buf = os_malloc(wpa_ie_len + 2 + 2 + PMKID_LEN +
+ sm->assoc_resp_ies_len);
+ if (rsn_ie_buf == NULL)
+ return -1;
+ os_memcpy(rsn_ie_buf, wpa_ie, wpa_ie_len);
+ res = wpa_insert_pmkid(rsn_ie_buf, wpa_ie_len,
+ sm->pmk_r1_name);
+ if (res < 0) {
+ os_free(rsn_ie_buf);
+ return -1;
+ }
+ wpa_ie_len += res;
+
+ if (sm->assoc_resp_ies) {
+ os_memcpy(rsn_ie_buf + wpa_ie_len, sm->assoc_resp_ies,
+ sm->assoc_resp_ies_len);
+ wpa_ie_len += sm->assoc_resp_ies_len;
+ }
+
+ wpa_ie = rsn_ie_buf;
+ }
+#endif /* CONFIG_IEEE80211R */
+
+ wpa_hexdump(MSG_DEBUG, "WPA: WPA IE for msg 2/4", wpa_ie, wpa_ie_len);
+
+ mic_len = wpa_mic_len(sm->key_mgmt);
+ hdrlen = mic_len == 24 ? sizeof(*reply192) : sizeof(*reply);
+ rbuf = wpa_sm_alloc_eapol(sm, IEEE802_1X_TYPE_EAPOL_KEY,
+ NULL, hdrlen + wpa_ie_len,
+ &rlen, (void *) &reply);
+ if (rbuf == NULL) {
+ os_free(rsn_ie_buf);
+ return -1;
+ }
+ reply192 = (struct wpa_eapol_key_192 *) reply;
+
+ reply->type = (sm->proto == WPA_PROTO_RSN ||
+ sm->proto == WPA_PROTO_OSEN) ?
+ EAPOL_KEY_TYPE_RSN : EAPOL_KEY_TYPE_WPA;
+ WPA_PUT_BE16(reply->key_info,
+ ver | WPA_KEY_INFO_KEY_TYPE | WPA_KEY_INFO_MIC);
+ if (sm->proto == WPA_PROTO_RSN || sm->proto == WPA_PROTO_OSEN)
+ WPA_PUT_BE16(reply->key_length, 0);
+ else
+ os_memcpy(reply->key_length, key->key_length, 2);
+ os_memcpy(reply->replay_counter, key->replay_counter,
+ WPA_REPLAY_COUNTER_LEN);
+ wpa_hexdump(MSG_DEBUG, "WPA: Replay Counter", reply->replay_counter,
+ WPA_REPLAY_COUNTER_LEN);
+
+ key_mic = reply192->key_mic; /* same offset for reply and reply192 */
+ if (mic_len == 24) {
+ WPA_PUT_BE16(reply192->key_data_length, wpa_ie_len);
+ os_memcpy(reply192 + 1, wpa_ie, wpa_ie_len);
+ } else {
+ WPA_PUT_BE16(reply->key_data_length, wpa_ie_len);
+ os_memcpy(reply + 1, wpa_ie, wpa_ie_len);
+ }
+ os_free(rsn_ie_buf);
+
+ os_memcpy(reply->key_nonce, nonce, WPA_NONCE_LEN);
+
+ wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: Sending EAPOL-Key 2/4");
+ wpa_eapol_key_send(sm, ptk->kck, ptk->kck_len, ver, dst, ETH_P_EAPOL,
+ rbuf, rlen, key_mic);
+
+ return 0;
+}
+
+
+static int wpa_derive_ptk(struct wpa_sm *sm, const unsigned char *src_addr,
+ const struct wpa_eapol_key *key, struct wpa_ptk *ptk)
+{
+#ifdef CONFIG_IEEE80211R
+ if (wpa_key_mgmt_ft(sm->key_mgmt))
+ return wpa_derive_ptk_ft(sm, src_addr, key, ptk);
+#endif /* CONFIG_IEEE80211R */
+
+ return wpa_pmk_to_ptk(sm->pmk, sm->pmk_len, "Pairwise key expansion",
+ sm->own_addr, sm->bssid, sm->snonce,
+ key->key_nonce, ptk, sm->key_mgmt,
+ sm->pairwise_cipher);
+}
+
+
+static void wpa_supplicant_process_1_of_4(struct wpa_sm *sm,
+ const unsigned char *src_addr,
+ const struct wpa_eapol_key *key,
+ u16 ver, const u8 *key_data,
+ size_t key_data_len)
+{
+ struct wpa_eapol_ie_parse ie;
+ struct wpa_ptk *ptk;
+ int res;
+ u8 *kde, *kde_buf = NULL;
+ size_t kde_len;
+
+ if (wpa_sm_get_network_ctx(sm) == NULL) {
+ wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, "WPA: No SSID info "
+ "found (msg 1 of 4)");
+ return;
+ }
+
+ wpa_sm_set_state(sm, WPA_4WAY_HANDSHAKE);
+ wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: RX message 1 of 4-Way "
+ "Handshake from " MACSTR " (ver=%d)", MAC2STR(src_addr), ver);
+
+ os_memset(&ie, 0, sizeof(ie));
+
+ if (sm->proto == WPA_PROTO_RSN || sm->proto == WPA_PROTO_OSEN) {
+ /* RSN: msg 1/4 should contain PMKID for the selected PMK */
+ wpa_hexdump(MSG_DEBUG, "RSN: msg 1/4 key data",
+ key_data, key_data_len);
+ if (wpa_supplicant_parse_ies(key_data, key_data_len, &ie) < 0)
+ goto failed;
+ if (ie.pmkid) {
+ wpa_hexdump(MSG_DEBUG, "RSN: PMKID from "
+ "Authenticator", ie.pmkid, PMKID_LEN);
+ }
+ }
+
+ res = wpa_supplicant_get_pmk(sm, src_addr, ie.pmkid);
+ if (res == -2) {
+ wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "RSN: Do not reply to "
+ "msg 1/4 - requesting full EAP authentication");
+ return;
+ }
+ if (res)
+ goto failed;
+
+ if (sm->renew_snonce) {
+ if (random_get_bytes(sm->snonce, WPA_NONCE_LEN)) {
+ wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+ "WPA: Failed to get random data for SNonce");
+ goto failed;
+ }
+ sm->renew_snonce = 0;
+ wpa_hexdump(MSG_DEBUG, "WPA: Renewed SNonce",
+ sm->snonce, WPA_NONCE_LEN);
+ }
+
+ /* Calculate PTK which will be stored as a temporary PTK until it has
+ * been verified when processing message 3/4. */
+ ptk = &sm->tptk;
+ wpa_derive_ptk(sm, src_addr, key, ptk);
+ if (sm->pairwise_cipher == WPA_CIPHER_TKIP) {
+ u8 buf[8];
+ /* Supplicant: swap tx/rx Mic keys */
+ os_memcpy(buf, &ptk->tk[16], 8);
+ os_memcpy(&ptk->tk[16], &ptk->tk[24], 8);
+ os_memcpy(&ptk->tk[24], buf, 8);
+ os_memset(buf, 0, sizeof(buf));
+ }
+ sm->tptk_set = 1;
+
+ kde = sm->assoc_wpa_ie;
+ kde_len = sm->assoc_wpa_ie_len;
+
+#ifdef CONFIG_P2P
+ if (sm->p2p) {
+ kde_buf = os_malloc(kde_len + 2 + RSN_SELECTOR_LEN + 1);
+ if (kde_buf) {
+ u8 *pos;
+ wpa_printf(MSG_DEBUG, "P2P: Add IP Address Request KDE "
+ "into EAPOL-Key 2/4");
+ os_memcpy(kde_buf, kde, kde_len);
+ kde = kde_buf;
+ pos = kde + kde_len;
+ *pos++ = WLAN_EID_VENDOR_SPECIFIC;
+ *pos++ = RSN_SELECTOR_LEN + 1;
+ RSN_SELECTOR_PUT(pos, WFA_KEY_DATA_IP_ADDR_REQ);
+ pos += RSN_SELECTOR_LEN;
+ *pos++ = 0x01;
+ kde_len = pos - kde;
+ }
+ }
+#endif /* CONFIG_P2P */
+
+ if (wpa_supplicant_send_2_of_4(sm, sm->bssid, key, ver, sm->snonce,
+ kde, kde_len, ptk))
+ goto failed;
+
+ os_free(kde_buf);
+ os_memcpy(sm->anonce, key->key_nonce, WPA_NONCE_LEN);
+ return;
+
+failed:
+ os_free(kde_buf);
+ wpa_sm_deauthenticate(sm, WLAN_REASON_UNSPECIFIED);
+}
+
+
+static void wpa_sm_start_preauth(void *eloop_ctx, void *timeout_ctx)
+{
+ struct wpa_sm *sm = eloop_ctx;
+ rsn_preauth_candidate_process(sm);
+}
+
+
+static void wpa_supplicant_key_neg_complete(struct wpa_sm *sm,
+ const u8 *addr, int secure)
+{
+ wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
+ "WPA: Key negotiation completed with "
+ MACSTR " [PTK=%s GTK=%s]", MAC2STR(addr),
+ wpa_cipher_txt(sm->pairwise_cipher),
+ wpa_cipher_txt(sm->group_cipher));
+ wpa_sm_cancel_auth_timeout(sm);
+ wpa_sm_set_state(sm, WPA_COMPLETED);
+
+ if (secure) {
+ wpa_sm_mlme_setprotection(
+ sm, addr, MLME_SETPROTECTION_PROTECT_TYPE_RX_TX,
+ MLME_SETPROTECTION_KEY_TYPE_PAIRWISE);
+ eapol_sm_notify_portValid(sm->eapol, TRUE);
+ if (wpa_key_mgmt_wpa_psk(sm->key_mgmt))
+ eapol_sm_notify_eap_success(sm->eapol, TRUE);
+ /*
+ * Start preauthentication after a short wait to avoid a
+ * possible race condition between the data receive and key
+ * configuration after the 4-Way Handshake. This increases the
+ * likelihood of the first preauth EAPOL-Start frame getting to
+ * the target AP.
+ */
+ eloop_register_timeout(1, 0, wpa_sm_start_preauth, sm, NULL);
+ }
+
+ if (sm->cur_pmksa && sm->cur_pmksa->opportunistic) {
+ wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
+ "RSN: Authenticator accepted "
+ "opportunistic PMKSA entry - marking it valid");
+ sm->cur_pmksa->opportunistic = 0;
+ }
+
+#ifdef CONFIG_IEEE80211R
+ if (wpa_key_mgmt_ft(sm->key_mgmt)) {
+ /* Prepare for the next transition */
+ wpa_ft_prepare_auth_request(sm, NULL);
+ }
+#endif /* CONFIG_IEEE80211R */
+}
+
+
+static void wpa_sm_rekey_ptk(void *eloop_ctx, void *timeout_ctx)
+{
+ struct wpa_sm *sm = eloop_ctx;
+ wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: Request PTK rekeying");
+ wpa_sm_key_request(sm, 0, 1);
+}
+
+
+static int wpa_supplicant_install_ptk(struct wpa_sm *sm,
+ const struct wpa_eapol_key *key)
+{
+ int keylen, rsclen;
+ enum wpa_alg alg;
+ const u8 *key_rsc;
+ u8 null_rsc[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
+
+ wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
+ "WPA: Installing PTK to the driver");
+
+ if (sm->pairwise_cipher == WPA_CIPHER_NONE) {
+ wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: Pairwise Cipher "
+ "Suite: NONE - do not use pairwise keys");
+ return 0;
+ }
+
+ if (!wpa_cipher_valid_pairwise(sm->pairwise_cipher)) {
+ wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+ "WPA: Unsupported pairwise cipher %d",
+ sm->pairwise_cipher);
+ return -1;
+ }
+
+ alg = wpa_cipher_to_alg(sm->pairwise_cipher);
+ keylen = wpa_cipher_key_len(sm->pairwise_cipher);
+ rsclen = wpa_cipher_rsc_len(sm->pairwise_cipher);
+
+ if (sm->proto == WPA_PROTO_RSN || sm->proto == WPA_PROTO_OSEN) {
+ key_rsc = null_rsc;
+ } else {
+ key_rsc = key->key_rsc;
+ wpa_hexdump(MSG_DEBUG, "WPA: RSC", key_rsc, rsclen);
+ }
+
+ if (wpa_sm_set_key(sm, alg, sm->bssid, 0, 1, key_rsc, rsclen,
+ sm->ptk.tk, keylen) < 0) {
+ wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+ "WPA: Failed to set PTK to the "
+ "driver (alg=%d keylen=%d bssid=" MACSTR ")",
+ alg, keylen, MAC2STR(sm->bssid));
+ return -1;
+ }
+
+ /* TK is not needed anymore in supplicant */
+ os_memset(sm->ptk.tk, 0, WPA_TK_MAX_LEN);
+
+ if (sm->wpa_ptk_rekey) {
+ eloop_cancel_timeout(wpa_sm_rekey_ptk, sm, NULL);
+ eloop_register_timeout(sm->wpa_ptk_rekey, 0, wpa_sm_rekey_ptk,
+ sm, NULL);
+ }
+
+ return 0;
+}
+
+
+static int wpa_supplicant_check_group_cipher(struct wpa_sm *sm,
+ int group_cipher,
+ int keylen, int maxkeylen,
+ int *key_rsc_len,
+ enum wpa_alg *alg)
+{
+ int klen;
+
+ *alg = wpa_cipher_to_alg(group_cipher);
+ if (*alg == WPA_ALG_NONE) {
+ wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+ "WPA: Unsupported Group Cipher %d",
+ group_cipher);
+ return -1;
+ }
+ *key_rsc_len = wpa_cipher_rsc_len(group_cipher);
+
+ klen = wpa_cipher_key_len(group_cipher);
+ if (keylen != klen || maxkeylen < klen) {
+ wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+ "WPA: Unsupported %s Group Cipher key length %d (%d)",
+ wpa_cipher_txt(group_cipher), keylen, maxkeylen);
+ return -1;
+ }
+ return 0;
+}
+
+
+struct wpa_gtk_data {
+ enum wpa_alg alg;
+ int tx, key_rsc_len, keyidx;
+ u8 gtk[32];
+ int gtk_len;
+};
+
+
+static int wpa_supplicant_install_gtk(struct wpa_sm *sm,
+ const struct wpa_gtk_data *gd,
+ const u8 *key_rsc)
+{
+ const u8 *_gtk = gd->gtk;
+ u8 gtk_buf[32];
+
+ wpa_hexdump_key(MSG_DEBUG, "WPA: Group Key", gd->gtk, gd->gtk_len);
+ wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
+ "WPA: Installing GTK to the driver (keyidx=%d tx=%d len=%d)",
+ gd->keyidx, gd->tx, gd->gtk_len);
+ wpa_hexdump(MSG_DEBUG, "WPA: RSC", key_rsc, gd->key_rsc_len);
+ if (sm->group_cipher == WPA_CIPHER_TKIP) {
+ /* Swap Tx/Rx keys for Michael MIC */
+ os_memcpy(gtk_buf, gd->gtk, 16);
+ os_memcpy(gtk_buf + 16, gd->gtk + 24, 8);
+ os_memcpy(gtk_buf + 24, gd->gtk + 16, 8);
+ _gtk = gtk_buf;
+ }
+ if (sm->pairwise_cipher == WPA_CIPHER_NONE) {
+ if (wpa_sm_set_key(sm, gd->alg, NULL,
+ gd->keyidx, 1, key_rsc, gd->key_rsc_len,
+ _gtk, gd->gtk_len) < 0) {
+ wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+ "WPA: Failed to set GTK to the driver "
+ "(Group only)");
+ os_memset(gtk_buf, 0, sizeof(gtk_buf));
+ return -1;
+ }
+ } else if (wpa_sm_set_key(sm, gd->alg, broadcast_ether_addr,
+ gd->keyidx, gd->tx, key_rsc, gd->key_rsc_len,
+ _gtk, gd->gtk_len) < 0) {
+ wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+ "WPA: Failed to set GTK to "
+ "the driver (alg=%d keylen=%d keyidx=%d)",
+ gd->alg, gd->gtk_len, gd->keyidx);
+ os_memset(gtk_buf, 0, sizeof(gtk_buf));
+ return -1;
+ }
+ os_memset(gtk_buf, 0, sizeof(gtk_buf));
+
+ return 0;
+}
+
+
+static int wpa_supplicant_gtk_tx_bit_workaround(const struct wpa_sm *sm,
+ int tx)
+{
+ if (tx && sm->pairwise_cipher != WPA_CIPHER_NONE) {
+ /* Ignore Tx bit for GTK if a pairwise key is used. One AP
+ * seemed to set this bit (incorrectly, since Tx is only when
+ * doing Group Key only APs) and without this workaround, the
+ * data connection does not work because wpa_supplicant
+ * configured non-zero keyidx to be used for unicast. */
+ wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
+ "WPA: Tx bit set for GTK, but pairwise "
+ "keys are used - ignore Tx bit");
+ return 0;
+ }
+ return tx;
+}
+
+
+static int wpa_supplicant_pairwise_gtk(struct wpa_sm *sm,
+ const struct wpa_eapol_key *key,
+ const u8 *gtk, size_t gtk_len,
+ int key_info)
+{
+ struct wpa_gtk_data gd;
+
+ /*
+ * IEEE Std 802.11i-2004 - 8.5.2 EAPOL-Key frames - Figure 43x
+ * GTK KDE format:
+ * KeyID[bits 0-1], Tx [bit 2], Reserved [bits 3-7]
+ * Reserved [bits 0-7]
+ * GTK
+ */
+
+ os_memset(&gd, 0, sizeof(gd));
+ wpa_hexdump_key(MSG_DEBUG, "RSN: received GTK in pairwise handshake",
+ gtk, gtk_len);
+
+ if (gtk_len < 2 || gtk_len - 2 > sizeof(gd.gtk))
+ return -1;
+
+ gd.keyidx = gtk[0] & 0x3;
+ gd.tx = wpa_supplicant_gtk_tx_bit_workaround(sm,
+ !!(gtk[0] & BIT(2)));
+ gtk += 2;
+ gtk_len -= 2;
+
+ os_memcpy(gd.gtk, gtk, gtk_len);
+ gd.gtk_len = gtk_len;
+
+ if (sm->group_cipher != WPA_CIPHER_GTK_NOT_USED &&
+ (wpa_supplicant_check_group_cipher(sm, sm->group_cipher,
+ gtk_len, gtk_len,
+ &gd.key_rsc_len, &gd.alg) ||
+ wpa_supplicant_install_gtk(sm, &gd, key->key_rsc))) {
+ wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
+ "RSN: Failed to install GTK");
+ os_memset(&gd, 0, sizeof(gd));
+ return -1;
+ }
+ os_memset(&gd, 0, sizeof(gd));
+
+ wpa_supplicant_key_neg_complete(sm, sm->bssid,
+ key_info & WPA_KEY_INFO_SECURE);
+ return 0;
+}
+
+
+static int ieee80211w_set_keys(struct wpa_sm *sm,
+ struct wpa_eapol_ie_parse *ie)
+{
+#ifdef CONFIG_IEEE80211W
+ if (!wpa_cipher_valid_mgmt_group(sm->mgmt_group_cipher))
+ return 0;
+
+ if (ie->igtk) {
+ size_t len;
+ const struct wpa_igtk_kde *igtk;
+ u16 keyidx;
+ len = wpa_cipher_key_len(sm->mgmt_group_cipher);
+ if (ie->igtk_len != WPA_IGTK_KDE_PREFIX_LEN + len)
+ return -1;
+ igtk = (const struct wpa_igtk_kde *) ie->igtk;
+ keyidx = WPA_GET_LE16(igtk->keyid);
+ wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: IGTK keyid %d "
+ "pn %02x%02x%02x%02x%02x%02x",
+ keyidx, MAC2STR(igtk->pn));
+ wpa_hexdump_key(MSG_DEBUG, "WPA: IGTK",
+ igtk->igtk, len);
+ if (keyidx > 4095) {
+ wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+ "WPA: Invalid IGTK KeyID %d", keyidx);
+ return -1;
+ }
+ if (wpa_sm_set_key(sm, wpa_cipher_to_alg(sm->mgmt_group_cipher),
+ broadcast_ether_addr,
+ keyidx, 0, igtk->pn, sizeof(igtk->pn),
+ igtk->igtk, len) < 0) {
+ wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+ "WPA: Failed to configure IGTK to the driver");
+ return -1;
+ }
+ }
+
+ return 0;
+#else /* CONFIG_IEEE80211W */
+ return 0;
+#endif /* CONFIG_IEEE80211W */
+}
+
+
+static void wpa_report_ie_mismatch(struct wpa_sm *sm,
+ const char *reason, const u8 *src_addr,
+ const u8 *wpa_ie, size_t wpa_ie_len,
+ const u8 *rsn_ie, size_t rsn_ie_len)
+{
+ wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, "WPA: %s (src=" MACSTR ")",
+ reason, MAC2STR(src_addr));
+
+ if (sm->ap_wpa_ie) {
+ wpa_hexdump(MSG_INFO, "WPA: WPA IE in Beacon/ProbeResp",
+ sm->ap_wpa_ie, sm->ap_wpa_ie_len);
+ }
+ if (wpa_ie) {
+ if (!sm->ap_wpa_ie) {
+ wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
+ "WPA: No WPA IE in Beacon/ProbeResp");
+ }
+ wpa_hexdump(MSG_INFO, "WPA: WPA IE in 3/4 msg",
+ wpa_ie, wpa_ie_len);
+ }
+
+ if (sm->ap_rsn_ie) {
+ wpa_hexdump(MSG_INFO, "WPA: RSN IE in Beacon/ProbeResp",
+ sm->ap_rsn_ie, sm->ap_rsn_ie_len);
+ }
+ if (rsn_ie) {
+ if (!sm->ap_rsn_ie) {
+ wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
+ "WPA: No RSN IE in Beacon/ProbeResp");
+ }
+ wpa_hexdump(MSG_INFO, "WPA: RSN IE in 3/4 msg",
+ rsn_ie, rsn_ie_len);
+ }
+
+ wpa_sm_deauthenticate(sm, WLAN_REASON_IE_IN_4WAY_DIFFERS);
+}
+
+
+#ifdef CONFIG_IEEE80211R
+
+static int ft_validate_mdie(struct wpa_sm *sm,
+ const unsigned char *src_addr,
+ struct wpa_eapol_ie_parse *ie,
+ const u8 *assoc_resp_mdie)
+{
+ struct rsn_mdie *mdie;
+
+ mdie = (struct rsn_mdie *) (ie->mdie + 2);
+ if (ie->mdie == NULL || ie->mdie_len < 2 + sizeof(*mdie) ||
+ os_memcmp(mdie->mobility_domain, sm->mobility_domain,
+ MOBILITY_DOMAIN_ID_LEN) != 0) {
+ wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "FT: MDIE in msg 3/4 did "
+ "not match with the current mobility domain");
+ return -1;
+ }
+
+ if (assoc_resp_mdie &&
+ (assoc_resp_mdie[1] != ie->mdie[1] ||
+ os_memcmp(assoc_resp_mdie, ie->mdie, 2 + ie->mdie[1]) != 0)) {
+ wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "FT: MDIE mismatch");
+ wpa_hexdump(MSG_DEBUG, "FT: MDIE in EAPOL-Key msg 3/4",
+ ie->mdie, 2 + ie->mdie[1]);
+ wpa_hexdump(MSG_DEBUG, "FT: MDIE in (Re)Association Response",
+ assoc_resp_mdie, 2 + assoc_resp_mdie[1]);
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static int ft_validate_ftie(struct wpa_sm *sm,
+ const unsigned char *src_addr,
+ struct wpa_eapol_ie_parse *ie,
+ const u8 *assoc_resp_ftie)
+{
+ if (ie->ftie == NULL) {
+ wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
+ "FT: No FTIE in EAPOL-Key msg 3/4");
+ return -1;
+ }
+
+ if (assoc_resp_ftie == NULL)
+ return 0;
+
+ if (assoc_resp_ftie[1] != ie->ftie[1] ||
+ os_memcmp(assoc_resp_ftie, ie->ftie, 2 + ie->ftie[1]) != 0) {
+ wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "FT: FTIE mismatch");
+ wpa_hexdump(MSG_DEBUG, "FT: FTIE in EAPOL-Key msg 3/4",
+ ie->ftie, 2 + ie->ftie[1]);
+ wpa_hexdump(MSG_DEBUG, "FT: FTIE in (Re)Association Response",
+ assoc_resp_ftie, 2 + assoc_resp_ftie[1]);
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static int ft_validate_rsnie(struct wpa_sm *sm,
+ const unsigned char *src_addr,
+ struct wpa_eapol_ie_parse *ie)
+{
+ struct wpa_ie_data rsn;
+
+ if (!ie->rsn_ie)
+ return 0;
+
+ /*
+ * Verify that PMKR1Name from EAPOL-Key message 3/4
+ * matches with the value we derived.
+ */
+ if (wpa_parse_wpa_ie_rsn(ie->rsn_ie, ie->rsn_ie_len, &rsn) < 0 ||
+ rsn.num_pmkid != 1 || rsn.pmkid == NULL) {
+ wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "FT: No PMKR1Name in "
+ "FT 4-way handshake message 3/4");
+ return -1;
+ }
+
+ if (os_memcmp_const(rsn.pmkid, sm->pmk_r1_name, WPA_PMK_NAME_LEN) != 0)
+ {
+ wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
+ "FT: PMKR1Name mismatch in "
+ "FT 4-way handshake message 3/4");
+ wpa_hexdump(MSG_DEBUG, "FT: PMKR1Name from Authenticator",
+ rsn.pmkid, WPA_PMK_NAME_LEN);
+ wpa_hexdump(MSG_DEBUG, "FT: Derived PMKR1Name",
+ sm->pmk_r1_name, WPA_PMK_NAME_LEN);
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static int wpa_supplicant_validate_ie_ft(struct wpa_sm *sm,
+ const unsigned char *src_addr,
+ struct wpa_eapol_ie_parse *ie)
+{
+ const u8 *pos, *end, *mdie = NULL, *ftie = NULL;
+
+ if (sm->assoc_resp_ies) {
+ pos = sm->assoc_resp_ies;
+ end = pos + sm->assoc_resp_ies_len;
+ while (pos + 2 < end) {
+ if (pos + 2 + pos[1] > end)
+ break;
+ switch (*pos) {
+ case WLAN_EID_MOBILITY_DOMAIN:
+ mdie = pos;
+ break;
+ case WLAN_EID_FAST_BSS_TRANSITION:
+ ftie = pos;
+ break;
+ }
+ pos += 2 + pos[1];
+ }
+ }
+
+ if (ft_validate_mdie(sm, src_addr, ie, mdie) < 0 ||
+ ft_validate_ftie(sm, src_addr, ie, ftie) < 0 ||
+ ft_validate_rsnie(sm, src_addr, ie) < 0)
+ return -1;
+
+ return 0;
+}
+
+#endif /* CONFIG_IEEE80211R */
+
+
+static int wpa_supplicant_validate_ie(struct wpa_sm *sm,
+ const unsigned char *src_addr,
+ struct wpa_eapol_ie_parse *ie)
+{
+ if (sm->ap_wpa_ie == NULL && sm->ap_rsn_ie == NULL) {
+ wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
+ "WPA: No WPA/RSN IE for this AP known. "
+ "Trying to get from scan results");
+ if (wpa_sm_get_beacon_ie(sm) < 0) {
+ wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+ "WPA: Could not find AP from "
+ "the scan results");
+ } else {
+ wpa_msg(sm->ctx->msg_ctx, MSG_DEBUG,
+ "WPA: Found the current AP from "
+ "updated scan results");
+ }
+ }
+
+ if (ie->wpa_ie == NULL && ie->rsn_ie == NULL &&
+ (sm->ap_wpa_ie || sm->ap_rsn_ie)) {
+ wpa_report_ie_mismatch(sm, "IE in 3/4 msg does not match "
+ "with IE in Beacon/ProbeResp (no IE?)",
+ src_addr, ie->wpa_ie, ie->wpa_ie_len,
+ ie->rsn_ie, ie->rsn_ie_len);
+ return -1;
+ }
+
+ if ((ie->wpa_ie && sm->ap_wpa_ie &&
+ (ie->wpa_ie_len != sm->ap_wpa_ie_len ||
+ os_memcmp(ie->wpa_ie, sm->ap_wpa_ie, ie->wpa_ie_len) != 0)) ||
+ (ie->rsn_ie && sm->ap_rsn_ie &&
+ wpa_compare_rsn_ie(wpa_key_mgmt_ft(sm->key_mgmt),
+ sm->ap_rsn_ie, sm->ap_rsn_ie_len,
+ ie->rsn_ie, ie->rsn_ie_len))) {
+ wpa_report_ie_mismatch(sm, "IE in 3/4 msg does not match "
+ "with IE in Beacon/ProbeResp",
+ src_addr, ie->wpa_ie, ie->wpa_ie_len,
+ ie->rsn_ie, ie->rsn_ie_len);
+ return -1;
+ }
+
+ if (sm->proto == WPA_PROTO_WPA &&
+ ie->rsn_ie && sm->ap_rsn_ie == NULL && sm->rsn_enabled) {
+ wpa_report_ie_mismatch(sm, "Possible downgrade attack "
+ "detected - RSN was enabled and RSN IE "
+ "was in msg 3/4, but not in "
+ "Beacon/ProbeResp",
+ src_addr, ie->wpa_ie, ie->wpa_ie_len,
+ ie->rsn_ie, ie->rsn_ie_len);
+ return -1;
+ }
+
+#ifdef CONFIG_IEEE80211R
+ if (wpa_key_mgmt_ft(sm->key_mgmt) &&
+ wpa_supplicant_validate_ie_ft(sm, src_addr, ie) < 0)
+ return -1;
+#endif /* CONFIG_IEEE80211R */
+
+ return 0;
+}
+
+
+/**
+ * wpa_supplicant_send_4_of_4 - Send message 4 of WPA/RSN 4-Way Handshake
+ * @sm: Pointer to WPA state machine data from wpa_sm_init()
+ * @dst: Destination address for the frame
+ * @key: Pointer to the EAPOL-Key frame header
+ * @ver: Version bits from EAPOL-Key Key Info
+ * @key_info: Key Info
+ * @ptk: PTK to use for keyed hash and encryption
+ * Returns: 0 on success, -1 on failure
+ */
+int wpa_supplicant_send_4_of_4(struct wpa_sm *sm, const unsigned char *dst,
+ const struct wpa_eapol_key *key,
+ u16 ver, u16 key_info,
+ struct wpa_ptk *ptk)
+{
+ size_t mic_len, hdrlen, rlen;
+ struct wpa_eapol_key *reply;
+ struct wpa_eapol_key_192 *reply192;
+ u8 *rbuf, *key_mic;
+
+ mic_len = wpa_mic_len(sm->key_mgmt);
+ hdrlen = mic_len == 24 ? sizeof(*reply192) : sizeof(*reply);
+ rbuf = wpa_sm_alloc_eapol(sm, IEEE802_1X_TYPE_EAPOL_KEY, NULL,
+ hdrlen, &rlen, (void *) &reply);
+ if (rbuf == NULL)
+ return -1;
+ reply192 = (struct wpa_eapol_key_192 *) reply;
+
+ reply->type = (sm->proto == WPA_PROTO_RSN ||
+ sm->proto == WPA_PROTO_OSEN) ?
+ EAPOL_KEY_TYPE_RSN : EAPOL_KEY_TYPE_WPA;
+ key_info &= WPA_KEY_INFO_SECURE;
+ key_info |= ver | WPA_KEY_INFO_KEY_TYPE | WPA_KEY_INFO_MIC;
+ WPA_PUT_BE16(reply->key_info, key_info);
+ if (sm->proto == WPA_PROTO_RSN || sm->proto == WPA_PROTO_OSEN)
+ WPA_PUT_BE16(reply->key_length, 0);
+ else
+ os_memcpy(reply->key_length, key->key_length, 2);
+ os_memcpy(reply->replay_counter, key->replay_counter,
+ WPA_REPLAY_COUNTER_LEN);
+
+ key_mic = reply192->key_mic; /* same offset for reply and reply192 */
+ if (mic_len == 24)
+ WPA_PUT_BE16(reply192->key_data_length, 0);
+ else
+ WPA_PUT_BE16(reply->key_data_length, 0);
+
+ wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: Sending EAPOL-Key 4/4");
+ wpa_eapol_key_send(sm, ptk->kck, ptk->kck_len, ver, dst, ETH_P_EAPOL,
+ rbuf, rlen, key_mic);
+
+ return 0;
+}
+
+
+static void wpa_supplicant_process_3_of_4(struct wpa_sm *sm,
+ const struct wpa_eapol_key *key,
+ u16 ver, const u8 *key_data,
+ size_t key_data_len)
+{
+ u16 key_info, keylen;
+ struct wpa_eapol_ie_parse ie;
+
+ wpa_sm_set_state(sm, WPA_4WAY_HANDSHAKE);
+ wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: RX message 3 of 4-Way "
+ "Handshake from " MACSTR " (ver=%d)", MAC2STR(sm->bssid), ver);
+
+ key_info = WPA_GET_BE16(key->key_info);
+
+ wpa_hexdump(MSG_DEBUG, "WPA: IE KeyData", key_data, key_data_len);
+ if (wpa_supplicant_parse_ies(key_data, key_data_len, &ie) < 0)
+ goto failed;
+ if (ie.gtk && !(key_info & WPA_KEY_INFO_ENCR_KEY_DATA)) {
+ wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+ "WPA: GTK IE in unencrypted key data");
+ goto failed;
+ }
+#ifdef CONFIG_IEEE80211W
+ if (ie.igtk && !(key_info & WPA_KEY_INFO_ENCR_KEY_DATA)) {
+ wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+ "WPA: IGTK KDE in unencrypted key data");
+ goto failed;
+ }
+
+ if (ie.igtk &&
+ wpa_cipher_valid_mgmt_group(sm->mgmt_group_cipher) &&
+ ie.igtk_len != WPA_IGTK_KDE_PREFIX_LEN +
+ (unsigned int) wpa_cipher_key_len(sm->mgmt_group_cipher)) {
+ wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+ "WPA: Invalid IGTK KDE length %lu",
+ (unsigned long) ie.igtk_len);
+ goto failed;
+ }
+#endif /* CONFIG_IEEE80211W */
+
+ if (wpa_supplicant_validate_ie(sm, sm->bssid, &ie) < 0)
+ goto failed;
+
+ if (os_memcmp(sm->anonce, key->key_nonce, WPA_NONCE_LEN) != 0) {
+ wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+ "WPA: ANonce from message 1 of 4-Way Handshake "
+ "differs from 3 of 4-Way Handshake - drop packet (src="
+ MACSTR ")", MAC2STR(sm->bssid));
+ goto failed;
+ }
+
+ keylen = WPA_GET_BE16(key->key_length);
+ if (keylen != wpa_cipher_key_len(sm->pairwise_cipher)) {
+ wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+ "WPA: Invalid %s key length %d (src=" MACSTR
+ ")", wpa_cipher_txt(sm->pairwise_cipher), keylen,
+ MAC2STR(sm->bssid));
+ goto failed;
+ }
+
+#ifdef CONFIG_P2P
+ if (ie.ip_addr_alloc) {
+ os_memcpy(sm->p2p_ip_addr, ie.ip_addr_alloc, 3 * 4);
+ wpa_hexdump(MSG_DEBUG, "P2P: IP address info",
+ sm->p2p_ip_addr, sizeof(sm->p2p_ip_addr));
+ }
+#endif /* CONFIG_P2P */
+
+ if (wpa_supplicant_send_4_of_4(sm, sm->bssid, key, ver, key_info,
+ &sm->ptk)) {
+ goto failed;
+ }
+
+ /* SNonce was successfully used in msg 3/4, so mark it to be renewed
+ * for the next 4-Way Handshake. If msg 3 is received again, the old
+ * SNonce will still be used to avoid changing PTK. */
+ sm->renew_snonce = 1;
+
+ if (key_info & WPA_KEY_INFO_INSTALL) {
+ if (wpa_supplicant_install_ptk(sm, key))
+ goto failed;
+ }
+
+ if (key_info & WPA_KEY_INFO_SECURE) {
+ wpa_sm_mlme_setprotection(
+ sm, sm->bssid, MLME_SETPROTECTION_PROTECT_TYPE_RX,
+ MLME_SETPROTECTION_KEY_TYPE_PAIRWISE);
+ eapol_sm_notify_portValid(sm->eapol, TRUE);
+ }
+ wpa_sm_set_state(sm, WPA_GROUP_HANDSHAKE);
+
+ if (sm->group_cipher == WPA_CIPHER_GTK_NOT_USED) {
+ wpa_supplicant_key_neg_complete(sm, sm->bssid,
+ key_info & WPA_KEY_INFO_SECURE);
+ } else if (ie.gtk &&
+ wpa_supplicant_pairwise_gtk(sm, key,
+ ie.gtk, ie.gtk_len, key_info) < 0) {
+ wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
+ "RSN: Failed to configure GTK");
+ goto failed;
+ }
+
+ if (ieee80211w_set_keys(sm, &ie) < 0) {
+ wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
+ "RSN: Failed to configure IGTK");
+ goto failed;
+ }
+
+ if (ie.gtk)
+ wpa_sm_set_rekey_offload(sm);
+
+ if (sm->proto == WPA_PROTO_RSN && wpa_key_mgmt_suite_b(sm->key_mgmt)) {
+ struct rsn_pmksa_cache_entry *sa;
+
+ sa = pmksa_cache_add(sm->pmksa, sm->pmk, sm->pmk_len,
+ sm->ptk.kck, sm->ptk.kck_len,
+ sm->bssid, sm->own_addr,
+ sm->network_ctx, sm->key_mgmt);
+ if (!sm->cur_pmksa)
+ sm->cur_pmksa = sa;
+ }
+
+ sm->msg_3_of_4_ok = 1;
+ return;
+
+failed:
+ wpa_sm_deauthenticate(sm, WLAN_REASON_UNSPECIFIED);
+}
+
+
+static int wpa_supplicant_process_1_of_2_rsn(struct wpa_sm *sm,
+ const u8 *keydata,
+ size_t keydatalen,
+ u16 key_info,
+ struct wpa_gtk_data *gd)
+{
+ int maxkeylen;
+ struct wpa_eapol_ie_parse ie;
+
+ wpa_hexdump(MSG_DEBUG, "RSN: msg 1/2 key data", keydata, keydatalen);
+ if (wpa_supplicant_parse_ies(keydata, keydatalen, &ie) < 0)
+ return -1;
+ if (ie.gtk && !(key_info & WPA_KEY_INFO_ENCR_KEY_DATA)) {
+ wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+ "WPA: GTK IE in unencrypted key data");
+ return -1;
+ }
+ if (ie.gtk == NULL) {
+ wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
+ "WPA: No GTK IE in Group Key msg 1/2");
+ return -1;
+ }
+ maxkeylen = gd->gtk_len = ie.gtk_len - 2;
+
+ if (wpa_supplicant_check_group_cipher(sm, sm->group_cipher,
+ gd->gtk_len, maxkeylen,
+ &gd->key_rsc_len, &gd->alg))
+ return -1;
+
+ wpa_hexdump_key(MSG_DEBUG, "RSN: received GTK in group key handshake",
+ ie.gtk, ie.gtk_len);
+ gd->keyidx = ie.gtk[0] & 0x3;
+ gd->tx = wpa_supplicant_gtk_tx_bit_workaround(sm,
+ !!(ie.gtk[0] & BIT(2)));
+ if (ie.gtk_len - 2 > sizeof(gd->gtk)) {
+ wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
+ "RSN: Too long GTK in GTK IE (len=%lu)",
+ (unsigned long) ie.gtk_len - 2);
+ return -1;
+ }
+ os_memcpy(gd->gtk, ie.gtk + 2, ie.gtk_len - 2);
+
+ if (ieee80211w_set_keys(sm, &ie) < 0)
+ wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
+ "RSN: Failed to configure IGTK");
+
+ return 0;
+}
+
+
+static int wpa_supplicant_process_1_of_2_wpa(struct wpa_sm *sm,
+ const struct wpa_eapol_key *key,
+ const u8 *key_data,
+ size_t key_data_len, u16 key_info,
+ u16 ver, struct wpa_gtk_data *gd)
+{
+ size_t maxkeylen;
+ u16 gtk_len;
+
+ gtk_len = WPA_GET_BE16(key->key_length);
+ maxkeylen = key_data_len;
+ if (ver == WPA_KEY_INFO_TYPE_HMAC_SHA1_AES) {
+ if (maxkeylen < 8) {
+ wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
+ "WPA: Too short maxkeylen (%lu)",
+ (unsigned long) maxkeylen);
+ return -1;
+ }
+ maxkeylen -= 8;
+ }
+
+ if (gtk_len > maxkeylen ||
+ wpa_supplicant_check_group_cipher(sm, sm->group_cipher,
+ gtk_len, maxkeylen,
+ &gd->key_rsc_len, &gd->alg))
+ return -1;
+
+ gd->gtk_len = gtk_len;
+ gd->keyidx = (key_info & WPA_KEY_INFO_KEY_INDEX_MASK) >>
+ WPA_KEY_INFO_KEY_INDEX_SHIFT;
+ if (ver == WPA_KEY_INFO_TYPE_HMAC_MD5_RC4 && sm->ptk.kek_len == 16) {
+#ifdef CONFIG_NO_RC4
+ wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+ "WPA: RC4 not supported in the build");
+ return -1;
+#else /* CONFIG_NO_RC4 */
+ u8 ek[32];
+ if (key_data_len > sizeof(gd->gtk)) {
+ wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+ "WPA: RC4 key data too long (%lu)",
+ (unsigned long) key_data_len);
+ return -1;
+ }
+ os_memcpy(ek, key->key_iv, 16);
+ os_memcpy(ek + 16, sm->ptk.kek, sm->ptk.kek_len);
+ os_memcpy(gd->gtk, key_data, key_data_len);
+ if (rc4_skip(ek, 32, 256, gd->gtk, key_data_len)) {
+ os_memset(ek, 0, sizeof(ek));
+ wpa_msg(sm->ctx->msg_ctx, MSG_ERROR,
+ "WPA: RC4 failed");
+ return -1;
+ }
+ os_memset(ek, 0, sizeof(ek));
+#endif /* CONFIG_NO_RC4 */
+ } else if (ver == WPA_KEY_INFO_TYPE_HMAC_SHA1_AES) {
+ if (maxkeylen % 8) {
+ wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+ "WPA: Unsupported AES-WRAP len %lu",
+ (unsigned long) maxkeylen);
+ return -1;
+ }
+ if (maxkeylen > sizeof(gd->gtk)) {
+ wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+ "WPA: AES-WRAP key data "
+ "too long (keydatalen=%lu maxkeylen=%lu)",
+ (unsigned long) key_data_len,
+ (unsigned long) maxkeylen);
+ return -1;
+ }
+ if (aes_unwrap(sm->ptk.kek, sm->ptk.kek_len, maxkeylen / 8,
+ key_data, gd->gtk)) {
+ wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+ "WPA: AES unwrap failed - could not decrypt "
+ "GTK");
+ return -1;
+ }
+ } else {
+ wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+ "WPA: Unsupported key_info type %d", ver);
+ return -1;
+ }
+ gd->tx = wpa_supplicant_gtk_tx_bit_workaround(
+ sm, !!(key_info & WPA_KEY_INFO_TXRX));
+ return 0;
+}
+
+
+static int wpa_supplicant_send_2_of_2(struct wpa_sm *sm,
+ const struct wpa_eapol_key *key,
+ int ver, u16 key_info)
+{
+ size_t mic_len, hdrlen, rlen;
+ struct wpa_eapol_key *reply;
+ struct wpa_eapol_key_192 *reply192;
+ u8 *rbuf, *key_mic;
+
+ mic_len = wpa_mic_len(sm->key_mgmt);
+ hdrlen = mic_len == 24 ? sizeof(*reply192) : sizeof(*reply);
+ rbuf = wpa_sm_alloc_eapol(sm, IEEE802_1X_TYPE_EAPOL_KEY, NULL,
+ hdrlen, &rlen, (void *) &reply);
+ if (rbuf == NULL)
+ return -1;
+ reply192 = (struct wpa_eapol_key_192 *) reply;
+
+ reply->type = (sm->proto == WPA_PROTO_RSN ||
+ sm->proto == WPA_PROTO_OSEN) ?
+ EAPOL_KEY_TYPE_RSN : EAPOL_KEY_TYPE_WPA;
+ key_info &= WPA_KEY_INFO_KEY_INDEX_MASK;
+ key_info |= ver | WPA_KEY_INFO_MIC | WPA_KEY_INFO_SECURE;
+ WPA_PUT_BE16(reply->key_info, key_info);
+ if (sm->proto == WPA_PROTO_RSN || sm->proto == WPA_PROTO_OSEN)
+ WPA_PUT_BE16(reply->key_length, 0);
+ else
+ os_memcpy(reply->key_length, key->key_length, 2);
+ os_memcpy(reply->replay_counter, key->replay_counter,
+ WPA_REPLAY_COUNTER_LEN);
+
+ key_mic = reply192->key_mic; /* same offset for reply and reply192 */
+ if (mic_len == 24)
+ WPA_PUT_BE16(reply192->key_data_length, 0);
+ else
+ WPA_PUT_BE16(reply->key_data_length, 0);
+
+ wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: Sending EAPOL-Key 2/2");
+ wpa_eapol_key_send(sm, sm->ptk.kck, sm->ptk.kck_len, ver, sm->bssid,
+ ETH_P_EAPOL, rbuf, rlen, key_mic);
+
+ return 0;
+}
+
+
+static void wpa_supplicant_process_1_of_2(struct wpa_sm *sm,
+ const unsigned char *src_addr,
+ const struct wpa_eapol_key *key,
+ const u8 *key_data,
+ size_t key_data_len, u16 ver)
+{
+ u16 key_info;
+ int rekey, ret;
+ struct wpa_gtk_data gd;
+
+ if (!sm->msg_3_of_4_ok) {
+ wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
+ "WPA: Group Key Handshake started prior to completion of 4-way handshake");
+ goto failed;
+ }
+
+ os_memset(&gd, 0, sizeof(gd));
+
+ rekey = wpa_sm_get_state(sm) == WPA_COMPLETED;
+ wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: RX message 1 of Group Key "
+ "Handshake from " MACSTR " (ver=%d)", MAC2STR(src_addr), ver);
+
+ key_info = WPA_GET_BE16(key->key_info);
+
+ if (sm->proto == WPA_PROTO_RSN || sm->proto == WPA_PROTO_OSEN) {
+ ret = wpa_supplicant_process_1_of_2_rsn(sm, key_data,
+ key_data_len, key_info,
+ &gd);
+ } else {
+ ret = wpa_supplicant_process_1_of_2_wpa(sm, key, key_data,
+ key_data_len,
+ key_info, ver, &gd);
+ }
+
+ wpa_sm_set_state(sm, WPA_GROUP_HANDSHAKE);
+
+ if (ret)
+ goto failed;
+
+ if (wpa_supplicant_install_gtk(sm, &gd, key->key_rsc) ||
+ wpa_supplicant_send_2_of_2(sm, key, ver, key_info))
+ goto failed;
+ os_memset(&gd, 0, sizeof(gd));
+
+ if (rekey) {
+ wpa_msg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: Group rekeying "
+ "completed with " MACSTR " [GTK=%s]",
+ MAC2STR(sm->bssid), wpa_cipher_txt(sm->group_cipher));
+ wpa_sm_cancel_auth_timeout(sm);
+ wpa_sm_set_state(sm, WPA_COMPLETED);
+ } else {
+ wpa_supplicant_key_neg_complete(sm, sm->bssid,
+ key_info &
+ WPA_KEY_INFO_SECURE);
+ }
+
+ wpa_sm_set_rekey_offload(sm);
+
+ return;
+
+failed:
+ os_memset(&gd, 0, sizeof(gd));
+ wpa_sm_deauthenticate(sm, WLAN_REASON_UNSPECIFIED);
+}
+
+
+static int wpa_supplicant_verify_eapol_key_mic(struct wpa_sm *sm,
+ struct wpa_eapol_key_192 *key,
+ u16 ver,
+ const u8 *buf, size_t len)
+{
+ u8 mic[WPA_EAPOL_KEY_MIC_MAX_LEN];
+ int ok = 0;
+ size_t mic_len = wpa_mic_len(sm->key_mgmt);
+
+ os_memcpy(mic, key->key_mic, mic_len);
+ if (sm->tptk_set) {
+ os_memset(key->key_mic, 0, mic_len);
+ wpa_eapol_key_mic(sm->tptk.kck, sm->tptk.kck_len, sm->key_mgmt,
+ ver, buf, len, key->key_mic);
+ if (os_memcmp_const(mic, key->key_mic, mic_len) != 0) {
+ wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+ "WPA: Invalid EAPOL-Key MIC "
+ "when using TPTK - ignoring TPTK");
+ } else {
+ ok = 1;
+ sm->tptk_set = 0;
+ sm->ptk_set = 1;
+ os_memcpy(&sm->ptk, &sm->tptk, sizeof(sm->ptk));
+ os_memset(&sm->tptk, 0, sizeof(sm->tptk));
+ }
+ }
+
+ if (!ok && sm->ptk_set) {
+ os_memset(key->key_mic, 0, mic_len);
+ wpa_eapol_key_mic(sm->ptk.kck, sm->ptk.kck_len, sm->key_mgmt,
+ ver, buf, len, key->key_mic);
+ if (os_memcmp_const(mic, key->key_mic, mic_len) != 0) {
+ wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+ "WPA: Invalid EAPOL-Key MIC - "
+ "dropping packet");
+ return -1;
+ }
+ ok = 1;
+ }
+
+ if (!ok) {
+ wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+ "WPA: Could not verify EAPOL-Key MIC - "
+ "dropping packet");
+ return -1;
+ }
+
+ os_memcpy(sm->rx_replay_counter, key->replay_counter,
+ WPA_REPLAY_COUNTER_LEN);
+ sm->rx_replay_counter_set = 1;
+ return 0;
+}
+
+
+/* Decrypt RSN EAPOL-Key key data (RC4 or AES-WRAP) */
+static int wpa_supplicant_decrypt_key_data(struct wpa_sm *sm,
+ struct wpa_eapol_key *key, u16 ver,
+ u8 *key_data, size_t *key_data_len)
+{
+ wpa_hexdump(MSG_DEBUG, "RSN: encrypted key data",
+ key_data, *key_data_len);
+ if (!sm->ptk_set) {
+ wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+ "WPA: PTK not available, cannot decrypt EAPOL-Key Key "
+ "Data");
+ return -1;
+ }
+
+ /* Decrypt key data here so that this operation does not need
+ * to be implemented separately for each message type. */
+ if (ver == WPA_KEY_INFO_TYPE_HMAC_MD5_RC4 && sm->ptk.kek_len == 16) {
+#ifdef CONFIG_NO_RC4
+ wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+ "WPA: RC4 not supported in the build");
+ return -1;
+#else /* CONFIG_NO_RC4 */
+ u8 ek[32];
+ os_memcpy(ek, key->key_iv, 16);
+ os_memcpy(ek + 16, sm->ptk.kek, sm->ptk.kek_len);
+ if (rc4_skip(ek, 32, 256, key_data, *key_data_len)) {
+ os_memset(ek, 0, sizeof(ek));
+ wpa_msg(sm->ctx->msg_ctx, MSG_ERROR,
+ "WPA: RC4 failed");
+ return -1;
+ }
+ os_memset(ek, 0, sizeof(ek));
+#endif /* CONFIG_NO_RC4 */
+ } else if (ver == WPA_KEY_INFO_TYPE_HMAC_SHA1_AES ||
+ ver == WPA_KEY_INFO_TYPE_AES_128_CMAC ||
+ sm->key_mgmt == WPA_KEY_MGMT_OSEN ||
+ wpa_key_mgmt_suite_b(sm->key_mgmt)) {
+ u8 *buf;
+ if (*key_data_len < 8 || *key_data_len % 8) {
+ wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+ "WPA: Unsupported AES-WRAP len %u",
+ (unsigned int) *key_data_len);
+ return -1;
+ }
+ *key_data_len -= 8; /* AES-WRAP adds 8 bytes */
+ buf = os_malloc(*key_data_len);
+ if (buf == NULL) {
+ wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+ "WPA: No memory for AES-UNWRAP buffer");
+ return -1;
+ }
+ if (aes_unwrap(sm->ptk.kek, sm->ptk.kek_len, *key_data_len / 8,
+ key_data, buf)) {
+ os_free(buf);
+ wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+ "WPA: AES unwrap failed - "
+ "could not decrypt EAPOL-Key key data");
+ return -1;
+ }
+ os_memcpy(key_data, buf, *key_data_len);
+ os_free(buf);
+ WPA_PUT_BE16(key->key_data_length, *key_data_len);
+ } else {
+ wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+ "WPA: Unsupported key_info type %d", ver);
+ return -1;
+ }
+ wpa_hexdump_key(MSG_DEBUG, "WPA: decrypted EAPOL-Key key data",
+ key_data, *key_data_len);
+ return 0;
+}
+
+
+/**
+ * wpa_sm_aborted_cached - Notify WPA that PMKSA caching was aborted
+ * @sm: Pointer to WPA state machine data from wpa_sm_init()
+ */
+void wpa_sm_aborted_cached(struct wpa_sm *sm)
+{
+ if (sm && sm->cur_pmksa) {
+ wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
+ "RSN: Cancelling PMKSA caching attempt");
+ sm->cur_pmksa = NULL;
+ }
+}
+
+
+static void wpa_eapol_key_dump(struct wpa_sm *sm,
+ const struct wpa_eapol_key *key,
+ unsigned int key_data_len,
+ const u8 *mic, unsigned int mic_len)
+{
+#ifndef CONFIG_NO_STDOUT_DEBUG
+ u16 key_info = WPA_GET_BE16(key->key_info);
+
+ wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, " EAPOL-Key type=%d", key->type);
+ wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
+ " key_info 0x%x (ver=%d keyidx=%d rsvd=%d %s%s%s%s%s%s%s%s)",
+ key_info, key_info & WPA_KEY_INFO_TYPE_MASK,
+ (key_info & WPA_KEY_INFO_KEY_INDEX_MASK) >>
+ WPA_KEY_INFO_KEY_INDEX_SHIFT,
+ (key_info & (BIT(13) | BIT(14) | BIT(15))) >> 13,
+ key_info & WPA_KEY_INFO_KEY_TYPE ? "Pairwise" : "Group",
+ key_info & WPA_KEY_INFO_INSTALL ? " Install" : "",
+ key_info & WPA_KEY_INFO_ACK ? " Ack" : "",
+ key_info & WPA_KEY_INFO_MIC ? " MIC" : "",
+ key_info & WPA_KEY_INFO_SECURE ? " Secure" : "",
+ key_info & WPA_KEY_INFO_ERROR ? " Error" : "",
+ key_info & WPA_KEY_INFO_REQUEST ? " Request" : "",
+ key_info & WPA_KEY_INFO_ENCR_KEY_DATA ? " Encr" : "");
+ wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
+ " key_length=%u key_data_length=%u",
+ WPA_GET_BE16(key->key_length), key_data_len);
+ wpa_hexdump(MSG_DEBUG, " replay_counter",
+ key->replay_counter, WPA_REPLAY_COUNTER_LEN);
+ wpa_hexdump(MSG_DEBUG, " key_nonce", key->key_nonce, WPA_NONCE_LEN);
+ wpa_hexdump(MSG_DEBUG, " key_iv", key->key_iv, 16);
+ wpa_hexdump(MSG_DEBUG, " key_rsc", key->key_rsc, 8);
+ wpa_hexdump(MSG_DEBUG, " key_id (reserved)", key->key_id, 8);
+ wpa_hexdump(MSG_DEBUG, " key_mic", mic, mic_len);
+#endif /* CONFIG_NO_STDOUT_DEBUG */
+}
+
+
+/**
+ * wpa_sm_rx_eapol - Process received WPA EAPOL frames
+ * @sm: Pointer to WPA state machine data from wpa_sm_init()
+ * @src_addr: Source MAC address of the EAPOL packet
+ * @buf: Pointer to the beginning of the EAPOL data (EAPOL header)
+ * @len: Length of the EAPOL frame
+ * Returns: 1 = WPA EAPOL-Key processed, 0 = not a WPA EAPOL-Key, -1 failure
+ *
+ * This function is called for each received EAPOL frame. Other than EAPOL-Key
+ * frames can be skipped if filtering is done elsewhere. wpa_sm_rx_eapol() is
+ * only processing WPA and WPA2 EAPOL-Key frames.
+ *
+ * The received EAPOL-Key packets are validated and valid packets are replied
+ * to. In addition, key material (PTK, GTK) is configured at the end of a
+ * successful key handshake.
+ */
+int wpa_sm_rx_eapol(struct wpa_sm *sm, const u8 *src_addr,
+ const u8 *buf, size_t len)
+{
+ size_t plen, data_len, key_data_len;
+ const struct ieee802_1x_hdr *hdr;
+ struct wpa_eapol_key *key;
+ struct wpa_eapol_key_192 *key192;
+ u16 key_info, ver;
+ u8 *tmp = NULL;
+ int ret = -1;
+ struct wpa_peerkey *peerkey = NULL;
+ u8 *key_data;
+ size_t mic_len, keyhdrlen;
+
+#ifdef CONFIG_IEEE80211R
+ sm->ft_completed = 0;
+#endif /* CONFIG_IEEE80211R */
+
+ mic_len = wpa_mic_len(sm->key_mgmt);
+ keyhdrlen = mic_len == 24 ? sizeof(*key192) : sizeof(*key);
+
+ if (len < sizeof(*hdr) + keyhdrlen) {
+ wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
+ "WPA: EAPOL frame too short to be a WPA "
+ "EAPOL-Key (len %lu, expecting at least %lu)",
+ (unsigned long) len,
+ (unsigned long) sizeof(*hdr) + keyhdrlen);
+ return 0;
+ }
+
+ hdr = (const struct ieee802_1x_hdr *) buf;
+ plen = be_to_host16(hdr->length);
+ data_len = plen + sizeof(*hdr);
+ wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
+ "IEEE 802.1X RX: version=%d type=%d length=%lu",
+ hdr->version, hdr->type, (unsigned long) plen);
+
+ if (hdr->version < EAPOL_VERSION) {
+ /* TODO: backwards compatibility */
+ }
+ if (hdr->type != IEEE802_1X_TYPE_EAPOL_KEY) {
+ wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
+ "WPA: EAPOL frame (type %u) discarded, "
+ "not a Key frame", hdr->type);
+ ret = 0;
+ goto out;
+ }
+ wpa_hexdump(MSG_MSGDUMP, "WPA: RX EAPOL-Key", buf, len);
+ if (plen > len - sizeof(*hdr) || plen < keyhdrlen) {
+ wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
+ "WPA: EAPOL frame payload size %lu "
+ "invalid (frame size %lu)",
+ (unsigned long) plen, (unsigned long) len);
+ ret = 0;
+ goto out;
+ }
+ if (data_len < len) {
+ wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
+ "WPA: ignoring %lu bytes after the IEEE 802.1X data",
+ (unsigned long) len - data_len);
+ }
+
+ /*
+ * Make a copy of the frame since we need to modify the buffer during
+ * MAC validation and Key Data decryption.
+ */
+ tmp = os_malloc(data_len);
+ if (tmp == NULL)
+ goto out;
+ os_memcpy(tmp, buf, data_len);
+ key = (struct wpa_eapol_key *) (tmp + sizeof(struct ieee802_1x_hdr));
+ key192 = (struct wpa_eapol_key_192 *)
+ (tmp + sizeof(struct ieee802_1x_hdr));
+ if (mic_len == 24)
+ key_data = (u8 *) (key192 + 1);
+ else
+ key_data = (u8 *) (key + 1);
+
+ if (key->type != EAPOL_KEY_TYPE_WPA && key->type != EAPOL_KEY_TYPE_RSN)
+ {
+ wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
+ "WPA: EAPOL-Key type (%d) unknown, discarded",
+ key->type);
+ ret = 0;
+ goto out;
+ }
+
+ if (mic_len == 24)
+ key_data_len = WPA_GET_BE16(key192->key_data_length);
+ else
+ key_data_len = WPA_GET_BE16(key->key_data_length);
+ wpa_eapol_key_dump(sm, key, key_data_len, key192->key_mic, mic_len);
+
+ if (key_data_len > plen - keyhdrlen) {
+ wpa_msg(sm->ctx->msg_ctx, MSG_INFO, "WPA: Invalid EAPOL-Key "
+ "frame - key_data overflow (%u > %u)",
+ (unsigned int) key_data_len,
+ (unsigned int) (plen - keyhdrlen));
+ goto out;
+ }
+
+ eapol_sm_notify_lower_layer_success(sm->eapol, 0);
+ key_info = WPA_GET_BE16(key->key_info);
+ ver = key_info & WPA_KEY_INFO_TYPE_MASK;
+ if (ver != WPA_KEY_INFO_TYPE_HMAC_MD5_RC4 &&
+#if defined(CONFIG_IEEE80211R) || defined(CONFIG_IEEE80211W)
+ ver != WPA_KEY_INFO_TYPE_AES_128_CMAC &&
+#endif /* CONFIG_IEEE80211R || CONFIG_IEEE80211W */
+ ver != WPA_KEY_INFO_TYPE_HMAC_SHA1_AES &&
+ !wpa_key_mgmt_suite_b(sm->key_mgmt) &&
+ sm->key_mgmt != WPA_KEY_MGMT_OSEN) {
+ wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
+ "WPA: Unsupported EAPOL-Key descriptor version %d",
+ ver);
+ goto out;
+ }
+
+ if (sm->key_mgmt == WPA_KEY_MGMT_OSEN &&
+ ver != WPA_KEY_INFO_TYPE_AKM_DEFINED) {
+ wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
+ "OSEN: Unsupported EAPOL-Key descriptor version %d",
+ ver);
+ goto out;
+ }
+
+ if (wpa_key_mgmt_suite_b(sm->key_mgmt) &&
+ ver != WPA_KEY_INFO_TYPE_AKM_DEFINED) {
+ wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
+ "RSN: Unsupported EAPOL-Key descriptor version %d (expected AKM defined = 0)",
+ ver);
+ goto out;
+ }
+
+#ifdef CONFIG_IEEE80211R
+ if (wpa_key_mgmt_ft(sm->key_mgmt)) {
+ /* IEEE 802.11r uses a new key_info type (AES-128-CMAC). */
+ if (ver != WPA_KEY_INFO_TYPE_AES_128_CMAC) {
+ wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
+ "FT: AP did not use AES-128-CMAC");
+ goto out;
+ }
+ } else
+#endif /* CONFIG_IEEE80211R */
+#ifdef CONFIG_IEEE80211W
+ if (wpa_key_mgmt_sha256(sm->key_mgmt)) {
+ if (ver != WPA_KEY_INFO_TYPE_AES_128_CMAC &&
+ sm->key_mgmt != WPA_KEY_MGMT_OSEN &&
+ !wpa_key_mgmt_suite_b(sm->key_mgmt)) {
+ wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
+ "WPA: AP did not use the "
+ "negotiated AES-128-CMAC");
+ goto out;
+ }
+ } else
+#endif /* CONFIG_IEEE80211W */
+ if (sm->pairwise_cipher == WPA_CIPHER_CCMP &&
+ !wpa_key_mgmt_suite_b(sm->key_mgmt) &&
+ ver != WPA_KEY_INFO_TYPE_HMAC_SHA1_AES) {
+ wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
+ "WPA: CCMP is used, but EAPOL-Key "
+ "descriptor version (%d) is not 2", ver);
+ if (sm->group_cipher != WPA_CIPHER_CCMP &&
+ !(key_info & WPA_KEY_INFO_KEY_TYPE)) {
+ /* Earlier versions of IEEE 802.11i did not explicitly
+ * require version 2 descriptor for all EAPOL-Key
+ * packets, so allow group keys to use version 1 if
+ * CCMP is not used for them. */
+ wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
+ "WPA: Backwards compatibility: allow invalid "
+ "version for non-CCMP group keys");
+ } else if (ver == WPA_KEY_INFO_TYPE_AES_128_CMAC) {
+ wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
+ "WPA: Interoperability workaround: allow incorrect (should have been HMAC-SHA1), but stronger (is AES-128-CMAC), descriptor version to be used");
+ } else
+ goto out;
+ } else if (sm->pairwise_cipher == WPA_CIPHER_GCMP &&
+ !wpa_key_mgmt_suite_b(sm->key_mgmt) &&
+ ver != WPA_KEY_INFO_TYPE_HMAC_SHA1_AES) {
+ wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
+ "WPA: GCMP is used, but EAPOL-Key "
+ "descriptor version (%d) is not 2", ver);
+ goto out;
+ }
+
+#ifdef CONFIG_PEERKEY
+ for (peerkey = sm->peerkey; peerkey; peerkey = peerkey->next) {
+ if (os_memcmp(peerkey->addr, src_addr, ETH_ALEN) == 0)
+ break;
+ }
+
+ if (!(key_info & WPA_KEY_INFO_SMK_MESSAGE) && peerkey) {
+ if (!peerkey->initiator && peerkey->replay_counter_set &&
+ os_memcmp(key->replay_counter, peerkey->replay_counter,
+ WPA_REPLAY_COUNTER_LEN) <= 0) {
+ wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+ "RSN: EAPOL-Key Replay Counter did not "
+ "increase (STK) - dropping packet");
+ goto out;
+ } else if (peerkey->initiator) {
+ u8 _tmp[WPA_REPLAY_COUNTER_LEN];
+ os_memcpy(_tmp, key->replay_counter,
+ WPA_REPLAY_COUNTER_LEN);
+ inc_byte_array(_tmp, WPA_REPLAY_COUNTER_LEN);
+ if (os_memcmp(_tmp, peerkey->replay_counter,
+ WPA_REPLAY_COUNTER_LEN) != 0) {
+ wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
+ "RSN: EAPOL-Key Replay "
+ "Counter did not match (STK) - "
+ "dropping packet");
+ goto out;
+ }
+ }
+ }
+
+ if (peerkey && peerkey->initiator && (key_info & WPA_KEY_INFO_ACK)) {
+ wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
+ "RSN: Ack bit in key_info from STK peer");
+ goto out;
+ }
+#endif /* CONFIG_PEERKEY */
+
+ if (!peerkey && sm->rx_replay_counter_set &&
+ os_memcmp(key->replay_counter, sm->rx_replay_counter,
+ WPA_REPLAY_COUNTER_LEN) <= 0) {
+ wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+ "WPA: EAPOL-Key Replay Counter did not increase - "
+ "dropping packet");
+ goto out;
+ }
+
+ if (!(key_info & (WPA_KEY_INFO_ACK | WPA_KEY_INFO_SMK_MESSAGE))
+#ifdef CONFIG_PEERKEY
+ && (peerkey == NULL || !peerkey->initiator)
+#endif /* CONFIG_PEERKEY */
+ ) {
+ wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
+ "WPA: No Ack bit in key_info");
+ goto out;
+ }
+
+ if (key_info & WPA_KEY_INFO_REQUEST) {
+ wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
+ "WPA: EAPOL-Key with Request bit - dropped");
+ goto out;
+ }
+
+ if ((key_info & WPA_KEY_INFO_MIC) && !peerkey &&
+ wpa_supplicant_verify_eapol_key_mic(sm, key192, ver, tmp, data_len))
+ goto out;
+
+#ifdef CONFIG_PEERKEY
+ if ((key_info & WPA_KEY_INFO_MIC) && peerkey &&
+ peerkey_verify_eapol_key_mic(sm, peerkey, key192, ver, tmp,
+ data_len))
+ goto out;
+#endif /* CONFIG_PEERKEY */
+
+ if ((sm->proto == WPA_PROTO_RSN || sm->proto == WPA_PROTO_OSEN) &&
+ (key_info & WPA_KEY_INFO_ENCR_KEY_DATA)) {
+ if (wpa_supplicant_decrypt_key_data(sm, key, ver, key_data,
+ &key_data_len))
+ goto out;
+ }
+
+ if (key_info & WPA_KEY_INFO_KEY_TYPE) {
+ if (key_info & WPA_KEY_INFO_KEY_INDEX_MASK) {
+ wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+ "WPA: Ignored EAPOL-Key (Pairwise) with "
+ "non-zero key index");
+ goto out;
+ }
+ if (peerkey) {
+ /* PeerKey 4-Way Handshake */
+ peerkey_rx_eapol_4way(sm, peerkey, key, key_info, ver,
+ key_data, key_data_len);
+ } else if (key_info & WPA_KEY_INFO_MIC) {
+ /* 3/4 4-Way Handshake */
+ wpa_supplicant_process_3_of_4(sm, key, ver, key_data,
+ key_data_len);
+ } else {
+ /* 1/4 4-Way Handshake */
+ wpa_supplicant_process_1_of_4(sm, src_addr, key,
+ ver, key_data,
+ key_data_len);
+ }
+ } else if (key_info & WPA_KEY_INFO_SMK_MESSAGE) {
+ /* PeerKey SMK Handshake */
+ peerkey_rx_eapol_smk(sm, src_addr, key, key_data_len, key_info,
+ ver);
+ } else {
+ if (key_info & WPA_KEY_INFO_MIC) {
+ /* 1/2 Group Key Handshake */
+ wpa_supplicant_process_1_of_2(sm, src_addr, key,
+ key_data, key_data_len,
+ ver);
+ } else {
+ wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+ "WPA: EAPOL-Key (Group) without Mic bit - "
+ "dropped");
+ }
+ }
+
+ ret = 1;
+
+out:
+ bin_clear_free(tmp, data_len);
+ return ret;
+}
+
+
+#ifdef CONFIG_CTRL_IFACE
+static u32 wpa_key_mgmt_suite(struct wpa_sm *sm)
+{
+ switch (sm->key_mgmt) {
+ case WPA_KEY_MGMT_IEEE8021X:
+ return ((sm->proto == WPA_PROTO_RSN ||
+ sm->proto == WPA_PROTO_OSEN) ?
+ RSN_AUTH_KEY_MGMT_UNSPEC_802_1X :
+ WPA_AUTH_KEY_MGMT_UNSPEC_802_1X);
+ case WPA_KEY_MGMT_PSK:
+ return (sm->proto == WPA_PROTO_RSN ?
+ RSN_AUTH_KEY_MGMT_PSK_OVER_802_1X :
+ WPA_AUTH_KEY_MGMT_PSK_OVER_802_1X);
+#ifdef CONFIG_IEEE80211R
+ case WPA_KEY_MGMT_FT_IEEE8021X:
+ return RSN_AUTH_KEY_MGMT_FT_802_1X;
+ case WPA_KEY_MGMT_FT_PSK:
+ return RSN_AUTH_KEY_MGMT_FT_PSK;
+#endif /* CONFIG_IEEE80211R */
+#ifdef CONFIG_IEEE80211W
+ case WPA_KEY_MGMT_IEEE8021X_SHA256:
+ return RSN_AUTH_KEY_MGMT_802_1X_SHA256;
+ case WPA_KEY_MGMT_PSK_SHA256:
+ return RSN_AUTH_KEY_MGMT_PSK_SHA256;
+#endif /* CONFIG_IEEE80211W */
+ case WPA_KEY_MGMT_CCKM:
+ return (sm->proto == WPA_PROTO_RSN ?
+ RSN_AUTH_KEY_MGMT_CCKM:
+ WPA_AUTH_KEY_MGMT_CCKM);
+ case WPA_KEY_MGMT_WPA_NONE:
+ return WPA_AUTH_KEY_MGMT_NONE;
+ case WPA_KEY_MGMT_IEEE8021X_SUITE_B:
+ return RSN_AUTH_KEY_MGMT_802_1X_SUITE_B;
+ case WPA_KEY_MGMT_IEEE8021X_SUITE_B_192:
+ return RSN_AUTH_KEY_MGMT_802_1X_SUITE_B_192;
+ default:
+ return 0;
+ }
+}
+
+
+#define RSN_SUITE "%02x-%02x-%02x-%d"
+#define RSN_SUITE_ARG(s) \
+((s) >> 24) & 0xff, ((s) >> 16) & 0xff, ((s) >> 8) & 0xff, (s) & 0xff
+
+/**
+ * wpa_sm_get_mib - Dump text list of MIB entries
+ * @sm: Pointer to WPA state machine data from wpa_sm_init()
+ * @buf: Buffer for the list
+ * @buflen: Length of the buffer
+ * Returns: Number of bytes written to buffer
+ *
+ * This function is used fetch dot11 MIB variables.
+ */
+int wpa_sm_get_mib(struct wpa_sm *sm, char *buf, size_t buflen)
+{
+ char pmkid_txt[PMKID_LEN * 2 + 1];
+ int rsna, ret;
+ size_t len;
+
+ if (sm->cur_pmksa) {
+ wpa_snprintf_hex(pmkid_txt, sizeof(pmkid_txt),
+ sm->cur_pmksa->pmkid, PMKID_LEN);
+ } else
+ pmkid_txt[0] = '\0';
+
+ if ((wpa_key_mgmt_wpa_psk(sm->key_mgmt) ||
+ wpa_key_mgmt_wpa_ieee8021x(sm->key_mgmt)) &&
+ sm->proto == WPA_PROTO_RSN)
+ rsna = 1;
+ else
+ rsna = 0;
+
+ ret = os_snprintf(buf, buflen,
+ "dot11RSNAOptionImplemented=TRUE\n"
+ "dot11RSNAPreauthenticationImplemented=TRUE\n"
+ "dot11RSNAEnabled=%s\n"
+ "dot11RSNAPreauthenticationEnabled=%s\n"
+ "dot11RSNAConfigVersion=%d\n"
+ "dot11RSNAConfigPairwiseKeysSupported=5\n"
+ "dot11RSNAConfigGroupCipherSize=%d\n"
+ "dot11RSNAConfigPMKLifetime=%d\n"
+ "dot11RSNAConfigPMKReauthThreshold=%d\n"
+ "dot11RSNAConfigNumberOfPTKSAReplayCounters=1\n"
+ "dot11RSNAConfigSATimeout=%d\n",
+ rsna ? "TRUE" : "FALSE",
+ rsna ? "TRUE" : "FALSE",
+ RSN_VERSION,
+ wpa_cipher_key_len(sm->group_cipher) * 8,
+ sm->dot11RSNAConfigPMKLifetime,
+ sm->dot11RSNAConfigPMKReauthThreshold,
+ sm->dot11RSNAConfigSATimeout);
+ if (os_snprintf_error(buflen, ret))
+ return 0;
+ len = ret;
+
+ ret = os_snprintf(
+ buf + len, buflen - len,
+ "dot11RSNAAuthenticationSuiteSelected=" RSN_SUITE "\n"
+ "dot11RSNAPairwiseCipherSelected=" RSN_SUITE "\n"
+ "dot11RSNAGroupCipherSelected=" RSN_SUITE "\n"
+ "dot11RSNAPMKIDUsed=%s\n"
+ "dot11RSNAAuthenticationSuiteRequested=" RSN_SUITE "\n"
+ "dot11RSNAPairwiseCipherRequested=" RSN_SUITE "\n"
+ "dot11RSNAGroupCipherRequested=" RSN_SUITE "\n"
+ "dot11RSNAConfigNumberOfGTKSAReplayCounters=0\n"
+ "dot11RSNA4WayHandshakeFailures=%u\n",
+ RSN_SUITE_ARG(wpa_key_mgmt_suite(sm)),
+ RSN_SUITE_ARG(wpa_cipher_to_suite(sm->proto,
+ sm->pairwise_cipher)),
+ RSN_SUITE_ARG(wpa_cipher_to_suite(sm->proto,
+ sm->group_cipher)),
+ pmkid_txt,
+ RSN_SUITE_ARG(wpa_key_mgmt_suite(sm)),
+ RSN_SUITE_ARG(wpa_cipher_to_suite(sm->proto,
+ sm->pairwise_cipher)),
+ RSN_SUITE_ARG(wpa_cipher_to_suite(sm->proto,
+ sm->group_cipher)),
+ sm->dot11RSNA4WayHandshakeFailures);
+ if (!os_snprintf_error(buflen - len, ret))
+ len += ret;
+
+ return (int) len;
+}
+#endif /* CONFIG_CTRL_IFACE */
+
+
+static void wpa_sm_pmksa_free_cb(struct rsn_pmksa_cache_entry *entry,
+ void *ctx, enum pmksa_free_reason reason)
+{
+ struct wpa_sm *sm = ctx;
+ int deauth = 0;
+
+ wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "RSN: PMKSA cache entry free_cb: "
+ MACSTR " reason=%d", MAC2STR(entry->aa), reason);
+
+ if (sm->cur_pmksa == entry) {
+ wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
+ "RSN: %s current PMKSA entry",
+ reason == PMKSA_REPLACE ? "replaced" : "removed");
+ pmksa_cache_clear_current(sm);
+
+ /*
+ * If an entry is simply being replaced, there's no need to
+ * deauthenticate because it will be immediately re-added.
+ * This happens when EAP authentication is completed again
+ * (reauth or failed PMKSA caching attempt).
+ */
+ if (reason != PMKSA_REPLACE)
+ deauth = 1;
+ }
+
+ if (reason == PMKSA_EXPIRE &&
+ (sm->pmk_len == entry->pmk_len &&
+ os_memcmp(sm->pmk, entry->pmk, sm->pmk_len) == 0)) {
+ wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
+ "RSN: deauthenticating due to expired PMK");
+ pmksa_cache_clear_current(sm);
+ deauth = 1;
+ }
+
+ if (deauth) {
+ os_memset(sm->pmk, 0, sizeof(sm->pmk));
+ wpa_sm_deauthenticate(sm, WLAN_REASON_UNSPECIFIED);
+ }
+}
+
+
+/**
+ * wpa_sm_init - Initialize WPA state machine
+ * @ctx: Context pointer for callbacks; this needs to be an allocated buffer
+ * Returns: Pointer to the allocated WPA state machine data
+ *
+ * This function is used to allocate a new WPA state machine and the returned
+ * value is passed to all WPA state machine calls.
+ */
+struct wpa_sm * wpa_sm_init(struct wpa_sm_ctx *ctx)
+{
+ struct wpa_sm *sm;
+
+ sm = os_zalloc(sizeof(*sm));
+ if (sm == NULL)
+ return NULL;
+ dl_list_init(&sm->pmksa_candidates);
+ sm->renew_snonce = 1;
+ sm->ctx = ctx;
+
+ sm->dot11RSNAConfigPMKLifetime = 43200;
+ sm->dot11RSNAConfigPMKReauthThreshold = 70;
+ sm->dot11RSNAConfigSATimeout = 60;
+
+ sm->pmksa = pmksa_cache_init(wpa_sm_pmksa_free_cb, sm, sm);
+ if (sm->pmksa == NULL) {
+ wpa_msg(sm->ctx->msg_ctx, MSG_ERROR,
+ "RSN: PMKSA cache initialization failed");
+ os_free(sm);
+ return NULL;
+ }
+
+ return sm;
+}
+
+
+/**
+ * wpa_sm_deinit - Deinitialize WPA state machine
+ * @sm: Pointer to WPA state machine data from wpa_sm_init()
+ */
+void wpa_sm_deinit(struct wpa_sm *sm)
+{
+ if (sm == NULL)
+ return;
+ pmksa_cache_deinit(sm->pmksa);
+ eloop_cancel_timeout(wpa_sm_start_preauth, sm, NULL);
+ eloop_cancel_timeout(wpa_sm_rekey_ptk, sm, NULL);
+ os_free(sm->assoc_wpa_ie);
+ os_free(sm->ap_wpa_ie);
+ os_free(sm->ap_rsn_ie);
+ wpa_sm_drop_sa(sm);
+ os_free(sm->ctx);
+ peerkey_deinit(sm);
+#ifdef CONFIG_IEEE80211R
+ os_free(sm->assoc_resp_ies);
+#endif /* CONFIG_IEEE80211R */
+ os_free(sm);
+}
+
+
+/**
+ * wpa_sm_notify_assoc - Notify WPA state machine about association
+ * @sm: Pointer to WPA state machine data from wpa_sm_init()
+ * @bssid: The BSSID of the new association
+ *
+ * This function is called to let WPA state machine know that the connection
+ * was established.
+ */
+void wpa_sm_notify_assoc(struct wpa_sm *sm, const u8 *bssid)
+{
+ int clear_ptk = 1;
+
+ if (sm == NULL)
+ return;
+
+ wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
+ "WPA: Association event - clear replay counter");
+ os_memcpy(sm->bssid, bssid, ETH_ALEN);
+ os_memset(sm->rx_replay_counter, 0, WPA_REPLAY_COUNTER_LEN);
+ sm->rx_replay_counter_set = 0;
+ sm->renew_snonce = 1;
+ if (os_memcmp(sm->preauth_bssid, bssid, ETH_ALEN) == 0)
+ rsn_preauth_deinit(sm);
+
+#ifdef CONFIG_IEEE80211R
+ if (wpa_ft_is_completed(sm)) {
+ /*
+ * Clear portValid to kick EAPOL state machine to re-enter
+ * AUTHENTICATED state to get the EAPOL port Authorized.
+ */
+ eapol_sm_notify_portValid(sm->eapol, FALSE);
+ wpa_supplicant_key_neg_complete(sm, sm->bssid, 1);
+
+ /* Prepare for the next transition */
+ wpa_ft_prepare_auth_request(sm, NULL);
+
+ clear_ptk = 0;
+ }
+#endif /* CONFIG_IEEE80211R */
+
+ if (clear_ptk) {
+ /*
+ * IEEE 802.11, 8.4.10: Delete PTK SA on (re)association if
+ * this is not part of a Fast BSS Transition.
+ */
+ wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: Clear old PTK");
+ sm->ptk_set = 0;
+ os_memset(&sm->ptk, 0, sizeof(sm->ptk));
+ sm->tptk_set = 0;
+ os_memset(&sm->tptk, 0, sizeof(sm->tptk));
+ }
+
+#ifdef CONFIG_TDLS
+ wpa_tdls_assoc(sm);
+#endif /* CONFIG_TDLS */
+
+#ifdef CONFIG_P2P
+ os_memset(sm->p2p_ip_addr, 0, sizeof(sm->p2p_ip_addr));
+#endif /* CONFIG_P2P */
+}
+
+
+/**
+ * wpa_sm_notify_disassoc - Notify WPA state machine about disassociation
+ * @sm: Pointer to WPA state machine data from wpa_sm_init()
+ *
+ * This function is called to let WPA state machine know that the connection
+ * was lost. This will abort any existing pre-authentication session.
+ */
+void wpa_sm_notify_disassoc(struct wpa_sm *sm)
+{
+ eloop_cancel_timeout(wpa_sm_start_preauth, sm, NULL);
+ eloop_cancel_timeout(wpa_sm_rekey_ptk, sm, NULL);
+ peerkey_deinit(sm);
+ rsn_preauth_deinit(sm);
+ pmksa_cache_clear_current(sm);
+ if (wpa_sm_get_state(sm) == WPA_4WAY_HANDSHAKE)
+ sm->dot11RSNA4WayHandshakeFailures++;
+#ifdef CONFIG_TDLS
+ wpa_tdls_disassoc(sm);
+#endif /* CONFIG_TDLS */
+
+ /* Keys are not needed in the WPA state machine anymore */
+ wpa_sm_drop_sa(sm);
+
+ sm->msg_3_of_4_ok = 0;
+}
+
+
+/**
+ * wpa_sm_set_pmk - Set PMK
+ * @sm: Pointer to WPA state machine data from wpa_sm_init()
+ * @pmk: The new PMK
+ * @pmk_len: The length of the new PMK in bytes
+ * @bssid: AA to add into PMKSA cache or %NULL to not cache the PMK
+ *
+ * Configure the PMK for WPA state machine.
+ */
+void wpa_sm_set_pmk(struct wpa_sm *sm, const u8 *pmk, size_t pmk_len,
+ const u8 *bssid)
+{
+ if (sm == NULL)
+ return;
+
+ sm->pmk_len = pmk_len;
+ os_memcpy(sm->pmk, pmk, pmk_len);
+
+#ifdef CONFIG_IEEE80211R
+ /* Set XXKey to be PSK for FT key derivation */
+ sm->xxkey_len = pmk_len;
+ os_memcpy(sm->xxkey, pmk, pmk_len);
+#endif /* CONFIG_IEEE80211R */
+
+ if (bssid) {
+ pmksa_cache_add(sm->pmksa, pmk, pmk_len, NULL, 0,
+ bssid, sm->own_addr,
+ sm->network_ctx, sm->key_mgmt);
+ }
+}
+
+
+/**
+ * wpa_sm_set_pmk_from_pmksa - Set PMK based on the current PMKSA
+ * @sm: Pointer to WPA state machine data from wpa_sm_init()
+ *
+ * Take the PMK from the current PMKSA into use. If no PMKSA is active, the PMK
+ * will be cleared.
+ */
+void wpa_sm_set_pmk_from_pmksa(struct wpa_sm *sm)
+{
+ if (sm == NULL)
+ return;
+
+ if (sm->cur_pmksa) {
+ sm->pmk_len = sm->cur_pmksa->pmk_len;
+ os_memcpy(sm->pmk, sm->cur_pmksa->pmk, sm->pmk_len);
+ } else {
+ sm->pmk_len = PMK_LEN;
+ os_memset(sm->pmk, 0, PMK_LEN);
+ }
+}
+
+
+/**
+ * wpa_sm_set_fast_reauth - Set fast reauthentication (EAP) enabled/disabled
+ * @sm: Pointer to WPA state machine data from wpa_sm_init()
+ * @fast_reauth: Whether fast reauthentication (EAP) is allowed
+ */
+void wpa_sm_set_fast_reauth(struct wpa_sm *sm, int fast_reauth)
+{
+ if (sm)
+ sm->fast_reauth = fast_reauth;
+}
+
+
+/**
+ * wpa_sm_set_scard_ctx - Set context pointer for smartcard callbacks
+ * @sm: Pointer to WPA state machine data from wpa_sm_init()
+ * @scard_ctx: Context pointer for smartcard related callback functions
+ */
+void wpa_sm_set_scard_ctx(struct wpa_sm *sm, void *scard_ctx)
+{
+ if (sm == NULL)
+ return;
+ sm->scard_ctx = scard_ctx;
+ if (sm->preauth_eapol)
+ eapol_sm_register_scard_ctx(sm->preauth_eapol, scard_ctx);
+}
+
+
+/**
+ * wpa_sm_set_config - Notification of current configration change
+ * @sm: Pointer to WPA state machine data from wpa_sm_init()
+ * @config: Pointer to current network configuration
+ *
+ * Notify WPA state machine that configuration has changed. config will be
+ * stored as a backpointer to network configuration. This can be %NULL to clear
+ * the stored pointed.
+ */
+void wpa_sm_set_config(struct wpa_sm *sm, struct rsn_supp_config *config)
+{
+ if (!sm)
+ return;
+
+ if (config) {
+ sm->network_ctx = config->network_ctx;
+ sm->peerkey_enabled = config->peerkey_enabled;
+ sm->allowed_pairwise_cipher = config->allowed_pairwise_cipher;
+ sm->proactive_key_caching = config->proactive_key_caching;
+ sm->eap_workaround = config->eap_workaround;
+ sm->eap_conf_ctx = config->eap_conf_ctx;
+ if (config->ssid) {
+ os_memcpy(sm->ssid, config->ssid, config->ssid_len);
+ sm->ssid_len = config->ssid_len;
+ } else
+ sm->ssid_len = 0;
+ sm->wpa_ptk_rekey = config->wpa_ptk_rekey;
+ sm->p2p = config->p2p;
+ } else {
+ sm->network_ctx = NULL;
+ sm->peerkey_enabled = 0;
+ sm->allowed_pairwise_cipher = 0;
+ sm->proactive_key_caching = 0;
+ sm->eap_workaround = 0;
+ sm->eap_conf_ctx = NULL;
+ sm->ssid_len = 0;
+ sm->wpa_ptk_rekey = 0;
+ sm->p2p = 0;
+ }
+}
+
+
+/**
+ * wpa_sm_set_own_addr - Set own MAC address
+ * @sm: Pointer to WPA state machine data from wpa_sm_init()
+ * @addr: Own MAC address
+ */
+void wpa_sm_set_own_addr(struct wpa_sm *sm, const u8 *addr)
+{
+ if (sm)
+ os_memcpy(sm->own_addr, addr, ETH_ALEN);
+}
+
+
+/**
+ * wpa_sm_set_ifname - Set network interface name
+ * @sm: Pointer to WPA state machine data from wpa_sm_init()
+ * @ifname: Interface name
+ * @bridge_ifname: Optional bridge interface name (for pre-auth)
+ */
+void wpa_sm_set_ifname(struct wpa_sm *sm, const char *ifname,
+ const char *bridge_ifname)
+{
+ if (sm) {
+ sm->ifname = ifname;
+ sm->bridge_ifname = bridge_ifname;
+ }
+}
+
+
+/**
+ * wpa_sm_set_eapol - Set EAPOL state machine pointer
+ * @sm: Pointer to WPA state machine data from wpa_sm_init()
+ * @eapol: Pointer to EAPOL state machine allocated with eapol_sm_init()
+ */
+void wpa_sm_set_eapol(struct wpa_sm *sm, struct eapol_sm *eapol)
+{
+ if (sm)
+ sm->eapol = eapol;
+}
+
+
+/**
+ * wpa_sm_set_param - Set WPA state machine parameters
+ * @sm: Pointer to WPA state machine data from wpa_sm_init()
+ * @param: Parameter field
+ * @value: Parameter value
+ * Returns: 0 on success, -1 on failure
+ */
+int wpa_sm_set_param(struct wpa_sm *sm, enum wpa_sm_conf_params param,
+ unsigned int value)
+{
+ int ret = 0;
+
+ if (sm == NULL)
+ return -1;
+
+ switch (param) {
+ case RSNA_PMK_LIFETIME:
+ if (value > 0)
+ sm->dot11RSNAConfigPMKLifetime = value;
+ else
+ ret = -1;
+ break;
+ case RSNA_PMK_REAUTH_THRESHOLD:
+ if (value > 0 && value <= 100)
+ sm->dot11RSNAConfigPMKReauthThreshold = value;
+ else
+ ret = -1;
+ break;
+ case RSNA_SA_TIMEOUT:
+ if (value > 0)
+ sm->dot11RSNAConfigSATimeout = value;
+ else
+ ret = -1;
+ break;
+ case WPA_PARAM_PROTO:
+ sm->proto = value;
+ break;
+ case WPA_PARAM_PAIRWISE:
+ sm->pairwise_cipher = value;
+ break;
+ case WPA_PARAM_GROUP:
+ sm->group_cipher = value;
+ break;
+ case WPA_PARAM_KEY_MGMT:
+ sm->key_mgmt = value;
+ break;
+#ifdef CONFIG_IEEE80211W
+ case WPA_PARAM_MGMT_GROUP:
+ sm->mgmt_group_cipher = value;
+ break;
+#endif /* CONFIG_IEEE80211W */
+ case WPA_PARAM_RSN_ENABLED:
+ sm->rsn_enabled = value;
+ break;
+ case WPA_PARAM_MFP:
+ sm->mfp = value;
+ break;
+ default:
+ break;
+ }
+
+ return ret;
+}
+
+
+/**
+ * wpa_sm_get_status - Get WPA state machine
+ * @sm: Pointer to WPA state machine data from wpa_sm_init()
+ * @buf: Buffer for status information
+ * @buflen: Maximum buffer length
+ * @verbose: Whether to include verbose status information
+ * Returns: Number of bytes written to buf.
+ *
+ * Query WPA state machine for status information. This function fills in
+ * a text area with current status information. If the buffer (buf) is not
+ * large enough, status information will be truncated to fit the buffer.
+ */
+int wpa_sm_get_status(struct wpa_sm *sm, char *buf, size_t buflen,
+ int verbose)
+{
+ char *pos = buf, *end = buf + buflen;
+ int ret;
+
+ ret = os_snprintf(pos, end - pos,
+ "pairwise_cipher=%s\n"
+ "group_cipher=%s\n"
+ "key_mgmt=%s\n",
+ wpa_cipher_txt(sm->pairwise_cipher),
+ wpa_cipher_txt(sm->group_cipher),
+ wpa_key_mgmt_txt(sm->key_mgmt, sm->proto));
+ if (os_snprintf_error(end - pos, ret))
+ return pos - buf;
+ pos += ret;
+
+ if (sm->mfp != NO_MGMT_FRAME_PROTECTION && sm->ap_rsn_ie) {
+ struct wpa_ie_data rsn;
+ if (wpa_parse_wpa_ie_rsn(sm->ap_rsn_ie, sm->ap_rsn_ie_len, &rsn)
+ >= 0 &&
+ rsn.capabilities & (WPA_CAPABILITY_MFPR |
+ WPA_CAPABILITY_MFPC)) {
+ ret = os_snprintf(pos, end - pos, "pmf=%d\n",
+ (rsn.capabilities &
+ WPA_CAPABILITY_MFPR) ? 2 : 1);
+ if (os_snprintf_error(end - pos, ret))
+ return pos - buf;
+ pos += ret;
+ }
+ }
+
+ return pos - buf;
+}
+
+
+int wpa_sm_pmf_enabled(struct wpa_sm *sm)
+{
+ struct wpa_ie_data rsn;
+
+ if (sm->mfp == NO_MGMT_FRAME_PROTECTION || !sm->ap_rsn_ie)
+ return 0;
+
+ if (wpa_parse_wpa_ie_rsn(sm->ap_rsn_ie, sm->ap_rsn_ie_len, &rsn) >= 0 &&
+ rsn.capabilities & (WPA_CAPABILITY_MFPR | WPA_CAPABILITY_MFPC))
+ return 1;
+
+ return 0;
+}
+
+
+/**
+ * wpa_sm_set_assoc_wpa_ie_default - Generate own WPA/RSN IE from configuration
+ * @sm: Pointer to WPA state machine data from wpa_sm_init()
+ * @wpa_ie: Pointer to buffer for WPA/RSN IE
+ * @wpa_ie_len: Pointer to the length of the wpa_ie buffer
+ * Returns: 0 on success, -1 on failure
+ */
+int wpa_sm_set_assoc_wpa_ie_default(struct wpa_sm *sm, u8 *wpa_ie,
+ size_t *wpa_ie_len)
+{
+ int res;
+
+ if (sm == NULL)
+ return -1;
+
+ res = wpa_gen_wpa_ie(sm, wpa_ie, *wpa_ie_len);
+ if (res < 0)
+ return -1;
+ *wpa_ie_len = res;
+
+ wpa_hexdump(MSG_DEBUG, "WPA: Set own WPA IE default",
+ wpa_ie, *wpa_ie_len);
+
+ if (sm->assoc_wpa_ie == NULL) {
+ /*
+ * Make a copy of the WPA/RSN IE so that 4-Way Handshake gets
+ * the correct version of the IE even if PMKSA caching is
+ * aborted (which would remove PMKID from IE generation).
+ */
+ sm->assoc_wpa_ie = os_malloc(*wpa_ie_len);
+ if (sm->assoc_wpa_ie == NULL)
+ return -1;
+
+ os_memcpy(sm->assoc_wpa_ie, wpa_ie, *wpa_ie_len);
+ sm->assoc_wpa_ie_len = *wpa_ie_len;
+ }
+
+ return 0;
+}
+
+
+/**
+ * wpa_sm_set_assoc_wpa_ie - Set own WPA/RSN IE from (Re)AssocReq
+ * @sm: Pointer to WPA state machine data from wpa_sm_init()
+ * @ie: Pointer to IE data (starting from id)
+ * @len: IE length
+ * Returns: 0 on success, -1 on failure
+ *
+ * Inform WPA state machine about the WPA/RSN IE used in (Re)Association
+ * Request frame. The IE will be used to override the default value generated
+ * with wpa_sm_set_assoc_wpa_ie_default().
+ */
+int wpa_sm_set_assoc_wpa_ie(struct wpa_sm *sm, const u8 *ie, size_t len)
+{
+ if (sm == NULL)
+ return -1;
+
+ os_free(sm->assoc_wpa_ie);
+ if (ie == NULL || len == 0) {
+ wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
+ "WPA: clearing own WPA/RSN IE");
+ sm->assoc_wpa_ie = NULL;
+ sm->assoc_wpa_ie_len = 0;
+ } else {
+ wpa_hexdump(MSG_DEBUG, "WPA: set own WPA/RSN IE", ie, len);
+ sm->assoc_wpa_ie = os_malloc(len);
+ if (sm->assoc_wpa_ie == NULL)
+ return -1;
+
+ os_memcpy(sm->assoc_wpa_ie, ie, len);
+ sm->assoc_wpa_ie_len = len;
+ }
+
+ return 0;
+}
+
+
+/**
+ * wpa_sm_set_ap_wpa_ie - Set AP WPA IE from Beacon/ProbeResp
+ * @sm: Pointer to WPA state machine data from wpa_sm_init()
+ * @ie: Pointer to IE data (starting from id)
+ * @len: IE length
+ * Returns: 0 on success, -1 on failure
+ *
+ * Inform WPA state machine about the WPA IE used in Beacon / Probe Response
+ * frame.
+ */
+int wpa_sm_set_ap_wpa_ie(struct wpa_sm *sm, const u8 *ie, size_t len)
+{
+ if (sm == NULL)
+ return -1;
+
+ os_free(sm->ap_wpa_ie);
+ if (ie == NULL || len == 0) {
+ wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
+ "WPA: clearing AP WPA IE");
+ sm->ap_wpa_ie = NULL;
+ sm->ap_wpa_ie_len = 0;
+ } else {
+ wpa_hexdump(MSG_DEBUG, "WPA: set AP WPA IE", ie, len);
+ sm->ap_wpa_ie = os_malloc(len);
+ if (sm->ap_wpa_ie == NULL)
+ return -1;
+
+ os_memcpy(sm->ap_wpa_ie, ie, len);
+ sm->ap_wpa_ie_len = len;
+ }
+
+ return 0;
+}
+
+
+/**
+ * wpa_sm_set_ap_rsn_ie - Set AP RSN IE from Beacon/ProbeResp
+ * @sm: Pointer to WPA state machine data from wpa_sm_init()
+ * @ie: Pointer to IE data (starting from id)
+ * @len: IE length
+ * Returns: 0 on success, -1 on failure
+ *
+ * Inform WPA state machine about the RSN IE used in Beacon / Probe Response
+ * frame.
+ */
+int wpa_sm_set_ap_rsn_ie(struct wpa_sm *sm, const u8 *ie, size_t len)
+{
+ if (sm == NULL)
+ return -1;
+
+ os_free(sm->ap_rsn_ie);
+ if (ie == NULL || len == 0) {
+ wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
+ "WPA: clearing AP RSN IE");
+ sm->ap_rsn_ie = NULL;
+ sm->ap_rsn_ie_len = 0;
+ } else {
+ wpa_hexdump(MSG_DEBUG, "WPA: set AP RSN IE", ie, len);
+ sm->ap_rsn_ie = os_malloc(len);
+ if (sm->ap_rsn_ie == NULL)
+ return -1;
+
+ os_memcpy(sm->ap_rsn_ie, ie, len);
+ sm->ap_rsn_ie_len = len;
+ }
+
+ return 0;
+}
+
+
+/**
+ * wpa_sm_parse_own_wpa_ie - Parse own WPA/RSN IE
+ * @sm: Pointer to WPA state machine data from wpa_sm_init()
+ * @data: Pointer to data area for parsing results
+ * Returns: 0 on success, -1 if IE is not known, or -2 on parsing failure
+ *
+ * Parse the contents of the own WPA or RSN IE from (Re)AssocReq and write the
+ * parsed data into data.
+ */
+int wpa_sm_parse_own_wpa_ie(struct wpa_sm *sm, struct wpa_ie_data *data)
+{
+ if (sm == NULL)
+ return -1;
+
+ if (sm->assoc_wpa_ie == NULL) {
+ wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
+ "WPA: No WPA/RSN IE available from association info");
+ return -1;
+ }
+ if (wpa_parse_wpa_ie(sm->assoc_wpa_ie, sm->assoc_wpa_ie_len, data))
+ return -2;
+ return 0;
+}
+
+
+int wpa_sm_pmksa_cache_list(struct wpa_sm *sm, char *buf, size_t len)
+{
+ return pmksa_cache_list(sm->pmksa, buf, len);
+}
+
+
+void wpa_sm_drop_sa(struct wpa_sm *sm)
+{
+ wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: Clear old PMK and PTK");
+ sm->ptk_set = 0;
+ sm->tptk_set = 0;
+ os_memset(sm->pmk, 0, sizeof(sm->pmk));
+ os_memset(&sm->ptk, 0, sizeof(sm->ptk));
+ os_memset(&sm->tptk, 0, sizeof(sm->tptk));
+#ifdef CONFIG_IEEE80211R
+ os_memset(sm->xxkey, 0, sizeof(sm->xxkey));
+ os_memset(sm->pmk_r0, 0, sizeof(sm->pmk_r0));
+ os_memset(sm->pmk_r1, 0, sizeof(sm->pmk_r1));
+#endif /* CONFIG_IEEE80211R */
+}
+
+
+int wpa_sm_has_ptk(struct wpa_sm *sm)
+{
+ if (sm == NULL)
+ return 0;
+ return sm->ptk_set;
+}
+
+
+void wpa_sm_update_replay_ctr(struct wpa_sm *sm, const u8 *replay_ctr)
+{
+ os_memcpy(sm->rx_replay_counter, replay_ctr, WPA_REPLAY_COUNTER_LEN);
+}
+
+
+void wpa_sm_pmksa_cache_flush(struct wpa_sm *sm, void *network_ctx)
+{
+ pmksa_cache_flush(sm->pmksa, network_ctx, NULL, 0);
+}
+
+
+#ifdef CONFIG_WNM
+int wpa_wnmsleep_install_key(struct wpa_sm *sm, u8 subelem_id, u8 *buf)
+{
+ u16 keyinfo;
+ u8 keylen; /* plaintext key len */
+ u8 *key_rsc;
+
+ if (subelem_id == WNM_SLEEP_SUBELEM_GTK) {
+ struct wpa_gtk_data gd;
+
+ os_memset(&gd, 0, sizeof(gd));
+ keylen = wpa_cipher_key_len(sm->group_cipher);
+ gd.key_rsc_len = wpa_cipher_rsc_len(sm->group_cipher);
+ gd.alg = wpa_cipher_to_alg(sm->group_cipher);
+ if (gd.alg == WPA_ALG_NONE) {
+ wpa_printf(MSG_DEBUG, "Unsupported group cipher suite");
+ return -1;
+ }
+
+ key_rsc = buf + 5;
+ keyinfo = WPA_GET_LE16(buf + 2);
+ gd.gtk_len = keylen;
+ if (gd.gtk_len != buf[4]) {
+ wpa_printf(MSG_DEBUG, "GTK len mismatch len %d vs %d",
+ gd.gtk_len, buf[4]);
+ return -1;
+ }
+ gd.keyidx = keyinfo & 0x03; /* B0 - B1 */
+ gd.tx = wpa_supplicant_gtk_tx_bit_workaround(
+ sm, !!(keyinfo & WPA_KEY_INFO_TXRX));
+
+ os_memcpy(gd.gtk, buf + 13, gd.gtk_len);
+
+ wpa_hexdump_key(MSG_DEBUG, "Install GTK (WNM SLEEP)",
+ gd.gtk, gd.gtk_len);
+ if (wpa_supplicant_install_gtk(sm, &gd, key_rsc)) {
+ os_memset(&gd, 0, sizeof(gd));
+ wpa_printf(MSG_DEBUG, "Failed to install the GTK in "
+ "WNM mode");
+ return -1;
+ }
+ os_memset(&gd, 0, sizeof(gd));
+#ifdef CONFIG_IEEE80211W
+ } else if (subelem_id == WNM_SLEEP_SUBELEM_IGTK) {
+ struct wpa_igtk_kde igd;
+ u16 keyidx;
+
+ os_memset(&igd, 0, sizeof(igd));
+ keylen = wpa_cipher_key_len(sm->mgmt_group_cipher);
+ os_memcpy(igd.keyid, buf + 2, 2);
+ os_memcpy(igd.pn, buf + 4, 6);
+
+ keyidx = WPA_GET_LE16(igd.keyid);
+ os_memcpy(igd.igtk, buf + 10, keylen);
+
+ wpa_hexdump_key(MSG_DEBUG, "Install IGTK (WNM SLEEP)",
+ igd.igtk, keylen);
+ if (wpa_sm_set_key(sm, wpa_cipher_to_alg(sm->mgmt_group_cipher),
+ broadcast_ether_addr,
+ keyidx, 0, igd.pn, sizeof(igd.pn),
+ igd.igtk, keylen) < 0) {
+ wpa_printf(MSG_DEBUG, "Failed to install the IGTK in "
+ "WNM mode");
+ os_memset(&igd, 0, sizeof(igd));
+ return -1;
+ }
+ os_memset(&igd, 0, sizeof(igd));
+#endif /* CONFIG_IEEE80211W */
+ } else {
+ wpa_printf(MSG_DEBUG, "Unknown element id");
+ return -1;
+ }
+
+ return 0;
+}
+#endif /* CONFIG_WNM */
+
+
+#ifdef CONFIG_PEERKEY
+int wpa_sm_rx_eapol_peerkey(struct wpa_sm *sm, const u8 *src_addr,
+ const u8 *buf, size_t len)
+{
+ struct wpa_peerkey *peerkey;
+
+ for (peerkey = sm->peerkey; peerkey; peerkey = peerkey->next) {
+ if (os_memcmp(peerkey->addr, src_addr, ETH_ALEN) == 0)
+ break;
+ }
+
+ if (!peerkey)
+ return 0;
+
+ wpa_sm_rx_eapol(sm, src_addr, buf, len);
+
+ return 1;
+}
+#endif /* CONFIG_PEERKEY */
+
+
+#ifdef CONFIG_P2P
+
+int wpa_sm_get_p2p_ip_addr(struct wpa_sm *sm, u8 *buf)
+{
+ if (sm == NULL || WPA_GET_BE32(sm->p2p_ip_addr) == 0)
+ return -1;
+ os_memcpy(buf, sm->p2p_ip_addr, 3 * 4);
+ return 0;
+}
+
+#endif /* CONFIG_P2P */
+
+
+void wpa_sm_set_rx_replay_ctr(struct wpa_sm *sm, const u8 *rx_replay_counter)
+{
+ if (rx_replay_counter == NULL)
+ return;
+
+ os_memcpy(sm->rx_replay_counter, rx_replay_counter,
+ WPA_REPLAY_COUNTER_LEN);
+ sm->rx_replay_counter_set = 1;
+ wpa_printf(MSG_DEBUG, "Updated key replay counter");
+}
+
+
+void wpa_sm_set_ptk_kck_kek(struct wpa_sm *sm,
+ const u8 *ptk_kck, size_t ptk_kck_len,
+ const u8 *ptk_kek, size_t ptk_kek_len)
+{
+ if (ptk_kck && ptk_kck_len <= WPA_KCK_MAX_LEN) {
+ os_memcpy(sm->ptk.kck, ptk_kck, ptk_kck_len);
+ sm->ptk.kck_len = ptk_kck_len;
+ wpa_printf(MSG_DEBUG, "Updated PTK KCK");
+ }
+ if (ptk_kek && ptk_kek_len <= WPA_KEK_MAX_LEN) {
+ os_memcpy(sm->ptk.kek, ptk_kek, ptk_kek_len);
+ sm->ptk.kek_len = ptk_kek_len;
+ wpa_printf(MSG_DEBUG, "Updated PTK KEK");
+ }
+ sm->ptk_set = 1;
+}
diff --git a/freebsd/contrib/wpa/src/rsn_supp/wpa.h b/freebsd/contrib/wpa/src/rsn_supp/wpa.h
new file mode 100644
index 00000000..e163b701
--- /dev/null
+++ b/freebsd/contrib/wpa/src/rsn_supp/wpa.h
@@ -0,0 +1,421 @@
+/*
+ * wpa_supplicant - WPA definitions
+ * Copyright (c) 2003-2015, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef WPA_H
+#define WPA_H
+
+#include "common/defs.h"
+#include "common/eapol_common.h"
+#include "common/wpa_common.h"
+#include "common/ieee802_11_defs.h"
+
+struct wpa_sm;
+struct eapol_sm;
+struct wpa_config_blob;
+struct hostapd_freq_params;
+
+struct wpa_sm_ctx {
+ void *ctx; /* pointer to arbitrary upper level context */
+ void *msg_ctx; /* upper level context for wpa_msg() calls */
+
+ void (*set_state)(void *ctx, enum wpa_states state);
+ enum wpa_states (*get_state)(void *ctx);
+ void (*deauthenticate)(void * ctx, int reason_code);
+ int (*set_key)(void *ctx, enum wpa_alg alg,
+ const u8 *addr, int key_idx, int set_tx,
+ const u8 *seq, size_t seq_len,
+ const u8 *key, size_t key_len);
+ void * (*get_network_ctx)(void *ctx);
+ int (*get_bssid)(void *ctx, u8 *bssid);
+ int (*ether_send)(void *ctx, const u8 *dest, u16 proto, const u8 *buf,
+ size_t len);
+ int (*get_beacon_ie)(void *ctx);
+ void (*cancel_auth_timeout)(void *ctx);
+ u8 * (*alloc_eapol)(void *ctx, u8 type, const void *data, u16 data_len,
+ size_t *msg_len, void **data_pos);
+ int (*add_pmkid)(void *ctx, const u8 *bssid, const u8 *pmkid);
+ int (*remove_pmkid)(void *ctx, const u8 *bssid, const u8 *pmkid);
+ void (*set_config_blob)(void *ctx, struct wpa_config_blob *blob);
+ const struct wpa_config_blob * (*get_config_blob)(void *ctx,
+ const char *name);
+ int (*mlme_setprotection)(void *ctx, const u8 *addr,
+ int protection_type, int key_type);
+ int (*update_ft_ies)(void *ctx, const u8 *md, const u8 *ies,
+ size_t ies_len);
+ int (*send_ft_action)(void *ctx, u8 action, const u8 *target_ap,
+ const u8 *ies, size_t ies_len);
+ int (*mark_authenticated)(void *ctx, const u8 *target_ap);
+#ifdef CONFIG_TDLS
+ int (*tdls_get_capa)(void *ctx, int *tdls_supported,
+ int *tdls_ext_setup, int *tdls_chan_switch);
+ int (*send_tdls_mgmt)(void *ctx, const u8 *dst,
+ u8 action_code, u8 dialog_token,
+ u16 status_code, u32 peer_capab,
+ int initiator, const u8 *buf, size_t len);
+ int (*tdls_oper)(void *ctx, int oper, const u8 *peer);
+ int (*tdls_peer_addset)(void *ctx, const u8 *addr, int add, u16 aid,
+ u16 capability, const u8 *supp_rates,
+ size_t supp_rates_len,
+ const struct ieee80211_ht_capabilities *ht_capab,
+ const struct ieee80211_vht_capabilities *vht_capab,
+ u8 qosinfo, int wmm, const u8 *ext_capab,
+ size_t ext_capab_len, const u8 *supp_channels,
+ size_t supp_channels_len,
+ const u8 *supp_oper_classes,
+ size_t supp_oper_classes_len);
+ int (*tdls_enable_channel_switch)(
+ void *ctx, const u8 *addr, u8 oper_class,
+ const struct hostapd_freq_params *params);
+ int (*tdls_disable_channel_switch)(void *ctx, const u8 *addr);
+#endif /* CONFIG_TDLS */
+ void (*set_rekey_offload)(void *ctx, const u8 *kek, size_t kek_len,
+ const u8 *kck, size_t kck_len,
+ const u8 *replay_ctr);
+ int (*key_mgmt_set_pmk)(void *ctx, const u8 *pmk, size_t pmk_len);
+};
+
+
+enum wpa_sm_conf_params {
+ RSNA_PMK_LIFETIME /* dot11RSNAConfigPMKLifetime */,
+ RSNA_PMK_REAUTH_THRESHOLD /* dot11RSNAConfigPMKReauthThreshold */,
+ RSNA_SA_TIMEOUT /* dot11RSNAConfigSATimeout */,
+ WPA_PARAM_PROTO,
+ WPA_PARAM_PAIRWISE,
+ WPA_PARAM_GROUP,
+ WPA_PARAM_KEY_MGMT,
+ WPA_PARAM_MGMT_GROUP,
+ WPA_PARAM_RSN_ENABLED,
+ WPA_PARAM_MFP
+};
+
+struct rsn_supp_config {
+ void *network_ctx;
+ int peerkey_enabled;
+ int allowed_pairwise_cipher; /* bitfield of WPA_CIPHER_* */
+ int proactive_key_caching;
+ int eap_workaround;
+ void *eap_conf_ctx;
+ const u8 *ssid;
+ size_t ssid_len;
+ int wpa_ptk_rekey;
+ int p2p;
+};
+
+#ifndef CONFIG_NO_WPA
+
+struct wpa_sm * wpa_sm_init(struct wpa_sm_ctx *ctx);
+void wpa_sm_deinit(struct wpa_sm *sm);
+void wpa_sm_notify_assoc(struct wpa_sm *sm, const u8 *bssid);
+void wpa_sm_notify_disassoc(struct wpa_sm *sm);
+void wpa_sm_set_pmk(struct wpa_sm *sm, const u8 *pmk, size_t pmk_len,
+ const u8 *bssid);
+void wpa_sm_set_pmk_from_pmksa(struct wpa_sm *sm);
+void wpa_sm_set_fast_reauth(struct wpa_sm *sm, int fast_reauth);
+void wpa_sm_set_scard_ctx(struct wpa_sm *sm, void *scard_ctx);
+void wpa_sm_set_config(struct wpa_sm *sm, struct rsn_supp_config *config);
+void wpa_sm_set_own_addr(struct wpa_sm *sm, const u8 *addr);
+void wpa_sm_set_ifname(struct wpa_sm *sm, const char *ifname,
+ const char *bridge_ifname);
+void wpa_sm_set_eapol(struct wpa_sm *sm, struct eapol_sm *eapol);
+int wpa_sm_set_assoc_wpa_ie(struct wpa_sm *sm, const u8 *ie, size_t len);
+int wpa_sm_set_assoc_wpa_ie_default(struct wpa_sm *sm, u8 *wpa_ie,
+ size_t *wpa_ie_len);
+int wpa_sm_set_ap_wpa_ie(struct wpa_sm *sm, const u8 *ie, size_t len);
+int wpa_sm_set_ap_rsn_ie(struct wpa_sm *sm, const u8 *ie, size_t len);
+int wpa_sm_get_mib(struct wpa_sm *sm, char *buf, size_t buflen);
+
+int wpa_sm_set_param(struct wpa_sm *sm, enum wpa_sm_conf_params param,
+ unsigned int value);
+
+int wpa_sm_get_status(struct wpa_sm *sm, char *buf, size_t buflen,
+ int verbose);
+int wpa_sm_pmf_enabled(struct wpa_sm *sm);
+
+void wpa_sm_key_request(struct wpa_sm *sm, int error, int pairwise);
+
+int wpa_parse_wpa_ie(const u8 *wpa_ie, size_t wpa_ie_len,
+ struct wpa_ie_data *data);
+
+void wpa_sm_aborted_cached(struct wpa_sm *sm);
+int wpa_sm_rx_eapol(struct wpa_sm *sm, const u8 *src_addr,
+ const u8 *buf, size_t len);
+int wpa_sm_parse_own_wpa_ie(struct wpa_sm *sm, struct wpa_ie_data *data);
+int wpa_sm_pmksa_cache_list(struct wpa_sm *sm, char *buf, size_t len);
+void wpa_sm_drop_sa(struct wpa_sm *sm);
+int wpa_sm_has_ptk(struct wpa_sm *sm);
+
+void wpa_sm_update_replay_ctr(struct wpa_sm *sm, const u8 *replay_ctr);
+
+void wpa_sm_pmksa_cache_flush(struct wpa_sm *sm, void *network_ctx);
+
+int wpa_sm_get_p2p_ip_addr(struct wpa_sm *sm, u8 *buf);
+
+void wpa_sm_set_rx_replay_ctr(struct wpa_sm *sm, const u8 *rx_replay_counter);
+void wpa_sm_set_ptk_kck_kek(struct wpa_sm *sm,
+ const u8 *ptk_kck, size_t ptk_kck_len,
+ const u8 *ptk_kek, size_t ptk_kek_len);
+
+#else /* CONFIG_NO_WPA */
+
+static inline struct wpa_sm * wpa_sm_init(struct wpa_sm_ctx *ctx)
+{
+ return (struct wpa_sm *) 1;
+}
+
+static inline void wpa_sm_deinit(struct wpa_sm *sm)
+{
+}
+
+static inline void wpa_sm_notify_assoc(struct wpa_sm *sm, const u8 *bssid)
+{
+}
+
+static inline void wpa_sm_notify_disassoc(struct wpa_sm *sm)
+{
+}
+
+static inline void wpa_sm_set_pmk(struct wpa_sm *sm, const u8 *pmk,
+ size_t pmk_len)
+{
+}
+
+static inline void wpa_sm_set_pmk_from_pmksa(struct wpa_sm *sm)
+{
+}
+
+static inline void wpa_sm_set_fast_reauth(struct wpa_sm *sm, int fast_reauth)
+{
+}
+
+static inline void wpa_sm_set_scard_ctx(struct wpa_sm *sm, void *scard_ctx)
+{
+}
+
+static inline void wpa_sm_set_config(struct wpa_sm *sm,
+ struct rsn_supp_config *config)
+{
+}
+
+static inline void wpa_sm_set_own_addr(struct wpa_sm *sm, const u8 *addr)
+{
+}
+
+static inline void wpa_sm_set_ifname(struct wpa_sm *sm, const char *ifname,
+ const char *bridge_ifname)
+{
+}
+
+static inline void wpa_sm_set_eapol(struct wpa_sm *sm, struct eapol_sm *eapol)
+{
+}
+
+static inline int wpa_sm_set_assoc_wpa_ie(struct wpa_sm *sm, const u8 *ie,
+ size_t len)
+{
+ return -1;
+}
+
+static inline int wpa_sm_set_assoc_wpa_ie_default(struct wpa_sm *sm,
+ u8 *wpa_ie,
+ size_t *wpa_ie_len)
+{
+ return -1;
+}
+
+static inline int wpa_sm_set_ap_wpa_ie(struct wpa_sm *sm, const u8 *ie,
+ size_t len)
+{
+ return -1;
+}
+
+static inline int wpa_sm_set_ap_rsn_ie(struct wpa_sm *sm, const u8 *ie,
+ size_t len)
+{
+ return -1;
+}
+
+static inline int wpa_sm_get_mib(struct wpa_sm *sm, char *buf, size_t buflen)
+{
+ return 0;
+}
+
+static inline int wpa_sm_set_param(struct wpa_sm *sm,
+ enum wpa_sm_conf_params param,
+ unsigned int value)
+{
+ return -1;
+}
+
+static inline int wpa_sm_get_status(struct wpa_sm *sm, char *buf,
+ size_t buflen, int verbose)
+{
+ return 0;
+}
+
+static inline int wpa_sm_pmf_enabled(struct wpa_sm *sm)
+{
+ return 0;
+}
+
+static inline void wpa_sm_key_request(struct wpa_sm *sm, int error,
+ int pairwise)
+{
+}
+
+static inline int wpa_parse_wpa_ie(const u8 *wpa_ie, size_t wpa_ie_len,
+ struct wpa_ie_data *data)
+{
+ return -1;
+}
+
+static inline void wpa_sm_aborted_cached(struct wpa_sm *sm)
+{
+}
+
+static inline int wpa_sm_rx_eapol(struct wpa_sm *sm, const u8 *src_addr,
+ const u8 *buf, size_t len)
+{
+ return -1;
+}
+
+static inline int wpa_sm_parse_own_wpa_ie(struct wpa_sm *sm,
+ struct wpa_ie_data *data)
+{
+ return -1;
+}
+
+static inline int wpa_sm_pmksa_cache_list(struct wpa_sm *sm, char *buf,
+ size_t len)
+{
+ return -1;
+}
+
+static inline void wpa_sm_drop_sa(struct wpa_sm *sm)
+{
+}
+
+static inline int wpa_sm_has_ptk(struct wpa_sm *sm)
+{
+ return 0;
+}
+
+static inline void wpa_sm_update_replay_ctr(struct wpa_sm *sm,
+ const u8 *replay_ctr)
+{
+}
+
+static inline void wpa_sm_pmksa_cache_flush(struct wpa_sm *sm,
+ void *network_ctx)
+{
+}
+
+static inline void wpa_sm_set_rx_replay_ctr(struct wpa_sm *sm,
+ const u8 *rx_replay_counter)
+{
+}
+
+static inline void wpa_sm_set_ptk_kck_kek(struct wpa_sm *sm, const u8 *ptk_kck,
+ const u8 *ptk_kek)
+{
+}
+
+#endif /* CONFIG_NO_WPA */
+
+#ifdef CONFIG_PEERKEY
+int wpa_sm_stkstart(struct wpa_sm *sm, const u8 *peer);
+int wpa_sm_rx_eapol_peerkey(struct wpa_sm *sm, const u8 *src_addr,
+ const u8 *buf, size_t len);
+#else /* CONFIG_PEERKEY */
+static inline int wpa_sm_stkstart(struct wpa_sm *sm, const u8 *peer)
+{
+ return -1;
+}
+
+static inline int wpa_sm_rx_eapol_peerkey(struct wpa_sm *sm, const u8 *src_addr,
+ const u8 *buf, size_t len)
+{
+ return 0;
+}
+#endif /* CONFIG_PEERKEY */
+
+#ifdef CONFIG_IEEE80211R
+
+int wpa_sm_set_ft_params(struct wpa_sm *sm, const u8 *ies, size_t ies_len);
+int wpa_ft_prepare_auth_request(struct wpa_sm *sm, const u8 *mdie);
+int wpa_ft_process_response(struct wpa_sm *sm, const u8 *ies, size_t ies_len,
+ int ft_action, const u8 *target_ap,
+ const u8 *ric_ies, size_t ric_ies_len);
+int wpa_ft_is_completed(struct wpa_sm *sm);
+void wpa_reset_ft_completed(struct wpa_sm *sm);
+int wpa_ft_validate_reassoc_resp(struct wpa_sm *sm, const u8 *ies,
+ size_t ies_len, const u8 *src_addr);
+int wpa_ft_start_over_ds(struct wpa_sm *sm, const u8 *target_ap,
+ const u8 *mdie);
+
+#else /* CONFIG_IEEE80211R */
+
+static inline int
+wpa_sm_set_ft_params(struct wpa_sm *sm, const u8 *ies, size_t ies_len)
+{
+ return 0;
+}
+
+static inline int wpa_ft_prepare_auth_request(struct wpa_sm *sm,
+ const u8 *mdie)
+{
+ return 0;
+}
+
+static inline int
+wpa_ft_process_response(struct wpa_sm *sm, const u8 *ies, size_t ies_len,
+ int ft_action, const u8 *target_ap)
+{
+ return 0;
+}
+
+static inline int wpa_ft_is_completed(struct wpa_sm *sm)
+{
+ return 0;
+}
+
+static inline void wpa_reset_ft_completed(struct wpa_sm *sm)
+{
+}
+
+static inline int
+wpa_ft_validate_reassoc_resp(struct wpa_sm *sm, const u8 *ies, size_t ies_len,
+ const u8 *src_addr)
+{
+ return -1;
+}
+
+#endif /* CONFIG_IEEE80211R */
+
+
+/* tdls.c */
+void wpa_tdls_ap_ies(struct wpa_sm *sm, const u8 *ies, size_t len);
+void wpa_tdls_assoc_resp_ies(struct wpa_sm *sm, const u8 *ies, size_t len);
+int wpa_tdls_start(struct wpa_sm *sm, const u8 *addr);
+void wpa_tdls_remove(struct wpa_sm *sm, const u8 *addr);
+int wpa_tdls_teardown_link(struct wpa_sm *sm, const u8 *addr, u16 reason_code);
+int wpa_tdls_send_discovery_request(struct wpa_sm *sm, const u8 *addr);
+int wpa_tdls_init(struct wpa_sm *sm);
+void wpa_tdls_teardown_peers(struct wpa_sm *sm);
+void wpa_tdls_deinit(struct wpa_sm *sm);
+void wpa_tdls_enable(struct wpa_sm *sm, int enabled);
+void wpa_tdls_disable_unreachable_link(struct wpa_sm *sm, const u8 *addr);
+const char * wpa_tdls_get_link_status(struct wpa_sm *sm, const u8 *addr);
+int wpa_tdls_is_external_setup(struct wpa_sm *sm);
+int wpa_tdls_enable_chan_switch(struct wpa_sm *sm, const u8 *addr,
+ u8 oper_class,
+ struct hostapd_freq_params *freq_params);
+int wpa_tdls_disable_chan_switch(struct wpa_sm *sm, const u8 *addr);
+
+int wpa_wnmsleep_install_key(struct wpa_sm *sm, u8 subelem_id, u8 *buf);
+
+#endif /* WPA_H */
diff --git a/freebsd/contrib/wpa/src/rsn_supp/wpa_ft.c b/freebsd/contrib/wpa/src/rsn_supp/wpa_ft.c
new file mode 100644
index 00000000..509a20e0
--- /dev/null
+++ b/freebsd/contrib/wpa/src/rsn_supp/wpa_ft.c
@@ -0,0 +1,849 @@
+#include <machine/rtems-bsd-user-space.h>
+
+/*
+ * WPA Supplicant - IEEE 802.11r - Fast BSS Transition
+ * Copyright (c) 2006-2015, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "crypto/aes_wrap.h"
+#include "crypto/random.h"
+#include "common/ieee802_11_defs.h"
+#include "common/ieee802_11_common.h"
+#include "wpa.h"
+#include "wpa_i.h"
+
+#ifdef CONFIG_IEEE80211R
+
+int wpa_derive_ptk_ft(struct wpa_sm *sm, const unsigned char *src_addr,
+ const struct wpa_eapol_key *key, struct wpa_ptk *ptk)
+{
+ u8 ptk_name[WPA_PMK_NAME_LEN];
+ const u8 *anonce = key->key_nonce;
+
+ if (sm->xxkey_len == 0) {
+ wpa_printf(MSG_DEBUG, "FT: XXKey not available for key "
+ "derivation");
+ return -1;
+ }
+
+ wpa_derive_pmk_r0(sm->xxkey, sm->xxkey_len, sm->ssid,
+ sm->ssid_len, sm->mobility_domain,
+ sm->r0kh_id, sm->r0kh_id_len, sm->own_addr,
+ sm->pmk_r0, sm->pmk_r0_name);
+ wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R0", sm->pmk_r0, PMK_LEN);
+ wpa_hexdump(MSG_DEBUG, "FT: PMKR0Name",
+ sm->pmk_r0_name, WPA_PMK_NAME_LEN);
+ wpa_derive_pmk_r1(sm->pmk_r0, sm->pmk_r0_name, sm->r1kh_id,
+ sm->own_addr, sm->pmk_r1, sm->pmk_r1_name);
+ wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R1", sm->pmk_r1, PMK_LEN);
+ wpa_hexdump(MSG_DEBUG, "FT: PMKR1Name", sm->pmk_r1_name,
+ WPA_PMK_NAME_LEN);
+ return wpa_pmk_r1_to_ptk(sm->pmk_r1, sm->snonce, anonce, sm->own_addr,
+ sm->bssid, sm->pmk_r1_name, ptk, ptk_name,
+ sm->key_mgmt, sm->pairwise_cipher);
+}
+
+
+/**
+ * wpa_sm_set_ft_params - Set FT (IEEE 802.11r) parameters
+ * @sm: Pointer to WPA state machine data from wpa_sm_init()
+ * @ies: Association Response IEs or %NULL to clear FT parameters
+ * @ies_len: Length of ies buffer in octets
+ * Returns: 0 on success, -1 on failure
+ */
+int wpa_sm_set_ft_params(struct wpa_sm *sm, const u8 *ies, size_t ies_len)
+{
+ struct wpa_ft_ies ft;
+
+ if (sm == NULL)
+ return 0;
+
+ if (wpa_ft_parse_ies(ies, ies_len, &ft) < 0)
+ return -1;
+
+ if (ft.mdie && ft.mdie_len < MOBILITY_DOMAIN_ID_LEN + 1)
+ return -1;
+
+ if (ft.mdie) {
+ wpa_hexdump(MSG_DEBUG, "FT: Mobility domain",
+ ft.mdie, MOBILITY_DOMAIN_ID_LEN);
+ os_memcpy(sm->mobility_domain, ft.mdie,
+ MOBILITY_DOMAIN_ID_LEN);
+ sm->mdie_ft_capab = ft.mdie[MOBILITY_DOMAIN_ID_LEN];
+ wpa_printf(MSG_DEBUG, "FT: Capability and Policy: 0x%02x",
+ sm->mdie_ft_capab);
+ } else
+ os_memset(sm->mobility_domain, 0, MOBILITY_DOMAIN_ID_LEN);
+
+ if (ft.r0kh_id) {
+ wpa_hexdump(MSG_DEBUG, "FT: R0KH-ID",
+ ft.r0kh_id, ft.r0kh_id_len);
+ os_memcpy(sm->r0kh_id, ft.r0kh_id, ft.r0kh_id_len);
+ sm->r0kh_id_len = ft.r0kh_id_len;
+ } else {
+ /* FIX: When should R0KH-ID be cleared? We need to keep the
+ * old R0KH-ID in order to be able to use this during FT. */
+ /*
+ * os_memset(sm->r0kh_id, 0, FT_R0KH_ID_LEN);
+ * sm->r0kh_id_len = 0;
+ */
+ }
+
+ if (ft.r1kh_id) {
+ wpa_hexdump(MSG_DEBUG, "FT: R1KH-ID",
+ ft.r1kh_id, FT_R1KH_ID_LEN);
+ os_memcpy(sm->r1kh_id, ft.r1kh_id, FT_R1KH_ID_LEN);
+ } else
+ os_memset(sm->r1kh_id, 0, FT_R1KH_ID_LEN);
+
+ os_free(sm->assoc_resp_ies);
+ sm->assoc_resp_ies = os_malloc(ft.mdie_len + 2 + ft.ftie_len + 2);
+ if (sm->assoc_resp_ies) {
+ u8 *pos = sm->assoc_resp_ies;
+ if (ft.mdie) {
+ os_memcpy(pos, ft.mdie - 2, ft.mdie_len + 2);
+ pos += ft.mdie_len + 2;
+ }
+ if (ft.ftie) {
+ os_memcpy(pos, ft.ftie - 2, ft.ftie_len + 2);
+ pos += ft.ftie_len + 2;
+ }
+ sm->assoc_resp_ies_len = pos - sm->assoc_resp_ies;
+ wpa_hexdump(MSG_DEBUG, "FT: Stored MDIE and FTIE from "
+ "(Re)Association Response",
+ sm->assoc_resp_ies, sm->assoc_resp_ies_len);
+ }
+
+ return 0;
+}
+
+
+/**
+ * wpa_ft_gen_req_ies - Generate FT (IEEE 802.11r) IEs for Auth/ReAssoc Request
+ * @sm: Pointer to WPA state machine data from wpa_sm_init()
+ * @len: Buffer for returning the length of the IEs
+ * @anonce: ANonce or %NULL if not yet available
+ * @pmk_name: PMKR0Name or PMKR1Name to be added into the RSN IE PMKID List
+ * @kck: 128-bit KCK for MIC or %NULL if no MIC is used
+ * @kck_len: KCK length in octets
+ * @target_ap: Target AP address
+ * @ric_ies: Optional IE(s), e.g., WMM TSPEC(s), for RIC-Request or %NULL
+ * @ric_ies_len: Length of ric_ies buffer in octets
+ * @ap_mdie: Mobility Domain IE from the target AP
+ * Returns: Pointer to buffer with IEs or %NULL on failure
+ *
+ * Caller is responsible for freeing the returned buffer with os_free();
+ */
+static u8 * wpa_ft_gen_req_ies(struct wpa_sm *sm, size_t *len,
+ const u8 *anonce, const u8 *pmk_name,
+ const u8 *kck, size_t kck_len,
+ const u8 *target_ap,
+ const u8 *ric_ies, size_t ric_ies_len,
+ const u8 *ap_mdie)
+{
+ size_t buf_len;
+ u8 *buf, *pos, *ftie_len, *ftie_pos;
+ struct rsn_mdie *mdie;
+ struct rsn_ftie *ftie;
+ struct rsn_ie_hdr *rsnie;
+ u16 capab;
+
+ sm->ft_completed = 0;
+
+ buf_len = 2 + sizeof(struct rsn_mdie) + 2 + sizeof(struct rsn_ftie) +
+ 2 + sm->r0kh_id_len + ric_ies_len + 100;
+ buf = os_zalloc(buf_len);
+ if (buf == NULL)
+ return NULL;
+ pos = buf;
+
+ /* RSNIE[PMKR0Name/PMKR1Name] */
+ rsnie = (struct rsn_ie_hdr *) pos;
+ rsnie->elem_id = WLAN_EID_RSN;
+ WPA_PUT_LE16(rsnie->version, RSN_VERSION);
+ pos = (u8 *) (rsnie + 1);
+
+ /* Group Suite Selector */
+ if (!wpa_cipher_valid_group(sm->group_cipher)) {
+ wpa_printf(MSG_WARNING, "FT: Invalid group cipher (%d)",
+ sm->group_cipher);
+ os_free(buf);
+ return NULL;
+ }
+ RSN_SELECTOR_PUT(pos, wpa_cipher_to_suite(WPA_PROTO_RSN,
+ sm->group_cipher));
+ pos += RSN_SELECTOR_LEN;
+
+ /* Pairwise Suite Count */
+ WPA_PUT_LE16(pos, 1);
+ pos += 2;
+
+ /* Pairwise Suite List */
+ if (!wpa_cipher_valid_pairwise(sm->pairwise_cipher)) {
+ wpa_printf(MSG_WARNING, "FT: Invalid pairwise cipher (%d)",
+ sm->pairwise_cipher);
+ os_free(buf);
+ return NULL;
+ }
+ RSN_SELECTOR_PUT(pos, wpa_cipher_to_suite(WPA_PROTO_RSN,
+ sm->pairwise_cipher));
+ pos += RSN_SELECTOR_LEN;
+
+ /* Authenticated Key Management Suite Count */
+ WPA_PUT_LE16(pos, 1);
+ pos += 2;
+
+ /* Authenticated Key Management Suite List */
+ if (sm->key_mgmt == WPA_KEY_MGMT_FT_IEEE8021X)
+ RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_802_1X);
+ else if (sm->key_mgmt == WPA_KEY_MGMT_FT_PSK)
+ RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_PSK);
+ else if (sm->key_mgmt == WPA_KEY_MGMT_FT_SAE)
+ RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_SAE);
+ else {
+ wpa_printf(MSG_WARNING, "FT: Invalid key management type (%d)",
+ sm->key_mgmt);
+ os_free(buf);
+ return NULL;
+ }
+ pos += RSN_SELECTOR_LEN;
+
+ /* RSN Capabilities */
+ capab = 0;
+#ifdef CONFIG_IEEE80211W
+ if (sm->mgmt_group_cipher == WPA_CIPHER_AES_128_CMAC)
+ capab |= WPA_CAPABILITY_MFPC;
+#endif /* CONFIG_IEEE80211W */
+ WPA_PUT_LE16(pos, capab);
+ pos += 2;
+
+ /* PMKID Count */
+ WPA_PUT_LE16(pos, 1);
+ pos += 2;
+
+ /* PMKID List [PMKR0Name/PMKR1Name] */
+ os_memcpy(pos, pmk_name, WPA_PMK_NAME_LEN);
+ pos += WPA_PMK_NAME_LEN;
+
+#ifdef CONFIG_IEEE80211W
+ if (sm->mgmt_group_cipher == WPA_CIPHER_AES_128_CMAC) {
+ /* Management Group Cipher Suite */
+ RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_AES_128_CMAC);
+ pos += RSN_SELECTOR_LEN;
+ }
+#endif /* CONFIG_IEEE80211W */
+
+ rsnie->len = (pos - (u8 *) rsnie) - 2;
+
+ /* MDIE */
+ *pos++ = WLAN_EID_MOBILITY_DOMAIN;
+ *pos++ = sizeof(*mdie);
+ mdie = (struct rsn_mdie *) pos;
+ pos += sizeof(*mdie);
+ os_memcpy(mdie->mobility_domain, sm->mobility_domain,
+ MOBILITY_DOMAIN_ID_LEN);
+ mdie->ft_capab = ap_mdie && ap_mdie[1] >= 3 ? ap_mdie[4] :
+ sm->mdie_ft_capab;
+
+ /* FTIE[SNonce, [R1KH-ID,] R0KH-ID ] */
+ ftie_pos = pos;
+ *pos++ = WLAN_EID_FAST_BSS_TRANSITION;
+ ftie_len = pos++;
+ ftie = (struct rsn_ftie *) pos;
+ pos += sizeof(*ftie);
+ os_memcpy(ftie->snonce, sm->snonce, WPA_NONCE_LEN);
+ if (anonce)
+ os_memcpy(ftie->anonce, anonce, WPA_NONCE_LEN);
+ if (kck) {
+ /* R1KH-ID sub-element in third FT message */
+ *pos++ = FTIE_SUBELEM_R1KH_ID;
+ *pos++ = FT_R1KH_ID_LEN;
+ os_memcpy(pos, sm->r1kh_id, FT_R1KH_ID_LEN);
+ pos += FT_R1KH_ID_LEN;
+ }
+ /* R0KH-ID sub-element */
+ *pos++ = FTIE_SUBELEM_R0KH_ID;
+ *pos++ = sm->r0kh_id_len;
+ os_memcpy(pos, sm->r0kh_id, sm->r0kh_id_len);
+ pos += sm->r0kh_id_len;
+ *ftie_len = pos - ftie_len - 1;
+
+ if (ric_ies) {
+ /* RIC Request */
+ os_memcpy(pos, ric_ies, ric_ies_len);
+ pos += ric_ies_len;
+ }
+
+ if (kck) {
+ /*
+ * IEEE Std 802.11r-2008, 11A.8.4
+ * MIC shall be calculated over:
+ * non-AP STA MAC address
+ * Target AP MAC address
+ * Transaction seq number (5 for ReassocReq, 3 otherwise)
+ * RSN IE
+ * MDIE
+ * FTIE (with MIC field set to 0)
+ * RIC-Request (if present)
+ */
+ /* Information element count */
+ ftie->mic_control[1] = 3 + ieee802_11_ie_count(ric_ies,
+ ric_ies_len);
+ if (wpa_ft_mic(kck, kck_len, sm->own_addr, target_ap, 5,
+ ((u8 *) mdie) - 2, 2 + sizeof(*mdie),
+ ftie_pos, 2 + *ftie_len,
+ (u8 *) rsnie, 2 + rsnie->len, ric_ies,
+ ric_ies_len, ftie->mic) < 0) {
+ wpa_printf(MSG_INFO, "FT: Failed to calculate MIC");
+ os_free(buf);
+ return NULL;
+ }
+ }
+
+ *len = pos - buf;
+
+ return buf;
+}
+
+
+static int wpa_ft_install_ptk(struct wpa_sm *sm, const u8 *bssid)
+{
+ int keylen;
+ enum wpa_alg alg;
+ u8 null_rsc[6] = { 0, 0, 0, 0, 0, 0 };
+
+ wpa_printf(MSG_DEBUG, "FT: Installing PTK to the driver.");
+
+ if (!wpa_cipher_valid_pairwise(sm->pairwise_cipher)) {
+ wpa_printf(MSG_WARNING, "FT: Unsupported pairwise cipher %d",
+ sm->pairwise_cipher);
+ return -1;
+ }
+
+ alg = wpa_cipher_to_alg(sm->pairwise_cipher);
+ keylen = wpa_cipher_key_len(sm->pairwise_cipher);
+
+ if (wpa_sm_set_key(sm, alg, bssid, 0, 1, null_rsc,
+ sizeof(null_rsc), (u8 *) sm->ptk.tk, keylen) < 0) {
+ wpa_printf(MSG_WARNING, "FT: Failed to set PTK to the driver");
+ return -1;
+ }
+
+ return 0;
+}
+
+
+/**
+ * wpa_ft_prepare_auth_request - Generate over-the-air auth request
+ * @sm: Pointer to WPA state machine data from wpa_sm_init()
+ * @mdie: Target AP MDIE
+ * Returns: 0 on success, -1 on failure
+ */
+int wpa_ft_prepare_auth_request(struct wpa_sm *sm, const u8 *mdie)
+{
+ u8 *ft_ies;
+ size_t ft_ies_len;
+
+ /* Generate a new SNonce */
+ if (random_get_bytes(sm->snonce, WPA_NONCE_LEN)) {
+ wpa_printf(MSG_INFO, "FT: Failed to generate a new SNonce");
+ return -1;
+ }
+
+ ft_ies = wpa_ft_gen_req_ies(sm, &ft_ies_len, NULL, sm->pmk_r0_name,
+ NULL, 0, sm->bssid, NULL, 0, mdie);
+ if (ft_ies) {
+ wpa_sm_update_ft_ies(sm, sm->mobility_domain,
+ ft_ies, ft_ies_len);
+ os_free(ft_ies);
+ }
+
+ return 0;
+}
+
+
+int wpa_ft_process_response(struct wpa_sm *sm, const u8 *ies, size_t ies_len,
+ int ft_action, const u8 *target_ap,
+ const u8 *ric_ies, size_t ric_ies_len)
+{
+ u8 *ft_ies;
+ size_t ft_ies_len;
+ struct wpa_ft_ies parse;
+ struct rsn_mdie *mdie;
+ struct rsn_ftie *ftie;
+ u8 ptk_name[WPA_PMK_NAME_LEN];
+ int ret;
+ const u8 *bssid;
+
+ wpa_hexdump(MSG_DEBUG, "FT: Response IEs", ies, ies_len);
+ wpa_hexdump(MSG_DEBUG, "FT: RIC IEs", ric_ies, ric_ies_len);
+
+ if (ft_action) {
+ if (!sm->over_the_ds_in_progress) {
+ wpa_printf(MSG_DEBUG, "FT: No over-the-DS in progress "
+ "- drop FT Action Response");
+ return -1;
+ }
+
+ if (os_memcmp(target_ap, sm->target_ap, ETH_ALEN) != 0) {
+ wpa_printf(MSG_DEBUG, "FT: No over-the-DS in progress "
+ "with this Target AP - drop FT Action "
+ "Response");
+ return -1;
+ }
+ }
+
+ if (!wpa_key_mgmt_ft(sm->key_mgmt)) {
+ wpa_printf(MSG_DEBUG, "FT: Reject FT IEs since FT is not "
+ "enabled for this connection");
+ return -1;
+ }
+
+ if (wpa_ft_parse_ies(ies, ies_len, &parse) < 0) {
+ wpa_printf(MSG_DEBUG, "FT: Failed to parse IEs");
+ return -1;
+ }
+
+ mdie = (struct rsn_mdie *) parse.mdie;
+ if (mdie == NULL || parse.mdie_len < sizeof(*mdie) ||
+ os_memcmp(mdie->mobility_domain, sm->mobility_domain,
+ MOBILITY_DOMAIN_ID_LEN) != 0) {
+ wpa_printf(MSG_DEBUG, "FT: Invalid MDIE");
+ return -1;
+ }
+
+ ftie = (struct rsn_ftie *) parse.ftie;
+ if (ftie == NULL || parse.ftie_len < sizeof(*ftie)) {
+ wpa_printf(MSG_DEBUG, "FT: Invalid FTIE");
+ return -1;
+ }
+
+ if (os_memcmp(ftie->snonce, sm->snonce, WPA_NONCE_LEN) != 0) {
+ wpa_printf(MSG_DEBUG, "FT: SNonce mismatch in FTIE");
+ wpa_hexdump(MSG_DEBUG, "FT: Received SNonce",
+ ftie->snonce, WPA_NONCE_LEN);
+ wpa_hexdump(MSG_DEBUG, "FT: Expected SNonce",
+ sm->snonce, WPA_NONCE_LEN);
+ return -1;
+ }
+
+ if (parse.r0kh_id == NULL) {
+ wpa_printf(MSG_DEBUG, "FT: No R0KH-ID subelem in FTIE");
+ return -1;
+ }
+
+ if (parse.r0kh_id_len != sm->r0kh_id_len ||
+ os_memcmp_const(parse.r0kh_id, sm->r0kh_id, parse.r0kh_id_len) != 0)
+ {
+ wpa_printf(MSG_DEBUG, "FT: R0KH-ID in FTIE did not match with "
+ "the current R0KH-ID");
+ wpa_hexdump(MSG_DEBUG, "FT: R0KH-ID in FTIE",
+ parse.r0kh_id, parse.r0kh_id_len);
+ wpa_hexdump(MSG_DEBUG, "FT: The current R0KH-ID",
+ sm->r0kh_id, sm->r0kh_id_len);
+ return -1;
+ }
+
+ if (parse.r1kh_id == NULL) {
+ wpa_printf(MSG_DEBUG, "FT: No R1KH-ID subelem in FTIE");
+ return -1;
+ }
+
+ if (parse.rsn_pmkid == NULL ||
+ os_memcmp_const(parse.rsn_pmkid, sm->pmk_r0_name, WPA_PMK_NAME_LEN))
+ {
+ wpa_printf(MSG_DEBUG, "FT: No matching PMKR0Name (PMKID) in "
+ "RSNIE");
+ return -1;
+ }
+
+ os_memcpy(sm->r1kh_id, parse.r1kh_id, FT_R1KH_ID_LEN);
+ wpa_hexdump(MSG_DEBUG, "FT: R1KH-ID", sm->r1kh_id, FT_R1KH_ID_LEN);
+ wpa_hexdump(MSG_DEBUG, "FT: SNonce", sm->snonce, WPA_NONCE_LEN);
+ wpa_hexdump(MSG_DEBUG, "FT: ANonce", ftie->anonce, WPA_NONCE_LEN);
+ os_memcpy(sm->anonce, ftie->anonce, WPA_NONCE_LEN);
+ wpa_derive_pmk_r1(sm->pmk_r0, sm->pmk_r0_name, sm->r1kh_id,
+ sm->own_addr, sm->pmk_r1, sm->pmk_r1_name);
+ wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R1", sm->pmk_r1, PMK_LEN);
+ wpa_hexdump(MSG_DEBUG, "FT: PMKR1Name",
+ sm->pmk_r1_name, WPA_PMK_NAME_LEN);
+
+ bssid = target_ap;
+ if (wpa_pmk_r1_to_ptk(sm->pmk_r1, sm->snonce, ftie->anonce,
+ sm->own_addr, bssid, sm->pmk_r1_name, &sm->ptk,
+ ptk_name, sm->key_mgmt, sm->pairwise_cipher) < 0)
+ return -1;
+
+ ft_ies = wpa_ft_gen_req_ies(sm, &ft_ies_len, ftie->anonce,
+ sm->pmk_r1_name,
+ sm->ptk.kck, sm->ptk.kck_len, bssid,
+ ric_ies, ric_ies_len,
+ parse.mdie ? parse.mdie - 2 : NULL);
+ if (ft_ies) {
+ wpa_sm_update_ft_ies(sm, sm->mobility_domain,
+ ft_ies, ft_ies_len);
+ os_free(ft_ies);
+ }
+
+ wpa_sm_mark_authenticated(sm, bssid);
+ ret = wpa_ft_install_ptk(sm, bssid);
+ if (ret) {
+ /*
+ * Some drivers do not support key configuration when we are
+ * not associated with the target AP. Work around this by
+ * trying again after the following reassociation gets
+ * completed.
+ */
+ wpa_printf(MSG_DEBUG, "FT: Failed to set PTK prior to "
+ "association - try again after reassociation");
+ sm->set_ptk_after_assoc = 1;
+ } else
+ sm->set_ptk_after_assoc = 0;
+
+ sm->ft_completed = 1;
+ if (ft_action) {
+ /*
+ * The caller is expected trigger re-association with the
+ * Target AP.
+ */
+ os_memcpy(sm->bssid, target_ap, ETH_ALEN);
+ }
+
+ return 0;
+}
+
+
+int wpa_ft_is_completed(struct wpa_sm *sm)
+{
+ if (sm == NULL)
+ return 0;
+
+ if (!wpa_key_mgmt_ft(sm->key_mgmt))
+ return 0;
+
+ return sm->ft_completed;
+}
+
+
+void wpa_reset_ft_completed(struct wpa_sm *sm)
+{
+ if (sm != NULL)
+ sm->ft_completed = 0;
+}
+
+
+static int wpa_ft_process_gtk_subelem(struct wpa_sm *sm, const u8 *gtk_elem,
+ size_t gtk_elem_len)
+{
+ u8 gtk[32];
+ int keyidx;
+ enum wpa_alg alg;
+ size_t gtk_len, keylen, rsc_len;
+
+ if (gtk_elem == NULL) {
+ wpa_printf(MSG_DEBUG, "FT: No GTK included in FTIE");
+ return 0;
+ }
+
+ wpa_hexdump_key(MSG_DEBUG, "FT: Received GTK in Reassoc Resp",
+ gtk_elem, gtk_elem_len);
+
+ if (gtk_elem_len < 11 + 24 || (gtk_elem_len - 11) % 8 ||
+ gtk_elem_len - 19 > sizeof(gtk)) {
+ wpa_printf(MSG_DEBUG, "FT: Invalid GTK sub-elem "
+ "length %lu", (unsigned long) gtk_elem_len);
+ return -1;
+ }
+ gtk_len = gtk_elem_len - 19;
+ if (aes_unwrap(sm->ptk.kek, sm->ptk.kek_len, gtk_len / 8, gtk_elem + 11,
+ gtk)) {
+ wpa_printf(MSG_WARNING, "FT: AES unwrap failed - could not "
+ "decrypt GTK");
+ return -1;
+ }
+
+ keylen = wpa_cipher_key_len(sm->group_cipher);
+ rsc_len = wpa_cipher_rsc_len(sm->group_cipher);
+ alg = wpa_cipher_to_alg(sm->group_cipher);
+ if (alg == WPA_ALG_NONE) {
+ wpa_printf(MSG_WARNING, "WPA: Unsupported Group Cipher %d",
+ sm->group_cipher);
+ return -1;
+ }
+
+ if (gtk_len < keylen) {
+ wpa_printf(MSG_DEBUG, "FT: Too short GTK in FTIE");
+ return -1;
+ }
+
+ /* Key Info[2] | Key Length[1] | RSC[8] | Key[5..32]. */
+
+ keyidx = WPA_GET_LE16(gtk_elem) & 0x03;
+
+ if (gtk_elem[2] != keylen) {
+ wpa_printf(MSG_DEBUG, "FT: GTK length mismatch: received %d "
+ "negotiated %lu",
+ gtk_elem[2], (unsigned long) keylen);
+ return -1;
+ }
+
+ wpa_hexdump_key(MSG_DEBUG, "FT: GTK from Reassoc Resp", gtk, keylen);
+ if (sm->group_cipher == WPA_CIPHER_TKIP) {
+ /* Swap Tx/Rx keys for Michael MIC */
+ u8 tmp[8];
+ os_memcpy(tmp, gtk + 16, 8);
+ os_memcpy(gtk + 16, gtk + 24, 8);
+ os_memcpy(gtk + 24, tmp, 8);
+ }
+ if (wpa_sm_set_key(sm, alg, broadcast_ether_addr, keyidx, 0,
+ gtk_elem + 3, rsc_len, gtk, keylen) < 0) {
+ wpa_printf(MSG_WARNING, "WPA: Failed to set GTK to the "
+ "driver.");
+ return -1;
+ }
+
+ return 0;
+}
+
+
+#ifdef CONFIG_IEEE80211W
+static int wpa_ft_process_igtk_subelem(struct wpa_sm *sm, const u8 *igtk_elem,
+ size_t igtk_elem_len)
+{
+ u8 igtk[WPA_IGTK_LEN];
+ u16 keyidx;
+
+ if (sm->mgmt_group_cipher != WPA_CIPHER_AES_128_CMAC)
+ return 0;
+
+ if (igtk_elem == NULL) {
+ wpa_printf(MSG_DEBUG, "FT: No IGTK included in FTIE");
+ return 0;
+ }
+
+ wpa_hexdump_key(MSG_DEBUG, "FT: Received IGTK in Reassoc Resp",
+ igtk_elem, igtk_elem_len);
+
+ if (igtk_elem_len != 2 + 6 + 1 + WPA_IGTK_LEN + 8) {
+ wpa_printf(MSG_DEBUG, "FT: Invalid IGTK sub-elem "
+ "length %lu", (unsigned long) igtk_elem_len);
+ return -1;
+ }
+ if (igtk_elem[8] != WPA_IGTK_LEN) {
+ wpa_printf(MSG_DEBUG, "FT: Invalid IGTK sub-elem Key Length "
+ "%d", igtk_elem[8]);
+ return -1;
+ }
+
+ if (aes_unwrap(sm->ptk.kek, sm->ptk.kek_len, WPA_IGTK_LEN / 8,
+ igtk_elem + 9, igtk)) {
+ wpa_printf(MSG_WARNING, "FT: AES unwrap failed - could not "
+ "decrypt IGTK");
+ return -1;
+ }
+
+ /* KeyID[2] | IPN[6] | Key Length[1] | Key[16+8] */
+
+ keyidx = WPA_GET_LE16(igtk_elem);
+
+ wpa_hexdump_key(MSG_DEBUG, "FT: IGTK from Reassoc Resp", igtk,
+ WPA_IGTK_LEN);
+ if (wpa_sm_set_key(sm, WPA_ALG_IGTK, broadcast_ether_addr, keyidx, 0,
+ igtk_elem + 2, 6, igtk, WPA_IGTK_LEN) < 0) {
+ wpa_printf(MSG_WARNING, "WPA: Failed to set IGTK to the "
+ "driver.");
+ return -1;
+ }
+
+ return 0;
+}
+#endif /* CONFIG_IEEE80211W */
+
+
+int wpa_ft_validate_reassoc_resp(struct wpa_sm *sm, const u8 *ies,
+ size_t ies_len, const u8 *src_addr)
+{
+ struct wpa_ft_ies parse;
+ struct rsn_mdie *mdie;
+ struct rsn_ftie *ftie;
+ unsigned int count;
+ u8 mic[WPA_EAPOL_KEY_MIC_MAX_LEN];
+
+ wpa_hexdump(MSG_DEBUG, "FT: Response IEs", ies, ies_len);
+
+ if (!wpa_key_mgmt_ft(sm->key_mgmt)) {
+ wpa_printf(MSG_DEBUG, "FT: Reject FT IEs since FT is not "
+ "enabled for this connection");
+ return -1;
+ }
+
+ if (wpa_ft_parse_ies(ies, ies_len, &parse) < 0) {
+ wpa_printf(MSG_DEBUG, "FT: Failed to parse IEs");
+ return -1;
+ }
+
+ mdie = (struct rsn_mdie *) parse.mdie;
+ if (mdie == NULL || parse.mdie_len < sizeof(*mdie) ||
+ os_memcmp(mdie->mobility_domain, sm->mobility_domain,
+ MOBILITY_DOMAIN_ID_LEN) != 0) {
+ wpa_printf(MSG_DEBUG, "FT: Invalid MDIE");
+ return -1;
+ }
+
+ ftie = (struct rsn_ftie *) parse.ftie;
+ if (ftie == NULL || parse.ftie_len < sizeof(*ftie)) {
+ wpa_printf(MSG_DEBUG, "FT: Invalid FTIE");
+ return -1;
+ }
+
+ if (os_memcmp(ftie->snonce, sm->snonce, WPA_NONCE_LEN) != 0) {
+ wpa_printf(MSG_DEBUG, "FT: SNonce mismatch in FTIE");
+ wpa_hexdump(MSG_DEBUG, "FT: Received SNonce",
+ ftie->snonce, WPA_NONCE_LEN);
+ wpa_hexdump(MSG_DEBUG, "FT: Expected SNonce",
+ sm->snonce, WPA_NONCE_LEN);
+ return -1;
+ }
+
+ if (os_memcmp(ftie->anonce, sm->anonce, WPA_NONCE_LEN) != 0) {
+ wpa_printf(MSG_DEBUG, "FT: ANonce mismatch in FTIE");
+ wpa_hexdump(MSG_DEBUG, "FT: Received ANonce",
+ ftie->anonce, WPA_NONCE_LEN);
+ wpa_hexdump(MSG_DEBUG, "FT: Expected ANonce",
+ sm->anonce, WPA_NONCE_LEN);
+ return -1;
+ }
+
+ if (parse.r0kh_id == NULL) {
+ wpa_printf(MSG_DEBUG, "FT: No R0KH-ID subelem in FTIE");
+ return -1;
+ }
+
+ if (parse.r0kh_id_len != sm->r0kh_id_len ||
+ os_memcmp_const(parse.r0kh_id, sm->r0kh_id, parse.r0kh_id_len) != 0)
+ {
+ wpa_printf(MSG_DEBUG, "FT: R0KH-ID in FTIE did not match with "
+ "the current R0KH-ID");
+ wpa_hexdump(MSG_DEBUG, "FT: R0KH-ID in FTIE",
+ parse.r0kh_id, parse.r0kh_id_len);
+ wpa_hexdump(MSG_DEBUG, "FT: The current R0KH-ID",
+ sm->r0kh_id, sm->r0kh_id_len);
+ return -1;
+ }
+
+ if (parse.r1kh_id == NULL) {
+ wpa_printf(MSG_DEBUG, "FT: No R1KH-ID subelem in FTIE");
+ return -1;
+ }
+
+ if (os_memcmp_const(parse.r1kh_id, sm->r1kh_id, FT_R1KH_ID_LEN) != 0) {
+ wpa_printf(MSG_DEBUG, "FT: Unknown R1KH-ID used in "
+ "ReassocResp");
+ return -1;
+ }
+
+ if (parse.rsn_pmkid == NULL ||
+ os_memcmp_const(parse.rsn_pmkid, sm->pmk_r1_name, WPA_PMK_NAME_LEN))
+ {
+ wpa_printf(MSG_DEBUG, "FT: No matching PMKR1Name (PMKID) in "
+ "RSNIE (pmkid=%d)", !!parse.rsn_pmkid);
+ return -1;
+ }
+
+ count = 3;
+ if (parse.ric)
+ count += ieee802_11_ie_count(parse.ric, parse.ric_len);
+ if (ftie->mic_control[1] != count) {
+ wpa_printf(MSG_DEBUG, "FT: Unexpected IE count in MIC "
+ "Control: received %u expected %u",
+ ftie->mic_control[1], count);
+ return -1;
+ }
+
+ if (wpa_ft_mic(sm->ptk.kck, sm->ptk.kck_len, sm->own_addr, src_addr, 6,
+ parse.mdie - 2, parse.mdie_len + 2,
+ parse.ftie - 2, parse.ftie_len + 2,
+ parse.rsn - 2, parse.rsn_len + 2,
+ parse.ric, parse.ric_len,
+ mic) < 0) {
+ wpa_printf(MSG_DEBUG, "FT: Failed to calculate MIC");
+ return -1;
+ }
+
+ if (os_memcmp_const(mic, ftie->mic, 16) != 0) {
+ wpa_printf(MSG_DEBUG, "FT: Invalid MIC in FTIE");
+ wpa_hexdump(MSG_MSGDUMP, "FT: Received MIC", ftie->mic, 16);
+ wpa_hexdump(MSG_MSGDUMP, "FT: Calculated MIC", mic, 16);
+ return -1;
+ }
+
+ if (wpa_ft_process_gtk_subelem(sm, parse.gtk, parse.gtk_len) < 0)
+ return -1;
+
+#ifdef CONFIG_IEEE80211W
+ if (wpa_ft_process_igtk_subelem(sm, parse.igtk, parse.igtk_len) < 0)
+ return -1;
+#endif /* CONFIG_IEEE80211W */
+
+ if (sm->set_ptk_after_assoc) {
+ wpa_printf(MSG_DEBUG, "FT: Try to set PTK again now that we "
+ "are associated");
+ if (wpa_ft_install_ptk(sm, src_addr) < 0)
+ return -1;
+ sm->set_ptk_after_assoc = 0;
+ }
+
+ if (parse.ric) {
+ wpa_hexdump(MSG_MSGDUMP, "FT: RIC Response",
+ parse.ric, parse.ric_len);
+ /* TODO: parse response and inform driver about results when
+ * using wpa_supplicant SME */
+ }
+
+ wpa_printf(MSG_DEBUG, "FT: Completed successfully");
+
+ return 0;
+}
+
+
+/**
+ * wpa_ft_start_over_ds - Generate over-the-DS auth request
+ * @sm: Pointer to WPA state machine data from wpa_sm_init()
+ * @target_ap: Target AP Address
+ * @mdie: Mobility Domain IE from the target AP
+ * Returns: 0 on success, -1 on failure
+ */
+int wpa_ft_start_over_ds(struct wpa_sm *sm, const u8 *target_ap,
+ const u8 *mdie)
+{
+ u8 *ft_ies;
+ size_t ft_ies_len;
+
+ wpa_printf(MSG_DEBUG, "FT: Request over-the-DS with " MACSTR,
+ MAC2STR(target_ap));
+
+ /* Generate a new SNonce */
+ if (random_get_bytes(sm->snonce, WPA_NONCE_LEN)) {
+ wpa_printf(MSG_INFO, "FT: Failed to generate a new SNonce");
+ return -1;
+ }
+
+ ft_ies = wpa_ft_gen_req_ies(sm, &ft_ies_len, NULL, sm->pmk_r0_name,
+ NULL, 0, target_ap, NULL, 0, mdie);
+ if (ft_ies) {
+ sm->over_the_ds_in_progress = 1;
+ os_memcpy(sm->target_ap, target_ap, ETH_ALEN);
+ wpa_sm_send_ft_action(sm, 1, target_ap, ft_ies, ft_ies_len);
+ os_free(ft_ies);
+ }
+
+ return 0;
+}
+
+#endif /* CONFIG_IEEE80211R */
diff --git a/freebsd/contrib/wpa/src/rsn_supp/wpa_i.h b/freebsd/contrib/wpa/src/rsn_supp/wpa_i.h
new file mode 100644
index 00000000..965a9c1d
--- /dev/null
+++ b/freebsd/contrib/wpa/src/rsn_supp/wpa_i.h
@@ -0,0 +1,371 @@
+/*
+ * Internal WPA/RSN supplicant state machine definitions
+ * Copyright (c) 2004-2015, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef WPA_I_H
+#define WPA_I_H
+
+#include "utils/list.h"
+
+struct wpa_peerkey;
+struct wpa_tdls_peer;
+struct wpa_eapol_key;
+
+/**
+ * struct wpa_sm - Internal WPA state machine data
+ */
+struct wpa_sm {
+ u8 pmk[PMK_LEN];
+ size_t pmk_len;
+ struct wpa_ptk ptk, tptk;
+ int ptk_set, tptk_set;
+ unsigned int msg_3_of_4_ok:1;
+ u8 snonce[WPA_NONCE_LEN];
+ u8 anonce[WPA_NONCE_LEN]; /* ANonce from the last 1/4 msg */
+ int renew_snonce;
+ u8 rx_replay_counter[WPA_REPLAY_COUNTER_LEN];
+ int rx_replay_counter_set;
+ u8 request_counter[WPA_REPLAY_COUNTER_LEN];
+
+ struct eapol_sm *eapol; /* EAPOL state machine from upper level code */
+
+ struct rsn_pmksa_cache *pmksa; /* PMKSA cache */
+ struct rsn_pmksa_cache_entry *cur_pmksa; /* current PMKSA entry */
+ struct dl_list pmksa_candidates;
+
+ struct l2_packet_data *l2_preauth;
+ struct l2_packet_data *l2_preauth_br;
+ struct l2_packet_data *l2_tdls;
+ u8 preauth_bssid[ETH_ALEN]; /* current RSN pre-auth peer or
+ * 00:00:00:00:00:00 if no pre-auth is
+ * in progress */
+ struct eapol_sm *preauth_eapol;
+
+ struct wpa_sm_ctx *ctx;
+
+ void *scard_ctx; /* context for smartcard callbacks */
+ int fast_reauth; /* whether EAP fast re-authentication is enabled */
+
+ void *network_ctx;
+ int peerkey_enabled;
+ int allowed_pairwise_cipher; /* bitfield of WPA_CIPHER_* */
+ int proactive_key_caching;
+ int eap_workaround;
+ void *eap_conf_ctx;
+ u8 ssid[32];
+ size_t ssid_len;
+ int wpa_ptk_rekey;
+ int p2p;
+
+ u8 own_addr[ETH_ALEN];
+ const char *ifname;
+ const char *bridge_ifname;
+ u8 bssid[ETH_ALEN];
+
+ unsigned int dot11RSNAConfigPMKLifetime;
+ unsigned int dot11RSNAConfigPMKReauthThreshold;
+ unsigned int dot11RSNAConfigSATimeout;
+
+ unsigned int dot11RSNA4WayHandshakeFailures;
+
+ /* Selected configuration (based on Beacon/ProbeResp WPA IE) */
+ unsigned int proto;
+ unsigned int pairwise_cipher;
+ unsigned int group_cipher;
+ unsigned int key_mgmt;
+ unsigned int mgmt_group_cipher;
+
+ int rsn_enabled; /* Whether RSN is enabled in configuration */
+ int mfp; /* 0 = disabled, 1 = optional, 2 = mandatory */
+
+ u8 *assoc_wpa_ie; /* Own WPA/RSN IE from (Re)AssocReq */
+ size_t assoc_wpa_ie_len;
+ u8 *ap_wpa_ie, *ap_rsn_ie;
+ size_t ap_wpa_ie_len, ap_rsn_ie_len;
+
+#ifdef CONFIG_PEERKEY
+ struct wpa_peerkey *peerkey;
+#endif /* CONFIG_PEERKEY */
+#ifdef CONFIG_TDLS
+ struct wpa_tdls_peer *tdls;
+ int tdls_prohibited;
+ int tdls_chan_switch_prohibited;
+ int tdls_disabled;
+
+ /* The driver supports TDLS */
+ int tdls_supported;
+
+ /*
+ * The driver requires explicit discovery/setup/teardown frames sent
+ * to it via tdls_mgmt.
+ */
+ int tdls_external_setup;
+
+ /* The driver supports TDLS channel switching */
+ int tdls_chan_switch;
+#endif /* CONFIG_TDLS */
+
+#ifdef CONFIG_IEEE80211R
+ u8 xxkey[PMK_LEN]; /* PSK or the second 256 bits of MSK */
+ size_t xxkey_len;
+ u8 pmk_r0[PMK_LEN];
+ u8 pmk_r0_name[WPA_PMK_NAME_LEN];
+ u8 pmk_r1[PMK_LEN];
+ u8 pmk_r1_name[WPA_PMK_NAME_LEN];
+ u8 mobility_domain[MOBILITY_DOMAIN_ID_LEN];
+ u8 r0kh_id[FT_R0KH_ID_MAX_LEN];
+ size_t r0kh_id_len;
+ u8 r1kh_id[FT_R1KH_ID_LEN];
+ int ft_completed;
+ int over_the_ds_in_progress;
+ u8 target_ap[ETH_ALEN]; /* over-the-DS target AP */
+ int set_ptk_after_assoc;
+ u8 mdie_ft_capab; /* FT Capability and Policy from target AP MDIE */
+ u8 *assoc_resp_ies; /* MDIE and FTIE from (Re)Association Response */
+ size_t assoc_resp_ies_len;
+#endif /* CONFIG_IEEE80211R */
+
+#ifdef CONFIG_P2P
+ u8 p2p_ip_addr[3 * 4];
+#endif /* CONFIG_P2P */
+};
+
+
+static inline void wpa_sm_set_state(struct wpa_sm *sm, enum wpa_states state)
+{
+ WPA_ASSERT(sm->ctx->set_state);
+ sm->ctx->set_state(sm->ctx->ctx, state);
+}
+
+static inline enum wpa_states wpa_sm_get_state(struct wpa_sm *sm)
+{
+ WPA_ASSERT(sm->ctx->get_state);
+ return sm->ctx->get_state(sm->ctx->ctx);
+}
+
+static inline void wpa_sm_deauthenticate(struct wpa_sm *sm, int reason_code)
+{
+ WPA_ASSERT(sm->ctx->deauthenticate);
+ sm->ctx->deauthenticate(sm->ctx->ctx, reason_code);
+}
+
+static inline int wpa_sm_set_key(struct wpa_sm *sm, enum wpa_alg alg,
+ const u8 *addr, int key_idx, int set_tx,
+ const u8 *seq, size_t seq_len,
+ const u8 *key, size_t key_len)
+{
+ WPA_ASSERT(sm->ctx->set_key);
+ return sm->ctx->set_key(sm->ctx->ctx, alg, addr, key_idx, set_tx,
+ seq, seq_len, key, key_len);
+}
+
+static inline void * wpa_sm_get_network_ctx(struct wpa_sm *sm)
+{
+ WPA_ASSERT(sm->ctx->get_network_ctx);
+ return sm->ctx->get_network_ctx(sm->ctx->ctx);
+}
+
+static inline int wpa_sm_get_bssid(struct wpa_sm *sm, u8 *bssid)
+{
+ WPA_ASSERT(sm->ctx->get_bssid);
+ return sm->ctx->get_bssid(sm->ctx->ctx, bssid);
+}
+
+static inline int wpa_sm_ether_send(struct wpa_sm *sm, const u8 *dest,
+ u16 proto, const u8 *buf, size_t len)
+{
+ WPA_ASSERT(sm->ctx->ether_send);
+ return sm->ctx->ether_send(sm->ctx->ctx, dest, proto, buf, len);
+}
+
+static inline int wpa_sm_get_beacon_ie(struct wpa_sm *sm)
+{
+ WPA_ASSERT(sm->ctx->get_beacon_ie);
+ return sm->ctx->get_beacon_ie(sm->ctx->ctx);
+}
+
+static inline void wpa_sm_cancel_auth_timeout(struct wpa_sm *sm)
+{
+ WPA_ASSERT(sm->ctx->cancel_auth_timeout);
+ sm->ctx->cancel_auth_timeout(sm->ctx->ctx);
+}
+
+static inline u8 * wpa_sm_alloc_eapol(struct wpa_sm *sm, u8 type,
+ const void *data, u16 data_len,
+ size_t *msg_len, void **data_pos)
+{
+ WPA_ASSERT(sm->ctx->alloc_eapol);
+ return sm->ctx->alloc_eapol(sm->ctx->ctx, type, data, data_len,
+ msg_len, data_pos);
+}
+
+static inline int wpa_sm_add_pmkid(struct wpa_sm *sm, const u8 *bssid,
+ const u8 *pmkid)
+{
+ WPA_ASSERT(sm->ctx->add_pmkid);
+ return sm->ctx->add_pmkid(sm->ctx->ctx, bssid, pmkid);
+}
+
+static inline int wpa_sm_remove_pmkid(struct wpa_sm *sm, const u8 *bssid,
+ const u8 *pmkid)
+{
+ WPA_ASSERT(sm->ctx->remove_pmkid);
+ return sm->ctx->remove_pmkid(sm->ctx->ctx, bssid, pmkid);
+}
+
+static inline int wpa_sm_mlme_setprotection(struct wpa_sm *sm, const u8 *addr,
+ int protect_type, int key_type)
+{
+ WPA_ASSERT(sm->ctx->mlme_setprotection);
+ return sm->ctx->mlme_setprotection(sm->ctx->ctx, addr, protect_type,
+ key_type);
+}
+
+static inline int wpa_sm_update_ft_ies(struct wpa_sm *sm, const u8 *md,
+ const u8 *ies, size_t ies_len)
+{
+ if (sm->ctx->update_ft_ies)
+ return sm->ctx->update_ft_ies(sm->ctx->ctx, md, ies, ies_len);
+ return -1;
+}
+
+static inline int wpa_sm_send_ft_action(struct wpa_sm *sm, u8 action,
+ const u8 *target_ap,
+ const u8 *ies, size_t ies_len)
+{
+ if (sm->ctx->send_ft_action)
+ return sm->ctx->send_ft_action(sm->ctx->ctx, action, target_ap,
+ ies, ies_len);
+ return -1;
+}
+
+static inline int wpa_sm_mark_authenticated(struct wpa_sm *sm,
+ const u8 *target_ap)
+{
+ if (sm->ctx->mark_authenticated)
+ return sm->ctx->mark_authenticated(sm->ctx->ctx, target_ap);
+ return -1;
+}
+
+static inline void wpa_sm_set_rekey_offload(struct wpa_sm *sm)
+{
+ if (!sm->ctx->set_rekey_offload)
+ return;
+ sm->ctx->set_rekey_offload(sm->ctx->ctx, sm->ptk.kek, sm->ptk.kek_len,
+ sm->ptk.kck, sm->ptk.kck_len,
+ sm->rx_replay_counter);
+}
+
+#ifdef CONFIG_TDLS
+static inline int wpa_sm_tdls_get_capa(struct wpa_sm *sm,
+ int *tdls_supported,
+ int *tdls_ext_setup,
+ int *tdls_chan_switch)
+{
+ if (sm->ctx->tdls_get_capa)
+ return sm->ctx->tdls_get_capa(sm->ctx->ctx, tdls_supported,
+ tdls_ext_setup, tdls_chan_switch);
+ return -1;
+}
+
+static inline int wpa_sm_send_tdls_mgmt(struct wpa_sm *sm, const u8 *dst,
+ u8 action_code, u8 dialog_token,
+ u16 status_code, u32 peer_capab,
+ int initiator, const u8 *buf,
+ size_t len)
+{
+ if (sm->ctx->send_tdls_mgmt)
+ return sm->ctx->send_tdls_mgmt(sm->ctx->ctx, dst, action_code,
+ dialog_token, status_code,
+ peer_capab, initiator, buf,
+ len);
+ return -1;
+}
+
+static inline int wpa_sm_tdls_oper(struct wpa_sm *sm, int oper,
+ const u8 *peer)
+{
+ if (sm->ctx->tdls_oper)
+ return sm->ctx->tdls_oper(sm->ctx->ctx, oper, peer);
+ return -1;
+}
+
+static inline int
+wpa_sm_tdls_peer_addset(struct wpa_sm *sm, const u8 *addr, int add,
+ u16 aid, u16 capability, const u8 *supp_rates,
+ size_t supp_rates_len,
+ const struct ieee80211_ht_capabilities *ht_capab,
+ const struct ieee80211_vht_capabilities *vht_capab,
+ u8 qosinfo, int wmm, const u8 *ext_capab,
+ size_t ext_capab_len, const u8 *supp_channels,
+ size_t supp_channels_len, const u8 *supp_oper_classes,
+ size_t supp_oper_classes_len)
+{
+ if (sm->ctx->tdls_peer_addset)
+ return sm->ctx->tdls_peer_addset(sm->ctx->ctx, addr, add,
+ aid, capability, supp_rates,
+ supp_rates_len, ht_capab,
+ vht_capab, qosinfo, wmm,
+ ext_capab, ext_capab_len,
+ supp_channels,
+ supp_channels_len,
+ supp_oper_classes,
+ supp_oper_classes_len);
+ return -1;
+}
+
+static inline int
+wpa_sm_tdls_enable_channel_switch(struct wpa_sm *sm, const u8 *addr,
+ u8 oper_class,
+ const struct hostapd_freq_params *freq_params)
+{
+ if (sm->ctx->tdls_enable_channel_switch)
+ return sm->ctx->tdls_enable_channel_switch(sm->ctx->ctx, addr,
+ oper_class,
+ freq_params);
+ return -1;
+}
+
+static inline int
+wpa_sm_tdls_disable_channel_switch(struct wpa_sm *sm, const u8 *addr)
+{
+ if (sm->ctx->tdls_disable_channel_switch)
+ return sm->ctx->tdls_disable_channel_switch(sm->ctx->ctx, addr);
+ return -1;
+}
+#endif /* CONFIG_TDLS */
+
+static inline int wpa_sm_key_mgmt_set_pmk(struct wpa_sm *sm,
+ const u8 *pmk, size_t pmk_len)
+{
+ if (!sm->proactive_key_caching)
+ return 0;
+ if (!sm->ctx->key_mgmt_set_pmk)
+ return -1;
+ return sm->ctx->key_mgmt_set_pmk(sm->ctx->ctx, pmk, pmk_len);
+}
+
+void wpa_eapol_key_send(struct wpa_sm *sm, const u8 *kck, size_t kck_len,
+ int ver, const u8 *dest, u16 proto,
+ u8 *msg, size_t msg_len, u8 *key_mic);
+int wpa_supplicant_send_2_of_4(struct wpa_sm *sm, const unsigned char *dst,
+ const struct wpa_eapol_key *key,
+ int ver, const u8 *nonce,
+ const u8 *wpa_ie, size_t wpa_ie_len,
+ struct wpa_ptk *ptk);
+int wpa_supplicant_send_4_of_4(struct wpa_sm *sm, const unsigned char *dst,
+ const struct wpa_eapol_key *key,
+ u16 ver, u16 key_info,
+ struct wpa_ptk *ptk);
+
+int wpa_derive_ptk_ft(struct wpa_sm *sm, const unsigned char *src_addr,
+ const struct wpa_eapol_key *key, struct wpa_ptk *ptk);
+
+void wpa_tdls_assoc(struct wpa_sm *sm);
+void wpa_tdls_disassoc(struct wpa_sm *sm);
+
+#endif /* WPA_I_H */
diff --git a/freebsd/contrib/wpa/src/rsn_supp/wpa_ie.c b/freebsd/contrib/wpa/src/rsn_supp/wpa_ie.c
new file mode 100644
index 00000000..2a22d7de
--- /dev/null
+++ b/freebsd/contrib/wpa/src/rsn_supp/wpa_ie.c
@@ -0,0 +1,607 @@
+#include <machine/rtems-bsd-user-space.h>
+
+/*
+ * wpa_supplicant - WPA/RSN IE and KDE processing
+ * Copyright (c) 2003-2015, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "wpa.h"
+#include "pmksa_cache.h"
+#include "common/ieee802_11_defs.h"
+#include "wpa_i.h"
+#include "wpa_ie.h"
+
+
+/**
+ * wpa_parse_wpa_ie - Parse WPA/RSN IE
+ * @wpa_ie: Pointer to WPA or RSN IE
+ * @wpa_ie_len: Length of the WPA/RSN IE
+ * @data: Pointer to data area for parsing results
+ * Returns: 0 on success, -1 on failure
+ *
+ * Parse the contents of WPA or RSN IE and write the parsed data into data.
+ */
+int wpa_parse_wpa_ie(const u8 *wpa_ie, size_t wpa_ie_len,
+ struct wpa_ie_data *data)
+{
+ if (wpa_ie_len >= 1 && wpa_ie[0] == WLAN_EID_RSN)
+ return wpa_parse_wpa_ie_rsn(wpa_ie, wpa_ie_len, data);
+ if (wpa_ie_len >= 6 && wpa_ie[0] == WLAN_EID_VENDOR_SPECIFIC &&
+ wpa_ie[1] >= 4 && WPA_GET_BE32(&wpa_ie[2]) == OSEN_IE_VENDOR_TYPE)
+ return wpa_parse_wpa_ie_rsn(wpa_ie, wpa_ie_len, data);
+ else
+ return wpa_parse_wpa_ie_wpa(wpa_ie, wpa_ie_len, data);
+}
+
+
+static int wpa_gen_wpa_ie_wpa(u8 *wpa_ie, size_t wpa_ie_len,
+ int pairwise_cipher, int group_cipher,
+ int key_mgmt)
+{
+ u8 *pos;
+ struct wpa_ie_hdr *hdr;
+ u32 suite;
+
+ if (wpa_ie_len < sizeof(*hdr) + WPA_SELECTOR_LEN +
+ 2 + WPA_SELECTOR_LEN + 2 + WPA_SELECTOR_LEN)
+ return -1;
+
+ hdr = (struct wpa_ie_hdr *) wpa_ie;
+ hdr->elem_id = WLAN_EID_VENDOR_SPECIFIC;
+ RSN_SELECTOR_PUT(hdr->oui, WPA_OUI_TYPE);
+ WPA_PUT_LE16(hdr->version, WPA_VERSION);
+ pos = (u8 *) (hdr + 1);
+
+ suite = wpa_cipher_to_suite(WPA_PROTO_WPA, group_cipher);
+ if (suite == 0) {
+ wpa_printf(MSG_WARNING, "Invalid group cipher (%d).",
+ group_cipher);
+ return -1;
+ }
+ RSN_SELECTOR_PUT(pos, suite);
+ pos += WPA_SELECTOR_LEN;
+
+ *pos++ = 1;
+ *pos++ = 0;
+ suite = wpa_cipher_to_suite(WPA_PROTO_WPA, pairwise_cipher);
+ if (suite == 0 ||
+ (!wpa_cipher_valid_pairwise(pairwise_cipher) &&
+ pairwise_cipher != WPA_CIPHER_NONE)) {
+ wpa_printf(MSG_WARNING, "Invalid pairwise cipher (%d).",
+ pairwise_cipher);
+ return -1;
+ }
+ RSN_SELECTOR_PUT(pos, suite);
+ pos += WPA_SELECTOR_LEN;
+
+ *pos++ = 1;
+ *pos++ = 0;
+ if (key_mgmt == WPA_KEY_MGMT_IEEE8021X) {
+ RSN_SELECTOR_PUT(pos, WPA_AUTH_KEY_MGMT_UNSPEC_802_1X);
+ } else if (key_mgmt == WPA_KEY_MGMT_PSK) {
+ RSN_SELECTOR_PUT(pos, WPA_AUTH_KEY_MGMT_PSK_OVER_802_1X);
+ } else if (key_mgmt == WPA_KEY_MGMT_WPA_NONE) {
+ RSN_SELECTOR_PUT(pos, WPA_AUTH_KEY_MGMT_NONE);
+ } else if (key_mgmt == WPA_KEY_MGMT_CCKM) {
+ RSN_SELECTOR_PUT(pos, WPA_AUTH_KEY_MGMT_CCKM);
+ } else {
+ wpa_printf(MSG_WARNING, "Invalid key management type (%d).",
+ key_mgmt);
+ return -1;
+ }
+ pos += WPA_SELECTOR_LEN;
+
+ /* WPA Capabilities; use defaults, so no need to include it */
+
+ hdr->len = (pos - wpa_ie) - 2;
+
+ WPA_ASSERT((size_t) (pos - wpa_ie) <= wpa_ie_len);
+
+ return pos - wpa_ie;
+}
+
+
+static int wpa_gen_wpa_ie_rsn(u8 *rsn_ie, size_t rsn_ie_len,
+ int pairwise_cipher, int group_cipher,
+ int key_mgmt, int mgmt_group_cipher,
+ struct wpa_sm *sm)
+{
+ u8 *pos;
+ struct rsn_ie_hdr *hdr;
+ u16 capab;
+ u32 suite;
+
+ if (rsn_ie_len < sizeof(*hdr) + RSN_SELECTOR_LEN +
+ 2 + RSN_SELECTOR_LEN + 2 + RSN_SELECTOR_LEN + 2 +
+ (sm->cur_pmksa ? 2 + PMKID_LEN : 0)) {
+ wpa_printf(MSG_DEBUG, "RSN: Too short IE buffer (%lu bytes)",
+ (unsigned long) rsn_ie_len);
+ return -1;
+ }
+
+ hdr = (struct rsn_ie_hdr *) rsn_ie;
+ hdr->elem_id = WLAN_EID_RSN;
+ WPA_PUT_LE16(hdr->version, RSN_VERSION);
+ pos = (u8 *) (hdr + 1);
+
+ suite = wpa_cipher_to_suite(WPA_PROTO_RSN, group_cipher);
+ if (suite == 0) {
+ wpa_printf(MSG_WARNING, "Invalid group cipher (%d).",
+ group_cipher);
+ return -1;
+ }
+ RSN_SELECTOR_PUT(pos, suite);
+ pos += RSN_SELECTOR_LEN;
+
+ *pos++ = 1;
+ *pos++ = 0;
+ suite = wpa_cipher_to_suite(WPA_PROTO_RSN, pairwise_cipher);
+ if (suite == 0 ||
+ (!wpa_cipher_valid_pairwise(pairwise_cipher) &&
+ pairwise_cipher != WPA_CIPHER_NONE)) {
+ wpa_printf(MSG_WARNING, "Invalid pairwise cipher (%d).",
+ pairwise_cipher);
+ return -1;
+ }
+ RSN_SELECTOR_PUT(pos, suite);
+ pos += RSN_SELECTOR_LEN;
+
+ *pos++ = 1;
+ *pos++ = 0;
+ if (key_mgmt == WPA_KEY_MGMT_IEEE8021X) {
+ RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_UNSPEC_802_1X);
+ } else if (key_mgmt == WPA_KEY_MGMT_PSK) {
+ RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_PSK_OVER_802_1X);
+ } else if (key_mgmt == WPA_KEY_MGMT_CCKM) {
+ RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_CCKM);
+#ifdef CONFIG_IEEE80211R
+ } else if (key_mgmt == WPA_KEY_MGMT_FT_IEEE8021X) {
+ RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_802_1X);
+ } else if (key_mgmt == WPA_KEY_MGMT_FT_PSK) {
+ RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_PSK);
+#endif /* CONFIG_IEEE80211R */
+#ifdef CONFIG_IEEE80211W
+ } else if (key_mgmt == WPA_KEY_MGMT_IEEE8021X_SHA256) {
+ RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_802_1X_SHA256);
+ } else if (key_mgmt == WPA_KEY_MGMT_PSK_SHA256) {
+ RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_PSK_SHA256);
+#endif /* CONFIG_IEEE80211W */
+#ifdef CONFIG_SAE
+ } else if (key_mgmt == WPA_KEY_MGMT_SAE) {
+ RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_SAE);
+ } else if (key_mgmt == WPA_KEY_MGMT_FT_SAE) {
+ RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_SAE);
+#endif /* CONFIG_SAE */
+ } else if (key_mgmt == WPA_KEY_MGMT_IEEE8021X_SUITE_B_192) {
+ RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_802_1X_SUITE_B_192);
+ } else if (key_mgmt == WPA_KEY_MGMT_IEEE8021X_SUITE_B) {
+ RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_802_1X_SUITE_B);
+ } else {
+ wpa_printf(MSG_WARNING, "Invalid key management type (%d).",
+ key_mgmt);
+ return -1;
+ }
+ pos += RSN_SELECTOR_LEN;
+
+ /* RSN Capabilities */
+ capab = 0;
+#ifdef CONFIG_IEEE80211W
+ if (sm->mfp)
+ capab |= WPA_CAPABILITY_MFPC;
+ if (sm->mfp == 2)
+ capab |= WPA_CAPABILITY_MFPR;
+#endif /* CONFIG_IEEE80211W */
+ WPA_PUT_LE16(pos, capab);
+ pos += 2;
+
+ if (sm->cur_pmksa) {
+ /* PMKID Count (2 octets, little endian) */
+ *pos++ = 1;
+ *pos++ = 0;
+ /* PMKID */
+ os_memcpy(pos, sm->cur_pmksa->pmkid, PMKID_LEN);
+ pos += PMKID_LEN;
+ }
+
+#ifdef CONFIG_IEEE80211W
+ if (wpa_cipher_valid_mgmt_group(mgmt_group_cipher)) {
+ if (!sm->cur_pmksa) {
+ /* PMKID Count */
+ WPA_PUT_LE16(pos, 0);
+ pos += 2;
+ }
+
+ /* Management Group Cipher Suite */
+ RSN_SELECTOR_PUT(pos, wpa_cipher_to_suite(WPA_PROTO_RSN,
+ mgmt_group_cipher));
+ pos += RSN_SELECTOR_LEN;
+ }
+#endif /* CONFIG_IEEE80211W */
+
+ hdr->len = (pos - rsn_ie) - 2;
+
+ WPA_ASSERT((size_t) (pos - rsn_ie) <= rsn_ie_len);
+
+ return pos - rsn_ie;
+}
+
+
+#ifdef CONFIG_HS20
+static int wpa_gen_wpa_ie_osen(u8 *wpa_ie, size_t wpa_ie_len,
+ int pairwise_cipher, int group_cipher,
+ int key_mgmt)
+{
+ u8 *pos, *len;
+ u32 suite;
+
+ if (wpa_ie_len < 2 + 4 + RSN_SELECTOR_LEN +
+ 2 + RSN_SELECTOR_LEN + 2 + RSN_SELECTOR_LEN)
+ return -1;
+
+ pos = wpa_ie;
+ *pos++ = WLAN_EID_VENDOR_SPECIFIC;
+ len = pos++; /* to be filled */
+ WPA_PUT_BE24(pos, OUI_WFA);
+ pos += 3;
+ *pos++ = HS20_OSEN_OUI_TYPE;
+
+ /* Group Data Cipher Suite */
+ suite = wpa_cipher_to_suite(WPA_PROTO_RSN, group_cipher);
+ if (suite == 0) {
+ wpa_printf(MSG_WARNING, "Invalid group cipher (%d).",
+ group_cipher);
+ return -1;
+ }
+ RSN_SELECTOR_PUT(pos, suite);
+ pos += RSN_SELECTOR_LEN;
+
+ /* Pairwise Cipher Suite Count and List */
+ WPA_PUT_LE16(pos, 1);
+ pos += 2;
+ suite = wpa_cipher_to_suite(WPA_PROTO_RSN, pairwise_cipher);
+ if (suite == 0 ||
+ (!wpa_cipher_valid_pairwise(pairwise_cipher) &&
+ pairwise_cipher != WPA_CIPHER_NONE)) {
+ wpa_printf(MSG_WARNING, "Invalid pairwise cipher (%d).",
+ pairwise_cipher);
+ return -1;
+ }
+ RSN_SELECTOR_PUT(pos, suite);
+ pos += RSN_SELECTOR_LEN;
+
+ /* AKM Suite Count and List */
+ WPA_PUT_LE16(pos, 1);
+ pos += 2;
+ RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_OSEN);
+ pos += RSN_SELECTOR_LEN;
+
+ *len = pos - len - 1;
+
+ WPA_ASSERT((size_t) (pos - wpa_ie) <= wpa_ie_len);
+
+ return pos - wpa_ie;
+}
+#endif /* CONFIG_HS20 */
+
+
+/**
+ * wpa_gen_wpa_ie - Generate WPA/RSN IE based on current security policy
+ * @sm: Pointer to WPA state machine data from wpa_sm_init()
+ * @wpa_ie: Pointer to memory area for the generated WPA/RSN IE
+ * @wpa_ie_len: Maximum length of the generated WPA/RSN IE
+ * Returns: Length of the generated WPA/RSN IE or -1 on failure
+ */
+int wpa_gen_wpa_ie(struct wpa_sm *sm, u8 *wpa_ie, size_t wpa_ie_len)
+{
+ if (sm->proto == WPA_PROTO_RSN)
+ return wpa_gen_wpa_ie_rsn(wpa_ie, wpa_ie_len,
+ sm->pairwise_cipher,
+ sm->group_cipher,
+ sm->key_mgmt, sm->mgmt_group_cipher,
+ sm);
+#ifdef CONFIG_HS20
+ else if (sm->proto == WPA_PROTO_OSEN)
+ return wpa_gen_wpa_ie_osen(wpa_ie, wpa_ie_len,
+ sm->pairwise_cipher,
+ sm->group_cipher,
+ sm->key_mgmt);
+#endif /* CONFIG_HS20 */
+ else
+ return wpa_gen_wpa_ie_wpa(wpa_ie, wpa_ie_len,
+ sm->pairwise_cipher,
+ sm->group_cipher,
+ sm->key_mgmt);
+}
+
+
+/**
+ * wpa_parse_vendor_specific - Parse Vendor Specific IEs
+ * @pos: Pointer to the IE header
+ * @end: Pointer to the end of the Key Data buffer
+ * @ie: Pointer to parsed IE data
+ * Returns: 0 on success, 1 if end mark is found, -1 on failure
+ */
+static int wpa_parse_vendor_specific(const u8 *pos, const u8 *end,
+ struct wpa_eapol_ie_parse *ie)
+{
+ unsigned int oui;
+
+ if (pos[1] < 4) {
+ wpa_printf(MSG_MSGDUMP, "Too short vendor specific IE ignored (len=%u)",
+ pos[1]);
+ return 1;
+ }
+
+ oui = WPA_GET_BE24(&pos[2]);
+ if (oui == OUI_MICROSOFT && pos[5] == WMM_OUI_TYPE && pos[1] > 4) {
+ if (pos[6] == WMM_OUI_SUBTYPE_INFORMATION_ELEMENT) {
+ ie->wmm = &pos[2];
+ ie->wmm_len = pos[1];
+ wpa_hexdump(MSG_DEBUG, "WPA: WMM IE",
+ ie->wmm, ie->wmm_len);
+ } else if (pos[6] == WMM_OUI_SUBTYPE_PARAMETER_ELEMENT) {
+ ie->wmm = &pos[2];
+ ie->wmm_len = pos[1];
+ wpa_hexdump(MSG_DEBUG, "WPA: WMM Parameter Element",
+ ie->wmm, ie->wmm_len);
+ }
+ }
+ return 0;
+}
+
+
+/**
+ * wpa_parse_generic - Parse EAPOL-Key Key Data Generic IEs
+ * @pos: Pointer to the IE header
+ * @end: Pointer to the end of the Key Data buffer
+ * @ie: Pointer to parsed IE data
+ * Returns: 0 on success, 1 if end mark is found, -1 on failure
+ */
+static int wpa_parse_generic(const u8 *pos, const u8 *end,
+ struct wpa_eapol_ie_parse *ie)
+{
+ if (pos[1] == 0)
+ return 1;
+
+ if (pos[1] >= 6 &&
+ RSN_SELECTOR_GET(pos + 2) == WPA_OUI_TYPE &&
+ pos[2 + WPA_SELECTOR_LEN] == 1 &&
+ pos[2 + WPA_SELECTOR_LEN + 1] == 0) {
+ ie->wpa_ie = pos;
+ ie->wpa_ie_len = pos[1] + 2;
+ wpa_hexdump(MSG_DEBUG, "WPA: WPA IE in EAPOL-Key",
+ ie->wpa_ie, ie->wpa_ie_len);
+ return 0;
+ }
+
+ if (pos + 1 + RSN_SELECTOR_LEN < end &&
+ pos[1] >= RSN_SELECTOR_LEN + PMKID_LEN &&
+ RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_PMKID) {
+ ie->pmkid = pos + 2 + RSN_SELECTOR_LEN;
+ wpa_hexdump(MSG_DEBUG, "WPA: PMKID in EAPOL-Key",
+ pos, pos[1] + 2);
+ return 0;
+ }
+
+ if (pos[1] > RSN_SELECTOR_LEN + 2 &&
+ RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_GROUPKEY) {
+ ie->gtk = pos + 2 + RSN_SELECTOR_LEN;
+ ie->gtk_len = pos[1] - RSN_SELECTOR_LEN;
+ wpa_hexdump_key(MSG_DEBUG, "WPA: GTK in EAPOL-Key",
+ pos, pos[1] + 2);
+ return 0;
+ }
+
+ if (pos[1] > RSN_SELECTOR_LEN + 2 &&
+ RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_MAC_ADDR) {
+ ie->mac_addr = pos + 2 + RSN_SELECTOR_LEN;
+ ie->mac_addr_len = pos[1] - RSN_SELECTOR_LEN;
+ wpa_hexdump(MSG_DEBUG, "WPA: MAC Address in EAPOL-Key",
+ pos, pos[1] + 2);
+ return 0;
+ }
+
+#ifdef CONFIG_PEERKEY
+ if (pos[1] > RSN_SELECTOR_LEN + 2 &&
+ RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_SMK) {
+ ie->smk = pos + 2 + RSN_SELECTOR_LEN;
+ ie->smk_len = pos[1] - RSN_SELECTOR_LEN;
+ wpa_hexdump_key(MSG_DEBUG, "WPA: SMK in EAPOL-Key",
+ pos, pos[1] + 2);
+ return 0;
+ }
+
+ if (pos[1] > RSN_SELECTOR_LEN + 2 &&
+ RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_NONCE) {
+ ie->nonce = pos + 2 + RSN_SELECTOR_LEN;
+ ie->nonce_len = pos[1] - RSN_SELECTOR_LEN;
+ wpa_hexdump(MSG_DEBUG, "WPA: Nonce in EAPOL-Key",
+ pos, pos[1] + 2);
+ return 0;
+ }
+
+ if (pos[1] > RSN_SELECTOR_LEN + 2 &&
+ RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_LIFETIME) {
+ ie->lifetime = pos + 2 + RSN_SELECTOR_LEN;
+ ie->lifetime_len = pos[1] - RSN_SELECTOR_LEN;
+ wpa_hexdump(MSG_DEBUG, "WPA: Lifetime in EAPOL-Key",
+ pos, pos[1] + 2);
+ return 0;
+ }
+
+ if (pos[1] > RSN_SELECTOR_LEN + 2 &&
+ RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_ERROR) {
+ ie->error = pos + 2 + RSN_SELECTOR_LEN;
+ ie->error_len = pos[1] - RSN_SELECTOR_LEN;
+ wpa_hexdump(MSG_DEBUG, "WPA: Error in EAPOL-Key",
+ pos, pos[1] + 2);
+ return 0;
+ }
+#endif /* CONFIG_PEERKEY */
+
+#ifdef CONFIG_IEEE80211W
+ if (pos[1] > RSN_SELECTOR_LEN + 2 &&
+ RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_IGTK) {
+ ie->igtk = pos + 2 + RSN_SELECTOR_LEN;
+ ie->igtk_len = pos[1] - RSN_SELECTOR_LEN;
+ wpa_hexdump_key(MSG_DEBUG, "WPA: IGTK in EAPOL-Key",
+ pos, pos[1] + 2);
+ return 0;
+ }
+#endif /* CONFIG_IEEE80211W */
+
+#ifdef CONFIG_P2P
+ if (pos[1] >= RSN_SELECTOR_LEN + 1 &&
+ RSN_SELECTOR_GET(pos + 2) == WFA_KEY_DATA_IP_ADDR_REQ) {
+ ie->ip_addr_req = pos + 2 + RSN_SELECTOR_LEN;
+ wpa_hexdump(MSG_DEBUG, "WPA: IP Address Request in EAPOL-Key",
+ ie->ip_addr_req, pos[1] - RSN_SELECTOR_LEN);
+ return 0;
+ }
+
+ if (pos[1] >= RSN_SELECTOR_LEN + 3 * 4 &&
+ RSN_SELECTOR_GET(pos + 2) == WFA_KEY_DATA_IP_ADDR_ALLOC) {
+ ie->ip_addr_alloc = pos + 2 + RSN_SELECTOR_LEN;
+ wpa_hexdump(MSG_DEBUG,
+ "WPA: IP Address Allocation in EAPOL-Key",
+ ie->ip_addr_alloc, pos[1] - RSN_SELECTOR_LEN);
+ return 0;
+ }
+#endif /* CONFIG_P2P */
+
+ return 0;
+}
+
+
+/**
+ * wpa_supplicant_parse_ies - Parse EAPOL-Key Key Data IEs
+ * @buf: Pointer to the Key Data buffer
+ * @len: Key Data Length
+ * @ie: Pointer to parsed IE data
+ * Returns: 0 on success, -1 on failure
+ */
+int wpa_supplicant_parse_ies(const u8 *buf, size_t len,
+ struct wpa_eapol_ie_parse *ie)
+{
+ const u8 *pos, *end;
+ int ret = 0;
+
+ os_memset(ie, 0, sizeof(*ie));
+ for (pos = buf, end = pos + len; pos + 1 < end; pos += 2 + pos[1]) {
+ if (pos[0] == 0xdd &&
+ ((pos == buf + len - 1) || pos[1] == 0)) {
+ /* Ignore padding */
+ break;
+ }
+ if (pos + 2 + pos[1] > end) {
+ wpa_printf(MSG_DEBUG, "WPA: EAPOL-Key Key Data "
+ "underflow (ie=%d len=%d pos=%d)",
+ pos[0], pos[1], (int) (pos - buf));
+ wpa_hexdump_key(MSG_DEBUG, "WPA: Key Data",
+ buf, len);
+ ret = -1;
+ break;
+ }
+ if (*pos == WLAN_EID_RSN) {
+ ie->rsn_ie = pos;
+ ie->rsn_ie_len = pos[1] + 2;
+ wpa_hexdump(MSG_DEBUG, "WPA: RSN IE in EAPOL-Key",
+ ie->rsn_ie, ie->rsn_ie_len);
+ } else if (*pos == WLAN_EID_MOBILITY_DOMAIN &&
+ pos[1] >= sizeof(struct rsn_mdie)) {
+ ie->mdie = pos;
+ ie->mdie_len = pos[1] + 2;
+ wpa_hexdump(MSG_DEBUG, "WPA: MDIE in EAPOL-Key",
+ ie->mdie, ie->mdie_len);
+ } else if (*pos == WLAN_EID_FAST_BSS_TRANSITION &&
+ pos[1] >= sizeof(struct rsn_ftie)) {
+ ie->ftie = pos;
+ ie->ftie_len = pos[1] + 2;
+ wpa_hexdump(MSG_DEBUG, "WPA: FTIE in EAPOL-Key",
+ ie->ftie, ie->ftie_len);
+ } else if (*pos == WLAN_EID_TIMEOUT_INTERVAL && pos[1] >= 5) {
+ if (pos[2] == WLAN_TIMEOUT_REASSOC_DEADLINE) {
+ ie->reassoc_deadline = pos;
+ wpa_hexdump(MSG_DEBUG, "WPA: Reassoc Deadline "
+ "in EAPOL-Key",
+ ie->reassoc_deadline, pos[1] + 2);
+ } else if (pos[2] == WLAN_TIMEOUT_KEY_LIFETIME) {
+ ie->key_lifetime = pos;
+ wpa_hexdump(MSG_DEBUG, "WPA: KeyLifetime "
+ "in EAPOL-Key",
+ ie->key_lifetime, pos[1] + 2);
+ } else {
+ wpa_hexdump(MSG_DEBUG, "WPA: Unrecognized "
+ "EAPOL-Key Key Data IE",
+ pos, 2 + pos[1]);
+ }
+ } else if (*pos == WLAN_EID_LINK_ID) {
+ if (pos[1] >= 18) {
+ ie->lnkid = pos;
+ ie->lnkid_len = pos[1] + 2;
+ }
+ } else if (*pos == WLAN_EID_EXT_CAPAB) {
+ ie->ext_capab = pos;
+ ie->ext_capab_len = pos[1] + 2;
+ } else if (*pos == WLAN_EID_SUPP_RATES) {
+ ie->supp_rates = pos;
+ ie->supp_rates_len = pos[1] + 2;
+ } else if (*pos == WLAN_EID_EXT_SUPP_RATES) {
+ ie->ext_supp_rates = pos;
+ ie->ext_supp_rates_len = pos[1] + 2;
+ } else if (*pos == WLAN_EID_HT_CAP &&
+ pos[1] >= sizeof(struct ieee80211_ht_capabilities)) {
+ ie->ht_capabilities = pos + 2;
+ } else if (*pos == WLAN_EID_VHT_AID) {
+ if (pos[1] >= 2)
+ ie->aid = WPA_GET_LE16(pos + 2) & 0x3fff;
+ } else if (*pos == WLAN_EID_VHT_CAP &&
+ pos[1] >= sizeof(struct ieee80211_vht_capabilities))
+ {
+ ie->vht_capabilities = pos + 2;
+ } else if (*pos == WLAN_EID_QOS && pos[1] >= 1) {
+ ie->qosinfo = pos[2];
+ } else if (*pos == WLAN_EID_SUPPORTED_CHANNELS) {
+ ie->supp_channels = pos + 2;
+ ie->supp_channels_len = pos[1];
+ } else if (*pos == WLAN_EID_SUPPORTED_OPERATING_CLASSES) {
+ /*
+ * The value of the Length field of the Supported
+ * Operating Classes element is between 2 and 253.
+ * Silently skip invalid elements to avoid interop
+ * issues when trying to use the value.
+ */
+ if (pos[1] >= 2 && pos[1] <= 253) {
+ ie->supp_oper_classes = pos + 2;
+ ie->supp_oper_classes_len = pos[1];
+ }
+ } else if (*pos == WLAN_EID_VENDOR_SPECIFIC) {
+ ret = wpa_parse_generic(pos, end, ie);
+ if (ret < 0)
+ break;
+ if (ret > 0) {
+ ret = 0;
+ break;
+ }
+
+ ret = wpa_parse_vendor_specific(pos, end, ie);
+ if (ret < 0)
+ break;
+ if (ret > 0) {
+ ret = 0;
+ break;
+ }
+ } else {
+ wpa_hexdump(MSG_DEBUG, "WPA: Unrecognized EAPOL-Key "
+ "Key Data IE", pos, 2 + pos[1]);
+ }
+ }
+
+ return ret;
+}
diff --git a/freebsd/contrib/wpa/src/rsn_supp/wpa_ie.h b/freebsd/contrib/wpa/src/rsn_supp/wpa_ie.h
new file mode 100644
index 00000000..fe95af0a
--- /dev/null
+++ b/freebsd/contrib/wpa/src/rsn_supp/wpa_ie.h
@@ -0,0 +1,72 @@
+/*
+ * wpa_supplicant - WPA/RSN IE and KDE definitions
+ * 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 WPA_IE_H
+#define WPA_IE_H
+
+struct wpa_sm;
+
+struct wpa_eapol_ie_parse {
+ const u8 *wpa_ie;
+ size_t wpa_ie_len;
+ const u8 *rsn_ie;
+ size_t rsn_ie_len;
+ const u8 *pmkid;
+ const u8 *gtk;
+ size_t gtk_len;
+ const u8 *mac_addr;
+ size_t mac_addr_len;
+#ifdef CONFIG_PEERKEY
+ const u8 *smk;
+ size_t smk_len;
+ const u8 *nonce;
+ size_t nonce_len;
+ const u8 *lifetime;
+ size_t lifetime_len;
+ const u8 *error;
+ size_t error_len;
+#endif /* CONFIG_PEERKEY */
+#ifdef CONFIG_IEEE80211W
+ const u8 *igtk;
+ size_t igtk_len;
+#endif /* CONFIG_IEEE80211W */
+ const u8 *mdie;
+ size_t mdie_len;
+ const u8 *ftie;
+ size_t ftie_len;
+ const u8 *reassoc_deadline;
+ const u8 *key_lifetime;
+ const u8 *lnkid;
+ size_t lnkid_len;
+ const u8 *ext_capab;
+ size_t ext_capab_len;
+ const u8 *supp_rates;
+ size_t supp_rates_len;
+ const u8 *ext_supp_rates;
+ size_t ext_supp_rates_len;
+ const u8 *ht_capabilities;
+ const u8 *vht_capabilities;
+ const u8 *supp_channels;
+ size_t supp_channels_len;
+ const u8 *supp_oper_classes;
+ size_t supp_oper_classes_len;
+ u8 qosinfo;
+ u16 aid;
+ const u8 *wmm;
+ size_t wmm_len;
+#ifdef CONFIG_P2P
+ const u8 *ip_addr_req;
+ const u8 *ip_addr_alloc;
+#endif /* CONFIG_P2P */
+};
+
+int wpa_supplicant_parse_ies(const u8 *buf, size_t len,
+ struct wpa_eapol_ie_parse *ie);
+int wpa_gen_wpa_ie(struct wpa_sm *sm, u8 *wpa_ie, size_t wpa_ie_len);
+
+#endif /* WPA_IE_H */
diff --git a/freebsd/contrib/wpa/src/tls/tlsv1_client.h b/freebsd/contrib/wpa/src/tls/tlsv1_client.h
new file mode 100644
index 00000000..a4e25e96
--- /dev/null
+++ b/freebsd/contrib/wpa/src/tls/tlsv1_client.h
@@ -0,0 +1,54 @@
+/*
+ * 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.
+ */
+
+#ifndef TLSV1_CLIENT_H
+#define TLSV1_CLIENT_H
+
+#include "tlsv1_cred.h"
+
+struct tlsv1_client;
+
+int tlsv1_client_global_init(void);
+void tlsv1_client_global_deinit(void);
+struct tlsv1_client * tlsv1_client_init(void);
+void tlsv1_client_deinit(struct tlsv1_client *conn);
+int tlsv1_client_established(struct tlsv1_client *conn);
+int tlsv1_client_prf(struct tlsv1_client *conn, const char *label,
+ int server_random_first, u8 *out, size_t out_len);
+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);
+int tlsv1_client_encrypt(struct tlsv1_client *conn,
+ const u8 *in_data, size_t in_len,
+ u8 *out_data, size_t out_len);
+struct wpabuf * tlsv1_client_decrypt(struct tlsv1_client *conn,
+ const u8 *in_data, size_t in_len,
+ int *need_more_data);
+int tlsv1_client_get_cipher(struct tlsv1_client *conn, char *buf,
+ size_t buflen);
+int tlsv1_client_shutdown(struct tlsv1_client *conn);
+int tlsv1_client_resumed(struct tlsv1_client *conn);
+int tlsv1_client_hello_ext(struct tlsv1_client *conn, int ext_type,
+ const u8 *data, size_t data_len);
+int tlsv1_client_get_random(struct tlsv1_client *conn, struct tls_random *data);
+int tlsv1_client_get_keyblock_size(struct tlsv1_client *conn);
+int tlsv1_client_set_cipher_list(struct tlsv1_client *conn, u8 *ciphers);
+int tlsv1_client_set_cred(struct tlsv1_client *conn,
+ struct tlsv1_credentials *cred);
+void tlsv1_client_set_time_checks(struct tlsv1_client *conn, int enabled);
+
+typedef int (*tlsv1_client_session_ticket_cb)
+(void *ctx, const u8 *ticket, size_t len, const u8 *client_random,
+ const u8 *server_random, u8 *master_secret);
+
+void tlsv1_client_set_session_ticket_cb(struct tlsv1_client *conn,
+ tlsv1_client_session_ticket_cb cb,
+ void *ctx);
+
+#endif /* TLSV1_CLIENT_H */
diff --git a/freebsd/contrib/wpa/src/tls/tlsv1_cred.h b/freebsd/contrib/wpa/src/tls/tlsv1_cred.h
new file mode 100644
index 00000000..68fbdc92
--- /dev/null
+++ b/freebsd/contrib/wpa/src/tls/tlsv1_cred.h
@@ -0,0 +1,40 @@
+/*
+ * TLSv1 credentials
+ * Copyright (c) 2006-2007, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef TLSV1_CRED_H
+#define TLSV1_CRED_H
+
+struct tlsv1_credentials {
+ struct x509_certificate *trusted_certs;
+ struct x509_certificate *cert;
+ struct crypto_private_key *key;
+
+ /* Diffie-Hellman parameters */
+ u8 *dh_p; /* prime */
+ size_t dh_p_len;
+ u8 *dh_g; /* generator */
+ size_t dh_g_len;
+};
+
+
+struct tlsv1_credentials * tlsv1_cred_alloc(void);
+void tlsv1_cred_free(struct tlsv1_credentials *cred);
+int tlsv1_set_ca_cert(struct tlsv1_credentials *cred, const char *cert,
+ const u8 *cert_blob, size_t cert_blob_len,
+ const char *path);
+int tlsv1_set_cert(struct tlsv1_credentials *cred, const char *cert,
+ const u8 *cert_blob, size_t cert_blob_len);
+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);
+int tlsv1_set_dhparams(struct tlsv1_credentials *cred, const char *dh_file,
+ const u8 *dh_blob, size_t dh_blob_len);
+
+#endif /* TLSV1_CRED_H */
diff --git a/freebsd/contrib/wpa/src/tls/tlsv1_server.h b/freebsd/contrib/wpa/src/tls/tlsv1_server.h
new file mode 100644
index 00000000..10e76993
--- /dev/null
+++ b/freebsd/contrib/wpa/src/tls/tlsv1_server.h
@@ -0,0 +1,53 @@
+/*
+ * 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.
+ */
+
+#ifndef TLSV1_SERVER_H
+#define TLSV1_SERVER_H
+
+#include "tlsv1_cred.h"
+
+struct tlsv1_server;
+
+int tlsv1_server_global_init(void);
+void tlsv1_server_global_deinit(void);
+struct tlsv1_server * tlsv1_server_init(struct tlsv1_credentials *cred);
+void tlsv1_server_deinit(struct tlsv1_server *conn);
+int tlsv1_server_established(struct tlsv1_server *conn);
+int tlsv1_server_prf(struct tlsv1_server *conn, const char *label,
+ int server_random_first, u8 *out, size_t out_len);
+u8 * tlsv1_server_handshake(struct tlsv1_server *conn,
+ const u8 *in_data, size_t in_len, size_t *out_len);
+int tlsv1_server_encrypt(struct tlsv1_server *conn,
+ const u8 *in_data, size_t in_len,
+ u8 *out_data, size_t out_len);
+int tlsv1_server_decrypt(struct tlsv1_server *conn,
+ const u8 *in_data, size_t in_len,
+ u8 *out_data, size_t out_len);
+int tlsv1_server_get_cipher(struct tlsv1_server *conn, char *buf,
+ size_t buflen);
+int tlsv1_server_shutdown(struct tlsv1_server *conn);
+int tlsv1_server_resumed(struct tlsv1_server *conn);
+int tlsv1_server_get_random(struct tlsv1_server *conn, struct tls_random *data);
+int tlsv1_server_get_keyblock_size(struct tlsv1_server *conn);
+int tlsv1_server_set_cipher_list(struct tlsv1_server *conn, u8 *ciphers);
+int tlsv1_server_set_verify(struct tlsv1_server *conn, int verify_peer);
+
+typedef int (*tlsv1_server_session_ticket_cb)
+(void *ctx, const u8 *ticket, size_t len, const u8 *client_random,
+ const u8 *server_random, u8 *master_secret);
+
+void tlsv1_server_set_session_ticket_cb(struct tlsv1_server *conn,
+ tlsv1_server_session_ticket_cb cb,
+ void *ctx);
+
+void tlsv1_server_set_log_cb(struct tlsv1_server *conn,
+ void (*cb)(void *ctx, const char *msg), void *ctx);
+
+void tlsv1_server_set_test_flags(struct tlsv1_server *conn, u32 flags);
+
+#endif /* TLSV1_SERVER_H */
diff --git a/freebsd/contrib/wpa/src/utils/base64.c b/freebsd/contrib/wpa/src/utils/base64.c
new file mode 100644
index 00000000..4127950f
--- /dev/null
+++ b/freebsd/contrib/wpa/src/utils/base64.c
@@ -0,0 +1,159 @@
+#include <machine/rtems-bsd-user-space.h>
+
+/*
+ * 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 "includes.h"
+
+#include "os.h"
+#include "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) & 0x3f];
+ *pos++ = base64_table[(((in[0] & 0x03) << 4) |
+ (in[1] >> 4)) & 0x3f];
+ *pos++ = base64_table[(((in[1] & 0x0f) << 2) |
+ (in[2] >> 6)) & 0x3f];
+ *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) & 0x3f];
+ if (end - in == 1) {
+ *pos++ = base64_table[((in[0] & 0x03) << 4) & 0x3f];
+ *pos++ = '=';
+ } else {
+ *pos++ = base64_table[(((in[0] & 0x03) << 4) |
+ (in[1] >> 4)) & 0x3f];
+ *pos++ = base64_table[((in[1] & 0x0f) << 2) & 0x3f];
+ }
+ *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;
+}
diff --git a/freebsd/contrib/wpa/src/utils/base64.h b/freebsd/contrib/wpa/src/utils/base64.h
new file mode 100644
index 00000000..aa21fd0f
--- /dev/null
+++ b/freebsd/contrib/wpa/src/utils/base64.h
@@ -0,0 +1,17 @@
+/*
+ * Base64 encoding/decoding (RFC1341)
+ * Copyright (c) 2005, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef BASE64_H
+#define BASE64_H
+
+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,
+ size_t *out_len);
+
+#endif /* BASE64_H */
diff --git a/freebsd/contrib/wpa/src/utils/build_config.h b/freebsd/contrib/wpa/src/utils/build_config.h
new file mode 100644
index 00000000..c6f4e437
--- /dev/null
+++ b/freebsd/contrib/wpa/src/utils/build_config.h
@@ -0,0 +1,50 @@
+/*
+ * wpa_supplicant/hostapd - Build time configuration defines
+ * Copyright (c) 2005-2006, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ *
+ * This header file can be used to define configuration defines that were
+ * originally defined in Makefile. This is mainly meant for IDE use or for
+ * systems that do not have suitable 'make' tool. In these cases, it may be
+ * easier to have a single place for defining all the needed C pre-processor
+ * defines.
+ */
+
+#ifndef BUILD_CONFIG_H
+#define BUILD_CONFIG_H
+
+/* Insert configuration defines, e.g., #define EAP_MD5, here, if needed. */
+
+#ifdef CONFIG_WIN32_DEFAULTS
+#define CONFIG_NATIVE_WINDOWS
+#define CONFIG_ANSI_C_EXTRA
+#define CONFIG_WINPCAP
+#define IEEE8021X_EAPOL
+#define PKCS12_FUNCS
+#define PCSC_FUNCS
+#define CONFIG_CTRL_IFACE
+#define CONFIG_CTRL_IFACE_NAMED_PIPE
+#define CONFIG_DRIVER_NDIS
+#define CONFIG_NDIS_EVENTS_INTEGRATED
+#define CONFIG_DEBUG_FILE
+#define EAP_MD5
+#define EAP_TLS
+#define EAP_MSCHAPv2
+#define EAP_PEAP
+#define EAP_TTLS
+#define EAP_GTC
+#define EAP_OTP
+#define EAP_LEAP
+#define EAP_TNC
+#define _CRT_SECURE_NO_DEPRECATE
+
+#ifdef USE_INTERNAL_CRYPTO
+#define CONFIG_TLS_INTERNAL_CLIENT
+#define CONFIG_INTERNAL_LIBTOMMATH
+#define CONFIG_CRYPTO_INTERNAL
+#endif /* USE_INTERNAL_CRYPTO */
+#endif /* CONFIG_WIN32_DEFAULTS */
+
+#endif /* BUILD_CONFIG_H */
diff --git a/freebsd/contrib/wpa/src/utils/common.c b/freebsd/contrib/wpa/src/utils/common.c
new file mode 100644
index 00000000..9665bd3c
--- /dev/null
+++ b/freebsd/contrib/wpa/src/utils/common.c
@@ -0,0 +1,1127 @@
+#include <machine/rtems-bsd-user-space.h>
+
+/*
+ * wpa_supplicant/hostapd / common helper functions, etc.
+ * Copyright (c) 2002-2007, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common/ieee802_11_defs.h"
+#include "common.h"
+
+
+static int hex2num(char c)
+{
+ if (c >= '0' && c <= '9')
+ return c - '0';
+ if (c >= 'a' && c <= 'f')
+ return c - 'a' + 10;
+ if (c >= 'A' && c <= 'F')
+ return c - 'A' + 10;
+ return -1;
+}
+
+
+int hex2byte(const char *hex)
+{
+ int a, b;
+ a = hex2num(*hex++);
+ if (a < 0)
+ return -1;
+ b = hex2num(*hex++);
+ if (b < 0)
+ return -1;
+ return (a << 4) | b;
+}
+
+
+static const char * hwaddr_parse(const char *txt, u8 *addr)
+{
+ size_t i;
+
+ for (i = 0; i < ETH_ALEN; i++) {
+ int a;
+
+ a = hex2byte(txt);
+ if (a < 0)
+ return NULL;
+ txt += 2;
+ addr[i] = a;
+ if (i < ETH_ALEN - 1 && *txt++ != ':')
+ return NULL;
+ }
+ return txt;
+}
+
+
+/**
+ * hwaddr_aton - Convert ASCII string to MAC address (colon-delimited format)
+ * @txt: MAC address as a string (e.g., "00:11:22:33:44:55")
+ * @addr: Buffer for the MAC address (ETH_ALEN = 6 bytes)
+ * Returns: 0 on success, -1 on failure (e.g., string not a MAC address)
+ */
+int hwaddr_aton(const char *txt, u8 *addr)
+{
+ return hwaddr_parse(txt, addr) ? 0 : -1;
+}
+
+
+/**
+ * hwaddr_masked_aton - Convert ASCII string with optional mask to MAC address (colon-delimited format)
+ * @txt: MAC address with optional mask as a string (e.g., "00:11:22:33:44:55/ff:ff:ff:ff:00:00")
+ * @addr: Buffer for the MAC address (ETH_ALEN = 6 bytes)
+ * @mask: Buffer for the MAC address mask (ETH_ALEN = 6 bytes)
+ * @maskable: Flag to indicate whether a mask is allowed
+ * Returns: 0 on success, -1 on failure (e.g., string not a MAC address)
+ */
+int hwaddr_masked_aton(const char *txt, u8 *addr, u8 *mask, u8 maskable)
+{
+ const char *r;
+
+ /* parse address part */
+ r = hwaddr_parse(txt, addr);
+ if (!r)
+ return -1;
+
+ /* check for optional mask */
+ if (*r == '\0' || isspace(*r)) {
+ /* no mask specified, assume default */
+ os_memset(mask, 0xff, ETH_ALEN);
+ } else if (maskable && *r == '/') {
+ /* mask specified and allowed */
+ r = hwaddr_parse(r + 1, mask);
+ /* parser error? */
+ if (!r)
+ return -1;
+ } else {
+ /* mask specified but not allowed or trailing garbage */
+ return -1;
+ }
+
+ return 0;
+}
+
+
+/**
+ * hwaddr_compact_aton - Convert ASCII string to MAC address (no colon delimitors format)
+ * @txt: MAC address as a string (e.g., "001122334455")
+ * @addr: Buffer for the MAC address (ETH_ALEN = 6 bytes)
+ * Returns: 0 on success, -1 on failure (e.g., string not a MAC address)
+ */
+int hwaddr_compact_aton(const char *txt, u8 *addr)
+{
+ int i;
+
+ for (i = 0; i < 6; i++) {
+ int a, b;
+
+ a = hex2num(*txt++);
+ if (a < 0)
+ return -1;
+ b = hex2num(*txt++);
+ if (b < 0)
+ return -1;
+ *addr++ = (a << 4) | b;
+ }
+
+ return 0;
+}
+
+/**
+ * hwaddr_aton2 - Convert ASCII string to MAC address (in any known format)
+ * @txt: MAC address as a string (e.g., 00:11:22:33:44:55 or 0011.2233.4455)
+ * @addr: Buffer for the MAC address (ETH_ALEN = 6 bytes)
+ * Returns: Characters used (> 0) on success, -1 on failure
+ */
+int hwaddr_aton2(const char *txt, u8 *addr)
+{
+ int i;
+ const char *pos = txt;
+
+ for (i = 0; i < 6; i++) {
+ int a, b;
+
+ while (*pos == ':' || *pos == '.' || *pos == '-')
+ pos++;
+
+ a = hex2num(*pos++);
+ if (a < 0)
+ return -1;
+ b = hex2num(*pos++);
+ if (b < 0)
+ return -1;
+ *addr++ = (a << 4) | b;
+ }
+
+ return pos - txt;
+}
+
+
+/**
+ * hexstr2bin - Convert ASCII hex string into binary data
+ * @hex: ASCII hex string (e.g., "01ab")
+ * @buf: Buffer for the binary data
+ * @len: Length of the text to convert in bytes (of buf); hex will be double
+ * this size
+ * Returns: 0 on success, -1 on failure (invalid hex string)
+ */
+int hexstr2bin(const char *hex, u8 *buf, size_t len)
+{
+ size_t i;
+ int a;
+ const char *ipos = hex;
+ u8 *opos = buf;
+
+ for (i = 0; i < len; i++) {
+ a = hex2byte(ipos);
+ if (a < 0)
+ return -1;
+ *opos++ = a;
+ ipos += 2;
+ }
+ return 0;
+}
+
+
+int hwaddr_mask_txt(char *buf, size_t len, const u8 *addr, const u8 *mask)
+{
+ size_t i;
+ int print_mask = 0;
+ int res;
+
+ for (i = 0; i < ETH_ALEN; i++) {
+ if (mask[i] != 0xff) {
+ print_mask = 1;
+ break;
+ }
+ }
+
+ if (print_mask)
+ res = os_snprintf(buf, len, MACSTR "/" MACSTR,
+ MAC2STR(addr), MAC2STR(mask));
+ else
+ res = os_snprintf(buf, len, MACSTR, MAC2STR(addr));
+ if (os_snprintf_error(len, res))
+ return -1;
+ return res;
+}
+
+
+/**
+ * inc_byte_array - Increment arbitrary length byte array by one
+ * @counter: Pointer to byte array
+ * @len: Length of the counter in bytes
+ *
+ * This function increments the last byte of the counter by one and continues
+ * rolling over to more significant bytes if the byte was incremented from
+ * 0xff to 0x00.
+ */
+void inc_byte_array(u8 *counter, size_t len)
+{
+ int pos = len - 1;
+ while (pos >= 0) {
+ counter[pos]++;
+ if (counter[pos] != 0)
+ break;
+ pos--;
+ }
+}
+
+
+void wpa_get_ntp_timestamp(u8 *buf)
+{
+ struct os_time now;
+ u32 sec, usec;
+ be32 tmp;
+
+ /* 64-bit NTP timestamp (time from 1900-01-01 00:00:00) */
+ os_get_time(&now);
+ sec = now.sec + 2208988800U; /* Epoch to 1900 */
+ /* Estimate 2^32/10^6 = 4295 - 1/32 - 1/512 */
+ usec = now.usec;
+ usec = 4295 * usec - (usec >> 5) - (usec >> 9);
+ tmp = host_to_be32(sec);
+ os_memcpy(buf, (u8 *) &tmp, 4);
+ tmp = host_to_be32(usec);
+ os_memcpy(buf + 4, (u8 *) &tmp, 4);
+}
+
+/**
+ * wpa_scnprintf - Simpler-to-use snprintf function
+ * @buf: Output buffer
+ * @size: Buffer size
+ * @fmt: format
+ *
+ * Simpler snprintf version that doesn't require further error checks - the
+ * return value only indicates how many bytes were actually written, excluding
+ * the NULL byte (i.e., 0 on error, size-1 if buffer is not big enough).
+ */
+int wpa_scnprintf(char *buf, size_t size, const char *fmt, ...)
+{
+ va_list ap;
+ int ret;
+
+ if (!size)
+ return 0;
+
+ va_start(ap, fmt);
+ ret = vsnprintf(buf, size, fmt, ap);
+ va_end(ap);
+
+ if (ret < 0)
+ return 0;
+ if ((size_t) ret >= size)
+ return size - 1;
+
+ return ret;
+}
+
+
+int wpa_snprintf_hex_sep(char *buf, size_t buf_size, const u8 *data, size_t len,
+ char sep)
+{
+ size_t i;
+ char *pos = buf, *end = buf + buf_size;
+ int ret;
+
+ if (buf_size == 0)
+ return 0;
+
+ for (i = 0; i < len; i++) {
+ ret = os_snprintf(pos, end - pos, "%02x%c",
+ data[i], sep);
+ if (os_snprintf_error(end - pos, ret)) {
+ end[-1] = '\0';
+ return pos - buf;
+ }
+ pos += ret;
+ }
+ pos[-1] = '\0';
+ return pos - buf;
+}
+
+
+static inline int _wpa_snprintf_hex(char *buf, size_t buf_size, const u8 *data,
+ size_t len, int uppercase)
+{
+ size_t i;
+ char *pos = buf, *end = buf + buf_size;
+ int ret;
+ if (buf_size == 0)
+ return 0;
+ for (i = 0; i < len; i++) {
+ ret = os_snprintf(pos, end - pos, uppercase ? "%02X" : "%02x",
+ data[i]);
+ if (os_snprintf_error(end - pos, ret)) {
+ end[-1] = '\0';
+ return pos - buf;
+ }
+ pos += ret;
+ }
+ end[-1] = '\0';
+ return pos - buf;
+}
+
+/**
+ * wpa_snprintf_hex - Print data as a hex string into a buffer
+ * @buf: Memory area to use as the output buffer
+ * @buf_size: Maximum buffer size in bytes (should be at least 2 * len + 1)
+ * @data: Data to be printed
+ * @len: Length of data in bytes
+ * Returns: Number of bytes written
+ */
+int wpa_snprintf_hex(char *buf, size_t buf_size, const u8 *data, size_t len)
+{
+ return _wpa_snprintf_hex(buf, buf_size, data, len, 0);
+}
+
+
+/**
+ * wpa_snprintf_hex_uppercase - Print data as a upper case hex string into buf
+ * @buf: Memory area to use as the output buffer
+ * @buf_size: Maximum buffer size in bytes (should be at least 2 * len + 1)
+ * @data: Data to be printed
+ * @len: Length of data in bytes
+ * Returns: Number of bytes written
+ */
+int wpa_snprintf_hex_uppercase(char *buf, size_t buf_size, const u8 *data,
+ size_t len)
+{
+ return _wpa_snprintf_hex(buf, buf_size, data, len, 1);
+}
+
+
+#ifdef CONFIG_ANSI_C_EXTRA
+
+#ifdef _WIN32_WCE
+void perror(const char *s)
+{
+ wpa_printf(MSG_ERROR, "%s: GetLastError: %d",
+ s, (int) GetLastError());
+}
+#endif /* _WIN32_WCE */
+
+
+int optind = 1;
+int optopt;
+char *optarg;
+
+int getopt(int argc, char *const argv[], const char *optstring)
+{
+ static int optchr = 1;
+ char *cp;
+
+ if (optchr == 1) {
+ if (optind >= argc) {
+ /* all arguments processed */
+ return EOF;
+ }
+
+ if (argv[optind][0] != '-' || argv[optind][1] == '\0') {
+ /* no option characters */
+ return EOF;
+ }
+ }
+
+ if (os_strcmp(argv[optind], "--") == 0) {
+ /* no more options */
+ optind++;
+ return EOF;
+ }
+
+ optopt = argv[optind][optchr];
+ cp = os_strchr(optstring, optopt);
+ if (cp == NULL || optopt == ':') {
+ if (argv[optind][++optchr] == '\0') {
+ optchr = 1;
+ optind++;
+ }
+ return '?';
+ }
+
+ if (cp[1] == ':') {
+ /* Argument required */
+ optchr = 1;
+ if (argv[optind][optchr + 1]) {
+ /* No space between option and argument */
+ optarg = &argv[optind++][optchr + 1];
+ } else if (++optind >= argc) {
+ /* option requires an argument */
+ return '?';
+ } else {
+ /* Argument in the next argv */
+ optarg = argv[optind++];
+ }
+ } else {
+ /* No argument */
+ if (argv[optind][++optchr] == '\0') {
+ optchr = 1;
+ optind++;
+ }
+ optarg = NULL;
+ }
+ return *cp;
+}
+#endif /* CONFIG_ANSI_C_EXTRA */
+
+
+#ifdef CONFIG_NATIVE_WINDOWS
+/**
+ * wpa_unicode2ascii_inplace - Convert unicode string into ASCII
+ * @str: Pointer to string to convert
+ *
+ * This function converts a unicode string to ASCII using the same
+ * buffer for output. If UNICODE is not set, the buffer is not
+ * modified.
+ */
+void wpa_unicode2ascii_inplace(TCHAR *str)
+{
+#ifdef UNICODE
+ char *dst = (char *) str;
+ while (*str)
+ *dst++ = (char) *str++;
+ *dst = '\0';
+#endif /* UNICODE */
+}
+
+
+TCHAR * wpa_strdup_tchar(const char *str)
+{
+#ifdef UNICODE
+ TCHAR *buf;
+ buf = os_malloc((strlen(str) + 1) * sizeof(TCHAR));
+ if (buf == NULL)
+ return NULL;
+ wsprintf(buf, L"%S", str);
+ return buf;
+#else /* UNICODE */
+ return os_strdup(str);
+#endif /* UNICODE */
+}
+#endif /* CONFIG_NATIVE_WINDOWS */
+
+
+void printf_encode(char *txt, size_t maxlen, const u8 *data, size_t len)
+{
+ char *end = txt + maxlen;
+ size_t i;
+
+ for (i = 0; i < len; i++) {
+ if (txt + 4 >= end)
+ break;
+
+ switch (data[i]) {
+ case '\"':
+ *txt++ = '\\';
+ *txt++ = '\"';
+ break;
+ case '\\':
+ *txt++ = '\\';
+ *txt++ = '\\';
+ break;
+ case '\033':
+ *txt++ = '\\';
+ *txt++ = 'e';
+ break;
+ case '\n':
+ *txt++ = '\\';
+ *txt++ = 'n';
+ break;
+ case '\r':
+ *txt++ = '\\';
+ *txt++ = 'r';
+ break;
+ case '\t':
+ *txt++ = '\\';
+ *txt++ = 't';
+ break;
+ default:
+ if (data[i] >= 32 && data[i] <= 127) {
+ *txt++ = data[i];
+ } else {
+ txt += os_snprintf(txt, end - txt, "\\x%02x",
+ data[i]);
+ }
+ break;
+ }
+ }
+
+ *txt = '\0';
+}
+
+
+size_t printf_decode(u8 *buf, size_t maxlen, const char *str)
+{
+ const char *pos = str;
+ size_t len = 0;
+ int val;
+
+ while (*pos) {
+ if (len + 1 >= maxlen)
+ break;
+ switch (*pos) {
+ case '\\':
+ pos++;
+ switch (*pos) {
+ case '\\':
+ buf[len++] = '\\';
+ pos++;
+ break;
+ case '"':
+ buf[len++] = '"';
+ pos++;
+ break;
+ case 'n':
+ buf[len++] = '\n';
+ pos++;
+ break;
+ case 'r':
+ buf[len++] = '\r';
+ pos++;
+ break;
+ case 't':
+ buf[len++] = '\t';
+ pos++;
+ break;
+ case 'e':
+ buf[len++] = '\033';
+ pos++;
+ break;
+ case 'x':
+ pos++;
+ val = hex2byte(pos);
+ if (val < 0) {
+ val = hex2num(*pos);
+ if (val < 0)
+ break;
+ buf[len++] = val;
+ pos++;
+ } else {
+ buf[len++] = val;
+ pos += 2;
+ }
+ break;
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ val = *pos++ - '0';
+ if (*pos >= '0' && *pos <= '7')
+ val = val * 8 + (*pos++ - '0');
+ if (*pos >= '0' && *pos <= '7')
+ val = val * 8 + (*pos++ - '0');
+ buf[len++] = val;
+ break;
+ default:
+ break;
+ }
+ break;
+ default:
+ buf[len++] = *pos++;
+ break;
+ }
+ }
+ if (maxlen > len)
+ buf[len] = '\0';
+
+ return len;
+}
+
+
+/**
+ * wpa_ssid_txt - Convert SSID to a printable string
+ * @ssid: SSID (32-octet string)
+ * @ssid_len: Length of ssid in octets
+ * Returns: Pointer to a printable string
+ *
+ * This function can be used to convert SSIDs into printable form. In most
+ * cases, SSIDs do not use unprintable characters, but IEEE 802.11 standard
+ * does not limit the used character set, so anything could be used in an SSID.
+ *
+ * This function uses a static buffer, so only one call can be used at the
+ * time, i.e., this is not re-entrant and the returned buffer must be used
+ * before calling this again.
+ */
+const char * wpa_ssid_txt(const u8 *ssid, size_t ssid_len)
+{
+ static char ssid_txt[SSID_MAX_LEN * 4 + 1];
+
+ if (ssid == NULL) {
+ ssid_txt[0] = '\0';
+ return ssid_txt;
+ }
+
+ printf_encode(ssid_txt, sizeof(ssid_txt), ssid, ssid_len);
+ return ssid_txt;
+}
+
+
+void * __hide_aliasing_typecast(void *foo)
+{
+ return foo;
+}
+
+
+char * wpa_config_parse_string(const char *value, size_t *len)
+{
+ if (*value == '"') {
+ const char *pos;
+ char *str;
+ value++;
+ pos = os_strrchr(value, '"');
+ if (pos == NULL || pos[1] != '\0')
+ return NULL;
+ *len = pos - value;
+ str = dup_binstr(value, *len);
+ if (str == NULL)
+ return NULL;
+ return str;
+ } else if (*value == 'P' && value[1] == '"') {
+ const char *pos;
+ char *tstr, *str;
+ size_t tlen;
+ value += 2;
+ pos = os_strrchr(value, '"');
+ if (pos == NULL || pos[1] != '\0')
+ return NULL;
+ tlen = pos - value;
+ tstr = dup_binstr(value, tlen);
+ if (tstr == NULL)
+ return NULL;
+
+ str = os_malloc(tlen + 1);
+ if (str == NULL) {
+ os_free(tstr);
+ return NULL;
+ }
+
+ *len = printf_decode((u8 *) str, tlen + 1, tstr);
+ os_free(tstr);
+
+ return str;
+ } else {
+ u8 *str;
+ size_t tlen, hlen = os_strlen(value);
+ if (hlen & 1)
+ return NULL;
+ tlen = hlen / 2;
+ str = os_malloc(tlen + 1);
+ if (str == NULL)
+ return NULL;
+ if (hexstr2bin(value, str, tlen)) {
+ os_free(str);
+ return NULL;
+ }
+ str[tlen] = '\0';
+ *len = tlen;
+ return (char *) str;
+ }
+}
+
+
+int is_hex(const u8 *data, size_t len)
+{
+ size_t i;
+
+ for (i = 0; i < len; i++) {
+ if (data[i] < 32 || data[i] >= 127)
+ return 1;
+ }
+ return 0;
+}
+
+
+size_t merge_byte_arrays(u8 *res, size_t res_len,
+ const u8 *src1, size_t src1_len,
+ const u8 *src2, size_t src2_len)
+{
+ size_t len = 0;
+
+ os_memset(res, 0, res_len);
+
+ if (src1) {
+ if (src1_len >= res_len) {
+ os_memcpy(res, src1, res_len);
+ return res_len;
+ }
+
+ os_memcpy(res, src1, src1_len);
+ len += src1_len;
+ }
+
+ if (src2) {
+ if (len + src2_len >= res_len) {
+ os_memcpy(res + len, src2, res_len - len);
+ return res_len;
+ }
+
+ os_memcpy(res + len, src2, src2_len);
+ len += src2_len;
+ }
+
+ return len;
+}
+
+
+char * dup_binstr(const void *src, size_t len)
+{
+ char *res;
+
+ if (src == NULL)
+ return NULL;
+ res = os_malloc(len + 1);
+ if (res == NULL)
+ return NULL;
+ os_memcpy(res, src, len);
+ res[len] = '\0';
+
+ return res;
+}
+
+
+int freq_range_list_parse(struct wpa_freq_range_list *res, const char *value)
+{
+ struct wpa_freq_range *freq = NULL, *n;
+ unsigned int count = 0;
+ const char *pos, *pos2, *pos3;
+
+ /*
+ * Comma separated list of frequency ranges.
+ * For example: 2412-2432,2462,5000-6000
+ */
+ pos = value;
+ while (pos && pos[0]) {
+ n = os_realloc_array(freq, count + 1,
+ sizeof(struct wpa_freq_range));
+ if (n == NULL) {
+ os_free(freq);
+ return -1;
+ }
+ freq = n;
+ freq[count].min = atoi(pos);
+ pos2 = os_strchr(pos, '-');
+ pos3 = os_strchr(pos, ',');
+ if (pos2 && (!pos3 || pos2 < pos3)) {
+ pos2++;
+ freq[count].max = atoi(pos2);
+ } else
+ freq[count].max = freq[count].min;
+ pos = pos3;
+ if (pos)
+ pos++;
+ count++;
+ }
+
+ os_free(res->range);
+ res->range = freq;
+ res->num = count;
+
+ return 0;
+}
+
+
+int freq_range_list_includes(const struct wpa_freq_range_list *list,
+ unsigned int freq)
+{
+ unsigned int i;
+
+ if (list == NULL)
+ return 0;
+
+ for (i = 0; i < list->num; i++) {
+ if (freq >= list->range[i].min && freq <= list->range[i].max)
+ return 1;
+ }
+
+ return 0;
+}
+
+
+char * freq_range_list_str(const struct wpa_freq_range_list *list)
+{
+ char *buf, *pos, *end;
+ size_t maxlen;
+ unsigned int i;
+ int res;
+
+ if (list->num == 0)
+ return NULL;
+
+ maxlen = list->num * 30;
+ buf = os_malloc(maxlen);
+ if (buf == NULL)
+ return NULL;
+ pos = buf;
+ end = buf + maxlen;
+
+ for (i = 0; i < list->num; i++) {
+ struct wpa_freq_range *range = &list->range[i];
+
+ if (range->min == range->max)
+ res = os_snprintf(pos, end - pos, "%s%u",
+ i == 0 ? "" : ",", range->min);
+ else
+ res = os_snprintf(pos, end - pos, "%s%u-%u",
+ i == 0 ? "" : ",",
+ range->min, range->max);
+ if (os_snprintf_error(end - pos, res)) {
+ os_free(buf);
+ return NULL;
+ }
+ pos += res;
+ }
+
+ return buf;
+}
+
+
+int int_array_len(const int *a)
+{
+ int i;
+ for (i = 0; a && a[i]; i++)
+ ;
+ return i;
+}
+
+
+void int_array_concat(int **res, const int *a)
+{
+ int reslen, alen, i;
+ int *n;
+
+ reslen = int_array_len(*res);
+ alen = int_array_len(a);
+
+ n = os_realloc_array(*res, reslen + alen + 1, sizeof(int));
+ if (n == NULL) {
+ os_free(*res);
+ *res = NULL;
+ return;
+ }
+ for (i = 0; i <= alen; i++)
+ n[reslen + i] = a[i];
+ *res = n;
+}
+
+
+static int freq_cmp(const void *a, const void *b)
+{
+ int _a = *(int *) a;
+ int _b = *(int *) b;
+
+ if (_a == 0)
+ return 1;
+ if (_b == 0)
+ return -1;
+ return _a - _b;
+}
+
+
+void int_array_sort_unique(int *a)
+{
+ int alen;
+ int i, j;
+
+ if (a == NULL)
+ return;
+
+ alen = int_array_len(a);
+ qsort(a, alen, sizeof(int), freq_cmp);
+
+ i = 0;
+ j = 1;
+ while (a[i] && a[j]) {
+ if (a[i] == a[j]) {
+ j++;
+ continue;
+ }
+ a[++i] = a[j++];
+ }
+ if (a[i])
+ i++;
+ a[i] = 0;
+}
+
+
+void int_array_add_unique(int **res, int a)
+{
+ int reslen;
+ int *n;
+
+ for (reslen = 0; *res && (*res)[reslen]; reslen++) {
+ if ((*res)[reslen] == a)
+ return; /* already in the list */
+ }
+
+ n = os_realloc_array(*res, reslen + 2, sizeof(int));
+ if (n == NULL) {
+ os_free(*res);
+ *res = NULL;
+ return;
+ }
+
+ n[reslen] = a;
+ n[reslen + 1] = 0;
+
+ *res = n;
+}
+
+
+void str_clear_free(char *str)
+{
+ if (str) {
+ size_t len = os_strlen(str);
+ os_memset(str, 0, len);
+ os_free(str);
+ }
+}
+
+
+void bin_clear_free(void *bin, size_t len)
+{
+ if (bin) {
+ os_memset(bin, 0, len);
+ os_free(bin);
+ }
+}
+
+
+int random_mac_addr(u8 *addr)
+{
+ if (os_get_random(addr, ETH_ALEN) < 0)
+ return -1;
+ addr[0] &= 0xfe; /* unicast */
+ addr[0] |= 0x02; /* locally administered */
+ return 0;
+}
+
+
+int random_mac_addr_keep_oui(u8 *addr)
+{
+ if (os_get_random(addr + 3, 3) < 0)
+ return -1;
+ addr[0] &= 0xfe; /* unicast */
+ addr[0] |= 0x02; /* locally administered */
+ return 0;
+}
+
+
+/**
+ * cstr_token - Get next token from const char string
+ * @str: a constant string to tokenize
+ * @delim: a string of delimiters
+ * @last: a pointer to a character following the returned token
+ * It has to be set to NULL for the first call and passed for any
+ * futher call.
+ * Returns: a pointer to token position in str or NULL
+ *
+ * This function is similar to str_token, but it can be used with both
+ * char and const char strings. Differences:
+ * - The str buffer remains unmodified
+ * - The returned token is not a NULL terminated string, but a token
+ * position in str buffer. If a return value is not NULL a size
+ * of the returned token could be calculated as (last - token).
+ */
+const char * cstr_token(const char *str, const char *delim, const char **last)
+{
+ const char *end, *token = str;
+
+ if (!str || !delim || !last)
+ return NULL;
+
+ if (*last)
+ token = *last;
+
+ while (*token && os_strchr(delim, *token))
+ token++;
+
+ if (!*token)
+ return NULL;
+
+ end = token + 1;
+
+ while (*end && !os_strchr(delim, *end))
+ end++;
+
+ *last = end;
+ return token;
+}
+
+
+/**
+ * str_token - Get next token from a string
+ * @buf: String to tokenize. Note that the string might be modified.
+ * @delim: String of delimiters
+ * @context: Pointer to save our context. Should be initialized with
+ * NULL on the first call, and passed for any further call.
+ * Returns: The next token, NULL if there are no more valid tokens.
+ */
+char * str_token(char *str, const char *delim, char **context)
+{
+ char *token = (char *) cstr_token(str, delim, (const char **) context);
+
+ if (token && **context)
+ *(*context)++ = '\0';
+
+ return token;
+}
+
+
+size_t utf8_unescape(const char *inp, size_t in_size,
+ char *outp, size_t out_size)
+{
+ size_t res_size = 0;
+
+ if (!inp || !outp)
+ return 0;
+
+ if (!in_size)
+ in_size = os_strlen(inp);
+
+ /* Advance past leading single quote */
+ if (*inp == '\'' && in_size) {
+ inp++;
+ in_size--;
+ }
+
+ while (in_size--) {
+ if (res_size >= out_size)
+ return 0;
+
+ switch (*inp) {
+ case '\'':
+ /* Terminate on bare single quote */
+ *outp = '\0';
+ return res_size;
+
+ case '\\':
+ if (!in_size--)
+ return 0;
+ inp++;
+ /* fall through */
+
+ default:
+ *outp++ = *inp++;
+ res_size++;
+ }
+ }
+
+ /* NUL terminate if space allows */
+ if (res_size < out_size)
+ *outp = '\0';
+
+ return res_size;
+}
+
+
+size_t utf8_escape(const char *inp, size_t in_size,
+ char *outp, size_t out_size)
+{
+ size_t res_size = 0;
+
+ if (!inp || !outp)
+ return 0;
+
+ /* inp may or may not be NUL terminated, but must be if 0 size
+ * is specified */
+ if (!in_size)
+ in_size = os_strlen(inp);
+
+ while (in_size--) {
+ if (res_size++ >= out_size)
+ return 0;
+
+ switch (*inp) {
+ case '\\':
+ case '\'':
+ if (res_size++ >= out_size)
+ return 0;
+ *outp++ = '\\';
+ /* fall through */
+
+ default:
+ *outp++ = *inp++;
+ break;
+ }
+ }
+
+ /* NUL terminate if space allows */
+ if (res_size < out_size)
+ *outp = '\0';
+
+ return res_size;
+}
+
+
+int is_ctrl_char(char c)
+{
+ return c > 0 && c < 32;
+}
diff --git a/freebsd/contrib/wpa/src/utils/common.h b/freebsd/contrib/wpa/src/utils/common.h
new file mode 100644
index 00000000..0b9cc3d8
--- /dev/null
+++ b/freebsd/contrib/wpa/src/utils/common.h
@@ -0,0 +1,559 @@
+/*
+ * wpa_supplicant/hostapd / common helper functions, etc.
+ * Copyright (c) 2002-2007, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef COMMON_H
+#define COMMON_H
+
+#include "os.h"
+
+#if defined(__linux__) || defined(__GLIBC__)
+#include <endian.h>
+#include <byteswap.h>
+#endif /* __linux__ */
+
+#if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__DragonFly__) || \
+ defined(__OpenBSD__)
+#include <sys/types.h>
+#include <sys/endian.h>
+#define __BYTE_ORDER _BYTE_ORDER
+#define __LITTLE_ENDIAN _LITTLE_ENDIAN
+#define __BIG_ENDIAN _BIG_ENDIAN
+#ifdef __OpenBSD__
+#define bswap_16 swap16
+#define bswap_32 swap32
+#define bswap_64 swap64
+#else /* __OpenBSD__ */
+#define bswap_16 bswap16
+#define bswap_32 bswap32
+#define bswap_64 bswap64
+#endif /* __OpenBSD__ */
+#endif /* defined(__FreeBSD__) || defined(__NetBSD__) ||
+ * defined(__DragonFly__) || defined(__OpenBSD__) */
+
+#ifdef __APPLE__
+#include <sys/types.h>
+#include <machine/endian.h>
+#define __BYTE_ORDER _BYTE_ORDER
+#define __LITTLE_ENDIAN _LITTLE_ENDIAN
+#define __BIG_ENDIAN _BIG_ENDIAN
+static inline unsigned short bswap_16(unsigned short v)
+{
+ return ((v & 0xff) << 8) | (v >> 8);
+}
+
+static inline unsigned int bswap_32(unsigned int v)
+{
+ return ((v & 0xff) << 24) | ((v & 0xff00) << 8) |
+ ((v & 0xff0000) >> 8) | (v >> 24);
+}
+#endif /* __APPLE__ */
+
+#ifdef CONFIG_NATIVE_WINDOWS
+#include <winsock.h>
+
+typedef int socklen_t;
+
+#ifndef MSG_DONTWAIT
+#define MSG_DONTWAIT 0 /* not supported */
+#endif
+
+#endif /* CONFIG_NATIVE_WINDOWS */
+
+#ifdef _MSC_VER
+#define inline __inline
+
+#undef vsnprintf
+#define vsnprintf _vsnprintf
+#undef close
+#define close closesocket
+#endif /* _MSC_VER */
+
+
+/* Define platform specific integer types */
+
+#ifdef _MSC_VER
+typedef UINT64 u64;
+typedef UINT32 u32;
+typedef UINT16 u16;
+typedef UINT8 u8;
+typedef INT64 s64;
+typedef INT32 s32;
+typedef INT16 s16;
+typedef INT8 s8;
+#define WPA_TYPES_DEFINED
+#endif /* _MSC_VER */
+
+#ifdef __vxworks
+typedef unsigned long long u64;
+typedef UINT32 u32;
+typedef UINT16 u16;
+typedef UINT8 u8;
+typedef long long s64;
+typedef INT32 s32;
+typedef INT16 s16;
+typedef INT8 s8;
+#define WPA_TYPES_DEFINED
+#endif /* __vxworks */
+
+#ifndef WPA_TYPES_DEFINED
+#ifdef CONFIG_USE_INTTYPES_H
+#include <inttypes.h>
+#else
+#include <stdint.h>
+#endif
+typedef uint64_t u64;
+typedef uint32_t u32;
+typedef uint16_t u16;
+typedef uint8_t u8;
+typedef int64_t s64;
+typedef int32_t s32;
+typedef int16_t s16;
+typedef int8_t s8;
+#define WPA_TYPES_DEFINED
+#endif /* !WPA_TYPES_DEFINED */
+
+
+/* Define platform specific byte swapping macros */
+
+#if defined(__CYGWIN__) || defined(CONFIG_NATIVE_WINDOWS)
+
+static inline unsigned short wpa_swap_16(unsigned short v)
+{
+ return ((v & 0xff) << 8) | (v >> 8);
+}
+
+static inline unsigned int wpa_swap_32(unsigned int v)
+{
+ return ((v & 0xff) << 24) | ((v & 0xff00) << 8) |
+ ((v & 0xff0000) >> 8) | (v >> 24);
+}
+
+#define le_to_host16(n) (n)
+#define host_to_le16(n) (n)
+#define be_to_host16(n) wpa_swap_16(n)
+#define host_to_be16(n) wpa_swap_16(n)
+#define le_to_host32(n) (n)
+#define host_to_le32(n) (n)
+#define be_to_host32(n) wpa_swap_32(n)
+#define host_to_be32(n) wpa_swap_32(n)
+
+#define WPA_BYTE_SWAP_DEFINED
+
+#endif /* __CYGWIN__ || CONFIG_NATIVE_WINDOWS */
+
+
+#ifndef WPA_BYTE_SWAP_DEFINED
+
+#ifndef __BYTE_ORDER
+#ifndef __LITTLE_ENDIAN
+#ifndef __BIG_ENDIAN
+#define __LITTLE_ENDIAN 1234
+#define __BIG_ENDIAN 4321
+#if defined(sparc)
+#define __BYTE_ORDER __BIG_ENDIAN
+#endif
+#endif /* __BIG_ENDIAN */
+#endif /* __LITTLE_ENDIAN */
+#endif /* __BYTE_ORDER */
+
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+#define le_to_host16(n) ((__force u16) (le16) (n))
+#define host_to_le16(n) ((__force le16) (u16) (n))
+#define be_to_host16(n) bswap_16((__force u16) (be16) (n))
+#define host_to_be16(n) ((__force be16) bswap_16((n)))
+#define le_to_host32(n) ((__force u32) (le32) (n))
+#define host_to_le32(n) ((__force le32) (u32) (n))
+#define be_to_host32(n) bswap_32((__force u32) (be32) (n))
+#define host_to_be32(n) ((__force be32) bswap_32((n)))
+#define le_to_host64(n) ((__force u64) (le64) (n))
+#define host_to_le64(n) ((__force le64) (u64) (n))
+#define be_to_host64(n) bswap_64((__force u64) (be64) (n))
+#define host_to_be64(n) ((__force be64) bswap_64((n)))
+#elif __BYTE_ORDER == __BIG_ENDIAN
+#define le_to_host16(n) bswap_16(n)
+#define host_to_le16(n) bswap_16(n)
+#define be_to_host16(n) (n)
+#define host_to_be16(n) (n)
+#define le_to_host32(n) bswap_32(n)
+#define host_to_le32(n) bswap_32(n)
+#define be_to_host32(n) (n)
+#define host_to_be32(n) (n)
+#define le_to_host64(n) bswap_64(n)
+#define host_to_le64(n) bswap_64(n)
+#define be_to_host64(n) (n)
+#define host_to_be64(n) (n)
+#ifndef WORDS_BIGENDIAN
+#define WORDS_BIGENDIAN
+#endif
+#else
+#error Could not determine CPU byte order
+#endif
+
+#define WPA_BYTE_SWAP_DEFINED
+#endif /* !WPA_BYTE_SWAP_DEFINED */
+
+
+/* Macros for handling unaligned memory accesses */
+
+static inline u16 WPA_GET_BE16(const u8 *a)
+{
+ return (a[0] << 8) | a[1];
+}
+
+static inline void WPA_PUT_BE16(u8 *a, u16 val)
+{
+ a[0] = val >> 8;
+ a[1] = val & 0xff;
+}
+
+static inline u16 WPA_GET_LE16(const u8 *a)
+{
+ return (a[1] << 8) | a[0];
+}
+
+static inline void WPA_PUT_LE16(u8 *a, u16 val)
+{
+ a[1] = val >> 8;
+ a[0] = val & 0xff;
+}
+
+static inline u32 WPA_GET_BE24(const u8 *a)
+{
+ return (a[0] << 16) | (a[1] << 8) | a[2];
+}
+
+static inline void WPA_PUT_BE24(u8 *a, u32 val)
+{
+ a[0] = (val >> 16) & 0xff;
+ a[1] = (val >> 8) & 0xff;
+ a[2] = val & 0xff;
+}
+
+static inline u32 WPA_GET_BE32(const u8 *a)
+{
+ return ((u32) a[0] << 24) | (a[1] << 16) | (a[2] << 8) | a[3];
+}
+
+static inline void WPA_PUT_BE32(u8 *a, u32 val)
+{
+ a[0] = (val >> 24) & 0xff;
+ a[1] = (val >> 16) & 0xff;
+ a[2] = (val >> 8) & 0xff;
+ a[3] = val & 0xff;
+}
+
+static inline u32 WPA_GET_LE32(const u8 *a)
+{
+ return ((u32) a[3] << 24) | (a[2] << 16) | (a[1] << 8) | a[0];
+}
+
+static inline void WPA_PUT_LE32(u8 *a, u32 val)
+{
+ a[3] = (val >> 24) & 0xff;
+ a[2] = (val >> 16) & 0xff;
+ a[1] = (val >> 8) & 0xff;
+ a[0] = val & 0xff;
+}
+
+static inline u64 WPA_GET_BE64(const u8 *a)
+{
+ return (((u64) a[0]) << 56) | (((u64) a[1]) << 48) |
+ (((u64) a[2]) << 40) | (((u64) a[3]) << 32) |
+ (((u64) a[4]) << 24) | (((u64) a[5]) << 16) |
+ (((u64) a[6]) << 8) | ((u64) a[7]);
+}
+
+static inline void WPA_PUT_BE64(u8 *a, u64 val)
+{
+ a[0] = val >> 56;
+ a[1] = val >> 48;
+ a[2] = val >> 40;
+ a[3] = val >> 32;
+ a[4] = val >> 24;
+ a[5] = val >> 16;
+ a[6] = val >> 8;
+ a[7] = val & 0xff;
+}
+
+static inline u64 WPA_GET_LE64(const u8 *a)
+{
+ return (((u64) a[7]) << 56) | (((u64) a[6]) << 48) |
+ (((u64) a[5]) << 40) | (((u64) a[4]) << 32) |
+ (((u64) a[3]) << 24) | (((u64) a[2]) << 16) |
+ (((u64) a[1]) << 8) | ((u64) a[0]);
+}
+
+static inline void WPA_PUT_LE64(u8 *a, u64 val)
+{
+ a[7] = val >> 56;
+ a[6] = val >> 48;
+ a[5] = val >> 40;
+ a[4] = val >> 32;
+ a[3] = val >> 24;
+ a[2] = val >> 16;
+ a[1] = val >> 8;
+ a[0] = val & 0xff;
+}
+
+
+#ifndef ETH_ALEN
+#define ETH_ALEN 6
+#endif
+#ifndef ETH_HLEN
+#define ETH_HLEN 14
+#endif
+#ifndef IFNAMSIZ
+#define IFNAMSIZ 16
+#endif
+#ifndef ETH_P_ALL
+#define ETH_P_ALL 0x0003
+#endif
+#ifndef ETH_P_80211_ENCAP
+#define ETH_P_80211_ENCAP 0x890d /* TDLS comes under this category */
+#endif
+#ifndef ETH_P_PAE
+#define ETH_P_PAE 0x888E /* Port Access Entity (IEEE 802.1X) */
+#endif /* ETH_P_PAE */
+#ifndef ETH_P_EAPOL
+#define ETH_P_EAPOL ETH_P_PAE
+#endif /* ETH_P_EAPOL */
+#ifndef ETH_P_RSN_PREAUTH
+#define ETH_P_RSN_PREAUTH 0x88c7
+#endif /* ETH_P_RSN_PREAUTH */
+#ifndef ETH_P_RRB
+#define ETH_P_RRB 0x890D
+#endif /* ETH_P_RRB */
+
+
+#ifdef __GNUC__
+#define PRINTF_FORMAT(a,b) __attribute__ ((format (printf, (a), (b))))
+#define STRUCT_PACKED __attribute__ ((packed))
+#else
+#define PRINTF_FORMAT(a,b)
+#define STRUCT_PACKED
+#endif
+
+
+#ifdef CONFIG_ANSI_C_EXTRA
+
+#if !defined(_MSC_VER) || _MSC_VER < 1400
+/* snprintf - used in number of places; sprintf() is _not_ a good replacement
+ * due to possible buffer overflow; see, e.g.,
+ * http://www.ijs.si/software/snprintf/ for portable implementation of
+ * snprintf. */
+int snprintf(char *str, size_t size, const char *format, ...);
+
+/* vsnprintf - only used for wpa_msg() in wpa_supplicant.c */
+int vsnprintf(char *str, size_t size, const char *format, va_list ap);
+#endif /* !defined(_MSC_VER) || _MSC_VER < 1400 */
+
+/* getopt - only used in main.c */
+int getopt(int argc, char *const argv[], const char *optstring);
+extern char *optarg;
+extern int optind;
+
+#ifndef CONFIG_NO_SOCKLEN_T_TYPEDEF
+#ifndef __socklen_t_defined
+typedef int socklen_t;
+#endif
+#endif
+
+/* inline - define as __inline or just define it to be empty, if needed */
+#ifdef CONFIG_NO_INLINE
+#define inline
+#else
+#define inline __inline
+#endif
+
+#ifndef __func__
+#define __func__ "__func__ not defined"
+#endif
+
+#ifndef bswap_16
+#define bswap_16(a) ((((u16) (a) << 8) & 0xff00) | (((u16) (a) >> 8) & 0xff))
+#endif
+
+#ifndef bswap_32
+#define bswap_32(a) ((((u32) (a) << 24) & 0xff000000) | \
+ (((u32) (a) << 8) & 0xff0000) | \
+ (((u32) (a) >> 8) & 0xff00) | \
+ (((u32) (a) >> 24) & 0xff))
+#endif
+
+#ifndef MSG_DONTWAIT
+#define MSG_DONTWAIT 0
+#endif
+
+#ifdef _WIN32_WCE
+void perror(const char *s);
+#endif /* _WIN32_WCE */
+
+#endif /* CONFIG_ANSI_C_EXTRA */
+
+#ifndef MAC2STR
+#define MAC2STR(a) (a)[0], (a)[1], (a)[2], (a)[3], (a)[4], (a)[5]
+#define MACSTR "%02x:%02x:%02x:%02x:%02x:%02x"
+
+/*
+ * Compact form for string representation of MAC address
+ * To be used, e.g., for constructing dbus paths for P2P Devices
+ */
+#define COMPACT_MACSTR "%02x%02x%02x%02x%02x%02x"
+#endif
+
+#ifndef BIT
+#define BIT(x) (1U << (x))
+#endif
+
+/*
+ * Definitions for sparse validation
+ * (http://kernel.org/pub/linux/kernel/people/josh/sparse/)
+ */
+#ifdef __CHECKER__
+#define __force __attribute__((force))
+#define __bitwise __attribute__((bitwise))
+#else
+#define __force
+#define __bitwise
+#endif
+
+typedef u16 __bitwise be16;
+typedef u16 __bitwise le16;
+typedef u32 __bitwise be32;
+typedef u32 __bitwise le32;
+typedef u64 __bitwise be64;
+typedef u64 __bitwise le64;
+
+#ifndef __must_check
+#if __GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4)
+#define __must_check __attribute__((__warn_unused_result__))
+#else
+#define __must_check
+#endif /* __GNUC__ */
+#endif /* __must_check */
+
+#ifndef __maybe_unused
+#if __GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4)
+#define __maybe_unused __attribute__((unused))
+#else
+#define __maybe_unused
+#endif /* __GNUC__ */
+#endif /* __must_check */
+
+int hwaddr_aton(const char *txt, u8 *addr);
+int hwaddr_masked_aton(const char *txt, u8 *addr, u8 *mask, u8 maskable);
+int hwaddr_compact_aton(const char *txt, u8 *addr);
+int hwaddr_aton2(const char *txt, u8 *addr);
+int hex2byte(const char *hex);
+int hexstr2bin(const char *hex, u8 *buf, size_t len);
+void inc_byte_array(u8 *counter, size_t len);
+void wpa_get_ntp_timestamp(u8 *buf);
+int wpa_scnprintf(char *buf, size_t size, const char *fmt, ...);
+int wpa_snprintf_hex_sep(char *buf, size_t buf_size, const u8 *data, size_t len,
+ char sep);
+int wpa_snprintf_hex(char *buf, size_t buf_size, const u8 *data, size_t len);
+int wpa_snprintf_hex_uppercase(char *buf, size_t buf_size, const u8 *data,
+ size_t len);
+
+int hwaddr_mask_txt(char *buf, size_t len, const u8 *addr, const u8 *mask);
+
+#ifdef CONFIG_NATIVE_WINDOWS
+void wpa_unicode2ascii_inplace(TCHAR *str);
+TCHAR * wpa_strdup_tchar(const char *str);
+#else /* CONFIG_NATIVE_WINDOWS */
+#define wpa_unicode2ascii_inplace(s) do { } while (0)
+#define wpa_strdup_tchar(s) strdup((s))
+#endif /* CONFIG_NATIVE_WINDOWS */
+
+void printf_encode(char *txt, size_t maxlen, const u8 *data, size_t len);
+size_t printf_decode(u8 *buf, size_t maxlen, const char *str);
+
+const char * wpa_ssid_txt(const u8 *ssid, size_t ssid_len);
+
+char * wpa_config_parse_string(const char *value, size_t *len);
+int is_hex(const u8 *data, size_t len);
+size_t merge_byte_arrays(u8 *res, size_t res_len,
+ const u8 *src1, size_t src1_len,
+ const u8 *src2, size_t src2_len);
+char * dup_binstr(const void *src, size_t len);
+
+static inline int is_zero_ether_addr(const u8 *a)
+{
+ return !(a[0] | a[1] | a[2] | a[3] | a[4] | a[5]);
+}
+
+static inline int is_broadcast_ether_addr(const u8 *a)
+{
+ return (a[0] & a[1] & a[2] & a[3] & a[4] & a[5]) == 0xff;
+}
+
+static inline int is_multicast_ether_addr(const u8 *a)
+{
+ return a[0] & 0x01;
+}
+
+#define broadcast_ether_addr (const u8 *) "\xff\xff\xff\xff\xff\xff"
+
+#include "wpa_debug.h"
+
+
+struct wpa_freq_range_list {
+ struct wpa_freq_range {
+ unsigned int min;
+ unsigned int max;
+ } *range;
+ unsigned int num;
+};
+
+int freq_range_list_parse(struct wpa_freq_range_list *res, const char *value);
+int freq_range_list_includes(const struct wpa_freq_range_list *list,
+ unsigned int freq);
+char * freq_range_list_str(const struct wpa_freq_range_list *list);
+
+int int_array_len(const int *a);
+void int_array_concat(int **res, const int *a);
+void int_array_sort_unique(int *a);
+void int_array_add_unique(int **res, int a);
+
+#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
+
+void str_clear_free(char *str);
+void bin_clear_free(void *bin, size_t len);
+
+int random_mac_addr(u8 *addr);
+int random_mac_addr_keep_oui(u8 *addr);
+
+const char * cstr_token(const char *str, const char *delim, const char **last);
+char * str_token(char *str, const char *delim, char **context);
+size_t utf8_escape(const char *inp, size_t in_size,
+ char *outp, size_t out_size);
+size_t utf8_unescape(const char *inp, size_t in_size,
+ char *outp, size_t out_size);
+int is_ctrl_char(char c);
+
+
+/*
+ * gcc 4.4 ends up generating strict-aliasing warnings about some very common
+ * networking socket uses that do not really result in a real problem and
+ * cannot be easily avoided with union-based type-punning due to struct
+ * definitions including another struct in system header files. To avoid having
+ * to fully disable strict-aliasing warnings, provide a mechanism to hide the
+ * typecast from aliasing for now. A cleaner solution will hopefully be found
+ * in the future to handle these cases.
+ */
+void * __hide_aliasing_typecast(void *foo);
+#define aliasing_hide_typecast(a,t) (t *) __hide_aliasing_typecast((a))
+
+#ifdef CONFIG_VALGRIND
+#include <valgrind/memcheck.h>
+#define WPA_MEM_DEFINED(ptr, len) VALGRIND_MAKE_MEM_DEFINED((ptr), (len))
+#else /* CONFIG_VALGRIND */
+#define WPA_MEM_DEFINED(ptr, len) do { } while (0)
+#endif /* CONFIG_VALGRIND */
+
+#endif /* COMMON_H */
diff --git a/freebsd/contrib/wpa/src/utils/eloop.c b/freebsd/contrib/wpa/src/utils/eloop.c
new file mode 100644
index 00000000..5b1cc862
--- /dev/null
+++ b/freebsd/contrib/wpa/src/utils/eloop.c
@@ -0,0 +1,1144 @@
+#include <machine/rtems-bsd-user-space.h>
+
+/*
+ * Event loop based on select() loop
+ * Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+#include <assert.h>
+
+#include "common.h"
+#include "trace.h"
+#include "list.h"
+#include "eloop.h"
+
+#if defined(CONFIG_ELOOP_POLL) && defined(CONFIG_ELOOP_EPOLL)
+#error Do not define both of poll and epoll
+#endif
+
+#if !defined(CONFIG_ELOOP_POLL) && !defined(CONFIG_ELOOP_EPOLL)
+#define CONFIG_ELOOP_SELECT
+#endif
+
+#ifdef CONFIG_ELOOP_POLL
+#include <poll.h>
+#endif /* CONFIG_ELOOP_POLL */
+
+#ifdef CONFIG_ELOOP_EPOLL
+#include <sys/epoll.h>
+#endif /* CONFIG_ELOOP_EPOLL */
+
+struct eloop_sock {
+ int sock;
+ void *eloop_data;
+ void *user_data;
+ eloop_sock_handler handler;
+ WPA_TRACE_REF(eloop);
+ WPA_TRACE_REF(user);
+ WPA_TRACE_INFO
+};
+
+struct eloop_timeout {
+ struct dl_list list;
+ struct os_reltime time;
+ void *eloop_data;
+ void *user_data;
+ eloop_timeout_handler handler;
+ WPA_TRACE_REF(eloop);
+ WPA_TRACE_REF(user);
+ WPA_TRACE_INFO
+};
+
+struct eloop_signal {
+ int sig;
+ void *user_data;
+ eloop_signal_handler handler;
+ int signaled;
+};
+
+struct eloop_sock_table {
+ int count;
+ struct eloop_sock *table;
+ eloop_event_type type;
+ int changed;
+};
+
+struct eloop_data {
+ int max_sock;
+
+ int count; /* sum of all table counts */
+#ifdef CONFIG_ELOOP_POLL
+ int max_pollfd_map; /* number of pollfds_map currently allocated */
+ int max_poll_fds; /* number of pollfds currently allocated */
+ struct pollfd *pollfds;
+ struct pollfd **pollfds_map;
+#endif /* CONFIG_ELOOP_POLL */
+#ifdef CONFIG_ELOOP_EPOLL
+ int epollfd;
+ int epoll_max_event_num;
+ int epoll_max_fd;
+ struct eloop_sock *epoll_table;
+ struct epoll_event *epoll_events;
+#endif /* CONFIG_ELOOP_EPOLL */
+ struct eloop_sock_table readers;
+ struct eloop_sock_table writers;
+ struct eloop_sock_table exceptions;
+
+ struct dl_list timeout;
+
+ int signal_count;
+ struct eloop_signal *signals;
+ int signaled;
+ int pending_terminate;
+
+ int terminate;
+};
+
+static struct eloop_data eloop;
+
+
+#ifdef WPA_TRACE
+
+static void eloop_sigsegv_handler(int sig)
+{
+ wpa_trace_show("eloop SIGSEGV");
+ abort();
+}
+
+static void eloop_trace_sock_add_ref(struct eloop_sock_table *table)
+{
+ int i;
+ if (table == NULL || table->table == NULL)
+ return;
+ for (i = 0; i < table->count; i++) {
+ wpa_trace_add_ref(&table->table[i], eloop,
+ table->table[i].eloop_data);
+ wpa_trace_add_ref(&table->table[i], user,
+ table->table[i].user_data);
+ }
+}
+
+
+static void eloop_trace_sock_remove_ref(struct eloop_sock_table *table)
+{
+ int i;
+ if (table == NULL || table->table == NULL)
+ return;
+ for (i = 0; i < table->count; i++) {
+ wpa_trace_remove_ref(&table->table[i], eloop,
+ table->table[i].eloop_data);
+ wpa_trace_remove_ref(&table->table[i], user,
+ table->table[i].user_data);
+ }
+}
+
+#else /* WPA_TRACE */
+
+#define eloop_trace_sock_add_ref(table) do { } while (0)
+#define eloop_trace_sock_remove_ref(table) do { } while (0)
+
+#endif /* WPA_TRACE */
+
+
+int eloop_init(void)
+{
+ os_memset(&eloop, 0, sizeof(eloop));
+ dl_list_init(&eloop.timeout);
+#ifdef CONFIG_ELOOP_EPOLL
+ eloop.epollfd = epoll_create1(0);
+ if (eloop.epollfd < 0) {
+ wpa_printf(MSG_ERROR, "%s: epoll_create1 failed. %s\n",
+ __func__, strerror(errno));
+ return -1;
+ }
+ eloop.readers.type = EVENT_TYPE_READ;
+ eloop.writers.type = EVENT_TYPE_WRITE;
+ eloop.exceptions.type = EVENT_TYPE_EXCEPTION;
+#endif /* CONFIG_ELOOP_EPOLL */
+#ifdef WPA_TRACE
+ signal(SIGSEGV, eloop_sigsegv_handler);
+#endif /* WPA_TRACE */
+ return 0;
+}
+
+
+static int eloop_sock_table_add_sock(struct eloop_sock_table *table,
+ int sock, eloop_sock_handler handler,
+ void *eloop_data, void *user_data)
+{
+#ifdef CONFIG_ELOOP_EPOLL
+ struct eloop_sock *temp_table;
+ struct epoll_event ev, *temp_events;
+ int next;
+#endif /* CONFIG_ELOOP_EPOLL */
+ struct eloop_sock *tmp;
+ int new_max_sock;
+
+ if (sock > eloop.max_sock)
+ new_max_sock = sock;
+ else
+ new_max_sock = eloop.max_sock;
+
+ if (table == NULL)
+ return -1;
+
+#ifdef CONFIG_ELOOP_POLL
+ if (new_max_sock >= eloop.max_pollfd_map) {
+ struct pollfd **nmap;
+ nmap = os_realloc_array(eloop.pollfds_map, new_max_sock + 50,
+ sizeof(struct pollfd *));
+ if (nmap == NULL)
+ return -1;
+
+ eloop.max_pollfd_map = new_max_sock + 50;
+ eloop.pollfds_map = nmap;
+ }
+
+ if (eloop.count + 1 > eloop.max_poll_fds) {
+ struct pollfd *n;
+ int nmax = eloop.count + 1 + 50;
+ n = os_realloc_array(eloop.pollfds, nmax,
+ sizeof(struct pollfd));
+ if (n == NULL)
+ return -1;
+
+ eloop.max_poll_fds = nmax;
+ eloop.pollfds = n;
+ }
+#endif /* CONFIG_ELOOP_POLL */
+#ifdef CONFIG_ELOOP_EPOLL
+ if (new_max_sock >= eloop.epoll_max_fd) {
+ next = eloop.epoll_max_fd == 0 ? 16 : eloop.epoll_max_fd * 2;
+ temp_table = os_realloc_array(eloop.epoll_table, next,
+ sizeof(struct eloop_sock));
+ if (temp_table == NULL)
+ return -1;
+
+ eloop.epoll_max_fd = next;
+ eloop.epoll_table = temp_table;
+ }
+
+ if (eloop.count + 1 > eloop.epoll_max_event_num) {
+ next = eloop.epoll_max_event_num == 0 ? 8 :
+ eloop.epoll_max_event_num * 2;
+ temp_events = os_realloc_array(eloop.epoll_events, next,
+ sizeof(struct epoll_event));
+ if (temp_events == NULL) {
+ wpa_printf(MSG_ERROR, "%s: malloc for epoll failed. "
+ "%s\n", __func__, strerror(errno));
+ return -1;
+ }
+
+ eloop.epoll_max_event_num = next;
+ eloop.epoll_events = temp_events;
+ }
+#endif /* CONFIG_ELOOP_EPOLL */
+
+ eloop_trace_sock_remove_ref(table);
+ tmp = os_realloc_array(table->table, table->count + 1,
+ sizeof(struct eloop_sock));
+ if (tmp == NULL) {
+ eloop_trace_sock_add_ref(table);
+ return -1;
+ }
+
+ tmp[table->count].sock = sock;
+ tmp[table->count].eloop_data = eloop_data;
+ tmp[table->count].user_data = user_data;
+ tmp[table->count].handler = handler;
+ wpa_trace_record(&tmp[table->count]);
+ table->count++;
+ table->table = tmp;
+ eloop.max_sock = new_max_sock;
+ eloop.count++;
+ table->changed = 1;
+ eloop_trace_sock_add_ref(table);
+
+#ifdef CONFIG_ELOOP_EPOLL
+ os_memset(&ev, 0, sizeof(ev));
+ switch (table->type) {
+ case EVENT_TYPE_READ:
+ ev.events = EPOLLIN;
+ break;
+ case EVENT_TYPE_WRITE:
+ ev.events = EPOLLOUT;
+ break;
+ /*
+ * Exceptions are always checked when using epoll, but I suppose it's
+ * possible that someone registered a socket *only* for exception
+ * handling.
+ */
+ case EVENT_TYPE_EXCEPTION:
+ ev.events = EPOLLERR | EPOLLHUP;
+ break;
+ }
+ ev.data.fd = sock;
+ if (epoll_ctl(eloop.epollfd, EPOLL_CTL_ADD, sock, &ev) < 0) {
+ wpa_printf(MSG_ERROR, "%s: epoll_ctl(ADD) for fd=%d "
+ "failed. %s\n", __func__, sock, strerror(errno));
+ return -1;
+ }
+ os_memcpy(&eloop.epoll_table[sock], &table->table[table->count - 1],
+ sizeof(struct eloop_sock));
+#endif /* CONFIG_ELOOP_EPOLL */
+ return 0;
+}
+
+
+static void eloop_sock_table_remove_sock(struct eloop_sock_table *table,
+ int sock)
+{
+ int i;
+
+ if (table == NULL || table->table == NULL || table->count == 0)
+ return;
+
+ for (i = 0; i < table->count; i++) {
+ if (table->table[i].sock == sock)
+ break;
+ }
+ if (i == table->count)
+ return;
+ eloop_trace_sock_remove_ref(table);
+ if (i != table->count - 1) {
+ os_memmove(&table->table[i], &table->table[i + 1],
+ (table->count - i - 1) *
+ sizeof(struct eloop_sock));
+ }
+ table->count--;
+ eloop.count--;
+ table->changed = 1;
+ eloop_trace_sock_add_ref(table);
+#ifdef CONFIG_ELOOP_EPOLL
+ if (epoll_ctl(eloop.epollfd, EPOLL_CTL_DEL, sock, NULL) < 0) {
+ wpa_printf(MSG_ERROR, "%s: epoll_ctl(DEL) for fd=%d "
+ "failed. %s\n", __func__, sock, strerror(errno));
+ return;
+ }
+ os_memset(&eloop.epoll_table[sock], 0, sizeof(struct eloop_sock));
+#endif /* CONFIG_ELOOP_EPOLL */
+}
+
+
+#ifdef CONFIG_ELOOP_POLL
+
+static struct pollfd * find_pollfd(struct pollfd **pollfds_map, int fd, int mx)
+{
+ if (fd < mx && fd >= 0)
+ return pollfds_map[fd];
+ return NULL;
+}
+
+
+static int eloop_sock_table_set_fds(struct eloop_sock_table *readers,
+ struct eloop_sock_table *writers,
+ struct eloop_sock_table *exceptions,
+ struct pollfd *pollfds,
+ struct pollfd **pollfds_map,
+ int max_pollfd_map)
+{
+ int i;
+ int nxt = 0;
+ int fd;
+ struct pollfd *pfd;
+
+ /* Clear pollfd lookup map. It will be re-populated below. */
+ os_memset(pollfds_map, 0, sizeof(struct pollfd *) * max_pollfd_map);
+
+ if (readers && readers->table) {
+ for (i = 0; i < readers->count; i++) {
+ fd = readers->table[i].sock;
+ assert(fd >= 0 && fd < max_pollfd_map);
+ pollfds[nxt].fd = fd;
+ pollfds[nxt].events = POLLIN;
+ pollfds[nxt].revents = 0;
+ pollfds_map[fd] = &(pollfds[nxt]);
+ nxt++;
+ }
+ }
+
+ if (writers && writers->table) {
+ for (i = 0; i < writers->count; i++) {
+ /*
+ * See if we already added this descriptor, update it
+ * if so.
+ */
+ fd = writers->table[i].sock;
+ assert(fd >= 0 && fd < max_pollfd_map);
+ pfd = pollfds_map[fd];
+ if (!pfd) {
+ pfd = &(pollfds[nxt]);
+ pfd->events = 0;
+ pfd->fd = fd;
+ pollfds[i].revents = 0;
+ pollfds_map[fd] = pfd;
+ nxt++;
+ }
+ pfd->events |= POLLOUT;
+ }
+ }
+
+ /*
+ * Exceptions are always checked when using poll, but I suppose it's
+ * possible that someone registered a socket *only* for exception
+ * handling. Set the POLLIN bit in this case.
+ */
+ if (exceptions && exceptions->table) {
+ for (i = 0; i < exceptions->count; i++) {
+ /*
+ * See if we already added this descriptor, just use it
+ * if so.
+ */
+ fd = exceptions->table[i].sock;
+ assert(fd >= 0 && fd < max_pollfd_map);
+ pfd = pollfds_map[fd];
+ if (!pfd) {
+ pfd = &(pollfds[nxt]);
+ pfd->events = POLLIN;
+ pfd->fd = fd;
+ pollfds[i].revents = 0;
+ pollfds_map[fd] = pfd;
+ nxt++;
+ }
+ }
+ }
+
+ return nxt;
+}
+
+
+static int eloop_sock_table_dispatch_table(struct eloop_sock_table *table,
+ struct pollfd **pollfds_map,
+ int max_pollfd_map,
+ short int revents)
+{
+ int i;
+ struct pollfd *pfd;
+
+ if (!table || !table->table)
+ return 0;
+
+ table->changed = 0;
+ for (i = 0; i < table->count; i++) {
+ pfd = find_pollfd(pollfds_map, table->table[i].sock,
+ max_pollfd_map);
+ if (!pfd)
+ continue;
+
+ if (!(pfd->revents & revents))
+ continue;
+
+ table->table[i].handler(table->table[i].sock,
+ table->table[i].eloop_data,
+ table->table[i].user_data);
+ if (table->changed)
+ return 1;
+ }
+
+ return 0;
+}
+
+
+static void eloop_sock_table_dispatch(struct eloop_sock_table *readers,
+ struct eloop_sock_table *writers,
+ struct eloop_sock_table *exceptions,
+ struct pollfd **pollfds_map,
+ int max_pollfd_map)
+{
+ if (eloop_sock_table_dispatch_table(readers, pollfds_map,
+ max_pollfd_map, POLLIN | POLLERR |
+ POLLHUP))
+ return; /* pollfds may be invalid at this point */
+
+ if (eloop_sock_table_dispatch_table(writers, pollfds_map,
+ max_pollfd_map, POLLOUT))
+ return; /* pollfds may be invalid at this point */
+
+ eloop_sock_table_dispatch_table(exceptions, pollfds_map,
+ max_pollfd_map, POLLERR | POLLHUP);
+}
+
+#endif /* CONFIG_ELOOP_POLL */
+
+#ifdef CONFIG_ELOOP_SELECT
+
+static void eloop_sock_table_set_fds(struct eloop_sock_table *table,
+ fd_set *fds)
+{
+ int i;
+
+ FD_ZERO(fds);
+
+ if (table->table == NULL)
+ return;
+
+ for (i = 0; i < table->count; i++) {
+ assert(table->table[i].sock >= 0);
+ FD_SET(table->table[i].sock, fds);
+ }
+}
+
+
+static void eloop_sock_table_dispatch(struct eloop_sock_table *table,
+ fd_set *fds)
+{
+ int i;
+
+ if (table == NULL || table->table == NULL)
+ return;
+
+ table->changed = 0;
+ for (i = 0; i < table->count; i++) {
+ if (FD_ISSET(table->table[i].sock, fds)) {
+ table->table[i].handler(table->table[i].sock,
+ table->table[i].eloop_data,
+ table->table[i].user_data);
+ if (table->changed)
+ break;
+ }
+ }
+}
+
+#endif /* CONFIG_ELOOP_SELECT */
+
+
+#ifdef CONFIG_ELOOP_EPOLL
+static void eloop_sock_table_dispatch(struct epoll_event *events, int nfds)
+{
+ struct eloop_sock *table;
+ int i;
+
+ for (i = 0; i < nfds; i++) {
+ table = &eloop.epoll_table[events[i].data.fd];
+ if (table->handler == NULL)
+ continue;
+ table->handler(table->sock, table->eloop_data,
+ table->user_data);
+ if (eloop.readers.changed ||
+ eloop.writers.changed ||
+ eloop.exceptions.changed)
+ break;
+ }
+}
+#endif /* CONFIG_ELOOP_EPOLL */
+
+
+static void eloop_sock_table_destroy(struct eloop_sock_table *table)
+{
+ if (table) {
+ int i;
+ for (i = 0; i < table->count && table->table; i++) {
+ wpa_printf(MSG_INFO, "ELOOP: remaining socket: "
+ "sock=%d eloop_data=%p user_data=%p "
+ "handler=%p",
+ table->table[i].sock,
+ table->table[i].eloop_data,
+ table->table[i].user_data,
+ table->table[i].handler);
+ wpa_trace_dump_funcname("eloop unregistered socket "
+ "handler",
+ table->table[i].handler);
+ wpa_trace_dump("eloop sock", &table->table[i]);
+ }
+ os_free(table->table);
+ }
+}
+
+
+int eloop_register_read_sock(int sock, eloop_sock_handler handler,
+ void *eloop_data, void *user_data)
+{
+ return eloop_register_sock(sock, EVENT_TYPE_READ, handler,
+ eloop_data, user_data);
+}
+
+
+void eloop_unregister_read_sock(int sock)
+{
+ eloop_unregister_sock(sock, EVENT_TYPE_READ);
+}
+
+
+static struct eloop_sock_table *eloop_get_sock_table(eloop_event_type type)
+{
+ switch (type) {
+ case EVENT_TYPE_READ:
+ return &eloop.readers;
+ case EVENT_TYPE_WRITE:
+ return &eloop.writers;
+ case EVENT_TYPE_EXCEPTION:
+ return &eloop.exceptions;
+ }
+
+ return NULL;
+}
+
+
+int eloop_register_sock(int sock, eloop_event_type type,
+ eloop_sock_handler handler,
+ void *eloop_data, void *user_data)
+{
+ struct eloop_sock_table *table;
+
+ assert(sock >= 0);
+ table = eloop_get_sock_table(type);
+ return eloop_sock_table_add_sock(table, sock, handler,
+ eloop_data, user_data);
+}
+
+
+void eloop_unregister_sock(int sock, eloop_event_type type)
+{
+ struct eloop_sock_table *table;
+
+ table = eloop_get_sock_table(type);
+ eloop_sock_table_remove_sock(table, sock);
+}
+
+
+int eloop_register_timeout(unsigned int secs, unsigned int usecs,
+ eloop_timeout_handler handler,
+ void *eloop_data, void *user_data)
+{
+ struct eloop_timeout *timeout, *tmp;
+ os_time_t now_sec;
+
+ timeout = os_zalloc(sizeof(*timeout));
+ if (timeout == NULL)
+ return -1;
+ if (os_get_reltime(&timeout->time) < 0) {
+ os_free(timeout);
+ return -1;
+ }
+ now_sec = timeout->time.sec;
+ timeout->time.sec += secs;
+ if (timeout->time.sec < now_sec) {
+ /*
+ * Integer overflow - assume long enough timeout to be assumed
+ * to be infinite, i.e., the timeout would never happen.
+ */
+ wpa_printf(MSG_DEBUG, "ELOOP: Too long timeout (secs=%u) to "
+ "ever happen - ignore it", secs);
+ os_free(timeout);
+ return 0;
+ }
+ timeout->time.usec += usecs;
+ while (timeout->time.usec >= 1000000) {
+ timeout->time.sec++;
+ timeout->time.usec -= 1000000;
+ }
+ timeout->eloop_data = eloop_data;
+ timeout->user_data = user_data;
+ timeout->handler = handler;
+ wpa_trace_add_ref(timeout, eloop, eloop_data);
+ wpa_trace_add_ref(timeout, user, user_data);
+ wpa_trace_record(timeout);
+
+ /* Maintain timeouts in order of increasing time */
+ dl_list_for_each(tmp, &eloop.timeout, struct eloop_timeout, list) {
+ if (os_reltime_before(&timeout->time, &tmp->time)) {
+ dl_list_add(tmp->list.prev, &timeout->list);
+ return 0;
+ }
+ }
+ dl_list_add_tail(&eloop.timeout, &timeout->list);
+
+ return 0;
+}
+
+
+static void eloop_remove_timeout(struct eloop_timeout *timeout)
+{
+ dl_list_del(&timeout->list);
+ wpa_trace_remove_ref(timeout, eloop, timeout->eloop_data);
+ wpa_trace_remove_ref(timeout, user, timeout->user_data);
+ os_free(timeout);
+}
+
+
+int eloop_cancel_timeout(eloop_timeout_handler handler,
+ void *eloop_data, void *user_data)
+{
+ struct eloop_timeout *timeout, *prev;
+ int removed = 0;
+
+ dl_list_for_each_safe(timeout, prev, &eloop.timeout,
+ struct eloop_timeout, list) {
+ if (timeout->handler == handler &&
+ (timeout->eloop_data == eloop_data ||
+ eloop_data == ELOOP_ALL_CTX) &&
+ (timeout->user_data == user_data ||
+ user_data == ELOOP_ALL_CTX)) {
+ eloop_remove_timeout(timeout);
+ removed++;
+ }
+ }
+
+ return removed;
+}
+
+
+int eloop_cancel_timeout_one(eloop_timeout_handler handler,
+ void *eloop_data, void *user_data,
+ struct os_reltime *remaining)
+{
+ struct eloop_timeout *timeout, *prev;
+ int removed = 0;
+ struct os_reltime now;
+
+ os_get_reltime(&now);
+ remaining->sec = remaining->usec = 0;
+
+ dl_list_for_each_safe(timeout, prev, &eloop.timeout,
+ struct eloop_timeout, list) {
+ if (timeout->handler == handler &&
+ (timeout->eloop_data == eloop_data) &&
+ (timeout->user_data == user_data)) {
+ removed = 1;
+ if (os_reltime_before(&now, &timeout->time))
+ os_reltime_sub(&timeout->time, &now, remaining);
+ eloop_remove_timeout(timeout);
+ break;
+ }
+ }
+ return removed;
+}
+
+
+int eloop_is_timeout_registered(eloop_timeout_handler handler,
+ void *eloop_data, void *user_data)
+{
+ struct eloop_timeout *tmp;
+
+ dl_list_for_each(tmp, &eloop.timeout, struct eloop_timeout, list) {
+ if (tmp->handler == handler &&
+ tmp->eloop_data == eloop_data &&
+ tmp->user_data == user_data)
+ return 1;
+ }
+
+ return 0;
+}
+
+
+int eloop_deplete_timeout(unsigned int req_secs, unsigned int req_usecs,
+ eloop_timeout_handler handler, void *eloop_data,
+ void *user_data)
+{
+ struct os_reltime now, requested, remaining;
+ struct eloop_timeout *tmp;
+
+ dl_list_for_each(tmp, &eloop.timeout, struct eloop_timeout, list) {
+ if (tmp->handler == handler &&
+ tmp->eloop_data == eloop_data &&
+ tmp->user_data == user_data) {
+ requested.sec = req_secs;
+ requested.usec = req_usecs;
+ os_get_reltime(&now);
+ os_reltime_sub(&tmp->time, &now, &remaining);
+ if (os_reltime_before(&requested, &remaining)) {
+ eloop_cancel_timeout(handler, eloop_data,
+ user_data);
+ eloop_register_timeout(requested.sec,
+ requested.usec,
+ handler, eloop_data,
+ user_data);
+ return 1;
+ }
+ return 0;
+ }
+ }
+
+ return -1;
+}
+
+
+int eloop_replenish_timeout(unsigned int req_secs, unsigned int req_usecs,
+ eloop_timeout_handler handler, void *eloop_data,
+ void *user_data)
+{
+ struct os_reltime now, requested, remaining;
+ struct eloop_timeout *tmp;
+
+ dl_list_for_each(tmp, &eloop.timeout, struct eloop_timeout, list) {
+ if (tmp->handler == handler &&
+ tmp->eloop_data == eloop_data &&
+ tmp->user_data == user_data) {
+ requested.sec = req_secs;
+ requested.usec = req_usecs;
+ os_get_reltime(&now);
+ os_reltime_sub(&tmp->time, &now, &remaining);
+ if (os_reltime_before(&remaining, &requested)) {
+ eloop_cancel_timeout(handler, eloop_data,
+ user_data);
+ eloop_register_timeout(requested.sec,
+ requested.usec,
+ handler, eloop_data,
+ user_data);
+ return 1;
+ }
+ return 0;
+ }
+ }
+
+ return -1;
+}
+
+
+#ifndef CONFIG_NATIVE_WINDOWS
+static void eloop_handle_alarm(int sig)
+{
+ wpa_printf(MSG_ERROR, "eloop: could not process SIGINT or SIGTERM in "
+ "two seconds. Looks like there\n"
+ "is a bug that ends up in a busy loop that "
+ "prevents clean shutdown.\n"
+ "Killing program forcefully.\n");
+ exit(1);
+}
+#endif /* CONFIG_NATIVE_WINDOWS */
+
+
+static void eloop_handle_signal(int sig)
+{
+ int i;
+
+#ifndef CONFIG_NATIVE_WINDOWS
+ if ((sig == SIGINT || sig == SIGTERM) && !eloop.pending_terminate) {
+ /* Use SIGALRM to break out from potential busy loops that
+ * would not allow the program to be killed. */
+ eloop.pending_terminate = 1;
+ signal(SIGALRM, eloop_handle_alarm);
+ alarm(2);
+ }
+#endif /* CONFIG_NATIVE_WINDOWS */
+
+ eloop.signaled++;
+ for (i = 0; i < eloop.signal_count; i++) {
+ if (eloop.signals[i].sig == sig) {
+ eloop.signals[i].signaled++;
+ break;
+ }
+ }
+}
+
+
+static void eloop_process_pending_signals(void)
+{
+ int i;
+
+ if (eloop.signaled == 0)
+ return;
+ eloop.signaled = 0;
+
+ if (eloop.pending_terminate) {
+#ifndef CONFIG_NATIVE_WINDOWS
+ alarm(0);
+#endif /* CONFIG_NATIVE_WINDOWS */
+ eloop.pending_terminate = 0;
+ }
+
+ for (i = 0; i < eloop.signal_count; i++) {
+ if (eloop.signals[i].signaled) {
+ eloop.signals[i].signaled = 0;
+ eloop.signals[i].handler(eloop.signals[i].sig,
+ eloop.signals[i].user_data);
+ }
+ }
+}
+
+
+int eloop_register_signal(int sig, eloop_signal_handler handler,
+ void *user_data)
+{
+ struct eloop_signal *tmp;
+
+ tmp = os_realloc_array(eloop.signals, eloop.signal_count + 1,
+ sizeof(struct eloop_signal));
+ if (tmp == NULL)
+ return -1;
+
+ tmp[eloop.signal_count].sig = sig;
+ tmp[eloop.signal_count].user_data = user_data;
+ tmp[eloop.signal_count].handler = handler;
+ tmp[eloop.signal_count].signaled = 0;
+ eloop.signal_count++;
+ eloop.signals = tmp;
+ signal(sig, eloop_handle_signal);
+
+ return 0;
+}
+
+
+int eloop_register_signal_terminate(eloop_signal_handler handler,
+ void *user_data)
+{
+ int ret = eloop_register_signal(SIGINT, handler, user_data);
+ if (ret == 0)
+ ret = eloop_register_signal(SIGTERM, handler, user_data);
+ return ret;
+}
+
+
+int eloop_register_signal_reconfig(eloop_signal_handler handler,
+ void *user_data)
+{
+#ifdef CONFIG_NATIVE_WINDOWS
+ return 0;
+#else /* CONFIG_NATIVE_WINDOWS */
+ return eloop_register_signal(SIGHUP, handler, user_data);
+#endif /* CONFIG_NATIVE_WINDOWS */
+}
+
+
+void eloop_run(void)
+{
+#ifdef CONFIG_ELOOP_POLL
+ int num_poll_fds;
+ int timeout_ms = 0;
+#endif /* CONFIG_ELOOP_POLL */
+#ifdef CONFIG_ELOOP_SELECT
+ fd_set *rfds, *wfds, *efds;
+ struct timeval _tv;
+#endif /* CONFIG_ELOOP_SELECT */
+#ifdef CONFIG_ELOOP_EPOLL
+ int timeout_ms = -1;
+#endif /* CONFIG_ELOOP_EPOLL */
+ int res;
+ struct os_reltime tv, now;
+
+#ifdef CONFIG_ELOOP_SELECT
+ rfds = os_malloc(sizeof(*rfds));
+ wfds = os_malloc(sizeof(*wfds));
+ efds = os_malloc(sizeof(*efds));
+ if (rfds == NULL || wfds == NULL || efds == NULL)
+ goto out;
+#endif /* CONFIG_ELOOP_SELECT */
+
+ while (!eloop.terminate &&
+ (!dl_list_empty(&eloop.timeout) || eloop.readers.count > 0 ||
+ eloop.writers.count > 0 || eloop.exceptions.count > 0)) {
+ struct eloop_timeout *timeout;
+
+ if (eloop.pending_terminate) {
+ /*
+ * This may happen in some corner cases where a signal
+ * is received during a blocking operation. We need to
+ * process the pending signals and exit if requested to
+ * avoid hitting the SIGALRM limit if the blocking
+ * operation took more than two seconds.
+ */
+ eloop_process_pending_signals();
+ if (eloop.terminate)
+ break;
+ }
+
+ timeout = dl_list_first(&eloop.timeout, struct eloop_timeout,
+ list);
+ if (timeout) {
+ os_get_reltime(&now);
+ if (os_reltime_before(&now, &timeout->time))
+ os_reltime_sub(&timeout->time, &now, &tv);
+ else
+ tv.sec = tv.usec = 0;
+#if defined(CONFIG_ELOOP_POLL) || defined(CONFIG_ELOOP_EPOLL)
+ timeout_ms = tv.sec * 1000 + tv.usec / 1000;
+#endif /* defined(CONFIG_ELOOP_POLL) || defined(CONFIG_ELOOP_EPOLL) */
+#ifdef CONFIG_ELOOP_SELECT
+ _tv.tv_sec = tv.sec;
+ _tv.tv_usec = tv.usec;
+#endif /* CONFIG_ELOOP_SELECT */
+ }
+
+#ifdef CONFIG_ELOOP_POLL
+ num_poll_fds = eloop_sock_table_set_fds(
+ &eloop.readers, &eloop.writers, &eloop.exceptions,
+ eloop.pollfds, eloop.pollfds_map,
+ eloop.max_pollfd_map);
+ res = poll(eloop.pollfds, num_poll_fds,
+ timeout ? timeout_ms : -1);
+#endif /* CONFIG_ELOOP_POLL */
+#ifdef CONFIG_ELOOP_SELECT
+ eloop_sock_table_set_fds(&eloop.readers, rfds);
+ eloop_sock_table_set_fds(&eloop.writers, wfds);
+ eloop_sock_table_set_fds(&eloop.exceptions, efds);
+ res = select(eloop.max_sock + 1, rfds, wfds, efds,
+ timeout ? &_tv : NULL);
+#endif /* CONFIG_ELOOP_SELECT */
+#ifdef CONFIG_ELOOP_EPOLL
+ if (eloop.count == 0) {
+ res = 0;
+ } else {
+ res = epoll_wait(eloop.epollfd, eloop.epoll_events,
+ eloop.count, timeout_ms);
+ }
+#endif /* CONFIG_ELOOP_EPOLL */
+ if (res < 0 && errno != EINTR && errno != 0) {
+ wpa_printf(MSG_ERROR, "eloop: %s: %s",
+#ifdef CONFIG_ELOOP_POLL
+ "poll"
+#endif /* CONFIG_ELOOP_POLL */
+#ifdef CONFIG_ELOOP_SELECT
+ "select"
+#endif /* CONFIG_ELOOP_SELECT */
+#ifdef CONFIG_ELOOP_EPOLL
+ "epoll"
+#endif /* CONFIG_ELOOP_EPOLL */
+ , strerror(errno));
+ goto out;
+ }
+
+ eloop.readers.changed = 0;
+ eloop.writers.changed = 0;
+ eloop.exceptions.changed = 0;
+
+ eloop_process_pending_signals();
+
+ /* check if some registered timeouts have occurred */
+ timeout = dl_list_first(&eloop.timeout, struct eloop_timeout,
+ list);
+ if (timeout) {
+ os_get_reltime(&now);
+ if (!os_reltime_before(&now, &timeout->time)) {
+ void *eloop_data = timeout->eloop_data;
+ void *user_data = timeout->user_data;
+ eloop_timeout_handler handler =
+ timeout->handler;
+ eloop_remove_timeout(timeout);
+ handler(eloop_data, user_data);
+ }
+
+ }
+
+ if (res <= 0)
+ continue;
+
+ if (eloop.readers.changed ||
+ eloop.writers.changed ||
+ eloop.exceptions.changed) {
+ /*
+ * Sockets may have been closed and reopened with the
+ * same FD in the signal or timeout handlers, so we
+ * must skip the previous results and check again
+ * whether any of the currently registered sockets have
+ * events.
+ */
+ continue;
+ }
+
+#ifdef CONFIG_ELOOP_POLL
+ eloop_sock_table_dispatch(&eloop.readers, &eloop.writers,
+ &eloop.exceptions, eloop.pollfds_map,
+ eloop.max_pollfd_map);
+#endif /* CONFIG_ELOOP_POLL */
+#ifdef CONFIG_ELOOP_SELECT
+ eloop_sock_table_dispatch(&eloop.readers, rfds);
+ eloop_sock_table_dispatch(&eloop.writers, wfds);
+ eloop_sock_table_dispatch(&eloop.exceptions, efds);
+#endif /* CONFIG_ELOOP_SELECT */
+#ifdef CONFIG_ELOOP_EPOLL
+ eloop_sock_table_dispatch(eloop.epoll_events, res);
+#endif /* CONFIG_ELOOP_EPOLL */
+ }
+
+ eloop.terminate = 0;
+out:
+#ifdef CONFIG_ELOOP_SELECT
+ os_free(rfds);
+ os_free(wfds);
+ os_free(efds);
+#endif /* CONFIG_ELOOP_SELECT */
+ return;
+}
+
+
+void eloop_terminate(void)
+{
+ eloop.terminate = 1;
+}
+
+
+void eloop_destroy(void)
+{
+ struct eloop_timeout *timeout, *prev;
+ struct os_reltime now;
+
+ os_get_reltime(&now);
+ dl_list_for_each_safe(timeout, prev, &eloop.timeout,
+ struct eloop_timeout, list) {
+ int sec, usec;
+ sec = timeout->time.sec - now.sec;
+ usec = timeout->time.usec - now.usec;
+ if (timeout->time.usec < now.usec) {
+ sec--;
+ usec += 1000000;
+ }
+ wpa_printf(MSG_INFO, "ELOOP: remaining timeout: %d.%06d "
+ "eloop_data=%p user_data=%p handler=%p",
+ sec, usec, timeout->eloop_data, timeout->user_data,
+ timeout->handler);
+ wpa_trace_dump_funcname("eloop unregistered timeout handler",
+ timeout->handler);
+ wpa_trace_dump("eloop timeout", timeout);
+ eloop_remove_timeout(timeout);
+ }
+ eloop_sock_table_destroy(&eloop.readers);
+ eloop_sock_table_destroy(&eloop.writers);
+ eloop_sock_table_destroy(&eloop.exceptions);
+ os_free(eloop.signals);
+
+#ifdef CONFIG_ELOOP_POLL
+ os_free(eloop.pollfds);
+ os_free(eloop.pollfds_map);
+#endif /* CONFIG_ELOOP_POLL */
+#ifdef CONFIG_ELOOP_EPOLL
+ os_free(eloop.epoll_table);
+ os_free(eloop.epoll_events);
+ close(eloop.epollfd);
+#endif /* CONFIG_ELOOP_EPOLL */
+}
+
+
+int eloop_terminated(void)
+{
+ return eloop.terminate || eloop.pending_terminate;
+}
+
+
+void eloop_wait_for_read_sock(int sock)
+{
+#ifdef CONFIG_ELOOP_POLL
+ struct pollfd pfd;
+
+ if (sock < 0)
+ return;
+
+ os_memset(&pfd, 0, sizeof(pfd));
+ pfd.fd = sock;
+ pfd.events = POLLIN;
+
+ poll(&pfd, 1, -1);
+#endif /* CONFIG_ELOOP_POLL */
+#if defined(CONFIG_ELOOP_SELECT) || defined(CONFIG_ELOOP_EPOLL)
+ /*
+ * We can use epoll() here. But epoll() requres 4 system calls.
+ * epoll_create1(), epoll_ctl() for ADD, epoll_wait, and close() for
+ * epoll fd. So select() is better for performance here.
+ */
+ fd_set rfds;
+
+ if (sock < 0)
+ return;
+
+ FD_ZERO(&rfds);
+ FD_SET(sock, &rfds);
+ select(sock + 1, &rfds, NULL, NULL, NULL);
+#endif /* defined(CONFIG_ELOOP_SELECT) || defined(CONFIG_ELOOP_EPOLL) */
+}
+
+#ifdef CONFIG_ELOOP_SELECT
+#undef CONFIG_ELOOP_SELECT
+#endif /* CONFIG_ELOOP_SELECT */
diff --git a/freebsd/contrib/wpa/src/utils/eloop.h b/freebsd/contrib/wpa/src/utils/eloop.h
new file mode 100644
index 00000000..07b8c0dc
--- /dev/null
+++ b/freebsd/contrib/wpa/src/utils/eloop.h
@@ -0,0 +1,359 @@
+/*
+ * Event loop
+ * Copyright (c) 2002-2006, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ *
+ * This file defines an event loop interface that supports processing events
+ * from registered timeouts (i.e., do something after N seconds), sockets
+ * (e.g., a new packet available for reading), and signals. eloop.c is an
+ * implementation of this interface using select() and sockets. This is
+ * suitable for most UNIX/POSIX systems. When porting to other operating
+ * systems, it may be necessary to replace that implementation with OS specific
+ * mechanisms.
+ */
+
+#ifndef ELOOP_H
+#define ELOOP_H
+
+/**
+ * ELOOP_ALL_CTX - eloop_cancel_timeout() magic number to match all timeouts
+ */
+#define ELOOP_ALL_CTX (void *) -1
+
+/**
+ * eloop_event_type - eloop socket event type for eloop_register_sock()
+ * @EVENT_TYPE_READ: Socket has data available for reading
+ * @EVENT_TYPE_WRITE: Socket has room for new data to be written
+ * @EVENT_TYPE_EXCEPTION: An exception has been reported
+ */
+typedef enum {
+ EVENT_TYPE_READ = 0,
+ EVENT_TYPE_WRITE,
+ EVENT_TYPE_EXCEPTION
+} eloop_event_type;
+
+/**
+ * eloop_sock_handler - eloop socket event callback type
+ * @sock: File descriptor number for the socket
+ * @eloop_ctx: Registered callback context data (eloop_data)
+ * @sock_ctx: Registered callback context data (user_data)
+ */
+typedef void (*eloop_sock_handler)(int sock, void *eloop_ctx, void *sock_ctx);
+
+/**
+ * eloop_event_handler - eloop generic event callback type
+ * @eloop_ctx: Registered callback context data (eloop_data)
+ * @sock_ctx: Registered callback context data (user_data)
+ */
+typedef void (*eloop_event_handler)(void *eloop_data, void *user_ctx);
+
+/**
+ * eloop_timeout_handler - eloop timeout event callback type
+ * @eloop_ctx: Registered callback context data (eloop_data)
+ * @sock_ctx: Registered callback context data (user_data)
+ */
+typedef void (*eloop_timeout_handler)(void *eloop_data, void *user_ctx);
+
+/**
+ * eloop_signal_handler - eloop signal event callback type
+ * @sig: Signal number
+ * @signal_ctx: Registered callback context data (user_data from
+ * eloop_register_signal(), eloop_register_signal_terminate(), or
+ * eloop_register_signal_reconfig() call)
+ */
+typedef void (*eloop_signal_handler)(int sig, void *signal_ctx);
+
+/**
+ * eloop_init() - Initialize global event loop data
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function must be called before any other eloop_* function.
+ */
+int eloop_init(void);
+
+/**
+ * eloop_register_read_sock - Register handler for read events
+ * @sock: File descriptor number for the socket
+ * @handler: Callback function to be called when data is available for reading
+ * @eloop_data: Callback context data (eloop_ctx)
+ * @user_data: Callback context data (sock_ctx)
+ * Returns: 0 on success, -1 on failure
+ *
+ * Register a read socket notifier for the given file descriptor. The handler
+ * function will be called whenever data is available for reading from the
+ * socket. The handler function is responsible for clearing the event after
+ * having processed it in order to avoid eloop from calling the handler again
+ * for the same event.
+ */
+int eloop_register_read_sock(int sock, eloop_sock_handler handler,
+ void *eloop_data, void *user_data);
+
+/**
+ * eloop_unregister_read_sock - Unregister handler for read events
+ * @sock: File descriptor number for the socket
+ *
+ * Unregister a read socket notifier that was previously registered with
+ * eloop_register_read_sock().
+ */
+void eloop_unregister_read_sock(int sock);
+
+/**
+ * eloop_register_sock - Register handler for socket events
+ * @sock: File descriptor number for the socket
+ * @type: Type of event to wait for
+ * @handler: Callback function to be called when the event is triggered
+ * @eloop_data: Callback context data (eloop_ctx)
+ * @user_data: Callback context data (sock_ctx)
+ * Returns: 0 on success, -1 on failure
+ *
+ * Register an event notifier for the given socket's file descriptor. The
+ * handler function will be called whenever the that event is triggered for the
+ * socket. The handler function is responsible for clearing the event after
+ * having processed it in order to avoid eloop from calling the handler again
+ * for the same event.
+ */
+int eloop_register_sock(int sock, eloop_event_type type,
+ eloop_sock_handler handler,
+ void *eloop_data, void *user_data);
+
+/**
+ * eloop_unregister_sock - Unregister handler for socket events
+ * @sock: File descriptor number for the socket
+ * @type: Type of event for which sock was registered
+ *
+ * Unregister a socket event notifier that was previously registered with
+ * eloop_register_sock().
+ */
+void eloop_unregister_sock(int sock, eloop_event_type type);
+
+/**
+ * eloop_register_event - Register handler for generic events
+ * @event: Event to wait (eloop implementation specific)
+ * @event_size: Size of event data
+ * @handler: Callback function to be called when event is triggered
+ * @eloop_data: Callback context data (eloop_data)
+ * @user_data: Callback context data (user_data)
+ * Returns: 0 on success, -1 on failure
+ *
+ * Register an event handler for the given event. This function is used to
+ * register eloop implementation specific events which are mainly targeted for
+ * operating system specific code (driver interface and l2_packet) since the
+ * portable code will not be able to use such an OS-specific call. The handler
+ * function will be called whenever the event is triggered. The handler
+ * function is responsible for clearing the event after having processed it in
+ * order to avoid eloop from calling the handler again for the same event.
+ *
+ * In case of Windows implementation (eloop_win.c), event pointer is of HANDLE
+ * type, i.e., void*. The callers are likely to have 'HANDLE h' type variable,
+ * and they would call this function with eloop_register_event(h, sizeof(h),
+ * ...).
+ */
+int eloop_register_event(void *event, size_t event_size,
+ eloop_event_handler handler,
+ void *eloop_data, void *user_data);
+
+/**
+ * eloop_unregister_event - Unregister handler for a generic event
+ * @event: Event to cancel (eloop implementation specific)
+ * @event_size: Size of event data
+ *
+ * Unregister a generic event notifier that was previously registered with
+ * eloop_register_event().
+ */
+void eloop_unregister_event(void *event, size_t event_size);
+
+/**
+ * eloop_register_timeout - Register timeout
+ * @secs: Number of seconds to the timeout
+ * @usecs: Number of microseconds to the timeout
+ * @handler: Callback function to be called when timeout occurs
+ * @eloop_data: Callback context data (eloop_ctx)
+ * @user_data: Callback context data (sock_ctx)
+ * Returns: 0 on success, -1 on failure
+ *
+ * Register a timeout that will cause the handler function to be called after
+ * given time.
+ */
+int eloop_register_timeout(unsigned int secs, unsigned int usecs,
+ eloop_timeout_handler handler,
+ void *eloop_data, void *user_data);
+
+/**
+ * eloop_cancel_timeout - Cancel timeouts
+ * @handler: Matching callback function
+ * @eloop_data: Matching eloop_data or %ELOOP_ALL_CTX to match all
+ * @user_data: Matching user_data or %ELOOP_ALL_CTX to match all
+ * Returns: Number of cancelled timeouts
+ *
+ * Cancel matching <handler,eloop_data,user_data> timeouts registered with
+ * eloop_register_timeout(). ELOOP_ALL_CTX can be used as a wildcard for
+ * cancelling all timeouts regardless of eloop_data/user_data.
+ */
+int eloop_cancel_timeout(eloop_timeout_handler handler,
+ void *eloop_data, void *user_data);
+
+/**
+ * eloop_cancel_timeout_one - Cancel a single timeout
+ * @handler: Matching callback function
+ * @eloop_data: Matching eloop_data
+ * @user_data: Matching user_data
+ * @remaining: Time left on the cancelled timer
+ * Returns: Number of cancelled timeouts
+ *
+ * Cancel matching <handler,eloop_data,user_data> timeout registered with
+ * eloop_register_timeout() and return the remaining time left.
+ */
+int eloop_cancel_timeout_one(eloop_timeout_handler handler,
+ void *eloop_data, void *user_data,
+ struct os_reltime *remaining);
+
+/**
+ * eloop_is_timeout_registered - Check if a timeout is already registered
+ * @handler: Matching callback function
+ * @eloop_data: Matching eloop_data
+ * @user_data: Matching user_data
+ * Returns: 1 if the timeout is registered, 0 if the timeout is not registered
+ *
+ * Determine if a matching <handler,eloop_data,user_data> timeout is registered
+ * with eloop_register_timeout().
+ */
+int eloop_is_timeout_registered(eloop_timeout_handler handler,
+ void *eloop_data, void *user_data);
+
+/**
+ * eloop_deplete_timeout - Deplete a timeout that is already registered
+ * @req_secs: Requested number of seconds to the timeout
+ * @req_usecs: Requested number of microseconds to the timeout
+ * @handler: Matching callback function
+ * @eloop_data: Matching eloop_data
+ * @user_data: Matching user_data
+ * Returns: 1 if the timeout is depleted, 0 if no change is made, -1 if no
+ * timeout matched
+ *
+ * Find a registered matching <handler,eloop_data,user_data> timeout. If found,
+ * deplete the timeout if remaining time is more than the requested time.
+ */
+int eloop_deplete_timeout(unsigned int req_secs, unsigned int req_usecs,
+ eloop_timeout_handler handler, void *eloop_data,
+ void *user_data);
+
+/**
+ * eloop_replenish_timeout - Replenish a timeout that is already registered
+ * @req_secs: Requested number of seconds to the timeout
+ * @req_usecs: Requested number of microseconds to the timeout
+ * @handler: Matching callback function
+ * @eloop_data: Matching eloop_data
+ * @user_data: Matching user_data
+ * Returns: 1 if the timeout is replenished, 0 if no change is made, -1 if no
+ * timeout matched
+ *
+ * Find a registered matching <handler,eloop_data,user_data> timeout. If found,
+ * replenish the timeout if remaining time is less than the requested time.
+ */
+int eloop_replenish_timeout(unsigned int req_secs, unsigned int req_usecs,
+ eloop_timeout_handler handler, void *eloop_data,
+ void *user_data);
+
+/**
+ * eloop_register_signal - Register handler for signals
+ * @sig: Signal number (e.g., SIGHUP)
+ * @handler: Callback function to be called when the signal is received
+ * @user_data: Callback context data (signal_ctx)
+ * Returns: 0 on success, -1 on failure
+ *
+ * Register a callback function that will be called when a signal is received.
+ * The callback function is actually called only after the system signal
+ * handler has returned. This means that the normal limits for sighandlers
+ * (i.e., only "safe functions" allowed) do not apply for the registered
+ * callback.
+ */
+int eloop_register_signal(int sig, eloop_signal_handler handler,
+ void *user_data);
+
+/**
+ * eloop_register_signal_terminate - Register handler for terminate signals
+ * @handler: Callback function to be called when the signal is received
+ * @user_data: Callback context data (signal_ctx)
+ * Returns: 0 on success, -1 on failure
+ *
+ * Register a callback function that will be called when a process termination
+ * signal is received. The callback function is actually called only after the
+ * system signal handler has returned. This means that the normal limits for
+ * sighandlers (i.e., only "safe functions" allowed) do not apply for the
+ * registered callback.
+ *
+ * This function is a more portable version of eloop_register_signal() since
+ * the knowledge of exact details of the signals is hidden in eloop
+ * implementation. In case of operating systems using signal(), this function
+ * registers handlers for SIGINT and SIGTERM.
+ */
+int eloop_register_signal_terminate(eloop_signal_handler handler,
+ void *user_data);
+
+/**
+ * eloop_register_signal_reconfig - Register handler for reconfig signals
+ * @handler: Callback function to be called when the signal is received
+ * @user_data: Callback context data (signal_ctx)
+ * Returns: 0 on success, -1 on failure
+ *
+ * Register a callback function that will be called when a reconfiguration /
+ * hangup signal is received. The callback function is actually called only
+ * after the system signal handler has returned. This means that the normal
+ * limits for sighandlers (i.e., only "safe functions" allowed) do not apply
+ * for the registered callback.
+ *
+ * This function is a more portable version of eloop_register_signal() since
+ * the knowledge of exact details of the signals is hidden in eloop
+ * implementation. In case of operating systems using signal(), this function
+ * registers a handler for SIGHUP.
+ */
+int eloop_register_signal_reconfig(eloop_signal_handler handler,
+ void *user_data);
+
+/**
+ * eloop_run - Start the event loop
+ *
+ * Start the event loop and continue running as long as there are any
+ * registered event handlers. This function is run after event loop has been
+ * initialized with event_init() and one or more events have been registered.
+ */
+void eloop_run(void);
+
+/**
+ * eloop_terminate - Terminate event loop
+ *
+ * Terminate event loop even if there are registered events. This can be used
+ * to request the program to be terminated cleanly.
+ */
+void eloop_terminate(void);
+
+/**
+ * eloop_destroy - Free any resources allocated for the event loop
+ *
+ * After calling eloop_destroy(), other eloop_* functions must not be called
+ * before re-running eloop_init().
+ */
+void eloop_destroy(void);
+
+/**
+ * eloop_terminated - Check whether event loop has been terminated
+ * Returns: 1 = event loop terminate, 0 = event loop still running
+ *
+ * This function can be used to check whether eloop_terminate() has been called
+ * to request termination of the event loop. This is normally used to abort
+ * operations that may still be queued to be run when eloop_terminate() was
+ * called.
+ */
+int eloop_terminated(void);
+
+/**
+ * eloop_wait_for_read_sock - Wait for a single reader
+ * @sock: File descriptor number for the socket
+ *
+ * Do a blocking wait for a single read socket.
+ */
+void eloop_wait_for_read_sock(int sock);
+
+#endif /* ELOOP_H */
diff --git a/freebsd/contrib/wpa/src/utils/ext_password.h b/freebsd/contrib/wpa/src/utils/ext_password.h
new file mode 100644
index 00000000..e3e46ea0
--- /dev/null
+++ b/freebsd/contrib/wpa/src/utils/ext_password.h
@@ -0,0 +1,33 @@
+/*
+ * 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.
+ */
+
+#ifndef EXT_PASSWORD_H
+#define EXT_PASSWORD_H
+
+struct ext_password_data;
+
+#ifdef CONFIG_EXT_PASSWORD
+
+struct ext_password_data * ext_password_init(const char *backend,
+ const char *params);
+void ext_password_deinit(struct ext_password_data *data);
+
+struct wpabuf * ext_password_get(struct ext_password_data *data,
+ const char *name);
+void ext_password_free(struct wpabuf *pw);
+
+#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)
+
+#endif /* CONFIG_EXT_PASSWORD */
+
+#endif /* EXT_PASSWORD_H */
diff --git a/freebsd/contrib/wpa/src/utils/includes.h b/freebsd/contrib/wpa/src/utils/includes.h
new file mode 100644
index 00000000..75513fc8
--- /dev/null
+++ b/freebsd/contrib/wpa/src/utils/includes.h
@@ -0,0 +1,45 @@
+/*
+ * wpa_supplicant/hostapd - Default include files
+ * Copyright (c) 2005-2006, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ *
+ * This header file is included into all C files so that commonly used header
+ * files can be selected with OS specific ifdef blocks in one place instead of
+ * having to have OS/C library specific selection in many files.
+ */
+
+#ifndef INCLUDES_H
+#define INCLUDES_H
+
+/* Include possible build time configuration before including anything else */
+#include "build_config.h"
+
+#include <stdlib.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <string.h>
+#ifndef _WIN32_WCE
+#include <signal.h>
+#include <sys/types.h>
+#include <errno.h>
+#endif /* _WIN32_WCE */
+#include <ctype.h>
+
+#ifndef _MSC_VER
+#include <unistd.h>
+#endif /* _MSC_VER */
+
+#ifndef CONFIG_NATIVE_WINDOWS
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#ifndef __vxworks
+#include <sys/uio.h>
+#include <sys/time.h>
+#endif /* __vxworks */
+#endif /* CONFIG_NATIVE_WINDOWS */
+
+#endif /* INCLUDES_H */
diff --git a/freebsd/contrib/wpa/src/utils/ip_addr.h b/freebsd/contrib/wpa/src/utils/ip_addr.h
new file mode 100644
index 00000000..0670411c
--- /dev/null
+++ b/freebsd/contrib/wpa/src/utils/ip_addr.h
@@ -0,0 +1,27 @@
+/*
+ * IP address processing
+ * Copyright (c) 2003-2006, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef IP_ADDR_H
+#define IP_ADDR_H
+
+struct hostapd_ip_addr {
+ int af; /* AF_INET / AF_INET6 */
+ union {
+ struct in_addr v4;
+#ifdef CONFIG_IPV6
+ struct in6_addr v6;
+#endif /* CONFIG_IPV6 */
+ u8 max_len[16];
+ } u;
+};
+
+const char * hostapd_ip_txt(const struct hostapd_ip_addr *addr, char *buf,
+ size_t buflen);
+int hostapd_parse_ip_addr(const char *txt, struct hostapd_ip_addr *addr);
+
+#endif /* IP_ADDR_H */
diff --git a/freebsd/contrib/wpa/src/utils/list.h b/freebsd/contrib/wpa/src/utils/list.h
new file mode 100644
index 00000000..ee2f4856
--- /dev/null
+++ b/freebsd/contrib/wpa/src/utils/list.h
@@ -0,0 +1,97 @@
+/*
+ * Doubly-linked list
+ * 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.
+ */
+
+#ifndef LIST_H
+#define LIST_H
+
+/**
+ * struct dl_list - Doubly-linked list
+ */
+struct dl_list {
+ struct dl_list *next;
+ struct dl_list *prev;
+};
+
+#define DL_LIST_HEAD_INIT(l) { &(l), &(l) }
+
+static inline void dl_list_init(struct dl_list *list)
+{
+ list->next = list;
+ list->prev = list;
+}
+
+static inline void dl_list_add(struct dl_list *list, struct dl_list *item)
+{
+ item->next = list->next;
+ item->prev = list;
+ list->next->prev = item;
+ list->next = item;
+}
+
+static inline void dl_list_add_tail(struct dl_list *list, struct dl_list *item)
+{
+ dl_list_add(list->prev, item);
+}
+
+static inline void dl_list_del(struct dl_list *item)
+{
+ item->next->prev = item->prev;
+ item->prev->next = item->next;
+ item->next = NULL;
+ item->prev = NULL;
+}
+
+static inline int dl_list_empty(struct dl_list *list)
+{
+ return list->next == list;
+}
+
+static inline unsigned int dl_list_len(struct dl_list *list)
+{
+ struct dl_list *item;
+ int count = 0;
+ for (item = list->next; item != list; item = item->next)
+ count++;
+ return count;
+}
+
+#ifndef offsetof
+#define offsetof(type, member) ((long) &((type *) 0)->member)
+#endif
+
+#define dl_list_entry(item, type, member) \
+ ((type *) ((char *) item - offsetof(type, member)))
+
+#define dl_list_first(list, type, member) \
+ (dl_list_empty((list)) ? NULL : \
+ dl_list_entry((list)->next, type, member))
+
+#define dl_list_last(list, type, member) \
+ (dl_list_empty((list)) ? NULL : \
+ dl_list_entry((list)->prev, type, member))
+
+#define dl_list_for_each(item, list, type, member) \
+ for (item = dl_list_entry((list)->next, type, member); \
+ &item->member != (list); \
+ item = dl_list_entry(item->member.next, type, member))
+
+#define dl_list_for_each_safe(item, n, list, type, member) \
+ for (item = dl_list_entry((list)->next, type, member), \
+ n = dl_list_entry(item->member.next, type, member); \
+ &item->member != (list); \
+ item = n, n = dl_list_entry(n->member.next, type, member))
+
+#define dl_list_for_each_reverse(item, list, type, member) \
+ for (item = dl_list_entry((list)->prev, type, member); \
+ &item->member != (list); \
+ item = dl_list_entry(item->member.prev, type, member))
+
+#define DEFINE_DL_LIST(name) \
+ struct dl_list name = { &(name), &(name) }
+
+#endif /* LIST_H */
diff --git a/freebsd/contrib/wpa/src/utils/os.h b/freebsd/contrib/wpa/src/utils/os.h
new file mode 100644
index 00000000..9e496fb6
--- /dev/null
+++ b/freebsd/contrib/wpa/src/utils/os.h
@@ -0,0 +1,664 @@
+/*
+ * OS specific functions
+ * Copyright (c) 2005-2009, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef OS_H
+#define OS_H
+
+typedef long os_time_t;
+
+/**
+ * os_sleep - Sleep (sec, usec)
+ * @sec: Number of seconds to sleep
+ * @usec: Number of microseconds to sleep
+ */
+void os_sleep(os_time_t sec, os_time_t usec);
+
+struct os_time {
+ os_time_t sec;
+ os_time_t usec;
+};
+
+struct os_reltime {
+ os_time_t sec;
+ os_time_t usec;
+};
+
+/**
+ * os_get_time - Get current time (sec, usec)
+ * @t: Pointer to buffer for the time
+ * Returns: 0 on success, -1 on failure
+ */
+int os_get_time(struct os_time *t);
+
+/**
+ * os_get_reltime - Get relative time (sec, usec)
+ * @t: Pointer to buffer for the time
+ * Returns: 0 on success, -1 on failure
+ */
+int os_get_reltime(struct os_reltime *t);
+
+
+/* Helpers for handling struct os_time */
+
+static inline int os_time_before(struct os_time *a, struct os_time *b)
+{
+ return (a->sec < b->sec) ||
+ (a->sec == b->sec && a->usec < b->usec);
+}
+
+
+static inline void os_time_sub(struct os_time *a, struct os_time *b,
+ struct os_time *res)
+{
+ res->sec = a->sec - b->sec;
+ res->usec = a->usec - b->usec;
+ if (res->usec < 0) {
+ res->sec--;
+ res->usec += 1000000;
+ }
+}
+
+
+/* Helpers for handling struct os_reltime */
+
+static inline int os_reltime_before(struct os_reltime *a,
+ struct os_reltime *b)
+{
+ return (a->sec < b->sec) ||
+ (a->sec == b->sec && a->usec < b->usec);
+}
+
+
+static inline void os_reltime_sub(struct os_reltime *a, struct os_reltime *b,
+ struct os_reltime *res)
+{
+ res->sec = a->sec - b->sec;
+ res->usec = a->usec - b->usec;
+ if (res->usec < 0) {
+ res->sec--;
+ res->usec += 1000000;
+ }
+}
+
+
+static inline void os_reltime_age(struct os_reltime *start,
+ struct os_reltime *age)
+{
+ struct os_reltime now;
+
+ os_get_reltime(&now);
+ os_reltime_sub(&now, start, age);
+}
+
+
+static inline int os_reltime_expired(struct os_reltime *now,
+ struct os_reltime *ts,
+ os_time_t timeout_secs)
+{
+ struct os_reltime age;
+
+ os_reltime_sub(now, ts, &age);
+ return (age.sec > timeout_secs) ||
+ (age.sec == timeout_secs && age.usec > 0);
+}
+
+
+static inline int os_reltime_initialized(struct os_reltime *t)
+{
+ return t->sec != 0 || t->usec != 0;
+}
+
+
+/**
+ * os_mktime - Convert broken-down time into seconds since 1970-01-01
+ * @year: Four digit year
+ * @month: Month (1 .. 12)
+ * @day: Day of month (1 .. 31)
+ * @hour: Hour (0 .. 23)
+ * @min: Minute (0 .. 59)
+ * @sec: Second (0 .. 60)
+ * @t: Buffer for returning calendar time representation (seconds since
+ * 1970-01-01 00:00:00)
+ * Returns: 0 on success, -1 on failure
+ *
+ * Note: The result is in seconds from Epoch, i.e., in UTC, not in local time
+ * which is used by POSIX mktime().
+ */
+int os_mktime(int year, int month, int day, int hour, int min, int sec,
+ os_time_t *t);
+
+struct os_tm {
+ int sec; /* 0..59 or 60 for leap seconds */
+ int min; /* 0..59 */
+ int hour; /* 0..23 */
+ int day; /* 1..31 */
+ int month; /* 1..12 */
+ int year; /* Four digit year */
+};
+
+int os_gmtime(os_time_t t, struct os_tm *tm);
+
+/**
+ * os_daemonize - Run in the background (detach from the controlling terminal)
+ * @pid_file: File name to write the process ID to or %NULL to skip this
+ * Returns: 0 on success, -1 on failure
+ */
+int os_daemonize(const char *pid_file);
+
+/**
+ * os_daemonize_terminate - Stop running in the background (remove pid file)
+ * @pid_file: File name to write the process ID to or %NULL to skip this
+ */
+void os_daemonize_terminate(const char *pid_file);
+
+/**
+ * os_get_random - Get cryptographically strong pseudo random data
+ * @buf: Buffer for pseudo random data
+ * @len: Length of the buffer
+ * Returns: 0 on success, -1 on failure
+ */
+int os_get_random(unsigned char *buf, size_t len);
+
+/**
+ * os_random - Get pseudo random value (not necessarily very strong)
+ * Returns: Pseudo random value
+ */
+unsigned long os_random(void);
+
+/**
+ * os_rel2abs_path - Get an absolute path for a file
+ * @rel_path: Relative path to a file
+ * Returns: Absolute path for the file or %NULL on failure
+ *
+ * This function tries to convert a relative path of a file to an absolute path
+ * in order for the file to be found even if current working directory has
+ * changed. The returned value is allocated and caller is responsible for
+ * freeing it. It is acceptable to just return the same path in an allocated
+ * buffer, e.g., return strdup(rel_path). This function is only used to find
+ * configuration files when os_daemonize() may have changed the current working
+ * directory and relative path would be pointing to a different location.
+ */
+char * os_rel2abs_path(const char *rel_path);
+
+/**
+ * os_program_init - Program initialization (called at start)
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function is called when a programs starts. If there are any OS specific
+ * processing that is needed, it can be placed here. It is also acceptable to
+ * just return 0 if not special processing is needed.
+ */
+int os_program_init(void);
+
+/**
+ * os_program_deinit - Program deinitialization (called just before exit)
+ *
+ * This function is called just before a program exists. If there are any OS
+ * specific processing, e.g., freeing resourced allocated in os_program_init(),
+ * it should be done here. It is also acceptable for this function to do
+ * nothing.
+ */
+void os_program_deinit(void);
+
+/**
+ * os_setenv - Set environment variable
+ * @name: Name of the variable
+ * @value: Value to set to the variable
+ * @overwrite: Whether existing variable should be overwritten
+ * Returns: 0 on success, -1 on error
+ *
+ * This function is only used for wpa_cli action scripts. OS wrapper does not
+ * need to implement this if such functionality is not needed.
+ */
+int os_setenv(const char *name, const char *value, int overwrite);
+
+/**
+ * os_unsetenv - Delete environent variable
+ * @name: Name of the variable
+ * Returns: 0 on success, -1 on error
+ *
+ * This function is only used for wpa_cli action scripts. OS wrapper does not
+ * need to implement this if such functionality is not needed.
+ */
+int os_unsetenv(const char *name);
+
+/**
+ * os_readfile - Read a file to an allocated memory buffer
+ * @name: Name of the file to read
+ * @len: For returning the length of the allocated buffer
+ * Returns: Pointer to the allocated buffer or %NULL on failure
+ *
+ * This function allocates memory and reads the given file to this buffer. Both
+ * binary and text files can be read with this function. The caller is
+ * responsible for freeing the returned buffer with os_free().
+ */
+char * os_readfile(const char *name, size_t *len);
+
+/**
+ * os_file_exists - Check whether the specified file exists
+ * @fname: Path and name of the file
+ * Returns: 1 if the file exists or 0 if not
+ */
+int os_file_exists(const char *fname);
+
+/**
+ * os_fdatasync - Sync a file's (for a given stream) state with storage device
+ * @stream: the stream to be flushed
+ * Returns: 0 if the operation succeeded or -1 on failure
+ */
+int os_fdatasync(FILE *stream);
+
+/**
+ * os_zalloc - Allocate and zero memory
+ * @size: Number of bytes to allocate
+ * Returns: Pointer to allocated and zeroed memory or %NULL on failure
+ *
+ * Caller is responsible for freeing the returned buffer with os_free().
+ */
+void * os_zalloc(size_t size);
+
+/**
+ * os_calloc - Allocate and zero memory for an array
+ * @nmemb: Number of members in the array
+ * @size: Number of bytes in each member
+ * Returns: Pointer to allocated and zeroed memory or %NULL on failure
+ *
+ * This function can be used as a wrapper for os_zalloc(nmemb * size) when an
+ * allocation is used for an array. The main benefit over os_zalloc() is in
+ * having an extra check to catch integer overflows in multiplication.
+ *
+ * Caller is responsible for freeing the returned buffer with os_free().
+ */
+static inline void * os_calloc(size_t nmemb, size_t size)
+{
+ if (size && nmemb > (~(size_t) 0) / size)
+ return NULL;
+ return os_zalloc(nmemb * size);
+}
+
+
+/*
+ * The following functions are wrapper for standard ANSI C or POSIX functions.
+ * By default, they are just defined to use the standard function name and no
+ * os_*.c implementation is needed for them. This avoids extra function calls
+ * by allowing the C pre-processor take care of the function name mapping.
+ *
+ * If the target system uses a C library that does not provide these functions,
+ * build_config.h can be used to define the wrappers to use a different
+ * function name. This can be done on function-by-function basis since the
+ * defines here are only used if build_config.h does not define the os_* name.
+ * If needed, os_*.c file can be used to implement the functions that are not
+ * included in the C library on the target system. Alternatively,
+ * OS_NO_C_LIB_DEFINES can be defined to skip all defines here in which case
+ * these functions need to be implemented in os_*.c file for the target system.
+ */
+
+#ifdef OS_NO_C_LIB_DEFINES
+
+/**
+ * os_malloc - Allocate dynamic memory
+ * @size: Size of the buffer to allocate
+ * Returns: Allocated buffer or %NULL on failure
+ *
+ * Caller is responsible for freeing the returned buffer with os_free().
+ */
+void * os_malloc(size_t size);
+
+/**
+ * os_realloc - Re-allocate dynamic memory
+ * @ptr: Old buffer from os_malloc() or os_realloc()
+ * @size: Size of the new buffer
+ * Returns: Allocated buffer or %NULL on failure
+ *
+ * Caller is responsible for freeing the returned buffer with os_free().
+ * If re-allocation fails, %NULL is returned and the original buffer (ptr) is
+ * not freed and caller is still responsible for freeing it.
+ */
+void * os_realloc(void *ptr, size_t size);
+
+/**
+ * os_free - Free dynamic memory
+ * @ptr: Old buffer from os_malloc() or os_realloc(); can be %NULL
+ */
+void os_free(void *ptr);
+
+/**
+ * os_memcpy - Copy memory area
+ * @dest: Destination
+ * @src: Source
+ * @n: Number of bytes to copy
+ * Returns: dest
+ *
+ * The memory areas src and dst must not overlap. os_memmove() can be used with
+ * overlapping memory.
+ */
+void * os_memcpy(void *dest, const void *src, size_t n);
+
+/**
+ * os_memmove - Copy memory area
+ * @dest: Destination
+ * @src: Source
+ * @n: Number of bytes to copy
+ * Returns: dest
+ *
+ * The memory areas src and dst may overlap.
+ */
+void * os_memmove(void *dest, const void *src, size_t n);
+
+/**
+ * os_memset - Fill memory with a constant byte
+ * @s: Memory area to be filled
+ * @c: Constant byte
+ * @n: Number of bytes started from s to fill with c
+ * Returns: s
+ */
+void * os_memset(void *s, int c, size_t n);
+
+/**
+ * os_memcmp - Compare memory areas
+ * @s1: First buffer
+ * @s2: Second buffer
+ * @n: Maximum numbers of octets to compare
+ * Returns: An integer less than, equal to, or greater than zero if s1 is
+ * found to be less than, to match, or be greater than s2. Only first n
+ * characters will be compared.
+ */
+int os_memcmp(const void *s1, const void *s2, size_t n);
+
+/**
+ * os_strdup - Duplicate a string
+ * @s: Source string
+ * Returns: Allocated buffer with the string copied into it or %NULL on failure
+ *
+ * Caller is responsible for freeing the returned buffer with os_free().
+ */
+char * os_strdup(const char *s);
+
+/**
+ * os_strlen - Calculate the length of a string
+ * @s: '\0' terminated string
+ * Returns: Number of characters in s (not counting the '\0' terminator)
+ */
+size_t os_strlen(const char *s);
+
+/**
+ * os_strcasecmp - Compare two strings ignoring case
+ * @s1: First string
+ * @s2: Second string
+ * Returns: An integer less than, equal to, or greater than zero if s1 is
+ * found to be less than, to match, or be greatred than s2
+ */
+int os_strcasecmp(const char *s1, const char *s2);
+
+/**
+ * os_strncasecmp - Compare two strings ignoring case
+ * @s1: First string
+ * @s2: Second string
+ * @n: Maximum numbers of characters to compare
+ * Returns: An integer less than, equal to, or greater than zero if s1 is
+ * found to be less than, to match, or be greater than s2. Only first n
+ * characters will be compared.
+ */
+int os_strncasecmp(const char *s1, const char *s2, size_t n);
+
+/**
+ * os_strchr - Locate the first occurrence of a character in string
+ * @s: String
+ * @c: Character to search for
+ * Returns: Pointer to the matched character or %NULL if not found
+ */
+char * os_strchr(const char *s, int c);
+
+/**
+ * os_strrchr - Locate the last occurrence of a character in string
+ * @s: String
+ * @c: Character to search for
+ * Returns: Pointer to the matched character or %NULL if not found
+ */
+char * os_strrchr(const char *s, int c);
+
+/**
+ * os_strcmp - Compare two strings
+ * @s1: First string
+ * @s2: Second string
+ * Returns: An integer less than, equal to, or greater than zero if s1 is
+ * found to be less than, to match, or be greatred than s2
+ */
+int os_strcmp(const char *s1, const char *s2);
+
+/**
+ * os_strncmp - Compare two strings
+ * @s1: First string
+ * @s2: Second string
+ * @n: Maximum numbers of characters to compare
+ * Returns: An integer less than, equal to, or greater than zero if s1 is
+ * found to be less than, to match, or be greater than s2. Only first n
+ * characters will be compared.
+ */
+int os_strncmp(const char *s1, const char *s2, size_t n);
+
+/**
+ * os_strstr - Locate a substring
+ * @haystack: String (haystack) to search from
+ * @needle: Needle to search from haystack
+ * Returns: Pointer to the beginning of the substring or %NULL if not found
+ */
+char * os_strstr(const char *haystack, const char *needle);
+
+/**
+ * os_snprintf - Print to a memory buffer
+ * @str: Memory buffer to print into
+ * @size: Maximum length of the str buffer
+ * @format: printf format
+ * Returns: Number of characters printed (not including trailing '\0').
+ *
+ * If the output buffer is truncated, number of characters which would have
+ * been written is returned. Since some C libraries return -1 in such a case,
+ * the caller must be prepared on that value, too, to indicate truncation.
+ *
+ * Note: Some C library implementations of snprintf() may not guarantee null
+ * termination in case the output is truncated. The OS wrapper function of
+ * os_snprintf() should provide this guarantee, i.e., to null terminate the
+ * output buffer if a C library version of the function is used and if that
+ * function does not guarantee null termination.
+ *
+ * If the target system does not include snprintf(), see, e.g.,
+ * http://www.ijs.si/software/snprintf/ for an example of a portable
+ * implementation of snprintf.
+ */
+int os_snprintf(char *str, size_t size, const char *format, ...);
+
+#else /* OS_NO_C_LIB_DEFINES */
+
+#ifdef WPA_TRACE
+void * os_malloc(size_t size);
+void * os_realloc(void *ptr, size_t size);
+void os_free(void *ptr);
+char * os_strdup(const char *s);
+#else /* WPA_TRACE */
+#ifndef os_malloc
+#define os_malloc(s) malloc((s))
+#endif
+#ifndef os_realloc
+#define os_realloc(p, s) realloc((p), (s))
+#endif
+#ifndef os_free
+#define os_free(p) free((p))
+#endif
+#ifndef os_strdup
+#ifdef _MSC_VER
+#define os_strdup(s) _strdup(s)
+#else
+#define os_strdup(s) strdup(s)
+#endif
+#endif
+#endif /* WPA_TRACE */
+
+#ifndef os_memcpy
+#define os_memcpy(d, s, n) memcpy((d), (s), (n))
+#endif
+#ifndef os_memmove
+#define os_memmove(d, s, n) memmove((d), (s), (n))
+#endif
+#ifndef os_memset
+#define os_memset(s, c, n) memset(s, c, n)
+#endif
+#ifndef os_memcmp
+#define os_memcmp(s1, s2, n) memcmp((s1), (s2), (n))
+#endif
+
+#ifndef os_strlen
+#define os_strlen(s) strlen(s)
+#endif
+#ifndef os_strcasecmp
+#ifdef _MSC_VER
+#define os_strcasecmp(s1, s2) _stricmp((s1), (s2))
+#else
+#define os_strcasecmp(s1, s2) strcasecmp((s1), (s2))
+#endif
+#endif
+#ifndef os_strncasecmp
+#ifdef _MSC_VER
+#define os_strncasecmp(s1, s2, n) _strnicmp((s1), (s2), (n))
+#else
+#define os_strncasecmp(s1, s2, n) strncasecmp((s1), (s2), (n))
+#endif
+#endif
+#ifndef os_strchr
+#define os_strchr(s, c) strchr((s), (c))
+#endif
+#ifndef os_strcmp
+#define os_strcmp(s1, s2) strcmp((s1), (s2))
+#endif
+#ifndef os_strncmp
+#define os_strncmp(s1, s2, n) strncmp((s1), (s2), (n))
+#endif
+#ifndef os_strrchr
+#define os_strrchr(s, c) strrchr((s), (c))
+#endif
+#ifndef os_strstr
+#define os_strstr(h, n) strstr((h), (n))
+#endif
+
+#ifndef os_snprintf
+#ifdef _MSC_VER
+#define os_snprintf _snprintf
+#else
+#define os_snprintf snprintf
+#endif
+#endif
+
+#endif /* OS_NO_C_LIB_DEFINES */
+
+
+static inline int os_snprintf_error(size_t size, int res)
+{
+ return res < 0 || (unsigned int) res >= size;
+}
+
+
+static inline void * os_realloc_array(void *ptr, size_t nmemb, size_t size)
+{
+ if (size && nmemb > (~(size_t) 0) / size)
+ return NULL;
+ return os_realloc(ptr, nmemb * size);
+}
+
+/**
+ * os_remove_in_array - Remove a member from an array by index
+ * @ptr: Pointer to the array
+ * @nmemb: Current member count of the array
+ * @size: The size per member of the array
+ * @idx: Index of the member to be removed
+ */
+static inline void os_remove_in_array(void *ptr, size_t nmemb, size_t size,
+ size_t idx)
+{
+ if (idx < nmemb - 1)
+ os_memmove(((unsigned char *) ptr) + idx * size,
+ ((unsigned char *) ptr) + (idx + 1) * size,
+ (nmemb - idx - 1) * size);
+}
+
+/**
+ * os_strlcpy - Copy a string with size bound and NUL-termination
+ * @dest: Destination
+ * @src: Source
+ * @siz: Size of the target buffer
+ * Returns: Total length of the target string (length of src) (not including
+ * NUL-termination)
+ *
+ * This function matches in behavior with the strlcpy(3) function in OpenBSD.
+ */
+size_t os_strlcpy(char *dest, const char *src, size_t siz);
+
+/**
+ * os_memcmp_const - Constant time memory comparison
+ * @a: First buffer to compare
+ * @b: Second buffer to compare
+ * @len: Number of octets to compare
+ * Returns: 0 if buffers are equal, non-zero if not
+ *
+ * This function is meant for comparing passwords or hash values where
+ * difference in execution time could provide external observer information
+ * about the location of the difference in the memory buffers. The return value
+ * does not behave like os_memcmp(), i.e., os_memcmp_const() cannot be used to
+ * sort items into a defined order. Unlike os_memcmp(), execution time of
+ * os_memcmp_const() does not depend on the contents of the compared memory
+ * buffers, but only on the total compared length.
+ */
+int os_memcmp_const(const void *a, const void *b, size_t len);
+
+/**
+ * os_exec - Execute an external program
+ * @program: Path to the program
+ * @arg: Command line argument string
+ * @wait_completion: Whether to wait until the program execution completes
+ * Returns: 0 on success, -1 on error
+ */
+int os_exec(const char *program, const char *arg, int wait_completion);
+
+
+#ifdef OS_REJECT_C_LIB_FUNCTIONS
+#define malloc OS_DO_NOT_USE_malloc
+#define realloc OS_DO_NOT_USE_realloc
+#define free OS_DO_NOT_USE_free
+#define memcpy OS_DO_NOT_USE_memcpy
+#define memmove OS_DO_NOT_USE_memmove
+#define memset OS_DO_NOT_USE_memset
+#define memcmp OS_DO_NOT_USE_memcmp
+#undef strdup
+#define strdup OS_DO_NOT_USE_strdup
+#define strlen OS_DO_NOT_USE_strlen
+#define strcasecmp OS_DO_NOT_USE_strcasecmp
+#define strncasecmp OS_DO_NOT_USE_strncasecmp
+#undef strchr
+#define strchr OS_DO_NOT_USE_strchr
+#undef strcmp
+#define strcmp OS_DO_NOT_USE_strcmp
+#undef strncmp
+#define strncmp OS_DO_NOT_USE_strncmp
+#undef strncpy
+#define strncpy OS_DO_NOT_USE_strncpy
+#define strrchr OS_DO_NOT_USE_strrchr
+#define strstr OS_DO_NOT_USE_strstr
+#undef snprintf
+#define snprintf OS_DO_NOT_USE_snprintf
+
+#define strcpy OS_DO_NOT_USE_strcpy
+#endif /* OS_REJECT_C_LIB_FUNCTIONS */
+
+
+#if defined(WPA_TRACE_BFD) && defined(CONFIG_TESTING_OPTIONS)
+#define TEST_FAIL() testing_test_fail()
+int testing_test_fail(void);
+#else
+#define TEST_FAIL() 0
+#endif
+
+#endif /* OS_H */
diff --git a/freebsd/contrib/wpa/src/utils/os_unix.c b/freebsd/contrib/wpa/src/utils/os_unix.c
new file mode 100644
index 00000000..a3abce4c
--- /dev/null
+++ b/freebsd/contrib/wpa/src/utils/os_unix.c
@@ -0,0 +1,851 @@
+#include <machine/rtems-bsd-user-space.h>
+
+/*
+ * OS specific functions for UNIX/POSIX systems
+ * Copyright (c) 2005-2009, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include <time.h>
+#include <sys/wait.h>
+
+#ifdef ANDROID
+#include <sys/capability.h>
+#include <sys/prctl.h>
+#include <private/android_filesystem_config.h>
+#endif /* ANDROID */
+
+#ifdef __MACH__
+#include <CoreServices/CoreServices.h>
+#include <mach/mach.h>
+#include <mach/mach_time.h>
+#endif /* __MACH__ */
+
+#include "os.h"
+#include "common.h"
+
+#ifdef WPA_TRACE
+
+#include "wpa_debug.h"
+#include "trace.h"
+#include "list.h"
+
+static struct dl_list alloc_list = DL_LIST_HEAD_INIT(alloc_list);
+
+#define ALLOC_MAGIC 0xa84ef1b2
+#define FREED_MAGIC 0x67fd487a
+
+struct os_alloc_trace {
+ unsigned int magic;
+ struct dl_list list;
+ size_t len;
+ WPA_TRACE_INFO
+} __attribute__((aligned(16)));
+
+#endif /* WPA_TRACE */
+
+
+void os_sleep(os_time_t sec, os_time_t usec)
+{
+ if (sec)
+ sleep(sec);
+ if (usec)
+ usleep(usec);
+}
+
+
+int os_get_time(struct os_time *t)
+{
+ int res;
+ struct timeval tv;
+ res = gettimeofday(&tv, NULL);
+ t->sec = tv.tv_sec;
+ t->usec = tv.tv_usec;
+ return res;
+}
+
+
+int os_get_reltime(struct os_reltime *t)
+{
+#ifndef __MACH__
+#if defined(CLOCK_BOOTTIME)
+ static clockid_t clock_id = CLOCK_BOOTTIME;
+#elif defined(CLOCK_MONOTONIC)
+ static clockid_t clock_id = CLOCK_MONOTONIC;
+#else
+ static clockid_t clock_id = CLOCK_REALTIME;
+#endif
+ struct timespec ts;
+ int res;
+
+ while (1) {
+ res = clock_gettime(clock_id, &ts);
+ if (res == 0) {
+ t->sec = ts.tv_sec;
+ t->usec = ts.tv_nsec / 1000;
+ return 0;
+ }
+ switch (clock_id) {
+#ifdef CLOCK_BOOTTIME
+ case CLOCK_BOOTTIME:
+ clock_id = CLOCK_MONOTONIC;
+ break;
+#endif
+#ifdef CLOCK_MONOTONIC
+ case CLOCK_MONOTONIC:
+ clock_id = CLOCK_REALTIME;
+ break;
+#endif
+ case CLOCK_REALTIME:
+ return -1;
+ }
+ }
+#else /* __MACH__ */
+ uint64_t abstime, nano;
+ static mach_timebase_info_data_t info = { 0, 0 };
+
+ if (!info.denom) {
+ if (mach_timebase_info(&info) != KERN_SUCCESS)
+ return -1;
+ }
+
+ abstime = mach_absolute_time();
+ nano = (abstime * info.numer) / info.denom;
+
+ t->sec = nano / NSEC_PER_SEC;
+ t->usec = (nano - (((uint64_t) t->sec) * NSEC_PER_SEC)) / NSEC_PER_USEC;
+
+ return 0;
+#endif /* __MACH__ */
+}
+
+
+int os_mktime(int year, int month, int day, int hour, int min, int sec,
+ os_time_t *t)
+{
+ struct tm tm, *tm1;
+ time_t t_local, t1, t2;
+ os_time_t tz_offset;
+
+ if (year < 1970 || month < 1 || month > 12 || day < 1 || day > 31 ||
+ hour < 0 || hour > 23 || min < 0 || min > 59 || sec < 0 ||
+ sec > 60)
+ return -1;
+
+ memset(&tm, 0, sizeof(tm));
+ tm.tm_year = year - 1900;
+ tm.tm_mon = month - 1;
+ tm.tm_mday = day;
+ tm.tm_hour = hour;
+ tm.tm_min = min;
+ tm.tm_sec = sec;
+
+ t_local = mktime(&tm);
+
+ /* figure out offset to UTC */
+ tm1 = localtime(&t_local);
+ if (tm1) {
+ t1 = mktime(tm1);
+ tm1 = gmtime(&t_local);
+ if (tm1) {
+ t2 = mktime(tm1);
+ tz_offset = t2 - t1;
+ } else
+ tz_offset = 0;
+ } else
+ tz_offset = 0;
+
+ *t = (os_time_t) t_local - tz_offset;
+ return 0;
+}
+
+
+int os_gmtime(os_time_t t, struct os_tm *tm)
+{
+ struct tm *tm2;
+ time_t t2 = t;
+
+ tm2 = gmtime(&t2);
+ if (tm2 == NULL)
+ return -1;
+ tm->sec = tm2->tm_sec;
+ tm->min = tm2->tm_min;
+ tm->hour = tm2->tm_hour;
+ tm->day = tm2->tm_mday;
+ tm->month = tm2->tm_mon + 1;
+ tm->year = tm2->tm_year + 1900;
+ return 0;
+}
+
+
+#ifdef __APPLE__
+#include <fcntl.h>
+static int os_daemon(int nochdir, int noclose)
+{
+ int devnull;
+
+ if (chdir("/") < 0)
+ return -1;
+
+ devnull = open("/dev/null", O_RDWR);
+ if (devnull < 0)
+ return -1;
+
+ if (dup2(devnull, STDIN_FILENO) < 0) {
+ close(devnull);
+ return -1;
+ }
+
+ if (dup2(devnull, STDOUT_FILENO) < 0) {
+ close(devnull);
+ return -1;
+ }
+
+ if (dup2(devnull, STDERR_FILENO) < 0) {
+ close(devnull);
+ return -1;
+ }
+
+ return 0;
+}
+#else /* __APPLE__ */
+#define os_daemon daemon
+#endif /* __APPLE__ */
+
+
+#ifdef __FreeBSD__
+#include <err.h>
+#include <libutil.h>
+#include <stdint.h>
+#endif /* __FreeBSD__ */
+
+int os_daemonize(const char *pid_file)
+{
+#if defined(__uClinux__) || defined(__sun__)
+ return -1;
+#else /* defined(__uClinux__) || defined(__sun__) */
+#ifdef __FreeBSD__
+ pid_t otherpid;
+ struct pidfh *pfh;
+
+ pfh = pidfile_open(pid_file, 0600, &otherpid);
+ if (pfh == NULL) {
+ if (errno == EEXIST) {
+ errx(1, "Daemon already running, pid: %jd.",
+ (intmax_t)otherpid);
+ }
+ warn("Cannot open or create pidfile.");
+ }
+#endif /* __FreeBSD__ */
+
+ if (os_daemon(0, 0)) {
+ perror("daemon");
+#ifdef __FreeBSD__
+ pidfile_remove(pfh);
+#endif /* __FreeBSD__ */
+ return -1;
+ }
+
+#ifndef __FreeBSD__
+ if (pid_file) {
+ FILE *f = fopen(pid_file, "w");
+ if (f) {
+ fprintf(f, "%u\n", getpid());
+ fclose(f);
+ }
+ }
+#else /* __FreeBSD__ */
+ pidfile_write(pfh);
+#endif /* __FreeBSD__ */
+
+ return -0;
+#endif /* defined(__uClinux__) || defined(__sun__) */
+}
+
+
+void os_daemonize_terminate(const char *pid_file)
+{
+ if (pid_file)
+ unlink(pid_file);
+}
+
+
+int os_get_random(unsigned char *buf, size_t len)
+{
+ FILE *f;
+ size_t rc;
+
+ if (TEST_FAIL())
+ return -1;
+
+ f = fopen("/dev/urandom", "rb");
+ if (f == NULL) {
+ printf("Could not open /dev/urandom.\n");
+ return -1;
+ }
+
+ rc = fread(buf, 1, len, f);
+ fclose(f);
+
+ return rc != len ? -1 : 0;
+}
+
+
+unsigned long os_random(void)
+{
+ return random();
+}
+
+
+char * os_rel2abs_path(const char *rel_path)
+{
+ char *buf = NULL, *cwd, *ret;
+ size_t len = 128, cwd_len, rel_len, ret_len;
+ int last_errno;
+
+ if (!rel_path)
+ return NULL;
+
+ if (rel_path[0] == '/')
+ return os_strdup(rel_path);
+
+ for (;;) {
+ buf = os_malloc(len);
+ if (buf == NULL)
+ return NULL;
+ cwd = getcwd(buf, len);
+ if (cwd == NULL) {
+ last_errno = errno;
+ os_free(buf);
+ if (last_errno != ERANGE)
+ return NULL;
+ len *= 2;
+ if (len > 2000)
+ return NULL;
+ } else {
+ buf[len - 1] = '\0';
+ break;
+ }
+ }
+
+ cwd_len = os_strlen(cwd);
+ rel_len = os_strlen(rel_path);
+ ret_len = cwd_len + 1 + rel_len + 1;
+ ret = os_malloc(ret_len);
+ if (ret) {
+ os_memcpy(ret, cwd, cwd_len);
+ ret[cwd_len] = '/';
+ os_memcpy(ret + cwd_len + 1, rel_path, rel_len);
+ ret[ret_len - 1] = '\0';
+ }
+ os_free(buf);
+ return ret;
+}
+
+
+int os_program_init(void)
+{
+#ifdef ANDROID
+ /*
+ * We ignore errors here since errors are normal if we
+ * are already running as non-root.
+ */
+#ifdef ANDROID_SETGROUPS_OVERRIDE
+ gid_t groups[] = { ANDROID_SETGROUPS_OVERRIDE };
+#else /* ANDROID_SETGROUPS_OVERRIDE */
+ gid_t groups[] = { AID_INET, AID_WIFI, AID_KEYSTORE };
+#endif /* ANDROID_SETGROUPS_OVERRIDE */
+ struct __user_cap_header_struct header;
+ struct __user_cap_data_struct cap;
+
+ setgroups(ARRAY_SIZE(groups), groups);
+
+ prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0);
+
+ setgid(AID_WIFI);
+ setuid(AID_WIFI);
+
+ header.version = _LINUX_CAPABILITY_VERSION;
+ header.pid = 0;
+ cap.effective = cap.permitted =
+ (1 << CAP_NET_ADMIN) | (1 << CAP_NET_RAW);
+ cap.inheritable = 0;
+ capset(&header, &cap);
+#endif /* ANDROID */
+
+ return 0;
+}
+
+
+void os_program_deinit(void)
+{
+#ifdef WPA_TRACE
+ struct os_alloc_trace *a;
+ unsigned long total = 0;
+ dl_list_for_each(a, &alloc_list, struct os_alloc_trace, list) {
+ total += a->len;
+ if (a->magic != ALLOC_MAGIC) {
+ wpa_printf(MSG_INFO, "MEMLEAK[%p]: invalid magic 0x%x "
+ "len %lu",
+ a, a->magic, (unsigned long) a->len);
+ continue;
+ }
+ wpa_printf(MSG_INFO, "MEMLEAK[%p]: len %lu",
+ a, (unsigned long) a->len);
+ wpa_trace_dump("memleak", a);
+ }
+ if (total)
+ wpa_printf(MSG_INFO, "MEMLEAK: total %lu bytes",
+ (unsigned long) total);
+#endif /* WPA_TRACE */
+}
+
+
+int os_setenv(const char *name, const char *value, int overwrite)
+{
+ return setenv(name, value, overwrite);
+}
+
+
+int os_unsetenv(const char *name)
+{
+#if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__APPLE__) || \
+ defined(__OpenBSD__)
+ unsetenv(name);
+ return 0;
+#else
+ return unsetenv(name);
+#endif
+}
+
+
+char * os_readfile(const char *name, size_t *len)
+{
+ FILE *f;
+ char *buf;
+ long pos;
+
+ f = fopen(name, "rb");
+ if (f == NULL)
+ return NULL;
+
+ if (fseek(f, 0, SEEK_END) < 0 || (pos = ftell(f)) < 0) {
+ fclose(f);
+ return NULL;
+ }
+ *len = pos;
+ if (fseek(f, 0, SEEK_SET) < 0) {
+ fclose(f);
+ return NULL;
+ }
+
+ buf = os_malloc(*len);
+ if (buf == NULL) {
+ fclose(f);
+ return NULL;
+ }
+
+ if (fread(buf, 1, *len, f) != *len) {
+ fclose(f);
+ os_free(buf);
+ return NULL;
+ }
+
+ fclose(f);
+
+ return buf;
+}
+
+
+int os_file_exists(const char *fname)
+{
+ FILE *f = fopen(fname, "rb");
+ if (f == NULL)
+ return 0;
+ fclose(f);
+ return 1;
+}
+
+
+int os_fdatasync(FILE *stream)
+{
+ if (!fflush(stream)) {
+#ifdef __linux__
+ return fdatasync(fileno(stream));
+#else /* !__linux__ */
+#ifdef F_FULLFSYNC
+ /* OS X does not implement fdatasync(). */
+ return fcntl(fileno(stream), F_FULLFSYNC);
+#else /* F_FULLFSYNC */
+ return fsync(fileno(stream));
+#endif /* F_FULLFSYNC */
+#endif /* __linux__ */
+ }
+
+ return -1;
+}
+
+
+#ifndef WPA_TRACE
+void * os_zalloc(size_t size)
+{
+ return calloc(1, size);
+}
+#endif /* WPA_TRACE */
+
+
+size_t os_strlcpy(char *dest, const char *src, size_t siz)
+{
+ const char *s = src;
+ size_t left = siz;
+
+ if (left) {
+ /* Copy string up to the maximum size of the dest buffer */
+ while (--left != 0) {
+ if ((*dest++ = *s++) == '\0')
+ break;
+ }
+ }
+
+ if (left == 0) {
+ /* Not enough room for the string; force NUL-termination */
+ if (siz != 0)
+ *dest = '\0';
+ while (*s++)
+ ; /* determine total src string length */
+ }
+
+ return s - src - 1;
+}
+
+
+int os_memcmp_const(const void *a, const void *b, size_t len)
+{
+ const u8 *aa = a;
+ const u8 *bb = b;
+ size_t i;
+ u8 res;
+
+ for (res = 0, i = 0; i < len; i++)
+ res |= aa[i] ^ bb[i];
+
+ return res;
+}
+
+
+#ifdef WPA_TRACE
+
+#if defined(WPA_TRACE_BFD) && defined(CONFIG_TESTING_OPTIONS)
+char wpa_trace_fail_func[256] = { 0 };
+unsigned int wpa_trace_fail_after;
+
+static int testing_fail_alloc(void)
+{
+ const char *func[WPA_TRACE_LEN];
+ size_t i, res, len;
+ char *pos, *next;
+ int match;
+
+ if (!wpa_trace_fail_after)
+ return 0;
+
+ res = wpa_trace_calling_func(func, WPA_TRACE_LEN);
+ i = 0;
+ if (i < res && os_strcmp(func[i], __func__) == 0)
+ i++;
+ if (i < res && os_strcmp(func[i], "os_malloc") == 0)
+ i++;
+ if (i < res && os_strcmp(func[i], "os_zalloc") == 0)
+ i++;
+ if (i < res && os_strcmp(func[i], "os_calloc") == 0)
+ i++;
+ if (i < res && os_strcmp(func[i], "os_realloc") == 0)
+ i++;
+ if (i < res && os_strcmp(func[i], "os_realloc_array") == 0)
+ i++;
+ if (i < res && os_strcmp(func[i], "os_strdup") == 0)
+ i++;
+
+ pos = wpa_trace_fail_func;
+
+ match = 0;
+ while (i < res) {
+ int allow_skip = 1;
+ int maybe = 0;
+
+ if (*pos == '=') {
+ allow_skip = 0;
+ pos++;
+ } else if (*pos == '?') {
+ maybe = 1;
+ pos++;
+ }
+ next = os_strchr(pos, ';');
+ if (next)
+ len = next - pos;
+ else
+ len = os_strlen(pos);
+ if (os_memcmp(pos, func[i], len) != 0) {
+ if (maybe && next) {
+ pos = next + 1;
+ continue;
+ }
+ if (allow_skip) {
+ i++;
+ continue;
+ }
+ return 0;
+ }
+ if (!next) {
+ match = 1;
+ break;
+ }
+ pos = next + 1;
+ i++;
+ }
+ if (!match)
+ return 0;
+
+ wpa_trace_fail_after--;
+ if (wpa_trace_fail_after == 0) {
+ wpa_printf(MSG_INFO, "TESTING: fail allocation at %s",
+ wpa_trace_fail_func);
+ for (i = 0; i < res; i++)
+ wpa_printf(MSG_INFO, "backtrace[%d] = %s",
+ (int) i, func[i]);
+ return 1;
+ }
+
+ return 0;
+}
+
+
+char wpa_trace_test_fail_func[256] = { 0 };
+unsigned int wpa_trace_test_fail_after;
+
+int testing_test_fail(void)
+{
+ const char *func[WPA_TRACE_LEN];
+ size_t i, res, len;
+ char *pos, *next;
+ int match;
+
+ if (!wpa_trace_test_fail_after)
+ return 0;
+
+ res = wpa_trace_calling_func(func, WPA_TRACE_LEN);
+ i = 0;
+ if (i < res && os_strcmp(func[i], __func__) == 0)
+ i++;
+
+ pos = wpa_trace_test_fail_func;
+
+ match = 0;
+ while (i < res) {
+ int allow_skip = 1;
+ int maybe = 0;
+
+ if (*pos == '=') {
+ allow_skip = 0;
+ pos++;
+ } else if (*pos == '?') {
+ maybe = 1;
+ pos++;
+ }
+ next = os_strchr(pos, ';');
+ if (next)
+ len = next - pos;
+ else
+ len = os_strlen(pos);
+ if (os_memcmp(pos, func[i], len) != 0) {
+ if (maybe && next) {
+ pos = next + 1;
+ continue;
+ }
+ if (allow_skip) {
+ i++;
+ continue;
+ }
+ return 0;
+ }
+ if (!next) {
+ match = 1;
+ break;
+ }
+ pos = next + 1;
+ i++;
+ }
+ if (!match)
+ return 0;
+
+ wpa_trace_test_fail_after--;
+ if (wpa_trace_test_fail_after == 0) {
+ wpa_printf(MSG_INFO, "TESTING: fail at %s",
+ wpa_trace_test_fail_func);
+ for (i = 0; i < res; i++)
+ wpa_printf(MSG_INFO, "backtrace[%d] = %s",
+ (int) i, func[i]);
+ return 1;
+ }
+
+ return 0;
+}
+
+#else
+
+static inline int testing_fail_alloc(void)
+{
+ return 0;
+}
+#endif
+
+void * os_malloc(size_t size)
+{
+ struct os_alloc_trace *a;
+
+ if (testing_fail_alloc())
+ return NULL;
+
+ a = malloc(sizeof(*a) + size);
+ if (a == NULL)
+ return NULL;
+ a->magic = ALLOC_MAGIC;
+ dl_list_add(&alloc_list, &a->list);
+ a->len = size;
+ wpa_trace_record(a);
+ return a + 1;
+}
+
+
+void * os_realloc(void *ptr, size_t size)
+{
+ struct os_alloc_trace *a;
+ size_t copy_len;
+ void *n;
+
+ if (ptr == NULL)
+ return os_malloc(size);
+
+ a = (struct os_alloc_trace *) ptr - 1;
+ if (a->magic != ALLOC_MAGIC) {
+ wpa_printf(MSG_INFO, "REALLOC[%p]: invalid magic 0x%x%s",
+ a, a->magic,
+ a->magic == FREED_MAGIC ? " (already freed)" : "");
+ wpa_trace_show("Invalid os_realloc() call");
+ abort();
+ }
+ n = os_malloc(size);
+ if (n == NULL)
+ return NULL;
+ copy_len = a->len;
+ if (copy_len > size)
+ copy_len = size;
+ os_memcpy(n, a + 1, copy_len);
+ os_free(ptr);
+ return n;
+}
+
+
+void os_free(void *ptr)
+{
+ struct os_alloc_trace *a;
+
+ if (ptr == NULL)
+ return;
+ a = (struct os_alloc_trace *) ptr - 1;
+ if (a->magic != ALLOC_MAGIC) {
+ wpa_printf(MSG_INFO, "FREE[%p]: invalid magic 0x%x%s",
+ a, a->magic,
+ a->magic == FREED_MAGIC ? " (already freed)" : "");
+ wpa_trace_show("Invalid os_free() call");
+ abort();
+ }
+ dl_list_del(&a->list);
+ a->magic = FREED_MAGIC;
+
+ wpa_trace_check_ref(ptr);
+ free(a);
+}
+
+
+void * os_zalloc(size_t size)
+{
+ void *ptr = os_malloc(size);
+ if (ptr)
+ os_memset(ptr, 0, size);
+ return ptr;
+}
+
+
+char * os_strdup(const char *s)
+{
+ size_t len;
+ char *d;
+ len = os_strlen(s);
+ d = os_malloc(len + 1);
+ if (d == NULL)
+ return NULL;
+ os_memcpy(d, s, len);
+ d[len] = '\0';
+ return d;
+}
+
+#endif /* WPA_TRACE */
+
+
+int os_exec(const char *program, const char *arg, int wait_completion)
+{
+ pid_t pid;
+ int pid_status;
+
+ pid = fork();
+ if (pid < 0) {
+ perror("fork");
+ return -1;
+ }
+
+ if (pid == 0) {
+ /* run the external command in the child process */
+ const int MAX_ARG = 30;
+ char *_program, *_arg, *pos;
+ char *argv[MAX_ARG + 1];
+ int i;
+
+ _program = os_strdup(program);
+ _arg = os_strdup(arg);
+
+ argv[0] = _program;
+
+ i = 1;
+ pos = _arg;
+ while (i < MAX_ARG && pos && *pos) {
+ while (*pos == ' ')
+ pos++;
+ if (*pos == '\0')
+ break;
+ argv[i++] = pos;
+ pos = os_strchr(pos, ' ');
+ if (pos)
+ *pos++ = '\0';
+ }
+ argv[i] = NULL;
+
+ execv(program, argv);
+ perror("execv");
+ os_free(_program);
+ os_free(_arg);
+ exit(0);
+ return -1;
+ }
+
+ if (wait_completion) {
+ /* wait for the child process to complete in the parent */
+ waitpid(pid, &pid_status, 0);
+ }
+
+ return 0;
+}
diff --git a/freebsd/contrib/wpa/src/utils/pcsc_funcs.h b/freebsd/contrib/wpa/src/utils/pcsc_funcs.h
new file mode 100644
index 00000000..eacd2a2d
--- /dev/null
+++ b/freebsd/contrib/wpa/src/utils/pcsc_funcs.h
@@ -0,0 +1,42 @@
+/*
+ * WPA Supplicant / PC/SC smartcard interface for USIM, GSM SIM
+ * Copyright (c) 2004-2006, 2012, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef PCSC_FUNCS_H
+#define PCSC_FUNCS_H
+
+#ifdef PCSC_FUNCS
+struct scard_data * scard_init(const char *reader);
+void scard_deinit(struct scard_data *scard);
+
+int scard_set_pin(struct scard_data *scard, const char *pin);
+int scard_get_imsi(struct scard_data *scard, char *imsi, size_t *len);
+int scard_get_mnc_len(struct scard_data *scard);
+int scard_gsm_auth(struct scard_data *scard, const unsigned char *_rand,
+ unsigned char *sres, unsigned char *kc);
+int scard_umts_auth(struct scard_data *scard, const unsigned char *_rand,
+ const unsigned char *autn,
+ unsigned char *res, size_t *res_len,
+ unsigned char *ik, unsigned char *ck, unsigned char *auts);
+int scard_get_pin_retry_counter(struct scard_data *scard);
+int scard_supports_umts(struct scard_data *scard);
+
+#else /* PCSC_FUNCS */
+
+#define scard_init(r) NULL
+#define scard_deinit(s) do { } while (0)
+#define scard_set_pin(s, p) -1
+#define scard_get_imsi(s, i, l) -1
+#define scard_get_mnc_len(s) -1
+#define scard_gsm_auth(s, r, s2, k) -1
+#define scard_umts_auth(s, r, a, r2, rl, i, c, a2) -1
+#define scard_get_pin_retry_counter(s) -1
+#define scard_supports_umts(s) 0
+
+#endif /* PCSC_FUNCS */
+
+#endif /* PCSC_FUNCS_H */
diff --git a/freebsd/contrib/wpa/src/utils/platform.h b/freebsd/contrib/wpa/src/utils/platform.h
new file mode 100644
index 00000000..46cfe785
--- /dev/null
+++ b/freebsd/contrib/wpa/src/utils/platform.h
@@ -0,0 +1,21 @@
+#ifndef PLATFORM_H
+#define PLATFORM_H
+
+#include "includes.h"
+#include "common.h"
+
+#define le16_to_cpu le_to_host16
+#define le32_to_cpu le_to_host32
+
+#define get_unaligned(p) \
+({ \
+ struct packed_dummy_struct { \
+ typeof(*(p)) __val; \
+ } __attribute__((packed)) *__ptr = (void *) (p); \
+ \
+ __ptr->__val; \
+})
+#define get_unaligned_le16(p) le16_to_cpu(get_unaligned((uint16_t *)(p)))
+#define get_unaligned_le32(p) le32_to_cpu(get_unaligned((uint32_t *)(p)))
+
+#endif /* PLATFORM_H */
diff --git a/freebsd/contrib/wpa/src/utils/state_machine.h b/freebsd/contrib/wpa/src/utils/state_machine.h
new file mode 100644
index 00000000..a5143157
--- /dev/null
+++ b/freebsd/contrib/wpa/src/utils/state_machine.h
@@ -0,0 +1,138 @@
+/*
+ * wpa_supplicant/hostapd - State machine definitions
+ * Copyright (c) 2002-2005, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ *
+ * This file includes a set of pre-processor macros that can be used to
+ * implement a state machine. In addition to including this header file, each
+ * file implementing a state machine must define STATE_MACHINE_DATA to be the
+ * data structure including state variables (enum machine_state,
+ * Boolean changed), and STATE_MACHINE_DEBUG_PREFIX to be a string that is used
+ * as a prefix for all debug messages. If SM_ENTRY_MA macro is used to define
+ * a group of state machines with shared data structure, STATE_MACHINE_ADDR
+ * needs to be defined to point to the MAC address used in debug output.
+ * SM_ENTRY_M macro can be used to define similar group of state machines
+ * without this additional debug info.
+ */
+
+#ifndef STATE_MACHINE_H
+#define STATE_MACHINE_H
+
+/**
+ * SM_STATE - Declaration of a state machine function
+ * @machine: State machine name
+ * @state: State machine state
+ *
+ * This macro is used to declare a state machine function. It is used in place
+ * of a C function definition to declare functions to be run when the state is
+ * entered by calling SM_ENTER or SM_ENTER_GLOBAL.
+ */
+#define SM_STATE(machine, state) \
+static void sm_ ## machine ## _ ## state ## _Enter(STATE_MACHINE_DATA *sm, \
+ int global)
+
+/**
+ * SM_ENTRY - State machine function entry point
+ * @machine: State machine name
+ * @state: State machine state
+ *
+ * This macro is used inside each state machine function declared with
+ * SM_STATE. SM_ENTRY should be in the beginning of the function body, but
+ * after declaration of possible local variables. This macro prints debug
+ * information about state transition and update the state machine state.
+ */
+#define SM_ENTRY(machine, state) \
+if (!global || sm->machine ## _state != machine ## _ ## state) { \
+ sm->changed = TRUE; \
+ wpa_printf(MSG_DEBUG, STATE_MACHINE_DEBUG_PREFIX ": " #machine \
+ " entering state " #state); \
+} \
+sm->machine ## _state = machine ## _ ## state;
+
+/**
+ * SM_ENTRY_M - State machine function entry point for state machine group
+ * @machine: State machine name
+ * @_state: State machine state
+ * @data: State variable prefix (full variable: prefix_state)
+ *
+ * This macro is like SM_ENTRY, but for state machine groups that use a shared
+ * data structure for more than one state machine. Both machine and prefix
+ * parameters are set to "sub-state machine" name. prefix is used to allow more
+ * than one state variable to be stored in the same data structure.
+ */
+#define SM_ENTRY_M(machine, _state, data) \
+if (!global || sm->data ## _ ## state != machine ## _ ## _state) { \
+ sm->changed = TRUE; \
+ wpa_printf(MSG_DEBUG, STATE_MACHINE_DEBUG_PREFIX ": " \
+ #machine " entering state " #_state); \
+} \
+sm->data ## _ ## state = machine ## _ ## _state;
+
+/**
+ * SM_ENTRY_MA - State machine function entry point for state machine group
+ * @machine: State machine name
+ * @_state: State machine state
+ * @data: State variable prefix (full variable: prefix_state)
+ *
+ * This macro is like SM_ENTRY_M, but a MAC address is included in debug
+ * output. STATE_MACHINE_ADDR has to be defined to point to the MAC address to
+ * be included in debug.
+ */
+#define SM_ENTRY_MA(machine, _state, data) \
+if (!global || sm->data ## _ ## state != machine ## _ ## _state) { \
+ sm->changed = TRUE; \
+ wpa_printf(MSG_DEBUG, STATE_MACHINE_DEBUG_PREFIX ": " MACSTR " " \
+ #machine " entering state " #_state, \
+ MAC2STR(STATE_MACHINE_ADDR)); \
+} \
+sm->data ## _ ## state = machine ## _ ## _state;
+
+/**
+ * SM_ENTER - Enter a new state machine state
+ * @machine: State machine name
+ * @state: State machine state
+ *
+ * This macro expands to a function call to a state machine function defined
+ * with SM_STATE macro. SM_ENTER is used in a state machine step function to
+ * move the state machine to a new state.
+ */
+#define SM_ENTER(machine, state) \
+sm_ ## machine ## _ ## state ## _Enter(sm, 0)
+
+/**
+ * SM_ENTER_GLOBAL - Enter a new state machine state based on global rule
+ * @machine: State machine name
+ * @state: State machine state
+ *
+ * This macro is like SM_ENTER, but this is used when entering a new state
+ * based on a global (not specific to any particular state) rule. A separate
+ * macro is used to avoid unwanted debug message floods when the same global
+ * rule is forcing a state machine to remain in on state.
+ */
+#define SM_ENTER_GLOBAL(machine, state) \
+sm_ ## machine ## _ ## state ## _Enter(sm, 1)
+
+/**
+ * SM_STEP - Declaration of a state machine step function
+ * @machine: State machine name
+ *
+ * This macro is used to declare a state machine step function. It is used in
+ * place of a C function definition to declare a function that is used to move
+ * state machine to a new state based on state variables. This function uses
+ * SM_ENTER and SM_ENTER_GLOBAL macros to enter new state.
+ */
+#define SM_STEP(machine) \
+static void sm_ ## machine ## _Step(STATE_MACHINE_DATA *sm)
+
+/**
+ * SM_STEP_RUN - Call the state machine step function
+ * @machine: State machine name
+ *
+ * This macro expands to a function call to a state machine step function
+ * defined with SM_STEP macro.
+ */
+#define SM_STEP_RUN(machine) sm_ ## machine ## _Step(sm)
+
+#endif /* STATE_MACHINE_H */
diff --git a/freebsd/contrib/wpa/src/utils/trace.h b/freebsd/contrib/wpa/src/utils/trace.h
new file mode 100644
index 00000000..43ed86c1
--- /dev/null
+++ b/freebsd/contrib/wpa/src/utils/trace.h
@@ -0,0 +1,69 @@
+/*
+ * Backtrace debugging
+ * 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.
+ */
+
+#ifndef TRACE_H
+#define TRACE_H
+
+#define WPA_TRACE_LEN 16
+
+#ifdef WPA_TRACE
+#include <execinfo.h>
+
+#include "list.h"
+
+#define WPA_TRACE_INFO void *btrace[WPA_TRACE_LEN]; int btrace_num;
+
+struct wpa_trace_ref {
+ struct dl_list list;
+ const void *addr;
+ WPA_TRACE_INFO
+};
+#define WPA_TRACE_REF(name) struct wpa_trace_ref wpa_trace_ref_##name
+
+#define wpa_trace_dump(title, ptr) \
+ wpa_trace_dump_func((title), (ptr)->btrace, (ptr)->btrace_num)
+void wpa_trace_dump_func(const char *title, void **btrace, int btrace_num);
+#define wpa_trace_record(ptr) \
+ (ptr)->btrace_num = backtrace((ptr)->btrace, WPA_TRACE_LEN)
+void wpa_trace_show(const char *title);
+#define wpa_trace_add_ref(ptr, name, addr) \
+ wpa_trace_add_ref_func(&(ptr)->wpa_trace_ref_##name, (addr))
+void wpa_trace_add_ref_func(struct wpa_trace_ref *ref, const void *addr);
+#define wpa_trace_remove_ref(ptr, name, addr) \
+ do { \
+ if ((addr)) \
+ dl_list_del(&(ptr)->wpa_trace_ref_##name.list); \
+ } while (0)
+void wpa_trace_check_ref(const void *addr);
+size_t wpa_trace_calling_func(const char *buf[], size_t len);
+
+#else /* WPA_TRACE */
+
+#define WPA_TRACE_INFO
+#define WPA_TRACE_REF(n)
+#define wpa_trace_dump(title, ptr) do { } while (0)
+#define wpa_trace_record(ptr) do { } while (0)
+#define wpa_trace_show(title) do { } while (0)
+#define wpa_trace_add_ref(ptr, name, addr) do { } while (0)
+#define wpa_trace_remove_ref(ptr, name, addr) do { } while (0)
+#define wpa_trace_check_ref(addr) do { } while (0)
+
+#endif /* WPA_TRACE */
+
+
+#ifdef WPA_TRACE_BFD
+
+void wpa_trace_dump_funcname(const char *title, void *pc);
+
+#else /* WPA_TRACE_BFD */
+
+#define wpa_trace_dump_funcname(title, pc) do { } while (0)
+
+#endif /* WPA_TRACE_BFD */
+
+#endif /* TRACE_H */
diff --git a/freebsd/contrib/wpa/src/utils/uuid.c b/freebsd/contrib/wpa/src/utils/uuid.c
new file mode 100644
index 00000000..a1dedf6e
--- /dev/null
+++ b/freebsd/contrib/wpa/src/utils/uuid.c
@@ -0,0 +1,73 @@
+#include <machine/rtems-bsd-user-space.h>
+
+/*
+ * 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 "includes.h"
+
+#include "common.h"
+#include "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 = os_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 (os_snprintf_error(max_len, 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;
+}
diff --git a/freebsd/contrib/wpa/src/utils/uuid.h b/freebsd/contrib/wpa/src/utils/uuid.h
new file mode 100644
index 00000000..5e860cbc
--- /dev/null
+++ b/freebsd/contrib/wpa/src/utils/uuid.h
@@ -0,0 +1,18 @@
+/*
+ * 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 */
diff --git a/freebsd/contrib/wpa/src/utils/wpa_debug.c b/freebsd/contrib/wpa/src/utils/wpa_debug.c
new file mode 100644
index 00000000..1b725ad5
--- /dev/null
+++ b/freebsd/contrib/wpa/src/utils/wpa_debug.c
@@ -0,0 +1,862 @@
+#include <machine/rtems-bsd-user-space.h>
+
+/*
+ * wpa_supplicant/hostapd / Debug prints
+ * Copyright (c) 2002-2013, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+
+#ifdef CONFIG_DEBUG_SYSLOG
+#include <syslog.h>
+
+static int wpa_debug_syslog = 0;
+#endif /* CONFIG_DEBUG_SYSLOG */
+
+#ifdef CONFIG_DEBUG_LINUX_TRACING
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <string.h>
+#include <stdio.h>
+
+static FILE *wpa_debug_tracing_file = NULL;
+
+#define WPAS_TRACE_PFX "wpas <%d>: "
+#endif /* CONFIG_DEBUG_LINUX_TRACING */
+
+
+int wpa_debug_level = MSG_INFO;
+int wpa_debug_show_keys = 0;
+int wpa_debug_timestamp = 0;
+
+
+#ifdef CONFIG_ANDROID_LOG
+
+#include <android/log.h>
+
+#ifndef ANDROID_LOG_NAME
+#define ANDROID_LOG_NAME "wpa_supplicant"
+#endif /* ANDROID_LOG_NAME */
+
+static int wpa_to_android_level(int level)
+{
+ if (level == MSG_ERROR)
+ return ANDROID_LOG_ERROR;
+ if (level == MSG_WARNING)
+ return ANDROID_LOG_WARN;
+ if (level == MSG_INFO)
+ return ANDROID_LOG_INFO;
+ return ANDROID_LOG_DEBUG;
+}
+
+#endif /* CONFIG_ANDROID_LOG */
+
+#ifndef CONFIG_NO_STDOUT_DEBUG
+
+#ifdef CONFIG_DEBUG_FILE
+static FILE *out_file = NULL;
+#endif /* CONFIG_DEBUG_FILE */
+
+
+void wpa_debug_print_timestamp(void)
+{
+#ifndef CONFIG_ANDROID_LOG
+ struct os_time tv;
+
+ if (!wpa_debug_timestamp)
+ return;
+
+ os_get_time(&tv);
+#ifdef CONFIG_DEBUG_FILE
+ if (out_file) {
+ fprintf(out_file, "%ld.%06u: ", (long) tv.sec,
+ (unsigned int) tv.usec);
+ } else
+#endif /* CONFIG_DEBUG_FILE */
+ printf("%ld.%06u: ", (long) tv.sec, (unsigned int) tv.usec);
+#endif /* CONFIG_ANDROID_LOG */
+}
+
+
+#ifdef CONFIG_DEBUG_SYSLOG
+#ifndef LOG_HOSTAPD
+#define LOG_HOSTAPD LOG_DAEMON
+#endif /* LOG_HOSTAPD */
+
+void wpa_debug_open_syslog(void)
+{
+ openlog("wpa_supplicant", LOG_PID | LOG_NDELAY, LOG_HOSTAPD);
+ wpa_debug_syslog++;
+}
+
+
+void wpa_debug_close_syslog(void)
+{
+ if (wpa_debug_syslog)
+ closelog();
+}
+
+
+static int syslog_priority(int level)
+{
+ switch (level) {
+ case MSG_MSGDUMP:
+ case MSG_DEBUG:
+ return LOG_DEBUG;
+ case MSG_INFO:
+ return LOG_NOTICE;
+ case MSG_WARNING:
+ return LOG_WARNING;
+ case MSG_ERROR:
+ return LOG_ERR;
+ }
+ return LOG_INFO;
+}
+#endif /* CONFIG_DEBUG_SYSLOG */
+
+
+#ifdef CONFIG_DEBUG_LINUX_TRACING
+
+int wpa_debug_open_linux_tracing(void)
+{
+ int mounts, trace_fd;
+ char buf[4096] = {};
+ ssize_t buflen;
+ char *line, *tmp1, *path = NULL;
+
+ mounts = open("/proc/mounts", O_RDONLY);
+ if (mounts < 0) {
+ printf("no /proc/mounts\n");
+ return -1;
+ }
+
+ buflen = read(mounts, buf, sizeof(buf) - 1);
+ close(mounts);
+ if (buflen < 0) {
+ printf("failed to read /proc/mounts\n");
+ return -1;
+ }
+
+ line = strtok_r(buf, "\n", &tmp1);
+ while (line) {
+ char *tmp2, *tmp_path, *fstype;
+ /* "<dev> <mountpoint> <fs type> ..." */
+ strtok_r(line, " ", &tmp2);
+ tmp_path = strtok_r(NULL, " ", &tmp2);
+ fstype = strtok_r(NULL, " ", &tmp2);
+ if (strcmp(fstype, "debugfs") == 0) {
+ path = tmp_path;
+ break;
+ }
+
+ line = strtok_r(NULL, "\n", &tmp1);
+ }
+
+ if (path == NULL) {
+ printf("debugfs mountpoint not found\n");
+ return -1;
+ }
+
+ snprintf(buf, sizeof(buf) - 1, "%s/tracing/trace_marker", path);
+
+ trace_fd = open(buf, O_WRONLY);
+ if (trace_fd < 0) {
+ printf("failed to open trace_marker file\n");
+ return -1;
+ }
+ wpa_debug_tracing_file = fdopen(trace_fd, "w");
+ if (wpa_debug_tracing_file == NULL) {
+ close(trace_fd);
+ printf("failed to fdopen()\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+
+void wpa_debug_close_linux_tracing(void)
+{
+ if (wpa_debug_tracing_file == NULL)
+ return;
+ fclose(wpa_debug_tracing_file);
+ wpa_debug_tracing_file = NULL;
+}
+
+#endif /* CONFIG_DEBUG_LINUX_TRACING */
+
+
+/**
+ * wpa_printf - conditional printf
+ * @level: priority level (MSG_*) of the message
+ * @fmt: printf format string, followed by optional arguments
+ *
+ * This function is used to print conditional debugging and error messages. The
+ * output may be directed to stdout, stderr, and/or syslog based on
+ * configuration.
+ *
+ * Note: New line '\n' is added to the end of the text when printing to stdout.
+ */
+void wpa_printf(int level, const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ if (level >= wpa_debug_level) {
+#ifdef CONFIG_ANDROID_LOG
+ __android_log_vprint(wpa_to_android_level(level),
+ ANDROID_LOG_NAME, fmt, ap);
+#else /* CONFIG_ANDROID_LOG */
+#ifdef CONFIG_DEBUG_SYSLOG
+ if (wpa_debug_syslog) {
+ vsyslog(syslog_priority(level), fmt, ap);
+ } else {
+#endif /* CONFIG_DEBUG_SYSLOG */
+ wpa_debug_print_timestamp();
+#ifdef CONFIG_DEBUG_FILE
+ if (out_file) {
+ vfprintf(out_file, fmt, ap);
+ fprintf(out_file, "\n");
+ } else {
+#endif /* CONFIG_DEBUG_FILE */
+ vprintf(fmt, ap);
+ printf("\n");
+#ifdef CONFIG_DEBUG_FILE
+ }
+#endif /* CONFIG_DEBUG_FILE */
+#ifdef CONFIG_DEBUG_SYSLOG
+ }
+#endif /* CONFIG_DEBUG_SYSLOG */
+#endif /* CONFIG_ANDROID_LOG */
+ }
+ va_end(ap);
+
+#ifdef CONFIG_DEBUG_LINUX_TRACING
+ if (wpa_debug_tracing_file != NULL) {
+ va_start(ap, fmt);
+ fprintf(wpa_debug_tracing_file, WPAS_TRACE_PFX, level);
+ vfprintf(wpa_debug_tracing_file, fmt, ap);
+ fprintf(wpa_debug_tracing_file, "\n");
+ fflush(wpa_debug_tracing_file);
+ va_end(ap);
+ }
+#endif /* CONFIG_DEBUG_LINUX_TRACING */
+}
+
+
+static void _wpa_hexdump(int level, const char *title, const u8 *buf,
+ size_t len, int show)
+{
+ size_t i;
+
+#ifdef CONFIG_DEBUG_LINUX_TRACING
+ if (wpa_debug_tracing_file != NULL) {
+ fprintf(wpa_debug_tracing_file,
+ WPAS_TRACE_PFX "%s - hexdump(len=%lu):",
+ level, title, (unsigned long) len);
+ if (buf == NULL) {
+ fprintf(wpa_debug_tracing_file, " [NULL]\n");
+ } else if (!show) {
+ fprintf(wpa_debug_tracing_file, " [REMOVED]\n");
+ } else {
+ for (i = 0; i < len; i++)
+ fprintf(wpa_debug_tracing_file,
+ " %02x", buf[i]);
+ }
+ fflush(wpa_debug_tracing_file);
+ }
+#endif /* CONFIG_DEBUG_LINUX_TRACING */
+
+ if (level < wpa_debug_level)
+ return;
+#ifdef CONFIG_ANDROID_LOG
+ {
+ const char *display;
+ char *strbuf = NULL;
+ size_t slen = len;
+ if (buf == NULL) {
+ display = " [NULL]";
+ } else if (len == 0) {
+ display = "";
+ } else if (show && len) {
+ /* Limit debug message length for Android log */
+ if (slen > 32)
+ slen = 32;
+ strbuf = os_malloc(1 + 3 * slen);
+ if (strbuf == NULL) {
+ wpa_printf(MSG_ERROR, "wpa_hexdump: Failed to "
+ "allocate message buffer");
+ return;
+ }
+
+ for (i = 0; i < slen; i++)
+ os_snprintf(&strbuf[i * 3], 4, " %02x",
+ buf[i]);
+
+ display = strbuf;
+ } else {
+ display = " [REMOVED]";
+ }
+
+ __android_log_print(wpa_to_android_level(level),
+ ANDROID_LOG_NAME,
+ "%s - hexdump(len=%lu):%s%s",
+ title, (long unsigned int) len, display,
+ len > slen ? " ..." : "");
+ bin_clear_free(strbuf, 1 + 3 * slen);
+ return;
+ }
+#else /* CONFIG_ANDROID_LOG */
+#ifdef CONFIG_DEBUG_SYSLOG
+ if (wpa_debug_syslog) {
+ const char *display;
+ char *strbuf = NULL;
+
+ if (buf == NULL) {
+ display = " [NULL]";
+ } else if (len == 0) {
+ display = "";
+ } else if (show && len) {
+ strbuf = os_malloc(1 + 3 * len);
+ if (strbuf == NULL) {
+ wpa_printf(MSG_ERROR, "wpa_hexdump: Failed to "
+ "allocate message buffer");
+ return;
+ }
+
+ for (i = 0; i < len; i++)
+ os_snprintf(&strbuf[i * 3], 4, " %02x",
+ buf[i]);
+
+ display = strbuf;
+ } else {
+ display = " [REMOVED]";
+ }
+
+ syslog(syslog_priority(level), "%s - hexdump(len=%lu):%s",
+ title, (unsigned long) len, display);
+ bin_clear_free(strbuf, 1 + 3 * len);
+ return;
+ }
+#endif /* CONFIG_DEBUG_SYSLOG */
+ wpa_debug_print_timestamp();
+#ifdef CONFIG_DEBUG_FILE
+ if (out_file) {
+ fprintf(out_file, "%s - hexdump(len=%lu):",
+ title, (unsigned long) len);
+ if (buf == NULL) {
+ fprintf(out_file, " [NULL]");
+ } else if (show) {
+ for (i = 0; i < len; i++)
+ fprintf(out_file, " %02x", buf[i]);
+ } else {
+ fprintf(out_file, " [REMOVED]");
+ }
+ fprintf(out_file, "\n");
+ } else {
+#endif /* CONFIG_DEBUG_FILE */
+ printf("%s - hexdump(len=%lu):", title, (unsigned long) len);
+ if (buf == NULL) {
+ printf(" [NULL]");
+ } else if (show) {
+ for (i = 0; i < len; i++)
+ printf(" %02x", buf[i]);
+ } else {
+ printf(" [REMOVED]");
+ }
+ printf("\n");
+#ifdef CONFIG_DEBUG_FILE
+ }
+#endif /* CONFIG_DEBUG_FILE */
+#endif /* CONFIG_ANDROID_LOG */
+}
+
+void wpa_hexdump(int level, const char *title, const void *buf, size_t len)
+{
+ _wpa_hexdump(level, title, buf, len, 1);
+}
+
+
+void wpa_hexdump_key(int level, const char *title, const void *buf, size_t len)
+{
+ _wpa_hexdump(level, title, buf, len, wpa_debug_show_keys);
+}
+
+
+static void _wpa_hexdump_ascii(int level, const char *title, const void *buf,
+ size_t len, int show)
+{
+ size_t i, llen;
+ const u8 *pos = buf;
+ const size_t line_len = 16;
+
+#ifdef CONFIG_DEBUG_LINUX_TRACING
+ if (wpa_debug_tracing_file != NULL) {
+ fprintf(wpa_debug_tracing_file,
+ WPAS_TRACE_PFX "%s - hexdump_ascii(len=%lu):",
+ level, title, (unsigned long) len);
+ if (buf == NULL) {
+ fprintf(wpa_debug_tracing_file, " [NULL]\n");
+ } else if (!show) {
+ fprintf(wpa_debug_tracing_file, " [REMOVED]\n");
+ } else {
+ /* can do ascii processing in userspace */
+ for (i = 0; i < len; i++)
+ fprintf(wpa_debug_tracing_file,
+ " %02x", pos[i]);
+ }
+ fflush(wpa_debug_tracing_file);
+ }
+#endif /* CONFIG_DEBUG_LINUX_TRACING */
+
+ if (level < wpa_debug_level)
+ return;
+#ifdef CONFIG_ANDROID_LOG
+ _wpa_hexdump(level, title, buf, len, show);
+#else /* CONFIG_ANDROID_LOG */
+ wpa_debug_print_timestamp();
+#ifdef CONFIG_DEBUG_FILE
+ if (out_file) {
+ if (!show) {
+ fprintf(out_file,
+ "%s - hexdump_ascii(len=%lu): [REMOVED]\n",
+ title, (unsigned long) len);
+ return;
+ }
+ if (buf == NULL) {
+ fprintf(out_file,
+ "%s - hexdump_ascii(len=%lu): [NULL]\n",
+ title, (unsigned long) len);
+ return;
+ }
+ fprintf(out_file, "%s - hexdump_ascii(len=%lu):\n",
+ title, (unsigned long) len);
+ while (len) {
+ llen = len > line_len ? line_len : len;
+ fprintf(out_file, " ");
+ for (i = 0; i < llen; i++)
+ fprintf(out_file, " %02x", pos[i]);
+ for (i = llen; i < line_len; i++)
+ fprintf(out_file, " ");
+ fprintf(out_file, " ");
+ for (i = 0; i < llen; i++) {
+ if (isprint(pos[i]))
+ fprintf(out_file, "%c", pos[i]);
+ else
+ fprintf(out_file, "_");
+ }
+ for (i = llen; i < line_len; i++)
+ fprintf(out_file, " ");
+ fprintf(out_file, "\n");
+ pos += llen;
+ len -= llen;
+ }
+ } else {
+#endif /* CONFIG_DEBUG_FILE */
+ if (!show) {
+ printf("%s - hexdump_ascii(len=%lu): [REMOVED]\n",
+ title, (unsigned long) len);
+ return;
+ }
+ if (buf == NULL) {
+ printf("%s - hexdump_ascii(len=%lu): [NULL]\n",
+ title, (unsigned long) len);
+ return;
+ }
+ printf("%s - hexdump_ascii(len=%lu):\n", title, (unsigned long) len);
+ while (len) {
+ llen = len > line_len ? line_len : len;
+ printf(" ");
+ for (i = 0; i < llen; i++)
+ printf(" %02x", pos[i]);
+ for (i = llen; i < line_len; i++)
+ printf(" ");
+ printf(" ");
+ for (i = 0; i < llen; i++) {
+ if (isprint(pos[i]))
+ printf("%c", pos[i]);
+ else
+ printf("_");
+ }
+ for (i = llen; i < line_len; i++)
+ printf(" ");
+ printf("\n");
+ pos += llen;
+ len -= llen;
+ }
+#ifdef CONFIG_DEBUG_FILE
+ }
+#endif /* CONFIG_DEBUG_FILE */
+#endif /* CONFIG_ANDROID_LOG */
+}
+
+
+void wpa_hexdump_ascii(int level, const char *title, const void *buf,
+ size_t len)
+{
+ _wpa_hexdump_ascii(level, title, buf, len, 1);
+}
+
+
+void wpa_hexdump_ascii_key(int level, const char *title, const void *buf,
+ size_t len)
+{
+ _wpa_hexdump_ascii(level, title, buf, len, wpa_debug_show_keys);
+}
+
+
+#ifdef CONFIG_DEBUG_FILE
+static char *last_path = NULL;
+#endif /* CONFIG_DEBUG_FILE */
+
+int wpa_debug_reopen_file(void)
+{
+#ifdef CONFIG_DEBUG_FILE
+ int rv;
+ if (last_path) {
+ char *tmp = os_strdup(last_path);
+ wpa_debug_close_file();
+ rv = wpa_debug_open_file(tmp);
+ os_free(tmp);
+ } else {
+ wpa_printf(MSG_ERROR, "Last-path was not set, cannot "
+ "re-open log file.");
+ rv = -1;
+ }
+ return rv;
+#else /* CONFIG_DEBUG_FILE */
+ return 0;
+#endif /* CONFIG_DEBUG_FILE */
+}
+
+
+int wpa_debug_open_file(const char *path)
+{
+#ifdef CONFIG_DEBUG_FILE
+ if (!path)
+ return 0;
+
+ if (last_path == NULL || os_strcmp(last_path, path) != 0) {
+ /* Save our path to enable re-open */
+ os_free(last_path);
+ last_path = os_strdup(path);
+ }
+
+ out_file = fopen(path, "a");
+ if (out_file == NULL) {
+ wpa_printf(MSG_ERROR, "wpa_debug_open_file: Failed to open "
+ "output file, using standard output");
+ return -1;
+ }
+#ifndef _WIN32
+ setvbuf(out_file, NULL, _IOLBF, 0);
+#endif /* _WIN32 */
+#else /* CONFIG_DEBUG_FILE */
+ (void)path;
+#endif /* CONFIG_DEBUG_FILE */
+ return 0;
+}
+
+
+void wpa_debug_close_file(void)
+{
+#ifdef CONFIG_DEBUG_FILE
+ if (!out_file)
+ return;
+ fclose(out_file);
+ out_file = NULL;
+ os_free(last_path);
+ last_path = NULL;
+#endif /* CONFIG_DEBUG_FILE */
+}
+
+
+void wpa_debug_setup_stdout(void)
+{
+#ifndef _WIN32
+ setvbuf(stdout, NULL, _IOLBF, 0);
+#endif /* _WIN32 */
+}
+
+#endif /* CONFIG_NO_STDOUT_DEBUG */
+
+
+#ifndef CONFIG_NO_WPA_MSG
+static wpa_msg_cb_func wpa_msg_cb = NULL;
+
+void wpa_msg_register_cb(wpa_msg_cb_func func)
+{
+ wpa_msg_cb = func;
+}
+
+
+static wpa_msg_get_ifname_func wpa_msg_ifname_cb = NULL;
+
+void wpa_msg_register_ifname_cb(wpa_msg_get_ifname_func func)
+{
+ wpa_msg_ifname_cb = func;
+}
+
+
+void wpa_msg(void *ctx, int level, const char *fmt, ...)
+{
+ va_list ap;
+ char *buf;
+ int buflen;
+ int len;
+ char prefix[130];
+
+ va_start(ap, fmt);
+ buflen = vsnprintf(NULL, 0, fmt, ap) + 1;
+ va_end(ap);
+
+ buf = os_malloc(buflen);
+ if (buf == NULL) {
+ wpa_printf(MSG_ERROR, "wpa_msg: Failed to allocate message "
+ "buffer");
+ return;
+ }
+ va_start(ap, fmt);
+ prefix[0] = '\0';
+ if (wpa_msg_ifname_cb) {
+ const char *ifname = wpa_msg_ifname_cb(ctx);
+ if (ifname) {
+ int res = os_snprintf(prefix, sizeof(prefix), "%s: ",
+ ifname);
+ if (os_snprintf_error(sizeof(prefix), res))
+ prefix[0] = '\0';
+ }
+ }
+ len = vsnprintf(buf, buflen, fmt, ap);
+ va_end(ap);
+ wpa_printf(level, "%s%s", prefix, buf);
+ if (wpa_msg_cb)
+ wpa_msg_cb(ctx, level, WPA_MSG_PER_INTERFACE, buf, len);
+ bin_clear_free(buf, buflen);
+}
+
+
+void wpa_msg_ctrl(void *ctx, int level, const char *fmt, ...)
+{
+ va_list ap;
+ char *buf;
+ int buflen;
+ int len;
+
+ if (!wpa_msg_cb)
+ return;
+
+ va_start(ap, fmt);
+ buflen = vsnprintf(NULL, 0, fmt, ap) + 1;
+ va_end(ap);
+
+ buf = os_malloc(buflen);
+ if (buf == NULL) {
+ wpa_printf(MSG_ERROR, "wpa_msg_ctrl: Failed to allocate "
+ "message buffer");
+ return;
+ }
+ va_start(ap, fmt);
+ len = vsnprintf(buf, buflen, fmt, ap);
+ va_end(ap);
+ wpa_msg_cb(ctx, level, WPA_MSG_PER_INTERFACE, buf, len);
+ bin_clear_free(buf, buflen);
+}
+
+
+void wpa_msg_global(void *ctx, int level, const char *fmt, ...)
+{
+ va_list ap;
+ char *buf;
+ int buflen;
+ int len;
+
+ va_start(ap, fmt);
+ buflen = vsnprintf(NULL, 0, fmt, ap) + 1;
+ va_end(ap);
+
+ buf = os_malloc(buflen);
+ if (buf == NULL) {
+ wpa_printf(MSG_ERROR, "wpa_msg_global: Failed to allocate "
+ "message buffer");
+ return;
+ }
+ va_start(ap, fmt);
+ len = vsnprintf(buf, buflen, fmt, ap);
+ va_end(ap);
+ wpa_printf(level, "%s", buf);
+ if (wpa_msg_cb)
+ wpa_msg_cb(ctx, level, WPA_MSG_GLOBAL, buf, len);
+ bin_clear_free(buf, buflen);
+}
+
+
+void wpa_msg_global_ctrl(void *ctx, int level, const char *fmt, ...)
+{
+ va_list ap;
+ char *buf;
+ int buflen;
+ int len;
+
+ if (!wpa_msg_cb)
+ return;
+
+ va_start(ap, fmt);
+ buflen = vsnprintf(NULL, 0, fmt, ap) + 1;
+ va_end(ap);
+
+ buf = os_malloc(buflen);
+ if (buf == NULL) {
+ wpa_printf(MSG_ERROR,
+ "wpa_msg_global_ctrl: Failed to allocate message buffer");
+ return;
+ }
+ va_start(ap, fmt);
+ len = vsnprintf(buf, buflen, fmt, ap);
+ va_end(ap);
+ wpa_msg_cb(ctx, level, WPA_MSG_GLOBAL, buf, len);
+ bin_clear_free(buf, buflen);
+}
+
+
+void wpa_msg_no_global(void *ctx, int level, const char *fmt, ...)
+{
+ va_list ap;
+ char *buf;
+ int buflen;
+ int len;
+
+ va_start(ap, fmt);
+ buflen = vsnprintf(NULL, 0, fmt, ap) + 1;
+ va_end(ap);
+
+ buf = os_malloc(buflen);
+ if (buf == NULL) {
+ wpa_printf(MSG_ERROR, "wpa_msg_no_global: Failed to allocate "
+ "message buffer");
+ return;
+ }
+ va_start(ap, fmt);
+ len = vsnprintf(buf, buflen, fmt, ap);
+ va_end(ap);
+ wpa_printf(level, "%s", buf);
+ if (wpa_msg_cb)
+ wpa_msg_cb(ctx, level, WPA_MSG_NO_GLOBAL, buf, len);
+ bin_clear_free(buf, buflen);
+}
+
+
+void wpa_msg_global_only(void *ctx, int level, const char *fmt, ...)
+{
+ va_list ap;
+ char *buf;
+ int buflen;
+ int len;
+
+ va_start(ap, fmt);
+ buflen = vsnprintf(NULL, 0, fmt, ap) + 1;
+ va_end(ap);
+
+ buf = os_malloc(buflen);
+ if (buf == NULL) {
+ wpa_printf(MSG_ERROR, "%s: Failed to allocate message buffer",
+ __func__);
+ return;
+ }
+ va_start(ap, fmt);
+ len = vsnprintf(buf, buflen, fmt, ap);
+ va_end(ap);
+ wpa_printf(level, "%s", buf);
+ if (wpa_msg_cb)
+ wpa_msg_cb(ctx, level, WPA_MSG_ONLY_GLOBAL, buf, len);
+ os_free(buf);
+}
+
+#endif /* CONFIG_NO_WPA_MSG */
+
+
+#ifndef CONFIG_NO_HOSTAPD_LOGGER
+static hostapd_logger_cb_func hostapd_logger_cb = NULL;
+
+void hostapd_logger_register_cb(hostapd_logger_cb_func func)
+{
+ hostapd_logger_cb = func;
+}
+
+
+void hostapd_logger(void *ctx, const u8 *addr, unsigned int module, int level,
+ const char *fmt, ...)
+{
+ va_list ap;
+ char *buf;
+ int buflen;
+ int len;
+
+ va_start(ap, fmt);
+ buflen = vsnprintf(NULL, 0, fmt, ap) + 1;
+ va_end(ap);
+
+ buf = os_malloc(buflen);
+ if (buf == NULL) {
+ wpa_printf(MSG_ERROR, "hostapd_logger: Failed to allocate "
+ "message buffer");
+ return;
+ }
+ va_start(ap, fmt);
+ len = vsnprintf(buf, buflen, fmt, ap);
+ va_end(ap);
+ if (hostapd_logger_cb)
+ hostapd_logger_cb(ctx, addr, module, level, buf, len);
+ else if (addr)
+ wpa_printf(MSG_DEBUG, "hostapd_logger: STA " MACSTR " - %s",
+ MAC2STR(addr), buf);
+ else
+ wpa_printf(MSG_DEBUG, "hostapd_logger: %s", buf);
+ bin_clear_free(buf, buflen);
+}
+#endif /* CONFIG_NO_HOSTAPD_LOGGER */
+
+
+const char * debug_level_str(int level)
+{
+ switch (level) {
+ case MSG_EXCESSIVE:
+ return "EXCESSIVE";
+ case MSG_MSGDUMP:
+ return "MSGDUMP";
+ case MSG_DEBUG:
+ return "DEBUG";
+ case MSG_INFO:
+ return "INFO";
+ case MSG_WARNING:
+ return "WARNING";
+ case MSG_ERROR:
+ return "ERROR";
+ default:
+ return "?";
+ }
+}
+
+
+int str_to_debug_level(const char *s)
+{
+ if (os_strcasecmp(s, "EXCESSIVE") == 0)
+ return MSG_EXCESSIVE;
+ if (os_strcasecmp(s, "MSGDUMP") == 0)
+ return MSG_MSGDUMP;
+ if (os_strcasecmp(s, "DEBUG") == 0)
+ return MSG_DEBUG;
+ if (os_strcasecmp(s, "INFO") == 0)
+ return MSG_INFO;
+ if (os_strcasecmp(s, "WARNING") == 0)
+ return MSG_WARNING;
+ if (os_strcasecmp(s, "ERROR") == 0)
+ return MSG_ERROR;
+ return -1;
+}
diff --git a/freebsd/contrib/wpa/src/utils/wpa_debug.h b/freebsd/contrib/wpa/src/utils/wpa_debug.h
new file mode 100644
index 00000000..17d8f963
--- /dev/null
+++ b/freebsd/contrib/wpa/src/utils/wpa_debug.h
@@ -0,0 +1,370 @@
+/*
+ * wpa_supplicant/hostapd / Debug prints
+ * Copyright (c) 2002-2013, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef WPA_DEBUG_H
+#define WPA_DEBUG_H
+
+#include "wpabuf.h"
+
+extern int wpa_debug_level;
+extern int wpa_debug_show_keys;
+extern int wpa_debug_timestamp;
+
+/* Debugging function - conditional printf and hex dump. Driver wrappers can
+ * use these for debugging purposes. */
+
+enum {
+ MSG_EXCESSIVE, MSG_MSGDUMP, MSG_DEBUG, MSG_INFO, MSG_WARNING, MSG_ERROR
+};
+
+#ifdef CONFIG_NO_STDOUT_DEBUG
+
+#define wpa_debug_print_timestamp() do { } while (0)
+#define wpa_printf(args...) do { } while (0)
+#define wpa_hexdump(l,t,b,le) do { } while (0)
+#define wpa_hexdump_buf(l,t,b) do { } while (0)
+#define wpa_hexdump_key(l,t,b,le) do { } while (0)
+#define wpa_hexdump_buf_key(l,t,b) do { } while (0)
+#define wpa_hexdump_ascii(l,t,b,le) do { } while (0)
+#define wpa_hexdump_ascii_key(l,t,b,le) do { } while (0)
+#define wpa_debug_open_file(p) do { } while (0)
+#define wpa_debug_close_file() do { } while (0)
+#define wpa_debug_setup_stdout() do { } while (0)
+#define wpa_dbg(args...) do { } while (0)
+
+static inline int wpa_debug_reopen_file(void)
+{
+ return 0;
+}
+
+#else /* CONFIG_NO_STDOUT_DEBUG */
+
+int wpa_debug_open_file(const char *path);
+int wpa_debug_reopen_file(void);
+void wpa_debug_close_file(void);
+void wpa_debug_setup_stdout(void);
+
+/**
+ * wpa_debug_printf_timestamp - Print timestamp for debug output
+ *
+ * This function prints a timestamp in seconds_from_1970.microsoconds
+ * format if debug output has been configured to include timestamps in debug
+ * messages.
+ */
+void wpa_debug_print_timestamp(void);
+
+/**
+ * wpa_printf - conditional printf
+ * @level: priority level (MSG_*) of the message
+ * @fmt: printf format string, followed by optional arguments
+ *
+ * This function is used to print conditional debugging and error messages. The
+ * output may be directed to stdout, stderr, and/or syslog based on
+ * configuration.
+ *
+ * Note: New line '\n' is added to the end of the text when printing to stdout.
+ */
+void wpa_printf(int level, const char *fmt, ...)
+PRINTF_FORMAT(2, 3);
+
+/**
+ * wpa_hexdump - conditional hex dump
+ * @level: priority level (MSG_*) of the message
+ * @title: title of for the message
+ * @buf: data buffer to be dumped
+ * @len: length of the buf
+ *
+ * This function is used to print conditional debugging and error messages. The
+ * output may be directed to stdout, stderr, and/or syslog based on
+ * configuration. The contents of buf is printed out has hex dump.
+ */
+void wpa_hexdump(int level, const char *title, const void *buf, size_t len);
+
+static inline void wpa_hexdump_buf(int level, const char *title,
+ const struct wpabuf *buf)
+{
+ wpa_hexdump(level, title, buf ? wpabuf_head(buf) : NULL,
+ buf ? wpabuf_len(buf) : 0);
+}
+
+/**
+ * wpa_hexdump_key - conditional hex dump, hide keys
+ * @level: priority level (MSG_*) of the message
+ * @title: title of for the message
+ * @buf: data buffer to be dumped
+ * @len: length of the buf
+ *
+ * This function is used to print conditional debugging and error messages. The
+ * output may be directed to stdout, stderr, and/or syslog based on
+ * configuration. The contents of buf is printed out has hex dump. This works
+ * like wpa_hexdump(), but by default, does not include secret keys (passwords,
+ * etc.) in debug output.
+ */
+void wpa_hexdump_key(int level, const char *title, const void *buf, size_t len);
+
+static inline void wpa_hexdump_buf_key(int level, const char *title,
+ const struct wpabuf *buf)
+{
+ wpa_hexdump_key(level, title, buf ? wpabuf_head(buf) : NULL,
+ buf ? wpabuf_len(buf) : 0);
+}
+
+/**
+ * wpa_hexdump_ascii - conditional hex dump
+ * @level: priority level (MSG_*) of the message
+ * @title: title of for the message
+ * @buf: data buffer to be dumped
+ * @len: length of the buf
+ *
+ * This function is used to print conditional debugging and error messages. The
+ * output may be directed to stdout, stderr, and/or syslog based on
+ * configuration. The contents of buf is printed out has hex dump with both
+ * the hex numbers and ASCII characters (for printable range) are shown. 16
+ * bytes per line will be shown.
+ */
+void wpa_hexdump_ascii(int level, const char *title, const void *buf,
+ size_t len);
+
+/**
+ * wpa_hexdump_ascii_key - conditional hex dump, hide keys
+ * @level: priority level (MSG_*) of the message
+ * @title: title of for the message
+ * @buf: data buffer to be dumped
+ * @len: length of the buf
+ *
+ * This function is used to print conditional debugging and error messages. The
+ * output may be directed to stdout, stderr, and/or syslog based on
+ * configuration. The contents of buf is printed out has hex dump with both
+ * the hex numbers and ASCII characters (for printable range) are shown. 16
+ * bytes per line will be shown. This works like wpa_hexdump_ascii(), but by
+ * default, does not include secret keys (passwords, etc.) in debug output.
+ */
+void wpa_hexdump_ascii_key(int level, const char *title, const void *buf,
+ size_t len);
+
+/*
+ * wpa_dbg() behaves like wpa_msg(), but it can be removed from build to reduce
+ * binary size. As such, it should be used with debugging messages that are not
+ * needed in the control interface while wpa_msg() has to be used for anything
+ * that needs to shown to control interface monitors.
+ */
+#define wpa_dbg(args...) wpa_msg(args)
+
+#endif /* CONFIG_NO_STDOUT_DEBUG */
+
+
+#ifdef CONFIG_NO_WPA_MSG
+#define wpa_msg(args...) do { } while (0)
+#define wpa_msg_ctrl(args...) do { } while (0)
+#define wpa_msg_global(args...) do { } while (0)
+#define wpa_msg_global_ctrl(args...) do { } while (0)
+#define wpa_msg_no_global(args...) do { } while (0)
+#define wpa_msg_global_only(args...) do { } while (0)
+#define wpa_msg_register_cb(f) do { } while (0)
+#define wpa_msg_register_ifname_cb(f) do { } while (0)
+#else /* CONFIG_NO_WPA_MSG */
+/**
+ * wpa_msg - Conditional printf for default target and ctrl_iface monitors
+ * @ctx: Pointer to context data; this is the ctx variable registered
+ * with struct wpa_driver_ops::init()
+ * @level: priority level (MSG_*) of the message
+ * @fmt: printf format string, followed by optional arguments
+ *
+ * This function is used to print conditional debugging and error messages. The
+ * output may be directed to stdout, stderr, and/or syslog based on
+ * configuration. This function is like wpa_printf(), but it also sends the
+ * same message to all attached ctrl_iface monitors.
+ *
+ * Note: New line '\n' is added to the end of the text when printing to stdout.
+ */
+void wpa_msg(void *ctx, int level, const char *fmt, ...) PRINTF_FORMAT(3, 4);
+
+/**
+ * wpa_msg_ctrl - Conditional printf for ctrl_iface monitors
+ * @ctx: Pointer to context data; this is the ctx variable registered
+ * with struct wpa_driver_ops::init()
+ * @level: priority level (MSG_*) of the message
+ * @fmt: printf format string, followed by optional arguments
+ *
+ * This function is used to print conditional debugging and error messages.
+ * This function is like wpa_msg(), but it sends the output only to the
+ * attached ctrl_iface monitors. In other words, it can be used for frequent
+ * events that do not need to be sent to syslog.
+ */
+void wpa_msg_ctrl(void *ctx, int level, const char *fmt, ...)
+PRINTF_FORMAT(3, 4);
+
+/**
+ * wpa_msg_global - Global printf for ctrl_iface monitors
+ * @ctx: Pointer to context data; this is the ctx variable registered
+ * with struct wpa_driver_ops::init()
+ * @level: priority level (MSG_*) of the message
+ * @fmt: printf format string, followed by optional arguments
+ *
+ * This function is used to print conditional debugging and error messages.
+ * This function is like wpa_msg(), but it sends the output as a global event,
+ * i.e., without being specific to an interface. For backwards compatibility,
+ * an old style event is also delivered on one of the interfaces (the one
+ * specified by the context data).
+ */
+void wpa_msg_global(void *ctx, int level, const char *fmt, ...)
+PRINTF_FORMAT(3, 4);
+
+/**
+ * wpa_msg_global_ctrl - Conditional global printf for ctrl_iface monitors
+ * @ctx: Pointer to context data; this is the ctx variable registered
+ * with struct wpa_driver_ops::init()
+ * @level: priority level (MSG_*) of the message
+ * @fmt: printf format string, followed by optional arguments
+ *
+ * This function is used to print conditional debugging and error messages.
+ * This function is like wpa_msg_global(), but it sends the output only to the
+ * attached global ctrl_iface monitors. In other words, it can be used for
+ * frequent events that do not need to be sent to syslog.
+ */
+void wpa_msg_global_ctrl(void *ctx, int level, const char *fmt, ...)
+PRINTF_FORMAT(3, 4);
+
+/**
+ * wpa_msg_no_global - Conditional printf for ctrl_iface monitors
+ * @ctx: Pointer to context data; this is the ctx variable registered
+ * with struct wpa_driver_ops::init()
+ * @level: priority level (MSG_*) of the message
+ * @fmt: printf format string, followed by optional arguments
+ *
+ * This function is used to print conditional debugging and error messages.
+ * This function is like wpa_msg(), but it does not send the output as a global
+ * event.
+ */
+void wpa_msg_no_global(void *ctx, int level, const char *fmt, ...)
+PRINTF_FORMAT(3, 4);
+
+/**
+ * wpa_msg_global_only - Conditional printf for ctrl_iface monitors
+ * @ctx: Pointer to context data; this is the ctx variable registered
+ * with struct wpa_driver_ops::init()
+ * @level: priority level (MSG_*) of the message
+ * @fmt: printf format string, followed by optional arguments
+ *
+ * This function is used to print conditional debugging and error messages.
+ * This function is like wpa_msg_global(), but it sends the output only as a
+ * global event.
+ */
+void wpa_msg_global_only(void *ctx, int level, const char *fmt, ...)
+PRINTF_FORMAT(3, 4);
+
+enum wpa_msg_type {
+ WPA_MSG_PER_INTERFACE,
+ WPA_MSG_GLOBAL,
+ WPA_MSG_NO_GLOBAL,
+ WPA_MSG_ONLY_GLOBAL,
+};
+
+typedef void (*wpa_msg_cb_func)(void *ctx, int level, enum wpa_msg_type type,
+ const char *txt, size_t len);
+
+/**
+ * wpa_msg_register_cb - Register callback function for wpa_msg() messages
+ * @func: Callback function (%NULL to unregister)
+ */
+void wpa_msg_register_cb(wpa_msg_cb_func func);
+
+typedef const char * (*wpa_msg_get_ifname_func)(void *ctx);
+void wpa_msg_register_ifname_cb(wpa_msg_get_ifname_func func);
+
+#endif /* CONFIG_NO_WPA_MSG */
+
+#ifdef CONFIG_NO_HOSTAPD_LOGGER
+#define hostapd_logger(args...) do { } while (0)
+#define hostapd_logger_register_cb(f) do { } while (0)
+#else /* CONFIG_NO_HOSTAPD_LOGGER */
+void hostapd_logger(void *ctx, const u8 *addr, unsigned int module, int level,
+ const char *fmt, ...) PRINTF_FORMAT(5, 6);
+
+typedef void (*hostapd_logger_cb_func)(void *ctx, const u8 *addr,
+ unsigned int module, int level,
+ const char *txt, size_t len);
+
+/**
+ * hostapd_logger_register_cb - Register callback function for hostapd_logger()
+ * @func: Callback function (%NULL to unregister)
+ */
+void hostapd_logger_register_cb(hostapd_logger_cb_func func);
+#endif /* CONFIG_NO_HOSTAPD_LOGGER */
+
+#define HOSTAPD_MODULE_IEEE80211 0x00000001
+#define HOSTAPD_MODULE_IEEE8021X 0x00000002
+#define HOSTAPD_MODULE_RADIUS 0x00000004
+#define HOSTAPD_MODULE_WPA 0x00000008
+#define HOSTAPD_MODULE_DRIVER 0x00000010
+#define HOSTAPD_MODULE_IAPP 0x00000020
+#define HOSTAPD_MODULE_MLME 0x00000040
+
+enum hostapd_logger_level {
+ HOSTAPD_LEVEL_DEBUG_VERBOSE = 0,
+ HOSTAPD_LEVEL_DEBUG = 1,
+ HOSTAPD_LEVEL_INFO = 2,
+ HOSTAPD_LEVEL_NOTICE = 3,
+ HOSTAPD_LEVEL_WARNING = 4
+};
+
+
+#ifdef CONFIG_DEBUG_SYSLOG
+
+void wpa_debug_open_syslog(void);
+void wpa_debug_close_syslog(void);
+
+#else /* CONFIG_DEBUG_SYSLOG */
+
+static inline void wpa_debug_open_syslog(void)
+{
+}
+
+static inline void wpa_debug_close_syslog(void)
+{
+}
+
+#endif /* CONFIG_DEBUG_SYSLOG */
+
+#ifdef CONFIG_DEBUG_LINUX_TRACING
+
+int wpa_debug_open_linux_tracing(void);
+void wpa_debug_close_linux_tracing(void);
+
+#else /* CONFIG_DEBUG_LINUX_TRACING */
+
+static inline int wpa_debug_open_linux_tracing(void)
+{
+ return 0;
+}
+
+static inline void wpa_debug_close_linux_tracing(void)
+{
+}
+
+#endif /* CONFIG_DEBUG_LINUX_TRACING */
+
+
+#ifdef EAPOL_TEST
+#define WPA_ASSERT(a) \
+ do { \
+ if (!(a)) { \
+ printf("WPA_ASSERT FAILED '" #a "' " \
+ "%s %s:%d\n", \
+ __FUNCTION__, __FILE__, __LINE__); \
+ exit(1); \
+ } \
+ } while (0)
+#else
+#define WPA_ASSERT(a) do { } while (0)
+#endif
+
+const char * debug_level_str(int level);
+int str_to_debug_level(const char *s);
+
+#endif /* WPA_DEBUG_H */
diff --git a/freebsd/contrib/wpa/src/utils/wpabuf.c b/freebsd/contrib/wpa/src/utils/wpabuf.c
new file mode 100644
index 00000000..c6e22aeb
--- /dev/null
+++ b/freebsd/contrib/wpa/src/utils/wpabuf.c
@@ -0,0 +1,314 @@
+#include <machine/rtems-bsd-user-space.h>
+
+/*
+ * Dynamic data buffer
+ * 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.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "trace.h"
+#include "wpabuf.h"
+
+#ifdef WPA_TRACE
+#define WPABUF_MAGIC 0x51a974e3
+
+struct wpabuf_trace {
+ unsigned int magic;
+} __attribute__((aligned(8)));
+
+static struct wpabuf_trace * wpabuf_get_trace(const struct wpabuf *buf)
+{
+ return (struct wpabuf_trace *)
+ ((const u8 *) buf - sizeof(struct wpabuf_trace));
+}
+#endif /* WPA_TRACE */
+
+
+static void wpabuf_overflow(const struct wpabuf *buf, size_t len)
+{
+#ifdef WPA_TRACE
+ struct wpabuf_trace *trace = wpabuf_get_trace(buf);
+ if (trace->magic != WPABUF_MAGIC) {
+ wpa_printf(MSG_ERROR, "wpabuf: invalid magic %x",
+ trace->magic);
+ }
+#endif /* WPA_TRACE */
+ wpa_printf(MSG_ERROR, "wpabuf %p (size=%lu used=%lu) overflow len=%lu",
+ buf, (unsigned long) buf->size, (unsigned long) buf->used,
+ (unsigned long) len);
+ wpa_trace_show("wpabuf overflow");
+ abort();
+}
+
+
+int wpabuf_resize(struct wpabuf **_buf, size_t add_len)
+{
+ struct wpabuf *buf = *_buf;
+#ifdef WPA_TRACE
+ struct wpabuf_trace *trace;
+#endif /* WPA_TRACE */
+
+ if (buf == NULL) {
+ *_buf = wpabuf_alloc(add_len);
+ return *_buf == NULL ? -1 : 0;
+ }
+
+#ifdef WPA_TRACE
+ trace = wpabuf_get_trace(buf);
+ if (trace->magic != WPABUF_MAGIC) {
+ wpa_printf(MSG_ERROR, "wpabuf: invalid magic %x",
+ trace->magic);
+ wpa_trace_show("wpabuf_resize invalid magic");
+ abort();
+ }
+#endif /* WPA_TRACE */
+
+ if (buf->used + add_len > buf->size) {
+ unsigned char *nbuf;
+ if (buf->flags & WPABUF_FLAG_EXT_DATA) {
+ nbuf = os_realloc(buf->buf, buf->used + add_len);
+ if (nbuf == NULL)
+ return -1;
+ os_memset(nbuf + buf->used, 0, add_len);
+ buf->buf = nbuf;
+ } else {
+#ifdef WPA_TRACE
+ nbuf = os_realloc(trace, sizeof(struct wpabuf_trace) +
+ sizeof(struct wpabuf) +
+ buf->used + add_len);
+ if (nbuf == NULL)
+ return -1;
+ trace = (struct wpabuf_trace *) nbuf;
+ buf = (struct wpabuf *) (trace + 1);
+ os_memset(nbuf + sizeof(struct wpabuf_trace) +
+ sizeof(struct wpabuf) + buf->used, 0,
+ add_len);
+#else /* WPA_TRACE */
+ nbuf = os_realloc(buf, sizeof(struct wpabuf) +
+ buf->used + add_len);
+ if (nbuf == NULL)
+ return -1;
+ buf = (struct wpabuf *) nbuf;
+ os_memset(nbuf + sizeof(struct wpabuf) + buf->used, 0,
+ add_len);
+#endif /* WPA_TRACE */
+ buf->buf = (u8 *) (buf + 1);
+ *_buf = buf;
+ }
+ buf->size = buf->used + add_len;
+ }
+
+ return 0;
+}
+
+
+/**
+ * wpabuf_alloc - Allocate a wpabuf of the given size
+ * @len: Length for the allocated buffer
+ * Returns: Buffer to the allocated wpabuf or %NULL on failure
+ */
+struct wpabuf * wpabuf_alloc(size_t len)
+{
+#ifdef WPA_TRACE
+ struct wpabuf_trace *trace = os_zalloc(sizeof(struct wpabuf_trace) +
+ sizeof(struct wpabuf) + len);
+ struct wpabuf *buf;
+ if (trace == NULL)
+ return NULL;
+ trace->magic = WPABUF_MAGIC;
+ buf = (struct wpabuf *) (trace + 1);
+#else /* WPA_TRACE */
+ struct wpabuf *buf = os_zalloc(sizeof(struct wpabuf) + len);
+ if (buf == NULL)
+ return NULL;
+#endif /* WPA_TRACE */
+
+ buf->size = len;
+ buf->buf = (u8 *) (buf + 1);
+ return buf;
+}
+
+
+struct wpabuf * wpabuf_alloc_ext_data(u8 *data, size_t len)
+{
+#ifdef WPA_TRACE
+ struct wpabuf_trace *trace = os_zalloc(sizeof(struct wpabuf_trace) +
+ sizeof(struct wpabuf));
+ struct wpabuf *buf;
+ if (trace == NULL)
+ return NULL;
+ trace->magic = WPABUF_MAGIC;
+ buf = (struct wpabuf *) (trace + 1);
+#else /* WPA_TRACE */
+ struct wpabuf *buf = os_zalloc(sizeof(struct wpabuf));
+ if (buf == NULL)
+ return NULL;
+#endif /* WPA_TRACE */
+
+ buf->size = len;
+ buf->used = len;
+ buf->buf = data;
+ buf->flags |= WPABUF_FLAG_EXT_DATA;
+
+ return buf;
+}
+
+
+struct wpabuf * wpabuf_alloc_copy(const void *data, size_t len)
+{
+ struct wpabuf *buf = wpabuf_alloc(len);
+ if (buf)
+ wpabuf_put_data(buf, data, len);
+ return buf;
+}
+
+
+struct wpabuf * wpabuf_dup(const struct wpabuf *src)
+{
+ struct wpabuf *buf = wpabuf_alloc(wpabuf_len(src));
+ if (buf)
+ wpabuf_put_data(buf, wpabuf_head(src), wpabuf_len(src));
+ return buf;
+}
+
+
+/**
+ * wpabuf_free - Free a wpabuf
+ * @buf: wpabuf buffer
+ */
+void wpabuf_free(struct wpabuf *buf)
+{
+#ifdef WPA_TRACE
+ struct wpabuf_trace *trace;
+ if (buf == NULL)
+ return;
+ trace = wpabuf_get_trace(buf);
+ if (trace->magic != WPABUF_MAGIC) {
+ wpa_printf(MSG_ERROR, "wpabuf_free: invalid magic %x",
+ trace->magic);
+ wpa_trace_show("wpabuf_free magic mismatch");
+ abort();
+ }
+ if (buf->flags & WPABUF_FLAG_EXT_DATA)
+ os_free(buf->buf);
+ os_free(trace);
+#else /* WPA_TRACE */
+ if (buf == NULL)
+ return;
+ if (buf->flags & WPABUF_FLAG_EXT_DATA)
+ os_free(buf->buf);
+ os_free(buf);
+#endif /* WPA_TRACE */
+}
+
+
+void wpabuf_clear_free(struct wpabuf *buf)
+{
+ if (buf) {
+ os_memset(wpabuf_mhead(buf), 0, wpabuf_len(buf));
+ wpabuf_free(buf);
+ }
+}
+
+
+void * wpabuf_put(struct wpabuf *buf, size_t len)
+{
+ void *tmp = wpabuf_mhead_u8(buf) + wpabuf_len(buf);
+ buf->used += len;
+ if (buf->used > buf->size) {
+ wpabuf_overflow(buf, len);
+ }
+ return tmp;
+}
+
+
+/**
+ * wpabuf_concat - Concatenate two buffers into a newly allocated one
+ * @a: First buffer
+ * @b: Second buffer
+ * Returns: wpabuf with concatenated a + b data or %NULL on failure
+ *
+ * Both buffers a and b will be freed regardless of the return value. Input
+ * buffers can be %NULL which is interpreted as an empty buffer.
+ */
+struct wpabuf * wpabuf_concat(struct wpabuf *a, struct wpabuf *b)
+{
+ struct wpabuf *n = NULL;
+ size_t len = 0;
+
+ if (b == NULL)
+ return a;
+
+ if (a)
+ len += wpabuf_len(a);
+ if (b)
+ len += wpabuf_len(b);
+
+ n = wpabuf_alloc(len);
+ if (n) {
+ if (a)
+ wpabuf_put_buf(n, a);
+ if (b)
+ wpabuf_put_buf(n, b);
+ }
+
+ wpabuf_free(a);
+ wpabuf_free(b);
+
+ return n;
+}
+
+
+/**
+ * wpabuf_zeropad - Pad buffer with 0x00 octets (prefix) to specified length
+ * @buf: Buffer to be padded
+ * @len: Length for the padded buffer
+ * Returns: wpabuf padded to len octets or %NULL on failure
+ *
+ * If buf is longer than len octets or of same size, it will be returned as-is.
+ * Otherwise a new buffer is allocated and prefixed with 0x00 octets followed
+ * by the source data. The source buffer will be freed on error, i.e., caller
+ * will only be responsible on freeing the returned buffer. If buf is %NULL,
+ * %NULL will be returned.
+ */
+struct wpabuf * wpabuf_zeropad(struct wpabuf *buf, size_t len)
+{
+ struct wpabuf *ret;
+ size_t blen;
+
+ if (buf == NULL)
+ return NULL;
+
+ blen = wpabuf_len(buf);
+ if (blen >= len)
+ return buf;
+
+ ret = wpabuf_alloc(len);
+ if (ret) {
+ os_memset(wpabuf_put(ret, len - blen), 0, len - blen);
+ wpabuf_put_buf(ret, buf);
+ }
+ wpabuf_free(buf);
+
+ return ret;
+}
+
+
+void wpabuf_printf(struct wpabuf *buf, char *fmt, ...)
+{
+ va_list ap;
+ void *tmp = wpabuf_mhead_u8(buf) + wpabuf_len(buf);
+ int res;
+
+ va_start(ap, fmt);
+ res = vsnprintf(tmp, buf->size - buf->used, fmt, ap);
+ va_end(ap);
+ if (res < 0 || (size_t) res >= buf->size - buf->used)
+ wpabuf_overflow(buf, res);
+ buf->used += res;
+}
diff --git a/freebsd/contrib/wpa/src/utils/wpabuf.h b/freebsd/contrib/wpa/src/utils/wpabuf.h
new file mode 100644
index 00000000..c3ef1bae
--- /dev/null
+++ b/freebsd/contrib/wpa/src/utils/wpabuf.h
@@ -0,0 +1,163 @@
+/*
+ * Dynamic data buffer
+ * 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 WPABUF_H
+#define WPABUF_H
+
+/* wpabuf::buf is a pointer to external data */
+#define WPABUF_FLAG_EXT_DATA BIT(0)
+
+/*
+ * Internal data structure for wpabuf. Please do not touch this directly from
+ * elsewhere. This is only defined in header file to allow inline functions
+ * from this file to access data.
+ */
+struct wpabuf {
+ size_t size; /* total size of the allocated buffer */
+ size_t used; /* length of data in the buffer */
+ u8 *buf; /* pointer to the head of the buffer */
+ unsigned int flags;
+ /* optionally followed by the allocated buffer */
+};
+
+
+int wpabuf_resize(struct wpabuf **buf, size_t add_len);
+struct wpabuf * wpabuf_alloc(size_t len);
+struct wpabuf * wpabuf_alloc_ext_data(u8 *data, size_t len);
+struct wpabuf * wpabuf_alloc_copy(const void *data, size_t len);
+struct wpabuf * wpabuf_dup(const struct wpabuf *src);
+void wpabuf_free(struct wpabuf *buf);
+void wpabuf_clear_free(struct wpabuf *buf);
+void * wpabuf_put(struct wpabuf *buf, size_t len);
+struct wpabuf * wpabuf_concat(struct wpabuf *a, struct wpabuf *b);
+struct wpabuf * wpabuf_zeropad(struct wpabuf *buf, size_t len);
+void wpabuf_printf(struct wpabuf *buf, char *fmt, ...) PRINTF_FORMAT(2, 3);
+
+
+/**
+ * wpabuf_size - Get the currently allocated size of a wpabuf buffer
+ * @buf: wpabuf buffer
+ * Returns: Currently allocated size of the buffer
+ */
+static inline size_t wpabuf_size(const struct wpabuf *buf)
+{
+ return buf->size;
+}
+
+/**
+ * wpabuf_len - Get the current length of a wpabuf buffer data
+ * @buf: wpabuf buffer
+ * Returns: Currently used length of the buffer
+ */
+static inline size_t wpabuf_len(const struct wpabuf *buf)
+{
+ return buf->used;
+}
+
+/**
+ * wpabuf_tailroom - Get size of available tail room in the end of the buffer
+ * @buf: wpabuf buffer
+ * Returns: Tail room (in bytes) of available space in the end of the buffer
+ */
+static inline size_t wpabuf_tailroom(const struct wpabuf *buf)
+{
+ return buf->size - buf->used;
+}
+
+/**
+ * wpabuf_head - Get pointer to the head of the buffer data
+ * @buf: wpabuf buffer
+ * Returns: Pointer to the head of the buffer data
+ */
+static inline const void * wpabuf_head(const struct wpabuf *buf)
+{
+ return buf->buf;
+}
+
+static inline const u8 * wpabuf_head_u8(const struct wpabuf *buf)
+{
+ return wpabuf_head(buf);
+}
+
+/**
+ * wpabuf_mhead - Get modifiable pointer to the head of the buffer data
+ * @buf: wpabuf buffer
+ * Returns: Pointer to the head of the buffer data
+ */
+static inline void * wpabuf_mhead(struct wpabuf *buf)
+{
+ return buf->buf;
+}
+
+static inline u8 * wpabuf_mhead_u8(struct wpabuf *buf)
+{
+ return wpabuf_mhead(buf);
+}
+
+static inline void wpabuf_put_u8(struct wpabuf *buf, u8 data)
+{
+ u8 *pos = wpabuf_put(buf, 1);
+ *pos = data;
+}
+
+static inline void wpabuf_put_le16(struct wpabuf *buf, u16 data)
+{
+ u8 *pos = wpabuf_put(buf, 2);
+ WPA_PUT_LE16(pos, data);
+}
+
+static inline void wpabuf_put_le32(struct wpabuf *buf, u32 data)
+{
+ u8 *pos = wpabuf_put(buf, 4);
+ WPA_PUT_LE32(pos, data);
+}
+
+static inline void wpabuf_put_be16(struct wpabuf *buf, u16 data)
+{
+ u8 *pos = wpabuf_put(buf, 2);
+ WPA_PUT_BE16(pos, data);
+}
+
+static inline void wpabuf_put_be24(struct wpabuf *buf, u32 data)
+{
+ u8 *pos = wpabuf_put(buf, 3);
+ WPA_PUT_BE24(pos, data);
+}
+
+static inline void wpabuf_put_be32(struct wpabuf *buf, u32 data)
+{
+ u8 *pos = wpabuf_put(buf, 4);
+ WPA_PUT_BE32(pos, data);
+}
+
+static inline void wpabuf_put_data(struct wpabuf *buf, const void *data,
+ size_t len)
+{
+ if (data)
+ os_memcpy(wpabuf_put(buf, len), data, len);
+}
+
+static inline void wpabuf_put_buf(struct wpabuf *dst,
+ const struct wpabuf *src)
+{
+ wpabuf_put_data(dst, wpabuf_head(src), wpabuf_len(src));
+}
+
+static inline void wpabuf_set(struct wpabuf *buf, const void *data, size_t len)
+{
+ buf->buf = (u8 *) data;
+ buf->flags = WPABUF_FLAG_EXT_DATA;
+ buf->size = buf->used = len;
+}
+
+static inline void wpabuf_put_str(struct wpabuf *dst, const char *str)
+{
+ wpabuf_put_data(dst, str, os_strlen(str));
+}
+
+#endif /* WPABUF_H */
diff --git a/freebsd/contrib/wpa/src/wps/http.h b/freebsd/contrib/wpa/src/wps/http.h
new file mode 100644
index 00000000..2fee3a8f
--- /dev/null
+++ b/freebsd/contrib/wpa/src/wps/http.h
@@ -0,0 +1,29 @@
+/*
+ * HTTP for WPS
+ * Copyright (c) 2000-2003 Intel Corporation
+ * Copyright (c) 2006-2007 Sony Corporation
+ * Copyright (c) 2008-2009 Atheros Communications
+ * Copyright (c) 2009, Jouni Malinen <j@w1.fi>
+ *
+ * See wps_upnp.c for more details on licensing and code history.
+ */
+
+#ifndef HTTP_H
+#define HTTP_H
+
+enum http_reply_code {
+ HTTP_OK = 200,
+ HTTP_BAD_REQUEST = 400,
+ UPNP_INVALID_ACTION = 401,
+ UPNP_INVALID_ARGS = 402,
+ HTTP_NOT_FOUND = 404,
+ HTTP_PRECONDITION_FAILED = 412,
+ HTTP_INTERNAL_SERVER_ERROR = 500,
+ HTTP_UNIMPLEMENTED = 501,
+ UPNP_ACTION_FAILED = 501,
+ UPNP_ARG_VALUE_INVALID = 600,
+ UPNP_ARG_VALUE_OUT_OF_RANGE = 601,
+ UPNP_OUT_OF_MEMORY = 603
+};
+
+#endif /* HTTP_H */
diff --git a/freebsd/contrib/wpa/src/wps/http_client.c b/freebsd/contrib/wpa/src/wps/http_client.c
new file mode 100644
index 00000000..c4f26dc9
--- /dev/null
+++ b/freebsd/contrib/wpa/src/wps/http_client.c
@@ -0,0 +1,364 @@
+#include <machine/rtems-bsd-user-space.h>
+
+/*
+ * http_client - HTTP client
+ * 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 "includes.h"
+#include <fcntl.h>
+
+#include "common.h"
+#include "eloop.h"
+#include "httpread.h"
+#include "http_client.h"
+
+
+#define HTTP_CLIENT_TIMEOUT_SEC 30
+
+
+struct http_client {
+ struct sockaddr_in dst;
+ int sd;
+ struct wpabuf *req;
+ size_t req_pos;
+ size_t max_response;
+
+ void (*cb)(void *ctx, struct http_client *c,
+ enum http_client_event event);
+ void *cb_ctx;
+ struct httpread *hread;
+ struct wpabuf body;
+};
+
+
+static void http_client_timeout(void *eloop_data, void *user_ctx)
+{
+ struct http_client *c = eloop_data;
+ wpa_printf(MSG_DEBUG, "HTTP: Timeout (c=%p)", c);
+ c->cb(c->cb_ctx, c, HTTP_CLIENT_TIMEOUT);
+}
+
+
+static void http_client_got_response(struct httpread *handle, void *cookie,
+ enum httpread_event e)
+{
+ struct http_client *c = cookie;
+
+ wpa_printf(MSG_DEBUG, "HTTP: httpread callback: handle=%p cookie=%p "
+ "e=%d", handle, cookie, e);
+
+ eloop_cancel_timeout(http_client_timeout, c, NULL);
+ switch (e) {
+ case HTTPREAD_EVENT_FILE_READY:
+ if (httpread_hdr_type_get(c->hread) == HTTPREAD_HDR_TYPE_REPLY)
+ {
+ int reply_code = httpread_reply_code_get(c->hread);
+ if (reply_code == 200 /* OK */) {
+ wpa_printf(MSG_DEBUG, "HTTP: Response OK from "
+ "%s:%d",
+ inet_ntoa(c->dst.sin_addr),
+ ntohs(c->dst.sin_port));
+ c->cb(c->cb_ctx, c, HTTP_CLIENT_OK);
+ } else {
+ wpa_printf(MSG_DEBUG, "HTTP: Error %d from "
+ "%s:%d", reply_code,
+ inet_ntoa(c->dst.sin_addr),
+ ntohs(c->dst.sin_port));
+ c->cb(c->cb_ctx, c, HTTP_CLIENT_INVALID_REPLY);
+ }
+ } else
+ c->cb(c->cb_ctx, c, HTTP_CLIENT_INVALID_REPLY);
+ break;
+ case HTTPREAD_EVENT_TIMEOUT:
+ c->cb(c->cb_ctx, c, HTTP_CLIENT_TIMEOUT);
+ break;
+ case HTTPREAD_EVENT_ERROR:
+ c->cb(c->cb_ctx, c, HTTP_CLIENT_FAILED);
+ break;
+ }
+}
+
+
+static void http_client_tx_ready(int sock, void *eloop_ctx, void *sock_ctx)
+{
+ struct http_client *c = eloop_ctx;
+ int res;
+ size_t send_len;
+
+ send_len = wpabuf_len(c->req) - c->req_pos;
+ wpa_printf(MSG_DEBUG, "HTTP: Send client request to %s:%d (%lu of %lu "
+ "bytes remaining)",
+ inet_ntoa(c->dst.sin_addr), ntohs(c->dst.sin_port),
+ (unsigned long) wpabuf_len(c->req),
+ (unsigned long) send_len);
+
+ res = send(c->sd, wpabuf_head_u8(c->req) + c->req_pos, send_len, 0);
+ if (res < 0) {
+ wpa_printf(MSG_DEBUG, "HTTP: Failed to send buffer: %s",
+ strerror(errno));
+ eloop_unregister_sock(c->sd, EVENT_TYPE_WRITE);
+ c->cb(c->cb_ctx, c, HTTP_CLIENT_FAILED);
+ return;
+ }
+
+ if ((size_t) res < send_len) {
+ wpa_printf(MSG_DEBUG, "HTTP: Sent %d of %lu bytes; %lu bytes "
+ "remaining",
+ res, (unsigned long) wpabuf_len(c->req),
+ (unsigned long) send_len - res);
+ c->req_pos += res;
+ return;
+ }
+
+ wpa_printf(MSG_DEBUG, "HTTP: Full client request sent to %s:%d",
+ inet_ntoa(c->dst.sin_addr), ntohs(c->dst.sin_port));
+ eloop_unregister_sock(c->sd, EVENT_TYPE_WRITE);
+ wpabuf_free(c->req);
+ c->req = NULL;
+
+ c->hread = httpread_create(c->sd, http_client_got_response, c,
+ c->max_response, HTTP_CLIENT_TIMEOUT_SEC);
+ if (c->hread == NULL) {
+ c->cb(c->cb_ctx, c, HTTP_CLIENT_FAILED);
+ return;
+ }
+}
+
+
+struct http_client * http_client_addr(struct sockaddr_in *dst,
+ struct wpabuf *req, size_t max_response,
+ void (*cb)(void *ctx,
+ struct http_client *c,
+ enum http_client_event event),
+ void *cb_ctx)
+{
+ struct http_client *c;
+
+ c = os_zalloc(sizeof(*c));
+ if (c == NULL)
+ return NULL;
+ c->sd = -1;
+ c->dst = *dst;
+ c->max_response = max_response;
+ c->cb = cb;
+ c->cb_ctx = cb_ctx;
+
+ c->sd = socket(AF_INET, SOCK_STREAM, 0);
+ if (c->sd < 0)
+ goto fail;
+
+ if (fcntl(c->sd, F_SETFL, O_NONBLOCK) != 0) {
+ wpa_printf(MSG_DEBUG, "HTTP: fnctl(O_NONBLOCK) failed: %s",
+ strerror(errno));
+ goto fail;
+ }
+
+ if (connect(c->sd, (struct sockaddr *) dst, sizeof(*dst))) {
+ if (errno != EINPROGRESS) {
+ wpa_printf(MSG_DEBUG, "HTTP: Failed to connect: %s",
+ strerror(errno));
+ goto fail;
+ }
+
+ /*
+ * Continue connecting in the background; eloop will call us
+ * once the connection is ready (or failed).
+ */
+ }
+
+ if (eloop_register_sock(c->sd, EVENT_TYPE_WRITE, http_client_tx_ready,
+ c, NULL) ||
+ eloop_register_timeout(HTTP_CLIENT_TIMEOUT_SEC, 0,
+ http_client_timeout, c, NULL))
+ goto fail;
+
+ c->req = req;
+
+ return c;
+
+fail:
+ http_client_free(c);
+ return NULL;
+}
+
+
+char * http_client_url_parse(const char *url, struct sockaddr_in *dst,
+ char **ret_path)
+{
+ char *u, *addr, *port, *path;
+
+ u = os_strdup(url);
+ if (u == NULL)
+ return NULL;
+
+ os_memset(dst, 0, sizeof(*dst));
+ dst->sin_family = AF_INET;
+ addr = u + 7;
+ path = os_strchr(addr, '/');
+ port = os_strchr(addr, ':');
+ if (path == NULL) {
+ path = "/";
+ } else {
+ *path = '\0'; /* temporary nul termination for address */
+ if (port > path)
+ port = NULL;
+ }
+ if (port)
+ *port++ = '\0';
+
+ if (inet_aton(addr, &dst->sin_addr) == 0) {
+ /* TODO: name lookup */
+ wpa_printf(MSG_DEBUG, "HTTP: Unsupported address in URL '%s' "
+ "(addr='%s' port='%s')",
+ url, addr, port);
+ os_free(u);
+ return NULL;
+ }
+
+ if (port)
+ dst->sin_port = htons(atoi(port));
+ else
+ dst->sin_port = htons(80);
+
+ if (*path == '\0') {
+ /* remove temporary nul termination for address */
+ *path = '/';
+ }
+
+ *ret_path = path;
+
+ return u;
+}
+
+
+struct http_client * http_client_url(const char *url,
+ struct wpabuf *req, size_t max_response,
+ void (*cb)(void *ctx,
+ struct http_client *c,
+ enum http_client_event event),
+ void *cb_ctx)
+{
+ struct sockaddr_in dst;
+ struct http_client *c;
+ char *u, *path;
+ struct wpabuf *req_buf = NULL;
+
+ if (os_strncmp(url, "http://", 7) != 0)
+ return NULL;
+ u = http_client_url_parse(url, &dst, &path);
+ if (u == NULL)
+ return NULL;
+
+ if (req == NULL) {
+ req_buf = wpabuf_alloc(os_strlen(url) + 1000);
+ if (req_buf == NULL) {
+ os_free(u);
+ return NULL;
+ }
+ req = req_buf;
+ wpabuf_printf(req,
+ "GET %s HTTP/1.1\r\n"
+ "Cache-Control: no-cache\r\n"
+ "Pragma: no-cache\r\n"
+ "Accept: text/xml, application/xml\r\n"
+ "User-Agent: wpa_supplicant\r\n"
+ "Host: %s:%d\r\n"
+ "\r\n",
+ path, inet_ntoa(dst.sin_addr),
+ ntohs(dst.sin_port));
+ }
+ os_free(u);
+
+ c = http_client_addr(&dst, req, max_response, cb, cb_ctx);
+ if (c == NULL) {
+ wpabuf_free(req_buf);
+ return NULL;
+ }
+
+ return c;
+}
+
+
+void http_client_free(struct http_client *c)
+{
+ if (c == NULL)
+ return;
+ httpread_destroy(c->hread);
+ wpabuf_free(c->req);
+ if (c->sd >= 0) {
+ eloop_unregister_sock(c->sd, EVENT_TYPE_WRITE);
+ close(c->sd);
+ }
+ eloop_cancel_timeout(http_client_timeout, c, NULL);
+ os_free(c);
+}
+
+
+struct wpabuf * http_client_get_body(struct http_client *c)
+{
+ if (c->hread == NULL)
+ return NULL;
+ wpabuf_set(&c->body, httpread_data_get(c->hread),
+ httpread_length_get(c->hread));
+ return &c->body;
+}
+
+
+char * http_client_get_hdr_line(struct http_client *c, const char *tag)
+{
+ if (c->hread == NULL)
+ return NULL;
+ return httpread_hdr_line_get(c->hread, tag);
+}
+
+
+char * http_link_update(char *url, const char *base)
+{
+ char *n;
+ size_t len;
+ const char *pos;
+
+ /* RFC 2396, Chapter 5.2 */
+ /* TODO: consider adding all cases described in RFC 2396 */
+
+ if (url == NULL)
+ return NULL;
+
+ if (os_strncmp(url, "http://", 7) == 0)
+ return url; /* absolute link */
+
+ if (os_strncmp(base, "http://", 7) != 0)
+ return url; /* unable to handle base URL */
+
+ len = os_strlen(url) + 1 + os_strlen(base) + 1;
+ n = os_malloc(len);
+ if (n == NULL)
+ return url; /* failed */
+
+ if (url[0] == '/') {
+ pos = os_strchr(base + 7, '/');
+ if (pos == NULL) {
+ os_snprintf(n, len, "%s%s", base, url);
+ } else {
+ os_memcpy(n, base, pos - base);
+ os_memcpy(n + (pos - base), url, os_strlen(url) + 1);
+ }
+ } else {
+ pos = os_strrchr(base + 7, '/');
+ if (pos == NULL) {
+ os_snprintf(n, len, "%s/%s", base, url);
+ } else {
+ os_memcpy(n, base, pos - base + 1);
+ os_memcpy(n + (pos - base) + 1, url, os_strlen(url) +
+ 1);
+ }
+ }
+
+ os_free(url);
+
+ return n;
+}
diff --git a/freebsd/contrib/wpa/src/wps/http_client.h b/freebsd/contrib/wpa/src/wps/http_client.h
new file mode 100644
index 00000000..ddee2adb
--- /dev/null
+++ b/freebsd/contrib/wpa/src/wps/http_client.h
@@ -0,0 +1,40 @@
+/*
+ * http_client - HTTP client
+ * 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.
+ */
+
+#ifndef HTTP_CLIENT_H
+#define HTTP_CLIENT_H
+
+struct http_client;
+
+enum http_client_event {
+ HTTP_CLIENT_FAILED,
+ HTTP_CLIENT_TIMEOUT,
+ HTTP_CLIENT_OK,
+ HTTP_CLIENT_INVALID_REPLY,
+};
+
+char * http_client_url_parse(const char *url, struct sockaddr_in *dst,
+ char **path);
+struct http_client * http_client_addr(struct sockaddr_in *dst,
+ struct wpabuf *req, size_t max_response,
+ void (*cb)(void *ctx,
+ struct http_client *c,
+ enum http_client_event event),
+ void *cb_ctx);
+struct http_client * http_client_url(const char *url,
+ struct wpabuf *req, size_t max_response,
+ void (*cb)(void *ctx,
+ struct http_client *c,
+ enum http_client_event event),
+ void *cb_ctx);
+void http_client_free(struct http_client *c);
+struct wpabuf * http_client_get_body(struct http_client *c);
+char * http_client_get_hdr_line(struct http_client *c, const char *tag);
+char * http_link_update(char *url, const char *base);
+
+#endif /* HTTP_CLIENT_H */
diff --git a/freebsd/contrib/wpa/src/wps/http_server.c b/freebsd/contrib/wpa/src/wps/http_server.c
new file mode 100644
index 00000000..edef1b2c
--- /dev/null
+++ b/freebsd/contrib/wpa/src/wps/http_server.c
@@ -0,0 +1,316 @@
+#include <machine/rtems-bsd-user-space.h>
+
+/*
+ * http_server - HTTP server
+ * 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 "includes.h"
+#include <fcntl.h>
+
+#include "common.h"
+#include "eloop.h"
+#include "httpread.h"
+#include "http_server.h"
+
+#define HTTP_SERVER_TIMEOUT 30
+#define HTTP_SERVER_MAX_REQ_LEN 8000
+#define HTTP_SERVER_MAX_CONNECTIONS 10
+
+struct http_request {
+ struct http_request *next;
+ struct http_server *srv;
+ int fd;
+ struct sockaddr_in cli;
+ struct httpread *hread;
+};
+
+struct http_server {
+ void (*cb)(void *ctx, struct http_request *req);
+ void *cb_ctx;
+
+ int fd;
+ int port;
+
+ struct http_request *requests;
+ unsigned int request_count;
+};
+
+
+static void http_request_cb(struct httpread *handle, void *cookie,
+ enum httpread_event en)
+{
+ struct http_request *req = cookie;
+ struct http_server *srv = req->srv;
+
+ if (en == HTTPREAD_EVENT_FILE_READY) {
+ wpa_printf(MSG_DEBUG, "HTTP: Request from %s:%d received",
+ inet_ntoa(req->cli.sin_addr),
+ ntohs(req->cli.sin_port));
+ srv->cb(srv->cb_ctx, req);
+ return;
+ }
+ wpa_printf(MSG_DEBUG, "HTTP: Request from %s:%d could not be received "
+ "completely", inet_ntoa(req->cli.sin_addr),
+ ntohs(req->cli.sin_port));
+ http_request_deinit(req);
+}
+
+
+static struct http_request * http_request_init(struct http_server *srv, int fd,
+ struct sockaddr_in *cli)
+{
+ struct http_request *req;
+
+ if (srv->request_count >= HTTP_SERVER_MAX_CONNECTIONS) {
+ wpa_printf(MSG_DEBUG, "HTTP: Too many concurrent requests");
+ return NULL;
+ }
+
+ req = os_zalloc(sizeof(*req));
+ if (req == NULL)
+ return NULL;
+
+ req->srv = srv;
+ req->fd = fd;
+ req->cli = *cli;
+
+ req->hread = httpread_create(req->fd, http_request_cb, req,
+ HTTP_SERVER_MAX_REQ_LEN,
+ HTTP_SERVER_TIMEOUT);
+ if (req->hread == NULL) {
+ http_request_deinit(req);
+ return NULL;
+ }
+
+ return req;
+}
+
+
+void http_request_deinit(struct http_request *req)
+{
+ struct http_request *r, *p;
+ struct http_server *srv;
+
+ if (req == NULL)
+ return;
+
+ srv = req->srv;
+ p = NULL;
+ r = srv->requests;
+ while (r) {
+ if (r == req) {
+ if (p)
+ p->next = r->next;
+ else
+ srv->requests = r->next;
+ srv->request_count--;
+ break;
+ }
+ p = r;
+ r = r->next;
+ }
+
+ httpread_destroy(req->hread);
+ close(req->fd);
+ os_free(req);
+}
+
+
+static void http_request_free_all(struct http_request *req)
+{
+ struct http_request *prev;
+ while (req) {
+ prev = req;
+ req = req->next;
+ http_request_deinit(prev);
+ }
+}
+
+
+void http_request_send(struct http_request *req, struct wpabuf *resp)
+{
+ int res;
+
+ wpa_printf(MSG_DEBUG, "HTTP: Send %lu byte response to %s:%d",
+ (unsigned long) wpabuf_len(resp),
+ inet_ntoa(req->cli.sin_addr),
+ ntohs(req->cli.sin_port));
+
+ res = send(req->fd, wpabuf_head(resp), wpabuf_len(resp), 0);
+ if (res < 0) {
+ wpa_printf(MSG_DEBUG, "HTTP: Send failed: %s",
+ strerror(errno));
+ } else if ((size_t) res < wpabuf_len(resp)) {
+ wpa_printf(MSG_DEBUG, "HTTP: Sent only %d of %lu bytes",
+ res, (unsigned long) wpabuf_len(resp));
+ /* TODO: add eloop handler for sending rest of the data */
+ }
+
+ wpabuf_free(resp);
+}
+
+
+void http_request_send_and_deinit(struct http_request *req,
+ struct wpabuf *resp)
+{
+ http_request_send(req, resp);
+ http_request_deinit(req);
+}
+
+
+enum httpread_hdr_type http_request_get_type(struct http_request *req)
+{
+ return httpread_hdr_type_get(req->hread);
+}
+
+
+char * http_request_get_uri(struct http_request *req)
+{
+ return httpread_uri_get(req->hread);
+}
+
+
+char * http_request_get_hdr(struct http_request *req)
+{
+ return httpread_hdr_get(req->hread);
+}
+
+
+char * http_request_get_data(struct http_request *req)
+{
+ return httpread_data_get(req->hread);
+}
+
+
+char * http_request_get_hdr_line(struct http_request *req, const char *tag)
+{
+ return httpread_hdr_line_get(req->hread, tag);
+}
+
+
+struct sockaddr_in * http_request_get_cli_addr(struct http_request *req)
+{
+ return &req->cli;
+}
+
+
+static void http_server_cb(int sd, void *eloop_ctx, void *sock_ctx)
+{
+ struct sockaddr_in addr;
+ socklen_t addr_len = sizeof(addr);
+ struct http_server *srv = eloop_ctx;
+ int conn;
+ struct http_request *req;
+
+ conn = accept(srv->fd, (struct sockaddr *) &addr, &addr_len);
+ if (conn < 0) {
+ wpa_printf(MSG_DEBUG, "HTTP: Failed to accept new connection: "
+ "%s", strerror(errno));
+ return;
+ }
+ wpa_printf(MSG_DEBUG, "HTTP: Connection from %s:%d",
+ inet_ntoa(addr.sin_addr), ntohs(addr.sin_port));
+
+ req = http_request_init(srv, conn, &addr);
+ if (req == NULL) {
+ close(conn);
+ return;
+ }
+
+ req->next = srv->requests;
+ srv->requests = req;
+ srv->request_count++;
+}
+
+
+struct http_server * http_server_init(struct in_addr *addr, int port,
+ void (*cb)(void *ctx,
+ struct http_request *req),
+ void *cb_ctx)
+{
+ struct sockaddr_in sin;
+ struct http_server *srv;
+ int on = 1;
+
+ srv = os_zalloc(sizeof(*srv));
+ if (srv == NULL)
+ return NULL;
+ srv->cb = cb;
+ srv->cb_ctx = cb_ctx;
+
+ srv->fd = socket(AF_INET, SOCK_STREAM, 0);
+ if (srv->fd < 0)
+ goto fail;
+
+ if (setsockopt(srv->fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0)
+ {
+ wpa_printf(MSG_DEBUG,
+ "HTTP: setsockopt(SO_REUSEADDR) failed: %s",
+ strerror(errno));
+ /* try to continue anyway */
+ }
+
+ if (fcntl(srv->fd, F_SETFL, O_NONBLOCK) < 0)
+ goto fail;
+ if (port < 0)
+ srv->port = 49152;
+ else
+ srv->port = port;
+
+ os_memset(&sin, 0, sizeof(sin));
+ sin.sin_family = AF_INET;
+ sin.sin_addr.s_addr = addr->s_addr;
+
+ for (;;) {
+ sin.sin_port = htons(srv->port);
+ if (bind(srv->fd, (struct sockaddr *) &sin, sizeof(sin)) == 0)
+ break;
+ if (errno == EADDRINUSE) {
+ /* search for unused port */
+ if (++srv->port == 65535 || port >= 0)
+ goto fail;
+ continue;
+ }
+ wpa_printf(MSG_DEBUG, "HTTP: Failed to bind server port %d: "
+ "%s", srv->port, strerror(errno));
+ goto fail;
+ }
+ if (listen(srv->fd, 10 /* max backlog */) < 0 ||
+ fcntl(srv->fd, F_SETFL, O_NONBLOCK) < 0 ||
+ eloop_register_sock(srv->fd, EVENT_TYPE_READ, http_server_cb,
+ srv, NULL))
+ goto fail;
+
+ wpa_printf(MSG_DEBUG, "HTTP: Started server on %s:%d",
+ inet_ntoa(*addr), srv->port);
+
+ return srv;
+
+fail:
+ http_server_deinit(srv);
+ return NULL;
+}
+
+
+void http_server_deinit(struct http_server *srv)
+{
+ if (srv == NULL)
+ return;
+ if (srv->fd >= 0) {
+ eloop_unregister_sock(srv->fd, EVENT_TYPE_READ);
+ close(srv->fd);
+ }
+ http_request_free_all(srv->requests);
+
+ os_free(srv);
+}
+
+
+int http_server_get_port(struct http_server *srv)
+{
+ return srv->port;
+}
diff --git a/freebsd/contrib/wpa/src/wps/http_server.h b/freebsd/contrib/wpa/src/wps/http_server.h
new file mode 100644
index 00000000..4b2b749f
--- /dev/null
+++ b/freebsd/contrib/wpa/src/wps/http_server.h
@@ -0,0 +1,33 @@
+/*
+ * http_server - HTTP server
+ * 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.
+ */
+
+#ifndef HTTP_SERVER_H
+#define HTTP_SERVER_H
+
+struct http_server;
+struct http_request;
+
+void http_request_deinit(struct http_request *req);
+void http_request_send(struct http_request *req, struct wpabuf *resp);
+void http_request_send_and_deinit(struct http_request *req,
+ struct wpabuf *resp);
+enum httpread_hdr_type http_request_get_type(struct http_request *req);
+char * http_request_get_uri(struct http_request *req);
+char * http_request_get_hdr(struct http_request *req);
+char * http_request_get_data(struct http_request *req);
+char * http_request_get_hdr_line(struct http_request *req, const char *tag);
+struct sockaddr_in * http_request_get_cli_addr(struct http_request *req);
+
+struct http_server * http_server_init(struct in_addr *addr, int port,
+ void (*cb)(void *ctx,
+ struct http_request *req),
+ void *cb_ctx);
+void http_server_deinit(struct http_server *srv);
+int http_server_get_port(struct http_server *srv);
+
+#endif /* HTTP_SERVER_H */
diff --git a/freebsd/contrib/wpa/src/wps/httpread.c b/freebsd/contrib/wpa/src/wps/httpread.c
new file mode 100644
index 00000000..74a1a4f5
--- /dev/null
+++ b/freebsd/contrib/wpa/src/wps/httpread.c
@@ -0,0 +1,850 @@
+#include <machine/rtems-bsd-user-space.h>
+
+/*
+ * httpread - Manage reading file(s) from HTTP/TCP socket
+ * Author: Ted Merrill
+ * Copyright 2008 Atheros Communications
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ *
+ * The files are buffered via internal callbacks from eloop, then presented to
+ * an application callback routine when completely read into memory. May also
+ * be used if no file is expected but just to get the header, including HTTP
+ * replies (e.g. HTTP/1.1 200 OK etc.).
+ *
+ * This does not attempt to be an optimally efficient implementation, but does
+ * attempt to be of reasonably small size and memory consumption; assuming that
+ * only small files are to be read. A maximum file size is provided by
+ * application and enforced.
+ *
+ * It is assumed that the application does not expect any of the following:
+ * -- transfer encoding other than chunked
+ * -- trailer fields
+ * It is assumed that, even if the other side requested that the connection be
+ * kept open, that we will close it (thus HTTP messages sent by application
+ * should have the connection closed field); this is allowed by HTTP/1.1 and
+ * simplifies things for us.
+ *
+ * Other limitations:
+ * -- HTTP header may not exceed a hard-coded size.
+ *
+ * Notes:
+ * This code would be massively simpler without some of the new features of
+ * HTTP/1.1, especially chunked data.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "eloop.h"
+#include "httpread.h"
+
+
+/* Tunable parameters */
+#define HTTPREAD_READBUF_SIZE 1024 /* read in chunks of this size */
+#define HTTPREAD_HEADER_MAX_SIZE 4096 /* max allowed for headers */
+#define HTTPREAD_BODYBUF_DELTA 4096 /* increase allocation by this */
+
+
+/* control instance -- actual definition (opaque to application)
+ */
+struct httpread {
+ /* information from creation */
+ int sd; /* descriptor of TCP socket to read from */
+ void (*cb)(struct httpread *handle, void *cookie,
+ enum httpread_event e); /* call on event */
+ void *cookie; /* pass to callback */
+ int max_bytes; /* maximum file size else abort it */
+ int timeout_seconds; /* 0 or total duration timeout period */
+
+ /* dynamically used information follows */
+
+ int got_hdr; /* nonzero when header is finalized */
+ char hdr[HTTPREAD_HEADER_MAX_SIZE+1]; /* headers stored here */
+ int hdr_nbytes;
+
+ enum httpread_hdr_type hdr_type;
+ int version; /* 1 if we've seen 1.1 */
+ int reply_code; /* for type REPLY, e.g. 200 for HTTP/1.1 200 OK */
+ int got_content_length; /* true if we know content length for sure */
+ int content_length; /* body length, iff got_content_length */
+ int chunked; /* nonzero for chunked data */
+ char *uri;
+
+ int got_body; /* nonzero when body is finalized */
+ char *body;
+ int body_nbytes;
+ int body_alloc_nbytes; /* amount allocated */
+
+ int got_file; /* here when we are done */
+
+ /* The following apply if data is chunked: */
+ int in_chunk_data; /* 0=in/at header, 1=in the data or tail*/
+ int chunk_start; /* offset in body of chunk hdr or data */
+ int chunk_size; /* data of chunk (not hdr or ending CRLF)*/
+ int in_trailer; /* in header fields after data (chunked only)*/
+ enum trailer_state {
+ trailer_line_begin = 0,
+ trailer_empty_cr, /* empty line + CR */
+ trailer_nonempty,
+ trailer_nonempty_cr,
+ } trailer_state;
+};
+
+
+/* Check words for equality, where words consist of graphical characters
+ * delimited by whitespace
+ * Returns nonzero if "equal" doing case insensitive comparison.
+ */
+static int word_eq(char *s1, char *s2)
+{
+ int c1;
+ int c2;
+ int end1 = 0;
+ int end2 = 0;
+ for (;;) {
+ c1 = *s1++;
+ c2 = *s2++;
+ if (isalpha(c1) && isupper(c1))
+ c1 = tolower(c1);
+ if (isalpha(c2) && isupper(c2))
+ c2 = tolower(c2);
+ end1 = !isgraph(c1);
+ end2 = !isgraph(c2);
+ if (end1 || end2 || c1 != c2)
+ break;
+ }
+ return end1 && end2; /* reached end of both words? */
+}
+
+
+static void httpread_timeout_handler(void *eloop_data, void *user_ctx);
+
+/* httpread_destroy -- if h is non-NULL, clean up
+ * This must eventually be called by the application following
+ * call of the application's callback and may be called
+ * earlier if desired.
+ */
+void httpread_destroy(struct httpread *h)
+{
+ wpa_printf(MSG_DEBUG, "httpread_destroy(%p)", h);
+ if (!h)
+ return;
+
+ eloop_cancel_timeout(httpread_timeout_handler, NULL, h);
+ eloop_unregister_sock(h->sd, EVENT_TYPE_READ);
+ os_free(h->body);
+ os_free(h->uri);
+ os_memset(h, 0, sizeof(*h)); /* aid debugging */
+ h->sd = -1; /* aid debugging */
+ os_free(h);
+}
+
+
+/* httpread_timeout_handler -- called on excessive total duration
+ */
+static void httpread_timeout_handler(void *eloop_data, void *user_ctx)
+{
+ struct httpread *h = user_ctx;
+ wpa_printf(MSG_DEBUG, "httpread timeout (%p)", h);
+ (*h->cb)(h, h->cookie, HTTPREAD_EVENT_TIMEOUT);
+}
+
+
+/* Analyze options only so far as is needed to correctly obtain the file.
+ * The application can look at the raw header to find other options.
+ */
+static int httpread_hdr_option_analyze(
+ struct httpread *h,
+ char *hbp /* pointer to current line in header buffer */
+ )
+{
+ if (word_eq(hbp, "CONTENT-LENGTH:")) {
+ while (isgraph(*hbp))
+ hbp++;
+ while (*hbp == ' ' || *hbp == '\t')
+ hbp++;
+ if (!isdigit(*hbp))
+ return -1;
+ h->content_length = atol(hbp);
+ if (h->content_length < 0 || h->content_length > h->max_bytes) {
+ wpa_printf(MSG_DEBUG,
+ "httpread: Unacceptable Content-Length %d",
+ h->content_length);
+ return -1;
+ }
+ h->got_content_length = 1;
+ return 0;
+ }
+ if (word_eq(hbp, "TRANSFER_ENCODING:") ||
+ word_eq(hbp, "TRANSFER-ENCODING:")) {
+ while (isgraph(*hbp))
+ hbp++;
+ while (*hbp == ' ' || *hbp == '\t')
+ hbp++;
+ /* There should (?) be no encodings of interest
+ * other than chunked...
+ */
+ if (word_eq(hbp, "CHUNKED")) {
+ h->chunked = 1;
+ h->in_chunk_data = 0;
+ /* ignore possible ;<parameters> */
+ }
+ return 0;
+ }
+ /* skip anything we don't know, which is a lot */
+ return 0;
+}
+
+
+static int httpread_hdr_analyze(struct httpread *h)
+{
+ char *hbp = h->hdr; /* pointer into h->hdr */
+ int standard_first_line = 1;
+
+ /* First line is special */
+ h->hdr_type = HTTPREAD_HDR_TYPE_UNKNOWN;
+ if (!isgraph(*hbp))
+ goto bad;
+ if (os_strncmp(hbp, "HTTP/", 5) == 0) {
+ h->hdr_type = HTTPREAD_HDR_TYPE_REPLY;
+ standard_first_line = 0;
+ hbp += 5;
+ if (hbp[0] == '1' && hbp[1] == '.' &&
+ isdigit(hbp[2]) && hbp[2] != '0')
+ h->version = 1;
+ while (isgraph(*hbp))
+ hbp++;
+ while (*hbp == ' ' || *hbp == '\t')
+ hbp++;
+ if (!isdigit(*hbp))
+ goto bad;
+ h->reply_code = atol(hbp);
+ } else if (word_eq(hbp, "GET"))
+ h->hdr_type = HTTPREAD_HDR_TYPE_GET;
+ else if (word_eq(hbp, "HEAD"))
+ h->hdr_type = HTTPREAD_HDR_TYPE_HEAD;
+ else if (word_eq(hbp, "POST"))
+ h->hdr_type = HTTPREAD_HDR_TYPE_POST;
+ else if (word_eq(hbp, "PUT"))
+ h->hdr_type = HTTPREAD_HDR_TYPE_PUT;
+ else if (word_eq(hbp, "DELETE"))
+ h->hdr_type = HTTPREAD_HDR_TYPE_DELETE;
+ else if (word_eq(hbp, "TRACE"))
+ h->hdr_type = HTTPREAD_HDR_TYPE_TRACE;
+ else if (word_eq(hbp, "CONNECT"))
+ h->hdr_type = HTTPREAD_HDR_TYPE_CONNECT;
+ else if (word_eq(hbp, "NOTIFY"))
+ h->hdr_type = HTTPREAD_HDR_TYPE_NOTIFY;
+ else if (word_eq(hbp, "M-SEARCH"))
+ h->hdr_type = HTTPREAD_HDR_TYPE_M_SEARCH;
+ else if (word_eq(hbp, "M-POST"))
+ h->hdr_type = HTTPREAD_HDR_TYPE_M_POST;
+ else if (word_eq(hbp, "SUBSCRIBE"))
+ h->hdr_type = HTTPREAD_HDR_TYPE_SUBSCRIBE;
+ else if (word_eq(hbp, "UNSUBSCRIBE"))
+ h->hdr_type = HTTPREAD_HDR_TYPE_UNSUBSCRIBE;
+ else {
+ }
+
+ if (standard_first_line) {
+ char *rawuri;
+ char *uri;
+ /* skip type */
+ while (isgraph(*hbp))
+ hbp++;
+ while (*hbp == ' ' || *hbp == '\t')
+ hbp++;
+ /* parse uri.
+ * Find length, allocate memory for translated
+ * copy, then translate by changing %<hex><hex>
+ * into represented value.
+ */
+ rawuri = hbp;
+ while (isgraph(*hbp))
+ hbp++;
+ h->uri = os_malloc((hbp - rawuri) + 1);
+ if (h->uri == NULL)
+ goto bad;
+ uri = h->uri;
+ while (rawuri < hbp) {
+ int c = *rawuri;
+ if (c == '%' &&
+ isxdigit(rawuri[1]) && isxdigit(rawuri[2])) {
+ *uri++ = hex2byte(rawuri + 1);
+ rawuri += 3;
+ } else {
+ *uri++ = c;
+ rawuri++;
+ }
+ }
+ *uri = 0; /* null terminate */
+ while (*hbp == ' ' || *hbp == '\t')
+ hbp++;
+ /* get version */
+ if (0 == strncmp(hbp, "HTTP/", 5)) {
+ hbp += 5;
+ if (hbp[0] == '1' && hbp[1] == '.' &&
+ isdigit(hbp[2]) && hbp[2] != '0')
+ h->version = 1;
+ }
+ }
+ /* skip rest of line */
+ while (*hbp)
+ if (*hbp++ == '\n')
+ break;
+
+ /* Remainder of lines are options, in any order;
+ * or empty line to terminate
+ */
+ for (;;) {
+ /* Empty line to terminate */
+ if (hbp[0] == '\n' ||
+ (hbp[0] == '\r' && hbp[1] == '\n'))
+ break;
+ if (!isgraph(*hbp))
+ goto bad;
+ if (httpread_hdr_option_analyze(h, hbp))
+ goto bad;
+ /* skip line */
+ while (*hbp)
+ if (*hbp++ == '\n')
+ break;
+ }
+
+ /* chunked overrides content-length always */
+ if (h->chunked)
+ h->got_content_length = 0;
+
+ /* For some types, we should not try to read a body
+ * This is in addition to the application determining
+ * that we should not read a body.
+ */
+ switch (h->hdr_type) {
+ case HTTPREAD_HDR_TYPE_REPLY:
+ /* Some codes can have a body and some not.
+ * For now, just assume that any other than 200
+ * do not...
+ */
+ if (h->reply_code != 200)
+ h->max_bytes = 0;
+ break;
+ case HTTPREAD_HDR_TYPE_GET:
+ case HTTPREAD_HDR_TYPE_HEAD:
+ /* in practice it appears that it is assumed
+ * that GETs have a body length of 0... ?
+ */
+ if (h->chunked == 0 && h->got_content_length == 0)
+ h->max_bytes = 0;
+ break;
+ case HTTPREAD_HDR_TYPE_POST:
+ case HTTPREAD_HDR_TYPE_PUT:
+ case HTTPREAD_HDR_TYPE_DELETE:
+ case HTTPREAD_HDR_TYPE_TRACE:
+ case HTTPREAD_HDR_TYPE_CONNECT:
+ case HTTPREAD_HDR_TYPE_NOTIFY:
+ case HTTPREAD_HDR_TYPE_M_SEARCH:
+ case HTTPREAD_HDR_TYPE_M_POST:
+ case HTTPREAD_HDR_TYPE_SUBSCRIBE:
+ case HTTPREAD_HDR_TYPE_UNSUBSCRIBE:
+ default:
+ break;
+ }
+
+ return 0;
+
+bad:
+ /* Error */
+ return -1;
+}
+
+
+/* httpread_read_handler -- called when socket ready to read
+ *
+ * Note: any extra data we read past end of transmitted file is ignored;
+ * if we were to support keeping connections open for multiple files then
+ * this would have to be addressed.
+ */
+static void httpread_read_handler(int sd, void *eloop_ctx, void *sock_ctx)
+{
+ struct httpread *h = sock_ctx;
+ int nread;
+ char *rbp; /* pointer into read buffer */
+ char *hbp; /* pointer into header buffer */
+ char *bbp; /* pointer into body buffer */
+ char readbuf[HTTPREAD_READBUF_SIZE]; /* temp use to read into */
+
+ /* read some at a time, then search for the interal
+ * boundaries between header and data and etc.
+ */
+ wpa_printf(MSG_DEBUG, "httpread: Trying to read more data(%p)", h);
+ nread = read(h->sd, readbuf, sizeof(readbuf));
+ if (nread < 0) {
+ wpa_printf(MSG_DEBUG, "httpread failed: %s", strerror(errno));
+ goto bad;
+ }
+ wpa_hexdump_ascii(MSG_MSGDUMP, "httpread - read", readbuf, nread);
+ if (nread == 0) {
+ /* end of transmission... this may be normal
+ * or may be an error... in some cases we can't
+ * tell which so we must assume it is normal then.
+ */
+ if (!h->got_hdr) {
+ /* Must at least have completed header */
+ wpa_printf(MSG_DEBUG, "httpread premature eof(%p)", h);
+ goto bad;
+ }
+ if (h->chunked || h->got_content_length) {
+ /* Premature EOF; e.g. dropped connection */
+ wpa_printf(MSG_DEBUG,
+ "httpread premature eof(%p) %d/%d",
+ h, h->body_nbytes,
+ h->content_length);
+ goto bad;
+ }
+ /* No explicit length, hopefully we have all the data
+ * although dropped connections can cause false
+ * end
+ */
+ wpa_printf(MSG_DEBUG, "httpread ok eof(%p)", h);
+ h->got_body = 1;
+ goto got_file;
+ }
+ rbp = readbuf;
+
+ /* Header consists of text lines (terminated by both CR and LF)
+ * and an empty line (CR LF only).
+ */
+ if (!h->got_hdr) {
+ hbp = h->hdr + h->hdr_nbytes;
+ /* add to headers until:
+ * -- we run out of data in read buffer
+ * -- or, we run out of header buffer room
+ * -- or, we get double CRLF in headers
+ */
+ for (;;) {
+ if (nread == 0)
+ goto get_more;
+ if (h->hdr_nbytes == HTTPREAD_HEADER_MAX_SIZE) {
+ wpa_printf(MSG_DEBUG,
+ "httpread: Too long header");
+ goto bad;
+ }
+ *hbp++ = *rbp++;
+ nread--;
+ h->hdr_nbytes++;
+ if (h->hdr_nbytes >= 4 &&
+ hbp[-1] == '\n' &&
+ hbp[-2] == '\r' &&
+ hbp[-3] == '\n' &&
+ hbp[-4] == '\r' ) {
+ h->got_hdr = 1;
+ *hbp = 0; /* null terminate */
+ break;
+ }
+ }
+ /* here we've just finished reading the header */
+ if (httpread_hdr_analyze(h)) {
+ wpa_printf(MSG_DEBUG, "httpread bad hdr(%p)", h);
+ goto bad;
+ }
+ if (h->max_bytes == 0) {
+ wpa_printf(MSG_DEBUG, "httpread no body hdr end(%p)",
+ h);
+ goto got_file;
+ }
+ if (h->got_content_length && h->content_length == 0) {
+ wpa_printf(MSG_DEBUG,
+ "httpread zero content length(%p)", h);
+ goto got_file;
+ }
+ }
+
+ /* Certain types of requests never have data and so
+ * must be specially recognized.
+ */
+ if (!os_strncasecmp(h->hdr, "SUBSCRIBE", 9) ||
+ !os_strncasecmp(h->hdr, "UNSUBSCRIBE", 11) ||
+ !os_strncasecmp(h->hdr, "HEAD", 4) ||
+ !os_strncasecmp(h->hdr, "GET", 3)) {
+ if (!h->got_body) {
+ wpa_printf(MSG_DEBUG, "httpread NO BODY for sp. type");
+ }
+ h->got_body = 1;
+ goto got_file;
+ }
+
+ /* Data can be just plain binary data, or if "chunked"
+ * consists of chunks each with a header, ending with
+ * an ending header.
+ */
+ if (nread == 0)
+ goto get_more;
+ if (!h->got_body) {
+ /* Here to get (more of) body */
+ /* ensure we have enough room for worst case for body
+ * plus a null termination character
+ */
+ if (h->body_alloc_nbytes < (h->body_nbytes + nread + 1)) {
+ char *new_body;
+ int new_alloc_nbytes;
+
+ if (h->body_nbytes >= h->max_bytes) {
+ wpa_printf(MSG_DEBUG,
+ "httpread: body_nbytes=%d >= max_bytes=%d",
+ h->body_nbytes, h->max_bytes);
+ goto bad;
+ }
+ new_alloc_nbytes = h->body_alloc_nbytes +
+ HTTPREAD_BODYBUF_DELTA;
+ /* For content-length case, the first time
+ * through we allocate the whole amount
+ * we need.
+ */
+ if (h->got_content_length &&
+ new_alloc_nbytes < (h->content_length + 1))
+ new_alloc_nbytes = h->content_length + 1;
+ if (new_alloc_nbytes < h->body_alloc_nbytes ||
+ new_alloc_nbytes > h->max_bytes +
+ HTTPREAD_BODYBUF_DELTA) {
+ wpa_printf(MSG_DEBUG,
+ "httpread: Unacceptable body length %d (body_alloc_nbytes=%u max_bytes=%u)",
+ new_alloc_nbytes,
+ h->body_alloc_nbytes,
+ h->max_bytes);
+ goto bad;
+ }
+ if ((new_body = os_realloc(h->body, new_alloc_nbytes))
+ == NULL) {
+ wpa_printf(MSG_DEBUG,
+ "httpread: Failed to reallocate buffer (len=%d)",
+ new_alloc_nbytes);
+ goto bad;
+ }
+
+ h->body = new_body;
+ h->body_alloc_nbytes = new_alloc_nbytes;
+ }
+ /* add bytes */
+ bbp = h->body + h->body_nbytes;
+ for (;;) {
+ int ncopy;
+ /* See if we need to stop */
+ if (h->chunked && h->in_chunk_data == 0) {
+ /* in chunk header */
+ char *cbp = h->body + h->chunk_start;
+ if (bbp-cbp >= 2 && bbp[-2] == '\r' &&
+ bbp[-1] == '\n') {
+ /* end of chunk hdr line */
+ /* hdr line consists solely
+ * of a hex numeral and CFLF
+ */
+ if (!isxdigit(*cbp)) {
+ wpa_printf(MSG_DEBUG,
+ "httpread: Unexpected chunk header value (not a hex digit)");
+ goto bad;
+ }
+ h->chunk_size = strtoul(cbp, NULL, 16);
+ if (h->chunk_size < 0 ||
+ h->chunk_size > h->max_bytes) {
+ wpa_printf(MSG_DEBUG,
+ "httpread: Invalid chunk size %d",
+ h->chunk_size);
+ goto bad;
+ }
+ /* throw away chunk header
+ * so we have only real data
+ */
+ h->body_nbytes = h->chunk_start;
+ bbp = cbp;
+ if (h->chunk_size == 0) {
+ /* end of chunking */
+ /* trailer follows */
+ h->in_trailer = 1;
+ wpa_printf(MSG_DEBUG,
+ "httpread end chunks(%p)",
+ h);
+ break;
+ }
+ h->in_chunk_data = 1;
+ /* leave chunk_start alone */
+ }
+ } else if (h->chunked) {
+ /* in chunk data */
+ if ((h->body_nbytes - h->chunk_start) ==
+ (h->chunk_size + 2)) {
+ /* end of chunk reached,
+ * new chunk starts
+ */
+ /* check chunk ended w/ CRLF
+ * which we'll throw away
+ */
+ if (bbp[-1] == '\n' &&
+ bbp[-2] == '\r') {
+ } else {
+ wpa_printf(MSG_DEBUG,
+ "httpread: Invalid chunk end");
+ goto bad;
+ }
+ h->body_nbytes -= 2;
+ bbp -= 2;
+ h->chunk_start = h->body_nbytes;
+ h->in_chunk_data = 0;
+ h->chunk_size = 0; /* just in case */
+ }
+ } else if (h->got_content_length &&
+ h->body_nbytes >= h->content_length) {
+ h->got_body = 1;
+ wpa_printf(MSG_DEBUG,
+ "httpread got content(%p)", h);
+ goto got_file;
+ }
+ if (nread <= 0)
+ break;
+ /* Now transfer. Optimize using memcpy where we can. */
+ if (h->chunked && h->in_chunk_data) {
+ /* copy up to remainder of chunk data
+ * plus the required CR+LF at end
+ */
+ ncopy = (h->chunk_start + h->chunk_size + 2) -
+ h->body_nbytes;
+ } else if (h->chunked) {
+ /*in chunk header -- don't optimize */
+ *bbp++ = *rbp++;
+ nread--;
+ h->body_nbytes++;
+ continue;
+ } else if (h->got_content_length) {
+ ncopy = h->content_length - h->body_nbytes;
+ } else {
+ ncopy = nread;
+ }
+ /* Note: should never be 0 */
+ if (ncopy < 0) {
+ wpa_printf(MSG_DEBUG,
+ "httpread: Invalid ncopy=%d", ncopy);
+ goto bad;
+ }
+ if (ncopy > nread)
+ ncopy = nread;
+ os_memcpy(bbp, rbp, ncopy);
+ bbp += ncopy;
+ h->body_nbytes += ncopy;
+ rbp += ncopy;
+ nread -= ncopy;
+ } /* body copy loop */
+ } /* !got_body */
+ if (h->chunked && h->in_trailer) {
+ /* If "chunked" then there is always a trailer,
+ * consisting of zero or more non-empty lines
+ * ending with CR LF and then an empty line w/ CR LF.
+ * We do NOT support trailers except to skip them --
+ * this is supported (generally) by the http spec.
+ */
+ for (;;) {
+ int c;
+ if (nread <= 0)
+ break;
+ c = *rbp++;
+ nread--;
+ switch (h->trailer_state) {
+ case trailer_line_begin:
+ if (c == '\r')
+ h->trailer_state = trailer_empty_cr;
+ else
+ h->trailer_state = trailer_nonempty;
+ break;
+ case trailer_empty_cr:
+ /* end empty line */
+ if (c == '\n') {
+ h->trailer_state = trailer_line_begin;
+ h->in_trailer = 0;
+ wpa_printf(MSG_DEBUG,
+ "httpread got content(%p)",
+ h);
+ h->got_body = 1;
+ goto got_file;
+ }
+ h->trailer_state = trailer_nonempty;
+ break;
+ case trailer_nonempty:
+ if (c == '\r')
+ h->trailer_state = trailer_nonempty_cr;
+ break;
+ case trailer_nonempty_cr:
+ if (c == '\n')
+ h->trailer_state = trailer_line_begin;
+ else
+ h->trailer_state = trailer_nonempty;
+ break;
+ }
+ }
+ }
+ goto get_more;
+
+bad:
+ /* Error */
+ wpa_printf(MSG_DEBUG, "httpread read/parse failure (%p)", h);
+ (*h->cb)(h, h->cookie, HTTPREAD_EVENT_ERROR);
+ return;
+
+get_more:
+ wpa_printf(MSG_DEBUG, "httpread: get more (%p)", h);
+ return;
+
+got_file:
+ wpa_printf(MSG_DEBUG, "httpread got file %d bytes type %d",
+ h->body_nbytes, h->hdr_type);
+ wpa_hexdump_ascii(MSG_MSGDUMP, "httpread: body",
+ h->body, h->body_nbytes);
+ /* Null terminate for convenience of some applications */
+ if (h->body)
+ h->body[h->body_nbytes] = 0; /* null terminate */
+ h->got_file = 1;
+ /* Assume that we do NOT support keeping connection alive,
+ * and just in case somehow we don't get destroyed right away,
+ * unregister now.
+ */
+ eloop_unregister_sock(h->sd, EVENT_TYPE_READ);
+ /* The application can destroy us whenever they feel like...
+ * cancel timeout.
+ */
+ eloop_cancel_timeout(httpread_timeout_handler, NULL, h);
+ (*h->cb)(h, h->cookie, HTTPREAD_EVENT_FILE_READY);
+}
+
+
+/* httpread_create -- start a new reading session making use of eloop.
+ * The new instance will use the socket descriptor for reading (until
+ * it gets a file and not after) but will not close the socket, even
+ * when the instance is destroyed (the application must do that).
+ * Return NULL on error.
+ *
+ * Provided that httpread_create successfully returns a handle,
+ * the callback fnc is called to handle httpread_event events.
+ * The caller should do destroy on any errors or unknown events.
+ *
+ * Pass max_bytes == 0 to not read body at all (required for e.g.
+ * reply to HEAD request).
+ */
+struct httpread * httpread_create(
+ int sd, /* descriptor of TCP socket to read from */
+ void (*cb)(struct httpread *handle, void *cookie,
+ enum httpread_event e), /* call on event */
+ void *cookie, /* pass to callback */
+ int max_bytes, /* maximum body size else abort it */
+ int timeout_seconds /* 0; or total duration timeout period */
+ )
+{
+ struct httpread *h = NULL;
+
+ h = os_zalloc(sizeof(*h));
+ if (h == NULL)
+ goto fail;
+ h->sd = sd;
+ h->cb = cb;
+ h->cookie = cookie;
+ h->max_bytes = max_bytes;
+ h->timeout_seconds = timeout_seconds;
+
+ if (timeout_seconds > 0 &&
+ eloop_register_timeout(timeout_seconds, 0,
+ httpread_timeout_handler, NULL, h)) {
+ /* No way to recover (from malloc failure) */
+ goto fail;
+ }
+ if (eloop_register_sock(sd, EVENT_TYPE_READ, httpread_read_handler,
+ NULL, h)) {
+ /* No way to recover (from malloc failure) */
+ goto fail;
+ }
+ return h;
+
+fail:
+
+ /* Error */
+ httpread_destroy(h);
+ return NULL;
+}
+
+
+/* httpread_hdr_type_get -- When file is ready, returns header type. */
+enum httpread_hdr_type httpread_hdr_type_get(struct httpread *h)
+{
+ return h->hdr_type;
+}
+
+
+/* httpread_uri_get -- When file is ready, uri_get returns (translated) URI
+ * or possibly NULL (which would be an error).
+ */
+char * httpread_uri_get(struct httpread *h)
+{
+ return h->uri;
+}
+
+
+/* httpread_reply_code_get -- When reply is ready, returns reply code */
+int httpread_reply_code_get(struct httpread *h)
+{
+ return h->reply_code;
+}
+
+
+/* httpread_length_get -- When file is ready, returns file length. */
+int httpread_length_get(struct httpread *h)
+{
+ return h->body_nbytes;
+}
+
+
+/* httpread_data_get -- When file is ready, returns file content
+ * with null byte appened.
+ * Might return NULL in some error condition.
+ */
+void * httpread_data_get(struct httpread *h)
+{
+ return h->body ? h->body : "";
+}
+
+
+/* httpread_hdr_get -- When file is ready, returns header content
+ * with null byte appended.
+ * Might return NULL in some error condition.
+ */
+char * httpread_hdr_get(struct httpread *h)
+{
+ return h->hdr;
+}
+
+
+/* httpread_hdr_line_get -- When file is ready, returns pointer
+ * to line within header content matching the given tag
+ * (after the tag itself and any spaces/tabs).
+ *
+ * The tag should end with a colon for reliable matching.
+ *
+ * If not found, returns NULL;
+ */
+char * httpread_hdr_line_get(struct httpread *h, const char *tag)
+{
+ int tag_len = os_strlen(tag);
+ char *hdr = h->hdr;
+ hdr = os_strchr(hdr, '\n');
+ if (hdr == NULL)
+ return NULL;
+ hdr++;
+ for (;;) {
+ if (!os_strncasecmp(hdr, tag, tag_len)) {
+ hdr += tag_len;
+ while (*hdr == ' ' || *hdr == '\t')
+ hdr++;
+ return hdr;
+ }
+ hdr = os_strchr(hdr, '\n');
+ if (hdr == NULL)
+ return NULL;
+ hdr++;
+ }
+}
diff --git a/freebsd/contrib/wpa/src/wps/httpread.h b/freebsd/contrib/wpa/src/wps/httpread.h
new file mode 100644
index 00000000..454b618b
--- /dev/null
+++ b/freebsd/contrib/wpa/src/wps/httpread.h
@@ -0,0 +1,117 @@
+/*
+ * httpread - Manage reading file(s) from HTTP/TCP socket
+ * Author: Ted Merrill
+ * Copyright 2008 Atheros Communications
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef HTTPREAD_H
+#define HTTPREAD_H
+
+/* event types (passed to callback) */
+enum httpread_event {
+ HTTPREAD_EVENT_FILE_READY = 1, /* including reply ready */
+ HTTPREAD_EVENT_TIMEOUT = 2,
+ HTTPREAD_EVENT_ERROR = 3 /* misc. error, esp malloc error */
+};
+
+
+/* header type detected
+ * available to callback via call to httpread_reply_code_get()
+ */
+enum httpread_hdr_type {
+ HTTPREAD_HDR_TYPE_UNKNOWN = 0, /* none of the following */
+ HTTPREAD_HDR_TYPE_REPLY = 1, /* hdr begins w/ HTTP/ */
+ HTTPREAD_HDR_TYPE_GET = 2, /* hdr begins with GET<sp> */
+ HTTPREAD_HDR_TYPE_HEAD = 3, /* hdr begins with HEAD<sp> */
+ HTTPREAD_HDR_TYPE_POST = 4, /* hdr begins with POST<sp> */
+ HTTPREAD_HDR_TYPE_PUT = 5, /* hdr begins with ... */
+ HTTPREAD_HDR_TYPE_DELETE = 6, /* hdr begins with ... */
+ HTTPREAD_HDR_TYPE_TRACE = 7, /* hdr begins with ... */
+ HTTPREAD_HDR_TYPE_CONNECT = 8, /* hdr begins with ... */
+ HTTPREAD_HDR_TYPE_NOTIFY = 9, /* hdr begins with ... */
+ HTTPREAD_HDR_TYPE_M_SEARCH = 10, /* hdr begins with ... */
+ HTTPREAD_HDR_TYPE_M_POST = 11, /* hdr begins with ... */
+ HTTPREAD_HDR_TYPE_SUBSCRIBE = 12, /* hdr begins with ... */
+ HTTPREAD_HDR_TYPE_UNSUBSCRIBE = 13, /* hdr begins with ... */
+
+ HTTPREAD_N_HDR_TYPES /* keep last */
+};
+
+
+/* control instance -- opaque struct declaration
+ */
+struct httpread;
+
+
+/* httpread_destroy -- if h is non-NULL, clean up
+ * This must eventually be called by the application following
+ * call of the application's callback and may be called
+ * earlier if desired.
+ */
+void httpread_destroy(struct httpread *h);
+
+/* httpread_create -- start a new reading session making use of eloop.
+ * The new instance will use the socket descriptor for reading (until
+ * it gets a file and not after) but will not close the socket, even
+ * when the instance is destroyed (the application must do that).
+ * Return NULL on error.
+ *
+ * Provided that httpread_create successfully returns a handle,
+ * the callback fnc is called to handle httpread_event events.
+ * The caller should do destroy on any errors or unknown events.
+ *
+ * Pass max_bytes == 0 to not read body at all (required for e.g.
+ * reply to HEAD request).
+ */
+struct httpread * httpread_create(
+ int sd, /* descriptor of TCP socket to read from */
+ void (*cb)(struct httpread *handle, void *cookie,
+ enum httpread_event e), /* call on event */
+ void *cookie, /* pass to callback */
+ int max_bytes, /* maximum file size else abort it */
+ int timeout_seconds /* 0; or total duration timeout period */
+ );
+
+/* httpread_hdr_type_get -- When file is ready, returns header type.
+ */
+enum httpread_hdr_type httpread_hdr_type_get(struct httpread *h);
+
+
+/* httpread_uri_get -- When file is ready, uri_get returns (translated) URI
+ * or possibly NULL (which would be an error).
+ */
+char *httpread_uri_get(struct httpread *h);
+
+/* httpread_reply_code_get -- When reply is ready, returns reply code */
+int httpread_reply_code_get(struct httpread *h);
+
+
+/* httpread_length_get -- When file is ready, returns file length. */
+int httpread_length_get(struct httpread *h);
+
+/* httpread_data_get -- When file is ready, returns file content
+ * with null byte appened.
+ * Might return NULL in some error condition.
+ */
+void * httpread_data_get(struct httpread *h);
+
+/* httpread_hdr_get -- When file is ready, returns header content
+ * with null byte appended.
+ * Might return NULL in some error condition.
+ */
+char * httpread_hdr_get(struct httpread *h);
+
+/* httpread_hdr_line_get -- When file is ready, returns pointer
+ * to line within header content matching the given tag
+ * (after the tag itself and any spaces/tabs).
+ *
+ * The tag should end with a colon for reliable matching.
+ *
+ * If not found, returns NULL;
+ */
+char * httpread_hdr_line_get(struct httpread *h, const char *tag);
+
+#endif /* HTTPREAD_H */
diff --git a/freebsd/contrib/wpa/src/wps/upnp_xml.c b/freebsd/contrib/wpa/src/wps/upnp_xml.c
new file mode 100644
index 00000000..af583ff6
--- /dev/null
+++ b/freebsd/contrib/wpa/src/wps/upnp_xml.c
@@ -0,0 +1,254 @@
+#include <machine/rtems-bsd-user-space.h>
+
+/*
+ * UPnP XML helper routines
+ * Copyright (c) 2000-2003 Intel Corporation
+ * Copyright (c) 2006-2007 Sony Corporation
+ * Copyright (c) 2008-2009 Atheros Communications
+ * Copyright (c) 2009, Jouni Malinen <j@w1.fi>
+ *
+ * See wps_upnp.c for more details on licensing and code history.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "base64.h"
+#include "http.h"
+#include "upnp_xml.h"
+
+
+/*
+ * XML parsing and formatting
+ *
+ * XML is a markup language based on unicode; usually (and in our case,
+ * always!) based on utf-8. utf-8 uses a variable number of bytes per
+ * character. utf-8 has the advantage that all non-ASCII unicode characters are
+ * represented by sequences of non-ascii (high bit set) bytes, whereas ASCII
+ * characters are single ascii bytes, thus we can use typical text processing.
+ *
+ * (One other interesting thing about utf-8 is that it is possible to look at
+ * any random byte and determine if it is the first byte of a character as
+ * versus a continuation byte).
+ *
+ * The base syntax of XML uses a few ASCII punctionation characters; any
+ * characters that would appear in the payload data are rewritten using
+ * sequences, e.g., &amp; for ampersand(&) and &lt for left angle bracket (<).
+ * Five such escapes total (more can be defined but that does not apply to our
+ * case). Thus we can safely parse for angle brackets etc.
+ *
+ * XML describes tree structures of tagged data, with each element beginning
+ * with an opening tag <label> and ending with a closing tag </label> with
+ * matching label. (There is also a self-closing tag <label/> which is supposed
+ * to be equivalent to <label></label>, i.e., no payload, but we are unlikely
+ * to see it for our purpose).
+ *
+ * Actually the opening tags are a little more complicated because they can
+ * contain "attributes" after the label (delimited by ascii space or tab chars)
+ * of the form attribute_label="value" or attribute_label='value'; as it turns
+ * out we do not have to read any of these attributes, just ignore them.
+ *
+ * Labels are any sequence of chars other than space, tab, right angle bracket
+ * (and ?), but may have an inner structure of <namespace><colon><plain_label>.
+ * As it turns out, we can ignore the namespaces, in fact we can ignore the
+ * entire tree hierarchy, because the plain labels we are looking for will be
+ * unique (not in general, but for this application). We do however have to be
+ * careful to skip over the namespaces.
+ *
+ * In generating XML we have to be more careful, but that is easy because
+ * everything we do is pretty canned. The only real care to take is to escape
+ * any special chars in our payload.
+ */
+
+/**
+ * xml_next_tag - Advance to next tag
+ * @in: Input
+ * @out: OUT: start of tag just after '<'
+ * @out_tagname: OUT: start of name of tag, skipping namespace
+ * @end: OUT: one after tag
+ * Returns: 0 on success, 1 on failure
+ *
+ * A tag has form:
+ * <left angle bracket><...><right angle bracket>
+ * Within the angle brackets, there is an optional leading forward slash (which
+ * makes the tag an ending tag), then an optional leading label (followed by
+ * colon) and then the tag name itself.
+ *
+ * Note that angle brackets present in the original data must have been encoded
+ * as &lt; and &gt; so they will not trouble us.
+ */
+int xml_next_tag(const char *in, const char **out,
+ const char **out_tagname, const char **end)
+{
+ while (*in && *in != '<')
+ in++;
+ if (*in != '<')
+ return 1;
+ *out = ++in;
+ if (*in == '/')
+ in++;
+ *out_tagname = in; /* maybe */
+ while (isalnum(*in) || *in == '-')
+ in++;
+ if (*in == ':')
+ *out_tagname = ++in;
+ while (*in && *in != '>')
+ in++;
+ if (*in != '>')
+ return 1;
+ *end = ++in;
+ return 0;
+}
+
+
+/* xml_data_encode -- format data for xml file, escaping special characters.
+ *
+ * Note that we assume we are using utf8 both as input and as output!
+ * In utf8, characters may be classed as follows:
+ * 0xxxxxxx(2) -- 1 byte ascii char
+ * 11xxxxxx(2) -- 1st byte of multi-byte char w/ unicode value >= 0x80
+ * 110xxxxx(2) -- 1st byte of 2 byte sequence (5 payload bits here)
+ * 1110xxxx(2) -- 1st byte of 3 byte sequence (4 payload bits here)
+ * 11110xxx(2) -- 1st byte of 4 byte sequence (3 payload bits here)
+ * 10xxxxxx(2) -- extension byte (6 payload bits per byte)
+ * Some values implied by the above are however illegal because they
+ * do not represent unicode chars or are not the shortest encoding.
+ * Actually, we can almost entirely ignore the above and just do
+ * text processing same as for ascii text.
+ *
+ * XML is written with arbitrary unicode characters, except that five
+ * characters have special meaning and so must be escaped where they
+ * appear in payload data... which we do here.
+ */
+void xml_data_encode(struct wpabuf *buf, const char *data, int len)
+{
+ int i;
+ for (i = 0; i < len; i++) {
+ u8 c = ((u8 *) data)[i];
+ if (c == '<') {
+ wpabuf_put_str(buf, "&lt;");
+ continue;
+ }
+ if (c == '>') {
+ wpabuf_put_str(buf, "&gt;");
+ continue;
+ }
+ if (c == '&') {
+ wpabuf_put_str(buf, "&amp;");
+ continue;
+ }
+ if (c == '\'') {
+ wpabuf_put_str(buf, "&apos;");
+ continue;
+ }
+ if (c == '"') {
+ wpabuf_put_str(buf, "&quot;");
+ continue;
+ }
+ /*
+ * We could try to represent control characters using the
+ * sequence: &#x; where x is replaced by a hex numeral, but not
+ * clear why we would do this.
+ */
+ wpabuf_put_u8(buf, c);
+ }
+}
+
+
+/* xml_add_tagged_data -- format tagged data as a new xml line.
+ *
+ * tag must not have any special chars.
+ * data may have special chars, which are escaped.
+ */
+void xml_add_tagged_data(struct wpabuf *buf, const char *tag, const char *data)
+{
+ wpabuf_printf(buf, "<%s>", tag);
+ xml_data_encode(buf, data, os_strlen(data));
+ wpabuf_printf(buf, "</%s>\n", tag);
+}
+
+
+/* A POST body looks something like (per upnp spec):
+ * <?xml version="1.0"?>
+ * <s:Envelope
+ * xmlns:s="http://schemas.xmlsoap.org/soap/envelope/"
+ * s:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
+ * <s:Body>
+ * <u:actionName xmlns:u="urn:schemas-upnp-org:service:serviceType:v">
+ * <argumentName>in arg value</argumentName>
+ * other in args and their values go here, if any
+ * </u:actionName>
+ * </s:Body>
+ * </s:Envelope>
+ *
+ * where :
+ * s: might be some other namespace name followed by colon
+ * u: might be some other namespace name followed by colon
+ * actionName will be replaced according to action requested
+ * schema following actionName will be WFA scheme instead
+ * argumentName will be actual argument name
+ * (in arg value) will be actual argument value
+ */
+char * xml_get_first_item(const char *doc, const char *item)
+{
+ const char *match = item;
+ int match_len = os_strlen(item);
+ const char *tag, *tagname, *end;
+ char *value;
+
+ /*
+ * This is crude: ignore any possible tag name conflicts and go right
+ * to the first tag of this name. This should be ok for the limited
+ * domain of UPnP messages.
+ */
+ for (;;) {
+ if (xml_next_tag(doc, &tag, &tagname, &end))
+ return NULL;
+ doc = end;
+ if (!os_strncasecmp(tagname, match, match_len) &&
+ *tag != '/' &&
+ (tagname[match_len] == '>' ||
+ !isgraph(tagname[match_len]))) {
+ break;
+ }
+ }
+ end = doc;
+ while (*end && *end != '<')
+ end++;
+ value = os_zalloc(1 + (end - doc));
+ if (value == NULL)
+ return NULL;
+ os_memcpy(value, doc, end - doc);
+ return value;
+}
+
+
+struct wpabuf * xml_get_base64_item(const char *data, const char *name,
+ enum http_reply_code *ret)
+{
+ char *msg;
+ struct wpabuf *buf;
+ unsigned char *decoded;
+ size_t len;
+
+ msg = xml_get_first_item(data, name);
+ if (msg == NULL) {
+ *ret = UPNP_ARG_VALUE_INVALID;
+ return NULL;
+ }
+
+ decoded = base64_decode((unsigned char *) msg, os_strlen(msg), &len);
+ os_free(msg);
+ if (decoded == NULL) {
+ *ret = UPNP_OUT_OF_MEMORY;
+ return NULL;
+ }
+
+ buf = wpabuf_alloc_ext_data(decoded, len);
+ if (buf == NULL) {
+ os_free(decoded);
+ *ret = UPNP_OUT_OF_MEMORY;
+ return NULL;
+ }
+ return buf;
+}
diff --git a/freebsd/contrib/wpa/src/wps/upnp_xml.h b/freebsd/contrib/wpa/src/wps/upnp_xml.h
new file mode 100644
index 00000000..616af3da
--- /dev/null
+++ b/freebsd/contrib/wpa/src/wps/upnp_xml.h
@@ -0,0 +1,25 @@
+/*
+ * UPnP XML helper routines
+ * Copyright (c) 2000-2003 Intel Corporation
+ * Copyright (c) 2006-2007 Sony Corporation
+ * Copyright (c) 2008-2009 Atheros Communications
+ * Copyright (c) 2009, Jouni Malinen <j@w1.fi>
+ *
+ * See wps_upnp.c for more details on licensing and code history.
+ */
+
+#ifndef UPNP_XML_H
+#define UPNP_XML_H
+
+#include "http.h"
+
+void xml_data_encode(struct wpabuf *buf, const char *data, int len);
+void xml_add_tagged_data(struct wpabuf *buf, const char *tag,
+ const char *data);
+int xml_next_tag(const char *in, const char **out,
+ const char **out_tagname, const char **end);
+char * xml_get_first_item(const char *doc, const char *item);
+struct wpabuf * xml_get_base64_item(const char *data, const char *name,
+ enum http_reply_code *ret);
+
+#endif /* UPNP_XML_H */
diff --git a/freebsd/contrib/wpa/src/wps/wps.c b/freebsd/contrib/wpa/src/wps/wps.c
new file mode 100644
index 00000000..fb6d8f65
--- /dev/null
+++ b/freebsd/contrib/wpa/src/wps/wps.c
@@ -0,0 +1,664 @@
+#include <machine/rtems-bsd-user-space.h>
+
+/*
+ * 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 "includes.h"
+
+#include "common.h"
+#include "crypto/dh_group5.h"
+#include "common/ieee802_11_defs.h"
+#include "wps_i.h"
+#include "wps_dev_attr.h"
+
+
+#ifdef CONFIG_WPS_TESTING
+int wps_version_number = 0x20;
+int wps_testing_dummy_cred = 0;
+int wps_corrupt_pkhash = 0;
+#endif /* CONFIG_WPS_TESTING */
+
+
+/**
+ * wps_init - Initialize WPS Registration protocol data
+ * @cfg: WPS configuration
+ * Returns: Pointer to allocated data or %NULL on failure
+ *
+ * This function is used to initialize WPS data for a registration protocol
+ * instance (i.e., each run of registration protocol as a Registrar of
+ * Enrollee. The caller is responsible for freeing this data after the
+ * registration run has been completed by calling wps_deinit().
+ */
+struct wps_data * wps_init(const struct wps_config *cfg)
+{
+ struct wps_data *data = os_zalloc(sizeof(*data));
+ if (data == NULL)
+ return NULL;
+ data->wps = cfg->wps;
+ data->registrar = cfg->registrar;
+ if (cfg->registrar) {
+ os_memcpy(data->uuid_r, cfg->wps->uuid, WPS_UUID_LEN);
+ } else {
+ os_memcpy(data->mac_addr_e, cfg->wps->dev.mac_addr, ETH_ALEN);
+ os_memcpy(data->uuid_e, cfg->wps->uuid, WPS_UUID_LEN);
+ }
+ if (cfg->pin) {
+ data->dev_pw_id = cfg->dev_pw_id;
+ data->dev_password = os_malloc(cfg->pin_len);
+ if (data->dev_password == NULL) {
+ os_free(data);
+ return NULL;
+ }
+ os_memcpy(data->dev_password, cfg->pin, cfg->pin_len);
+ data->dev_password_len = cfg->pin_len;
+ wpa_hexdump_key(MSG_DEBUG, "WPS: AP PIN dev_password",
+ data->dev_password, data->dev_password_len);
+ }
+
+#ifdef CONFIG_WPS_NFC
+ if (cfg->pin == NULL &&
+ cfg->dev_pw_id == DEV_PW_NFC_CONNECTION_HANDOVER)
+ data->dev_pw_id = cfg->dev_pw_id;
+
+ if (cfg->wps->ap && !cfg->registrar && cfg->wps->ap_nfc_dev_pw_id) {
+ /* Keep AP PIN as alternative Device Password */
+ data->alt_dev_pw_id = data->dev_pw_id;
+ data->alt_dev_password = data->dev_password;
+ data->alt_dev_password_len = data->dev_password_len;
+
+ data->dev_pw_id = cfg->wps->ap_nfc_dev_pw_id;
+ data->dev_password =
+ os_malloc(wpabuf_len(cfg->wps->ap_nfc_dev_pw));
+ if (data->dev_password == NULL) {
+ os_free(data);
+ return NULL;
+ }
+ os_memcpy(data->dev_password,
+ wpabuf_head(cfg->wps->ap_nfc_dev_pw),
+ wpabuf_len(cfg->wps->ap_nfc_dev_pw));
+ data->dev_password_len = wpabuf_len(cfg->wps->ap_nfc_dev_pw);
+ wpa_hexdump_key(MSG_DEBUG, "WPS: NFC dev_password",
+ data->dev_password, data->dev_password_len);
+ }
+#endif /* CONFIG_WPS_NFC */
+
+ data->pbc = cfg->pbc;
+ if (cfg->pbc) {
+ /* Use special PIN '00000000' for PBC */
+ data->dev_pw_id = DEV_PW_PUSHBUTTON;
+ bin_clear_free(data->dev_password, data->dev_password_len);
+ data->dev_password = (u8 *) os_strdup("00000000");
+ if (data->dev_password == NULL) {
+ os_free(data);
+ return NULL;
+ }
+ data->dev_password_len = 8;
+ }
+
+ data->state = data->registrar ? RECV_M1 : SEND_M1;
+
+ if (cfg->assoc_wps_ie) {
+ struct wps_parse_attr attr;
+ wpa_hexdump_buf(MSG_DEBUG, "WPS: WPS IE from (Re)AssocReq",
+ cfg->assoc_wps_ie);
+ if (wps_parse_msg(cfg->assoc_wps_ie, &attr) < 0) {
+ wpa_printf(MSG_DEBUG, "WPS: Failed to parse WPS IE "
+ "from (Re)AssocReq");
+ } else if (attr.request_type == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS: No Request Type attribute "
+ "in (Re)AssocReq WPS IE");
+ } else {
+ wpa_printf(MSG_DEBUG, "WPS: Request Type (from WPS IE "
+ "in (Re)AssocReq WPS IE): %d",
+ *attr.request_type);
+ data->request_type = *attr.request_type;
+ }
+ }
+
+ if (cfg->new_ap_settings) {
+ data->new_ap_settings =
+ os_malloc(sizeof(*data->new_ap_settings));
+ if (data->new_ap_settings == NULL) {
+ bin_clear_free(data->dev_password,
+ data->dev_password_len);
+ os_free(data);
+ return NULL;
+ }
+ os_memcpy(data->new_ap_settings, cfg->new_ap_settings,
+ sizeof(*data->new_ap_settings));
+ }
+
+ if (cfg->peer_addr)
+ os_memcpy(data->peer_dev.mac_addr, cfg->peer_addr, ETH_ALEN);
+ if (cfg->p2p_dev_addr)
+ os_memcpy(data->p2p_dev_addr, cfg->p2p_dev_addr, ETH_ALEN);
+
+ data->use_psk_key = cfg->use_psk_key;
+ data->pbc_in_m1 = cfg->pbc_in_m1;
+
+ if (cfg->peer_pubkey_hash) {
+ os_memcpy(data->peer_pubkey_hash, cfg->peer_pubkey_hash,
+ WPS_OOB_PUBKEY_HASH_LEN);
+ data->peer_pubkey_hash_set = 1;
+ }
+
+ return data;
+}
+
+
+/**
+ * wps_deinit - Deinitialize WPS Registration protocol data
+ * @data: WPS Registration protocol data from wps_init()
+ */
+void wps_deinit(struct wps_data *data)
+{
+#ifdef CONFIG_WPS_NFC
+ if (data->registrar && data->nfc_pw_token)
+ wps_registrar_remove_nfc_pw_token(data->wps->registrar,
+ data->nfc_pw_token);
+#endif /* CONFIG_WPS_NFC */
+
+ if (data->wps_pin_revealed) {
+ wpa_printf(MSG_DEBUG, "WPS: Full PIN information revealed and "
+ "negotiation failed");
+ if (data->registrar)
+ wps_registrar_invalidate_pin(data->wps->registrar,
+ data->uuid_e);
+ } else if (data->registrar)
+ wps_registrar_unlock_pin(data->wps->registrar, data->uuid_e);
+
+ wpabuf_free(data->dh_privkey);
+ wpabuf_free(data->dh_pubkey_e);
+ wpabuf_free(data->dh_pubkey_r);
+ wpabuf_free(data->last_msg);
+ bin_clear_free(data->dev_password, data->dev_password_len);
+ bin_clear_free(data->alt_dev_password, data->alt_dev_password_len);
+ bin_clear_free(data->new_psk, data->new_psk_len);
+ wps_device_data_free(&data->peer_dev);
+ bin_clear_free(data->new_ap_settings, sizeof(*data->new_ap_settings));
+ dh5_free(data->dh_ctx);
+ os_free(data);
+}
+
+
+/**
+ * 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)
+{
+ struct wps_parse_attr attr;
+
+ /*
+ * 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 ||
+ !attr.selected_registrar || *attr.selected_registrar == 0 ||
+ !attr.dev_password_id ||
+ 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_PUSHBUTTON))
+ return 0;
+#endif /* CONFIG_WPS_STRICT */
+
+ return 1;
+}
+
+
+static int is_selected_pin_registrar(struct wps_parse_attr *attr)
+{
+ /*
+ * 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)
+ 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)
+{
+ struct wps_parse_attr attr;
+
+ if (wps_parse_msg(msg, &attr) < 0)
+ return 0;
+
+ return is_selected_pin_registrar(&attr);
+}
+
+
+/**
+ * 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_parse_attr attr;
+ unsigned int i;
+ const u8 *pos;
+ const u8 bcast[ETH_ALEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
+
+ if (wps_parse_msg(msg, &attr) < 0)
+ return 0;
+
+ if (!attr.version2 && ver1_compat) {
+ /*
+ * Version 1.0 AP - AuthorizedMACs not used, so revert back to
+ * old mechanism of using SelectedRegistrar.
+ */
+ return is_selected_pin_registrar(&attr);
+ }
+
+ if (!attr.authorized_macs)
+ return 0;
+
+ pos = attr.authorized_macs;
+ for (i = 0; i < attr.authorized_macs_len / ETH_ALEN; i++) {
+ if (os_memcmp(pos, addr, ETH_ALEN) == 0)
+ return 2;
+ if (os_memcmp(pos, bcast, ETH_ALEN) == 0)
+ return 1;
+ pos += ETH_ALEN;
+ }
+
+ return 0;
+}
+
+
+/**
+ * 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;
+ int sel_a, sel_b;
+
+ if (wps_a == NULL || wps_parse_msg(wps_a, &attr) < 0)
+ return 1;
+ sel_a = attr.selected_registrar && *attr.selected_registrar != 0;
+
+ if (wps_b == NULL || wps_parse_msg(wps_b, &attr) < 0)
+ return -1;
+ sel_b = attr.selected_registrar && *attr.selected_registrar != 0;
+
+ if (sel_a && !sel_b)
+ return -1;
+ if (!sel_a && sel_b)
+ return 1;
+
+ return 0;
+}
+
+
+/**
+ * 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;
+
+ if (wps_parse_msg(msg, &attr) < 0)
+ return NULL;
+ return attr.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;
+
+ if (msg == NULL || wps_parse_msg(msg, &attr) < 0)
+ return 0;
+ return attr.version2 != NULL;
+}
+
+
+/**
+ * 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");
+
+ ie = wpabuf_alloc(500);
+ if (ie == NULL)
+ 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, 0) ||
+ wps_build_assoc_state(NULL, ie) ||
+ wps_build_config_error(ie, WPS_CFG_NO_ERROR) ||
+ wps_build_dev_password_id(ie, pw_id) ||
+ 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) ||
+ 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;
+ }
+
+ return wps_ie_encapsulate(ie);
+}
+
+
+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);
+ }
+}
+
+
+int wps_attr_text(struct wpabuf *data, char *buf, char *end)
+{
+ struct wps_parse_attr attr;
+ char *pos = buf;
+ int ret;
+
+ if (wps_parse_msg(data, &attr) < 0)
+ return -1;
+
+ if (attr.wps_state) {
+ if (*attr.wps_state == WPS_STATE_NOT_CONFIGURED)
+ ret = os_snprintf(pos, end - pos,
+ "wps_state=unconfigured\n");
+ else if (*attr.wps_state == WPS_STATE_CONFIGURED)
+ ret = os_snprintf(pos, end - pos,
+ "wps_state=configured\n");
+ else
+ ret = 0;
+ if (os_snprintf_error(end - pos, ret))
+ return pos - buf;
+ pos += ret;
+ }
+
+ if (attr.ap_setup_locked && *attr.ap_setup_locked) {
+ ret = os_snprintf(pos, end - pos,
+ "wps_ap_setup_locked=1\n");
+ if (os_snprintf_error(end - pos, ret))
+ return pos - buf;
+ pos += ret;
+ }
+
+ if (attr.selected_registrar && *attr.selected_registrar) {
+ ret = os_snprintf(pos, end - pos,
+ "wps_selected_registrar=1\n");
+ if (os_snprintf_error(end - pos, ret))
+ return pos - buf;
+ pos += ret;
+ }
+
+ if (attr.dev_password_id) {
+ ret = os_snprintf(pos, end - pos,
+ "wps_device_password_id=%u\n",
+ WPA_GET_BE16(attr.dev_password_id));
+ if (os_snprintf_error(end - pos, ret))
+ return pos - buf;
+ pos += ret;
+ }
+
+ if (attr.sel_reg_config_methods) {
+ ret = os_snprintf(pos, end - pos,
+ "wps_selected_registrar_config_methods="
+ "0x%04x\n",
+ WPA_GET_BE16(attr.sel_reg_config_methods));
+ if (os_snprintf_error(end - pos, ret))
+ return pos - buf;
+ pos += ret;
+ }
+
+ if (attr.primary_dev_type) {
+ char devtype[WPS_DEV_TYPE_BUFSIZE];
+ ret = os_snprintf(pos, end - pos,
+ "wps_primary_device_type=%s\n",
+ wps_dev_type_bin2str(attr.primary_dev_type,
+ devtype,
+ sizeof(devtype)));
+ if (os_snprintf_error(end - pos, ret))
+ return pos - buf;
+ pos += ret;
+ }
+
+ if (attr.dev_name) {
+ char *str = os_malloc(attr.dev_name_len + 1);
+ size_t i;
+ if (str == NULL)
+ return pos - buf;
+ for (i = 0; i < attr.dev_name_len; i++) {
+ if (attr.dev_name[i] == 0 ||
+ is_ctrl_char(attr.dev_name[i]))
+ str[i] = '_';
+ else
+ str[i] = attr.dev_name[i];
+ }
+ str[i] = '\0';
+ ret = os_snprintf(pos, end - pos, "wps_device_name=%s\n", str);
+ os_free(str);
+ if (os_snprintf_error(end - pos, ret))
+ return pos - buf;
+ pos += ret;
+ }
+
+ if (attr.config_methods) {
+ ret = os_snprintf(pos, end - pos,
+ "wps_config_methods=0x%04x\n",
+ WPA_GET_BE16(attr.config_methods));
+ if (os_snprintf_error(end - pos, ret))
+ return pos - buf;
+ pos += ret;
+ }
+
+ return pos - buf;
+}
+
+
+const char * wps_ei_str(enum wps_error_indication ei)
+{
+ switch (ei) {
+ case WPS_EI_NO_ERROR:
+ return "No Error";
+ case WPS_EI_SECURITY_TKIP_ONLY_PROHIBITED:
+ return "TKIP Only Prohibited";
+ case WPS_EI_SECURITY_WEP_PROHIBITED:
+ return "WEP Prohibited";
+ case WPS_EI_AUTH_FAILURE:
+ return "Authentication Failure";
+ default:
+ return "Unknown";
+ }
+}
diff --git a/freebsd/contrib/wpa/src/wps/wps.h b/freebsd/contrib/wpa/src/wps/wps.h
new file mode 100644
index 00000000..2c91d167
--- /dev/null
+++ b/freebsd/contrib/wpa/src/wps/wps.h
@@ -0,0 +1,1041 @@
+/*
+ * Wi-Fi Protected Setup
+ * Copyright (c) 2007-2013, 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 "common/ieee802_11_defs.h"
+#include "wps_defs.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_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
+ */
+struct wps_credential {
+ u8 ssid[SSID_MAX_LEN];
+ 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;
+};
+
+#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, WPS_RF_60GHZ 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;
+
+ /**
+ * peer_pubkey_hash - Peer public key hash or %NULL if not known
+ */
+ const u8 *peer_pubkey_hash;
+};
+
+struct wps_data * wps_init(const struct wps_config *cfg);
+
+void wps_deinit(struct wps_data *data);
+
+/**
+ * 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
+};
+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);
+int wps_is_selected_pin_registrar(const struct wpabuf *msg);
+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
+ * @p2p_dev_addr: P2P Device Address of the Enrollee or all zeros if not
+ * @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 *p2p_dev_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;
+
+ /**
+ * force_per_enrollee_psk - Force per-Enrollee random PSK
+ *
+ * This forces per-Enrollee random PSK to be generated even if a default
+ * PSK is set for a network.
+ */
+ int force_per_enrollee_psk;
+};
+
+
+/**
+ * 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_PBC_ACTIVE - PBC mode was activated
+ */
+ WPS_EV_PBC_ACTIVE,
+
+ /**
+ * WPS_EV_PBC_DISABLE - PBC mode was disabled
+ */
+ WPS_EV_PBC_DISABLE,
+
+ /**
+ * 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;
+ u8 peer_macaddr[ETH_ALEN];
+ } fail;
+
+ struct wps_event_success {
+ u8 peer_macaddr[ETH_ALEN];
+ } success;
+
+ struct wps_event_pwd_auth_fail {
+ int enrollee;
+ int part;
+ u8 peer_macaddr[ETH_ALEN];
+ } 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;
+};
+
+/**
+ * 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;
+};
+
+/**
+ * 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[SSID_MAX_LEN];
+
+ /**
+ * 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;
+
+ /**
+ * encr_types - Current AP encryption type (WPS_ENCR_*)
+ */
+ u16 ap_encr_type;
+
+ /**
+ * ap_auth_type - Current AP authentication types (WPS_AUTH_*)
+ */
+ u16 ap_auth_type;
+
+ /**
+ * 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);
+
+ /**
+ * rf_band_cb - Fetch currently used RF band
+ * @ctx: Higher layer context data (cb_ctx)
+ * Return: Current used RF band or 0 if not known
+ */
+ int (*rf_band_cb)(void *ctx);
+
+ /**
+ * 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;
+
+ u16 ap_nfc_dev_pw_id;
+ struct wpabuf *ap_nfc_dh_pubkey;
+ struct wpabuf *ap_nfc_dh_privkey;
+ struct wpabuf *ap_nfc_dev_pw;
+};
+
+struct wps_registrar *
+wps_registrar_init(struct wps_context *wps,
+ const struct wps_registrar_config *cfg);
+void wps_registrar_deinit(struct wps_registrar *reg);
+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_wps_cancel(struct wps_registrar *reg);
+int wps_registrar_unlock_pin(struct wps_registrar *reg, const u8 *uuid);
+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);
+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 pk_hash_provided_oob);
+int wps_registrar_add_nfc_password_token(struct wps_registrar *reg,
+ const u8 *oob_dev_pw,
+ size_t oob_dev_pw_len);
+void wps_registrar_flush(struct wps_registrar *reg);
+
+int wps_build_credential_wrap(struct wpabuf *msg,
+ const struct wps_credential *cred);
+
+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);
+void wps_free_pending_msgs(struct upnp_pending_message *msgs);
+
+struct wpabuf * wps_get_oob_cred(struct wps_context *wps, int rf_band,
+ int channel);
+int wps_oob_use_cred(struct wps_context *wps, struct wps_parse_attr *attr);
+int wps_attr_text(struct wpabuf *data, char *buf, char *end);
+const char * wps_ei_str(enum wps_error_indication ei);
+
+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, const u8 *addr);
+const u8 * wps_er_get_sta_uuid(struct wps_er *er, const u8 *addr);
+int wps_er_learn(struct wps_er *er, const u8 *uuid, const u8 *addr,
+ const u8 *pin, size_t pin_len);
+int wps_er_set_config(struct wps_er *er, const u8 *uuid, const u8 *addr,
+ const struct wps_credential *cred);
+int wps_er_config(struct wps_er *er, const u8 *uuid, const u8 *addr,
+ const u8 *pin, size_t pin_len,
+ const struct wps_credential *cred);
+struct wpabuf * wps_er_config_token_from_cred(struct wps_context *wps,
+ struct wps_credential *cred);
+struct wpabuf * wps_er_nfc_config_token(struct wps_er *er, const u8 *uuid,
+ const u8 *addr);
+struct wpabuf * wps_er_nfc_handover_sel(struct wps_er *er,
+ struct wps_context *wps, const u8 *uuid,
+ const u8 *addr, struct wpabuf *pubkey);
+
+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);
+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_build(int ndef, int id, struct wpabuf *pubkey,
+ struct wpabuf *dev_pw);
+int wps_nfc_gen_dh(struct wpabuf **pubkey, struct wpabuf **privkey);
+struct wpabuf * wps_nfc_token_gen(int ndef, int *id, struct wpabuf **pubkey,
+ struct wpabuf **privkey,
+ struct wpabuf **dev_pw);
+struct wpabuf * wps_build_nfc_handover_req(struct wps_context *ctx,
+ struct wpabuf *nfc_dh_pubkey);
+struct wpabuf * wps_build_nfc_handover_sel(struct wps_context *ctx,
+ struct wpabuf *nfc_dh_pubkey,
+ const u8 *bssid, int freq);
+struct wpabuf * wps_build_nfc_handover_req_p2p(struct wps_context *ctx,
+ struct wpabuf *nfc_dh_pubkey);
+struct wpabuf * wps_build_nfc_handover_sel_p2p(struct wps_context *ctx,
+ int nfc_dev_pw_id,
+ struct wpabuf *nfc_dh_pubkey,
+ struct wpabuf *nfc_dev_pw);
+
+/* ndef.c */
+struct wpabuf * ndef_parse_wifi(const struct wpabuf *buf);
+struct wpabuf * ndef_build_wifi(const struct wpabuf *buf);
+struct wpabuf * ndef_parse_p2p(const struct wpabuf *buf);
+struct wpabuf * ndef_build_p2p(const struct wpabuf *buf);
+
+#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 */
+
+#endif /* WPS_H */
diff --git a/freebsd/contrib/wpa/src/wps/wps_attr_build.c b/freebsd/contrib/wpa/src/wps/wps_attr_build.c
new file mode 100644
index 00000000..742e082c
--- /dev/null
+++ b/freebsd/contrib/wpa/src/wps/wps_attr_build.c
@@ -0,0 +1,487 @@
+#include <machine/rtems-bsd-user-space.h>
+
+/*
+ * 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 "includes.h"
+
+#include "common.h"
+#include "crypto/aes_wrap.h"
+#include "crypto/crypto.h"
+#include "crypto/dh_group5.h"
+#include "crypto/sha256.h"
+#include "crypto/random.h"
+#include "common/ieee802_11_defs.h"
+#include "wps_i.h"
+
+
+int wps_build_public_key(struct wps_data *wps, struct wpabuf *msg)
+{
+ struct wpabuf *pubkey;
+
+ wpa_printf(MSG_DEBUG, "WPS: * Public Key");
+ wpabuf_free(wps->dh_privkey);
+ wps->dh_privkey = NULL;
+ if (wps->dev_pw_id != DEV_PW_DEFAULT && wps->wps->dh_privkey &&
+ wps->wps->dh_ctx) {
+ wpa_printf(MSG_DEBUG, "WPS: Using pre-configured DH keys");
+ if (wps->wps->dh_pubkey == NULL) {
+ wpa_printf(MSG_DEBUG,
+ "WPS: wps->wps->dh_pubkey == NULL");
+ return -1;
+ }
+ 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->dev_pw_id == DEV_PW_NFC_CONNECTION_HANDOVER) &&
+ (wps->wps->ap ||
+ (wps->wps->ap_nfc_dh_pubkey &&
+ wps->wps->ap_nfc_dev_pw_id ==
+ DEV_PW_NFC_CONNECTION_HANDOVER &&
+ wps->dev_pw_id == DEV_PW_NFC_CONNECTION_HANDOVER)) &&
+ (wps->dev_pw_id == wps->wps->ap_nfc_dev_pw_id ||
+ wps->wps->ap_nfc_dh_pubkey)) {
+ wpa_printf(MSG_DEBUG, "WPS: Using NFC password token DH keys");
+ if (wps->wps->ap_nfc_dh_privkey == NULL) {
+ wpa_printf(MSG_DEBUG,
+ "WPS: wps->wps->ap_nfc_dh_privkey == NULL");
+ return -1;
+ }
+ if (wps->wps->ap_nfc_dh_pubkey == NULL) {
+ wpa_printf(MSG_DEBUG,
+ "WPS: wps->wps->ap_nfc_dh_pubkey == NULL");
+ return -1;
+ }
+ 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");
+ dh5_free(wps->dh_ctx);
+ wps->dh_ctx = dh5_init(&wps->dh_privkey, &pubkey);
+ 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);
+
+ wpabuf_put_be16(msg, ATTR_PUBLIC_KEY);
+ wpabuf_put_be16(msg, wpabuf_len(pubkey));
+ wpabuf_put_buf(msg, 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;
+ }
+
+ 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)
+{
+ if (wpabuf_tailroom(msg) < 4 + WPS_UUID_LEN)
+ return -1;
+ 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);
+ hmac_sha256_vector(wps->authkey, WPS_AUTHKEY_LEN, 2, addr, len, hash);
+
+ 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.
+ */
+ if (wpabuf_tailroom(msg) < 5)
+ return -1;
+ 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)
+{
+ u8 *len;
+
+#ifdef CONFIG_WPS_TESTING
+ if (WPS_VERSION == 0x10)
+ return 0;
+#endif /* CONFIG_WPS_TESTING */
+
+ if (wpabuf_tailroom(msg) <
+ 7 + 3 + (req_to_enroll ? 3 : 0) +
+ (auth_macs ? 2 + auth_macs_count * ETH_ALEN : 0))
+ return -1;
+ 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);
+
+#ifdef CONFIG_WPS_TESTING
+ if (WPS_VERSION > 0x20) {
+ if (wpabuf_tailroom(msg) < 5)
+ return -1;
+ 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;
+ /* WPA/WPA2-Enterprise enrollment not supported through WPS */
+ auth_types &= ~WPS_AUTH_WPA;
+ auth_types &= ~WPS_AUTH_WPA2;
+ auth_types &= ~WPS_AUTH_SHARED;
+ 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;
+ encr_types &= ~WPS_ENCR_WEP;
+ 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");
+ hmac_sha256(wps->authkey, WPS_AUTHKEY_LEN, wpabuf_head(msg),
+ wpabuf_len(msg), hash);
+
+ 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);
+ if (aes_128_cbc_encrypt(wps->keywrapkey, iv, data, wpabuf_len(plain)))
+ 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];
+
+ wpa_printf(MSG_DEBUG, "WPS: * OOB Device Password (dev_pw_id=%u)",
+ dev_pw_id);
+ addr[0] = wpabuf_head(pubkey);
+ hash_len = wpabuf_len(pubkey);
+ sha256_vector(1, addr, &hash_len, pubkey_hash);
+#ifdef CONFIG_WPS_TESTING
+ if (wps_corrupt_pkhash) {
+ wpa_hexdump(MSG_DEBUG, "WPS: Real Public Key Hash",
+ pubkey_hash, WPS_OOB_PUBKEY_HASH_LEN);
+ wpa_printf(MSG_INFO, "WPS: Testing - corrupt public key hash");
+ pubkey_hash[WPS_OOB_PUBKEY_HASH_LEN - 2]++;
+ }
+#endif /* CONFIG_WPS_TESTING */
+
+ wpabuf_put_be16(msg, ATTR_OOB_DEVICE_PASSWORD);
+ wpabuf_put_be16(msg, WPS_OOB_PUBKEY_HASH_LEN + 2 + dev_pw_len);
+ wpa_hexdump(MSG_DEBUG, "WPS: Public Key Hash",
+ pubkey_hash, WPS_OOB_PUBKEY_HASH_LEN);
+ wpabuf_put_data(msg, pubkey_hash, WPS_OOB_PUBKEY_HASH_LEN);
+ wpabuf_put_be16(msg, dev_pw_id);
+ if (dev_pw) {
+ wpa_hexdump_key(MSG_DEBUG, "WPS: OOB Device Password",
+ dev_pw, dev_pw_len);
+ 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;
+}
+
+
+int wps_build_mac_addr(struct wpabuf *msg, const u8 *addr)
+{
+ wpa_printf(MSG_DEBUG, "WPS: * MAC Address (" MACSTR ")",
+ MAC2STR(addr));
+ wpabuf_put_be16(msg, ATTR_MAC_ADDR);
+ wpabuf_put_be16(msg, ETH_ALEN);
+ wpabuf_put_data(msg, addr, ETH_ALEN);
+ return 0;
+}
+
+
+int wps_build_rf_bands_attr(struct wpabuf *msg, u8 rf_bands)
+{
+ wpa_printf(MSG_DEBUG, "WPS: * RF Bands (%x)", rf_bands);
+ wpabuf_put_be16(msg, ATTR_RF_BANDS);
+ wpabuf_put_be16(msg, 1);
+ wpabuf_put_u8(msg, rf_bands);
+ return 0;
+}
+
+
+int wps_build_ap_channel(struct wpabuf *msg, u16 ap_channel)
+{
+ wpa_printf(MSG_DEBUG, "WPS: * AP Channel (%u)", ap_channel);
+ wpabuf_put_be16(msg, ATTR_AP_CHANNEL);
+ wpabuf_put_be16(msg, 2);
+ wpabuf_put_be16(msg, ap_channel);
+ return 0;
+}
diff --git a/freebsd/contrib/wpa/src/wps/wps_attr_parse.c b/freebsd/contrib/wpa/src/wps/wps_attr_parse.c
new file mode 100644
index 00000000..748fe0ac
--- /dev/null
+++ b/freebsd/contrib/wpa/src/wps/wps_attr_parse.c
@@ -0,0 +1,665 @@
+#include <machine/rtems-bsd-user-space.h>
+
+/*
+ * 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 "includes.h"
+
+#include "common.h"
+#include "wps_defs.h"
+#include "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_EXCESSIVE, "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;
+ case WFA_ELEM_REGISTRAR_CONFIGURATION_METHODS:
+ if (len != 2) {
+ wpa_printf(MSG_DEBUG, "WPS: Invalid Registrar Configuration Methods length %u",
+ len);
+ return -1;
+ }
+ attr->registrar_configuration_methods = pos;
+ break;
+ default:
+ wpa_printf(MSG_MSGDUMP, "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_MSGDUMP, "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 ||
+ len > WPS_OOB_PUBKEY_HASH_LEN + 2 +
+ WPS_OOB_DEVICE_PASSWORD_LEN ||
+ (len < WPS_OOB_PUBKEY_HASH_LEN + 2 +
+ WPS_OOB_DEVICE_PASSWORD_MIN_LEN &&
+ WPA_GET_BE16(pos + WPS_OOB_PUBKEY_HASH_LEN) !=
+ DEV_PW_NFC_CONNECTION_HANDOVER)) {
+ 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_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;
+ if (len > WPS_MANUFACTURER_MAX_LEN)
+ attr->manufacturer_len = WPS_MANUFACTURER_MAX_LEN;
+ else
+ attr->manufacturer_len = len;
+ break;
+ case ATTR_MODEL_NAME:
+ attr->model_name = pos;
+ if (len > WPS_MODEL_NAME_MAX_LEN)
+ attr->model_name_len = WPS_MODEL_NAME_MAX_LEN;
+ else
+ attr->model_name_len = len;
+ break;
+ case ATTR_MODEL_NUMBER:
+ attr->model_number = pos;
+ if (len > WPS_MODEL_NUMBER_MAX_LEN)
+ attr->model_number_len = WPS_MODEL_NUMBER_MAX_LEN;
+ else
+ attr->model_number_len = len;
+ break;
+ case ATTR_SERIAL_NUMBER:
+ attr->serial_number = pos;
+ if (len > WPS_SERIAL_NUMBER_MAX_LEN)
+ attr->serial_number_len = WPS_SERIAL_NUMBER_MAX_LEN;
+ else
+ attr->serial_number_len = len;
+ break;
+ case ATTR_DEV_NAME:
+ if (len > WPS_DEV_NAME_MAX_LEN) {
+ wpa_printf(MSG_DEBUG,
+ "WPS: Ignore too long Device Name (len=%u)",
+ len);
+ break;
+ }
+ attr->dev_name = pos;
+ attr->dev_name_len = len;
+ break;
+ case ATTR_PUBLIC_KEY:
+ /*
+ * The Public Key attribute is supposed to be exactly 192 bytes
+ * in length. Allow couple of bytes shorter one to try to
+ * interoperate with implementations that do not use proper
+ * zero-padding.
+ */
+ if (len < 190 || len > 192) {
+ wpa_printf(MSG_DEBUG,
+ "WPS: Ignore Public Key with unexpected length %u",
+ len);
+ break;
+ }
+ 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:
+ if (len > SSID_MAX_LEN) {
+ wpa_printf(MSG_DEBUG,
+ "WPS: Ignore too long SSID (len=%u)", len);
+ break;
+ }
+ attr->ssid = pos;
+ attr->ssid_len = len;
+ break;
+ case ATTR_NETWORK_KEY:
+ attr->network_key = pos;
+ attr->network_key_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_EXCESSIVE, "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;
+}
diff --git a/freebsd/contrib/wpa/src/wps/wps_attr_parse.h b/freebsd/contrib/wpa/src/wps/wps_attr_parse.h
new file mode 100644
index 00000000..8188fe91
--- /dev/null
+++ b/freebsd/contrib/wpa/src/wps/wps_attr_parse.h
@@ -0,0 +1,104 @@
+/*
+ * 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.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 *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 */
+ const u8 *registrar_configuration_methods; /* 2 octets */
+
+ /* variable length fields */
+ const u8 *manufacturer;
+ const u8 *model_name;
+ const u8 *model_number;
+ const u8 *serial_number;
+ const u8 *dev_name;
+ const u8 *public_key;
+ const u8 *encr_settings;
+ const u8 *ssid; /* <= 32 octets */
+ const u8 *network_key; /* <= 64 octets */
+ const u8 *authorized_macs; /* <= 30 octets */
+ const u8 *sec_dev_type_list; /* <= 128 octets */
+ const u8 *oob_dev_password; /* 38..54 octets */
+ u16 manufacturer_len;
+ u16 model_name_len;
+ u16 model_number_len;
+ u16 serial_number_len;
+ u16 dev_name_len;
+ u16 public_key_len;
+ u16 encr_settings_len;
+ u16 ssid_len;
+ u16 network_key_len;
+ u16 authorized_macs_len;
+ u16 sec_dev_type_list_len;
+ u16 oob_dev_password_len;
+
+ /* attributes that can occur multiple times */
+#define MAX_CRED_COUNT 10
+#define MAX_REQ_DEV_TYPE_COUNT 10
+
+ unsigned int num_cred;
+ unsigned int num_req_dev_type;
+ unsigned int num_vendor_ext;
+
+ u16 cred_len[MAX_CRED_COUNT];
+ u16 vendor_ext_len[MAX_WPS_PARSE_VENDOR_EXT];
+
+ const u8 *cred[MAX_CRED_COUNT];
+ const u8 *req_dev_type[MAX_REQ_DEV_TYPE_COUNT];
+ const u8 *vendor_ext[MAX_WPS_PARSE_VENDOR_EXT];
+};
+
+int wps_parse_msg(const struct wpabuf *msg, struct wps_parse_attr *attr);
+
+#endif /* WPS_ATTR_PARSE_H */
diff --git a/freebsd/contrib/wpa/src/wps/wps_attr_process.c b/freebsd/contrib/wpa/src/wps/wps_attr_process.c
new file mode 100644
index 00000000..329649df
--- /dev/null
+++ b/freebsd/contrib/wpa/src/wps/wps_attr_process.c
@@ -0,0 +1,274 @@
+#include <machine/rtems-bsd-user-space.h>
+
+/*
+ * 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 "includes.h"
+
+#include "common.h"
+#include "crypto/sha256.h"
+#include "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;
+ hmac_sha256_vector(wps->authkey, WPS_AUTHKEY_LEN, 2, addr, len, hash);
+
+ if (os_memcmp_const(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;
+ }
+
+ hmac_sha256(wps->authkey, WPS_AUTHKEY_LEN, head, len, hash);
+ if (os_memcmp_const(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_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_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))
+ 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);
+}
diff --git a/freebsd/contrib/wpa/src/wps/wps_common.c b/freebsd/contrib/wpa/src/wps/wps_common.c
new file mode 100644
index 00000000..b6c83892
--- /dev/null
+++ b/freebsd/contrib/wpa/src/wps/wps_common.c
@@ -0,0 +1,911 @@
+#include <machine/rtems-bsd-user-space.h>
+
+/*
+ * 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 "includes.h"
+
+#include "common.h"
+#include "common/defs.h"
+#include "common/ieee802_11_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_i.h"
+#include "wps_dev_attr.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);
+ hmac_sha256_vector(key, SHA256_MAC_LEN, 4, addr, len, hash);
+ 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 */
+ wpabuf_free(wps->dh_privkey);
+ wps->dh_privkey = NULL;
+
+ 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);
+ sha256_vector(1, addr, len, dhkey);
+ 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;
+ hmac_sha256_vector(dhkey, sizeof(dhkey), 3, addr, len, kdk);
+ 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];
+
+ hmac_sha256(wps->authkey, WPS_AUTHKEY_LEN, dev_passwd,
+ (dev_passwd_len + 1) / 2, hash);
+ os_memcpy(wps->psk1, hash, WPS_PSK_LEN);
+ hmac_sha256(wps->authkey, WPS_AUTHKEY_LEN,
+ dev_passwd + (dev_passwd_len + 1) / 2,
+ dev_passwd_len / 2, hash);
+ 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);
+ if (aes_128_cbc_decrypt(wps->keywrapkey, encr, wpabuf_mhead(decrypted),
+ wpabuf_len(decrypted))) {
+ wpabuf_free(decrypted);
+ 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;
+}
+
+
+/**
+ * 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) {
+ struct os_time now;
+ os_get_time(&now);
+ val = os_random() ^ now.sec ^ now.usec;
+ }
+ 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;
+}
+
+
+void wps_fail_event(struct wps_context *wps, enum wps_msg_type msg,
+ u16 config_error, u16 error_indication, const u8 *mac_addr)
+{
+ union wps_event_data data;
+
+ if (wps->event_cb == NULL)
+ return;
+
+ os_memset(&data, 0, sizeof(data));
+ data.fail.msg = msg;
+ data.fail.config_error = config_error;
+ data.fail.error_indication = error_indication;
+ os_memcpy(data.fail.peer_macaddr, mac_addr, ETH_ALEN);
+ wps->event_cb(wps->cb_ctx, WPS_EV_FAIL, &data);
+}
+
+
+void wps_success_event(struct wps_context *wps, const u8 *mac_addr)
+{
+ union wps_event_data data;
+
+ if (wps->event_cb == NULL)
+ return;
+
+ os_memset(&data, 0, sizeof(data));
+ os_memcpy(data.success.peer_macaddr, mac_addr, ETH_ALEN);
+ wps->event_cb(wps->cb_ctx, WPS_EV_SUCCESS, &data);
+}
+
+
+void wps_pwd_auth_fail_event(struct wps_context *wps, int enrollee, int part,
+ const u8 *mac_addr)
+{
+ union wps_event_data data;
+
+ if (wps->event_cb == NULL)
+ return;
+
+ os_memset(&data, 0, sizeof(data));
+ data.pwd_auth_fail.enrollee = enrollee;
+ data.pwd_auth_fail.part = part;
+ os_memcpy(data.pwd_auth_fail.peer_macaddr, mac_addr, ETH_ALEN);
+ wps->event_cb(wps->cb_ctx, WPS_EV_PWD_AUTH_FAIL, &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);
+}
+
+
+void wps_pbc_active_event(struct wps_context *wps)
+{
+ if (wps->event_cb == NULL)
+ return;
+
+ wps->event_cb(wps->cb_ctx, WPS_EV_PBC_ACTIVE, NULL);
+}
+
+
+void wps_pbc_disable_event(struct wps_context *wps)
+{
+ if (wps->event_cb == NULL)
+ return;
+
+ wps->event_cb(wps->cb_ctx, WPS_EV_PBC_DISABLE, NULL);
+}
+
+
+#ifdef CONFIG_WPS_OOB
+
+struct wpabuf * wps_get_oob_cred(struct wps_context *wps, int rf_band,
+ int channel)
+{
+ struct wps_data data;
+ struct wpabuf *plain;
+
+ plain = wpabuf_alloc(500);
+ if (plain == NULL) {
+ wpa_printf(MSG_ERROR, "WPS: Failed to allocate memory for OOB "
+ "credential");
+ return NULL;
+ }
+
+ os_memset(&data, 0, sizeof(data));
+ data.wps = wps;
+ data.auth_type = wps->auth_types;
+ data.encr_type = wps->encr_types;
+ if (wps_build_cred(&data, plain) ||
+ (rf_band && wps_build_rf_bands_attr(plain, rf_band)) ||
+ (channel && wps_build_ap_channel(plain, channel)) ||
+ wps_build_mac_addr(plain, wps->dev.mac_addr) ||
+ wps_build_wfa_ext(plain, 0, NULL, 0)) {
+ os_free(data.new_psk);
+ wpabuf_free(plain);
+ return NULL;
+ }
+
+ if (wps->wps_state == WPS_STATE_NOT_CONFIGURED && data.new_psk &&
+ wps->ap) {
+ struct wps_credential cred;
+
+ wpa_printf(MSG_DEBUG, "WPS: Moving to Configured state based "
+ "on credential token generation");
+
+ os_memset(&cred, 0, sizeof(cred));
+ os_memcpy(cred.ssid, wps->ssid, wps->ssid_len);
+ cred.ssid_len = 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, data.new_psk, data.new_psk_len);
+ cred.key_len = data.new_psk_len;
+
+ wps->wps_state = WPS_STATE_CONFIGURED;
+ wpa_hexdump_ascii_key(MSG_DEBUG,
+ "WPS: Generated random passphrase",
+ data.new_psk, data.new_psk_len);
+ if (wps->cred_cb)
+ wps->cred_cb(wps->cb_ctx, &cred);
+ }
+
+ os_free(data.new_psk);
+
+ return plain;
+}
+
+
+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_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;
+}
+
+
+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 = os_strchr(str, '-');
+ if (pos == NULL)
+ return -1;
+ pos++;
+ if (hexstr2bin(pos, &dev_type[2], 4))
+ return -1;
+ pos = 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 = os_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 (os_snprintf_error(buf_len, ret))
+ 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 || str[0] == '\0') {
+ /* Default to enabling methods based on build configuration */
+ methods |= WPS_CONFIG_DISPLAY | WPS_CONFIG_KEYPAD;
+ methods |= WPS_CONFIG_VIRT_DISPLAY;
+#ifdef CONFIG_WPS_NFC
+ methods |= WPS_CONFIG_NFC_INTERFACE;
+#endif /* CONFIG_WPS_NFC */
+#ifdef CONFIG_P2P
+ methods |= WPS_CONFIG_P2PS;
+#endif /* CONFIG_P2P */
+ } 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;
+ 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;
+ if (os_strstr(str, "p2ps"))
+ methods |= WPS_CONFIG_P2PS;
+ }
+
+ 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_build(int ndef, int id, struct wpabuf *pubkey,
+ struct wpabuf *dev_pw)
+{
+ struct wpabuf *ret;
+
+ if (pubkey == NULL || dev_pw == NULL)
+ return NULL;
+
+ 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;
+}
+
+
+int wps_nfc_gen_dh(struct wpabuf **pubkey, struct wpabuf **privkey)
+{
+ struct wpabuf *priv = NULL, *pub = NULL;
+ void *dh_ctx;
+
+ dh_ctx = dh5_init(&priv, &pub);
+ if (dh_ctx == NULL)
+ return -1;
+ pub = wpabuf_zeropad(pub, 192);
+ if (pub == NULL) {
+ wpabuf_free(priv);
+ return -1;
+ }
+ wpa_hexdump_buf(MSG_DEBUG, "WPS: Generated new DH pubkey", pub);
+ dh5_free(dh_ctx);
+
+ wpabuf_free(*pubkey);
+ *pubkey = pub;
+ wpabuf_free(*privkey);
+ *privkey = priv;
+
+ return 0;
+}
+
+
+struct wpabuf * wps_nfc_token_gen(int ndef, int *id, struct wpabuf **pubkey,
+ struct wpabuf **privkey,
+ struct wpabuf **dev_pw)
+{
+ struct wpabuf *pw;
+ 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;
+ }
+
+ if (wps_nfc_gen_dh(pubkey, privkey) < 0) {
+ wpabuf_free(pw);
+ return NULL;
+ }
+
+ *id = 0x10 + val % 0xfff0;
+ wpabuf_free(*dev_pw);
+ *dev_pw = pw;
+
+ return wps_nfc_token_build(ndef, *id, *pubkey, *dev_pw);
+}
+
+
+struct wpabuf * wps_build_nfc_handover_req(struct wps_context *ctx,
+ struct wpabuf *nfc_dh_pubkey)
+{
+ struct wpabuf *msg;
+ void *len;
+
+ if (ctx == NULL)
+ return NULL;
+
+ wpa_printf(MSG_DEBUG, "WPS: Building attributes for NFC connection "
+ "handover request");
+
+ if (nfc_dh_pubkey == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS: No NFC OOB Device Password "
+ "configured");
+ return NULL;
+ }
+
+ msg = wpabuf_alloc(1000);
+ if (msg == NULL)
+ return msg;
+ len = wpabuf_put(msg, 2);
+
+ if (wps_build_oob_dev_pw(msg, DEV_PW_NFC_CONNECTION_HANDOVER,
+ nfc_dh_pubkey, NULL, 0) ||
+ wps_build_uuid_e(msg, ctx->uuid) ||
+ wps_build_wfa_ext(msg, 0, NULL, 0)) {
+ wpabuf_free(msg);
+ return NULL;
+ }
+
+ WPA_PUT_BE16(len, wpabuf_len(msg) - 2);
+
+ return msg;
+}
+
+
+static int wps_build_ssid(struct wpabuf *msg, struct wps_context *wps)
+{
+ wpa_printf(MSG_DEBUG, "WPS: * SSID");
+ wpa_hexdump_ascii(MSG_DEBUG, "WPS: SSID in Connection Handover Select",
+ wps->ssid, wps->ssid_len);
+ wpabuf_put_be16(msg, ATTR_SSID);
+ wpabuf_put_be16(msg, wps->ssid_len);
+ wpabuf_put_data(msg, wps->ssid, wps->ssid_len);
+ return 0;
+}
+
+
+static int wps_build_ap_freq(struct wpabuf *msg, int freq)
+{
+ enum hostapd_hw_mode mode;
+ u8 channel, rf_band;
+ u16 ap_channel;
+
+ if (freq <= 0)
+ return 0;
+
+ mode = ieee80211_freq_to_chan(freq, &channel);
+ if (mode == NUM_HOSTAPD_MODES)
+ return 0; /* Unknown channel */
+
+ if (mode == HOSTAPD_MODE_IEEE80211G || mode == HOSTAPD_MODE_IEEE80211B)
+ rf_band = WPS_RF_24GHZ;
+ else if (mode == HOSTAPD_MODE_IEEE80211A)
+ rf_band = WPS_RF_50GHZ;
+ else if (mode == HOSTAPD_MODE_IEEE80211AD)
+ rf_band = WPS_RF_60GHZ;
+ else
+ return 0; /* Unknown band */
+ ap_channel = channel;
+
+ if (wps_build_rf_bands_attr(msg, rf_band) ||
+ wps_build_ap_channel(msg, ap_channel))
+ return -1;
+
+ return 0;
+}
+
+
+struct wpabuf * wps_build_nfc_handover_sel(struct wps_context *ctx,
+ struct wpabuf *nfc_dh_pubkey,
+ const u8 *bssid, int freq)
+{
+ struct wpabuf *msg;
+ void *len;
+
+ if (ctx == NULL)
+ return NULL;
+
+ wpa_printf(MSG_DEBUG, "WPS: Building attributes for NFC connection "
+ "handover select");
+
+ if (nfc_dh_pubkey == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS: No NFC OOB Device Password "
+ "configured");
+ return NULL;
+ }
+
+ msg = wpabuf_alloc(1000);
+ if (msg == NULL)
+ return msg;
+ len = wpabuf_put(msg, 2);
+
+ if (wps_build_oob_dev_pw(msg, DEV_PW_NFC_CONNECTION_HANDOVER,
+ nfc_dh_pubkey, NULL, 0) ||
+ wps_build_ssid(msg, ctx) ||
+ wps_build_ap_freq(msg, freq) ||
+ (bssid && wps_build_mac_addr(msg, bssid)) ||
+ wps_build_wfa_ext(msg, 0, NULL, 0)) {
+ wpabuf_free(msg);
+ return NULL;
+ }
+
+ WPA_PUT_BE16(len, wpabuf_len(msg) - 2);
+
+ return msg;
+}
+
+
+struct wpabuf * wps_build_nfc_handover_req_p2p(struct wps_context *ctx,
+ struct wpabuf *nfc_dh_pubkey)
+{
+ struct wpabuf *msg;
+
+ if (ctx == NULL)
+ return NULL;
+
+ wpa_printf(MSG_DEBUG, "WPS: Building attributes for NFC connection "
+ "handover request (P2P)");
+
+ if (nfc_dh_pubkey == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS: No NFC DH Public Key configured");
+ return NULL;
+ }
+
+ msg = wpabuf_alloc(1000);
+ if (msg == NULL)
+ return msg;
+
+ if (wps_build_manufacturer(&ctx->dev, msg) ||
+ wps_build_model_name(&ctx->dev, msg) ||
+ wps_build_model_number(&ctx->dev, msg) ||
+ wps_build_oob_dev_pw(msg, DEV_PW_NFC_CONNECTION_HANDOVER,
+ nfc_dh_pubkey, NULL, 0) ||
+ wps_build_rf_bands(&ctx->dev, msg, 0) ||
+ wps_build_serial_number(&ctx->dev, msg) ||
+ wps_build_uuid_e(msg, ctx->uuid) ||
+ wps_build_wfa_ext(msg, 0, NULL, 0)) {
+ wpabuf_free(msg);
+ return NULL;
+ }
+
+ return msg;
+}
+
+
+struct wpabuf * wps_build_nfc_handover_sel_p2p(struct wps_context *ctx,
+ int nfc_dev_pw_id,
+ struct wpabuf *nfc_dh_pubkey,
+ struct wpabuf *nfc_dev_pw)
+{
+ struct wpabuf *msg;
+ const u8 *dev_pw;
+ size_t dev_pw_len;
+
+ if (ctx == NULL)
+ return NULL;
+
+ wpa_printf(MSG_DEBUG, "WPS: Building attributes for NFC connection "
+ "handover select (P2P)");
+
+ if (nfc_dh_pubkey == NULL ||
+ (nfc_dev_pw_id != DEV_PW_NFC_CONNECTION_HANDOVER &&
+ nfc_dev_pw == NULL)) {
+ wpa_printf(MSG_DEBUG, "WPS: No NFC OOB Device Password "
+ "configured");
+ return NULL;
+ }
+
+ msg = wpabuf_alloc(1000);
+ if (msg == NULL)
+ return msg;
+
+ if (nfc_dev_pw) {
+ dev_pw = wpabuf_head(nfc_dev_pw);
+ dev_pw_len = wpabuf_len(nfc_dev_pw);
+ } else {
+ dev_pw = NULL;
+ dev_pw_len = 0;
+ }
+
+ if (wps_build_manufacturer(&ctx->dev, msg) ||
+ wps_build_model_name(&ctx->dev, msg) ||
+ wps_build_model_number(&ctx->dev, msg) ||
+ wps_build_oob_dev_pw(msg, nfc_dev_pw_id, nfc_dh_pubkey,
+ dev_pw, dev_pw_len) ||
+ wps_build_rf_bands(&ctx->dev, msg, 0) ||
+ wps_build_serial_number(&ctx->dev, msg) ||
+ wps_build_uuid_e(msg, ctx->uuid) ||
+ wps_build_wfa_ext(msg, 0, NULL, 0)) {
+ wpabuf_free(msg);
+ return NULL;
+ }
+
+ return msg;
+}
+
+#endif /* CONFIG_WPS_NFC */
diff --git a/freebsd/contrib/wpa/src/wps/wps_defs.h b/freebsd/contrib/wpa/src/wps/wps_defs.h
new file mode 100644
index 00000000..a23b979d
--- /dev/null
+++ b/freebsd/contrib/wpa/src/wps/wps_defs.h
@@ -0,0 +1,379 @@
+/*
+ * 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;
+extern int wps_corrupt_pkhash;
+#define WPS_VERSION wps_version_number
+
+#else /* CONFIG_WPS_TESTING */
+
+#define WPS_VERSION 0x20
+
+#endif /* CONFIG_WPS_TESTING */
+
+/* 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
+#define WPS_DEV_NAME_MAX_LEN 32
+#define WPS_MANUFACTURER_MAX_LEN 64
+#define WPS_MODEL_NAME_MAX_LEN 32
+#define WPS_MODEL_NUMBER_MAX_LEN 32
+#define WPS_SERIAL_NUMBER_MAX_LEN 32
+
+/* 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,
+ WFA_ELEM_REGISTRAR_CONFIGURATION_METHODS = 0x05
+};
+
+/* 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,
+ DEV_PW_NFC_CONNECTION_HANDOVER = 0x0007,
+ DEV_PW_P2PS_DEFAULT = 0x0008
+};
+
+/* 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_AUTH_OPEN 0x0001
+#define WPS_AUTH_WPAPSK 0x0002
+#define WPS_AUTH_SHARED 0x0004 /* deprecated */
+#define WPS_AUTH_WPA 0x0008
+#define WPS_AUTH_WPA2 0x0010
+#define WPS_AUTH_WPA2PSK 0x0020
+#define WPS_AUTH_TYPES (WPS_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 /* deprecated */
+#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,
+ WPS_CFG_60G_CHAN_NOT_SUPPORTED = 19,
+ WPS_CFG_PUBLIC_KEY_HASH_MISMATCH = 20
+};
+
+/* 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,
+ WPS_EI_AUTH_FAILURE,
+ NUM_WPS_EI_VALUES
+};
+
+/* RF Bands */
+#define WPS_RF_24GHZ 0x01
+#define WPS_RF_50GHZ 0x02
+#define WPS_RF_60GHZ 0x04
+
+/* 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
+#define WPS_CONFIG_VIRT_PUSHBUTTON 0x0280
+#define WPS_CONFIG_PHY_PUSHBUTTON 0x0480
+#define WPS_CONFIG_P2PS 0x1000
+#define WPS_CONFIG_VIRT_DISPLAY 0x2008
+#define WPS_CONFIG_PHY_DISPLAY 0x4008
+
+/* 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,
+ WPS_DEV_AUDIO = 11,
+};
+
+enum wps_dev_subcateg {
+ WPS_DEV_COMPUTER_PC = 1,
+ WPS_DEV_COMPUTER_SERVER = 2,
+ WPS_DEV_COMPUTER_MEDIA_CENTER = 3,
+ WPS_DEV_COMPUTER_ULTRA_MOBILE = 4,
+ WPS_DEV_COMPUTER_NOTEBOOK = 5,
+ WPS_DEV_COMPUTER_DESKTOP = 6,
+ WPS_DEV_COMPUTER_MID = 7,
+ WPS_DEV_COMPUTER_NETBOOK = 8,
+ WPS_DEV_COMPUTER_TABLET = 9,
+ WPS_DEV_INPUT_KEYBOARD = 1,
+ WPS_DEV_INPUT_MOUSE = 2,
+ WPS_DEV_INPUT_JOYSTICK = 3,
+ WPS_DEV_INPUT_TRACKBALL = 4,
+ WPS_DEV_INPUT_GAMING = 5,
+ WPS_DEV_INPUT_REMOTE = 6,
+ WPS_DEV_INPUT_TOUCHSCREEN = 7,
+ WPS_DEV_INPUT_BIOMETRIC_READER = 8,
+ WPS_DEV_INPUT_BARCODE_READER = 9,
+ WPS_DEV_PRINTER_PRINTER = 1,
+ WPS_DEV_PRINTER_SCANNER = 2,
+ WPS_DEV_PRINTER_FAX = 3,
+ WPS_DEV_PRINTER_COPIER = 4,
+ WPS_DEV_PRINTER_ALL_IN_ONE = 5,
+ WPS_DEV_CAMERA_DIGITAL_STILL_CAMERA = 1,
+ WPS_DEV_CAMERA_VIDEO = 2,
+ WPS_DEV_CAMERA_WEB = 3,
+ WPS_DEV_CAMERA_SECURITY = 4,
+ 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_NETWORK_INFRA_GATEWAY = 4,
+ WPS_DEV_NETWORK_INFRA_BRIDGE = 5,
+ WPS_DEV_DISPLAY_TV = 1,
+ WPS_DEV_DISPLAY_PICTURE_FRAME = 2,
+ WPS_DEV_DISPLAY_PROJECTOR = 3,
+ WPS_DEV_DISPLAY_MONITOR = 4,
+ WPS_DEV_MULTIMEDIA_DAR = 1,
+ WPS_DEV_MULTIMEDIA_PVR = 2,
+ WPS_DEV_MULTIMEDIA_MCX = 3,
+ WPS_DEV_MULTIMEDIA_SET_TOP_BOX = 4,
+ WPS_DEV_MULTIMEDIA_MEDIA_SERVER = 5,
+ WPS_DEV_MULTIMEDIA_PORTABLE_VIDEO_PLAYER = 6,
+ WPS_DEV_GAMING_XBOX = 1,
+ WPS_DEV_GAMING_XBOX360 = 2,
+ WPS_DEV_GAMING_PLAYSTATION = 3,
+ WPS_DEV_GAMING_GAME_CONSOLE = 4,
+ WPS_DEV_GAMING_PORTABLE_DEVICE = 5,
+ WPS_DEV_PHONE_WINDOWS_MOBILE = 1,
+ WPS_DEV_PHONE_SINGLE_MODE = 2,
+ WPS_DEV_PHONE_DUAL_MODE = 3,
+ WPS_DEV_PHONE_SP_SINGLE_MODE = 4,
+ WPS_DEV_PHONE_SP_DUAL_MODE = 5,
+ WPS_DEV_AUDIO_TUNER_RECV = 1,
+ WPS_DEV_AUDIO_SPEAKERS = 2,
+ WPS_DEV_AUDIO_PMP = 3,
+ WPS_DEV_AUDIO_HEADSET = 4,
+ WPS_DEV_AUDIO_HEADPHONES = 5,
+ WPS_DEV_AUDIO_MICROPHONE = 6,
+ WPS_DEV_AUDIO_HOME_THEATRE = 7,
+};
+
+
+/* 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
+
+#endif /* WPS_DEFS_H */
diff --git a/freebsd/contrib/wpa/src/wps/wps_dev_attr.c b/freebsd/contrib/wpa/src/wps/wps_dev_attr.c
new file mode 100644
index 00000000..1cc6cee8
--- /dev/null
+++ b/freebsd/contrib/wpa/src/wps/wps_dev_attr.c
@@ -0,0 +1,421 @@
+#include <machine/rtems-bsd-user-space.h>
+
+/*
+ * 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 "includes.h"
+
+#include "common.h"
+#include "wps_i.h"
+#include "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;
+}
+
+
+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,
+ u8 rf_band)
+{
+ return wps_build_rf_bands_attr(msg, rf_band ? rf_band : dev->rf_bands);
+}
+
+
+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 = dup_binstr(str, str_len);
+ if (dev->manufacturer == NULL)
+ return -1;
+
+ 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 = dup_binstr(str, str_len);
+ if (dev->model_name == NULL)
+ return -1;
+
+ 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 = dup_binstr(str, str_len);
+ if (dev->model_number == NULL)
+ return -1;
+
+ 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 = dup_binstr(str, str_len);
+ if (dev->serial_number == NULL)
+ return -1;
+
+ 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 = dup_binstr(str, str_len);
+ if (dev->device_name == NULL)
+ return -1;
+
+ return 0;
+}
+
+
+static int wps_process_primary_dev_type(struct wps_device_data *dev,
+ const u8 *dev_type)
+{
+#ifndef CONFIG_NO_STDOUT_DEBUG
+ char devtype[WPS_DEV_TYPE_BUFSIZE];
+#endif /* CONFIG_NO_STDOUT_DEBUG */
+
+ 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_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;
+}
diff --git a/freebsd/contrib/wpa/src/wps/wps_dev_attr.h b/freebsd/contrib/wpa/src/wps/wps_dev_attr.h
new file mode 100644
index 00000000..c9034add
--- /dev/null
+++ b/freebsd/contrib/wpa/src/wps/wps_dev_attr.h
@@ -0,0 +1,39 @@
+/*
+ * 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_serial_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,
+ u8 rf_band);
+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_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 */
diff --git a/freebsd/contrib/wpa/src/wps/wps_enrollee.c b/freebsd/contrib/wpa/src/wps/wps_enrollee.c
new file mode 100644
index 00000000..e2cfa571
--- /dev/null
+++ b/freebsd/contrib/wpa/src/wps/wps_enrollee.c
@@ -0,0 +1,1510 @@
+#include <machine/rtems-bsd-user-space.h>
+
+/*
+ * 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 "includes.h"
+
+#include "common.h"
+#include "crypto/crypto.h"
+#include "crypto/sha256.h"
+#include "crypto/random.h"
+#include "wps_i.h"
+#include "wps_dev_attr.h"
+
+
+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);
+ hmac_sha256_vector(wps->authkey, WPS_AUTHKEY_LEN, 4, addr, len, hash);
+ 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;
+ hmac_sha256_vector(wps->authkey, WPS_AUTHKEY_LEN, 4, addr, len, hash);
+ 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;
+ config_methods &= ~(WPS_CONFIG_VIRT_PUSHBUTTON |
+ WPS_CONFIG_PHY_PUSHBUTTON);
+ }
+
+ if (wps_build_version(msg) ||
+ wps_build_msg_type(msg, WPS_M1) ||
+ wps_build_uuid_e(msg, wps->uuid_e) ||
+ wps_build_mac_addr(msg, wps->mac_addr_e) ||
+ wps_build_enrollee_nonce(wps, msg) ||
+ wps_build_public_key(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(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->wps->rf_band_cb(wps->wps->cb_ctx)) ||
+ 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);
+
+ if (wps->wps->ap && random_pool_ready() != 1) {
+ wpa_printf(MSG_INFO,
+ "WPS: Not enough entropy in random pool to proceed - do not allow AP PIN to be used");
+ return NULL;
+ }
+
+ 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->ap_auth_type;
+
+ /*
+ * Work around issues with Windows 7 WPS implementation not liking
+ * multiple Authentication Type bits in M7 AP Settings attribute by
+ * showing only the most secure option from current configuration.
+ */
+ 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_AUTH_OPEN)
+ auth_type = WPS_AUTH_OPEN;
+
+ 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->ap_encr_type;
+
+ /*
+ * Work around issues with Windows 7 WPS implementation not liking
+ * multiple Encryption Type bits in M7 AP Settings attribute by
+ * showing only the most secure option from current configuration.
+ */
+ if (wps->wps->ap_auth_type & (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;
+ }
+
+ 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)
+{
+ if ((wps->wps->ap_auth_type & (WPS_AUTH_WPAPSK | WPS_AUTH_WPA2PSK)) &&
+ wps->wps->network_key_len == 0) {
+ char hex[65];
+ u8 psk[32];
+ /* Generate a random per-device PSK */
+ if (random_pool_ready() != 1 ||
+ random_get_bytes(psk, sizeof(psk)) < 0) {
+ wpa_printf(MSG_INFO,
+ "WPS: Could not generate random PSK");
+ return -1;
+ }
+ wpa_hexdump_key(MSG_DEBUG, "WPS: Generated per-device PSK",
+ psk, sizeof(psk));
+ wpa_printf(MSG_DEBUG, "WPS: * Network Key (len=%u)",
+ (unsigned int) wps->new_psk_len * 2);
+ wpa_snprintf_hex(hex, sizeof(hex), psk, sizeof(psk));
+ wpabuf_put_be16(msg, ATTR_NETWORK_KEY);
+ wpabuf_put_be16(msg, sizeof(psk) * 2);
+ wpabuf_put_data(msg, hex, sizeof(psk) * 2);
+ if (wps->wps->registrar) {
+ wps_cb_new_psk(wps->wps->registrar,
+ wps->peer_dev.mac_addr,
+ wps->p2p_dev_addr, psk, sizeof(psk));
+ }
+ return 0;
+ }
+
+ wpa_printf(MSG_DEBUG, "WPS: * Network Key (len=%u)",
+ (unsigned int) wps->wps->network_key_len);
+ 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)
+{
+ const u8 *start, *end;
+ int ret;
+
+ 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;
+ }
+
+ wpa_printf(MSG_DEBUG, "WPS: * AP Settings based on current configuration");
+ start = wpabuf_put(plain, 0);
+ ret = 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);
+ end = wpabuf_put(plain, 0);
+
+ wpa_hexdump_key(MSG_DEBUG, "WPS: Plaintext AP Settings",
+ start, end - start);
+
+ return ret;
+}
+
+
+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->peer_dev.mac_addr);
+ 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;
+ }
+
+ if (wps->peer_pubkey_hash_set) {
+ u8 hash[WPS_HASH_LEN];
+ sha256_vector(1, &pk, &pk_len, hash);
+ if (os_memcmp_const(hash, wps->peer_pubkey_hash,
+ WPS_OOB_PUBKEY_HASH_LEN) != 0) {
+ wpa_printf(MSG_ERROR, "WPS: Public Key hash mismatch");
+ wpa_hexdump(MSG_DEBUG, "WPS: Received public key",
+ pk, pk_len);
+ wpa_hexdump(MSG_DEBUG, "WPS: Calculated public key "
+ "hash", hash, WPS_OOB_PUBKEY_HASH_LEN);
+ wpa_hexdump(MSG_DEBUG, "WPS: Expected public key hash",
+ wps->peer_pubkey_hash,
+ WPS_OOB_PUBKEY_HASH_LEN);
+ wps->config_error = WPS_CFG_PUBLIC_KEY_HASH_MISMATCH;
+ 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;
+
+ if (wps_derive_keys(wps) < 0)
+ return -1;
+
+ 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);
+ hmac_sha256_vector(wps->authkey, WPS_AUTHKEY_LEN, 4, addr, len, hash);
+
+ if (os_memcmp_const(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, wps->peer_dev.mac_addr);
+ 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);
+ hmac_sha256_vector(wps->authkey, WPS_AUTHKEY_LEN, 4, addr, len, hash);
+
+ if (os_memcmp_const(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, wps->peer_dev.mac_addr);
+ 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");
+ 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))
+ return -1;
+
+ 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");
+ return -1;
+ }
+#endif /* CONFIG_WPS_STRICT */
+ }
+
+ 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;
+ return -2;
+ }
+
+ wpa_printf(MSG_INFO, "WPS: Reject Credential due to "
+ "invalid encr_type 0x%x", wps->cred.encr_type);
+ return -1;
+ }
+
+ 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;
+ }
+
+ return ret;
+}
+
+
+static int wps_process_creds(struct wps_data *wps, const u8 *cred[],
+ u16 cred_len[], unsigned int 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;
+
+ if (!wps->wps->ap)
+ return 0;
+
+ if (wps_process_ap_settings(attr, &cred) < 0)
+ return -1;
+
+ 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");
+ return -1;
+ }
+#endif /* CONFIG_WPS_STRICT */
+ }
+
+ 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;
+ return -1;
+ }
+
+ wpa_printf(MSG_INFO, "WPS: Reject new AP settings due to "
+ "invalid encr_type 0x%x", cred.encr_type);
+ return -1;
+ }
+
+#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;
+ return -1;
+ }
+ }
+#endif /* CONFIG_WPS_STRICT */
+
+ 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;
+ }
+
+ if (wps->wps->cred_cb) {
+ cred.cred_attr = wpabuf_head(attrs);
+ cred.cred_attr_len = wpabuf_len(attrs);
+ ret = wps->wps->cred_cb(wps->wps->cb_ctx, &cred);
+ }
+
+ return ret;
+}
+
+
+static int wps_process_dev_pw_id(struct wps_data *wps, const u8 *dev_pw_id)
+{
+ u16 id;
+
+ if (dev_pw_id == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS: Device Password ID");
+ return -1;
+ }
+
+ id = WPA_GET_BE16(dev_pw_id);
+ if (wps->dev_pw_id == id) {
+ wpa_printf(MSG_DEBUG, "WPS: Device Password ID %u", id);
+ return 0;
+ }
+
+#ifdef CONFIG_P2P
+ if ((id == DEV_PW_DEFAULT &&
+ wps->dev_pw_id == DEV_PW_REGISTRAR_SPECIFIED) ||
+ (id == DEV_PW_REGISTRAR_SPECIFIED &&
+ wps->dev_pw_id == DEV_PW_DEFAULT)) {
+ /*
+ * Common P2P use cases indicate whether the PIN is from the
+ * client or GO using Device Password Id in M1/M2 in a way that
+ * does not look fully compliant with WSC specification. Anyway,
+ * this is deployed and needs to be allowed, so ignore changes
+ * between Registrar-Specified and Default PIN.
+ */
+ wpa_printf(MSG_DEBUG, "WPS: Allow PIN Device Password ID "
+ "change");
+ return 0;
+ }
+#endif /* CONFIG_P2P */
+
+ wpa_printf(MSG_DEBUG, "WPS: Registrar trying to change Device Password "
+ "ID from %u to %u", wps->dev_pw_id, id);
+
+ if (wps->dev_pw_id == DEV_PW_PUSHBUTTON && id == DEV_PW_DEFAULT) {
+ wpa_printf(MSG_DEBUG,
+ "WPS: Workaround - ignore PBC-to-PIN change");
+ return 0;
+ }
+
+ if (wps->alt_dev_password && wps->alt_dev_pw_id == id) {
+ wpa_printf(MSG_DEBUG, "WPS: Found a matching Device Password");
+ bin_clear_free(wps->dev_password, wps->dev_password_len);
+ wps->dev_pw_id = wps->alt_dev_pw_id;
+ wps->dev_password = wps->alt_dev_password;
+ wps->dev_password_len = wps->alt_dev_password_len;
+ wps->alt_dev_password = NULL;
+ wps->alt_dev_password_len = 0;
+ return 0;
+ }
+
+ return -1;
+}
+
+
+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_process_dev_pw_id(wps, attr->dev_password_id)) {
+ 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;
+ }
+
+#ifdef CONFIG_WPS_NFC
+ if (wps->peer_pubkey_hash_set) {
+ struct wpabuf *decrypted;
+ struct wps_parse_attr eattr;
+
+ 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;
+ }
+
+ 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)) {
+ wpabuf_free(decrypted);
+ wps->state = SEND_WSC_NACK;
+ return WPS_CONTINUE;
+ }
+ wpabuf_free(decrypted);
+
+ wps->state = WPS_MSG_DONE;
+ return WPS_CONTINUE;
+ }
+#endif /* CONFIG_WPS_NFC */
+
+ 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;
+
+ wpa_printf(MSG_DEBUG, "WPS: Received M4");
+
+ if (wps->state != RECV_M4) {
+ wpa_printf(MSG_DEBUG, "WPS: Unexpected state (%d) for "
+ "receiving M4", wps->state);
+ wps->state = SEND_WSC_NACK;
+ return WPS_CONTINUE;
+ }
+
+ 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;
+ 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_m4_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_r_snonce1(wps, eattr.r_snonce1)) {
+ wpabuf_free(decrypted);
+ wps->state = SEND_WSC_NACK;
+ return WPS_CONTINUE;
+ }
+ wpabuf_free(decrypted);
+
+ wps->state = SEND_M5;
+ return WPS_CONTINUE;
+}
+
+
+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;
+
+ wpa_printf(MSG_DEBUG, "WPS: Received M6");
+
+ if (wps->state != RECV_M6) {
+ wpa_printf(MSG_DEBUG, "WPS: Unexpected state (%d) for "
+ "receiving M6", wps->state);
+ wps->state = SEND_WSC_NACK;
+ return WPS_CONTINUE;
+ }
+
+ if (wps_process_enrollee_nonce(wps, attr->enrollee_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_m6_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_r_snonce2(wps, eattr.r_snonce2)) {
+ wpabuf_free(decrypted);
+ wps->state = SEND_WSC_NACK;
+ return WPS_CONTINUE;
+ }
+ 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;
+ return WPS_CONTINUE;
+}
+
+
+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;
+
+ wpa_printf(MSG_DEBUG, "WPS: Received M8");
+
+ if (wps->state != RECV_M8) {
+ wpa_printf(MSG_DEBUG, "WPS: Unexpected state (%d) for "
+ "receiving M8", wps->state);
+ wps->state = SEND_WSC_NACK;
+ return WPS_CONTINUE;
+ }
+
+ if (wps_process_enrollee_nonce(wps, attr->enrollee_nonce) ||
+ wps_process_authenticator(wps, attr->authenticator, msg)) {
+ wps->state = SEND_WSC_NACK;
+ return WPS_CONTINUE;
+ }
+
+ 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;
+ 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_m8_encr(decrypted, wps->wps->ap,
+ 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_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;
+ return WPS_CONTINUE;
+ }
+ wpabuf_free(decrypted);
+
+ wps->state = WPS_MSG_DONE;
+ 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.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.msg_type == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS: No Message Type attribute");
+ wps->state = SEND_WSC_NACK;
+ return WPS_CONTINUE;
+ }
+
+ switch (*attr.msg_type) {
+ case WPS_M2:
+ if (wps_validate_m2(msg) < 0)
+ return WPS_FAILURE;
+ ret = wps_process_m2(wps, msg, &attr);
+ break;
+ case WPS_M2D:
+ if (wps_validate_m2d(msg) < 0)
+ return WPS_FAILURE;
+ ret = wps_process_m2d(wps, &attr);
+ break;
+ case WPS_M4:
+ if (wps_validate_m4(msg) < 0)
+ return WPS_FAILURE;
+ 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,
+ wps->peer_dev.mac_addr);
+ break;
+ case WPS_M6:
+ if (wps_validate_m6(msg) < 0)
+ return WPS_FAILURE;
+ 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,
+ wps->peer_dev.mac_addr);
+ break;
+ case WPS_M8:
+ if (wps_validate_m8(msg) < 0)
+ return WPS_FAILURE;
+ 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,
+ wps->peer_dev.mac_addr);
+ break;
+ default:
+ wpa_printf(MSG_DEBUG, "WPS: Unsupported Message Type %d",
+ *attr.msg_type);
+ return WPS_FAILURE;
+ }
+
+ /*
+ * 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);
+ }
+
+ 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;
+ }
+
+ 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_ACK && wps->wps->ap) {
+ wpa_printf(MSG_DEBUG, "WPS: External Registrar registration "
+ "completed successfully");
+ wps_success_event(wps->wps, wps->peer_dev.mac_addr);
+ wps->state = WPS_FINISHED;
+ return WPS_DONE;
+ }
+
+ 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;
+ u16 config_error;
+
+ wpa_printf(MSG_DEBUG, "WPS: Received 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;
+ }
+
+ 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);
+ 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");
+ 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);
+ 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: 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, wps->peer_dev.mac_addr);
+ break;
+ case RECV_M6:
+ wps_fail_event(wps->wps, WPS_M5, config_error,
+ wps->error_indication, wps->peer_dev.mac_addr);
+ break;
+ case RECV_M8:
+ wps_fail_event(wps->wps, WPS_M7, config_error,
+ wps->error_indication, wps->peer_dev.mac_addr);
+ break;
+ default:
+ break;
+ }
+
+ /* Followed by NACK if Enrollee is Supplicant or EAP-Failure if
+ * Enrollee is Authenticator */
+ wps->state = SEND_WSC_NACK;
+
+ return WPS_FAILURE;
+}
+
+
+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_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;
+ }
+}
diff --git a/freebsd/contrib/wpa/src/wps/wps_er.h b/freebsd/contrib/wpa/src/wps/wps_er.h
new file mode 100644
index 00000000..4b48ff6a
--- /dev/null
+++ b/freebsd/contrib/wpa/src/wps/wps_er.h
@@ -0,0 +1,112 @@
+/*
+ * Wi-Fi Protected Setup - External Registrar
+ * 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.
+ */
+
+#ifndef WPS_ER_H
+#define WPS_ER_H
+
+#include "utils/list.h"
+
+struct wps_er_sta {
+ struct dl_list list;
+ struct wps_er_ap *ap;
+ u8 addr[ETH_ALEN];
+ u16 config_methods;
+ u8 uuid[WPS_UUID_LEN];
+ u8 pri_dev_type[8];
+ u16 dev_passwd_id;
+ int m1_received;
+ char *manufacturer;
+ char *model_name;
+ char *model_number;
+ char *serial_number;
+ char *dev_name;
+ struct wps_data *wps;
+ struct http_client *http;
+ struct wps_credential *cred;
+};
+
+struct wps_er_ap {
+ struct dl_list list;
+ struct wps_er *er;
+ struct dl_list sta; /* list of STAs/Enrollees using this AP */
+ struct in_addr addr;
+ char *location;
+ struct http_client *http;
+ struct wps_data *wps;
+
+ u8 uuid[WPS_UUID_LEN];
+ u8 pri_dev_type[8];
+ u8 wps_state;
+ u8 mac_addr[ETH_ALEN];
+ char *friendly_name;
+ char *manufacturer;
+ char *manufacturer_url;
+ char *model_description;
+ char *model_name;
+ char *model_number;
+ char *model_url;
+ char *serial_number;
+ char *udn;
+ char *upc;
+
+ char *scpd_url;
+ char *control_url;
+ char *event_sub_url;
+
+ int subscribed;
+ u8 sid[WPS_UUID_LEN];
+ unsigned int id;
+
+ struct wps_credential *ap_settings;
+
+ void (*m1_handler)(struct wps_er_ap *ap, struct wpabuf *m1);
+};
+
+struct wps_er_ap_settings {
+ struct dl_list list;
+ u8 uuid[WPS_UUID_LEN];
+ struct wps_credential ap_settings;
+};
+
+struct wps_er {
+ struct wps_context *wps;
+ char ifname[17];
+ int forced_ifname;
+ u8 mac_addr[ETH_ALEN]; /* mac addr of network i.f. we use */
+ char *ip_addr_text; /* IP address of network i.f. we use */
+ unsigned ip_addr; /* IP address of network i.f. we use (host order) */
+ int multicast_sd;
+ int ssdp_sd;
+ struct dl_list ap;
+ struct dl_list ap_unsubscribing;
+ struct dl_list ap_settings;
+ struct http_server *http_srv;
+ int http_port;
+ unsigned int next_ap_id;
+ unsigned int event_id;
+ int deinitializing;
+ void (*deinit_done_cb)(void *ctx);
+ void *deinit_done_ctx;
+ struct in_addr filter_addr;
+ int skip_set_sel_reg;
+ const u8 *set_sel_reg_uuid_filter;
+};
+
+
+/* wps_er.c */
+void wps_er_ap_add(struct wps_er *er, const u8 *uuid, struct in_addr *addr,
+ const char *location, int max_age);
+void wps_er_ap_remove(struct wps_er *er, struct in_addr *addr);
+int wps_er_ap_cache_settings(struct wps_er *er, struct in_addr *addr);
+
+/* wps_er_ssdp.c */
+int wps_er_ssdp_init(struct wps_er *er);
+void wps_er_ssdp_deinit(struct wps_er *er);
+void wps_er_send_ssdp_msearch(struct wps_er *er);
+
+#endif /* WPS_ER_H */
diff --git a/freebsd/contrib/wpa/src/wps/wps_i.h b/freebsd/contrib/wpa/src/wps/wps_i.h
new file mode 100644
index 00000000..f7154f87
--- /dev/null
+++ b/freebsd/contrib/wpa/src/wps/wps_i.h
@@ -0,0 +1,218 @@
+/*
+ * 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"
+
+struct wps_nfc_pw_token;
+
+/**
+ * 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;
+ u8 *alt_dev_password;
+ size_t alt_dev_password_len;
+ u16 alt_dev_pw_id;
+
+ u8 peer_pubkey_hash[WPS_OOB_PUBKEY_HASH_LEN];
+ int peer_pubkey_hash_set;
+
+ /**
+ * 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;
+
+ struct wps_nfc_pw_token *nfc_pw_token;
+};
+
+
+/* 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, const u8 *mac_addr);
+void wps_success_event(struct wps_context *wps, const u8 *mac_addr);
+void wps_pwd_auth_fail_event(struct wps_context *wps, int enrollee, int part,
+ const u8 *mac_addr);
+void wps_pbc_overlap_event(struct wps_context *wps);
+void wps_pbc_timeout_event(struct wps_context *wps);
+void wps_pbc_active_event(struct wps_context *wps);
+void wps_pbc_disable_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);
+
+/* wps_attr_build.c */
+int wps_build_public_key(struct wps_data *wps, struct wpabuf *msg);
+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);
+int wps_build_mac_addr(struct wpabuf *msg, const u8 *addr);
+int wps_build_rf_bands_attr(struct wpabuf *msg, u8 rf_bands);
+int wps_build_ap_channel(struct wpabuf *msg, u16 ap_channel);
+
+/* 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,
+ u16 dev_pw_id);
+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);
+void wps_registrar_remove_nfc_pw_token(struct wps_registrar *reg,
+ struct wps_nfc_pw_token *token);
+int wps_cb_new_psk(struct wps_registrar *reg, const u8 *mac_addr,
+ const u8 *p2p_dev_addr, const u8 *psk, size_t psk_len);
+
+#endif /* WPS_I_H */
diff --git a/freebsd/contrib/wpa/src/wps/wps_registrar.c b/freebsd/contrib/wpa/src/wps/wps_registrar.c
new file mode 100644
index 00000000..261b4557
--- /dev/null
+++ b/freebsd/contrib/wpa/src/wps/wps_registrar.c
@@ -0,0 +1,3672 @@
+#include <machine/rtems-bsd-user-space.h>
+
+/*
+ * Wi-Fi Protected Setup - Registrar
+ * Copyright (c) 2008-2013, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+
+#include "utils/common.h"
+#include "utils/base64.h"
+#include "utils/eloop.h"
+#include "utils/uuid.h"
+#include "utils/list.h"
+#include "crypto/crypto.h"
+#include "crypto/sha256.h"
+#include "crypto/random.h"
+#include "common/ieee802_11_defs.h"
+#include "wps_i.h"
+#include "wps_dev_attr.h"
+#include "wps_upnp.h"
+#include "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];
+ unsigned int peer_pk_hash_known:1;
+ u16 pw_id;
+ u8 dev_pw[WPS_OOB_DEVICE_PASSWORD_LEN * 2 + 1];
+ size_t dev_pw_len;
+ int pk_hash_provided_oob; /* whether own PK hash was provided OOB */
+};
+
+
+static void wps_remove_nfc_pw_token(struct wps_nfc_pw_token *token)
+{
+ dl_list_del(&token->list);
+ bin_clear_free(token, sizeof(*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 */
+
+
+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_reltime expiration;
+ u8 enrollee_addr[ETH_ALEN];
+};
+
+
+static void wps_free_pin(struct wps_uuid_pin *pin)
+{
+ bin_clear_free(pin->pin, pin->pin_len);
+ 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);
+}
+
+
+struct wps_pbc_session {
+ struct wps_pbc_session *next;
+ u8 addr[ETH_ALEN];
+ u8 uuid_e[WPS_UUID_LEN];
+ struct os_reltime 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 *p2p_dev_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;
+ int force_per_enrollee_psk;
+
+ 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];
+#ifdef WPS_WORKAROUNDS
+ struct os_reltime pbc_ignore_start;
+#endif /* WPS_WORKAROUNDS */
+};
+
+
+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_remove_pin(struct wps_registrar *reg,
+ struct wps_uuid_pin *pin);
+
+
+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 = 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_reltime now;
+
+ os_get_reltime(&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 = 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 (os_reltime_expired(&now, &pbc->timestamp,
+ 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_reltime now;
+
+ os_get_reltime(&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 (os_reltime_expired(&now, &pbc->timestamp,
+ 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;
+ 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;
+ }
+}
+
+
+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;
+ methods &= ~(WPS_CONFIG_VIRT_PUSHBUTTON |
+ WPS_CONFIG_PHY_PUSHBUTTON);
+ 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;
+ methods &= ~(WPS_CONFIG_VIRT_PUSHBUTTON |
+ WPS_CONFIG_PHY_PUSHBUTTON);
+ 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;
+
+ while (*count < WPS_MAX_AUTHORIZED_MACS) {
+ if (is_zero_ether_addr(reg->authorized_macs_union[*count]))
+ break;
+ (*count)++;
+ }
+
+ 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 = os_zalloc(sizeof(*reg));
+ if (reg == NULL)
+ return NULL;
+
+ dl_list_init(&reg->pins);
+ dl_list_init(&reg->nfc_pw_tokens);
+ reg->wps = wps;
+ reg->new_psk_cb = cfg->new_psk_cb;
+ reg->set_ie_cb = cfg->set_ie_cb;
+ reg->pin_needed_cb = cfg->pin_needed_cb;
+ 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;
+ reg->force_per_enrollee_psk = cfg->force_per_enrollee_psk;
+
+ if (wps_set_ie(reg)) {
+ wps_registrar_deinit(reg);
+ return NULL;
+ }
+
+ return reg;
+}
+
+
+void wps_registrar_flush(struct wps_registrar *reg)
+{
+ if (reg == NULL)
+ return;
+ wps_free_pins(&reg->pins);
+ wps_free_nfc_pw_tokens(&reg->nfc_pw_tokens, 0);
+ wps_free_pbc_sessions(reg->pbc_sessions);
+ reg->pbc_sessions = NULL;
+ wps_free_devices(reg->devices);
+ reg->devices = NULL;
+#ifdef WPS_WORKAROUNDS
+ reg->pbc_ignore_start.sec = 0;
+#endif /* WPS_WORKAROUNDS */
+}
+
+
+/**
+ * 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);
+ wps_registrar_flush(reg);
+ wpabuf_free(reg->extra_cred);
+ os_free(reg);
+}
+
+
+static void wps_registrar_invalidate_unused(struct wps_registrar *reg)
+{
+ struct wps_uuid_pin *pin;
+
+ dl_list_for_each(pin, &reg->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 = 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 = 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_reltime(&p->expiration);
+ p->expiration.sec += timeout;
+ }
+
+ if (p->wildcard_uuid)
+ wps_registrar_invalidate_unused(reg);
+
+ dl_list_add(&reg->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, 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);
+
+ 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, 0);
+}
+
+
+static void wps_registrar_expire_pins(struct wps_registrar *reg)
+{
+ struct wps_uuid_pin *pin, *prev;
+ struct os_reltime now;
+
+ os_get_reltime(&now);
+ dl_list_for_each_safe(pin, prev, &reg->pins, struct wps_uuid_pin, list)
+ {
+ if ((pin->flags & PIN_EXPIRES) &&
+ os_reltime_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, &reg->pins, struct wps_uuid_pin, list)
+ {
+ if (dev_pw && pin->pin &&
+ (dev_pw_len != pin->pin_len ||
+ os_memcmp_const(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, &reg->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, &reg->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, &reg->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, &reg->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;
+}
+
+
+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, 0);
+}
+
+
+static void wps_registrar_pbc_timeout(void *eloop_ctx, void *timeout_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, 0);
+
+ wps_pbc_active_event(reg->wps);
+ eloop_cancel_timeout(wps_registrar_set_selected_timeout, reg, NULL);
+ eloop_cancel_timeout(wps_registrar_pbc_timeout, reg, NULL);
+ eloop_register_timeout(WPS_PBC_WALK_TIME, 0, wps_registrar_pbc_timeout,
+ reg, NULL);
+ return 0;
+}
+
+
+static void wps_registrar_pbc_completed(struct wps_registrar *reg)
+{
+ wpa_printf(MSG_DEBUG, "WPS: PBC completed - stopping PBC mode");
+ eloop_cancel_timeout(wps_registrar_pbc_timeout, reg, NULL);
+ wps_registrar_stop_pbc(reg);
+ wps_pbc_disable_event(reg->wps);
+}
+
+
+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, 0);
+}
+
+
+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);
+#ifdef WPS_WORKAROUNDS
+ os_get_reltime(&registrar->pbc_ignore_start);
+#endif /* WPS_WORKAROUNDS */
+ os_memcpy(registrar->pbc_ignore_uuid, uuid_e, WPS_UUID_LEN);
+ } else {
+ wps_registrar_pin_completed(registrar);
+ }
+
+ 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);
+ }
+}
+
+
+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);
+ return 1;
+ } else if (reg->selected_registrar) {
+ /* 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;
+ }
+ 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 = 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_reltime now, dur;
+ os_get_reltime(&now);
+ os_reltime_sub(&now, &reg->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);
+ }
+}
+
+
+int wps_cb_new_psk(struct wps_registrar *reg, const u8 *mac_addr,
+ const u8 *p2p_dev_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, p2p_dev_addr, psk,
+ psk_len);
+}
+
+
+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);
+}
+
+
+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;
+ methods &= ~(WPS_CONFIG_VIRT_PUSHBUTTON |
+ WPS_CONFIG_PHY_PUSHBUTTON);
+ 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(&reg->wps->dev, beacon, 0)) ||
+ wps_build_wfa_ext(beacon, 0, auth_macs, count) ||
+ wps_build_vendor_ext(&reg->wps->dev, beacon)) {
+ wpabuf_free(beacon);
+ wpabuf_free(probe);
+ return -1;
+ }
+
+#ifdef CONFIG_P2P
+ if (wps_build_dev_name(&reg->wps->dev, beacon) ||
+ wps_build_primary_dev_type(&reg->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(&reg->wps->dev, probe) ||
+ wps_build_probe_config_methods(reg, probe) ||
+ (reg->dualband && wps_build_rf_bands(&reg->wps->dev, probe, 0)) ||
+ wps_build_wfa_ext(probe, 0, auth_macs, count) ||
+ wps_build_vendor_ext(&reg->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;
+
+ bin_clear_free(wps->dev_password, wps->dev_password_len);
+ 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) {
+ if (wps->nfc_pw_token->pw_id == DEV_PW_NFC_CONNECTION_HANDOVER)
+ {
+ wpa_printf(MSG_DEBUG, "WPS: Using NFC connection "
+ "handover and abbreviated WPS handshake "
+ "without Device Password");
+ return 0;
+ }
+ 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;
+ } else if (wps->dev_pw_id >= 0x10 &&
+ wps->wps->ap_nfc_dev_pw_id == wps->dev_pw_id &&
+ wps->wps->ap_nfc_dev_pw) {
+ wpa_printf(MSG_DEBUG, "WPS: Use OOB Device Password from own NFC Password Token");
+ pin = wpabuf_head(wps->wps->ap_nfc_dev_pw);
+ pin_len = wpabuf_len(wps->wps->ap_nfc_dev_pw);
+#endif /* CONFIG_WPS_NFC */
+ } else {
+ pin = wps_registrar_get_pin(wps->wps->registrar, wps->uuid_e,
+ &pin_len);
+ if (pin && wps->dev_pw_id >= 0x10) {
+ wpa_printf(MSG_DEBUG, "WPS: No match for OOB Device "
+ "Password ID, but PIN found");
+ /*
+ * See whether Enrollee is willing to use PIN instead.
+ */
+ wps->dev_pw_id = DEV_PW_DEFAULT;
+ }
+ }
+ if (pin == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS: No Device Password available for "
+ "the Enrollee (context %p registrar %p)",
+ wps->wps, wps->wps->registrar);
+ wps_cb_pin_needed(wps->wps->registrar, wps->uuid_e,
+ &wps->peer_dev);
+ return -1;
+ }
+
+ wps->dev_password = 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);
+ hmac_sha256_vector(wps->authkey, WPS_AUTHKEY_LEN, 4, addr, len, hash);
+ 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;
+ hmac_sha256_vector(wps->authkey, WPS_AUTHKEY_LEN, 4, addr, len, hash);
+ 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_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_mac_addr(msg, cred->mac_addr))
+ 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_AUTH_OPEN)
+ wps->auth_type = WPS_AUTH_OPEN;
+ 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_NONE)
+ wps->encr_type = WPS_ENCR_NONE;
+#ifdef CONFIG_TESTING_OPTIONS
+ else if (wps->encr_type & WPS_ENCR_WEP)
+ wps->encr_type = WPS_ENCR_WEP;
+#endif /* CONFIG_TESTING_OPTIONS */
+ 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_pool_ready() != 1 ||
+ random_get_bytes(r, sizeof(r)) < 0) {
+ wpa_printf(MSG_INFO,
+ "WPS: Could not generate random PSK");
+ 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->wps->registrar->force_per_enrollee_psk &&
+ 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->registrar->force_per_enrollee_psk &&
+ 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 = os_malloc(wps->new_psk_len);
+ if (wps->new_psk == NULL)
+ return -1;
+ if (random_pool_ready() != 1 ||
+ random_get_bytes(wps->new_psk, wps->new_psk_len) < 0) {
+ wpa_printf(MSG_INFO,
+ "WPS: Could not generate random PSK");
+ 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;
+ int config_in_m2 = 0;
+
+ 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_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->wps->rf_band_cb(wps->wps->cb_ctx)) ||
+ 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)) {
+ wpabuf_free(msg);
+ return NULL;
+ }
+
+#ifdef CONFIG_WPS_NFC
+ if (wps->nfc_pw_token && wps->nfc_pw_token->pk_hash_provided_oob &&
+ wps->nfc_pw_token->pw_id == DEV_PW_NFC_CONNECTION_HANDOVER) {
+ /*
+ * Use abbreviated handshake since public key hash allowed
+ * Enrollee to validate our public key similarly to how Enrollee
+ * public key was validated. There is no need to validate Device
+ * Password in this case.
+ */
+ struct wpabuf *plain = wpabuf_alloc(500);
+ if (plain == NULL ||
+ wps_build_cred(wps, plain) ||
+ wps_build_key_wrap_auth(wps, plain) ||
+ wps_build_encr_settings(wps, msg, plain)) {
+ wpabuf_free(msg);
+ wpabuf_free(plain);
+ return NULL;
+ }
+ wpabuf_free(plain);
+ config_in_m2 = 1;
+ }
+#endif /* CONFIG_WPS_NFC */
+
+ if (wps_build_authenticator(wps, msg)) {
+ wpabuf_free(msg);
+ return NULL;
+ }
+
+ wps->int_reg = 1;
+ wps->state = config_in_m2 ? RECV_DONE : 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->wps->rf_band_cb(wps->wps->cb_ctx)) ||
+ 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);
+ hmac_sha256_vector(wps->authkey, WPS_AUTHKEY_LEN, 4, addr, len, hash);
+
+ if (os_memcmp_const(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, wps->mac_addr_e);
+ 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);
+ hmac_sha256_vector(wps->authkey, WPS_AUTHKEY_LEN, 4, addr, len, hash);
+
+ if (os_memcmp_const(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");
+ wps_registrar_invalidate_pin(wps->wps->registrar, wps->uuid_e);
+ wps->config_error = WPS_CFG_DEV_PASSWORD_AUTH_FAILURE;
+ wps_pwd_auth_fail_event(wps->wps, 0, 2, wps->mac_addr_e);
+ return -1;
+ }
+
+ wpa_printf(MSG_DEBUG, "WPS: Enrollee proved knowledge of the second "
+ "half of the device password");
+ wps->wps_pin_revealed = 0;
+ 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);
+
+ 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_P2PS_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 &&
+#ifdef CONFIG_WPS_NFC
+ wps->dev_pw_id != DEV_PW_NFC_CONNECTION_HANDOVER &&
+#endif /* CONFIG_WPS_NFC */
+ (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 ||
+ wps->dev_pw_id == DEV_PW_NFC_CONNECTION_HANDOVER) {
+ struct wps_nfc_pw_token *token;
+ const u8 *addr[1];
+ u8 hash[WPS_HASH_LEN];
+
+ wpa_printf(MSG_DEBUG, "WPS: Searching for NFC token match for id=%d (ctx %p registrar %p)",
+ wps->dev_pw_id, wps->wps, wps->wps->registrar);
+ token = wps_get_nfc_pw_token(
+ &wps->wps->registrar->nfc_pw_tokens, wps->dev_pw_id);
+ if (token && token->peer_pk_hash_known) {
+ size_t len;
+
+ 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;
+ len = attr->public_key_len;
+ sha256_vector(1, addr, &len, hash);
+ if (os_memcmp_const(hash,
+ wps->nfc_pw_token->pubkey_hash,
+ WPS_OOB_PUBKEY_HASH_LEN) != 0) {
+ wpa_printf(MSG_ERROR, "WPS: Public Key hash "
+ "mismatch");
+ wps->state = SEND_M2D;
+ wps->config_error =
+ WPS_CFG_PUBLIC_KEY_HASH_MISMATCH;
+ return WPS_CONTINUE;
+ }
+ } else if (token) {
+ wpa_printf(MSG_DEBUG, "WPS: Found matching NFC "
+ "Password Token (no peer PK hash)");
+ wps->nfc_pw_token = token;
+ } else if (wps->dev_pw_id >= 0x10 &&
+ wps->wps->ap_nfc_dev_pw_id == wps->dev_pw_id &&
+ wps->wps->ap_nfc_dev_pw) {
+ wpa_printf(MSG_DEBUG, "WPS: Found match with own NFC Password Token");
+ }
+ }
+#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->mac_addr_e);
+ 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 {
+ /*
+ * 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);
+
+ 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, wps->mac_addr_e);
+ 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, wps->mac_addr_e);
+ 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, wps->mac_addr_e);
+ 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, wps->mac_addr_e);
+ break;
+ case RECV_M5:
+ wps_fail_event(wps->wps, WPS_M4, config_error,
+ wps->error_indication, wps->mac_addr_e);
+ break;
+ case RECV_M7:
+ wps_fail_event(wps->wps, WPS_M6, config_error,
+ wps->error_indication, wps->mac_addr_e);
+ break;
+ case RECV_DONE:
+ wps_fail_event(wps->wps, WPS_M8, config_error,
+ wps->error_indication, wps->mac_addr_e);
+ 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)) {
+ 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;
+ if (wps->wps->rf_band_cb(wps->wps->cb_ctx) == WPS_RF_60GHZ) {
+ cred.auth_type = WPS_AUTH_WPA2PSK;
+ cred.encr_type = WPS_ENCR_AES;
+ } else {
+ 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->p2p_dev_addr, 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);
+#ifdef WPS_WORKAROUNDS
+ os_get_reltime(&wps->wps->registrar->pbc_ignore_start);
+#endif /* WPS_WORKAROUNDS */
+ os_memcpy(wps->wps->registrar->pbc_ignore_uuid, wps->uuid_e,
+ WPS_UUID_LEN);
+ } else {
+ wps_registrar_pin_completed(wps->wps->registrar);
+ }
+ /* TODO: maintain AuthorizedMACs somewhere separately for each ER and
+ * merge them into APs own list.. */
+
+ wps_success_event(wps->wps, wps->mac_addr_e);
+
+ 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, wps->mac_addr_e);
+ }
+ 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 wps_registrar_set_selected_timeout(void *eloop_ctx,
+ void *timeout_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, 0);
+}
+
+
+#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, &reg->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,
+ u16 dev_pw_id)
+{
+ 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;
+ methods &= ~(WPS_CONFIG_VIRT_PUSHBUTTON |
+ WPS_CONFIG_PHY_PUSHBUTTON);
+ if (reg->pbc) {
+ reg->sel_reg_dev_password_id_override =
+ DEV_PW_PUSHBUTTON;
+ wps_set_pushbutton(&methods, reg->wps->config_methods);
+ } else if (dev_pw_id)
+ reg->sel_reg_dev_password_id_override = dev_pw_id;
+ 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");
+
+ 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 = os_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 (os_snprintf_error(buflen - len, ret))
+ return len;
+ len += ret;
+
+ return len;
+}
+
+
+int wps_registrar_config_ap(struct wps_registrar *reg,
+ struct wps_credential *cred)
+{
+ wpa_printf(MSG_DEBUG, "WPS: encr_type=0x%x", cred->encr_type);
+ 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;
+ }
+
+ 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,
+ int pk_hash_provided_oob)
+{
+ struct wps_nfc_pw_token *token;
+
+ if (dev_pw_len > WPS_OOB_DEVICE_PASSWORD_LEN)
+ return -1;
+
+ if (pw_id == DEV_PW_NFC_CONNECTION_HANDOVER &&
+ (pubkey_hash == NULL || !pk_hash_provided_oob)) {
+ wpa_printf(MSG_DEBUG, "WPS: Unexpected NFC Password Token "
+ "addition - missing public key hash");
+ return -1;
+ }
+
+ wps_free_nfc_pw_tokens(&reg->nfc_pw_tokens, pw_id);
+
+ token = os_zalloc(sizeof(*token));
+ if (token == NULL)
+ return -1;
+
+ token->peer_pk_hash_known = pubkey_hash != NULL;
+ if (pubkey_hash)
+ os_memcpy(token->pubkey_hash, pubkey_hash,
+ WPS_OOB_PUBKEY_HASH_LEN);
+ token->pw_id = pw_id;
+ token->pk_hash_provided_oob = pk_hash_provided_oob;
+ if (dev_pw) {
+ wpa_snprintf_hex_uppercase((char *) token->dev_pw,
+ sizeof(token->dev_pw),
+ dev_pw, dev_pw_len);
+ token->dev_pw_len = dev_pw_len * 2;
+ }
+
+ dl_list_add(&reg->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, pw_id);
+ 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);
+
+ wpa_printf(MSG_DEBUG, "WPS: Added NFC Device Password %u to Registrar",
+ pw_id);
+
+ 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 ||
+ 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, 0);
+}
+
+
+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, 0);
+
+ /*
+ * Free the NFC password token if it was used only for a single protocol
+ * run. The static handover case uses the same password token multiple
+ * times, so do not free that case here.
+ */
+ if (token->peer_pk_hash_known)
+ os_free(token);
+}
+
+#endif /* CONFIG_WPS_NFC */
diff --git a/freebsd/contrib/wpa/src/wps/wps_upnp.c b/freebsd/contrib/wpa/src/wps/wps_upnp.c
new file mode 100644
index 00000000..d66ca357
--- /dev/null
+++ b/freebsd/contrib/wpa/src/wps/wps_upnp.c
@@ -0,0 +1,1214 @@
+#include <machine/rtems-bsd-user-space.h>
+
+/*
+ * UPnP WPS Device
+ * Copyright (c) 2000-2003 Intel Corporation
+ * Copyright (c) 2006-2007 Sony Corporation
+ * Copyright (c) 2008-2009 Atheros Communications
+ * Copyright (c) 2009-2010, Jouni Malinen <j@w1.fi>
+ *
+ * See below for more details on licensing and code history.
+ */
+
+/*
+ * This has been greatly stripped down from the original file
+ * (upnp_wps_device.c) by Ted Merrill, Atheros Communications
+ * in order to eliminate use of the bulky libupnp library etc.
+ *
+ * History:
+ * upnp_wps_device.c is/was a shim layer between wps_opt_upnp.c and
+ * the libupnp library.
+ * The layering (by Sony) was well done; only a very minor modification
+ * to API of upnp_wps_device.c was required.
+ * libupnp was found to be undesirable because:
+ * -- It consumed too much code and data space
+ * -- It uses multiple threads, making debugging more difficult
+ * and possibly reducing reliability.
+ * -- It uses static variables and only supports one instance.
+ * The shim and libupnp are here replaced by special code written
+ * specifically for the needs of hostapd.
+ * Various shortcuts can and are taken to keep the code size small.
+ * Generally, execution time is not as crucial.
+ *
+ * BUGS:
+ * -- UPnP requires that we be able to resolve domain names.
+ * While uncommon, if we have to do it then it will stall the entire
+ * hostapd program, which is bad.
+ * This is because we use the standard linux getaddrinfo() function
+ * which is syncronous.
+ * An asyncronous solution would be to use the free "ares" library.
+ * -- Does not have a robust output buffering scheme. Uses a single
+ * fixed size output buffer per TCP/HTTP connection, with possible (although
+ * unlikely) possibility of overflow and likely excessive use of RAM.
+ * A better solution would be to write the HTTP output as a buffered stream,
+ * using chunking: (handle header specially, then) generate data with
+ * a printf-like function into a buffer, catching buffer full condition,
+ * then send it out surrounded by http chunking.
+ * -- There is some code that could be separated out into the common
+ * library to be shared with wpa_supplicant.
+ * -- Needs renaming with module prefix to avoid polluting the debugger
+ * namespace and causing possible collisions with other static fncs
+ * and structure declarations when using the debugger.
+ * -- The http error code generation is pretty bogus, hopefully no one cares.
+ *
+ * Author: Ted Merrill, Atheros Communications, based upon earlier work
+ * as explained above and below.
+ *
+ * Copyright:
+ * Copyright 2008 Atheros Communications.
+ *
+ * The original header (of upnp_wps_device.c) reads:
+ *
+ * Copyright (c) 2006-2007 Sony Corporation. All Rights Reserved.
+ *
+ * File Name: upnp_wps_device.c
+ * Description: EAP-WPS UPnP device source
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Sony Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * Portions from Intel libupnp files, e.g. genlib/net/http/httpreadwrite.c
+ * typical header:
+ *
+ * Copyright (c) 2000-2003 Intel Corporation
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * * Neither name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL INTEL OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+/*
+ * Overview of WPS over UPnP:
+ *
+ * UPnP is a protocol that allows devices to discover each other and control
+ * each other. In UPnP terminology, a device is either a "device" (a server
+ * that provides information about itself and allows itself to be controlled)
+ * or a "control point" (a client that controls "devices") or possibly both.
+ * This file implements a UPnP "device".
+ *
+ * For us, we use mostly basic UPnP discovery, but the control part of interest
+ * is WPS carried via UPnP messages. There is quite a bit of basic UPnP
+ * discovery to do before we can get to WPS, however.
+ *
+ * UPnP discovery begins with "devices" send out multicast UDP packets to a
+ * certain fixed multicast IP address and port, and "control points" sending
+ * out other such UDP packets.
+ *
+ * The packets sent by devices are NOTIFY packets (not to be confused with TCP
+ * NOTIFY packets that are used later) and those sent by control points are
+ * M-SEARCH packets. These packets contain a simple HTTP style header. The
+ * packets are sent redundantly to get around packet loss. Devices respond to
+ * M-SEARCH packets with HTTP-like UDP packets containing HTTP/1.1 200 OK
+ * messages, which give similar information as the UDP NOTIFY packets.
+ *
+ * The above UDP packets advertise the (arbitrary) TCP ports that the
+ * respective parties will listen to. The control point can then do a HTTP
+ * SUBSCRIBE (something like an HTTP PUT) after which the device can do a
+ * separate HTTP NOTIFY (also like an HTTP PUT) to do event messaging.
+ *
+ * The control point will also do HTTP GET of the "device file" listed in the
+ * original UDP information from the device (see UPNP_WPS_DEVICE_XML_FILE
+ * data), and based on this will do additional GETs... HTTP POSTs are done to
+ * cause an action.
+ *
+ * Beyond some basic information in HTTP headers, additional information is in
+ * the HTTP bodies, in a format set by the SOAP and XML standards, a markup
+ * language related to HTML used for web pages. This language is intended to
+ * provide the ultimate in self-documentation by providing a universal
+ * namespace based on pseudo-URLs called URIs. Note that although a URI looks
+ * like a URL (a web address), they are never accessed as such but are used
+ * only as identifiers.
+ *
+ * The POST of a GetDeviceInfo gets information similar to what might be
+ * obtained from a probe request or response on Wi-Fi. WPS messages M1-M8
+ * are passed via a POST of a PutMessage; the M1-M8 WPS messages are converted
+ * to a bin64 ascii representation for encapsulation. When proxying messages,
+ * WLANEvent and PutWLANResponse are used.
+ *
+ * This of course glosses over a lot of details.
+ */
+
+#include "includes.h"
+
+#include <time.h>
+#include <net/if.h>
+#include <netdb.h>
+#include <sys/ioctl.h>
+
+#include "common.h"
+#include "uuid.h"
+#include "base64.h"
+#include "wps.h"
+#include "wps_i.h"
+#include "wps_upnp.h"
+#include "wps_upnp_i.h"
+
+
+/*
+ * UPnP allows a client ("control point") to send a server like us ("device")
+ * a domain name for registration, and we are supposed to resolve it. This is
+ * bad because, using the standard Linux library, we will stall the entire
+ * hostapd waiting for resolution.
+ *
+ * The "correct" solution would be to use an event driven library for domain
+ * name resolution such as "ares". However, this would increase code size
+ * further. Since it is unlikely that we'll actually see such domain names, we
+ * can just refuse to accept them.
+ */
+#define NO_DOMAIN_NAME_RESOLUTION 1 /* 1 to allow only dotted ip addresses */
+
+
+/*
+ * UPnP does not scale well. If we were in a room with thousands of control
+ * points then potentially we could be expected to handle subscriptions for
+ * each of them, which would exhaust our memory. So we must set a limit. In
+ * practice we are unlikely to see more than one or two.
+ */
+#define MAX_SUBSCRIPTIONS 4 /* how many subscribing clients we handle */
+#define MAX_ADDR_PER_SUBSCRIPTION 8
+
+/* Maximum number of Probe Request events per second */
+#define MAX_EVENTS_PER_SEC 5
+
+
+static struct upnp_wps_device_sm *shared_upnp_device = NULL;
+
+
+/* Write the current date/time per RFC */
+void format_date(struct wpabuf *buf)
+{
+ const char *weekday_str = "Sun\0Mon\0Tue\0Wed\0Thu\0Fri\0Sat";
+ const char *month_str = "Jan\0Feb\0Mar\0Apr\0May\0Jun\0"
+ "Jul\0Aug\0Sep\0Oct\0Nov\0Dec";
+ struct tm *date;
+ time_t t;
+
+ t = time(NULL);
+ date = gmtime(&t);
+ if (date == NULL)
+ return;
+ wpabuf_printf(buf, "%s, %02d %s %d %02d:%02d:%02d GMT",
+ &weekday_str[date->tm_wday * 4], date->tm_mday,
+ &month_str[date->tm_mon * 4], date->tm_year + 1900,
+ date->tm_hour, date->tm_min, date->tm_sec);
+}
+
+
+/***************************************************************************
+ * UUIDs (unique identifiers)
+ *
+ * These are supposed to be unique in all the world.
+ * Sometimes permanent ones are used, sometimes temporary ones
+ * based on random numbers... there are different rules for valid content
+ * of different types.
+ * Each uuid is 16 bytes long.
+ **************************************************************************/
+
+/* uuid_make -- construct a random UUID
+ * The UPnP documents don't seem to offer any guidelines as to which method to
+ * use for constructing UUIDs for subscriptions. Presumably any method from
+ * rfc4122 is good enough; I've chosen random number method.
+ */
+static int uuid_make(u8 uuid[UUID_LEN])
+{
+ if (os_get_random(uuid, UUID_LEN) < 0)
+ return -1;
+
+ /* Replace certain bits as specified in rfc4122 or X.667 */
+ uuid[6] &= 0x0f; uuid[6] |= (4 << 4); /* version 4 == random gen */
+ uuid[8] &= 0x3f; uuid[8] |= 0x80;
+
+ return 0;
+}
+
+
+/*
+ * Subscriber address handling.
+ * Since a subscriber may have an arbitrary number of addresses, we have to
+ * add a bunch of code to handle them.
+ *
+ * Addresses are passed in text, and MAY be domain names instead of the (usual
+ * and expected) dotted IP addresses. Resolving domain names consumes a lot of
+ * resources. Worse, we are currently using the standard Linux getaddrinfo()
+ * which will block the entire program until complete or timeout! The proper
+ * solution would be to use the "ares" library or similar with more state
+ * machine steps etc. or just disable domain name resolution by setting
+ * NO_DOMAIN_NAME_RESOLUTION to 1 at top of this file.
+ */
+
+/* subscr_addr_delete -- delete single unlinked subscriber address
+ * (be sure to unlink first if need be)
+ */
+void subscr_addr_delete(struct subscr_addr *a)
+{
+ /*
+ * Note: do NOT free domain_and_port or path because they point to
+ * memory within the allocation of "a".
+ */
+ os_free(a);
+}
+
+
+/* subscr_addr_free_all -- unlink and delete list of subscriber addresses. */
+static void subscr_addr_free_all(struct subscription *s)
+{
+ struct subscr_addr *a, *tmp;
+ dl_list_for_each_safe(a, tmp, &s->addr_list, struct subscr_addr, list)
+ {
+ dl_list_del(&a->list);
+ subscr_addr_delete(a);
+ }
+}
+
+
+/* subscr_addr_add_url -- add address(es) for one url to subscription */
+static void subscr_addr_add_url(struct subscription *s, const char *url,
+ size_t url_len)
+{
+ int alloc_len;
+ char *scratch_mem = NULL;
+ char *mem;
+ char *host;
+ char *delim;
+ char *path;
+ int port = 80; /* port to send to (default is port 80) */
+ struct addrinfo hints;
+ struct addrinfo *result = NULL;
+ struct addrinfo *rp;
+ int rerr;
+ size_t host_len, path_len;
+
+ /* url MUST begin with http: */
+ if (url_len < 7 || os_strncasecmp(url, "http://", 7))
+ goto fail;
+ url += 7;
+ url_len -= 7;
+
+ /* Make a copy of the string to allow modification during parsing */
+ scratch_mem = dup_binstr(url, url_len);
+ if (scratch_mem == NULL)
+ goto fail;
+ wpa_printf(MSG_DEBUG, "WPS UPnP: Adding URL '%s'", scratch_mem);
+ host = scratch_mem;
+ path = os_strchr(host, '/');
+ if (path)
+ *path++ = '\0'; /* null terminate host */
+
+ /* Process and remove optional port component */
+ delim = os_strchr(host, ':');
+ if (delim) {
+ *delim = '\0'; /* null terminate host name for now */
+ if (isdigit(delim[1]))
+ port = atol(delim + 1);
+ }
+
+ /*
+ * getaddrinfo does the right thing with dotted decimal notations, or
+ * will resolve domain names. Resolving domain names will unfortunately
+ * hang the entire program until it is resolved or it times out
+ * internal to getaddrinfo; fortunately we think that the use of actual
+ * domain names (vs. dotted decimal notations) should be uncommon.
+ */
+ os_memset(&hints, 0, sizeof(struct addrinfo));
+ hints.ai_family = AF_INET; /* IPv4 */
+ hints.ai_socktype = SOCK_STREAM;
+#if NO_DOMAIN_NAME_RESOLUTION
+ /* Suppress domain name resolutions that would halt
+ * the program for periods of time
+ */
+ hints.ai_flags = AI_NUMERICHOST;
+#else
+ /* Allow domain name resolution. */
+ hints.ai_flags = 0;
+#endif
+ hints.ai_protocol = 0; /* Any protocol? */
+ rerr = getaddrinfo(host, NULL /* fill in port ourselves */,
+ &hints, &result);
+ if (rerr) {
+ wpa_printf(MSG_INFO, "WPS UPnP: Resolve error %d (%s) on: %s",
+ rerr, gai_strerror(rerr), host);
+ goto fail;
+ }
+
+ if (delim)
+ *delim = ':'; /* Restore port */
+
+ host_len = os_strlen(host);
+ path_len = path ? os_strlen(path) : 0;
+ alloc_len = host_len + 1 + 1 + path_len + 1;
+
+ for (rp = result; rp; rp = rp->ai_next) {
+ struct subscr_addr *a;
+
+ /* Limit no. of address to avoid denial of service attack */
+ if (dl_list_len(&s->addr_list) >= MAX_ADDR_PER_SUBSCRIPTION) {
+ wpa_printf(MSG_INFO, "WPS UPnP: subscr_addr_add_url: "
+ "Ignoring excessive addresses");
+ break;
+ }
+
+ a = os_zalloc(sizeof(*a) + alloc_len);
+ if (a == NULL)
+ break;
+ mem = (char *) (a + 1);
+ a->domain_and_port = mem;
+ os_memcpy(mem, host, host_len);
+ mem += host_len + 1;
+ a->path = mem;
+ if (path == NULL || path[0] != '/')
+ *mem++ = '/';
+ if (path)
+ os_memcpy(mem, path, path_len);
+ os_memcpy(&a->saddr, rp->ai_addr, sizeof(a->saddr));
+ a->saddr.sin_port = htons(port);
+
+ dl_list_add(&s->addr_list, &a->list);
+ }
+
+fail:
+ if (result)
+ freeaddrinfo(result);
+ os_free(scratch_mem);
+}
+
+
+/* subscr_addr_list_create -- create list from urls in string.
+ * Each url is enclosed by angle brackets.
+ */
+static void subscr_addr_list_create(struct subscription *s,
+ const char *url_list)
+{
+ const char *end;
+ wpa_printf(MSG_DEBUG, "WPS UPnP: Parsing URL list '%s'", url_list);
+ for (;;) {
+ while (*url_list == ' ' || *url_list == '\t')
+ url_list++;
+ if (*url_list != '<')
+ break;
+ url_list++;
+ end = os_strchr(url_list, '>');
+ if (end == NULL)
+ break;
+ subscr_addr_add_url(s, url_list, end - url_list);
+ url_list = end + 1;
+ }
+}
+
+
+static void wpabuf_put_property(struct wpabuf *buf, const char *name,
+ const char *value)
+{
+ wpabuf_put_str(buf, "<e:property>");
+ wpabuf_printf(buf, "<%s>", name);
+ if (value)
+ wpabuf_put_str(buf, value);
+ wpabuf_printf(buf, "</%s>", name);
+ wpabuf_put_str(buf, "</e:property>\n");
+}
+
+
+/**
+ * upnp_wps_device_send_event - Queue event messages for subscribers
+ * @sm: WPS UPnP state machine from upnp_wps_device_init()
+ *
+ * This function queues the last WLANEvent to be sent for all currently
+ * subscribed UPnP control points. sm->wlanevent must have been set with the
+ * encoded data before calling this function.
+ */
+static void upnp_wps_device_send_event(struct upnp_wps_device_sm *sm)
+{
+ /* Enqueue event message for all subscribers */
+ struct wpabuf *buf; /* holds event message */
+ int buf_size = 0;
+ struct subscription *s, *tmp;
+ /* Actually, utf-8 is the default, but it doesn't hurt to specify it */
+ const char *format_head =
+ "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
+ "<e:propertyset xmlns:e=\"urn:schemas-upnp-org:event-1-0\">\n";
+ const char *format_tail = "</e:propertyset>\n";
+ struct os_reltime now;
+
+ if (dl_list_empty(&sm->subscriptions)) {
+ /* optimize */
+ return;
+ }
+
+ if (os_get_reltime(&now) == 0) {
+ if (now.sec != sm->last_event_sec) {
+ sm->last_event_sec = now.sec;
+ sm->num_events_in_sec = 1;
+ } else {
+ sm->num_events_in_sec++;
+ /*
+ * In theory, this should apply to all WLANEvent
+ * notifications, but EAP messages are of much higher
+ * priority and Probe Request notifications should not
+ * be allowed to drop EAP messages, so only throttle
+ * Probe Request notifications.
+ */
+ if (sm->num_events_in_sec > MAX_EVENTS_PER_SEC &&
+ sm->wlanevent_type ==
+ UPNP_WPS_WLANEVENT_TYPE_PROBE) {
+ wpa_printf(MSG_DEBUG, "WPS UPnP: Throttle "
+ "event notifications (%u seen "
+ "during one second)",
+ sm->num_events_in_sec);
+ return;
+ }
+ }
+ }
+
+ /* Determine buffer size needed first */
+ buf_size += os_strlen(format_head);
+ buf_size += 50 + 2 * os_strlen("WLANEvent");
+ if (sm->wlanevent)
+ buf_size += os_strlen(sm->wlanevent);
+ buf_size += os_strlen(format_tail);
+
+ buf = wpabuf_alloc(buf_size);
+ if (buf == NULL)
+ return;
+ wpabuf_put_str(buf, format_head);
+ wpabuf_put_property(buf, "WLANEvent", sm->wlanevent);
+ wpabuf_put_str(buf, format_tail);
+
+ wpa_printf(MSG_MSGDUMP, "WPS UPnP: WLANEvent message:\n%s",
+ (char *) wpabuf_head(buf));
+
+ dl_list_for_each_safe(s, tmp, &sm->subscriptions, struct subscription,
+ list) {
+ event_add(s, buf,
+ sm->wlanevent_type == UPNP_WPS_WLANEVENT_TYPE_PROBE);
+ }
+
+ wpabuf_free(buf);
+}
+
+
+/*
+ * Event subscription (subscriber machines register with us to receive event
+ * messages).
+ * This is the result of an incoming HTTP over TCP SUBSCRIBE request.
+ */
+
+/* subscription_destroy -- destroy an unlinked subscription
+ * Be sure to unlink first if necessary.
+ */
+void subscription_destroy(struct subscription *s)
+{
+ struct upnp_wps_device_interface *iface;
+ wpa_printf(MSG_DEBUG, "WPS UPnP: Destroy subscription %p", s);
+ subscr_addr_free_all(s);
+ event_delete_all(s);
+ dl_list_for_each(iface, &s->sm->interfaces,
+ struct upnp_wps_device_interface, list)
+ upnp_er_remove_notification(iface->wps->registrar, s);
+ os_free(s);
+}
+
+
+/* subscription_list_age -- remove expired subscriptions */
+static void subscription_list_age(struct upnp_wps_device_sm *sm, time_t now)
+{
+ struct subscription *s, *tmp;
+ dl_list_for_each_safe(s, tmp, &sm->subscriptions,
+ struct subscription, list) {
+ if (s->timeout_time > now)
+ break;
+ wpa_printf(MSG_DEBUG, "WPS UPnP: Removing aged subscription");
+ dl_list_del(&s->list);
+ subscription_destroy(s);
+ }
+}
+
+
+/* subscription_find -- return existing subscription matching uuid, if any
+ * returns NULL if not found
+ */
+struct subscription * subscription_find(struct upnp_wps_device_sm *sm,
+ const u8 uuid[UUID_LEN])
+{
+ struct subscription *s;
+ dl_list_for_each(s, &sm->subscriptions, struct subscription, list) {
+ if (os_memcmp(s->uuid, uuid, UUID_LEN) == 0)
+ return s; /* Found match */
+ }
+ return NULL;
+}
+
+
+static struct wpabuf * build_fake_wsc_ack(void)
+{
+ struct wpabuf *msg = wpabuf_alloc(100);
+ if (msg == NULL)
+ return NULL;
+ wpabuf_put_u8(msg, UPNP_WPS_WLANEVENT_TYPE_EAP);
+ wpabuf_put_str(msg, "00:00:00:00:00:00");
+ if (wps_build_version(msg) ||
+ wps_build_msg_type(msg, WPS_WSC_ACK)) {
+ wpabuf_free(msg);
+ return NULL;
+ }
+ /* Enrollee Nonce */
+ wpabuf_put_be16(msg, ATTR_ENROLLEE_NONCE);
+ wpabuf_put_be16(msg, WPS_NONCE_LEN);
+ wpabuf_put(msg, WPS_NONCE_LEN);
+ /* Registrar Nonce */
+ wpabuf_put_be16(msg, ATTR_REGISTRAR_NONCE);
+ wpabuf_put_be16(msg, WPS_NONCE_LEN);
+ wpabuf_put(msg, WPS_NONCE_LEN);
+ if (wps_build_wfa_ext(msg, 0, NULL, 0)) {
+ wpabuf_free(msg);
+ return NULL;
+ }
+ return msg;
+}
+
+
+/* subscription_first_event -- send format/queue event that is automatically
+ * sent on a new subscription.
+ */
+static int subscription_first_event(struct subscription *s)
+{
+ /*
+ * Actually, utf-8 is the default, but it doesn't hurt to specify it.
+ *
+ * APStatus is apparently a bit set,
+ * 0x1 = configuration change (but is always set?)
+ * 0x10 = ap is locked
+ *
+ * Per UPnP spec, we send out the last value of each variable, even
+ * for WLANEvent, whatever it was.
+ */
+ char *wlan_event;
+ struct wpabuf *buf;
+ int ap_status = 1; /* TODO: add 0x10 if access point is locked */
+ const char *head =
+ "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
+ "<e:propertyset xmlns:e=\"urn:schemas-upnp-org:event-1-0\">\n";
+ const char *tail = "</e:propertyset>\n";
+ char txt[10];
+ int ret;
+
+ if (s->sm->wlanevent == NULL) {
+ /*
+ * There has been no events before the subscription. However,
+ * UPnP device architecture specification requires all the
+ * evented variables to be included, so generate a dummy event
+ * for this particular case using a WSC_ACK and all-zeros
+ * nonces. The ER (UPnP control point) will ignore this, but at
+ * least it will learn that WLANEvent variable will be used in
+ * event notifications in the future.
+ */
+ struct wpabuf *msg;
+ wpa_printf(MSG_DEBUG, "WPS UPnP: Use a fake WSC_ACK as the "
+ "initial WLANEvent");
+ msg = build_fake_wsc_ack();
+ if (msg) {
+ s->sm->wlanevent = (char *)
+ base64_encode(wpabuf_head(msg),
+ wpabuf_len(msg), NULL);
+ wpabuf_free(msg);
+ }
+ }
+
+ wlan_event = s->sm->wlanevent;
+ if (wlan_event == NULL || *wlan_event == '\0') {
+ wpa_printf(MSG_DEBUG, "WPS UPnP: WLANEvent not known for "
+ "initial event message");
+ wlan_event = "";
+ }
+ buf = wpabuf_alloc(500 + os_strlen(wlan_event));
+ if (buf == NULL)
+ return -1;
+
+ wpabuf_put_str(buf, head);
+ wpabuf_put_property(buf, "STAStatus", "1");
+ os_snprintf(txt, sizeof(txt), "%d", ap_status);
+ wpabuf_put_property(buf, "APStatus", txt);
+ if (*wlan_event)
+ wpabuf_put_property(buf, "WLANEvent", wlan_event);
+ wpabuf_put_str(buf, tail);
+
+ ret = event_add(s, buf, 0);
+ if (ret) {
+ wpabuf_free(buf);
+ return ret;
+ }
+ wpabuf_free(buf);
+
+ return 0;
+}
+
+
+/**
+ * subscription_start - Remember a UPnP control point to send events to.
+ * @sm: WPS UPnP state machine from upnp_wps_device_init()
+ * @callback_urls: Callback URLs
+ * Returns: %NULL on error, or pointer to new subscription structure.
+ */
+struct subscription * subscription_start(struct upnp_wps_device_sm *sm,
+ const char *callback_urls)
+{
+ struct subscription *s;
+ time_t now = time(NULL);
+ time_t expire = now + UPNP_SUBSCRIBE_SEC;
+ char str[80];
+
+ /* Get rid of expired subscriptions so we have room */
+ subscription_list_age(sm, now);
+
+ /* If too many subscriptions, remove oldest */
+ if (dl_list_len(&sm->subscriptions) >= MAX_SUBSCRIPTIONS) {
+ s = dl_list_first(&sm->subscriptions, struct subscription,
+ list);
+ if (s) {
+ wpa_printf(MSG_INFO,
+ "WPS UPnP: Too many subscriptions, trashing oldest");
+ dl_list_del(&s->list);
+ subscription_destroy(s);
+ }
+ }
+
+ s = os_zalloc(sizeof(*s));
+ if (s == NULL)
+ return NULL;
+ dl_list_init(&s->addr_list);
+ dl_list_init(&s->event_queue);
+
+ s->sm = sm;
+ s->timeout_time = expire;
+ if (uuid_make(s->uuid) < 0) {
+ subscription_destroy(s);
+ return NULL;
+ }
+ subscr_addr_list_create(s, callback_urls);
+ if (dl_list_empty(&s->addr_list)) {
+ wpa_printf(MSG_DEBUG, "WPS UPnP: No valid callback URLs in "
+ "'%s' - drop subscription", callback_urls);
+ subscription_destroy(s);
+ return NULL;
+ }
+
+ /* Add to end of list, since it has the highest expiration time */
+ dl_list_add_tail(&sm->subscriptions, &s->list);
+ /* Queue up immediate event message (our last event)
+ * as required by UPnP spec.
+ */
+ if (subscription_first_event(s)) {
+ wpa_printf(MSG_INFO, "WPS UPnP: Dropping subscriber due to "
+ "event backlog");
+ dl_list_del(&s->list);
+ subscription_destroy(s);
+ return NULL;
+ }
+ uuid_bin2str(s->uuid, str, sizeof(str));
+ wpa_printf(MSG_DEBUG,
+ "WPS UPnP: Subscription %p (SID %s) started with %s",
+ s, str, callback_urls);
+ /* Schedule sending this */
+ event_send_all_later(sm);
+ return s;
+}
+
+
+/* subscription_renew -- find subscription and reset timeout */
+struct subscription * subscription_renew(struct upnp_wps_device_sm *sm,
+ const u8 uuid[UUID_LEN])
+{
+ time_t now = time(NULL);
+ time_t expire = now + UPNP_SUBSCRIBE_SEC;
+ struct subscription *s = subscription_find(sm, uuid);
+ if (s == NULL)
+ return NULL;
+ wpa_printf(MSG_DEBUG, "WPS UPnP: Subscription renewed");
+ dl_list_del(&s->list);
+ s->timeout_time = expire;
+ /* add back to end of list, since it now has highest expiry */
+ dl_list_add_tail(&sm->subscriptions, &s->list);
+ return s;
+}
+
+
+/**
+ * upnp_wps_device_send_wlan_event - Event notification
+ * @sm: WPS UPnP state machine from upnp_wps_device_init()
+ * @from_mac_addr: Source (Enrollee) MAC address for the event
+ * @ev_type: Event type
+ * @msg: Event data
+ * Returns: 0 on success, -1 on failure
+ *
+ * Tell external Registrars (UPnP control points) that something happened. In
+ * particular, events include WPS messages from clients that are proxied to
+ * external Registrars.
+ */
+int upnp_wps_device_send_wlan_event(struct upnp_wps_device_sm *sm,
+ const u8 from_mac_addr[ETH_ALEN],
+ enum upnp_wps_wlanevent_type ev_type,
+ const struct wpabuf *msg)
+{
+ int ret = -1;
+ char type[2];
+ const u8 *mac = from_mac_addr;
+ char mac_text[18];
+ u8 *raw = NULL;
+ size_t raw_len;
+ char *val;
+ size_t val_len;
+ int pos = 0;
+
+ if (!sm)
+ goto fail;
+
+ os_snprintf(type, sizeof(type), "%1u", ev_type);
+
+ raw_len = 1 + 17 + (msg ? wpabuf_len(msg) : 0);
+ raw = os_zalloc(raw_len);
+ if (!raw)
+ goto fail;
+
+ *(raw + pos) = (u8) ev_type;
+ pos += 1;
+ os_snprintf(mac_text, sizeof(mac_text), MACSTR, MAC2STR(mac));
+ wpa_printf(MSG_DEBUG, "WPS UPnP: Proxying WLANEvent from %s",
+ mac_text);
+ os_memcpy(raw + pos, mac_text, 17);
+ pos += 17;
+ if (msg) {
+ os_memcpy(raw + pos, wpabuf_head(msg), wpabuf_len(msg));
+ pos += wpabuf_len(msg);
+ }
+ raw_len = pos;
+
+ val = (char *) base64_encode(raw, raw_len, &val_len);
+ if (val == NULL)
+ goto fail;
+
+ os_free(sm->wlanevent);
+ sm->wlanevent = val;
+ sm->wlanevent_type = ev_type;
+ upnp_wps_device_send_event(sm);
+
+ ret = 0;
+
+fail:
+ os_free(raw);
+
+ return ret;
+}
+
+
+#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
+#include <sys/sysctl.h>
+#include <net/route.h>
+#include <net/if_dl.h>
+
+static int eth_get(const char *device, u8 ea[ETH_ALEN])
+{
+ struct if_msghdr *ifm;
+ struct sockaddr_dl *sdl;
+ u_char *p, *buf;
+ size_t len;
+ int mib[] = { CTL_NET, AF_ROUTE, 0, AF_LINK, NET_RT_IFLIST, 0 };
+
+ if (sysctl(mib, 6, NULL, &len, NULL, 0) < 0)
+ return -1;
+ if ((buf = os_malloc(len)) == NULL)
+ return -1;
+ if (sysctl(mib, 6, buf, &len, NULL, 0) < 0) {
+ os_free(buf);
+ return -1;
+ }
+ for (p = buf; p < buf + len; p += ifm->ifm_msglen) {
+ ifm = (struct if_msghdr *)p;
+ sdl = (struct sockaddr_dl *)(ifm + 1);
+ if (ifm->ifm_type != RTM_IFINFO ||
+ (ifm->ifm_addrs & RTA_IFP) == 0)
+ continue;
+ if (sdl->sdl_family != AF_LINK || sdl->sdl_nlen == 0 ||
+ os_memcmp(sdl->sdl_data, device, sdl->sdl_nlen) != 0)
+ continue;
+ os_memcpy(ea, LLADDR(sdl), sdl->sdl_alen);
+ break;
+ }
+ os_free(buf);
+
+ if (p >= buf + len) {
+ errno = ESRCH;
+ return -1;
+ }
+ return 0;
+}
+#endif /* __FreeBSD__ */
+
+
+/**
+ * get_netif_info - Get hw and IP addresses for network device
+ * @net_if: Selected network interface name
+ * @ip_addr: Buffer for returning IP address in network byte order
+ * @ip_addr_text: Buffer for returning a pointer to allocated IP address text
+ * @mac: Buffer for returning MAC address
+ * Returns: 0 on success, -1 on failure
+ */
+int get_netif_info(const char *net_if, unsigned *ip_addr, char **ip_addr_text,
+ u8 mac[ETH_ALEN])
+{
+ struct ifreq req;
+ int sock = -1;
+ struct sockaddr_in *addr;
+ struct in_addr in_addr;
+
+ *ip_addr_text = os_zalloc(16);
+ if (*ip_addr_text == NULL)
+ goto fail;
+
+ sock = socket(AF_INET, SOCK_DGRAM, 0);
+ if (sock < 0)
+ goto fail;
+
+ os_strlcpy(req.ifr_name, net_if, sizeof(req.ifr_name));
+ if (ioctl(sock, SIOCGIFADDR, &req) < 0) {
+ wpa_printf(MSG_ERROR, "WPS UPnP: SIOCGIFADDR failed: %d (%s)",
+ errno, strerror(errno));
+ goto fail;
+ }
+ addr = (void *) &req.ifr_addr;
+ *ip_addr = addr->sin_addr.s_addr;
+ in_addr.s_addr = *ip_addr;
+ os_snprintf(*ip_addr_text, 16, "%s", inet_ntoa(in_addr));
+
+#ifdef __linux__
+ os_strlcpy(req.ifr_name, net_if, sizeof(req.ifr_name));
+ if (ioctl(sock, SIOCGIFHWADDR, &req) < 0) {
+ wpa_printf(MSG_ERROR, "WPS UPnP: SIOCGIFHWADDR failed: "
+ "%d (%s)", errno, strerror(errno));
+ goto fail;
+ }
+ os_memcpy(mac, req.ifr_addr.sa_data, 6);
+#elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
+ if (eth_get(net_if, mac) < 0) {
+ wpa_printf(MSG_ERROR, "WPS UPnP: Failed to get MAC address");
+ goto fail;
+ }
+#else
+#error MAC address fetch not implemented
+#endif
+
+ close(sock);
+ return 0;
+
+fail:
+ if (sock >= 0)
+ close(sock);
+ os_free(*ip_addr_text);
+ *ip_addr_text = NULL;
+ return -1;
+}
+
+
+static void upnp_wps_free_msearchreply(struct dl_list *head)
+{
+ struct advertisement_state_machine *a, *tmp;
+ dl_list_for_each_safe(a, tmp, head, struct advertisement_state_machine,
+ list)
+ msearchreply_state_machine_stop(a);
+}
+
+
+static void upnp_wps_free_subscriptions(struct dl_list *head,
+ struct wps_registrar *reg)
+{
+ struct subscription *s, *tmp;
+ dl_list_for_each_safe(s, tmp, head, struct subscription, list) {
+ if (reg && s->reg != reg)
+ continue;
+ dl_list_del(&s->list);
+ subscription_destroy(s);
+ }
+}
+
+
+/**
+ * upnp_wps_device_stop - Stop WPS UPnP operations on an interface
+ * @sm: WPS UPnP state machine from upnp_wps_device_init()
+ */
+static void upnp_wps_device_stop(struct upnp_wps_device_sm *sm)
+{
+ if (!sm || !sm->started)
+ return;
+
+ wpa_printf(MSG_DEBUG, "WPS UPnP: Stop device");
+ web_listener_stop(sm);
+ ssdp_listener_stop(sm);
+ upnp_wps_free_msearchreply(&sm->msearch_replies);
+ upnp_wps_free_subscriptions(&sm->subscriptions, NULL);
+
+ advertisement_state_machine_stop(sm, 1);
+
+ event_send_stop_all(sm);
+ os_free(sm->wlanevent);
+ sm->wlanevent = NULL;
+ os_free(sm->ip_addr_text);
+ sm->ip_addr_text = NULL;
+ if (sm->multicast_sd >= 0)
+ close(sm->multicast_sd);
+ sm->multicast_sd = -1;
+
+ sm->started = 0;
+}
+
+
+/**
+ * upnp_wps_device_start - Start WPS UPnP operations on an interface
+ * @sm: WPS UPnP state machine from upnp_wps_device_init()
+ * @net_if: Selected network interface name
+ * Returns: 0 on success, -1 on failure
+ */
+static int upnp_wps_device_start(struct upnp_wps_device_sm *sm, char *net_if)
+{
+ if (!sm || !net_if)
+ return -1;
+
+ if (sm->started)
+ upnp_wps_device_stop(sm);
+
+ sm->multicast_sd = -1;
+ sm->ssdp_sd = -1;
+ sm->started = 1;
+ sm->advertise_count = 0;
+
+ /* Fix up linux multicast handling */
+ if (add_ssdp_network(net_if))
+ goto fail;
+
+ /* Determine which IP and mac address we're using */
+ if (get_netif_info(net_if, &sm->ip_addr, &sm->ip_addr_text,
+ sm->mac_addr)) {
+ wpa_printf(MSG_INFO, "WPS UPnP: Could not get IP/MAC address "
+ "for %s. Does it have IP address?", net_if);
+ goto fail;
+ }
+
+ /* Listen for incoming TCP connections so that others
+ * can fetch our "xml files" from us.
+ */
+ if (web_listener_start(sm))
+ goto fail;
+
+ /* Set up for receiving discovery (UDP) packets */
+ if (ssdp_listener_start(sm))
+ goto fail;
+
+ /* Set up for sending multicast */
+ if (ssdp_open_multicast(sm) < 0)
+ goto fail;
+
+ /*
+ * Broadcast NOTIFY messages to let the world know we exist.
+ * This is done via a state machine since the messages should not be
+ * all sent out at once.
+ */
+ if (advertisement_state_machine_start(sm))
+ goto fail;
+
+ return 0;
+
+fail:
+ upnp_wps_device_stop(sm);
+ return -1;
+}
+
+
+static struct upnp_wps_device_interface *
+upnp_wps_get_iface(struct upnp_wps_device_sm *sm, void *priv)
+{
+ struct upnp_wps_device_interface *iface;
+ dl_list_for_each(iface, &sm->interfaces,
+ struct upnp_wps_device_interface, list) {
+ if (iface->priv == priv)
+ return iface;
+ }
+ return NULL;
+}
+
+
+/**
+ * upnp_wps_device_deinit - Deinitialize WPS UPnP
+ * @sm: WPS UPnP state machine from upnp_wps_device_init()
+ * @priv: External context data that was used in upnp_wps_device_init() call
+ */
+void upnp_wps_device_deinit(struct upnp_wps_device_sm *sm, void *priv)
+{
+ struct upnp_wps_device_interface *iface;
+
+ if (!sm)
+ return;
+
+ iface = upnp_wps_get_iface(sm, priv);
+ if (iface == NULL) {
+ wpa_printf(MSG_ERROR, "WPS UPnP: Could not find the interface "
+ "instance to deinit");
+ return;
+ }
+ wpa_printf(MSG_DEBUG, "WPS UPnP: Deinit interface instance %p", iface);
+ if (dl_list_len(&sm->interfaces) == 1) {
+ wpa_printf(MSG_DEBUG, "WPS UPnP: Deinitializing last instance "
+ "- free global device instance");
+ upnp_wps_device_stop(sm);
+ } else
+ upnp_wps_free_subscriptions(&sm->subscriptions,
+ iface->wps->registrar);
+ dl_list_del(&iface->list);
+
+ if (iface->peer.wps)
+ wps_deinit(iface->peer.wps);
+ os_free(iface->ctx->ap_pin);
+ os_free(iface->ctx);
+ os_free(iface);
+
+ if (dl_list_empty(&sm->interfaces)) {
+ os_free(sm->root_dir);
+ os_free(sm->desc_url);
+ os_free(sm);
+ shared_upnp_device = NULL;
+ }
+}
+
+
+/**
+ * upnp_wps_device_init - Initialize WPS UPnP
+ * @ctx: callback table; we must eventually free it
+ * @wps: Pointer to longterm WPS context
+ * @priv: External context data that will be used in callbacks
+ * @net_if: Selected network interface name
+ * Returns: WPS UPnP state or %NULL on failure
+ */
+struct upnp_wps_device_sm *
+upnp_wps_device_init(struct upnp_wps_device_ctx *ctx, struct wps_context *wps,
+ void *priv, char *net_if)
+{
+ struct upnp_wps_device_sm *sm;
+ struct upnp_wps_device_interface *iface;
+ int start = 0;
+
+ iface = os_zalloc(sizeof(*iface));
+ if (iface == NULL) {
+ os_free(ctx->ap_pin);
+ os_free(ctx);
+ return NULL;
+ }
+ wpa_printf(MSG_DEBUG, "WPS UPnP: Init interface instance %p", iface);
+
+ iface->ctx = ctx;
+ iface->wps = wps;
+ iface->priv = priv;
+
+ if (shared_upnp_device) {
+ wpa_printf(MSG_DEBUG, "WPS UPnP: Share existing device "
+ "context");
+ sm = shared_upnp_device;
+ } else {
+ wpa_printf(MSG_DEBUG, "WPS UPnP: Initialize device context");
+ sm = os_zalloc(sizeof(*sm));
+ if (!sm) {
+ wpa_printf(MSG_ERROR, "WPS UPnP: upnp_wps_device_init "
+ "failed");
+ os_free(iface);
+ os_free(ctx->ap_pin);
+ os_free(ctx);
+ return NULL;
+ }
+ shared_upnp_device = sm;
+
+ dl_list_init(&sm->msearch_replies);
+ dl_list_init(&sm->subscriptions);
+ dl_list_init(&sm->interfaces);
+ start = 1;
+ }
+
+ dl_list_add(&sm->interfaces, &iface->list);
+
+ if (start && upnp_wps_device_start(sm, net_if)) {
+ upnp_wps_device_deinit(sm, priv);
+ return NULL;
+ }
+
+
+ return sm;
+}
+
+
+/**
+ * upnp_wps_subscribers - Check whether there are any event subscribers
+ * @sm: WPS UPnP state machine from upnp_wps_device_init()
+ * Returns: 0 if no subscribers, 1 if subscribers
+ */
+int upnp_wps_subscribers(struct upnp_wps_device_sm *sm)
+{
+ return !dl_list_empty(&sm->subscriptions);
+}
+
+
+int upnp_wps_set_ap_pin(struct upnp_wps_device_sm *sm, const char *ap_pin)
+{
+ struct upnp_wps_device_interface *iface;
+ if (sm == NULL)
+ return 0;
+
+ dl_list_for_each(iface, &sm->interfaces,
+ struct upnp_wps_device_interface, list) {
+ os_free(iface->ctx->ap_pin);
+ if (ap_pin) {
+ iface->ctx->ap_pin = os_strdup(ap_pin);
+ if (iface->ctx->ap_pin == NULL)
+ return -1;
+ } else
+ iface->ctx->ap_pin = NULL;
+ }
+
+ return 0;
+}
diff --git a/freebsd/contrib/wpa/src/wps/wps_upnp.h b/freebsd/contrib/wpa/src/wps/wps_upnp.h
new file mode 100644
index 00000000..87b7ab14
--- /dev/null
+++ b/freebsd/contrib/wpa/src/wps/wps_upnp.h
@@ -0,0 +1,48 @@
+/*
+ * UPnP WPS Device
+ * Copyright (c) 2000-2003 Intel Corporation
+ * Copyright (c) 2006-2007 Sony Corporation
+ * Copyright (c) 2008-2009 Atheros Communications
+ * Copyright (c) 2009, Jouni Malinen <j@w1.fi>
+ *
+ * See wps_upnp.c for more details on licensing and code history.
+ */
+
+#ifndef WPS_UPNP_H
+#define WPS_UPNP_H
+
+struct upnp_wps_device_sm;
+struct wps_context;
+struct wps_data;
+
+struct upnp_wps_peer {
+ struct wps_data *wps;
+};
+
+enum upnp_wps_wlanevent_type {
+ UPNP_WPS_WLANEVENT_TYPE_PROBE = 1,
+ UPNP_WPS_WLANEVENT_TYPE_EAP = 2
+};
+
+struct upnp_wps_device_ctx {
+ int (*rx_req_put_wlan_response)(
+ void *priv, enum upnp_wps_wlanevent_type ev_type,
+ const u8 *mac_addr, const struct wpabuf *msg,
+ enum wps_msg_type msg_type);
+
+ char *ap_pin;
+};
+
+struct upnp_wps_device_sm *
+upnp_wps_device_init(struct upnp_wps_device_ctx *ctx, struct wps_context *wps,
+ void *priv, char *net_if);
+void upnp_wps_device_deinit(struct upnp_wps_device_sm *sm, void *priv);
+
+int upnp_wps_device_send_wlan_event(struct upnp_wps_device_sm *sm,
+ const u8 from_mac_addr[ETH_ALEN],
+ enum upnp_wps_wlanevent_type ev_type,
+ const struct wpabuf *msg);
+int upnp_wps_subscribers(struct upnp_wps_device_sm *sm);
+int upnp_wps_set_ap_pin(struct upnp_wps_device_sm *sm, const char *ap_pin);
+
+#endif /* WPS_UPNP_H */
diff --git a/freebsd/contrib/wpa/src/wps/wps_upnp_ap.c b/freebsd/contrib/wpa/src/wps/wps_upnp_ap.c
new file mode 100644
index 00000000..e705d445
--- /dev/null
+++ b/freebsd/contrib/wpa/src/wps/wps_upnp_ap.c
@@ -0,0 +1,85 @@
+#include <machine/rtems-bsd-user-space.h>
+
+/*
+ * Wi-Fi Protected Setup - UPnP AP functionality
+ * 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 "includes.h"
+
+#include "common.h"
+#include "eloop.h"
+#include "uuid.h"
+#include "wps_i.h"
+#include "wps_upnp.h"
+#include "wps_upnp_i.h"
+
+
+static void upnp_er_set_selected_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+ struct subscription *s = eloop_ctx;
+ struct wps_registrar *reg = timeout_ctx;
+ wpa_printf(MSG_DEBUG, "WPS: SetSelectedRegistrar from ER timed out");
+ s->selected_registrar = 0;
+ wps_registrar_selected_registrar_changed(reg, 0);
+}
+
+
+int upnp_er_set_selected_registrar(struct wps_registrar *reg,
+ struct subscription *s,
+ const struct wpabuf *msg)
+{
+ struct wps_parse_attr attr;
+
+ wpa_hexdump_buf(MSG_MSGDUMP, "WPS: SetSelectedRegistrar attributes",
+ msg);
+ if (wps_validate_upnp_set_selected_registrar(msg) < 0 ||
+ wps_parse_msg(msg, &attr) < 0)
+ return -1;
+
+ s->reg = reg;
+ eloop_cancel_timeout(upnp_er_set_selected_timeout, s, reg);
+
+ os_memset(s->authorized_macs, 0, sizeof(s->authorized_macs));
+ if (attr.selected_registrar == NULL || *attr.selected_registrar == 0) {
+ wpa_printf(MSG_DEBUG, "WPS: SetSelectedRegistrar: Disable "
+ "Selected Registrar");
+ s->selected_registrar = 0;
+ } else {
+ s->selected_registrar = 1;
+ s->dev_password_id = attr.dev_password_id ?
+ WPA_GET_BE16(attr.dev_password_id) : DEV_PW_DEFAULT;
+ s->config_methods = attr.sel_reg_config_methods ?
+ WPA_GET_BE16(attr.sel_reg_config_methods) : -1;
+ if (attr.authorized_macs) {
+ int count = attr.authorized_macs_len / ETH_ALEN;
+ if (count > WPS_MAX_AUTHORIZED_MACS)
+ count = WPS_MAX_AUTHORIZED_MACS;
+ os_memcpy(s->authorized_macs, attr.authorized_macs,
+ count * ETH_ALEN);
+ } else if (!attr.version2) {
+ wpa_printf(MSG_DEBUG, "WPS: Add broadcast "
+ "AuthorizedMACs for WPS 1.0 ER");
+ os_memset(s->authorized_macs, 0xff, ETH_ALEN);
+ }
+ eloop_register_timeout(WPS_PBC_WALK_TIME, 0,
+ upnp_er_set_selected_timeout, s, reg);
+ }
+
+ wps_registrar_selected_registrar_changed(reg, 0);
+
+ return 0;
+}
+
+
+void upnp_er_remove_notification(struct wps_registrar *reg,
+ struct subscription *s)
+{
+ s->selected_registrar = 0;
+ eloop_cancel_timeout(upnp_er_set_selected_timeout, s, reg);
+ if (reg)
+ wps_registrar_selected_registrar_changed(reg, 0);
+}
diff --git a/freebsd/contrib/wpa/src/wps/wps_upnp_event.c b/freebsd/contrib/wpa/src/wps/wps_upnp_event.c
new file mode 100644
index 00000000..92a603b0
--- /dev/null
+++ b/freebsd/contrib/wpa/src/wps/wps_upnp_event.c
@@ -0,0 +1,423 @@
+#include <machine/rtems-bsd-user-space.h>
+
+/*
+ * UPnP WPS Device - Event processing
+ * Copyright (c) 2000-2003 Intel Corporation
+ * Copyright (c) 2006-2007 Sony Corporation
+ * Copyright (c) 2008-2009 Atheros Communications
+ * Copyright (c) 2009-2010, Jouni Malinen <j@w1.fi>
+ *
+ * See wps_upnp.c for more details on licensing and code history.
+ */
+
+#include "includes.h"
+#include <assert.h>
+
+#include "common.h"
+#include "eloop.h"
+#include "uuid.h"
+#include "http_client.h"
+#include "wps_defs.h"
+#include "wps_upnp.h"
+#include "wps_upnp_i.h"
+
+/*
+ * Event message generation (to subscribers)
+ *
+ * We make a separate copy for each message for each subscriber. This memory
+ * wasted could be limited (adding code complexity) by sharing copies, keeping
+ * a usage count and freeing when zero.
+ *
+ * Sending a message requires using a HTTP over TCP NOTIFY
+ * (like a PUT) which requires a number of states..
+ */
+
+#define MAX_EVENTS_QUEUED 20 /* How far behind queued events */
+#define MAX_FAILURES 10 /* Drop subscription after this many failures */
+
+/* How long to wait before sending event */
+#define EVENT_DELAY_SECONDS 0
+#define EVENT_DELAY_MSEC 0
+
+/*
+ * Event information that we send to each subscriber is remembered in this
+ * struct. The event cannot be sent by simple UDP; it has to be sent by a HTTP
+ * over TCP transaction which requires various states.. It may also need to be
+ * retried at a different address (if more than one is available).
+ *
+ * TODO: As an optimization we could share data between subscribers.
+ */
+struct wps_event_ {
+ struct dl_list list;
+ struct subscription *s; /* parent */
+ unsigned subscriber_sequence; /* which event for this subscription*/
+ unsigned int retry; /* which retry */
+ struct subscr_addr *addr; /* address to connect to */
+ struct wpabuf *data; /* event data to send */
+ struct http_client *http_event;
+};
+
+
+/* event_clean -- clean sockets etc. of event
+ * Leaves data, retry count etc. alone.
+ */
+static void event_clean(struct wps_event_ *e)
+{
+ if (e->s->current_event == e)
+ e->s->current_event = NULL;
+ http_client_free(e->http_event);
+ e->http_event = NULL;
+}
+
+
+/* event_delete -- delete single unqueued event
+ * (be sure to dequeue first if need be)
+ */
+static void event_delete(struct wps_event_ *e)
+{
+ wpa_printf(MSG_DEBUG, "WPS UPnP: Delete event %p", e);
+ event_clean(e);
+ wpabuf_free(e->data);
+ os_free(e);
+}
+
+
+/* event_dequeue -- get next event from the queue
+ * Returns NULL if empty.
+ */
+static struct wps_event_ *event_dequeue(struct subscription *s)
+{
+ struct wps_event_ *e;
+ e = dl_list_first(&s->event_queue, struct wps_event_, list);
+ if (e) {
+ wpa_printf(MSG_DEBUG, "WPS UPnP: Dequeue event %p for "
+ "subscription %p", e, s);
+ dl_list_del(&e->list);
+ }
+ return e;
+}
+
+
+/* event_delete_all -- delete entire event queue and current event */
+void event_delete_all(struct subscription *s)
+{
+ struct wps_event_ *e;
+ while ((e = event_dequeue(s)) != NULL)
+ event_delete(e);
+ if (s->current_event) {
+ event_delete(s->current_event);
+ /* will set: s->current_event = NULL; */
+ }
+}
+
+
+/**
+ * event_retry - Called when we had a failure delivering event msg
+ * @e: Event
+ * @do_next_address: skip address e.g. on connect fail
+ */
+static void event_retry(struct wps_event_ *e, int do_next_address)
+{
+ struct subscription *s = e->s;
+ struct upnp_wps_device_sm *sm = s->sm;
+
+ wpa_printf(MSG_DEBUG, "WPS UPnP: Retry event %p for subscription %p",
+ e, s);
+ event_clean(e);
+ /* will set: s->current_event = NULL; */
+
+ if (do_next_address) {
+ e->retry++;
+ wpa_printf(MSG_DEBUG, "WPS UPnP: Try address %d", e->retry);
+ }
+ if (e->retry >= dl_list_len(&s->addr_list)) {
+ wpa_printf(MSG_DEBUG, "WPS UPnP: Giving up on sending event "
+ "for %s", e->addr->domain_and_port);
+ event_delete(e);
+ s->last_event_failed = 1;
+ if (!dl_list_empty(&s->event_queue))
+ event_send_all_later(s->sm);
+ return;
+ }
+ dl_list_add(&s->event_queue, &e->list);
+ event_send_all_later(sm);
+}
+
+
+static struct wpabuf * event_build_message(struct wps_event_ *e)
+{
+ struct wpabuf *buf;
+ char *b;
+
+ buf = wpabuf_alloc(1000 + wpabuf_len(e->data));
+ if (buf == NULL)
+ return NULL;
+ wpabuf_printf(buf, "NOTIFY %s HTTP/1.1\r\n", e->addr->path);
+ wpabuf_put_str(buf, "SERVER: Unspecified, UPnP/1.0, Unspecified\r\n");
+ wpabuf_printf(buf, "HOST: %s\r\n", e->addr->domain_and_port);
+ wpabuf_put_str(buf, "CONTENT-TYPE: text/xml; charset=\"utf-8\"\r\n"
+ "NT: upnp:event\r\n"
+ "NTS: upnp:propchange\r\n");
+ wpabuf_put_str(buf, "SID: uuid:");
+ b = wpabuf_put(buf, 0);
+ uuid_bin2str(e->s->uuid, b, 80);
+ wpabuf_put(buf, os_strlen(b));
+ wpabuf_put_str(buf, "\r\n");
+ wpabuf_printf(buf, "SEQ: %u\r\n", e->subscriber_sequence);
+ wpabuf_printf(buf, "CONTENT-LENGTH: %d\r\n",
+ (int) wpabuf_len(e->data));
+ wpabuf_put_str(buf, "\r\n"); /* terminating empty line */
+ wpabuf_put_buf(buf, e->data);
+ return buf;
+}
+
+
+static void event_addr_failure(struct wps_event_ *e)
+{
+ struct subscription *s = e->s;
+
+ e->addr->num_failures++;
+ wpa_printf(MSG_DEBUG, "WPS UPnP: Failed to send event %p to %s "
+ "(num_failures=%u)",
+ e, e->addr->domain_and_port, e->addr->num_failures);
+
+ if (e->addr->num_failures < MAX_FAILURES) {
+ /* Try other addresses, if available */
+ event_retry(e, 1);
+ return;
+ }
+
+ /*
+ * If other side doesn't like what we say, forget about them.
+ * (There is no way to tell other side that we are dropping them...).
+ */
+ wpa_printf(MSG_DEBUG, "WPS UPnP: Deleting subscription %p "
+ "address %s due to errors", s, e->addr->domain_and_port);
+ dl_list_del(&e->addr->list);
+ subscr_addr_delete(e->addr);
+ e->addr = NULL;
+
+ if (dl_list_empty(&s->addr_list)) {
+ /* if we've given up on all addresses */
+ wpa_printf(MSG_DEBUG, "WPS UPnP: Removing subscription %p "
+ "with no addresses", s);
+ dl_list_del(&s->list);
+ subscription_destroy(s);
+ return;
+ }
+
+ /* Try other addresses, if available */
+ event_retry(e, 0);
+}
+
+
+static void event_http_cb(void *ctx, struct http_client *c,
+ enum http_client_event event)
+{
+ struct wps_event_ *e = ctx;
+ struct subscription *s = e->s;
+
+ wpa_printf(MSG_DEBUG, "WPS UPnP: HTTP client callback: e=%p c=%p "
+ "event=%d", e, c, event);
+ switch (event) {
+ case HTTP_CLIENT_OK:
+ wpa_printf(MSG_DEBUG,
+ "WPS UPnP: Got event %p reply OK from %s",
+ e, e->addr->domain_and_port);
+ e->addr->num_failures = 0;
+ s->last_event_failed = 0;
+ event_delete(e);
+
+ /* Schedule sending more if there is more to send */
+ if (!dl_list_empty(&s->event_queue))
+ event_send_all_later(s->sm);
+ break;
+ case HTTP_CLIENT_FAILED:
+ wpa_printf(MSG_DEBUG, "WPS UPnP: Event send failure");
+ event_addr_failure(e);
+ break;
+ case HTTP_CLIENT_INVALID_REPLY:
+ wpa_printf(MSG_DEBUG, "WPS UPnP: Invalid reply");
+ event_addr_failure(e);
+ break;
+ case HTTP_CLIENT_TIMEOUT:
+ wpa_printf(MSG_DEBUG, "WPS UPnP: Event send timeout");
+ event_addr_failure(e);
+ break;
+ }
+}
+
+
+/* event_send_start -- prepare to send a event message to subscriber
+ *
+ * This gets complicated because:
+ * -- The message is sent via TCP and we have to keep the stream open
+ * for 30 seconds to get a response... then close it.
+ * -- But we might have other event happen in the meantime...
+ * we have to queue them, if we lose them then the subscriber will
+ * be forced to unsubscribe and subscribe again.
+ * -- If multiple URLs are provided then we are supposed to try successive
+ * ones after 30 second timeout.
+ * -- The URLs might use domain names instead of dotted decimal addresses,
+ * and resolution of those may cause unwanted sleeping.
+ * -- Doing the initial TCP connect can take a while, so we have to come
+ * back after connection and then send the data.
+ *
+ * Returns nonzero on error;
+ *
+ * Prerequisite: No current event send (s->current_event == NULL)
+ * and non-empty queue.
+ */
+static int event_send_start(struct subscription *s)
+{
+ struct wps_event_ *e;
+ unsigned int itry;
+ struct wpabuf *buf;
+
+ /*
+ * Assume we are called ONLY with no current event and ONLY with
+ * nonempty event queue and ONLY with at least one address to send to.
+ */
+ if (dl_list_empty(&s->addr_list) ||
+ s->current_event ||
+ dl_list_empty(&s->event_queue))
+ return -1;
+
+ s->current_event = e = event_dequeue(s);
+
+ /* Use address according to number of retries */
+ itry = 0;
+ dl_list_for_each(e->addr, &s->addr_list, struct subscr_addr, list)
+ if (itry++ == e->retry)
+ break;
+ if (itry < e->retry)
+ return -1;
+
+ buf = event_build_message(e);
+ if (buf == NULL) {
+ event_retry(e, 0);
+ return -1;
+ }
+
+ e->http_event = http_client_addr(&e->addr->saddr, buf, 0,
+ event_http_cb, e);
+ if (e->http_event == NULL) {
+ wpabuf_free(buf);
+ event_retry(e, 0);
+ return -1;
+ }
+
+ return 0;
+}
+
+
+/* event_send_all_later_handler -- actually send events as needed */
+static void event_send_all_later_handler(void *eloop_data, void *user_ctx)
+{
+ struct upnp_wps_device_sm *sm = user_ctx;
+ struct subscription *s, *tmp;
+ int nerrors = 0;
+
+ sm->event_send_all_queued = 0;
+ dl_list_for_each_safe(s, tmp, &sm->subscriptions, struct subscription,
+ list) {
+ if (s->current_event == NULL /* not busy */ &&
+ !dl_list_empty(&s->event_queue) /* more to do */) {
+ if (event_send_start(s))
+ nerrors++;
+ }
+ }
+
+ if (nerrors) {
+ /* Try again later */
+ event_send_all_later(sm);
+ }
+}
+
+
+/* event_send_all_later -- schedule sending events to all subscribers
+ * that need it.
+ * This avoids two problems:
+ * -- After getting a subscription, we should not send the first event
+ * until after our reply is fully queued to be sent back,
+ * -- Possible stack depth or infinite recursion issues.
+ */
+void event_send_all_later(struct upnp_wps_device_sm *sm)
+{
+ /*
+ * The exact time in the future isn't too important. Waiting a bit
+ * might let us do several together.
+ */
+ if (sm->event_send_all_queued)
+ return;
+ sm->event_send_all_queued = 1;
+ eloop_register_timeout(EVENT_DELAY_SECONDS, EVENT_DELAY_MSEC,
+ event_send_all_later_handler, NULL, sm);
+}
+
+
+/* event_send_stop_all -- cleanup */
+void event_send_stop_all(struct upnp_wps_device_sm *sm)
+{
+ if (sm->event_send_all_queued)
+ eloop_cancel_timeout(event_send_all_later_handler, NULL, sm);
+ sm->event_send_all_queued = 0;
+}
+
+
+/**
+ * event_add - Add a new event to a queue
+ * @s: Subscription
+ * @data: Event data (is copied; caller retains ownership)
+ * @probereq: Whether this is a Probe Request event
+ * Returns: 0 on success, -1 on error, 1 on max event queue limit reached
+ */
+int event_add(struct subscription *s, const struct wpabuf *data, int probereq)
+{
+ struct wps_event_ *e;
+ unsigned int len;
+
+ len = dl_list_len(&s->event_queue);
+ if (len >= MAX_EVENTS_QUEUED) {
+ wpa_printf(MSG_DEBUG, "WPS UPnP: Too many events queued for "
+ "subscriber %p", s);
+ if (probereq)
+ return 1;
+
+ /* Drop oldest entry to allow EAP event to be stored. */
+ e = event_dequeue(s);
+ if (!e)
+ return 1;
+ event_delete(e);
+ }
+
+ if (s->last_event_failed && probereq && len > 0) {
+ /*
+ * Avoid queuing frames for subscribers that may have left
+ * without unsubscribing.
+ */
+ wpa_printf(MSG_DEBUG, "WPS UPnP: Do not queue more Probe "
+ "Request frames for subscription %p since last "
+ "delivery failed", s);
+ return -1;
+ }
+
+ e = os_zalloc(sizeof(*e));
+ if (e == NULL)
+ return -1;
+ dl_list_init(&e->list);
+ e->s = s;
+ e->data = wpabuf_dup(data);
+ if (e->data == NULL) {
+ os_free(e);
+ return -1;
+ }
+ e->subscriber_sequence = s->next_subscriber_sequence++;
+ if (s->next_subscriber_sequence == 0)
+ s->next_subscriber_sequence++;
+ wpa_printf(MSG_DEBUG, "WPS UPnP: Queue event %p for subscriber %p "
+ "(queue len %u)", e, s, len + 1);
+ dl_list_add_tail(&s->event_queue, &e->list);
+ event_send_all_later(s->sm);
+ return 0;
+}
diff --git a/freebsd/contrib/wpa/src/wps/wps_upnp_i.h b/freebsd/contrib/wpa/src/wps/wps_upnp_i.h
new file mode 100644
index 00000000..f289fe68
--- /dev/null
+++ b/freebsd/contrib/wpa/src/wps/wps_upnp_i.h
@@ -0,0 +1,193 @@
+/*
+ * UPnP for WPS / internal definitions
+ * Copyright (c) 2000-2003 Intel Corporation
+ * Copyright (c) 2006-2007 Sony Corporation
+ * Copyright (c) 2008-2009 Atheros Communications
+ * Copyright (c) 2009, Jouni Malinen <j@w1.fi>
+ *
+ * See wps_upnp.c for more details on licensing and code history.
+ */
+
+#ifndef WPS_UPNP_I_H
+#define WPS_UPNP_I_H
+
+#include "utils/list.h"
+#include "http.h"
+
+#define UPNP_MULTICAST_ADDRESS "239.255.255.250" /* for UPnP multicasting */
+#define UPNP_MULTICAST_PORT 1900 /* UDP port to monitor for UPnP */
+
+/* min subscribe time per UPnP standard */
+#define UPNP_SUBSCRIBE_SEC_MIN 1800
+/* subscribe time we use */
+#define UPNP_SUBSCRIBE_SEC (UPNP_SUBSCRIBE_SEC_MIN + 1)
+
+/* "filenames" used in URLs that we service via our "web server": */
+#define UPNP_WPS_DEVICE_XML_FILE "wps_device.xml"
+#define UPNP_WPS_SCPD_XML_FILE "wps_scpd.xml"
+#define UPNP_WPS_DEVICE_CONTROL_FILE "wps_control"
+#define UPNP_WPS_DEVICE_EVENT_FILE "wps_event"
+
+#define MULTICAST_MAX_READ 1600 /* max bytes we'll read for UPD request */
+
+
+struct upnp_wps_device_sm;
+struct wps_registrar;
+
+
+enum advertisement_type_enum {
+ ADVERTISE_UP = 0,
+ ADVERTISE_DOWN = 1,
+ MSEARCH_REPLY = 2
+};
+
+/*
+ * Advertisements are broadcast via UDP NOTIFYs, and are also the essence of
+ * the reply to UDP M-SEARCH requests. This struct handles both cases.
+ *
+ * A state machine is needed because a number of variant forms must be sent in
+ * separate packets and spread out in time to avoid congestion.
+ */
+struct advertisement_state_machine {
+ struct dl_list list;
+ enum advertisement_type_enum type;
+ int state;
+ int nerrors;
+ struct sockaddr_in client; /* for M-SEARCH replies */
+};
+
+
+/*
+ * An address of a subscriber (who may have multiple addresses). We are
+ * supposed to send (via TCP) updates to each subscriber, trying each address
+ * for a subscriber until we find one that seems to work.
+ */
+struct subscr_addr {
+ struct dl_list list;
+ char *domain_and_port; /* domain and port part of url */
+ char *path; /* "filepath" part of url (from "mem") */
+ struct sockaddr_in saddr; /* address for doing connect */
+ unsigned num_failures;
+};
+
+
+/*
+ * Subscribers to our events are recorded in this struct. This includes a max
+ * of one outgoing connection (sending an "event message") per subscriber. We
+ * also have to age out subscribers unless they renew.
+ */
+struct subscription {
+ struct dl_list list;
+ struct upnp_wps_device_sm *sm; /* parent */
+ time_t timeout_time; /* when to age out the subscription */
+ unsigned next_subscriber_sequence; /* number our messages */
+ /*
+ * This uuid identifies the subscription and is randomly generated by
+ * us and given to the subscriber when the subscription is accepted;
+ * and is then included with each event sent to the subscriber.
+ */
+ u8 uuid[UUID_LEN];
+ /* Linked list of address alternatives (rotate through on failure) */
+ struct dl_list addr_list;
+ struct dl_list event_queue; /* Queued event messages. */
+ struct wps_event_ *current_event; /* non-NULL if being sent (not in q)
+ */
+ int last_event_failed; /* Whether delivery of last event failed */
+
+ /* Information from SetSelectedRegistrar action */
+ u8 selected_registrar;
+ u16 dev_password_id;
+ u16 config_methods;
+ u8 authorized_macs[WPS_MAX_AUTHORIZED_MACS][ETH_ALEN];
+ struct wps_registrar *reg;
+};
+
+
+struct upnp_wps_device_interface {
+ struct dl_list list;
+ struct upnp_wps_device_ctx *ctx; /* callback table */
+ struct wps_context *wps;
+ void *priv;
+
+ /* FIX: maintain separate structures for each UPnP peer */
+ struct upnp_wps_peer peer;
+};
+
+/*
+ * Our instance data corresponding to the AP device. Note that there may be
+ * multiple wireless interfaces sharing the same UPnP device instance. Each
+ * such interface is stored in the list of struct upnp_wps_device_interface
+ * instances.
+ *
+ * This is known as an opaque struct declaration to users of the WPS UPnP code.
+ */
+struct upnp_wps_device_sm {
+ struct dl_list interfaces; /* struct upnp_wps_device_interface */
+ char *root_dir;
+ char *desc_url;
+ int started; /* nonzero if we are active */
+ u8 mac_addr[ETH_ALEN]; /* mac addr of network i.f. we use */
+ char *ip_addr_text; /* IP address of network i.f. we use */
+ unsigned ip_addr; /* IP address of network i.f. we use (host order) */
+ int multicast_sd; /* send multicast messages over this socket */
+ int ssdp_sd; /* receive discovery UPD packets on socket */
+ int ssdp_sd_registered; /* nonzero if we must unregister */
+ unsigned advertise_count; /* how many advertisements done */
+ struct advertisement_state_machine advertisement;
+ struct dl_list msearch_replies;
+ int web_port; /* our port that others get xml files from */
+ struct http_server *web_srv;
+ /* Note: subscriptions are kept in expiry order */
+ struct dl_list subscriptions;
+ int event_send_all_queued; /* if we are scheduled to send events soon
+ */
+
+ char *wlanevent; /* the last WLANEvent data */
+ enum upnp_wps_wlanevent_type wlanevent_type;
+ os_time_t last_event_sec;
+ unsigned int num_events_in_sec;
+};
+
+/* wps_upnp.c */
+void format_date(struct wpabuf *buf);
+struct subscription * subscription_start(struct upnp_wps_device_sm *sm,
+ const char *callback_urls);
+struct subscription * subscription_renew(struct upnp_wps_device_sm *sm,
+ const u8 uuid[UUID_LEN]);
+void subscription_destroy(struct subscription *s);
+struct subscription * subscription_find(struct upnp_wps_device_sm *sm,
+ const u8 uuid[UUID_LEN]);
+void subscr_addr_delete(struct subscr_addr *a);
+int get_netif_info(const char *net_if, unsigned *ip_addr, char **ip_addr_text,
+ u8 mac[ETH_ALEN]);
+
+/* wps_upnp_ssdp.c */
+void msearchreply_state_machine_stop(struct advertisement_state_machine *a);
+int advertisement_state_machine_start(struct upnp_wps_device_sm *sm);
+void advertisement_state_machine_stop(struct upnp_wps_device_sm *sm,
+ int send_byebye);
+void ssdp_listener_stop(struct upnp_wps_device_sm *sm);
+int ssdp_listener_start(struct upnp_wps_device_sm *sm);
+int ssdp_listener_open(void);
+int add_ssdp_network(const char *net_if);
+int ssdp_open_multicast_sock(u32 ip_addr, const char *forced_ifname);
+int ssdp_open_multicast(struct upnp_wps_device_sm *sm);
+
+/* wps_upnp_web.c */
+int web_listener_start(struct upnp_wps_device_sm *sm);
+void web_listener_stop(struct upnp_wps_device_sm *sm);
+
+/* wps_upnp_event.c */
+int event_add(struct subscription *s, const struct wpabuf *data, int probereq);
+void event_delete_all(struct subscription *s);
+void event_send_all_later(struct upnp_wps_device_sm *sm);
+void event_send_stop_all(struct upnp_wps_device_sm *sm);
+
+/* wps_upnp_ap.c */
+int upnp_er_set_selected_registrar(struct wps_registrar *reg,
+ struct subscription *s,
+ const struct wpabuf *msg);
+void upnp_er_remove_notification(struct wps_registrar *reg,
+ struct subscription *s);
+
+#endif /* WPS_UPNP_I_H */
diff --git a/freebsd/contrib/wpa/src/wps/wps_upnp_ssdp.c b/freebsd/contrib/wpa/src/wps/wps_upnp_ssdp.c
new file mode 100644
index 00000000..ed972494
--- /dev/null
+++ b/freebsd/contrib/wpa/src/wps/wps_upnp_ssdp.c
@@ -0,0 +1,950 @@
+#include <machine/rtems-bsd-user-space.h>
+
+/*
+ * UPnP SSDP for WPS
+ * Copyright (c) 2000-2003 Intel Corporation
+ * Copyright (c) 2006-2007 Sony Corporation
+ * Copyright (c) 2008-2009 Atheros Communications
+ * Copyright (c) 2009-2013, Jouni Malinen <j@w1.fi>
+ *
+ * See wps_upnp.c for more details on licensing and code history.
+ */
+
+#include "includes.h"
+
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <net/route.h>
+#ifdef __linux__
+#include <net/if.h>
+#endif /* __linux__ */
+
+#include "common.h"
+#include "uuid.h"
+#include "eloop.h"
+#include "wps.h"
+#include "wps_upnp.h"
+#include "wps_upnp_i.h"
+
+#define UPNP_CACHE_SEC (UPNP_CACHE_SEC_MIN + 1) /* cache time we use */
+#define UPNP_CACHE_SEC_MIN 1800 /* min cachable time per UPnP standard */
+#define UPNP_ADVERTISE_REPEAT 2 /* no more than 3 */
+#define MAX_MSEARCH 20 /* max simultaneous M-SEARCH replies ongoing */
+#define SSDP_TARGET "239.0.0.0"
+#define SSDP_NETMASK "255.0.0.0"
+
+
+/* Check tokens for equality, where tokens consist of letters, digits,
+ * underscore and hyphen, and are matched case insensitive.
+ */
+static int token_eq(const char *s1, const char *s2)
+{
+ int c1;
+ int c2;
+ int end1 = 0;
+ int end2 = 0;
+ for (;;) {
+ c1 = *s1++;
+ c2 = *s2++;
+ if (isalpha(c1) && isupper(c1))
+ c1 = tolower(c1);
+ if (isalpha(c2) && isupper(c2))
+ c2 = tolower(c2);
+ end1 = !(isalnum(c1) || c1 == '_' || c1 == '-');
+ end2 = !(isalnum(c2) || c2 == '_' || c2 == '-');
+ if (end1 || end2 || c1 != c2)
+ break;
+ }
+ return end1 && end2; /* reached end of both words? */
+}
+
+
+/* Return length of token (see above for definition of token) */
+static int token_length(const char *s)
+{
+ const char *begin = s;
+ for (;; s++) {
+ int c = *s;
+ int end = !(isalnum(c) || c == '_' || c == '-');
+ if (end)
+ break;
+ }
+ return s - begin;
+}
+
+
+/* return length of interword separation.
+ * This accepts only spaces/tabs and thus will not traverse a line
+ * or buffer ending.
+ */
+static int word_separation_length(const char *s)
+{
+ const char *begin = s;
+ for (;; s++) {
+ int c = *s;
+ if (c == ' ' || c == '\t')
+ continue;
+ break;
+ }
+ return s - begin;
+}
+
+
+/* No. of chars through (including) end of line */
+static int line_length(const char *l)
+{
+ const char *lp = l;
+ while (*lp && *lp != '\n')
+ lp++;
+ if (*lp == '\n')
+ lp++;
+ return lp - l;
+}
+
+
+static int str_starts(const char *str, const char *start)
+{
+ return os_strncmp(str, start, os_strlen(start)) == 0;
+}
+
+
+/***************************************************************************
+ * Advertisements.
+ * These are multicast to the world to tell them we are here.
+ * The individual packets are spread out in time to limit loss,
+ * and then after a much longer period of time the whole sequence
+ * is repeated again (for NOTIFYs only).
+ **************************************************************************/
+
+/**
+ * next_advertisement - Build next message and advance the state machine
+ * @a: Advertisement state
+ * @islast: Buffer for indicating whether this is the last message (= 1)
+ * Returns: The new message (caller is responsible for freeing this)
+ *
+ * Note: next_advertisement is shared code with msearchreply_* functions
+ */
+static struct wpabuf *
+next_advertisement(struct upnp_wps_device_sm *sm,
+ struct advertisement_state_machine *a, int *islast)
+{
+ struct wpabuf *msg;
+ char *NTString = "";
+ char uuid_string[80];
+ struct upnp_wps_device_interface *iface;
+
+ *islast = 0;
+ iface = dl_list_first(&sm->interfaces,
+ struct upnp_wps_device_interface, list);
+ if (!iface)
+ return NULL;
+ uuid_bin2str(iface->wps->uuid, uuid_string, sizeof(uuid_string));
+ msg = wpabuf_alloc(800); /* more than big enough */
+ if (msg == NULL)
+ return NULL;
+ switch (a->type) {
+ case ADVERTISE_UP:
+ case ADVERTISE_DOWN:
+ NTString = "NT";
+ wpabuf_put_str(msg, "NOTIFY * HTTP/1.1\r\n");
+ wpabuf_printf(msg, "HOST: %s:%d\r\n",
+ UPNP_MULTICAST_ADDRESS, UPNP_MULTICAST_PORT);
+ wpabuf_printf(msg, "CACHE-CONTROL: max-age=%d\r\n",
+ UPNP_CACHE_SEC);
+ wpabuf_printf(msg, "NTS: %s\r\n",
+ (a->type == ADVERTISE_UP ?
+ "ssdp:alive" : "ssdp:byebye"));
+ break;
+ case MSEARCH_REPLY:
+ NTString = "ST";
+ wpabuf_put_str(msg, "HTTP/1.1 200 OK\r\n");
+ wpabuf_printf(msg, "CACHE-CONTROL: max-age=%d\r\n",
+ UPNP_CACHE_SEC);
+
+ wpabuf_put_str(msg, "DATE: ");
+ format_date(msg);
+ wpabuf_put_str(msg, "\r\n");
+
+ wpabuf_put_str(msg, "EXT:\r\n");
+ break;
+ }
+
+ if (a->type != ADVERTISE_DOWN) {
+ /* Where others may get our XML files from */
+ wpabuf_printf(msg, "LOCATION: http://%s:%d/%s\r\n",
+ sm->ip_addr_text, sm->web_port,
+ UPNP_WPS_DEVICE_XML_FILE);
+ }
+
+ /* The SERVER line has three comma-separated fields:
+ * operating system / version
+ * upnp version
+ * software package / version
+ * However, only the UPnP version is really required, the
+ * others can be place holders... for security reasons
+ * it is better to NOT provide extra information.
+ */
+ wpabuf_put_str(msg, "SERVER: Unspecified, UPnP/1.0, Unspecified\r\n");
+
+ switch (a->state / UPNP_ADVERTISE_REPEAT) {
+ case 0:
+ wpabuf_printf(msg, "%s: upnp:rootdevice\r\n", NTString);
+ wpabuf_printf(msg, "USN: uuid:%s::upnp:rootdevice\r\n",
+ uuid_string);
+ break;
+ case 1:
+ wpabuf_printf(msg, "%s: uuid:%s\r\n", NTString, uuid_string);
+ wpabuf_printf(msg, "USN: uuid:%s\r\n", uuid_string);
+ break;
+ case 2:
+ wpabuf_printf(msg, "%s: urn:schemas-wifialliance-org:device:"
+ "WFADevice:1\r\n", NTString);
+ wpabuf_printf(msg, "USN: uuid:%s::urn:schemas-wifialliance-"
+ "org:device:WFADevice:1\r\n", uuid_string);
+ break;
+ case 3:
+ wpabuf_printf(msg, "%s: urn:schemas-wifialliance-org:service:"
+ "WFAWLANConfig:1\r\n", NTString);
+ wpabuf_printf(msg, "USN: uuid:%s::urn:schemas-wifialliance-"
+ "org:service:WFAWLANConfig:1\r\n", uuid_string);
+ break;
+ }
+ wpabuf_put_str(msg, "\r\n");
+
+ if (a->state + 1 >= 4 * UPNP_ADVERTISE_REPEAT)
+ *islast = 1;
+
+ return msg;
+}
+
+
+static void advertisement_state_machine_handler(void *eloop_data,
+ void *user_ctx);
+
+
+/**
+ * advertisement_state_machine_stop - Stop SSDP advertisements
+ * @sm: WPS UPnP state machine from upnp_wps_device_init()
+ * @send_byebye: Send byebye advertisement messages immediately
+ */
+void advertisement_state_machine_stop(struct upnp_wps_device_sm *sm,
+ int send_byebye)
+{
+ struct advertisement_state_machine *a = &sm->advertisement;
+ int islast = 0;
+ struct wpabuf *msg;
+ struct sockaddr_in dest;
+
+ eloop_cancel_timeout(advertisement_state_machine_handler, NULL, sm);
+ if (!send_byebye || sm->multicast_sd < 0)
+ return;
+
+ a->type = ADVERTISE_DOWN;
+ a->state = 0;
+
+ os_memset(&dest, 0, sizeof(dest));
+ dest.sin_family = AF_INET;
+ dest.sin_addr.s_addr = inet_addr(UPNP_MULTICAST_ADDRESS);
+ dest.sin_port = htons(UPNP_MULTICAST_PORT);
+
+ while (!islast) {
+ msg = next_advertisement(sm, a, &islast);
+ if (msg == NULL)
+ break;
+ if (sendto(sm->multicast_sd, wpabuf_head(msg), wpabuf_len(msg),
+ 0, (struct sockaddr *) &dest, sizeof(dest)) < 0) {
+ wpa_printf(MSG_INFO, "WPS UPnP: Advertisement sendto "
+ "failed: %d (%s)", errno, strerror(errno));
+ }
+ wpabuf_free(msg);
+ a->state++;
+ }
+}
+
+
+static void advertisement_state_machine_handler(void *eloop_data,
+ void *user_ctx)
+{
+ struct upnp_wps_device_sm *sm = user_ctx;
+ struct advertisement_state_machine *a = &sm->advertisement;
+ struct wpabuf *msg;
+ int next_timeout_msec = 100;
+ int next_timeout_sec = 0;
+ struct sockaddr_in dest;
+ int islast = 0;
+
+ /*
+ * Each is sent twice (in case lost) w/ 100 msec delay between;
+ * spec says no more than 3 times.
+ * One pair for rootdevice, one pair for uuid, and a pair each for
+ * each of the two urns.
+ * The entire sequence must be repeated before cache control timeout
+ * (which is min 1800 seconds),
+ * recommend random portion of half of the advertised cache control age
+ * to ensure against loss... perhaps 1800/4 + rand*1800/4 ?
+ * Delay random interval < 100 msec prior to initial sending.
+ * TTL of 4
+ */
+
+ wpa_printf(MSG_MSGDUMP, "WPS UPnP: Advertisement state=%d", a->state);
+ msg = next_advertisement(sm, a, &islast);
+ if (msg == NULL)
+ return;
+
+ os_memset(&dest, 0, sizeof(dest));
+ dest.sin_family = AF_INET;
+ dest.sin_addr.s_addr = inet_addr(UPNP_MULTICAST_ADDRESS);
+ dest.sin_port = htons(UPNP_MULTICAST_PORT);
+
+ if (sendto(sm->multicast_sd, wpabuf_head(msg), wpabuf_len(msg), 0,
+ (struct sockaddr *) &dest, sizeof(dest)) == -1) {
+ wpa_printf(MSG_ERROR, "WPS UPnP: Advertisement sendto failed:"
+ "%d (%s)", errno, strerror(errno));
+ next_timeout_msec = 0;
+ next_timeout_sec = 10; /* ... later */
+ } else if (islast) {
+ a->state = 0; /* wrap around */
+ if (a->type == ADVERTISE_DOWN) {
+ wpa_printf(MSG_DEBUG, "WPS UPnP: ADVERTISE_DOWN->UP");
+ a->type = ADVERTISE_UP;
+ /* do it all over again right away */
+ } else {
+ u16 r;
+ /*
+ * Start over again after a long timeout
+ * (see notes above)
+ */
+ next_timeout_msec = 0;
+ if (os_get_random((void *) &r, sizeof(r)) < 0)
+ r = 32768;
+ next_timeout_sec = UPNP_CACHE_SEC / 4 +
+ (((UPNP_CACHE_SEC / 4) * r) >> 16);
+ sm->advertise_count++;
+ wpa_printf(MSG_DEBUG, "WPS UPnP: ADVERTISE_UP (#%u); "
+ "next in %d sec",
+ sm->advertise_count, next_timeout_sec);
+ }
+ } else {
+ a->state++;
+ }
+
+ wpabuf_free(msg);
+
+ eloop_register_timeout(next_timeout_sec, next_timeout_msec,
+ advertisement_state_machine_handler, NULL, sm);
+}
+
+
+/**
+ * advertisement_state_machine_start - Start SSDP advertisements
+ * @sm: WPS UPnP state machine from upnp_wps_device_init()
+ * Returns: 0 on success, -1 on failure
+ */
+int advertisement_state_machine_start(struct upnp_wps_device_sm *sm)
+{
+ struct advertisement_state_machine *a = &sm->advertisement;
+ int next_timeout_msec;
+
+ advertisement_state_machine_stop(sm, 0);
+
+ /*
+ * Start out advertising down, this automatically switches
+ * to advertising up which signals our restart.
+ */
+ a->type = ADVERTISE_DOWN;
+ a->state = 0;
+ /* (other fields not used here) */
+
+ /* First timeout should be random interval < 100 msec */
+ next_timeout_msec = (100 * (os_random() & 0xFF)) >> 8;
+ return eloop_register_timeout(0, next_timeout_msec,
+ advertisement_state_machine_handler,
+ NULL, sm);
+}
+
+
+/***************************************************************************
+ * M-SEARCH replies
+ * These are very similar to the multicast advertisements, with some
+ * small changes in data content; and they are sent (UDP) to a specific
+ * unicast address instead of multicast.
+ * They are sent in response to a UDP M-SEARCH packet.
+ **************************************************************************/
+
+/**
+ * msearchreply_state_machine_stop - Stop M-SEARCH reply state machine
+ * @a: Selected advertisement/reply state
+ */
+void msearchreply_state_machine_stop(struct advertisement_state_machine *a)
+{
+ wpa_printf(MSG_DEBUG, "WPS UPnP: M-SEARCH stop");
+ dl_list_del(&a->list);
+ os_free(a);
+}
+
+
+static void msearchreply_state_machine_handler(void *eloop_data,
+ void *user_ctx)
+{
+ struct advertisement_state_machine *a = user_ctx;
+ struct upnp_wps_device_sm *sm = eloop_data;
+ struct wpabuf *msg;
+ int next_timeout_msec = 100;
+ int next_timeout_sec = 0;
+ int islast = 0;
+
+ /*
+ * Each response is sent twice (in case lost) w/ 100 msec delay
+ * between; spec says no more than 3 times.
+ * One pair for rootdevice, one pair for uuid, and a pair each for
+ * each of the two urns.
+ */
+
+ /* TODO: should only send the requested response types */
+
+ wpa_printf(MSG_MSGDUMP, "WPS UPnP: M-SEARCH reply state=%d (%s:%d)",
+ a->state, inet_ntoa(a->client.sin_addr),
+ ntohs(a->client.sin_port));
+ msg = next_advertisement(sm, a, &islast);
+ if (msg == NULL)
+ return;
+
+ /*
+ * Send it on the multicast socket to avoid having to set up another
+ * socket.
+ */
+ if (sendto(sm->multicast_sd, wpabuf_head(msg), wpabuf_len(msg), 0,
+ (struct sockaddr *) &a->client, sizeof(a->client)) < 0) {
+ wpa_printf(MSG_DEBUG, "WPS UPnP: M-SEARCH reply sendto "
+ "errno %d (%s) for %s:%d",
+ errno, strerror(errno),
+ inet_ntoa(a->client.sin_addr),
+ ntohs(a->client.sin_port));
+ /* Ignore error and hope for the best */
+ }
+ wpabuf_free(msg);
+ if (islast) {
+ wpa_printf(MSG_DEBUG, "WPS UPnP: M-SEARCH reply done");
+ msearchreply_state_machine_stop(a);
+ return;
+ }
+ a->state++;
+
+ wpa_printf(MSG_MSGDUMP, "WPS UPnP: M-SEARCH reply in %d.%03d sec",
+ next_timeout_sec, next_timeout_msec);
+ eloop_register_timeout(next_timeout_sec, next_timeout_msec,
+ msearchreply_state_machine_handler, sm, a);
+}
+
+
+/**
+ * msearchreply_state_machine_start - Reply to M-SEARCH discovery request
+ * @sm: WPS UPnP state machine from upnp_wps_device_init()
+ * @client: Client address
+ * @mx: Maximum delay in seconds
+ *
+ * Use TTL of 4 (this was done when socket set up).
+ * A response should be given in randomized portion of min(MX,120) seconds
+ *
+ * UPnP-arch-DeviceArchitecture, 1.2.3:
+ * To be found, a device must send a UDP response to the source IP address and
+ * port that sent the request to the multicast channel. Devices respond if the
+ * ST header of the M-SEARCH request is "ssdp:all", "upnp:rootdevice", "uuid:"
+ * followed by a UUID that exactly matches one advertised by the device.
+ */
+static void msearchreply_state_machine_start(struct upnp_wps_device_sm *sm,
+ struct sockaddr_in *client,
+ int mx)
+{
+ struct advertisement_state_machine *a;
+ int next_timeout_sec;
+ int next_timeout_msec;
+ int replies;
+
+ replies = dl_list_len(&sm->msearch_replies);
+ wpa_printf(MSG_DEBUG, "WPS UPnP: M-SEARCH reply start (%d "
+ "outstanding)", replies);
+ if (replies >= MAX_MSEARCH) {
+ wpa_printf(MSG_INFO, "WPS UPnP: Too many outstanding "
+ "M-SEARCH replies");
+ return;
+ }
+
+ a = os_zalloc(sizeof(*a));
+ if (a == NULL)
+ return;
+ a->type = MSEARCH_REPLY;
+ a->state = 0;
+ os_memcpy(&a->client, client, sizeof(*client));
+ /* Wait time depending on MX value */
+ next_timeout_msec = (1000 * mx * (os_random() & 0xFF)) >> 8;
+ next_timeout_sec = next_timeout_msec / 1000;
+ next_timeout_msec = next_timeout_msec % 1000;
+ if (eloop_register_timeout(next_timeout_sec, next_timeout_msec,
+ msearchreply_state_machine_handler, sm,
+ a)) {
+ /* No way to recover (from malloc failure) */
+ goto fail;
+ }
+ /* Remember for future cleanup */
+ dl_list_add(&sm->msearch_replies, &a->list);
+ return;
+
+fail:
+ wpa_printf(MSG_INFO, "WPS UPnP: M-SEARCH reply failure!");
+ eloop_cancel_timeout(msearchreply_state_machine_handler, sm, a);
+ os_free(a);
+}
+
+
+/**
+ * ssdp_parse_msearch - Process a received M-SEARCH
+ * @sm: WPS UPnP state machine from upnp_wps_device_init()
+ * @client: Client address
+ * @data: NULL terminated M-SEARCH message
+ *
+ * Given that we have received a header w/ M-SEARCH, act upon it
+ *
+ * Format of M-SEARCH (case insensitive!):
+ *
+ * First line must be:
+ * M-SEARCH * HTTP/1.1
+ * Other lines in arbitrary order:
+ * HOST:239.255.255.250:1900
+ * ST:<varies -- must match>
+ * MAN:"ssdp:discover"
+ * MX:<varies>
+ *
+ * It should be noted that when Microsoft Vista is still learning its IP
+ * address, it sends out host lines like: HOST:[FF02::C]:1900
+ */
+static void ssdp_parse_msearch(struct upnp_wps_device_sm *sm,
+ struct sockaddr_in *client, const char *data)
+{
+#ifndef CONFIG_NO_STDOUT_DEBUG
+ const char *start = data;
+#endif /* CONFIG_NO_STDOUT_DEBUG */
+ int got_host = 0;
+ int got_st = 0, st_match = 0;
+ int got_man = 0;
+ int got_mx = 0;
+ int mx = 0;
+
+ /*
+ * Skip first line M-SEARCH * HTTP/1.1
+ * (perhaps we should check remainder of the line for syntax)
+ */
+ data += line_length(data);
+
+ /* Parse remaining lines */
+ for (; *data != '\0'; data += line_length(data)) {
+ if (token_eq(data, "host")) {
+ /* The host line indicates who the packet
+ * is addressed to... but do we really care?
+ * Note that Microsoft sometimes does funny
+ * stuff with the HOST: line.
+ */
+#if 0 /* could be */
+ data += token_length(data);
+ data += word_separation_length(data);
+ if (*data != ':')
+ goto bad;
+ data++;
+ data += word_separation_length(data);
+ /* UPNP_MULTICAST_ADDRESS */
+ if (!str_starts(data, "239.255.255.250"))
+ goto bad;
+ data += os_strlen("239.255.255.250");
+ if (*data == ':') {
+ if (!str_starts(data, ":1900"))
+ goto bad;
+ }
+#endif /* could be */
+ got_host = 1;
+ continue;
+ } else if (token_eq(data, "st")) {
+ /* There are a number of forms; we look
+ * for one that matches our case.
+ */
+ got_st = 1;
+ data += token_length(data);
+ data += word_separation_length(data);
+ if (*data != ':')
+ continue;
+ data++;
+ data += word_separation_length(data);
+ if (str_starts(data, "ssdp:all")) {
+ st_match = 1;
+ continue;
+ }
+ if (str_starts(data, "upnp:rootdevice")) {
+ st_match = 1;
+ continue;
+ }
+ if (str_starts(data, "uuid:")) {
+ char uuid_string[80];
+ struct upnp_wps_device_interface *iface;
+ iface = dl_list_first(
+ &sm->interfaces,
+ struct upnp_wps_device_interface,
+ list);
+ if (!iface)
+ continue;
+ data += os_strlen("uuid:");
+ uuid_bin2str(iface->wps->uuid, uuid_string,
+ sizeof(uuid_string));
+ if (str_starts(data, uuid_string))
+ st_match = 1;
+ continue;
+ }
+#if 0
+ /* FIX: should we really reply to IGD string? */
+ if (str_starts(data, "urn:schemas-upnp-org:device:"
+ "InternetGatewayDevice:1")) {
+ st_match = 1;
+ continue;
+ }
+#endif
+ if (str_starts(data, "urn:schemas-wifialliance-org:"
+ "service:WFAWLANConfig:1")) {
+ st_match = 1;
+ continue;
+ }
+ if (str_starts(data, "urn:schemas-wifialliance-org:"
+ "device:WFADevice:1")) {
+ st_match = 1;
+ continue;
+ }
+ continue;
+ } else if (token_eq(data, "man")) {
+ data += token_length(data);
+ data += word_separation_length(data);
+ if (*data != ':')
+ continue;
+ data++;
+ data += word_separation_length(data);
+ if (!str_starts(data, "\"ssdp:discover\"")) {
+ wpa_printf(MSG_DEBUG, "WPS UPnP: Unexpected "
+ "M-SEARCH man-field");
+ goto bad;
+ }
+ got_man = 1;
+ continue;
+ } else if (token_eq(data, "mx")) {
+ data += token_length(data);
+ data += word_separation_length(data);
+ if (*data != ':')
+ continue;
+ data++;
+ data += word_separation_length(data);
+ mx = atol(data);
+ got_mx = 1;
+ continue;
+ }
+ /* ignore anything else */
+ }
+ if (!got_host || !got_st || !got_man || !got_mx || mx < 0) {
+ wpa_printf(MSG_DEBUG, "WPS UPnP: Invalid M-SEARCH: %d %d %d "
+ "%d mx=%d", got_host, got_st, got_man, got_mx, mx);
+ goto bad;
+ }
+ if (!st_match) {
+ wpa_printf(MSG_DEBUG, "WPS UPnP: Ignored M-SEARCH (no ST "
+ "match)");
+ return;
+ }
+ if (mx > 120)
+ mx = 120; /* UPnP-arch-DeviceArchitecture, 1.2.3 */
+ msearchreply_state_machine_start(sm, client, mx);
+ return;
+
+bad:
+ wpa_printf(MSG_INFO, "WPS UPnP: Failed to parse M-SEARCH");
+ wpa_printf(MSG_MSGDUMP, "WPS UPnP: M-SEARCH data:\n%s", start);
+}
+
+
+/* Listening for (UDP) discovery (M-SEARCH) packets */
+
+/**
+ * ssdp_listener_stop - Stop SSDP listered
+ * @sm: WPS UPnP state machine from upnp_wps_device_init()
+ *
+ * This function stops the SSDP listener that was started by calling
+ * ssdp_listener_start().
+ */
+void ssdp_listener_stop(struct upnp_wps_device_sm *sm)
+{
+ if (sm->ssdp_sd_registered) {
+ eloop_unregister_sock(sm->ssdp_sd, EVENT_TYPE_READ);
+ sm->ssdp_sd_registered = 0;
+ }
+
+ if (sm->ssdp_sd != -1) {
+ close(sm->ssdp_sd);
+ sm->ssdp_sd = -1;
+ }
+
+ eloop_cancel_timeout(msearchreply_state_machine_handler, sm,
+ ELOOP_ALL_CTX);
+}
+
+
+static void ssdp_listener_handler(int sd, void *eloop_ctx, void *sock_ctx)
+{
+ struct upnp_wps_device_sm *sm = sock_ctx;
+ struct sockaddr_in addr; /* client address */
+ socklen_t addr_len;
+ int nread;
+ char buf[MULTICAST_MAX_READ], *pos;
+
+ addr_len = sizeof(addr);
+ nread = recvfrom(sm->ssdp_sd, buf, sizeof(buf) - 1, 0,
+ (struct sockaddr *) &addr, &addr_len);
+ if (nread <= 0)
+ return;
+ buf[nread] = '\0'; /* need null termination for algorithm */
+
+ if (str_starts(buf, "NOTIFY ")) {
+ /*
+ * Silently ignore NOTIFYs to avoid filling debug log with
+ * unwanted messages.
+ */
+ return;
+ }
+
+ pos = os_strchr(buf, '\n');
+ if (pos)
+ *pos = '\0';
+ wpa_printf(MSG_MSGDUMP, "WPS UPnP: Received SSDP packet from %s:%d: "
+ "%s", inet_ntoa(addr.sin_addr), ntohs(addr.sin_port), buf);
+ if (pos)
+ *pos = '\n';
+
+ /* Parse first line */
+ if (os_strncasecmp(buf, "M-SEARCH", os_strlen("M-SEARCH")) == 0 &&
+ !isgraph(buf[strlen("M-SEARCH")])) {
+ ssdp_parse_msearch(sm, &addr, buf);
+ return;
+ }
+
+ /* Ignore anything else */
+}
+
+
+int ssdp_listener_open(void)
+{
+ struct sockaddr_in addr;
+ struct ip_mreq mcast_addr;
+ int on = 1;
+ /* per UPnP spec, keep IP packet time to live (TTL) small */
+ unsigned char ttl = 4;
+ int sd;
+
+ sd = socket(AF_INET, SOCK_DGRAM, 0);
+ if (sd < 0 ||
+ fcntl(sd, F_SETFL, O_NONBLOCK) != 0 ||
+ setsockopt(sd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)))
+ goto fail;
+ os_memset(&addr, 0, sizeof(addr));
+ addr.sin_family = AF_INET;
+ addr.sin_addr.s_addr = htonl(INADDR_ANY);
+ addr.sin_port = htons(UPNP_MULTICAST_PORT);
+ if (bind(sd, (struct sockaddr *) &addr, sizeof(addr)))
+ goto fail;
+ os_memset(&mcast_addr, 0, sizeof(mcast_addr));
+ mcast_addr.imr_interface.s_addr = htonl(INADDR_ANY);
+ mcast_addr.imr_multiaddr.s_addr = inet_addr(UPNP_MULTICAST_ADDRESS);
+ if (setsockopt(sd, IPPROTO_IP, IP_ADD_MEMBERSHIP,
+ (char *) &mcast_addr, sizeof(mcast_addr)) ||
+ setsockopt(sd, IPPROTO_IP, IP_MULTICAST_TTL,
+ &ttl, sizeof(ttl)))
+ goto fail;
+
+ return sd;
+
+fail:
+ if (sd >= 0)
+ close(sd);
+ return -1;
+}
+
+
+/**
+ * ssdp_listener_start - Set up for receiving discovery (UDP) packets
+ * @sm: WPS UPnP state machine from upnp_wps_device_init()
+ * Returns: 0 on success, -1 on failure
+ *
+ * The SSDP listener is stopped by calling ssdp_listener_stop().
+ */
+int ssdp_listener_start(struct upnp_wps_device_sm *sm)
+{
+ sm->ssdp_sd = ssdp_listener_open();
+
+ if (eloop_register_sock(sm->ssdp_sd, EVENT_TYPE_READ,
+ ssdp_listener_handler, NULL, sm))
+ goto fail;
+ sm->ssdp_sd_registered = 1;
+ return 0;
+
+fail:
+ /* Error */
+ wpa_printf(MSG_ERROR, "WPS UPnP: ssdp_listener_start failed");
+ ssdp_listener_stop(sm);
+ return -1;
+}
+
+
+/**
+ * add_ssdp_network - Add routing entry for SSDP
+ * @net_if: Selected network interface name
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function assures that the multicast address will be properly
+ * handled by Linux networking code (by a modification to routing tables).
+ * This must be done per network interface. It really only needs to be done
+ * once after booting up, but it does not hurt to call this more frequently
+ * "to be safe".
+ */
+int add_ssdp_network(const char *net_if)
+{
+#ifdef __linux__
+ int ret = -1;
+ int sock = -1;
+ struct rtentry rt;
+ struct sockaddr_in *sin;
+
+ if (!net_if)
+ goto fail;
+
+ os_memset(&rt, 0, sizeof(rt));
+ sock = socket(AF_INET, SOCK_DGRAM, 0);
+ if (sock < 0)
+ goto fail;
+
+ rt.rt_dev = (char *) net_if;
+ sin = aliasing_hide_typecast(&rt.rt_dst, struct sockaddr_in);
+ sin->sin_family = AF_INET;
+ sin->sin_port = 0;
+ sin->sin_addr.s_addr = inet_addr(SSDP_TARGET);
+ sin = aliasing_hide_typecast(&rt.rt_genmask, struct sockaddr_in);
+ sin->sin_family = AF_INET;
+ sin->sin_port = 0;
+ sin->sin_addr.s_addr = inet_addr(SSDP_NETMASK);
+ rt.rt_flags = RTF_UP;
+ if (ioctl(sock, SIOCADDRT, &rt) < 0) {
+ if (errno == EPERM) {
+ wpa_printf(MSG_DEBUG, "add_ssdp_network: No "
+ "permissions to add routing table entry");
+ /* Continue to allow testing as non-root */
+ } else if (errno != EEXIST) {
+ wpa_printf(MSG_INFO, "add_ssdp_network() ioctl errno "
+ "%d (%s)", errno, strerror(errno));
+ goto fail;
+ }
+ }
+
+ ret = 0;
+
+fail:
+ if (sock >= 0)
+ close(sock);
+
+ return ret;
+#else /* __linux__ */
+ return 0;
+#endif /* __linux__ */
+}
+
+
+int ssdp_open_multicast_sock(u32 ip_addr, const char *forced_ifname)
+{
+ int sd;
+ /* per UPnP-arch-DeviceArchitecture, 1. Discovery, keep IP packet
+ * time to live (TTL) small */
+ unsigned char ttl = 4;
+
+ sd = socket(AF_INET, SOCK_DGRAM, 0);
+ if (sd < 0)
+ return -1;
+
+ if (forced_ifname) {
+#ifdef __linux__
+ struct ifreq req;
+ os_memset(&req, 0, sizeof(req));
+ os_strlcpy(req.ifr_name, forced_ifname, sizeof(req.ifr_name));
+ if (setsockopt(sd, SOL_SOCKET, SO_BINDTODEVICE, &req,
+ sizeof(req)) < 0) {
+ wpa_printf(MSG_INFO, "WPS UPnP: Failed to bind "
+ "multicast socket to ifname %s: %s",
+ forced_ifname, strerror(errno));
+ close(sd);
+ return -1;
+ }
+#endif /* __linux__ */
+ }
+
+#if 0 /* maybe ok if we sometimes block on writes */
+ if (fcntl(sd, F_SETFL, O_NONBLOCK) != 0) {
+ close(sd);
+ return -1;
+ }
+#endif
+
+ if (setsockopt(sd, IPPROTO_IP, IP_MULTICAST_IF,
+ &ip_addr, sizeof(ip_addr))) {
+ wpa_printf(MSG_DEBUG, "WPS: setsockopt(IP_MULTICAST_IF) %x: "
+ "%d (%s)", ip_addr, errno, strerror(errno));
+ close(sd);
+ return -1;
+ }
+ if (setsockopt(sd, IPPROTO_IP, IP_MULTICAST_TTL,
+ &ttl, sizeof(ttl))) {
+ wpa_printf(MSG_DEBUG, "WPS: setsockopt(IP_MULTICAST_TTL): "
+ "%d (%s)", errno, strerror(errno));
+ close(sd);
+ return -1;
+ }
+
+#if 0 /* not needed, because we don't receive using multicast_sd */
+ {
+ struct ip_mreq mreq;
+ mreq.imr_multiaddr.s_addr = inet_addr(UPNP_MULTICAST_ADDRESS);
+ mreq.imr_interface.s_addr = ip_addr;
+ wpa_printf(MSG_DEBUG, "WPS UPnP: Multicast addr 0x%x if addr "
+ "0x%x",
+ mreq.imr_multiaddr.s_addr,
+ mreq.imr_interface.s_addr);
+ if (setsockopt(sd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq,
+ sizeof(mreq))) {
+ wpa_printf(MSG_ERROR,
+ "WPS UPnP: setsockopt "
+ "IP_ADD_MEMBERSHIP errno %d (%s)",
+ errno, strerror(errno));
+ close(sd);
+ return -1;
+ }
+ }
+#endif /* not needed */
+
+ /*
+ * TODO: What about IP_MULTICAST_LOOP? It seems to be on by default?
+ * which aids debugging I suppose but isn't really necessary?
+ */
+
+ return sd;
+}
+
+
+/**
+ * ssdp_open_multicast - Open socket for sending multicast SSDP messages
+ * @sm: WPS UPnP state machine from upnp_wps_device_init()
+ * Returns: 0 on success, -1 on failure
+ */
+int ssdp_open_multicast(struct upnp_wps_device_sm *sm)
+{
+ sm->multicast_sd = ssdp_open_multicast_sock(sm->ip_addr, NULL);
+ if (sm->multicast_sd < 0)
+ return -1;
+ return 0;
+}
diff --git a/freebsd/contrib/wpa/src/wps/wps_upnp_web.c b/freebsd/contrib/wpa/src/wps/wps_upnp_web.c
new file mode 100644
index 00000000..8c3bbd1b
--- /dev/null
+++ b/freebsd/contrib/wpa/src/wps/wps_upnp_web.c
@@ -0,0 +1,1352 @@
+#include <machine/rtems-bsd-user-space.h>
+
+/*
+ * UPnP WPS Device - Web connections
+ * Copyright (c) 2000-2003 Intel Corporation
+ * Copyright (c) 2006-2007 Sony Corporation
+ * Copyright (c) 2008-2009 Atheros Communications
+ * Copyright (c) 2009, Jouni Malinen <j@w1.fi>
+ *
+ * See wps_upnp.c for more details on licensing and code history.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "base64.h"
+#include "uuid.h"
+#include "httpread.h"
+#include "http_server.h"
+#include "wps_i.h"
+#include "wps_upnp.h"
+#include "wps_upnp_i.h"
+#include "upnp_xml.h"
+
+/***************************************************************************
+ * Web connections (we serve pages of info about ourselves, handle
+ * requests, etc. etc.).
+ **************************************************************************/
+
+#define WEB_CONNECTION_TIMEOUT_SEC 30 /* Drop web connection after t.o. */
+#define WEB_CONNECTION_MAX_READ 8000 /* Max we'll read for TCP request */
+#define MAX_WEB_CONNECTIONS 10 /* max simultaneous web connects */
+
+
+static const char *urn_wfawlanconfig =
+ "urn:schemas-wifialliance-org:service:WFAWLANConfig:1";
+static const char *http_server_hdr =
+ "Server: unspecified, UPnP/1.0, unspecified\r\n";
+static const char *http_connection_close =
+ "Connection: close\r\n";
+
+/*
+ * "Files" that we serve via HTTP. The format of these files is given by
+ * WFA WPS specifications. Extra white space has been removed to save space.
+ */
+
+static const char wps_scpd_xml[] =
+"<?xml version=\"1.0\"?>\n"
+"<scpd xmlns=\"urn:schemas-upnp-org:service-1-0\">\n"
+"<specVersion><major>1</major><minor>0</minor></specVersion>\n"
+"<actionList>\n"
+"<action>\n"
+"<name>GetDeviceInfo</name>\n"
+"<argumentList>\n"
+"<argument>\n"
+"<name>NewDeviceInfo</name>\n"
+"<direction>out</direction>\n"
+"<relatedStateVariable>DeviceInfo</relatedStateVariable>\n"
+"</argument>\n"
+"</argumentList>\n"
+"</action>\n"
+"<action>\n"
+"<name>PutMessage</name>\n"
+"<argumentList>\n"
+"<argument>\n"
+"<name>NewInMessage</name>\n"
+"<direction>in</direction>\n"
+"<relatedStateVariable>InMessage</relatedStateVariable>\n"
+"</argument>\n"
+"<argument>\n"
+"<name>NewOutMessage</name>\n"
+"<direction>out</direction>\n"
+"<relatedStateVariable>OutMessage</relatedStateVariable>\n"
+"</argument>\n"
+"</argumentList>\n"
+"</action>\n"
+"<action>\n"
+"<name>PutWLANResponse</name>\n"
+"<argumentList>\n"
+"<argument>\n"
+"<name>NewMessage</name>\n"
+"<direction>in</direction>\n"
+"<relatedStateVariable>Message</relatedStateVariable>\n"
+"</argument>\n"
+"<argument>\n"
+"<name>NewWLANEventType</name>\n"
+"<direction>in</direction>\n"
+"<relatedStateVariable>WLANEventType</relatedStateVariable>\n"
+"</argument>\n"
+"<argument>\n"
+"<name>NewWLANEventMAC</name>\n"
+"<direction>in</direction>\n"
+"<relatedStateVariable>WLANEventMAC</relatedStateVariable>\n"
+"</argument>\n"
+"</argumentList>\n"
+"</action>\n"
+"<action>\n"
+"<name>SetSelectedRegistrar</name>\n"
+"<argumentList>\n"
+"<argument>\n"
+"<name>NewMessage</name>\n"
+"<direction>in</direction>\n"
+"<relatedStateVariable>Message</relatedStateVariable>\n"
+"</argument>\n"
+"</argumentList>\n"
+"</action>\n"
+"</actionList>\n"
+"<serviceStateTable>\n"
+"<stateVariable sendEvents=\"no\">\n"
+"<name>Message</name>\n"
+"<dataType>bin.base64</dataType>\n"
+"</stateVariable>\n"
+"<stateVariable sendEvents=\"no\">\n"
+"<name>InMessage</name>\n"
+"<dataType>bin.base64</dataType>\n"
+"</stateVariable>\n"
+"<stateVariable sendEvents=\"no\">\n"
+"<name>OutMessage</name>\n"
+"<dataType>bin.base64</dataType>\n"
+"</stateVariable>\n"
+"<stateVariable sendEvents=\"no\">\n"
+"<name>DeviceInfo</name>\n"
+"<dataType>bin.base64</dataType>\n"
+"</stateVariable>\n"
+"<stateVariable sendEvents=\"yes\">\n"
+"<name>APStatus</name>\n"
+"<dataType>ui1</dataType>\n"
+"</stateVariable>\n"
+"<stateVariable sendEvents=\"yes\">\n"
+"<name>STAStatus</name>\n"
+"<dataType>ui1</dataType>\n"
+"</stateVariable>\n"
+"<stateVariable sendEvents=\"yes\">\n"
+"<name>WLANEvent</name>\n"
+"<dataType>bin.base64</dataType>\n"
+"</stateVariable>\n"
+"<stateVariable sendEvents=\"no\">\n"
+"<name>WLANEventType</name>\n"
+"<dataType>ui1</dataType>\n"
+"</stateVariable>\n"
+"<stateVariable sendEvents=\"no\">\n"
+"<name>WLANEventMAC</name>\n"
+"<dataType>string</dataType>\n"
+"</stateVariable>\n"
+"<stateVariable sendEvents=\"no\">\n"
+"<name>WLANResponse</name>\n"
+"<dataType>bin.base64</dataType>\n"
+"</stateVariable>\n"
+"</serviceStateTable>\n"
+"</scpd>\n"
+;
+
+
+static const char *wps_device_xml_prefix =
+ "<?xml version=\"1.0\"?>\n"
+ "<root xmlns=\"urn:schemas-upnp-org:device-1-0\">\n"
+ "<specVersion>\n"
+ "<major>1</major>\n"
+ "<minor>0</minor>\n"
+ "</specVersion>\n"
+ "<device>\n"
+ "<deviceType>urn:schemas-wifialliance-org:device:WFADevice:1"
+ "</deviceType>\n";
+
+static const char *wps_device_xml_postfix =
+ "<serviceList>\n"
+ "<service>\n"
+ "<serviceType>urn:schemas-wifialliance-org:service:WFAWLANConfig:1"
+ "</serviceType>\n"
+ "<serviceId>urn:wifialliance-org:serviceId:WFAWLANConfig1</serviceId>"
+ "\n"
+ "<SCPDURL>" UPNP_WPS_SCPD_XML_FILE "</SCPDURL>\n"
+ "<controlURL>" UPNP_WPS_DEVICE_CONTROL_FILE "</controlURL>\n"
+ "<eventSubURL>" UPNP_WPS_DEVICE_EVENT_FILE "</eventSubURL>\n"
+ "</service>\n"
+ "</serviceList>\n"
+ "</device>\n"
+ "</root>\n";
+
+
+/* format_wps_device_xml -- produce content of "file" wps_device.xml
+ * (UPNP_WPS_DEVICE_XML_FILE)
+ */
+static void format_wps_device_xml(struct upnp_wps_device_interface *iface,
+ struct upnp_wps_device_sm *sm,
+ struct wpabuf *buf)
+{
+ const char *s;
+ char uuid_string[80];
+
+ wpabuf_put_str(buf, wps_device_xml_prefix);
+
+ /*
+ * Add required fields with default values if not configured. Add
+ * optional and recommended fields only if configured.
+ */
+ s = iface->wps->friendly_name;
+ s = ((s && *s) ? s : "WPS Access Point");
+ xml_add_tagged_data(buf, "friendlyName", s);
+
+ s = iface->wps->dev.manufacturer;
+ s = ((s && *s) ? s : "");
+ xml_add_tagged_data(buf, "manufacturer", s);
+
+ if (iface->wps->manufacturer_url)
+ xml_add_tagged_data(buf, "manufacturerURL",
+ iface->wps->manufacturer_url);
+
+ if (iface->wps->model_description)
+ xml_add_tagged_data(buf, "modelDescription",
+ iface->wps->model_description);
+
+ s = iface->wps->dev.model_name;
+ s = ((s && *s) ? s : "");
+ xml_add_tagged_data(buf, "modelName", s);
+
+ if (iface->wps->dev.model_number)
+ xml_add_tagged_data(buf, "modelNumber",
+ iface->wps->dev.model_number);
+
+ if (iface->wps->model_url)
+ xml_add_tagged_data(buf, "modelURL", iface->wps->model_url);
+
+ if (iface->wps->dev.serial_number)
+ xml_add_tagged_data(buf, "serialNumber",
+ iface->wps->dev.serial_number);
+
+ uuid_bin2str(iface->wps->uuid, uuid_string, sizeof(uuid_string));
+ s = uuid_string;
+ /* Need "uuid:" prefix, thus we can't use xml_add_tagged_data()
+ * easily...
+ */
+ wpabuf_put_str(buf, "<UDN>uuid:");
+ xml_data_encode(buf, s, os_strlen(s));
+ wpabuf_put_str(buf, "</UDN>\n");
+
+ if (iface->wps->upc)
+ xml_add_tagged_data(buf, "UPC", iface->wps->upc);
+
+ wpabuf_put_str(buf, wps_device_xml_postfix);
+}
+
+
+static void http_put_reply_code(struct wpabuf *buf, enum http_reply_code code)
+{
+ wpabuf_put_str(buf, "HTTP/1.1 ");
+ switch (code) {
+ case HTTP_OK:
+ wpabuf_put_str(buf, "200 OK\r\n");
+ break;
+ case HTTP_BAD_REQUEST:
+ wpabuf_put_str(buf, "400 Bad request\r\n");
+ break;
+ case HTTP_PRECONDITION_FAILED:
+ wpabuf_put_str(buf, "412 Precondition failed\r\n");
+ break;
+ case HTTP_UNIMPLEMENTED:
+ wpabuf_put_str(buf, "501 Unimplemented\r\n");
+ break;
+ case HTTP_INTERNAL_SERVER_ERROR:
+ default:
+ wpabuf_put_str(buf, "500 Internal server error\r\n");
+ break;
+ }
+}
+
+
+static void http_put_date(struct wpabuf *buf)
+{
+ wpabuf_put_str(buf, "Date: ");
+ format_date(buf);
+ wpabuf_put_str(buf, "\r\n");
+}
+
+
+static void http_put_empty(struct wpabuf *buf, enum http_reply_code code)
+{
+ http_put_reply_code(buf, code);
+ wpabuf_put_str(buf, http_server_hdr);
+ wpabuf_put_str(buf, http_connection_close);
+ wpabuf_put_str(buf, "Content-Length: 0\r\n"
+ "\r\n");
+}
+
+
+/* Given that we have received a header w/ GET, act upon it
+ *
+ * Format of GET (case-insensitive):
+ *
+ * First line must be:
+ * GET /<file> HTTP/1.1
+ * Since we don't do anything fancy we just ignore other lines.
+ *
+ * Our response (if no error) which includes only required lines is:
+ * HTTP/1.1 200 OK
+ * Connection: close
+ * Content-Type: text/xml
+ * Date: <rfc1123-date>
+ *
+ * Header lines must end with \r\n
+ * Per RFC 2616, content-length: is not required but connection:close
+ * would appear to be required (given that we will be closing it!).
+ */
+static void web_connection_parse_get(struct upnp_wps_device_sm *sm,
+ struct http_request *hreq, char *filename)
+{
+ struct wpabuf *buf; /* output buffer, allocated */
+ char *put_length_here;
+ char *body_start;
+ enum {
+ GET_DEVICE_XML_FILE,
+ GET_SCPD_XML_FILE
+ } req;
+ size_t extra_len = 0;
+ int body_length;
+ char len_buf[10];
+ struct upnp_wps_device_interface *iface;
+
+ iface = dl_list_first(&sm->interfaces,
+ struct upnp_wps_device_interface, list);
+ if (iface == NULL) {
+ http_request_deinit(hreq);
+ return;
+ }
+
+ /*
+ * It is not required that filenames be case insensitive but it is
+ * allowed and cannot hurt here.
+ */
+ if (os_strcasecmp(filename, UPNP_WPS_DEVICE_XML_FILE) == 0) {
+ wpa_printf(MSG_DEBUG, "WPS UPnP: HTTP GET for device XML");
+ req = GET_DEVICE_XML_FILE;
+ extra_len = 3000;
+ if (iface->wps->friendly_name)
+ extra_len += os_strlen(iface->wps->friendly_name);
+ if (iface->wps->manufacturer_url)
+ extra_len += os_strlen(iface->wps->manufacturer_url);
+ if (iface->wps->model_description)
+ extra_len += os_strlen(iface->wps->model_description);
+ if (iface->wps->model_url)
+ extra_len += os_strlen(iface->wps->model_url);
+ if (iface->wps->upc)
+ extra_len += os_strlen(iface->wps->upc);
+ } else if (!os_strcasecmp(filename, UPNP_WPS_SCPD_XML_FILE)) {
+ wpa_printf(MSG_DEBUG, "WPS UPnP: HTTP GET for SCPD XML");
+ req = GET_SCPD_XML_FILE;
+ extra_len = os_strlen(wps_scpd_xml);
+ } else {
+ /* File not found */
+ wpa_printf(MSG_DEBUG, "WPS UPnP: HTTP GET file not found: %s",
+ filename);
+ buf = wpabuf_alloc(200);
+ if (buf == NULL) {
+ http_request_deinit(hreq);
+ return;
+ }
+ wpabuf_put_str(buf,
+ "HTTP/1.1 404 Not Found\r\n"
+ "Connection: close\r\n");
+
+ http_put_date(buf);
+
+ /* terminating empty line */
+ wpabuf_put_str(buf, "\r\n");
+
+ goto send_buf;
+ }
+
+ buf = wpabuf_alloc(1000 + extra_len);
+ if (buf == NULL) {
+ http_request_deinit(hreq);
+ return;
+ }
+
+ wpabuf_put_str(buf,
+ "HTTP/1.1 200 OK\r\n"
+ "Content-Type: text/xml; charset=\"utf-8\"\r\n");
+ wpabuf_put_str(buf, "Server: Unspecified, UPnP/1.0, Unspecified\r\n");
+ wpabuf_put_str(buf, "Connection: close\r\n");
+ wpabuf_put_str(buf, "Content-Length: ");
+ /*
+ * We will paste the length in later, leaving some extra whitespace.
+ * HTTP code is supposed to be tolerant of extra whitespace.
+ */
+ put_length_here = wpabuf_put(buf, 0);
+ wpabuf_put_str(buf, " \r\n");
+
+ http_put_date(buf);
+
+ /* terminating empty line */
+ wpabuf_put_str(buf, "\r\n");
+
+ body_start = wpabuf_put(buf, 0);
+
+ switch (req) {
+ case GET_DEVICE_XML_FILE:
+ format_wps_device_xml(iface, sm, buf);
+ break;
+ case GET_SCPD_XML_FILE:
+ wpabuf_put_str(buf, wps_scpd_xml);
+ break;
+ }
+
+ /* Now patch in the content length at the end */
+ body_length = (char *) wpabuf_put(buf, 0) - body_start;
+ os_snprintf(len_buf, 10, "%d", body_length);
+ os_memcpy(put_length_here, len_buf, os_strlen(len_buf));
+
+send_buf:
+ http_request_send_and_deinit(hreq, buf);
+}
+
+
+static enum http_reply_code
+web_process_get_device_info(struct upnp_wps_device_sm *sm,
+ struct wpabuf **reply, const char **replyname)
+{
+ static const char *name = "NewDeviceInfo";
+ struct wps_config cfg;
+ struct upnp_wps_device_interface *iface;
+ struct upnp_wps_peer *peer;
+
+ iface = dl_list_first(&sm->interfaces,
+ struct upnp_wps_device_interface, list);
+
+ wpa_printf(MSG_DEBUG, "WPS UPnP: GetDeviceInfo");
+
+ if (!iface || iface->ctx->ap_pin == NULL)
+ return HTTP_INTERNAL_SERVER_ERROR;
+
+ peer = &iface->peer;
+
+ /*
+ * Request for DeviceInfo, i.e., M1 TLVs. This is a start of WPS
+ * registration over UPnP with the AP acting as an Enrollee. It should
+ * be noted that this is frequently used just to get the device data,
+ * i.e., there may not be any intent to actually complete the
+ * registration.
+ */
+
+ if (peer->wps)
+ wps_deinit(peer->wps);
+
+ os_memset(&cfg, 0, sizeof(cfg));
+ cfg.wps = iface->wps;
+ cfg.pin = (u8 *) iface->ctx->ap_pin;
+ cfg.pin_len = os_strlen(iface->ctx->ap_pin);
+ peer->wps = wps_init(&cfg);
+ if (peer->wps) {
+ enum wsc_op_code op_code;
+ *reply = wps_get_msg(peer->wps, &op_code);
+ if (*reply == NULL) {
+ wps_deinit(peer->wps);
+ peer->wps = NULL;
+ }
+ } else
+ *reply = NULL;
+ if (*reply == NULL) {
+ wpa_printf(MSG_INFO, "WPS UPnP: Failed to get DeviceInfo");
+ return HTTP_INTERNAL_SERVER_ERROR;
+ }
+ *replyname = name;
+ return HTTP_OK;
+}
+
+
+static enum http_reply_code
+web_process_put_message(struct upnp_wps_device_sm *sm, char *data,
+ struct wpabuf **reply, const char **replyname)
+{
+ struct wpabuf *msg;
+ static const char *name = "NewOutMessage";
+ enum http_reply_code ret;
+ enum wps_process_res res;
+ enum wsc_op_code op_code;
+ struct upnp_wps_device_interface *iface;
+
+ iface = dl_list_first(&sm->interfaces,
+ struct upnp_wps_device_interface, list);
+ if (!iface)
+ return HTTP_INTERNAL_SERVER_ERROR;
+
+ /*
+ * PutMessage is used by external UPnP-based Registrar to perform WPS
+ * operation with the access point itself; as compared with
+ * PutWLANResponse which is for proxying.
+ */
+ wpa_printf(MSG_DEBUG, "WPS UPnP: PutMessage");
+ msg = xml_get_base64_item(data, "NewInMessage", &ret);
+ if (msg == NULL)
+ return ret;
+ res = wps_process_msg(iface->peer.wps, WSC_UPnP, msg);
+ if (res == WPS_FAILURE)
+ *reply = NULL;
+ else
+ *reply = wps_get_msg(iface->peer.wps, &op_code);
+ wpabuf_free(msg);
+ if (*reply == NULL)
+ return HTTP_INTERNAL_SERVER_ERROR;
+ *replyname = name;
+ return HTTP_OK;
+}
+
+
+static enum http_reply_code
+web_process_put_wlan_response(struct upnp_wps_device_sm *sm, char *data,
+ struct wpabuf **reply, const char **replyname)
+{
+ struct wpabuf *msg;
+ enum http_reply_code ret;
+ u8 macaddr[ETH_ALEN];
+ int ev_type;
+ int type;
+ char *val;
+ struct upnp_wps_device_interface *iface;
+ int ok = 0;
+
+ /*
+ * External UPnP-based Registrar is passing us a message to be proxied
+ * over to a Wi-Fi -based client of ours.
+ */
+
+ wpa_printf(MSG_DEBUG, "WPS UPnP: PutWLANResponse");
+ msg = xml_get_base64_item(data, "NewMessage", &ret);
+ if (msg == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS UPnP: Could not extract NewMessage "
+ "from PutWLANResponse");
+ return ret;
+ }
+ val = xml_get_first_item(data, "NewWLANEventType");
+ if (val == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS UPnP: No NewWLANEventType in "
+ "PutWLANResponse");
+ wpabuf_free(msg);
+ return UPNP_ARG_VALUE_INVALID;
+ }
+ ev_type = atol(val);
+ os_free(val);
+ val = xml_get_first_item(data, "NewWLANEventMAC");
+ if (val == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS UPnP: No NewWLANEventMAC in "
+ "PutWLANResponse");
+ wpabuf_free(msg);
+ return UPNP_ARG_VALUE_INVALID;
+ }
+ if (hwaddr_aton(val, macaddr)) {
+ wpa_printf(MSG_DEBUG, "WPS UPnP: Invalid NewWLANEventMAC in "
+ "PutWLANResponse: '%s'", val);
+#ifdef CONFIG_WPS_STRICT
+ {
+ struct wps_parse_attr attr;
+ if (wps_parse_msg(msg, &attr) < 0 || attr.version2) {
+ wpabuf_free(msg);
+ os_free(val);
+ return UPNP_ARG_VALUE_INVALID;
+ }
+ }
+#endif /* CONFIG_WPS_STRICT */
+ if (hwaddr_aton2(val, macaddr) > 0) {
+ /*
+ * At least some versions of Intel PROset seem to be
+ * using dot-deliminated MAC address format here.
+ */
+ wpa_printf(MSG_DEBUG, "WPS UPnP: Workaround - allow "
+ "incorrect MAC address format in "
+ "NewWLANEventMAC: %s -> " MACSTR,
+ val, MAC2STR(macaddr));
+ } else {
+ wpabuf_free(msg);
+ os_free(val);
+ return UPNP_ARG_VALUE_INVALID;
+ }
+ }
+ os_free(val);
+ if (ev_type == UPNP_WPS_WLANEVENT_TYPE_EAP) {
+ struct wps_parse_attr attr;
+ if (wps_parse_msg(msg, &attr) < 0 ||
+ attr.msg_type == NULL)
+ type = -1;
+ else
+ type = *attr.msg_type;
+ wpa_printf(MSG_DEBUG, "WPS UPnP: Message Type %d", type);
+ } else
+ type = -1;
+ dl_list_for_each(iface, &sm->interfaces,
+ struct upnp_wps_device_interface, list) {
+ if (iface->ctx->rx_req_put_wlan_response &&
+ iface->ctx->rx_req_put_wlan_response(iface->priv, ev_type,
+ macaddr, msg, type)
+ == 0)
+ ok = 1;
+ }
+
+ if (!ok) {
+ wpa_printf(MSG_INFO, "WPS UPnP: Fail: sm->ctx->"
+ "rx_req_put_wlan_response");
+ wpabuf_free(msg);
+ return HTTP_INTERNAL_SERVER_ERROR;
+ }
+ wpabuf_free(msg);
+ *replyname = NULL;
+ *reply = NULL;
+ return HTTP_OK;
+}
+
+
+static int find_er_addr(struct subscription *s, struct sockaddr_in *cli)
+{
+ struct subscr_addr *a;
+
+ dl_list_for_each(a, &s->addr_list, struct subscr_addr, list) {
+ if (cli->sin_addr.s_addr == a->saddr.sin_addr.s_addr)
+ return 1;
+ }
+ return 0;
+}
+
+
+static struct subscription * find_er(struct upnp_wps_device_sm *sm,
+ struct sockaddr_in *cli)
+{
+ struct subscription *s;
+ dl_list_for_each(s, &sm->subscriptions, struct subscription, list)
+ if (find_er_addr(s, cli))
+ return s;
+ return NULL;
+}
+
+
+static enum http_reply_code
+web_process_set_selected_registrar(struct upnp_wps_device_sm *sm,
+ struct sockaddr_in *cli, char *data,
+ struct wpabuf **reply,
+ const char **replyname)
+{
+ struct wpabuf *msg;
+ enum http_reply_code ret;
+ struct subscription *s;
+ struct upnp_wps_device_interface *iface;
+ int err = 0;
+
+ wpa_printf(MSG_DEBUG, "WPS UPnP: SetSelectedRegistrar");
+ s = find_er(sm, cli);
+ if (s == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS UPnP: Ignore SetSelectedRegistrar "
+ "from unknown ER");
+ return UPNP_ACTION_FAILED;
+ }
+ msg = xml_get_base64_item(data, "NewMessage", &ret);
+ if (msg == NULL)
+ return ret;
+ dl_list_for_each(iface, &sm->interfaces,
+ struct upnp_wps_device_interface, list) {
+ if (upnp_er_set_selected_registrar(iface->wps->registrar, s,
+ msg))
+ err = 1;
+ }
+ wpabuf_free(msg);
+ if (err)
+ return HTTP_INTERNAL_SERVER_ERROR;
+ *replyname = NULL;
+ *reply = NULL;
+ return HTTP_OK;
+}
+
+
+static const char *soap_prefix =
+ "<?xml version=\"1.0\"?>\n"
+ "<s:Envelope xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\" "
+ "s:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">\n"
+ "<s:Body>\n";
+static const char *soap_postfix =
+ "</s:Body>\n</s:Envelope>\n";
+
+static const char *soap_error_prefix =
+ "<s:Fault>\n"
+ "<faultcode>s:Client</faultcode>\n"
+ "<faultstring>UPnPError</faultstring>\n"
+ "<detail>\n"
+ "<UPnPError xmlns=\"urn:schemas-upnp-org:control-1-0\">\n";
+static const char *soap_error_postfix =
+ "<errorDescription>Error</errorDescription>\n"
+ "</UPnPError>\n"
+ "</detail>\n"
+ "</s:Fault>\n";
+
+static void web_connection_send_reply(struct http_request *req,
+ enum http_reply_code ret,
+ const char *action, int action_len,
+ const struct wpabuf *reply,
+ const char *replyname)
+{
+ struct wpabuf *buf;
+ char *replydata;
+ char *put_length_here = NULL;
+ char *body_start = NULL;
+
+ if (reply) {
+ size_t len;
+ replydata = (char *) base64_encode(wpabuf_head(reply),
+ wpabuf_len(reply), &len);
+ } else
+ replydata = NULL;
+
+ /* Parameters of the response:
+ * action(action_len) -- action we are responding to
+ * replyname -- a name we need for the reply
+ * replydata -- NULL or null-terminated string
+ */
+ buf = wpabuf_alloc(1000 + (replydata ? os_strlen(replydata) : 0U) +
+ (action_len > 0 ? action_len * 2 : 0));
+ if (buf == NULL) {
+ wpa_printf(MSG_INFO, "WPS UPnP: Cannot allocate reply to "
+ "POST");
+ os_free(replydata);
+ http_request_deinit(req);
+ return;
+ }
+
+ /*
+ * Assuming we will be successful, put in the output header first.
+ * Note: we do not keep connections alive (and httpread does
+ * not support it)... therefore we must have Connection: close.
+ */
+ if (ret == HTTP_OK) {
+ wpabuf_put_str(buf,
+ "HTTP/1.1 200 OK\r\n"
+ "Content-Type: text/xml; "
+ "charset=\"utf-8\"\r\n");
+ } else {
+ wpabuf_printf(buf, "HTTP/1.1 %d Error\r\n", ret);
+ }
+ wpabuf_put_str(buf, http_connection_close);
+
+ wpabuf_put_str(buf, "Content-Length: ");
+ /*
+ * We will paste the length in later, leaving some extra whitespace.
+ * HTTP code is supposed to be tolerant of extra whitespace.
+ */
+ put_length_here = wpabuf_put(buf, 0);
+ wpabuf_put_str(buf, " \r\n");
+
+ http_put_date(buf);
+
+ /* terminating empty line */
+ wpabuf_put_str(buf, "\r\n");
+
+ body_start = wpabuf_put(buf, 0);
+
+ if (ret == HTTP_OK) {
+ wpabuf_put_str(buf, soap_prefix);
+ wpabuf_put_str(buf, "<u:");
+ wpabuf_put_data(buf, action, action_len);
+ wpabuf_put_str(buf, "Response xmlns:u=\"");
+ wpabuf_put_str(buf, urn_wfawlanconfig);
+ wpabuf_put_str(buf, "\">\n");
+ if (replydata && replyname) {
+ /* TODO: might possibly need to escape part of reply
+ * data? ...
+ * probably not, unlikely to have ampersand(&) or left
+ * angle bracket (<) in it...
+ */
+ wpabuf_printf(buf, "<%s>", replyname);
+ wpabuf_put_str(buf, replydata);
+ wpabuf_printf(buf, "</%s>\n", replyname);
+ }
+ wpabuf_put_str(buf, "</u:");
+ wpabuf_put_data(buf, action, action_len);
+ wpabuf_put_str(buf, "Response>\n");
+ wpabuf_put_str(buf, soap_postfix);
+ } else {
+ /* Error case */
+ wpabuf_put_str(buf, soap_prefix);
+ wpabuf_put_str(buf, soap_error_prefix);
+ wpabuf_printf(buf, "<errorCode>%d</errorCode>\n", ret);
+ wpabuf_put_str(buf, soap_error_postfix);
+ wpabuf_put_str(buf, soap_postfix);
+ }
+ os_free(replydata);
+
+ /* Now patch in the content length at the end */
+ if (body_start && put_length_here) {
+ int body_length = (char *) wpabuf_put(buf, 0) - body_start;
+ char len_buf[10];
+ os_snprintf(len_buf, sizeof(len_buf), "%d", body_length);
+ os_memcpy(put_length_here, len_buf, os_strlen(len_buf));
+ }
+
+ http_request_send_and_deinit(req, buf);
+}
+
+
+static const char * web_get_action(struct http_request *req,
+ size_t *action_len)
+{
+ const char *match;
+ int match_len;
+ char *b;
+ char *action;
+
+ *action_len = 0;
+ /* The SOAPAction line of the header tells us what we want to do */
+ b = http_request_get_hdr_line(req, "SOAPAction:");
+ if (b == NULL)
+ return NULL;
+ if (*b == '"')
+ b++;
+ else
+ return NULL;
+ match = urn_wfawlanconfig;
+ match_len = os_strlen(urn_wfawlanconfig) - 1;
+ if (os_strncasecmp(b, match, match_len))
+ return NULL;
+ b += match_len;
+ /* skip over version */
+ while (isgraph(*b) && *b != '#')
+ b++;
+ if (*b != '#')
+ return NULL;
+ b++;
+ /* Following the sharp(#) should be the action and a double quote */
+ action = b;
+ while (isgraph(*b) && *b != '"')
+ b++;
+ if (*b != '"')
+ return NULL;
+ *action_len = b - action;
+ return action;
+}
+
+
+/* Given that we have received a header w/ POST, act upon it
+ *
+ * Format of POST (case-insensitive):
+ *
+ * First line must be:
+ * POST /<file> HTTP/1.1
+ * Since we don't do anything fancy we just ignore other lines.
+ *
+ * Our response (if no error) which includes only required lines is:
+ * HTTP/1.1 200 OK
+ * Connection: close
+ * Content-Type: text/xml
+ * Date: <rfc1123-date>
+ *
+ * Header lines must end with \r\n
+ * Per RFC 2616, content-length: is not required but connection:close
+ * would appear to be required (given that we will be closing it!).
+ */
+static void web_connection_parse_post(struct upnp_wps_device_sm *sm,
+ struct sockaddr_in *cli,
+ struct http_request *req,
+ const char *filename)
+{
+ enum http_reply_code ret;
+ char *data = http_request_get_data(req); /* body of http msg */
+ const char *action = NULL;
+ size_t action_len = 0;
+ const char *replyname = NULL; /* argument name for the reply */
+ struct wpabuf *reply = NULL; /* data for the reply */
+
+ if (os_strcasecmp(filename, UPNP_WPS_DEVICE_CONTROL_FILE)) {
+ wpa_printf(MSG_INFO, "WPS UPnP: Invalid POST filename %s",
+ filename);
+ ret = HTTP_NOT_FOUND;
+ goto bad;
+ }
+
+ ret = UPNP_INVALID_ACTION;
+ action = web_get_action(req, &action_len);
+ if (action == NULL)
+ goto bad;
+
+ if (!os_strncasecmp("GetDeviceInfo", action, action_len))
+ ret = web_process_get_device_info(sm, &reply, &replyname);
+ else if (!os_strncasecmp("PutMessage", action, action_len))
+ ret = web_process_put_message(sm, data, &reply, &replyname);
+ else if (!os_strncasecmp("PutWLANResponse", action, action_len))
+ ret = web_process_put_wlan_response(sm, data, &reply,
+ &replyname);
+ else if (!os_strncasecmp("SetSelectedRegistrar", action, action_len))
+ ret = web_process_set_selected_registrar(sm, cli, data, &reply,
+ &replyname);
+ else
+ wpa_printf(MSG_INFO, "WPS UPnP: Unknown POST type");
+
+bad:
+ if (ret != HTTP_OK)
+ wpa_printf(MSG_INFO, "WPS UPnP: POST failure ret=%d", ret);
+ web_connection_send_reply(req, ret, action, action_len, reply,
+ replyname);
+ wpabuf_free(reply);
+}
+
+
+/* Given that we have received a header w/ SUBSCRIBE, act upon it
+ *
+ * Format of SUBSCRIBE (case-insensitive):
+ *
+ * First line must be:
+ * SUBSCRIBE /wps_event HTTP/1.1
+ *
+ * Our response (if no error) which includes only required lines is:
+ * HTTP/1.1 200 OK
+ * Server: xx, UPnP/1.0, xx
+ * SID: uuid:xxxxxxxxx
+ * Timeout: Second-<n>
+ * Content-Length: 0
+ * Date: xxxx
+ *
+ * Header lines must end with \r\n
+ * Per RFC 2616, content-length: is not required but connection:close
+ * would appear to be required (given that we will be closing it!).
+ */
+static void web_connection_parse_subscribe(struct upnp_wps_device_sm *sm,
+ struct http_request *req,
+ const char *filename)
+{
+ struct wpabuf *buf;
+ char *b;
+ char *hdr = http_request_get_hdr(req);
+ char *h;
+ char *match;
+ int match_len;
+ char *end;
+ int len;
+ int got_nt = 0;
+ u8 uuid[UUID_LEN];
+ int got_uuid = 0;
+ char *callback_urls = NULL;
+ struct subscription *s = NULL;
+ enum http_reply_code ret = HTTP_INTERNAL_SERVER_ERROR;
+
+ buf = wpabuf_alloc(1000);
+ if (buf == NULL) {
+ http_request_deinit(req);
+ return;
+ }
+
+ wpa_hexdump_ascii(MSG_DEBUG, "WPS UPnP: HTTP SUBSCRIBE",
+ (u8 *) hdr, os_strlen(hdr));
+
+ /* Parse/validate headers */
+ h = hdr;
+ /* First line: SUBSCRIBE /wps_event HTTP/1.1
+ * has already been parsed.
+ */
+ if (os_strcasecmp(filename, UPNP_WPS_DEVICE_EVENT_FILE) != 0) {
+ ret = HTTP_PRECONDITION_FAILED;
+ goto error;
+ }
+ wpa_printf(MSG_DEBUG, "WPS UPnP: HTTP SUBSCRIBE for event");
+ end = os_strchr(h, '\n');
+
+ while (end) {
+ /* Option line by option line */
+ h = end + 1;
+ end = os_strchr(h, '\n');
+ if (end == NULL)
+ break; /* no unterminated lines allowed */
+
+ /* NT assures that it is our type of subscription;
+ * not used for a renewal.
+ **/
+ match = "NT:";
+ match_len = os_strlen(match);
+ if (os_strncasecmp(h, match, match_len) == 0) {
+ h += match_len;
+ while (*h == ' ' || *h == '\t')
+ h++;
+ match = "upnp:event";
+ match_len = os_strlen(match);
+ if (os_strncasecmp(h, match, match_len) != 0) {
+ ret = HTTP_BAD_REQUEST;
+ goto error;
+ }
+ got_nt = 1;
+ continue;
+ }
+ /* HOST should refer to us */
+#if 0
+ match = "HOST:";
+ match_len = os_strlen(match);
+ if (os_strncasecmp(h, match, match_len) == 0) {
+ h += match_len;
+ while (*h == ' ' || *h == '\t')
+ h++;
+ .....
+ }
+#endif
+ /* CALLBACK gives one or more URLs for NOTIFYs
+ * to be sent as a result of the subscription.
+ * Each URL is enclosed in angle brackets.
+ */
+ match = "CALLBACK:";
+ match_len = os_strlen(match);
+ if (os_strncasecmp(h, match, match_len) == 0) {
+ h += match_len;
+ while (*h == ' ' || *h == '\t')
+ h++;
+ len = end - h;
+ os_free(callback_urls);
+ callback_urls = dup_binstr(h, len);
+ if (callback_urls == NULL) {
+ ret = HTTP_INTERNAL_SERVER_ERROR;
+ goto error;
+ }
+ if (len > 0 && callback_urls[len - 1] == '\r')
+ callback_urls[len - 1] = '\0';
+ continue;
+ }
+ /* SID is only for renewal */
+ match = "SID:";
+ match_len = os_strlen(match);
+ if (os_strncasecmp(h, match, match_len) == 0) {
+ h += match_len;
+ while (*h == ' ' || *h == '\t')
+ h++;
+ match = "uuid:";
+ match_len = os_strlen(match);
+ if (os_strncasecmp(h, match, match_len) != 0) {
+ ret = HTTP_BAD_REQUEST;
+ goto error;
+ }
+ h += match_len;
+ while (*h == ' ' || *h == '\t')
+ h++;
+ if (uuid_str2bin(h, uuid)) {
+ ret = HTTP_BAD_REQUEST;
+ goto error;
+ }
+ got_uuid = 1;
+ continue;
+ }
+ /* TIMEOUT is requested timeout, but apparently we can
+ * just ignore this.
+ */
+ }
+
+ if (got_uuid) {
+ /* renewal */
+ wpa_printf(MSG_DEBUG, "WPS UPnP: Subscription renewal");
+ if (callback_urls) {
+ ret = HTTP_BAD_REQUEST;
+ goto error;
+ }
+ s = subscription_renew(sm, uuid);
+ if (s == NULL) {
+ char str[80];
+ uuid_bin2str(uuid, str, sizeof(str));
+ wpa_printf(MSG_DEBUG, "WPS UPnP: Could not find "
+ "SID %s", str);
+ ret = HTTP_PRECONDITION_FAILED;
+ goto error;
+ }
+ } else if (callback_urls) {
+ wpa_printf(MSG_DEBUG, "WPS UPnP: New subscription");
+ if (!got_nt) {
+ ret = HTTP_PRECONDITION_FAILED;
+ goto error;
+ }
+ s = subscription_start(sm, callback_urls);
+ if (s == NULL) {
+ ret = HTTP_INTERNAL_SERVER_ERROR;
+ goto error;
+ }
+ } else {
+ ret = HTTP_PRECONDITION_FAILED;
+ goto error;
+ }
+
+ /* success */
+ http_put_reply_code(buf, HTTP_OK);
+ wpabuf_put_str(buf, http_server_hdr);
+ wpabuf_put_str(buf, http_connection_close);
+ wpabuf_put_str(buf, "Content-Length: 0\r\n");
+ wpabuf_put_str(buf, "SID: uuid:");
+ /* subscription id */
+ b = wpabuf_put(buf, 0);
+ uuid_bin2str(s->uuid, b, 80);
+ wpa_printf(MSG_DEBUG, "WPS UPnP: Assigned SID %s", b);
+ wpabuf_put(buf, os_strlen(b));
+ wpabuf_put_str(buf, "\r\n");
+ wpabuf_printf(buf, "Timeout: Second-%d\r\n", UPNP_SUBSCRIBE_SEC);
+ http_put_date(buf);
+ /* And empty line to terminate header: */
+ wpabuf_put_str(buf, "\r\n");
+
+ os_free(callback_urls);
+ http_request_send_and_deinit(req, buf);
+ return;
+
+error:
+ /* Per UPnP spec:
+ * Errors
+ * Incompatible headers
+ * 400 Bad Request. If SID header and one of NT or CALLBACK headers
+ * are present, the publisher must respond with HTTP error
+ * 400 Bad Request.
+ * Missing or invalid CALLBACK
+ * 412 Precondition Failed. If CALLBACK header is missing or does not
+ * contain a valid HTTP URL, the publisher must respond with HTTP
+ * error 412 Precondition Failed.
+ * Invalid NT
+ * 412 Precondition Failed. If NT header does not equal upnp:event,
+ * the publisher must respond with HTTP error 412 Precondition
+ * Failed.
+ * [For resubscription, use 412 if unknown uuid].
+ * Unable to accept subscription
+ * 5xx. If a publisher is not able to accept a subscription (such as
+ * due to insufficient resources), it must respond with a
+ * HTTP 500-series error code.
+ * 599 Too many subscriptions (not a standard HTTP error)
+ */
+ wpa_printf(MSG_DEBUG, "WPS UPnP: SUBSCRIBE failed - return %d", ret);
+ http_put_empty(buf, ret);
+ http_request_send_and_deinit(req, buf);
+ os_free(callback_urls);
+}
+
+
+/* Given that we have received a header w/ UNSUBSCRIBE, act upon it
+ *
+ * Format of UNSUBSCRIBE (case-insensitive):
+ *
+ * First line must be:
+ * UNSUBSCRIBE /wps_event HTTP/1.1
+ *
+ * Our response (if no error) which includes only required lines is:
+ * HTTP/1.1 200 OK
+ * Content-Length: 0
+ *
+ * Header lines must end with \r\n
+ * Per RFC 2616, content-length: is not required but connection:close
+ * would appear to be required (given that we will be closing it!).
+ */
+static void web_connection_parse_unsubscribe(struct upnp_wps_device_sm *sm,
+ struct http_request *req,
+ const char *filename)
+{
+ struct wpabuf *buf;
+ char *hdr = http_request_get_hdr(req);
+ char *h;
+ char *match;
+ int match_len;
+ char *end;
+ u8 uuid[UUID_LEN];
+ int got_uuid = 0;
+ struct subscription *s = NULL;
+ enum http_reply_code ret = HTTP_INTERNAL_SERVER_ERROR;
+
+ /* Parse/validate headers */
+ h = hdr;
+ /* First line: UNSUBSCRIBE /wps_event HTTP/1.1
+ * has already been parsed.
+ */
+ if (os_strcasecmp(filename, UPNP_WPS_DEVICE_EVENT_FILE) != 0) {
+ ret = HTTP_PRECONDITION_FAILED;
+ goto send_msg;
+ }
+ wpa_printf(MSG_DEBUG, "WPS UPnP: HTTP UNSUBSCRIBE for event");
+ end = os_strchr(h, '\n');
+
+ while (end) {
+ /* Option line by option line */
+ h = end + 1;
+ end = os_strchr(h, '\n');
+ if (end == NULL)
+ break; /* no unterminated lines allowed */
+
+ /* HOST should refer to us */
+#if 0
+ match = "HOST:";
+ match_len = os_strlen(match);
+ if (os_strncasecmp(h, match, match_len) == 0) {
+ h += match_len;
+ while (*h == ' ' || *h == '\t')
+ h++;
+ .....
+ }
+#endif
+ match = "SID:";
+ match_len = os_strlen(match);
+ if (os_strncasecmp(h, match, match_len) == 0) {
+ h += match_len;
+ while (*h == ' ' || *h == '\t')
+ h++;
+ match = "uuid:";
+ match_len = os_strlen(match);
+ if (os_strncasecmp(h, match, match_len) != 0) {
+ ret = HTTP_BAD_REQUEST;
+ goto send_msg;
+ }
+ h += match_len;
+ while (*h == ' ' || *h == '\t')
+ h++;
+ if (uuid_str2bin(h, uuid)) {
+ ret = HTTP_BAD_REQUEST;
+ goto send_msg;
+ }
+ got_uuid = 1;
+ continue;
+ }
+
+ match = "NT:";
+ match_len = os_strlen(match);
+ if (os_strncasecmp(h, match, match_len) == 0) {
+ ret = HTTP_BAD_REQUEST;
+ goto send_msg;
+ }
+
+ match = "CALLBACK:";
+ match_len = os_strlen(match);
+ if (os_strncasecmp(h, match, match_len) == 0) {
+ ret = HTTP_BAD_REQUEST;
+ goto send_msg;
+ }
+ }
+
+ if (got_uuid) {
+ char str[80];
+
+ uuid_bin2str(uuid, str, sizeof(str));
+
+ s = subscription_find(sm, uuid);
+ if (s) {
+ struct subscr_addr *sa;
+ sa = dl_list_first(&s->addr_list, struct subscr_addr,
+ list);
+ wpa_printf(MSG_DEBUG,
+ "WPS UPnP: Unsubscribing %p (SID %s) %s",
+ s, str, (sa && sa->domain_and_port) ?
+ sa->domain_and_port : "-null-");
+ dl_list_del(&s->list);
+ subscription_destroy(s);
+ } else {
+ wpa_printf(MSG_INFO,
+ "WPS UPnP: Could not find matching subscription to unsubscribe (SID %s)",
+ str);
+ ret = HTTP_PRECONDITION_FAILED;
+ goto send_msg;
+ }
+ } else {
+ wpa_printf(MSG_INFO, "WPS UPnP: Unsubscribe fails (not "
+ "found)");
+ ret = HTTP_PRECONDITION_FAILED;
+ goto send_msg;
+ }
+
+ ret = HTTP_OK;
+
+send_msg:
+ buf = wpabuf_alloc(200);
+ if (buf == NULL) {
+ http_request_deinit(req);
+ return;
+ }
+ http_put_empty(buf, ret);
+ http_request_send_and_deinit(req, buf);
+}
+
+
+/* Send error in response to unknown requests */
+static void web_connection_unimplemented(struct http_request *req)
+{
+ struct wpabuf *buf;
+ buf = wpabuf_alloc(200);
+ if (buf == NULL) {
+ http_request_deinit(req);
+ return;
+ }
+ http_put_empty(buf, HTTP_UNIMPLEMENTED);
+ http_request_send_and_deinit(req, buf);
+}
+
+
+
+/* Called when we have gotten an apparently valid http request.
+ */
+static void web_connection_check_data(void *ctx, struct http_request *req)
+{
+ struct upnp_wps_device_sm *sm = ctx;
+ enum httpread_hdr_type htype = http_request_get_type(req);
+ char *filename = http_request_get_uri(req);
+ struct sockaddr_in *cli = http_request_get_cli_addr(req);
+
+ if (!filename) {
+ wpa_printf(MSG_INFO, "WPS UPnP: Could not get HTTP URI");
+ http_request_deinit(req);
+ return;
+ }
+ /* Trim leading slashes from filename */
+ while (*filename == '/')
+ filename++;
+
+ wpa_printf(MSG_DEBUG, "WPS UPnP: Got HTTP request type %d from %s:%d",
+ htype, inet_ntoa(cli->sin_addr), htons(cli->sin_port));
+
+ switch (htype) {
+ case HTTPREAD_HDR_TYPE_GET:
+ web_connection_parse_get(sm, req, filename);
+ break;
+ case HTTPREAD_HDR_TYPE_POST:
+ web_connection_parse_post(sm, cli, req, filename);
+ break;
+ case HTTPREAD_HDR_TYPE_SUBSCRIBE:
+ web_connection_parse_subscribe(sm, req, filename);
+ break;
+ case HTTPREAD_HDR_TYPE_UNSUBSCRIBE:
+ web_connection_parse_unsubscribe(sm, req, filename);
+ break;
+
+ /* We are not required to support M-POST; just plain
+ * POST is supposed to work, so we only support that.
+ * If for some reason we need to support M-POST, it is
+ * mostly the same as POST, with small differences.
+ */
+ default:
+ /* Send 501 for anything else */
+ web_connection_unimplemented(req);
+ break;
+ }
+}
+
+
+/*
+ * Listening for web connections
+ * We have a single TCP listening port, and hand off connections as we get
+ * them.
+ */
+
+void web_listener_stop(struct upnp_wps_device_sm *sm)
+{
+ http_server_deinit(sm->web_srv);
+ sm->web_srv = NULL;
+}
+
+
+int web_listener_start(struct upnp_wps_device_sm *sm)
+{
+ struct in_addr addr;
+ addr.s_addr = sm->ip_addr;
+ sm->web_srv = http_server_init(&addr, -1, web_connection_check_data,
+ sm);
+ if (sm->web_srv == NULL) {
+ web_listener_stop(sm);
+ return -1;
+ }
+ sm->web_port = http_server_get_port(sm->web_srv);
+
+ return 0;
+}
diff --git a/freebsd/contrib/wpa/wpa_supplicant/ap.h b/freebsd/contrib/wpa/wpa_supplicant/ap.h
new file mode 100644
index 00000000..594168cf
--- /dev/null
+++ b/freebsd/contrib/wpa/wpa_supplicant/ap.h
@@ -0,0 +1,98 @@
+/*
+ * WPA Supplicant - Basic AP mode support routines
+ * Copyright (c) 2003-2009, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2009, Atheros Communications
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef AP_H
+#define AP_H
+
+int wpa_supplicant_create_ap(struct wpa_supplicant *wpa_s,
+ struct wpa_ssid *ssid);
+void wpa_supplicant_ap_deinit(struct wpa_supplicant *wpa_s);
+void wpa_supplicant_ap_rx_eapol(struct wpa_supplicant *wpa_s,
+ const u8 *src_addr, const u8 *buf, size_t len);
+int wpa_supplicant_ap_wps_pbc(struct wpa_supplicant *wpa_s, const u8 *bssid,
+ const u8 *p2p_dev_addr);
+int wpa_supplicant_ap_wps_pin(struct wpa_supplicant *wpa_s, const u8 *bssid,
+ const char *pin, char *buf, size_t buflen,
+ int timeout);
+int wpa_supplicant_ap_wps_cancel(struct wpa_supplicant *wpa_s);
+void wpas_wps_ap_pin_disable(struct wpa_supplicant *wpa_s);
+const char * wpas_wps_ap_pin_random(struct wpa_supplicant *wpa_s, int timeout);
+const char * wpas_wps_ap_pin_get(struct wpa_supplicant *wpa_s);
+int wpas_wps_ap_pin_set(struct wpa_supplicant *wpa_s, const char *pin,
+ int timeout);
+int ap_ctrl_iface_sta_first(struct wpa_supplicant *wpa_s,
+ char *buf, size_t buflen);
+int ap_ctrl_iface_sta(struct wpa_supplicant *wpa_s, const char *txtaddr,
+ char *buf, size_t buflen);
+int ap_ctrl_iface_sta_next(struct wpa_supplicant *wpa_s, const char *txtaddr,
+ char *buf, size_t buflen);
+int ap_ctrl_iface_sta_deauthenticate(struct wpa_supplicant *wpa_s,
+ const char *txtaddr);
+int ap_ctrl_iface_sta_disassociate(struct wpa_supplicant *wpa_s,
+ const char *txtaddr);
+int ap_ctrl_iface_wpa_get_status(struct wpa_supplicant *wpa_s, char *buf,
+ size_t buflen, int verbose);
+void ap_tx_status(void *ctx, const u8 *addr,
+ const u8 *buf, size_t len, int ack);
+void ap_eapol_tx_status(void *ctx, const u8 *dst,
+ const u8 *data, size_t len, int ack);
+void ap_client_poll_ok(void *ctx, const u8 *addr);
+void ap_rx_from_unknown_sta(void *ctx, const u8 *addr, int wds);
+void ap_mgmt_rx(void *ctx, struct rx_mgmt *rx_mgmt);
+void ap_mgmt_tx_cb(void *ctx, const u8 *buf, size_t len, u16 stype, int ok);
+int wpa_supplicant_ap_update_beacon(struct wpa_supplicant *wpa_s);
+int wpa_supplicant_ap_mac_addr_filter(struct wpa_supplicant *wpa_s,
+ const u8 *addr);
+void wpa_supplicant_ap_pwd_auth_fail(struct wpa_supplicant *wpa_s);
+int ap_switch_channel(struct wpa_supplicant *wpa_s,
+ struct csa_settings *settings);
+int ap_ctrl_iface_chanswitch(struct wpa_supplicant *wpa_s, const char *txtaddr);
+void wpas_ap_ch_switch(struct wpa_supplicant *wpa_s, int freq, int ht,
+ int offset, int width, int cf1, int cf2);
+struct wpabuf * wpas_ap_wps_nfc_config_token(struct wpa_supplicant *wpa_s,
+ int ndef);
+#ifdef CONFIG_AP
+struct wpabuf * wpas_ap_wps_nfc_handover_sel(struct wpa_supplicant *wpa_s,
+ int ndef);
+#else /* CONFIG_AP */
+static inline struct wpabuf *
+wpas_ap_wps_nfc_handover_sel(struct wpa_supplicant *wpa_s,
+ int ndef)
+{
+ return NULL;
+}
+#endif /* CONFIG_AP */
+
+int wpas_ap_wps_nfc_report_handover(struct wpa_supplicant *wpa_s,
+ const struct wpabuf *req,
+ const struct wpabuf *sel);
+int wpas_ap_wps_add_nfc_pw(struct wpa_supplicant *wpa_s, u16 pw_id,
+ const struct wpabuf *pw, const u8 *pubkey_hash);
+
+struct hostapd_config;
+void wpa_supplicant_conf_ap_ht(struct wpa_supplicant *wpa_s,
+ struct wpa_ssid *ssid,
+ struct hostapd_config *conf);
+
+int wpas_ap_stop_ap(struct wpa_supplicant *wpa_s);
+
+void wpas_event_dfs_radar_detected(struct wpa_supplicant *wpa_s,
+ struct dfs_event *radar);
+void wpas_event_dfs_cac_started(struct wpa_supplicant *wpa_s,
+ struct dfs_event *radar);
+void wpas_event_dfs_cac_finished(struct wpa_supplicant *wpa_s,
+ struct dfs_event *radar);
+void wpas_event_dfs_cac_aborted(struct wpa_supplicant *wpa_s,
+ struct dfs_event *radar);
+void wpas_event_dfs_cac_nop_finished(struct wpa_supplicant *wpa_s,
+ struct dfs_event *radar);
+
+void ap_periodic(struct wpa_supplicant *wpa_s);
+
+#endif /* AP_H */
diff --git a/freebsd/contrib/wpa/wpa_supplicant/autoscan.h b/freebsd/contrib/wpa/wpa_supplicant/autoscan.h
new file mode 100644
index 00000000..e2a76522
--- /dev/null
+++ b/freebsd/contrib/wpa/wpa_supplicant/autoscan.h
@@ -0,0 +1,49 @@
+/*
+ * WPA Supplicant - auto scan
+ * Copyright (c) 2012, Intel Corporation. All rights reserved.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef AUTOSCAN_H
+#define AUTOSCAN_H
+
+struct wpa_supplicant;
+
+struct autoscan_ops {
+ const char *name;
+
+ void * (*init)(struct wpa_supplicant *wpa_s, const char *params);
+ void (*deinit)(void *priv);
+
+ int (*notify_scan)(void *priv, struct wpa_scan_results *scan_res);
+};
+
+#ifdef CONFIG_AUTOSCAN
+
+int autoscan_init(struct wpa_supplicant *wpa_s, int req_scan);
+void autoscan_deinit(struct wpa_supplicant *wpa_s);
+int autoscan_notify_scan(struct wpa_supplicant *wpa_s,
+ struct wpa_scan_results *scan_res);
+
+#else /* CONFIG_AUTOSCAN */
+
+static inline int autoscan_init(struct wpa_supplicant *wpa_s, int req_scan)
+{
+ return 0;
+}
+
+static inline void autoscan_deinit(struct wpa_supplicant *wpa_s)
+{
+}
+
+static inline int autoscan_notify_scan(struct wpa_supplicant *wpa_s,
+ struct wpa_scan_results *scan_res)
+{
+ return 0;
+}
+
+#endif /* CONFIG_AUTOSCAN */
+
+#endif /* AUTOSCAN_H */
diff --git a/freebsd/contrib/wpa/wpa_supplicant/bgscan.h b/freebsd/contrib/wpa/wpa_supplicant/bgscan.h
new file mode 100644
index 00000000..9131e4ec
--- /dev/null
+++ b/freebsd/contrib/wpa/wpa_supplicant/bgscan.h
@@ -0,0 +1,73 @@
+/*
+ * WPA Supplicant - background scan and roaming interface
+ * Copyright (c) 2009-2010, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef BGSCAN_H
+#define BGSCAN_H
+
+struct wpa_supplicant;
+struct wpa_ssid;
+
+struct bgscan_ops {
+ const char *name;
+
+ void * (*init)(struct wpa_supplicant *wpa_s, const char *params,
+ const struct wpa_ssid *ssid);
+ void (*deinit)(void *priv);
+
+ int (*notify_scan)(void *priv, struct wpa_scan_results *scan_res);
+ void (*notify_beacon_loss)(void *priv);
+ void (*notify_signal_change)(void *priv, int above,
+ int current_signal,
+ int current_noise,
+ int current_txrate);
+};
+
+#ifdef CONFIG_BGSCAN
+
+int bgscan_init(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid,
+ const char *name);
+void bgscan_deinit(struct wpa_supplicant *wpa_s);
+int bgscan_notify_scan(struct wpa_supplicant *wpa_s,
+ struct wpa_scan_results *scan_res);
+void bgscan_notify_beacon_loss(struct wpa_supplicant *wpa_s);
+void bgscan_notify_signal_change(struct wpa_supplicant *wpa_s, int above,
+ int current_signal, int current_noise,
+ int current_txrate);
+
+#else /* CONFIG_BGSCAN */
+
+static inline int bgscan_init(struct wpa_supplicant *wpa_s,
+ struct wpa_ssid *ssid, const char name)
+{
+ return 0;
+}
+
+static inline void bgscan_deinit(struct wpa_supplicant *wpa_s)
+{
+}
+
+static inline int bgscan_notify_scan(struct wpa_supplicant *wpa_s,
+ struct wpa_scan_results *scan_res)
+{
+ return 0;
+}
+
+static inline void bgscan_notify_beacon_loss(struct wpa_supplicant *wpa_s)
+{
+}
+
+static inline void bgscan_notify_signal_change(struct wpa_supplicant *wpa_s,
+ int above, int current_signal,
+ int current_noise,
+ int current_txrate)
+{
+}
+
+#endif /* CONFIG_BGSCAN */
+
+#endif /* BGSCAN_H */
diff --git a/freebsd/contrib/wpa/wpa_supplicant/blacklist.c b/freebsd/contrib/wpa/wpa_supplicant/blacklist.c
new file mode 100644
index 00000000..c8ebc69e
--- /dev/null
+++ b/freebsd/contrib/wpa/wpa_supplicant/blacklist.c
@@ -0,0 +1,143 @@
+#include <machine/rtems-bsd-user-space.h>
+
+/*
+ * wpa_supplicant - Temporary BSSID blacklist
+ * Copyright (c) 2003-2007, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "wpa_supplicant_i.h"
+#include "blacklist.h"
+
+/**
+ * wpa_blacklist_get - Get the blacklist entry for a BSSID
+ * @wpa_s: Pointer to wpa_supplicant data
+ * @bssid: BSSID
+ * Returns: Matching blacklist entry for the BSSID or %NULL if not found
+ */
+struct wpa_blacklist * wpa_blacklist_get(struct wpa_supplicant *wpa_s,
+ const u8 *bssid)
+{
+ struct wpa_blacklist *e;
+
+ if (wpa_s == NULL || bssid == NULL)
+ return NULL;
+
+ e = wpa_s->blacklist;
+ while (e) {
+ if (os_memcmp(e->bssid, bssid, ETH_ALEN) == 0)
+ return e;
+ e = e->next;
+ }
+
+ return NULL;
+}
+
+
+/**
+ * wpa_blacklist_add - Add an BSSID to the blacklist
+ * @wpa_s: Pointer to wpa_supplicant data
+ * @bssid: BSSID to be added to the blacklist
+ * Returns: Current blacklist count on success, -1 on failure
+ *
+ * This function adds the specified BSSID to the blacklist or increases the
+ * blacklist count if the BSSID was already listed. It should be called when
+ * an association attempt fails either due to the selected BSS rejecting
+ * association or due to timeout.
+ *
+ * This blacklist is used to force %wpa_supplicant to go through all available
+ * BSSes before retrying to associate with an BSS that rejected or timed out
+ * association. It does not prevent the listed BSS from being used; it only
+ * changes the order in which they are tried.
+ */
+int wpa_blacklist_add(struct wpa_supplicant *wpa_s, const u8 *bssid)
+{
+ struct wpa_blacklist *e;
+
+ if (wpa_s == NULL || bssid == NULL)
+ return -1;
+
+ e = wpa_blacklist_get(wpa_s, bssid);
+ if (e) {
+ e->count++;
+ wpa_printf(MSG_DEBUG, "BSSID " MACSTR " blacklist count "
+ "incremented to %d",
+ MAC2STR(bssid), e->count);
+ return e->count;
+ }
+
+ e = os_zalloc(sizeof(*e));
+ if (e == NULL)
+ return -1;
+ os_memcpy(e->bssid, bssid, ETH_ALEN);
+ e->count = 1;
+ e->next = wpa_s->blacklist;
+ wpa_s->blacklist = e;
+ wpa_printf(MSG_DEBUG, "Added BSSID " MACSTR " into blacklist",
+ MAC2STR(bssid));
+
+ return e->count;
+}
+
+
+/**
+ * wpa_blacklist_del - Remove an BSSID from the blacklist
+ * @wpa_s: Pointer to wpa_supplicant data
+ * @bssid: BSSID to be removed from the blacklist
+ * Returns: 0 on success, -1 on failure
+ */
+int wpa_blacklist_del(struct wpa_supplicant *wpa_s, const u8 *bssid)
+{
+ struct wpa_blacklist *e, *prev = NULL;
+
+ if (wpa_s == NULL || bssid == NULL)
+ return -1;
+
+ e = wpa_s->blacklist;
+ while (e) {
+ if (os_memcmp(e->bssid, bssid, ETH_ALEN) == 0) {
+ if (prev == NULL) {
+ wpa_s->blacklist = e->next;
+ } else {
+ prev->next = e->next;
+ }
+ wpa_printf(MSG_DEBUG, "Removed BSSID " MACSTR " from "
+ "blacklist", MAC2STR(bssid));
+ os_free(e);
+ return 0;
+ }
+ prev = e;
+ e = e->next;
+ }
+ return -1;
+}
+
+
+/**
+ * wpa_blacklist_clear - Clear the blacklist of all entries
+ * @wpa_s: Pointer to wpa_supplicant data
+ */
+void wpa_blacklist_clear(struct wpa_supplicant *wpa_s)
+{
+ struct wpa_blacklist *e, *prev;
+ int max_count = 0;
+
+ e = wpa_s->blacklist;
+ wpa_s->blacklist = NULL;
+ while (e) {
+ if (e->count > max_count)
+ max_count = e->count;
+ prev = e;
+ e = e->next;
+ wpa_printf(MSG_DEBUG, "Removed BSSID " MACSTR " from "
+ "blacklist (clear)", MAC2STR(prev->bssid));
+ os_free(prev);
+ }
+
+ wpa_s->extra_blacklist_count += max_count;
+}
diff --git a/freebsd/contrib/wpa/wpa_supplicant/blacklist.h b/freebsd/contrib/wpa/wpa_supplicant/blacklist.h
new file mode 100644
index 00000000..ae06986f
--- /dev/null
+++ b/freebsd/contrib/wpa/wpa_supplicant/blacklist.h
@@ -0,0 +1,24 @@
+/*
+ * wpa_supplicant - Temporary BSSID blacklist
+ * Copyright (c) 2003-2007, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef BLACKLIST_H
+#define BLACKLIST_H
+
+struct wpa_blacklist {
+ struct wpa_blacklist *next;
+ u8 bssid[ETH_ALEN];
+ int count;
+};
+
+struct wpa_blacklist * wpa_blacklist_get(struct wpa_supplicant *wpa_s,
+ const u8 *bssid);
+int wpa_blacklist_add(struct wpa_supplicant *wpa_s, const u8 *bssid);
+int wpa_blacklist_del(struct wpa_supplicant *wpa_s, const u8 *bssid);
+void wpa_blacklist_clear(struct wpa_supplicant *wpa_s);
+
+#endif /* BLACKLIST_H */
diff --git a/freebsd/contrib/wpa/wpa_supplicant/bss.c b/freebsd/contrib/wpa/wpa_supplicant/bss.c
new file mode 100644
index 00000000..ff40bef0
--- /dev/null
+++ b/freebsd/contrib/wpa/wpa_supplicant/bss.c
@@ -0,0 +1,1238 @@
+#include <machine/rtems-bsd-user-space.h>
+
+/*
+ * BSS table
+ * Copyright (c) 2009-2015, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+
+#include "utils/common.h"
+#include "utils/eloop.h"
+#include "common/ieee802_11_defs.h"
+#include "drivers/driver.h"
+#include "wpa_supplicant_i.h"
+#include "config.h"
+#include "notify.h"
+#include "scan.h"
+#include "bss.h"
+
+
+#define WPA_BSS_FREQ_CHANGED_FLAG BIT(0)
+#define WPA_BSS_SIGNAL_CHANGED_FLAG BIT(1)
+#define WPA_BSS_PRIVACY_CHANGED_FLAG BIT(2)
+#define WPA_BSS_MODE_CHANGED_FLAG BIT(3)
+#define WPA_BSS_WPAIE_CHANGED_FLAG BIT(4)
+#define WPA_BSS_RSNIE_CHANGED_FLAG BIT(5)
+#define WPA_BSS_WPS_CHANGED_FLAG BIT(6)
+#define WPA_BSS_RATES_CHANGED_FLAG BIT(7)
+#define WPA_BSS_IES_CHANGED_FLAG BIT(8)
+
+
+static void wpa_bss_set_hessid(struct wpa_bss *bss)
+{
+#ifdef CONFIG_INTERWORKING
+ const u8 *ie = wpa_bss_get_ie(bss, WLAN_EID_INTERWORKING);
+ if (ie == NULL || (ie[1] != 7 && ie[1] != 9)) {
+ os_memset(bss->hessid, 0, ETH_ALEN);
+ return;
+ }
+ if (ie[1] == 7)
+ os_memcpy(bss->hessid, ie + 3, ETH_ALEN);
+ else
+ os_memcpy(bss->hessid, ie + 5, ETH_ALEN);
+#endif /* CONFIG_INTERWORKING */
+}
+
+
+/**
+ * wpa_bss_anqp_alloc - Allocate ANQP data structure for a BSS entry
+ * Returns: Allocated ANQP data structure or %NULL on failure
+ *
+ * The allocated ANQP data structure has its users count set to 1. It may be
+ * shared by multiple BSS entries and each shared entry is freed with
+ * wpa_bss_anqp_free().
+ */
+struct wpa_bss_anqp * wpa_bss_anqp_alloc(void)
+{
+ struct wpa_bss_anqp *anqp;
+ anqp = os_zalloc(sizeof(*anqp));
+ if (anqp == NULL)
+ return NULL;
+ anqp->users = 1;
+ return anqp;
+}
+
+
+/**
+ * wpa_bss_anqp_clone - Clone an ANQP data structure
+ * @anqp: ANQP data structure from wpa_bss_anqp_alloc()
+ * Returns: Cloned ANQP data structure or %NULL on failure
+ */
+static struct wpa_bss_anqp * wpa_bss_anqp_clone(struct wpa_bss_anqp *anqp)
+{
+ struct wpa_bss_anqp *n;
+
+ n = os_zalloc(sizeof(*n));
+ if (n == NULL)
+ return NULL;
+
+#define ANQP_DUP(f) if (anqp->f) n->f = wpabuf_dup(anqp->f)
+#ifdef CONFIG_INTERWORKING
+ ANQP_DUP(capability_list);
+ ANQP_DUP(venue_name);
+ ANQP_DUP(network_auth_type);
+ ANQP_DUP(roaming_consortium);
+ ANQP_DUP(ip_addr_type_availability);
+ ANQP_DUP(nai_realm);
+ ANQP_DUP(anqp_3gpp);
+ ANQP_DUP(domain_name);
+#endif /* CONFIG_INTERWORKING */
+#ifdef CONFIG_HS20
+ ANQP_DUP(hs20_capability_list);
+ ANQP_DUP(hs20_operator_friendly_name);
+ ANQP_DUP(hs20_wan_metrics);
+ ANQP_DUP(hs20_connection_capability);
+ ANQP_DUP(hs20_operating_class);
+ ANQP_DUP(hs20_osu_providers_list);
+#endif /* CONFIG_HS20 */
+#undef ANQP_DUP
+
+ return n;
+}
+
+
+/**
+ * wpa_bss_anqp_unshare_alloc - Unshare ANQP data (if shared) in a BSS entry
+ * @bss: BSS entry
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function ensures the specific BSS entry has an ANQP data structure that
+ * is not shared with any other BSS entry.
+ */
+int wpa_bss_anqp_unshare_alloc(struct wpa_bss *bss)
+{
+ struct wpa_bss_anqp *anqp;
+
+ if (bss->anqp && bss->anqp->users > 1) {
+ /* allocated, but shared - clone an unshared copy */
+ anqp = wpa_bss_anqp_clone(bss->anqp);
+ if (anqp == NULL)
+ return -1;
+ anqp->users = 1;
+ bss->anqp->users--;
+ bss->anqp = anqp;
+ return 0;
+ }
+
+ if (bss->anqp)
+ return 0; /* already allocated and not shared */
+
+ /* not allocated - allocate a new storage area */
+ bss->anqp = wpa_bss_anqp_alloc();
+ return bss->anqp ? 0 : -1;
+}
+
+
+/**
+ * wpa_bss_anqp_free - Free an ANQP data structure
+ * @anqp: ANQP data structure from wpa_bss_anqp_alloc() or wpa_bss_anqp_clone()
+ */
+static void wpa_bss_anqp_free(struct wpa_bss_anqp *anqp)
+{
+ if (anqp == NULL)
+ return;
+
+ anqp->users--;
+ if (anqp->users > 0) {
+ /* Another BSS entry holds a pointer to this ANQP info */
+ return;
+ }
+
+#ifdef CONFIG_INTERWORKING
+ wpabuf_free(anqp->capability_list);
+ wpabuf_free(anqp->venue_name);
+ wpabuf_free(anqp->network_auth_type);
+ wpabuf_free(anqp->roaming_consortium);
+ wpabuf_free(anqp->ip_addr_type_availability);
+ wpabuf_free(anqp->nai_realm);
+ wpabuf_free(anqp->anqp_3gpp);
+ wpabuf_free(anqp->domain_name);
+#endif /* CONFIG_INTERWORKING */
+#ifdef CONFIG_HS20
+ wpabuf_free(anqp->hs20_capability_list);
+ wpabuf_free(anqp->hs20_operator_friendly_name);
+ wpabuf_free(anqp->hs20_wan_metrics);
+ wpabuf_free(anqp->hs20_connection_capability);
+ wpabuf_free(anqp->hs20_operating_class);
+ wpabuf_free(anqp->hs20_osu_providers_list);
+#endif /* CONFIG_HS20 */
+
+ os_free(anqp);
+}
+
+
+static void wpa_bss_update_pending_connect(struct wpa_supplicant *wpa_s,
+ struct wpa_bss *old_bss,
+ struct wpa_bss *new_bss)
+{
+ struct wpa_radio_work *work;
+ struct wpa_connect_work *cwork;
+
+ work = radio_work_pending(wpa_s, "sme-connect");
+ if (!work)
+ work = radio_work_pending(wpa_s, "connect");
+ if (!work)
+ return;
+
+ cwork = work->ctx;
+ if (cwork->bss != old_bss)
+ return;
+
+ wpa_printf(MSG_DEBUG,
+ "Update BSS pointer for the pending connect radio work");
+ cwork->bss = new_bss;
+ if (!new_bss)
+ cwork->bss_removed = 1;
+}
+
+
+static void wpa_bss_remove(struct wpa_supplicant *wpa_s, struct wpa_bss *bss,
+ const char *reason)
+{
+ if (wpa_s->last_scan_res) {
+ unsigned int i;
+ for (i = 0; i < wpa_s->last_scan_res_used; i++) {
+ if (wpa_s->last_scan_res[i] == bss) {
+ os_memmove(&wpa_s->last_scan_res[i],
+ &wpa_s->last_scan_res[i + 1],
+ (wpa_s->last_scan_res_used - i - 1)
+ * sizeof(struct wpa_bss *));
+ wpa_s->last_scan_res_used--;
+ break;
+ }
+ }
+ }
+ wpa_bss_update_pending_connect(wpa_s, bss, NULL);
+ dl_list_del(&bss->list);
+ dl_list_del(&bss->list_id);
+ wpa_s->num_bss--;
+ wpa_dbg(wpa_s, MSG_DEBUG, "BSS: Remove id %u BSSID " MACSTR
+ " SSID '%s' due to %s", bss->id, MAC2STR(bss->bssid),
+ wpa_ssid_txt(bss->ssid, bss->ssid_len), reason);
+ wpas_notify_bss_removed(wpa_s, bss->bssid, bss->id);
+ wpa_bss_anqp_free(bss->anqp);
+ os_free(bss);
+}
+
+
+/**
+ * wpa_bss_get - Fetch a BSS table entry based on BSSID and SSID
+ * @wpa_s: Pointer to wpa_supplicant data
+ * @bssid: BSSID
+ * @ssid: SSID
+ * @ssid_len: Length of @ssid
+ * Returns: Pointer to the BSS entry or %NULL if not found
+ */
+struct wpa_bss * wpa_bss_get(struct wpa_supplicant *wpa_s, const u8 *bssid,
+ const u8 *ssid, size_t ssid_len)
+{
+ struct wpa_bss *bss;
+ if (!wpa_supplicant_filter_bssid_match(wpa_s, bssid))
+ return NULL;
+ dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) {
+ if (os_memcmp(bss->bssid, bssid, ETH_ALEN) == 0 &&
+ bss->ssid_len == ssid_len &&
+ os_memcmp(bss->ssid, ssid, ssid_len) == 0)
+ return bss;
+ }
+ return NULL;
+}
+
+
+static void calculate_update_time(const struct os_reltime *fetch_time,
+ unsigned int age_ms,
+ struct os_reltime *update_time)
+{
+ os_time_t usec;
+
+ update_time->sec = fetch_time->sec;
+ update_time->usec = fetch_time->usec;
+ update_time->sec -= age_ms / 1000;
+ usec = (age_ms % 1000) * 1000;
+ if (update_time->usec < usec) {
+ update_time->sec--;
+ update_time->usec += 1000000;
+ }
+ update_time->usec -= usec;
+}
+
+
+static void wpa_bss_copy_res(struct wpa_bss *dst, struct wpa_scan_res *src,
+ struct os_reltime *fetch_time)
+{
+ dst->flags = src->flags;
+ os_memcpy(dst->bssid, src->bssid, ETH_ALEN);
+ dst->freq = src->freq;
+ dst->beacon_int = src->beacon_int;
+ dst->caps = src->caps;
+ dst->qual = src->qual;
+ dst->noise = src->noise;
+ dst->level = src->level;
+ dst->tsf = src->tsf;
+ dst->est_throughput = src->est_throughput;
+ dst->snr = src->snr;
+
+ calculate_update_time(fetch_time, src->age, &dst->last_update);
+}
+
+
+static int wpa_bss_known(struct wpa_supplicant *wpa_s, struct wpa_bss *bss)
+{
+ struct wpa_ssid *ssid;
+
+ for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next) {
+ if (ssid->ssid == NULL || ssid->ssid_len == 0)
+ continue;
+ if (ssid->ssid_len == bss->ssid_len &&
+ os_memcmp(ssid->ssid, bss->ssid, ssid->ssid_len) == 0)
+ return 1;
+ }
+
+ return 0;
+}
+
+
+static int wpa_bss_in_use(struct wpa_supplicant *wpa_s, struct wpa_bss *bss)
+{
+ if (bss == wpa_s->current_bss)
+ return 1;
+
+ if (wpa_s->current_bss &&
+ (bss->ssid_len != wpa_s->current_bss->ssid_len ||
+ os_memcmp(bss->ssid, wpa_s->current_bss->ssid,
+ bss->ssid_len) != 0))
+ return 0; /* SSID has changed */
+
+ return !is_zero_ether_addr(bss->bssid) &&
+ (os_memcmp(bss->bssid, wpa_s->bssid, ETH_ALEN) == 0 ||
+ os_memcmp(bss->bssid, wpa_s->pending_bssid, ETH_ALEN) == 0);
+}
+
+
+static int wpa_bss_remove_oldest_unknown(struct wpa_supplicant *wpa_s)
+{
+ struct wpa_bss *bss;
+
+ dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) {
+ if (!wpa_bss_known(wpa_s, bss)) {
+ wpa_bss_remove(wpa_s, bss, __func__);
+ return 0;
+ }
+ }
+
+ return -1;
+}
+
+
+static int wpa_bss_remove_oldest(struct wpa_supplicant *wpa_s)
+{
+ struct wpa_bss *bss;
+
+ /*
+ * Remove the oldest entry that does not match with any configured
+ * network.
+ */
+ if (wpa_bss_remove_oldest_unknown(wpa_s) == 0)
+ return 0;
+
+ /*
+ * Remove the oldest entry that isn't currently in use.
+ */
+ dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) {
+ if (!wpa_bss_in_use(wpa_s, bss)) {
+ wpa_bss_remove(wpa_s, bss, __func__);
+ return 0;
+ }
+ }
+
+ return -1;
+}
+
+
+static struct wpa_bss * wpa_bss_add(struct wpa_supplicant *wpa_s,
+ const u8 *ssid, size_t ssid_len,
+ struct wpa_scan_res *res,
+ struct os_reltime *fetch_time)
+{
+ struct wpa_bss *bss;
+
+ bss = os_zalloc(sizeof(*bss) + res->ie_len + res->beacon_ie_len);
+ if (bss == NULL)
+ return NULL;
+ bss->id = wpa_s->bss_next_id++;
+ bss->last_update_idx = wpa_s->bss_update_idx;
+ wpa_bss_copy_res(bss, res, fetch_time);
+ os_memcpy(bss->ssid, ssid, ssid_len);
+ bss->ssid_len = ssid_len;
+ bss->ie_len = res->ie_len;
+ bss->beacon_ie_len = res->beacon_ie_len;
+ os_memcpy(bss + 1, res + 1, res->ie_len + res->beacon_ie_len);
+ wpa_bss_set_hessid(bss);
+
+ if (wpa_s->num_bss + 1 > wpa_s->conf->bss_max_count &&
+ wpa_bss_remove_oldest(wpa_s) != 0) {
+ wpa_printf(MSG_ERROR, "Increasing the MAX BSS count to %d "
+ "because all BSSes are in use. We should normally "
+ "not get here!", (int) wpa_s->num_bss + 1);
+ wpa_s->conf->bss_max_count = wpa_s->num_bss + 1;
+ }
+
+ dl_list_add_tail(&wpa_s->bss, &bss->list);
+ dl_list_add_tail(&wpa_s->bss_id, &bss->list_id);
+ wpa_s->num_bss++;
+ wpa_dbg(wpa_s, MSG_DEBUG, "BSS: Add new id %u BSSID " MACSTR
+ " SSID '%s' freq %d",
+ bss->id, MAC2STR(bss->bssid), wpa_ssid_txt(ssid, ssid_len),
+ bss->freq);
+ wpas_notify_bss_added(wpa_s, bss->bssid, bss->id);
+ return bss;
+}
+
+
+static int are_ies_equal(const struct wpa_bss *old,
+ const struct wpa_scan_res *new_res, u32 ie)
+{
+ const u8 *old_ie, *new_ie;
+ struct wpabuf *old_ie_buff = NULL;
+ struct wpabuf *new_ie_buff = NULL;
+ int new_ie_len, old_ie_len, ret, is_multi;
+
+ switch (ie) {
+ case WPA_IE_VENDOR_TYPE:
+ old_ie = wpa_bss_get_vendor_ie(old, ie);
+ new_ie = wpa_scan_get_vendor_ie(new_res, ie);
+ is_multi = 0;
+ break;
+ case WPS_IE_VENDOR_TYPE:
+ old_ie_buff = wpa_bss_get_vendor_ie_multi(old, ie);
+ new_ie_buff = wpa_scan_get_vendor_ie_multi(new_res, ie);
+ is_multi = 1;
+ break;
+ case WLAN_EID_RSN:
+ case WLAN_EID_SUPP_RATES:
+ case WLAN_EID_EXT_SUPP_RATES:
+ old_ie = wpa_bss_get_ie(old, ie);
+ new_ie = wpa_scan_get_ie(new_res, ie);
+ is_multi = 0;
+ break;
+ default:
+ wpa_printf(MSG_DEBUG, "bss: %s: cannot compare IEs", __func__);
+ return 0;
+ }
+
+ if (is_multi) {
+ /* in case of multiple IEs stored in buffer */
+ old_ie = old_ie_buff ? wpabuf_head_u8(old_ie_buff) : NULL;
+ new_ie = new_ie_buff ? wpabuf_head_u8(new_ie_buff) : NULL;
+ old_ie_len = old_ie_buff ? wpabuf_len(old_ie_buff) : 0;
+ new_ie_len = new_ie_buff ? wpabuf_len(new_ie_buff) : 0;
+ } else {
+ /* in case of single IE */
+ old_ie_len = old_ie ? old_ie[1] + 2 : 0;
+ new_ie_len = new_ie ? new_ie[1] + 2 : 0;
+ }
+
+ if (!old_ie || !new_ie)
+ ret = !old_ie && !new_ie;
+ else
+ ret = (old_ie_len == new_ie_len &&
+ os_memcmp(old_ie, new_ie, old_ie_len) == 0);
+
+ wpabuf_free(old_ie_buff);
+ wpabuf_free(new_ie_buff);
+
+ return ret;
+}
+
+
+static u32 wpa_bss_compare_res(const struct wpa_bss *old,
+ const struct wpa_scan_res *new_res)
+{
+ u32 changes = 0;
+ int caps_diff = old->caps ^ new_res->caps;
+
+ if (old->freq != new_res->freq)
+ changes |= WPA_BSS_FREQ_CHANGED_FLAG;
+
+ if (old->level != new_res->level)
+ changes |= WPA_BSS_SIGNAL_CHANGED_FLAG;
+
+ if (caps_diff & IEEE80211_CAP_PRIVACY)
+ changes |= WPA_BSS_PRIVACY_CHANGED_FLAG;
+
+ if (caps_diff & IEEE80211_CAP_IBSS)
+ changes |= WPA_BSS_MODE_CHANGED_FLAG;
+
+ if (old->ie_len == new_res->ie_len &&
+ os_memcmp(old + 1, new_res + 1, old->ie_len) == 0)
+ return changes;
+ changes |= WPA_BSS_IES_CHANGED_FLAG;
+
+ if (!are_ies_equal(old, new_res, WPA_IE_VENDOR_TYPE))
+ changes |= WPA_BSS_WPAIE_CHANGED_FLAG;
+
+ if (!are_ies_equal(old, new_res, WLAN_EID_RSN))
+ changes |= WPA_BSS_RSNIE_CHANGED_FLAG;
+
+ if (!are_ies_equal(old, new_res, WPS_IE_VENDOR_TYPE))
+ changes |= WPA_BSS_WPS_CHANGED_FLAG;
+
+ if (!are_ies_equal(old, new_res, WLAN_EID_SUPP_RATES) ||
+ !are_ies_equal(old, new_res, WLAN_EID_EXT_SUPP_RATES))
+ changes |= WPA_BSS_RATES_CHANGED_FLAG;
+
+ return changes;
+}
+
+
+static void notify_bss_changes(struct wpa_supplicant *wpa_s, u32 changes,
+ const struct wpa_bss *bss)
+{
+ if (changes & WPA_BSS_FREQ_CHANGED_FLAG)
+ wpas_notify_bss_freq_changed(wpa_s, bss->id);
+
+ if (changes & WPA_BSS_SIGNAL_CHANGED_FLAG)
+ wpas_notify_bss_signal_changed(wpa_s, bss->id);
+
+ if (changes & WPA_BSS_PRIVACY_CHANGED_FLAG)
+ wpas_notify_bss_privacy_changed(wpa_s, bss->id);
+
+ if (changes & WPA_BSS_MODE_CHANGED_FLAG)
+ wpas_notify_bss_mode_changed(wpa_s, bss->id);
+
+ if (changes & WPA_BSS_WPAIE_CHANGED_FLAG)
+ wpas_notify_bss_wpaie_changed(wpa_s, bss->id);
+
+ if (changes & WPA_BSS_RSNIE_CHANGED_FLAG)
+ wpas_notify_bss_rsnie_changed(wpa_s, bss->id);
+
+ if (changes & WPA_BSS_WPS_CHANGED_FLAG)
+ wpas_notify_bss_wps_changed(wpa_s, bss->id);
+
+ if (changes & WPA_BSS_IES_CHANGED_FLAG)
+ wpas_notify_bss_ies_changed(wpa_s, bss->id);
+
+ if (changes & WPA_BSS_RATES_CHANGED_FLAG)
+ wpas_notify_bss_rates_changed(wpa_s, bss->id);
+
+ wpas_notify_bss_seen(wpa_s, bss->id);
+}
+
+
+static struct wpa_bss *
+wpa_bss_update(struct wpa_supplicant *wpa_s, struct wpa_bss *bss,
+ struct wpa_scan_res *res, struct os_reltime *fetch_time)
+{
+ u32 changes;
+
+ changes = wpa_bss_compare_res(bss, res);
+ if (changes & WPA_BSS_FREQ_CHANGED_FLAG)
+ wpa_printf(MSG_DEBUG, "BSS: " MACSTR " changed freq %d --> %d",
+ MAC2STR(bss->bssid), bss->freq, res->freq);
+ bss->scan_miss_count = 0;
+ bss->last_update_idx = wpa_s->bss_update_idx;
+ wpa_bss_copy_res(bss, res, fetch_time);
+ /* Move the entry to the end of the list */
+ dl_list_del(&bss->list);
+#ifdef CONFIG_P2P
+ if (wpa_bss_get_vendor_ie(bss, P2P_IE_VENDOR_TYPE) &&
+ !wpa_scan_get_vendor_ie(res, P2P_IE_VENDOR_TYPE)) {
+ /*
+ * This can happen when non-P2P station interface runs a scan
+ * without P2P IE in the Probe Request frame. P2P GO would reply
+ * to that with a Probe Response that does not include P2P IE.
+ * Do not update the IEs in this BSS entry to avoid such loss of
+ * information that may be needed for P2P operations to
+ * determine group information.
+ */
+ wpa_dbg(wpa_s, MSG_DEBUG, "BSS: Do not update scan IEs for "
+ MACSTR " since that would remove P2P IE information",
+ MAC2STR(bss->bssid));
+ } else
+#endif /* CONFIG_P2P */
+ if (bss->ie_len + bss->beacon_ie_len >=
+ res->ie_len + res->beacon_ie_len) {
+ os_memcpy(bss + 1, res + 1, res->ie_len + res->beacon_ie_len);
+ bss->ie_len = res->ie_len;
+ bss->beacon_ie_len = res->beacon_ie_len;
+ } else {
+ struct wpa_bss *nbss;
+ struct dl_list *prev = bss->list_id.prev;
+ dl_list_del(&bss->list_id);
+ nbss = os_realloc(bss, sizeof(*bss) + res->ie_len +
+ res->beacon_ie_len);
+ if (nbss) {
+ unsigned int i;
+ for (i = 0; i < wpa_s->last_scan_res_used; i++) {
+ if (wpa_s->last_scan_res[i] == bss) {
+ wpa_s->last_scan_res[i] = nbss;
+ break;
+ }
+ }
+ if (wpa_s->current_bss == bss)
+ wpa_s->current_bss = nbss;
+ wpa_bss_update_pending_connect(wpa_s, bss, nbss);
+ bss = nbss;
+ os_memcpy(bss + 1, res + 1,
+ res->ie_len + res->beacon_ie_len);
+ bss->ie_len = res->ie_len;
+ bss->beacon_ie_len = res->beacon_ie_len;
+ }
+ dl_list_add(prev, &bss->list_id);
+ }
+ if (changes & WPA_BSS_IES_CHANGED_FLAG)
+ wpa_bss_set_hessid(bss);
+ dl_list_add_tail(&wpa_s->bss, &bss->list);
+
+ notify_bss_changes(wpa_s, changes, bss);
+
+ return bss;
+}
+
+
+/**
+ * wpa_bss_update_start - Start a BSS table update from scan results
+ * @wpa_s: Pointer to wpa_supplicant data
+ *
+ * This function is called at the start of each BSS table update round for new
+ * scan results. The actual scan result entries are indicated with calls to
+ * wpa_bss_update_scan_res() and the update round is finished with a call to
+ * wpa_bss_update_end().
+ */
+void wpa_bss_update_start(struct wpa_supplicant *wpa_s)
+{
+ wpa_s->bss_update_idx++;
+ wpa_dbg(wpa_s, MSG_DEBUG, "BSS: Start scan result update %u",
+ wpa_s->bss_update_idx);
+ wpa_s->last_scan_res_used = 0;
+}
+
+
+/**
+ * wpa_bss_update_scan_res - Update a BSS table entry based on a scan result
+ * @wpa_s: Pointer to wpa_supplicant data
+ * @res: Scan result
+ * @fetch_time: Time when the result was fetched from the driver
+ *
+ * This function updates a BSS table entry (or adds one) based on a scan result.
+ * This is called separately for each scan result between the calls to
+ * wpa_bss_update_start() and wpa_bss_update_end().
+ */
+void wpa_bss_update_scan_res(struct wpa_supplicant *wpa_s,
+ struct wpa_scan_res *res,
+ struct os_reltime *fetch_time)
+{
+ const u8 *ssid, *p2p, *mesh;
+ struct wpa_bss *bss;
+
+ if (wpa_s->conf->ignore_old_scan_res) {
+ struct os_reltime update;
+ calculate_update_time(fetch_time, res->age, &update);
+ if (os_reltime_before(&update, &wpa_s->scan_trigger_time)) {
+ struct os_reltime age;
+ os_reltime_sub(&wpa_s->scan_trigger_time, &update,
+ &age);
+ wpa_dbg(wpa_s, MSG_DEBUG, "BSS: Ignore driver BSS "
+ "table entry that is %u.%06u seconds older "
+ "than our scan trigger",
+ (unsigned int) age.sec,
+ (unsigned int) age.usec);
+ return;
+ }
+ }
+
+ ssid = wpa_scan_get_ie(res, WLAN_EID_SSID);
+ if (ssid == NULL) {
+ wpa_dbg(wpa_s, MSG_DEBUG, "BSS: No SSID IE included for "
+ MACSTR, MAC2STR(res->bssid));
+ return;
+ }
+ if (ssid[1] > SSID_MAX_LEN) {
+ wpa_dbg(wpa_s, MSG_DEBUG, "BSS: Too long SSID IE included for "
+ MACSTR, MAC2STR(res->bssid));
+ return;
+ }
+
+ p2p = wpa_scan_get_vendor_ie(res, P2P_IE_VENDOR_TYPE);
+#ifdef CONFIG_P2P
+ if (p2p == NULL &&
+ wpa_s->p2p_group_interface != NOT_P2P_GROUP_INTERFACE) {
+ /*
+ * If it's a P2P specific interface, then don't update
+ * the scan result without a P2P IE.
+ */
+ wpa_printf(MSG_DEBUG, "BSS: No P2P IE - skipping BSS " MACSTR
+ " update for P2P interface", MAC2STR(res->bssid));
+ return;
+ }
+#endif /* CONFIG_P2P */
+ if (p2p && ssid[1] == P2P_WILDCARD_SSID_LEN &&
+ os_memcmp(ssid + 2, P2P_WILDCARD_SSID, P2P_WILDCARD_SSID_LEN) == 0)
+ return; /* Skip P2P listen discovery results here */
+
+ /* TODO: add option for ignoring BSSes we are not interested in
+ * (to save memory) */
+
+ mesh = wpa_scan_get_ie(res, WLAN_EID_MESH_ID);
+ if (mesh && mesh[1] <= SSID_MAX_LEN)
+ ssid = mesh;
+
+ bss = wpa_bss_get(wpa_s, res->bssid, ssid + 2, ssid[1]);
+ if (bss == NULL)
+ bss = wpa_bss_add(wpa_s, ssid + 2, ssid[1], res, fetch_time);
+ else {
+ bss = wpa_bss_update(wpa_s, bss, res, fetch_time);
+ if (wpa_s->last_scan_res) {
+ unsigned int i;
+ for (i = 0; i < wpa_s->last_scan_res_used; i++) {
+ if (bss == wpa_s->last_scan_res[i]) {
+ /* Already in the list */
+ return;
+ }
+ }
+ }
+ }
+
+ if (bss == NULL)
+ return;
+ if (wpa_s->last_scan_res_used >= wpa_s->last_scan_res_size) {
+ struct wpa_bss **n;
+ unsigned int siz;
+ if (wpa_s->last_scan_res_size == 0)
+ siz = 32;
+ else
+ siz = wpa_s->last_scan_res_size * 2;
+ n = os_realloc_array(wpa_s->last_scan_res, siz,
+ sizeof(struct wpa_bss *));
+ if (n == NULL)
+ return;
+ wpa_s->last_scan_res = n;
+ wpa_s->last_scan_res_size = siz;
+ }
+
+ if (wpa_s->last_scan_res)
+ wpa_s->last_scan_res[wpa_s->last_scan_res_used++] = bss;
+}
+
+
+static int wpa_bss_included_in_scan(const struct wpa_bss *bss,
+ const struct scan_info *info)
+{
+ int found;
+ size_t i;
+
+ if (info == NULL)
+ return 1;
+
+ if (info->num_freqs) {
+ found = 0;
+ for (i = 0; i < info->num_freqs; i++) {
+ if (bss->freq == info->freqs[i]) {
+ found = 1;
+ break;
+ }
+ }
+ if (!found)
+ return 0;
+ }
+
+ if (info->num_ssids) {
+ found = 0;
+ for (i = 0; i < info->num_ssids; i++) {
+ const struct wpa_driver_scan_ssid *s = &info->ssids[i];
+ if ((s->ssid == NULL || s->ssid_len == 0) ||
+ (s->ssid_len == bss->ssid_len &&
+ os_memcmp(s->ssid, bss->ssid, bss->ssid_len) ==
+ 0)) {
+ found = 1;
+ break;
+ }
+ }
+ if (!found)
+ return 0;
+ }
+
+ return 1;
+}
+
+
+/**
+ * wpa_bss_update_end - End a BSS table update from scan results
+ * @wpa_s: Pointer to wpa_supplicant data
+ * @info: Information about scan parameters
+ * @new_scan: Whether this update round was based on a new scan
+ *
+ * This function is called at the end of each BSS table update round for new
+ * scan results. The start of the update was indicated with a call to
+ * wpa_bss_update_start().
+ */
+void wpa_bss_update_end(struct wpa_supplicant *wpa_s, struct scan_info *info,
+ int new_scan)
+{
+ struct wpa_bss *bss, *n;
+
+ os_get_reltime(&wpa_s->last_scan);
+ if (!new_scan)
+ return; /* do not expire entries without new scan */
+
+ dl_list_for_each_safe(bss, n, &wpa_s->bss, struct wpa_bss, list) {
+ if (wpa_bss_in_use(wpa_s, bss))
+ continue;
+ if (!wpa_bss_included_in_scan(bss, info))
+ continue; /* expire only BSSes that were scanned */
+ if (bss->last_update_idx < wpa_s->bss_update_idx)
+ bss->scan_miss_count++;
+ if (bss->scan_miss_count >=
+ wpa_s->conf->bss_expiration_scan_count) {
+ wpa_bss_remove(wpa_s, bss, "no match in scan");
+ }
+ }
+
+ wpa_printf(MSG_DEBUG, "BSS: last_scan_res_used=%u/%u",
+ wpa_s->last_scan_res_used, wpa_s->last_scan_res_size);
+}
+
+
+/**
+ * wpa_bss_flush_by_age - Flush old BSS entries
+ * @wpa_s: Pointer to wpa_supplicant data
+ * @age: Maximum entry age in seconds
+ *
+ * Remove BSS entries that have not been updated during the last @age seconds.
+ */
+void wpa_bss_flush_by_age(struct wpa_supplicant *wpa_s, int age)
+{
+ struct wpa_bss *bss, *n;
+ struct os_reltime t;
+
+ if (dl_list_empty(&wpa_s->bss))
+ return;
+
+ os_get_reltime(&t);
+ t.sec -= age;
+
+ dl_list_for_each_safe(bss, n, &wpa_s->bss, struct wpa_bss, list) {
+ if (wpa_bss_in_use(wpa_s, bss))
+ continue;
+
+ if (os_reltime_before(&bss->last_update, &t)) {
+ wpa_bss_remove(wpa_s, bss, __func__);
+ } else
+ break;
+ }
+}
+
+
+/**
+ * wpa_bss_init - Initialize BSS table
+ * @wpa_s: Pointer to wpa_supplicant data
+ * Returns: 0 on success, -1 on failure
+ *
+ * This prepares BSS table lists and timer for periodic updates. The BSS table
+ * is deinitialized with wpa_bss_deinit() once not needed anymore.
+ */
+int wpa_bss_init(struct wpa_supplicant *wpa_s)
+{
+ dl_list_init(&wpa_s->bss);
+ dl_list_init(&wpa_s->bss_id);
+ return 0;
+}
+
+
+/**
+ * wpa_bss_flush - Flush all unused BSS entries
+ * @wpa_s: Pointer to wpa_supplicant data
+ */
+void wpa_bss_flush(struct wpa_supplicant *wpa_s)
+{
+ struct wpa_bss *bss, *n;
+
+ wpa_s->clear_driver_scan_cache = 1;
+
+ if (wpa_s->bss.next == NULL)
+ return; /* BSS table not yet initialized */
+
+ dl_list_for_each_safe(bss, n, &wpa_s->bss, struct wpa_bss, list) {
+ if (wpa_bss_in_use(wpa_s, bss))
+ continue;
+ wpa_bss_remove(wpa_s, bss, __func__);
+ }
+}
+
+
+/**
+ * wpa_bss_deinit - Deinitialize BSS table
+ * @wpa_s: Pointer to wpa_supplicant data
+ */
+void wpa_bss_deinit(struct wpa_supplicant *wpa_s)
+{
+ wpa_bss_flush(wpa_s);
+}
+
+
+/**
+ * wpa_bss_get_bssid - Fetch a BSS table entry based on BSSID
+ * @wpa_s: Pointer to wpa_supplicant data
+ * @bssid: BSSID
+ * Returns: Pointer to the BSS entry or %NULL if not found
+ */
+struct wpa_bss * wpa_bss_get_bssid(struct wpa_supplicant *wpa_s,
+ const u8 *bssid)
+{
+ struct wpa_bss *bss;
+ if (!wpa_supplicant_filter_bssid_match(wpa_s, bssid))
+ return NULL;
+ dl_list_for_each_reverse(bss, &wpa_s->bss, struct wpa_bss, list) {
+ if (os_memcmp(bss->bssid, bssid, ETH_ALEN) == 0)
+ return bss;
+ }
+ return NULL;
+}
+
+
+/**
+ * wpa_bss_get_bssid_latest - Fetch the latest BSS table entry based on BSSID
+ * @wpa_s: Pointer to wpa_supplicant data
+ * @bssid: BSSID
+ * Returns: Pointer to the BSS entry or %NULL if not found
+ *
+ * This function is like wpa_bss_get_bssid(), but full BSS table is iterated to
+ * find the entry that has the most recent update. This can help in finding the
+ * correct entry in cases where the SSID of the AP may have changed recently
+ * (e.g., in WPS reconfiguration cases).
+ */
+struct wpa_bss * wpa_bss_get_bssid_latest(struct wpa_supplicant *wpa_s,
+ const u8 *bssid)
+{
+ struct wpa_bss *bss, *found = NULL;
+ if (!wpa_supplicant_filter_bssid_match(wpa_s, bssid))
+ return NULL;
+ dl_list_for_each_reverse(bss, &wpa_s->bss, struct wpa_bss, list) {
+ if (os_memcmp(bss->bssid, bssid, ETH_ALEN) != 0)
+ continue;
+ if (found == NULL ||
+ os_reltime_before(&found->last_update, &bss->last_update))
+ found = bss;
+ }
+ return found;
+}
+
+
+#ifdef CONFIG_P2P
+/**
+ * wpa_bss_get_p2p_dev_addr - Fetch a BSS table entry based on P2P Device Addr
+ * @wpa_s: Pointer to wpa_supplicant data
+ * @dev_addr: P2P Device Address of the GO
+ * Returns: Pointer to the BSS entry or %NULL if not found
+ */
+struct wpa_bss * wpa_bss_get_p2p_dev_addr(struct wpa_supplicant *wpa_s,
+ const u8 *dev_addr)
+{
+ struct wpa_bss *bss;
+ dl_list_for_each_reverse(bss, &wpa_s->bss, struct wpa_bss, list) {
+ u8 addr[ETH_ALEN];
+ if (p2p_parse_dev_addr((const u8 *) (bss + 1), bss->ie_len,
+ addr) == 0 &&
+ os_memcmp(addr, dev_addr, ETH_ALEN) == 0)
+ return bss;
+ }
+ return NULL;
+}
+#endif /* CONFIG_P2P */
+
+
+/**
+ * wpa_bss_get_id - Fetch a BSS table entry based on identifier
+ * @wpa_s: Pointer to wpa_supplicant data
+ * @id: Unique identifier (struct wpa_bss::id) assigned for the entry
+ * Returns: Pointer to the BSS entry or %NULL if not found
+ */
+struct wpa_bss * wpa_bss_get_id(struct wpa_supplicant *wpa_s, unsigned int id)
+{
+ struct wpa_bss *bss;
+ dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) {
+ if (bss->id == id)
+ return bss;
+ }
+ return NULL;
+}
+
+
+/**
+ * wpa_bss_get_id_range - Fetch a BSS table entry based on identifier range
+ * @wpa_s: Pointer to wpa_supplicant data
+ * @idf: Smallest allowed identifier assigned for the entry
+ * @idf: Largest allowed identifier assigned for the entry
+ * Returns: Pointer to the BSS entry or %NULL if not found
+ *
+ * This function is similar to wpa_bss_get_id() but allows a BSS entry with the
+ * smallest id value to be fetched within the specified range without the
+ * caller having to know the exact id.
+ */
+struct wpa_bss * wpa_bss_get_id_range(struct wpa_supplicant *wpa_s,
+ unsigned int idf, unsigned int idl)
+{
+ struct wpa_bss *bss;
+ dl_list_for_each(bss, &wpa_s->bss_id, struct wpa_bss, list_id) {
+ if (bss->id >= idf && bss->id <= idl)
+ return bss;
+ }
+ return NULL;
+}
+
+
+/**
+ * wpa_bss_get_ie - Fetch a specified information element from a BSS entry
+ * @bss: BSS table entry
+ * @ie: Information element identitifier (WLAN_EID_*)
+ * Returns: Pointer to the information element (id field) or %NULL if not found
+ *
+ * This function returns the first matching information element in the BSS
+ * entry.
+ */
+const u8 * wpa_bss_get_ie(const struct wpa_bss *bss, u8 ie)
+{
+ const u8 *end, *pos;
+
+ pos = (const u8 *) (bss + 1);
+ end = pos + bss->ie_len;
+
+ while (pos + 1 < end) {
+ if (pos + 2 + pos[1] > end)
+ break;
+ if (pos[0] == ie)
+ return pos;
+ pos += 2 + pos[1];
+ }
+
+ return NULL;
+}
+
+
+/**
+ * wpa_bss_get_vendor_ie - Fetch a vendor information element from a BSS entry
+ * @bss: BSS table entry
+ * @vendor_type: Vendor type (four octets starting the IE payload)
+ * Returns: Pointer to the information element (id field) or %NULL if not found
+ *
+ * This function returns the first matching information element in the BSS
+ * entry.
+ */
+const u8 * wpa_bss_get_vendor_ie(const struct wpa_bss *bss, u32 vendor_type)
+{
+ const u8 *end, *pos;
+
+ pos = (const u8 *) (bss + 1);
+ end = pos + bss->ie_len;
+
+ while (pos + 1 < end) {
+ if (pos + 2 + pos[1] > end)
+ break;
+ if (pos[0] == WLAN_EID_VENDOR_SPECIFIC && pos[1] >= 4 &&
+ vendor_type == WPA_GET_BE32(&pos[2]))
+ return pos;
+ pos += 2 + pos[1];
+ }
+
+ return NULL;
+}
+
+
+/**
+ * wpa_bss_get_vendor_ie_beacon - Fetch a vendor information from a BSS entry
+ * @bss: BSS table entry
+ * @vendor_type: Vendor type (four octets starting the IE payload)
+ * Returns: Pointer to the information element (id field) or %NULL if not found
+ *
+ * This function returns the first matching information element in the BSS
+ * entry.
+ *
+ * This function is like wpa_bss_get_vendor_ie(), but uses IE buffer only
+ * from Beacon frames instead of either Beacon or Probe Response frames.
+ */
+const u8 * wpa_bss_get_vendor_ie_beacon(const struct wpa_bss *bss,
+ u32 vendor_type)
+{
+ const u8 *end, *pos;
+
+ if (bss->beacon_ie_len == 0)
+ return NULL;
+
+ pos = (const u8 *) (bss + 1);
+ pos += bss->ie_len;
+ end = pos + bss->beacon_ie_len;
+
+ while (pos + 1 < end) {
+ if (pos + 2 + pos[1] > end)
+ break;
+ if (pos[0] == WLAN_EID_VENDOR_SPECIFIC && pos[1] >= 4 &&
+ vendor_type == WPA_GET_BE32(&pos[2]))
+ return pos;
+ pos += 2 + pos[1];
+ }
+
+ return NULL;
+}
+
+
+/**
+ * wpa_bss_get_vendor_ie_multi - Fetch vendor IE data from a BSS entry
+ * @bss: BSS table entry
+ * @vendor_type: Vendor type (four octets starting the IE payload)
+ * Returns: Pointer to the information element payload or %NULL if not found
+ *
+ * This function returns concatenated payload of possibly fragmented vendor
+ * specific information elements in the BSS entry. The caller is responsible for
+ * freeing the returned buffer.
+ */
+struct wpabuf * wpa_bss_get_vendor_ie_multi(const struct wpa_bss *bss,
+ u32 vendor_type)
+{
+ struct wpabuf *buf;
+ const u8 *end, *pos;
+
+ buf = wpabuf_alloc(bss->ie_len);
+ if (buf == NULL)
+ return NULL;
+
+ pos = (const u8 *) (bss + 1);
+ end = pos + bss->ie_len;
+
+ while (pos + 1 < end) {
+ if (pos + 2 + pos[1] > end)
+ break;
+ if (pos[0] == WLAN_EID_VENDOR_SPECIFIC && pos[1] >= 4 &&
+ vendor_type == WPA_GET_BE32(&pos[2]))
+ wpabuf_put_data(buf, pos + 2 + 4, pos[1] - 4);
+ pos += 2 + pos[1];
+ }
+
+ if (wpabuf_len(buf) == 0) {
+ wpabuf_free(buf);
+ buf = NULL;
+ }
+
+ return buf;
+}
+
+
+/**
+ * wpa_bss_get_vendor_ie_multi_beacon - Fetch vendor IE data from a BSS entry
+ * @bss: BSS table entry
+ * @vendor_type: Vendor type (four octets starting the IE payload)
+ * Returns: Pointer to the information element payload or %NULL if not found
+ *
+ * This function returns concatenated payload of possibly fragmented vendor
+ * specific information elements in the BSS entry. The caller is responsible for
+ * freeing the returned buffer.
+ *
+ * This function is like wpa_bss_get_vendor_ie_multi(), but uses IE buffer only
+ * from Beacon frames instead of either Beacon or Probe Response frames.
+ */
+struct wpabuf * wpa_bss_get_vendor_ie_multi_beacon(const struct wpa_bss *bss,
+ u32 vendor_type)
+{
+ struct wpabuf *buf;
+ const u8 *end, *pos;
+
+ buf = wpabuf_alloc(bss->beacon_ie_len);
+ if (buf == NULL)
+ return NULL;
+
+ pos = (const u8 *) (bss + 1);
+ pos += bss->ie_len;
+ end = pos + bss->beacon_ie_len;
+
+ while (pos + 1 < end) {
+ if (pos + 2 + pos[1] > end)
+ break;
+ if (pos[0] == WLAN_EID_VENDOR_SPECIFIC && pos[1] >= 4 &&
+ vendor_type == WPA_GET_BE32(&pos[2]))
+ wpabuf_put_data(buf, pos + 2 + 4, pos[1] - 4);
+ pos += 2 + pos[1];
+ }
+
+ if (wpabuf_len(buf) == 0) {
+ wpabuf_free(buf);
+ buf = NULL;
+ }
+
+ return buf;
+}
+
+
+/**
+ * wpa_bss_get_max_rate - Get maximum legacy TX rate supported in a BSS
+ * @bss: BSS table entry
+ * Returns: Maximum legacy rate in units of 500 kbps
+ */
+int wpa_bss_get_max_rate(const struct wpa_bss *bss)
+{
+ int rate = 0;
+ const u8 *ie;
+ int i;
+
+ ie = wpa_bss_get_ie(bss, WLAN_EID_SUPP_RATES);
+ for (i = 0; ie && i < ie[1]; i++) {
+ if ((ie[i + 2] & 0x7f) > rate)
+ rate = ie[i + 2] & 0x7f;
+ }
+
+ ie = wpa_bss_get_ie(bss, WLAN_EID_EXT_SUPP_RATES);
+ for (i = 0; ie && i < ie[1]; i++) {
+ if ((ie[i + 2] & 0x7f) > rate)
+ rate = ie[i + 2] & 0x7f;
+ }
+
+ return rate;
+}
+
+
+/**
+ * wpa_bss_get_bit_rates - Get legacy TX rates supported in a BSS
+ * @bss: BSS table entry
+ * @rates: Buffer for returning a pointer to the rates list (units of 500 kbps)
+ * Returns: number of legacy TX rates or -1 on failure
+ *
+ * The caller is responsible for freeing the returned buffer with os_free() in
+ * case of success.
+ */
+int wpa_bss_get_bit_rates(const struct wpa_bss *bss, u8 **rates)
+{
+ const u8 *ie, *ie2;
+ int i, j;
+ unsigned int len;
+ u8 *r;
+
+ ie = wpa_bss_get_ie(bss, WLAN_EID_SUPP_RATES);
+ ie2 = wpa_bss_get_ie(bss, WLAN_EID_EXT_SUPP_RATES);
+
+ len = (ie ? ie[1] : 0) + (ie2 ? ie2[1] : 0);
+
+ r = os_malloc(len);
+ if (!r)
+ return -1;
+
+ for (i = 0; ie && i < ie[1]; i++)
+ r[i] = ie[i + 2] & 0x7f;
+
+ for (j = 0; ie2 && j < ie2[1]; j++)
+ r[i + j] = ie2[j + 2] & 0x7f;
+
+ *rates = r;
+ return len;
+}
diff --git a/freebsd/contrib/wpa/wpa_supplicant/bss.h b/freebsd/contrib/wpa/wpa_supplicant/bss.h
new file mode 100644
index 00000000..b215380e
--- /dev/null
+++ b/freebsd/contrib/wpa/wpa_supplicant/bss.h
@@ -0,0 +1,150 @@
+/*
+ * BSS table
+ * Copyright (c) 2009-2015, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef BSS_H
+#define BSS_H
+
+struct wpa_scan_res;
+
+#define WPA_BSS_QUAL_INVALID BIT(0)
+#define WPA_BSS_NOISE_INVALID BIT(1)
+#define WPA_BSS_LEVEL_INVALID BIT(2)
+#define WPA_BSS_LEVEL_DBM BIT(3)
+#define WPA_BSS_AUTHENTICATED BIT(4)
+#define WPA_BSS_ASSOCIATED BIT(5)
+#define WPA_BSS_ANQP_FETCH_TRIED BIT(6)
+
+/**
+ * struct wpa_bss_anqp - ANQP data for a BSS entry (struct wpa_bss)
+ */
+struct wpa_bss_anqp {
+ /** Number of BSS entries referring to this ANQP data instance */
+ unsigned int users;
+#ifdef CONFIG_INTERWORKING
+ struct wpabuf *capability_list;
+ struct wpabuf *venue_name;
+ struct wpabuf *network_auth_type;
+ struct wpabuf *roaming_consortium;
+ struct wpabuf *ip_addr_type_availability;
+ struct wpabuf *nai_realm;
+ struct wpabuf *anqp_3gpp;
+ struct wpabuf *domain_name;
+#endif /* CONFIG_INTERWORKING */
+#ifdef CONFIG_HS20
+ struct wpabuf *hs20_capability_list;
+ struct wpabuf *hs20_operator_friendly_name;
+ struct wpabuf *hs20_wan_metrics;
+ struct wpabuf *hs20_connection_capability;
+ struct wpabuf *hs20_operating_class;
+ struct wpabuf *hs20_osu_providers_list;
+#endif /* CONFIG_HS20 */
+};
+
+/**
+ * struct wpa_bss - BSS table
+ *
+ * This structure is used to store information about neighboring BSSes in
+ * generic format. It is mainly updated based on scan results from the driver.
+ */
+struct wpa_bss {
+ /** List entry for struct wpa_supplicant::bss */
+ struct dl_list list;
+ /** List entry for struct wpa_supplicant::bss_id */
+ struct dl_list list_id;
+ /** Unique identifier for this BSS entry */
+ unsigned int id;
+ /** Number of counts without seeing this BSS */
+ unsigned int scan_miss_count;
+ /** Index of the last scan update */
+ unsigned int last_update_idx;
+ /** Information flags about the BSS/IBSS (WPA_BSS_*) */
+ unsigned int flags;
+ /** BSSID */
+ u8 bssid[ETH_ALEN];
+ /** HESSID */
+ u8 hessid[ETH_ALEN];
+ /** SSID */
+ u8 ssid[SSID_MAX_LEN];
+ /** Length of SSID */
+ size_t ssid_len;
+ /** Frequency of the channel in MHz (e.g., 2412 = channel 1) */
+ int freq;
+ /** Beacon interval in TUs (host byte order) */
+ u16 beacon_int;
+ /** Capability information field in host byte order */
+ u16 caps;
+ /** Signal quality */
+ int qual;
+ /** Noise level */
+ int noise;
+ /** Signal level */
+ int level;
+ /** Timestamp of last Beacon/Probe Response frame */
+ u64 tsf;
+ /** Time of the last update (i.e., Beacon or Probe Response RX) */
+ struct os_reltime last_update;
+ /** Estimated throughput in kbps */
+ unsigned int est_throughput;
+ /** Signal-to-noise ratio in dB */
+ int snr;
+ /** ANQP data */
+ struct wpa_bss_anqp *anqp;
+ /** Length of the following IE field in octets (from Probe Response) */
+ size_t ie_len;
+ /** Length of the following Beacon IE field in octets */
+ size_t beacon_ie_len;
+ /* followed by ie_len octets of IEs */
+ /* followed by beacon_ie_len octets of IEs */
+};
+
+void wpa_bss_update_start(struct wpa_supplicant *wpa_s);
+void wpa_bss_update_scan_res(struct wpa_supplicant *wpa_s,
+ struct wpa_scan_res *res,
+ struct os_reltime *fetch_time);
+void wpa_bss_update_end(struct wpa_supplicant *wpa_s, struct scan_info *info,
+ int new_scan);
+int wpa_bss_init(struct wpa_supplicant *wpa_s);
+void wpa_bss_deinit(struct wpa_supplicant *wpa_s);
+void wpa_bss_flush(struct wpa_supplicant *wpa_s);
+void wpa_bss_flush_by_age(struct wpa_supplicant *wpa_s, int age);
+struct wpa_bss * wpa_bss_get(struct wpa_supplicant *wpa_s, const u8 *bssid,
+ const u8 *ssid, size_t ssid_len);
+struct wpa_bss * wpa_bss_get_bssid(struct wpa_supplicant *wpa_s,
+ const u8 *bssid);
+struct wpa_bss * wpa_bss_get_bssid_latest(struct wpa_supplicant *wpa_s,
+ const u8 *bssid);
+struct wpa_bss * wpa_bss_get_p2p_dev_addr(struct wpa_supplicant *wpa_s,
+ const u8 *dev_addr);
+struct wpa_bss * wpa_bss_get_id(struct wpa_supplicant *wpa_s, unsigned int id);
+struct wpa_bss * wpa_bss_get_id_range(struct wpa_supplicant *wpa_s,
+ unsigned int idf, unsigned int idl);
+const u8 * wpa_bss_get_ie(const struct wpa_bss *bss, u8 ie);
+const u8 * wpa_bss_get_vendor_ie(const struct wpa_bss *bss, u32 vendor_type);
+const u8 * wpa_bss_get_vendor_ie_beacon(const struct wpa_bss *bss,
+ u32 vendor_type);
+struct wpabuf * wpa_bss_get_vendor_ie_multi(const struct wpa_bss *bss,
+ u32 vendor_type);
+struct wpabuf * wpa_bss_get_vendor_ie_multi_beacon(const struct wpa_bss *bss,
+ u32 vendor_type);
+int wpa_bss_get_max_rate(const struct wpa_bss *bss);
+int wpa_bss_get_bit_rates(const struct wpa_bss *bss, u8 **rates);
+struct wpa_bss_anqp * wpa_bss_anqp_alloc(void);
+int wpa_bss_anqp_unshare_alloc(struct wpa_bss *bss);
+
+static inline int bss_is_dmg(const struct wpa_bss *bss)
+{
+ return bss->freq > 45000;
+}
+
+static inline void wpa_bss_update_level(struct wpa_bss *bss, int new_level)
+{
+ if (bss != NULL && new_level < 0)
+ bss->level = new_level;
+}
+
+#endif /* BSS_H */
diff --git a/freebsd/contrib/wpa/wpa_supplicant/config.c b/freebsd/contrib/wpa/wpa_supplicant/config.c
new file mode 100644
index 00000000..992ba83d
--- /dev/null
+++ b/freebsd/contrib/wpa/wpa_supplicant/config.c
@@ -0,0 +1,4359 @@
+#include <machine/rtems-bsd-user-space.h>
+
+/*
+ * WPA Supplicant / Configuration parser and common functions
+ * Copyright (c) 2003-2015, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "utils/uuid.h"
+#include "utils/ip_addr.h"
+#include "crypto/sha1.h"
+#include "rsn_supp/wpa.h"
+#include "eap_peer/eap.h"
+#include "p2p/p2p.h"
+#include "fst/fst.h"
+#include "config.h"
+
+
+#if !defined(CONFIG_CTRL_IFACE) && defined(CONFIG_NO_CONFIG_WRITE)
+#define NO_CONFIG_WRITE
+#endif
+
+/*
+ * Structure for network configuration parsing. This data is used to implement
+ * a generic parser for each network block variable. The table of configuration
+ * variables is defined below in this file (ssid_fields[]).
+ */
+struct parse_data {
+ /* Configuration variable name */
+ char *name;
+
+ /* Parser function for this variable */
+ int (*parser)(const struct parse_data *data, struct wpa_ssid *ssid,
+ int line, const char *value);
+
+#ifndef NO_CONFIG_WRITE
+ /* Writer function (i.e., to get the variable in text format from
+ * internal presentation). */
+ char * (*writer)(const struct parse_data *data, struct wpa_ssid *ssid);
+#endif /* NO_CONFIG_WRITE */
+
+ /* Variable specific parameters for the parser. */
+ void *param1, *param2, *param3, *param4;
+
+ /* 0 = this variable can be included in debug output and ctrl_iface
+ * 1 = this variable contains key/private data and it must not be
+ * included in debug output unless explicitly requested. In
+ * addition, this variable will not be readable through the
+ * ctrl_iface.
+ */
+ int key_data;
+};
+
+
+static int wpa_config_parse_str(const struct parse_data *data,
+ struct wpa_ssid *ssid,
+ int line, const char *value)
+{
+ size_t res_len, *dst_len;
+ char **dst, *tmp;
+
+ if (os_strcmp(value, "NULL") == 0) {
+ wpa_printf(MSG_DEBUG, "Unset configuration string '%s'",
+ data->name);
+ tmp = NULL;
+ res_len = 0;
+ goto set;
+ }
+
+ tmp = wpa_config_parse_string(value, &res_len);
+ if (tmp == NULL) {
+ wpa_printf(MSG_ERROR, "Line %d: failed to parse %s '%s'.",
+ line, data->name,
+ data->key_data ? "[KEY DATA REMOVED]" : value);
+ return -1;
+ }
+
+ if (data->key_data) {
+ wpa_hexdump_ascii_key(MSG_MSGDUMP, data->name,
+ (u8 *) tmp, res_len);
+ } else {
+ wpa_hexdump_ascii(MSG_MSGDUMP, data->name,
+ (u8 *) tmp, res_len);
+ }
+
+ if (data->param3 && res_len < (size_t) data->param3) {
+ wpa_printf(MSG_ERROR, "Line %d: too short %s (len=%lu "
+ "min_len=%ld)", line, data->name,
+ (unsigned long) res_len, (long) data->param3);
+ os_free(tmp);
+ return -1;
+ }
+
+ if (data->param4 && res_len > (size_t) data->param4) {
+ wpa_printf(MSG_ERROR, "Line %d: too long %s (len=%lu "
+ "max_len=%ld)", line, data->name,
+ (unsigned long) res_len, (long) data->param4);
+ os_free(tmp);
+ return -1;
+ }
+
+set:
+ dst = (char **) (((u8 *) ssid) + (long) data->param1);
+ dst_len = (size_t *) (((u8 *) ssid) + (long) data->param2);
+ os_free(*dst);
+ *dst = tmp;
+ if (data->param2)
+ *dst_len = res_len;
+
+ return 0;
+}
+
+
+#ifndef NO_CONFIG_WRITE
+static char * wpa_config_write_string_ascii(const u8 *value, size_t len)
+{
+ char *buf;
+
+ buf = os_malloc(len + 3);
+ if (buf == NULL)
+ return NULL;
+ buf[0] = '"';
+ os_memcpy(buf + 1, value, len);
+ buf[len + 1] = '"';
+ buf[len + 2] = '\0';
+
+ return buf;
+}
+
+
+static char * wpa_config_write_string_hex(const u8 *value, size_t len)
+{
+ char *buf;
+
+ buf = os_zalloc(2 * len + 1);
+ if (buf == NULL)
+ return NULL;
+ wpa_snprintf_hex(buf, 2 * len + 1, value, len);
+
+ return buf;
+}
+
+
+static char * wpa_config_write_string(const u8 *value, size_t len)
+{
+ if (value == NULL)
+ return NULL;
+
+ if (is_hex(value, len))
+ return wpa_config_write_string_hex(value, len);
+ else
+ return wpa_config_write_string_ascii(value, len);
+}
+
+
+static char * wpa_config_write_str(const struct parse_data *data,
+ struct wpa_ssid *ssid)
+{
+ size_t len;
+ char **src;
+
+ src = (char **) (((u8 *) ssid) + (long) data->param1);
+ if (*src == NULL)
+ return NULL;
+
+ if (data->param2)
+ len = *((size_t *) (((u8 *) ssid) + (long) data->param2));
+ else
+ len = os_strlen(*src);
+
+ return wpa_config_write_string((const u8 *) *src, len);
+}
+#endif /* NO_CONFIG_WRITE */
+
+
+static int wpa_config_parse_int(const struct parse_data *data,
+ struct wpa_ssid *ssid,
+ int line, const char *value)
+{
+ int val, *dst;
+ char *end;
+
+ dst = (int *) (((u8 *) ssid) + (long) data->param1);
+ val = strtol(value, &end, 0);
+ if (*end) {
+ wpa_printf(MSG_ERROR, "Line %d: invalid number \"%s\"",
+ line, value);
+ return -1;
+ }
+ *dst = val;
+ wpa_printf(MSG_MSGDUMP, "%s=%d (0x%x)", data->name, *dst, *dst);
+
+ if (data->param3 && *dst < (long) data->param3) {
+ wpa_printf(MSG_ERROR, "Line %d: too small %s (value=%d "
+ "min_value=%ld)", line, data->name, *dst,
+ (long) data->param3);
+ *dst = (long) data->param3;
+ return -1;
+ }
+
+ if (data->param4 && *dst > (long) data->param4) {
+ wpa_printf(MSG_ERROR, "Line %d: too large %s (value=%d "
+ "max_value=%ld)", line, data->name, *dst,
+ (long) data->param4);
+ *dst = (long) data->param4;
+ return -1;
+ }
+
+ return 0;
+}
+
+
+#ifndef NO_CONFIG_WRITE
+static char * wpa_config_write_int(const struct parse_data *data,
+ struct wpa_ssid *ssid)
+{
+ int *src, res;
+ char *value;
+
+ src = (int *) (((u8 *) ssid) + (long) data->param1);
+
+ value = os_malloc(20);
+ if (value == NULL)
+ return NULL;
+ res = os_snprintf(value, 20, "%d", *src);
+ if (os_snprintf_error(20, res)) {
+ os_free(value);
+ return NULL;
+ }
+ value[20 - 1] = '\0';
+ return value;
+}
+#endif /* NO_CONFIG_WRITE */
+
+
+static int wpa_config_parse_addr_list(const struct parse_data *data,
+ int line, const char *value,
+ u8 **list, size_t *num, char *name,
+ u8 abort_on_error, u8 masked)
+{
+ const char *pos;
+ u8 *buf, *n, addr[2 * ETH_ALEN];
+ size_t count;
+
+ buf = NULL;
+ count = 0;
+
+ pos = value;
+ while (pos && *pos) {
+ while (*pos == ' ')
+ pos++;
+
+ if (hwaddr_masked_aton(pos, addr, &addr[ETH_ALEN], masked)) {
+ if (abort_on_error || count == 0) {
+ wpa_printf(MSG_ERROR,
+ "Line %d: Invalid %s address '%s'",
+ line, name, value);
+ os_free(buf);
+ return -1;
+ }
+ /* continue anyway since this could have been from a
+ * truncated configuration file line */
+ wpa_printf(MSG_INFO,
+ "Line %d: Ignore likely truncated %s address '%s'",
+ line, name, pos);
+ } else {
+ n = os_realloc_array(buf, count + 1, 2 * ETH_ALEN);
+ if (n == NULL) {
+ os_free(buf);
+ return -1;
+ }
+ buf = n;
+ os_memmove(buf + 2 * ETH_ALEN, buf,
+ count * 2 * ETH_ALEN);
+ os_memcpy(buf, addr, 2 * ETH_ALEN);
+ count++;
+ wpa_printf(MSG_MSGDUMP,
+ "%s: addr=" MACSTR " mask=" MACSTR,
+ name, MAC2STR(addr),
+ MAC2STR(&addr[ETH_ALEN]));
+ }
+
+ pos = os_strchr(pos, ' ');
+ }
+
+ os_free(*list);
+ *list = buf;
+ *num = count;
+
+ return 0;
+}
+
+
+#ifndef NO_CONFIG_WRITE
+static char * wpa_config_write_addr_list(const struct parse_data *data,
+ const u8 *list, size_t num, char *name)
+{
+ char *value, *end, *pos;
+ int res;
+ size_t i;
+
+ if (list == NULL || num == 0)
+ return NULL;
+
+ value = os_malloc(2 * 20 * num);
+ if (value == NULL)
+ return NULL;
+ pos = value;
+ end = value + 2 * 20 * num;
+
+ for (i = num; i > 0; i--) {
+ const u8 *a = list + (i - 1) * 2 * ETH_ALEN;
+ const u8 *m = a + ETH_ALEN;
+
+ if (i < num)
+ *pos++ = ' ';
+ res = hwaddr_mask_txt(pos, end - pos, a, m);
+ if (res < 0) {
+ os_free(value);
+ return NULL;
+ }
+ pos += res;
+ }
+
+ return value;
+}
+#endif /* NO_CONFIG_WRITE */
+
+static int wpa_config_parse_bssid(const struct parse_data *data,
+ struct wpa_ssid *ssid, int line,
+ const char *value)
+{
+ if (value[0] == '\0' || os_strcmp(value, "\"\"") == 0 ||
+ os_strcmp(value, "any") == 0) {
+ ssid->bssid_set = 0;
+ wpa_printf(MSG_MSGDUMP, "BSSID any");
+ return 0;
+ }
+ if (hwaddr_aton(value, ssid->bssid)) {
+ wpa_printf(MSG_ERROR, "Line %d: Invalid BSSID '%s'.",
+ line, value);
+ return -1;
+ }
+ ssid->bssid_set = 1;
+ wpa_hexdump(MSG_MSGDUMP, "BSSID", ssid->bssid, ETH_ALEN);
+ return 0;
+}
+
+
+#ifndef NO_CONFIG_WRITE
+static char * wpa_config_write_bssid(const struct parse_data *data,
+ struct wpa_ssid *ssid)
+{
+ char *value;
+ int res;
+
+ if (!ssid->bssid_set)
+ return NULL;
+
+ value = os_malloc(20);
+ if (value == NULL)
+ return NULL;
+ res = os_snprintf(value, 20, MACSTR, MAC2STR(ssid->bssid));
+ if (os_snprintf_error(20, res)) {
+ os_free(value);
+ return NULL;
+ }
+ value[20 - 1] = '\0';
+ return value;
+}
+#endif /* NO_CONFIG_WRITE */
+
+
+static int wpa_config_parse_bssid_blacklist(const struct parse_data *data,
+ struct wpa_ssid *ssid, int line,
+ const char *value)
+{
+ return wpa_config_parse_addr_list(data, line, value,
+ &ssid->bssid_blacklist,
+ &ssid->num_bssid_blacklist,
+ "bssid_blacklist", 1, 1);
+}
+
+
+#ifndef NO_CONFIG_WRITE
+static char * wpa_config_write_bssid_blacklist(const struct parse_data *data,
+ struct wpa_ssid *ssid)
+{
+ return wpa_config_write_addr_list(data, ssid->bssid_blacklist,
+ ssid->num_bssid_blacklist,
+ "bssid_blacklist");
+}
+#endif /* NO_CONFIG_WRITE */
+
+
+static int wpa_config_parse_bssid_whitelist(const struct parse_data *data,
+ struct wpa_ssid *ssid, int line,
+ const char *value)
+{
+ return wpa_config_parse_addr_list(data, line, value,
+ &ssid->bssid_whitelist,
+ &ssid->num_bssid_whitelist,
+ "bssid_whitelist", 1, 1);
+}
+
+
+#ifndef NO_CONFIG_WRITE
+static char * wpa_config_write_bssid_whitelist(const struct parse_data *data,
+ struct wpa_ssid *ssid)
+{
+ return wpa_config_write_addr_list(data, ssid->bssid_whitelist,
+ ssid->num_bssid_whitelist,
+ "bssid_whitelist");
+}
+#endif /* NO_CONFIG_WRITE */
+
+
+static int wpa_config_parse_psk(const struct parse_data *data,
+ struct wpa_ssid *ssid, int line,
+ const char *value)
+{
+#ifdef CONFIG_EXT_PASSWORD
+ if (os_strncmp(value, "ext:", 4) == 0) {
+ str_clear_free(ssid->passphrase);
+ ssid->passphrase = NULL;
+ ssid->psk_set = 0;
+ os_free(ssid->ext_psk);
+ ssid->ext_psk = os_strdup(value + 4);
+ if (ssid->ext_psk == NULL)
+ return -1;
+ wpa_printf(MSG_DEBUG, "PSK: External password '%s'",
+ ssid->ext_psk);
+ return 0;
+ }
+#endif /* CONFIG_EXT_PASSWORD */
+
+ if (*value == '"') {
+#ifndef CONFIG_NO_PBKDF2
+ const char *pos;
+ size_t len;
+
+ value++;
+ pos = os_strrchr(value, '"');
+ if (pos)
+ len = pos - value;
+ else
+ len = os_strlen(value);
+ if (len < 8 || len > 63) {
+ wpa_printf(MSG_ERROR, "Line %d: Invalid passphrase "
+ "length %lu (expected: 8..63) '%s'.",
+ line, (unsigned long) len, value);
+ return -1;
+ }
+ wpa_hexdump_ascii_key(MSG_MSGDUMP, "PSK (ASCII passphrase)",
+ (u8 *) value, len);
+ if (ssid->passphrase && os_strlen(ssid->passphrase) == len &&
+ os_memcmp(ssid->passphrase, value, len) == 0)
+ return 0;
+ ssid->psk_set = 0;
+ str_clear_free(ssid->passphrase);
+ ssid->passphrase = dup_binstr(value, len);
+ if (ssid->passphrase == NULL)
+ return -1;
+ return 0;
+#else /* CONFIG_NO_PBKDF2 */
+ wpa_printf(MSG_ERROR, "Line %d: ASCII passphrase not "
+ "supported.", line);
+ return -1;
+#endif /* CONFIG_NO_PBKDF2 */
+ }
+
+ if (hexstr2bin(value, ssid->psk, PMK_LEN) ||
+ value[PMK_LEN * 2] != '\0') {
+ wpa_printf(MSG_ERROR, "Line %d: Invalid PSK '%s'.",
+ line, value);
+ return -1;
+ }
+
+ str_clear_free(ssid->passphrase);
+ ssid->passphrase = NULL;
+
+ ssid->psk_set = 1;
+ wpa_hexdump_key(MSG_MSGDUMP, "PSK", ssid->psk, PMK_LEN);
+ return 0;
+}
+
+
+#ifndef NO_CONFIG_WRITE
+static char * wpa_config_write_psk(const struct parse_data *data,
+ struct wpa_ssid *ssid)
+{
+#ifdef CONFIG_EXT_PASSWORD
+ if (ssid->ext_psk) {
+ size_t len = 4 + os_strlen(ssid->ext_psk) + 1;
+ char *buf = os_malloc(len);
+ int res;
+
+ if (buf == NULL)
+ return NULL;
+ res = os_snprintf(buf, len, "ext:%s", ssid->ext_psk);
+ if (os_snprintf_error(len, res)) {
+ os_free(buf);
+ buf = NULL;
+ }
+ return buf;
+ }
+#endif /* CONFIG_EXT_PASSWORD */
+
+ if (ssid->passphrase)
+ return wpa_config_write_string_ascii(
+ (const u8 *) ssid->passphrase,
+ os_strlen(ssid->passphrase));
+
+ if (ssid->psk_set)
+ return wpa_config_write_string_hex(ssid->psk, PMK_LEN);
+
+ return NULL;
+}
+#endif /* NO_CONFIG_WRITE */
+
+
+static int wpa_config_parse_proto(const struct parse_data *data,
+ struct wpa_ssid *ssid, int line,
+ const char *value)
+{
+ int val = 0, last, errors = 0;
+ char *start, *end, *buf;
+
+ buf = os_strdup(value);
+ if (buf == NULL)
+ return -1;
+ start = buf;
+
+ while (*start != '\0') {
+ while (*start == ' ' || *start == '\t')
+ start++;
+ if (*start == '\0')
+ break;
+ end = start;
+ while (*end != ' ' && *end != '\t' && *end != '\0')
+ end++;
+ last = *end == '\0';
+ *end = '\0';
+ if (os_strcmp(start, "WPA") == 0)
+ val |= WPA_PROTO_WPA;
+ else if (os_strcmp(start, "RSN") == 0 ||
+ os_strcmp(start, "WPA2") == 0)
+ val |= WPA_PROTO_RSN;
+ else if (os_strcmp(start, "OSEN") == 0)
+ val |= WPA_PROTO_OSEN;
+ else {
+ wpa_printf(MSG_ERROR, "Line %d: invalid proto '%s'",
+ line, start);
+ errors++;
+ }
+
+ if (last)
+ break;
+ start = end + 1;
+ }
+ os_free(buf);
+
+ if (val == 0) {
+ wpa_printf(MSG_ERROR,
+ "Line %d: no proto values configured.", line);
+ errors++;
+ }
+
+ wpa_printf(MSG_MSGDUMP, "proto: 0x%x", val);
+ ssid->proto = val;
+ return errors ? -1 : 0;
+}
+
+
+#ifndef NO_CONFIG_WRITE
+static char * wpa_config_write_proto(const struct parse_data *data,
+ struct wpa_ssid *ssid)
+{
+ int ret;
+ char *buf, *pos, *end;
+
+ pos = buf = os_zalloc(20);
+ if (buf == NULL)
+ return NULL;
+ end = buf + 20;
+
+ if (ssid->proto & WPA_PROTO_WPA) {
+ ret = os_snprintf(pos, end - pos, "%sWPA",
+ pos == buf ? "" : " ");
+ if (os_snprintf_error(end - pos, ret))
+ return buf;
+ pos += ret;
+ }
+
+ if (ssid->proto & WPA_PROTO_RSN) {
+ ret = os_snprintf(pos, end - pos, "%sRSN",
+ pos == buf ? "" : " ");
+ if (os_snprintf_error(end - pos, ret))
+ return buf;
+ pos += ret;
+ }
+
+ if (ssid->proto & WPA_PROTO_OSEN) {
+ ret = os_snprintf(pos, end - pos, "%sOSEN",
+ pos == buf ? "" : " ");
+ if (os_snprintf_error(end - pos, ret))
+ return buf;
+ pos += ret;
+ }
+
+ if (pos == buf) {
+ os_free(buf);
+ buf = NULL;
+ }
+
+ return buf;
+}
+#endif /* NO_CONFIG_WRITE */
+
+
+static int wpa_config_parse_key_mgmt(const struct parse_data *data,
+ struct wpa_ssid *ssid, int line,
+ const char *value)
+{
+ int val = 0, last, errors = 0;
+ char *start, *end, *buf;
+
+ buf = os_strdup(value);
+ if (buf == NULL)
+ return -1;
+ start = buf;
+
+ while (*start != '\0') {
+ while (*start == ' ' || *start == '\t')
+ start++;
+ if (*start == '\0')
+ break;
+ end = start;
+ while (*end != ' ' && *end != '\t' && *end != '\0')
+ end++;
+ last = *end == '\0';
+ *end = '\0';
+ if (os_strcmp(start, "WPA-PSK") == 0)
+ val |= WPA_KEY_MGMT_PSK;
+ else if (os_strcmp(start, "WPA-EAP") == 0)
+ val |= WPA_KEY_MGMT_IEEE8021X;
+ else if (os_strcmp(start, "IEEE8021X") == 0)
+ val |= WPA_KEY_MGMT_IEEE8021X_NO_WPA;
+ else if (os_strcmp(start, "NONE") == 0)
+ val |= WPA_KEY_MGMT_NONE;
+ else if (os_strcmp(start, "WPA-NONE") == 0)
+ val |= WPA_KEY_MGMT_WPA_NONE;
+#ifdef CONFIG_IEEE80211R
+ else if (os_strcmp(start, "FT-PSK") == 0)
+ val |= WPA_KEY_MGMT_FT_PSK;
+ else if (os_strcmp(start, "FT-EAP") == 0)
+ val |= WPA_KEY_MGMT_FT_IEEE8021X;
+#endif /* CONFIG_IEEE80211R */
+#ifdef CONFIG_IEEE80211W
+ else if (os_strcmp(start, "WPA-PSK-SHA256") == 0)
+ val |= WPA_KEY_MGMT_PSK_SHA256;
+ else if (os_strcmp(start, "WPA-EAP-SHA256") == 0)
+ val |= WPA_KEY_MGMT_IEEE8021X_SHA256;
+#endif /* CONFIG_IEEE80211W */
+#ifdef CONFIG_WPS
+ else if (os_strcmp(start, "WPS") == 0)
+ val |= WPA_KEY_MGMT_WPS;
+#endif /* CONFIG_WPS */
+#ifdef CONFIG_SAE
+ else if (os_strcmp(start, "SAE") == 0)
+ val |= WPA_KEY_MGMT_SAE;
+ else if (os_strcmp(start, "FT-SAE") == 0)
+ val |= WPA_KEY_MGMT_FT_SAE;
+#endif /* CONFIG_SAE */
+#ifdef CONFIG_HS20
+ else if (os_strcmp(start, "OSEN") == 0)
+ val |= WPA_KEY_MGMT_OSEN;
+#endif /* CONFIG_HS20 */
+#ifdef CONFIG_SUITEB
+ else if (os_strcmp(start, "WPA-EAP-SUITE-B") == 0)
+ val |= WPA_KEY_MGMT_IEEE8021X_SUITE_B;
+#endif /* CONFIG_SUITEB */
+#ifdef CONFIG_SUITEB192
+ else if (os_strcmp(start, "WPA-EAP-SUITE-B-192") == 0)
+ val |= WPA_KEY_MGMT_IEEE8021X_SUITE_B_192;
+#endif /* CONFIG_SUITEB192 */
+ else {
+ wpa_printf(MSG_ERROR, "Line %d: invalid key_mgmt '%s'",
+ line, start);
+ errors++;
+ }
+
+ if (last)
+ break;
+ start = end + 1;
+ }
+ os_free(buf);
+
+ if (val == 0) {
+ wpa_printf(MSG_ERROR,
+ "Line %d: no key_mgmt values configured.", line);
+ errors++;
+ }
+
+ wpa_printf(MSG_MSGDUMP, "key_mgmt: 0x%x", val);
+ ssid->key_mgmt = val;
+ return errors ? -1 : 0;
+}
+
+
+#ifndef NO_CONFIG_WRITE
+static char * wpa_config_write_key_mgmt(const struct parse_data *data,
+ struct wpa_ssid *ssid)
+{
+ char *buf, *pos, *end;
+ int ret;
+
+ pos = buf = os_zalloc(100);
+ if (buf == NULL)
+ return NULL;
+ end = buf + 100;
+
+ if (ssid->key_mgmt & WPA_KEY_MGMT_PSK) {
+ ret = os_snprintf(pos, end - pos, "%sWPA-PSK",
+ pos == buf ? "" : " ");
+ if (os_snprintf_error(end - pos, ret)) {
+ end[-1] = '\0';
+ return buf;
+ }
+ pos += ret;
+ }
+
+ if (ssid->key_mgmt & WPA_KEY_MGMT_IEEE8021X) {
+ ret = os_snprintf(pos, end - pos, "%sWPA-EAP",
+ pos == buf ? "" : " ");
+ if (os_snprintf_error(end - pos, ret)) {
+ end[-1] = '\0';
+ return buf;
+ }
+ pos += ret;
+ }
+
+ if (ssid->key_mgmt & WPA_KEY_MGMT_IEEE8021X_NO_WPA) {
+ ret = os_snprintf(pos, end - pos, "%sIEEE8021X",
+ pos == buf ? "" : " ");
+ if (os_snprintf_error(end - pos, ret)) {
+ end[-1] = '\0';
+ return buf;
+ }
+ pos += ret;
+ }
+
+ if (ssid->key_mgmt & WPA_KEY_MGMT_NONE) {
+ ret = os_snprintf(pos, end - pos, "%sNONE",
+ pos == buf ? "" : " ");
+ if (os_snprintf_error(end - pos, ret)) {
+ end[-1] = '\0';
+ return buf;
+ }
+ pos += ret;
+ }
+
+ if (ssid->key_mgmt & WPA_KEY_MGMT_WPA_NONE) {
+ ret = os_snprintf(pos, end - pos, "%sWPA-NONE",
+ pos == buf ? "" : " ");
+ if (os_snprintf_error(end - pos, ret)) {
+ end[-1] = '\0';
+ return buf;
+ }
+ pos += ret;
+ }
+
+#ifdef CONFIG_IEEE80211R
+ if (ssid->key_mgmt & WPA_KEY_MGMT_FT_PSK) {
+ ret = os_snprintf(pos, end - pos, "%sFT-PSK",
+ pos == buf ? "" : " ");
+ if (os_snprintf_error(end - pos, ret)) {
+ end[-1] = '\0';
+ return buf;
+ }
+ pos += ret;
+ }
+
+ if (ssid->key_mgmt & WPA_KEY_MGMT_FT_IEEE8021X) {
+ ret = os_snprintf(pos, end - pos, "%sFT-EAP",
+ pos == buf ? "" : " ");
+ if (os_snprintf_error(end - pos, ret)) {
+ end[-1] = '\0';
+ return buf;
+ }
+ pos += ret;
+ }
+#endif /* CONFIG_IEEE80211R */
+
+#ifdef CONFIG_IEEE80211W
+ if (ssid->key_mgmt & WPA_KEY_MGMT_PSK_SHA256) {
+ ret = os_snprintf(pos, end - pos, "%sWPA-PSK-SHA256",
+ pos == buf ? "" : " ");
+ if (os_snprintf_error(end - pos, ret)) {
+ end[-1] = '\0';
+ return buf;
+ }
+ pos += ret;
+ }
+
+ if (ssid->key_mgmt & WPA_KEY_MGMT_IEEE8021X_SHA256) {
+ ret = os_snprintf(pos, end - pos, "%sWPA-EAP-SHA256",
+ pos == buf ? "" : " ");
+ if (os_snprintf_error(end - pos, ret)) {
+ end[-1] = '\0';
+ return buf;
+ }
+ pos += ret;
+ }
+#endif /* CONFIG_IEEE80211W */
+
+#ifdef CONFIG_WPS
+ if (ssid->key_mgmt & WPA_KEY_MGMT_WPS) {
+ ret = os_snprintf(pos, end - pos, "%sWPS",
+ pos == buf ? "" : " ");
+ if (os_snprintf_error(end - pos, ret)) {
+ end[-1] = '\0';
+ return buf;
+ }
+ pos += ret;
+ }
+#endif /* CONFIG_WPS */
+
+#ifdef CONFIG_SAE
+ if (ssid->key_mgmt & WPA_KEY_MGMT_SAE) {
+ ret = os_snprintf(pos, end - pos, "%sSAE",
+ pos == buf ? "" : " ");
+ if (os_snprintf_error(end - pos, ret)) {
+ end[-1] = '\0';
+ return buf;
+ }
+ pos += ret;
+ }
+
+ if (ssid->key_mgmt & WPA_KEY_MGMT_FT_SAE) {
+ ret = os_snprintf(pos, end - pos, "%sFT-SAE",
+ pos == buf ? "" : " ");
+ if (os_snprintf_error(end - pos, ret)) {
+ end[-1] = '\0';
+ return buf;
+ }
+ pos += ret;
+ }
+#endif /* CONFIG_SAE */
+
+#ifdef CONFIG_HS20
+ if (ssid->key_mgmt & WPA_KEY_MGMT_OSEN) {
+ ret = os_snprintf(pos, end - pos, "%sOSEN",
+ pos == buf ? "" : " ");
+ if (os_snprintf_error(end - pos, ret)) {
+ end[-1] = '\0';
+ return buf;
+ }
+ pos += ret;
+ }
+#endif /* CONFIG_HS20 */
+
+#ifdef CONFIG_SUITEB
+ if (ssid->key_mgmt & WPA_KEY_MGMT_IEEE8021X_SUITE_B) {
+ ret = os_snprintf(pos, end - pos, "%sWPA-EAP-SUITE-B",
+ pos == buf ? "" : " ");
+ if (os_snprintf_error(end - pos, ret)) {
+ end[-1] = '\0';
+ return buf;
+ }
+ pos += ret;
+ }
+#endif /* CONFIG_SUITEB */
+
+#ifdef CONFIG_SUITEB192
+ if (ssid->key_mgmt & WPA_KEY_MGMT_IEEE8021X_SUITE_B_192) {
+ ret = os_snprintf(pos, end - pos, "%sWPA-EAP-SUITE-B-192",
+ pos == buf ? "" : " ");
+ if (os_snprintf_error(end - pos, ret)) {
+ end[-1] = '\0';
+ return buf;
+ }
+ pos += ret;
+ }
+#endif /* CONFIG_SUITEB192 */
+
+ if (pos == buf) {
+ os_free(buf);
+ buf = NULL;
+ }
+
+ return buf;
+}
+#endif /* NO_CONFIG_WRITE */
+
+
+static int wpa_config_parse_cipher(int line, const char *value)
+{
+ int val = wpa_parse_cipher(value);
+ if (val < 0) {
+ wpa_printf(MSG_ERROR, "Line %d: invalid cipher '%s'.",
+ line, value);
+ return -1;
+ }
+ if (val == 0) {
+ wpa_printf(MSG_ERROR, "Line %d: no cipher values configured.",
+ line);
+ return -1;
+ }
+ return val;
+}
+
+
+#ifndef NO_CONFIG_WRITE
+static char * wpa_config_write_cipher(int cipher)
+{
+ char *buf = os_zalloc(50);
+ if (buf == NULL)
+ return NULL;
+
+ if (wpa_write_ciphers(buf, buf + 50, cipher, " ") < 0) {
+ os_free(buf);
+ return NULL;
+ }
+
+ return buf;
+}
+#endif /* NO_CONFIG_WRITE */
+
+
+static int wpa_config_parse_pairwise(const struct parse_data *data,
+ struct wpa_ssid *ssid, int line,
+ const char *value)
+{
+ int val;
+ val = wpa_config_parse_cipher(line, value);
+ if (val == -1)
+ return -1;
+ if (val & ~WPA_ALLOWED_PAIRWISE_CIPHERS) {
+ wpa_printf(MSG_ERROR, "Line %d: not allowed pairwise cipher "
+ "(0x%x).", line, val);
+ return -1;
+ }
+
+ wpa_printf(MSG_MSGDUMP, "pairwise: 0x%x", val);
+ ssid->pairwise_cipher = val;
+ return 0;
+}
+
+
+#ifndef NO_CONFIG_WRITE
+static char * wpa_config_write_pairwise(const struct parse_data *data,
+ struct wpa_ssid *ssid)
+{
+ return wpa_config_write_cipher(ssid->pairwise_cipher);
+}
+#endif /* NO_CONFIG_WRITE */
+
+
+static int wpa_config_parse_group(const struct parse_data *data,
+ struct wpa_ssid *ssid, int line,
+ const char *value)
+{
+ int val;
+ val = wpa_config_parse_cipher(line, value);
+ if (val == -1)
+ return -1;
+
+ /*
+ * Backwards compatibility - filter out WEP ciphers that were previously
+ * allowed.
+ */
+ val &= ~(WPA_CIPHER_WEP104 | WPA_CIPHER_WEP40);
+
+ if (val & ~WPA_ALLOWED_GROUP_CIPHERS) {
+ wpa_printf(MSG_ERROR, "Line %d: not allowed group cipher "
+ "(0x%x).", line, val);
+ return -1;
+ }
+
+ wpa_printf(MSG_MSGDUMP, "group: 0x%x", val);
+ ssid->group_cipher = val;
+ return 0;
+}
+
+
+#ifndef NO_CONFIG_WRITE
+static char * wpa_config_write_group(const struct parse_data *data,
+ struct wpa_ssid *ssid)
+{
+ return wpa_config_write_cipher(ssid->group_cipher);
+}
+#endif /* NO_CONFIG_WRITE */
+
+
+static int wpa_config_parse_auth_alg(const struct parse_data *data,
+ struct wpa_ssid *ssid, int line,
+ const char *value)
+{
+ int val = 0, last, errors = 0;
+ char *start, *end, *buf;
+
+ buf = os_strdup(value);
+ if (buf == NULL)
+ return -1;
+ start = buf;
+
+ while (*start != '\0') {
+ while (*start == ' ' || *start == '\t')
+ start++;
+ if (*start == '\0')
+ break;
+ end = start;
+ while (*end != ' ' && *end != '\t' && *end != '\0')
+ end++;
+ last = *end == '\0';
+ *end = '\0';
+ if (os_strcmp(start, "OPEN") == 0)
+ val |= WPA_AUTH_ALG_OPEN;
+ else if (os_strcmp(start, "SHARED") == 0)
+ val |= WPA_AUTH_ALG_SHARED;
+ else if (os_strcmp(start, "LEAP") == 0)
+ val |= WPA_AUTH_ALG_LEAP;
+ else {
+ wpa_printf(MSG_ERROR, "Line %d: invalid auth_alg '%s'",
+ line, start);
+ errors++;
+ }
+
+ if (last)
+ break;
+ start = end + 1;
+ }
+ os_free(buf);
+
+ if (val == 0) {
+ wpa_printf(MSG_ERROR,
+ "Line %d: no auth_alg values configured.", line);
+ errors++;
+ }
+
+ wpa_printf(MSG_MSGDUMP, "auth_alg: 0x%x", val);
+ ssid->auth_alg = val;
+ return errors ? -1 : 0;
+}
+
+
+#ifndef NO_CONFIG_WRITE
+static char * wpa_config_write_auth_alg(const struct parse_data *data,
+ struct wpa_ssid *ssid)
+{
+ char *buf, *pos, *end;
+ int ret;
+
+ pos = buf = os_zalloc(30);
+ if (buf == NULL)
+ return NULL;
+ end = buf + 30;
+
+ if (ssid->auth_alg & WPA_AUTH_ALG_OPEN) {
+ ret = os_snprintf(pos, end - pos, "%sOPEN",
+ pos == buf ? "" : " ");
+ if (os_snprintf_error(end - pos, ret)) {
+ end[-1] = '\0';
+ return buf;
+ }
+ pos += ret;
+ }
+
+ if (ssid->auth_alg & WPA_AUTH_ALG_SHARED) {
+ ret = os_snprintf(pos, end - pos, "%sSHARED",
+ pos == buf ? "" : " ");
+ if (os_snprintf_error(end - pos, ret)) {
+ end[-1] = '\0';
+ return buf;
+ }
+ pos += ret;
+ }
+
+ if (ssid->auth_alg & WPA_AUTH_ALG_LEAP) {
+ ret = os_snprintf(pos, end - pos, "%sLEAP",
+ pos == buf ? "" : " ");
+ if (os_snprintf_error(end - pos, ret)) {
+ end[-1] = '\0';
+ return buf;
+ }
+ pos += ret;
+ }
+
+ if (pos == buf) {
+ os_free(buf);
+ buf = NULL;
+ }
+
+ return buf;
+}
+#endif /* NO_CONFIG_WRITE */
+
+
+static int * wpa_config_parse_int_array(const char *value)
+{
+ int *freqs;
+ size_t used, len;
+ const char *pos;
+
+ used = 0;
+ len = 10;
+ freqs = os_calloc(len + 1, sizeof(int));
+ if (freqs == NULL)
+ return NULL;
+
+ pos = value;
+ while (pos) {
+ while (*pos == ' ')
+ pos++;
+ if (used == len) {
+ int *n;
+ size_t i;
+ n = os_realloc_array(freqs, len * 2 + 1, sizeof(int));
+ if (n == NULL) {
+ os_free(freqs);
+ return NULL;
+ }
+ for (i = len; i <= len * 2; i++)
+ n[i] = 0;
+ freqs = n;
+ len *= 2;
+ }
+
+ freqs[used] = atoi(pos);
+ if (freqs[used] == 0)
+ break;
+ used++;
+ pos = os_strchr(pos + 1, ' ');
+ }
+
+ return freqs;
+}
+
+
+static int wpa_config_parse_scan_freq(const struct parse_data *data,
+ struct wpa_ssid *ssid, int line,
+ const char *value)
+{
+ int *freqs;
+
+ freqs = wpa_config_parse_int_array(value);
+ if (freqs == NULL)
+ return -1;
+ if (freqs[0] == 0) {
+ os_free(freqs);
+ freqs = NULL;
+ }
+ os_free(ssid->scan_freq);
+ ssid->scan_freq = freqs;
+
+ return 0;
+}
+
+
+static int wpa_config_parse_freq_list(const struct parse_data *data,
+ struct wpa_ssid *ssid, int line,
+ const char *value)
+{
+ int *freqs;
+
+ freqs = wpa_config_parse_int_array(value);
+ if (freqs == NULL)
+ return -1;
+ if (freqs[0] == 0) {
+ os_free(freqs);
+ freqs = NULL;
+ }
+ os_free(ssid->freq_list);
+ ssid->freq_list = freqs;
+
+ return 0;
+}
+
+
+#ifndef NO_CONFIG_WRITE
+static char * wpa_config_write_freqs(const struct parse_data *data,
+ const int *freqs)
+{
+ char *buf, *pos, *end;
+ int i, ret;
+ size_t count;
+
+ if (freqs == NULL)
+ return NULL;
+
+ count = 0;
+ for (i = 0; freqs[i]; i++)
+ count++;
+
+ pos = buf = os_zalloc(10 * count + 1);
+ if (buf == NULL)
+ return NULL;
+ end = buf + 10 * count + 1;
+
+ for (i = 0; freqs[i]; i++) {
+ ret = os_snprintf(pos, end - pos, "%s%u",
+ i == 0 ? "" : " ", freqs[i]);
+ if (os_snprintf_error(end - pos, ret)) {
+ end[-1] = '\0';
+ return buf;
+ }
+ pos += ret;
+ }
+
+ return buf;
+}
+
+
+static char * wpa_config_write_scan_freq(const struct parse_data *data,
+ struct wpa_ssid *ssid)
+{
+ return wpa_config_write_freqs(data, ssid->scan_freq);
+}
+
+
+static char * wpa_config_write_freq_list(const struct parse_data *data,
+ struct wpa_ssid *ssid)
+{
+ return wpa_config_write_freqs(data, ssid->freq_list);
+}
+#endif /* NO_CONFIG_WRITE */
+
+
+#ifdef IEEE8021X_EAPOL
+static int wpa_config_parse_eap(const struct parse_data *data,
+ struct wpa_ssid *ssid, int line,
+ const char *value)
+{
+ int last, errors = 0;
+ char *start, *end, *buf;
+ struct eap_method_type *methods = NULL, *tmp;
+ size_t num_methods = 0;
+
+ buf = os_strdup(value);
+ if (buf == NULL)
+ return -1;
+ start = buf;
+
+ while (*start != '\0') {
+ while (*start == ' ' || *start == '\t')
+ start++;
+ if (*start == '\0')
+ break;
+ end = start;
+ while (*end != ' ' && *end != '\t' && *end != '\0')
+ end++;
+ last = *end == '\0';
+ *end = '\0';
+ tmp = methods;
+ methods = os_realloc_array(methods, num_methods + 1,
+ sizeof(*methods));
+ if (methods == NULL) {
+ os_free(tmp);
+ os_free(buf);
+ return -1;
+ }
+ methods[num_methods].method = eap_peer_get_type(
+ start, &methods[num_methods].vendor);
+ if (methods[num_methods].vendor == EAP_VENDOR_IETF &&
+ methods[num_methods].method == EAP_TYPE_NONE) {
+ wpa_printf(MSG_ERROR, "Line %d: unknown EAP method "
+ "'%s'", line, start);
+ wpa_printf(MSG_ERROR, "You may need to add support for"
+ " this EAP method during wpa_supplicant\n"
+ "build time configuration.\n"
+ "See README for more information.");
+ errors++;
+ } else if (methods[num_methods].vendor == EAP_VENDOR_IETF &&
+ methods[num_methods].method == EAP_TYPE_LEAP)
+ ssid->leap++;
+ else
+ ssid->non_leap++;
+ num_methods++;
+ if (last)
+ break;
+ start = end + 1;
+ }
+ os_free(buf);
+
+ tmp = methods;
+ methods = os_realloc_array(methods, num_methods + 1, sizeof(*methods));
+ if (methods == NULL) {
+ os_free(tmp);
+ return -1;
+ }
+ methods[num_methods].vendor = EAP_VENDOR_IETF;
+ methods[num_methods].method = EAP_TYPE_NONE;
+ num_methods++;
+
+ wpa_hexdump(MSG_MSGDUMP, "eap methods",
+ (u8 *) methods, num_methods * sizeof(*methods));
+ os_free(ssid->eap.eap_methods);
+ ssid->eap.eap_methods = methods;
+ return errors ? -1 : 0;
+}
+
+
+#ifndef NO_CONFIG_WRITE
+static char * wpa_config_write_eap(const struct parse_data *data,
+ struct wpa_ssid *ssid)
+{
+ int i, ret;
+ char *buf, *pos, *end;
+ const struct eap_method_type *eap_methods = ssid->eap.eap_methods;
+ const char *name;
+
+ if (eap_methods == NULL)
+ return NULL;
+
+ pos = buf = os_zalloc(100);
+ if (buf == NULL)
+ return NULL;
+ end = buf + 100;
+
+ for (i = 0; eap_methods[i].vendor != EAP_VENDOR_IETF ||
+ eap_methods[i].method != EAP_TYPE_NONE; i++) {
+ name = eap_get_name(eap_methods[i].vendor,
+ eap_methods[i].method);
+ if (name) {
+ ret = os_snprintf(pos, end - pos, "%s%s",
+ pos == buf ? "" : " ", name);
+ if (os_snprintf_error(end - pos, ret))
+ break;
+ pos += ret;
+ }
+ }
+
+ end[-1] = '\0';
+
+ return buf;
+}
+#endif /* NO_CONFIG_WRITE */
+
+
+static int wpa_config_parse_password(const struct parse_data *data,
+ struct wpa_ssid *ssid, int line,
+ const char *value)
+{
+ u8 *hash;
+
+ if (os_strcmp(value, "NULL") == 0) {
+ wpa_printf(MSG_DEBUG, "Unset configuration string 'password'");
+ bin_clear_free(ssid->eap.password, ssid->eap.password_len);
+ ssid->eap.password = NULL;
+ ssid->eap.password_len = 0;
+ return 0;
+ }
+
+#ifdef CONFIG_EXT_PASSWORD
+ if (os_strncmp(value, "ext:", 4) == 0) {
+ char *name = os_strdup(value + 4);
+ if (name == NULL)
+ return -1;
+ bin_clear_free(ssid->eap.password, ssid->eap.password_len);
+ ssid->eap.password = (u8 *) name;
+ ssid->eap.password_len = os_strlen(name);
+ ssid->eap.flags &= ~EAP_CONFIG_FLAGS_PASSWORD_NTHASH;
+ ssid->eap.flags |= EAP_CONFIG_FLAGS_EXT_PASSWORD;
+ return 0;
+ }
+#endif /* CONFIG_EXT_PASSWORD */
+
+ if (os_strncmp(value, "hash:", 5) != 0) {
+ char *tmp;
+ size_t res_len;
+
+ tmp = wpa_config_parse_string(value, &res_len);
+ if (tmp == NULL) {
+ wpa_printf(MSG_ERROR, "Line %d: failed to parse "
+ "password.", line);
+ return -1;
+ }
+ wpa_hexdump_ascii_key(MSG_MSGDUMP, data->name,
+ (u8 *) tmp, res_len);
+
+ bin_clear_free(ssid->eap.password, ssid->eap.password_len);
+ ssid->eap.password = (u8 *) tmp;
+ ssid->eap.password_len = res_len;
+ ssid->eap.flags &= ~EAP_CONFIG_FLAGS_PASSWORD_NTHASH;
+ ssid->eap.flags &= ~EAP_CONFIG_FLAGS_EXT_PASSWORD;
+
+ return 0;
+ }
+
+
+ /* NtPasswordHash: hash:<32 hex digits> */
+ if (os_strlen(value + 5) != 2 * 16) {
+ wpa_printf(MSG_ERROR, "Line %d: Invalid password hash length "
+ "(expected 32 hex digits)", line);
+ return -1;
+ }
+
+ hash = os_malloc(16);
+ if (hash == NULL)
+ return -1;
+
+ if (hexstr2bin(value + 5, hash, 16)) {
+ os_free(hash);
+ wpa_printf(MSG_ERROR, "Line %d: Invalid password hash", line);
+ return -1;
+ }
+
+ wpa_hexdump_key(MSG_MSGDUMP, data->name, hash, 16);
+
+ bin_clear_free(ssid->eap.password, ssid->eap.password_len);
+ ssid->eap.password = hash;
+ ssid->eap.password_len = 16;
+ ssid->eap.flags |= EAP_CONFIG_FLAGS_PASSWORD_NTHASH;
+ ssid->eap.flags &= ~EAP_CONFIG_FLAGS_EXT_PASSWORD;
+
+ return 0;
+}
+
+
+#ifndef NO_CONFIG_WRITE
+static char * wpa_config_write_password(const struct parse_data *data,
+ struct wpa_ssid *ssid)
+{
+ char *buf;
+
+ if (ssid->eap.password == NULL)
+ return NULL;
+
+#ifdef CONFIG_EXT_PASSWORD
+ if (ssid->eap.flags & EAP_CONFIG_FLAGS_EXT_PASSWORD) {
+ buf = os_zalloc(4 + ssid->eap.password_len + 1);
+ if (buf == NULL)
+ return NULL;
+ os_memcpy(buf, "ext:", 4);
+ os_memcpy(buf + 4, ssid->eap.password, ssid->eap.password_len);
+ return buf;
+ }
+#endif /* CONFIG_EXT_PASSWORD */
+
+ if (!(ssid->eap.flags & EAP_CONFIG_FLAGS_PASSWORD_NTHASH)) {
+ return wpa_config_write_string(
+ ssid->eap.password, ssid->eap.password_len);
+ }
+
+ buf = os_malloc(5 + 32 + 1);
+ if (buf == NULL)
+ return NULL;
+
+ os_memcpy(buf, "hash:", 5);
+ wpa_snprintf_hex(buf + 5, 32 + 1, ssid->eap.password, 16);
+
+ return buf;
+}
+#endif /* NO_CONFIG_WRITE */
+#endif /* IEEE8021X_EAPOL */
+
+
+static int wpa_config_parse_wep_key(u8 *key, size_t *len, int line,
+ const char *value, int idx)
+{
+ char *buf, title[20];
+ int res;
+
+ buf = wpa_config_parse_string(value, len);
+ if (buf == NULL) {
+ wpa_printf(MSG_ERROR, "Line %d: Invalid WEP key %d '%s'.",
+ line, idx, value);
+ return -1;
+ }
+ if (*len > MAX_WEP_KEY_LEN) {
+ wpa_printf(MSG_ERROR, "Line %d: Too long WEP key %d '%s'.",
+ line, idx, value);
+ os_free(buf);
+ return -1;
+ }
+ if (*len && *len != 5 && *len != 13 && *len != 16) {
+ wpa_printf(MSG_ERROR, "Line %d: Invalid WEP key length %u - "
+ "this network block will be ignored",
+ line, (unsigned int) *len);
+ }
+ os_memcpy(key, buf, *len);
+ str_clear_free(buf);
+ res = os_snprintf(title, sizeof(title), "wep_key%d", idx);
+ if (!os_snprintf_error(sizeof(title), res))
+ wpa_hexdump_key(MSG_MSGDUMP, title, key, *len);
+ return 0;
+}
+
+
+static int wpa_config_parse_wep_key0(const struct parse_data *data,
+ struct wpa_ssid *ssid, int line,
+ const char *value)
+{
+ return wpa_config_parse_wep_key(ssid->wep_key[0],
+ &ssid->wep_key_len[0], line,
+ value, 0);
+}
+
+
+static int wpa_config_parse_wep_key1(const struct parse_data *data,
+ struct wpa_ssid *ssid, int line,
+ const char *value)
+{
+ return wpa_config_parse_wep_key(ssid->wep_key[1],
+ &ssid->wep_key_len[1], line,
+ value, 1);
+}
+
+
+static int wpa_config_parse_wep_key2(const struct parse_data *data,
+ struct wpa_ssid *ssid, int line,
+ const char *value)
+{
+ return wpa_config_parse_wep_key(ssid->wep_key[2],
+ &ssid->wep_key_len[2], line,
+ value, 2);
+}
+
+
+static int wpa_config_parse_wep_key3(const struct parse_data *data,
+ struct wpa_ssid *ssid, int line,
+ const char *value)
+{
+ return wpa_config_parse_wep_key(ssid->wep_key[3],
+ &ssid->wep_key_len[3], line,
+ value, 3);
+}
+
+
+#ifndef NO_CONFIG_WRITE
+static char * wpa_config_write_wep_key(struct wpa_ssid *ssid, int idx)
+{
+ if (ssid->wep_key_len[idx] == 0)
+ return NULL;
+ return wpa_config_write_string(ssid->wep_key[idx],
+ ssid->wep_key_len[idx]);
+}
+
+
+static char * wpa_config_write_wep_key0(const struct parse_data *data,
+ struct wpa_ssid *ssid)
+{
+ return wpa_config_write_wep_key(ssid, 0);
+}
+
+
+static char * wpa_config_write_wep_key1(const struct parse_data *data,
+ struct wpa_ssid *ssid)
+{
+ return wpa_config_write_wep_key(ssid, 1);
+}
+
+
+static char * wpa_config_write_wep_key2(const struct parse_data *data,
+ struct wpa_ssid *ssid)
+{
+ return wpa_config_write_wep_key(ssid, 2);
+}
+
+
+static char * wpa_config_write_wep_key3(const struct parse_data *data,
+ struct wpa_ssid *ssid)
+{
+ return wpa_config_write_wep_key(ssid, 3);
+}
+#endif /* NO_CONFIG_WRITE */
+
+
+#ifdef CONFIG_P2P
+
+static int wpa_config_parse_go_p2p_dev_addr(const struct parse_data *data,
+ struct wpa_ssid *ssid, int line,
+ const char *value)
+{
+ if (value[0] == '\0' || os_strcmp(value, "\"\"") == 0 ||
+ os_strcmp(value, "any") == 0) {
+ os_memset(ssid->go_p2p_dev_addr, 0, ETH_ALEN);
+ wpa_printf(MSG_MSGDUMP, "GO P2P Device Address any");
+ return 0;
+ }
+ if (hwaddr_aton(value, ssid->go_p2p_dev_addr)) {
+ wpa_printf(MSG_ERROR, "Line %d: Invalid GO P2P Device Address '%s'.",
+ line, value);
+ return -1;
+ }
+ ssid->bssid_set = 1;
+ wpa_printf(MSG_MSGDUMP, "GO P2P Device Address " MACSTR,
+ MAC2STR(ssid->go_p2p_dev_addr));
+ return 0;
+}
+
+
+#ifndef NO_CONFIG_WRITE
+static char * wpa_config_write_go_p2p_dev_addr(const struct parse_data *data,
+ struct wpa_ssid *ssid)
+{
+ char *value;
+ int res;
+
+ if (is_zero_ether_addr(ssid->go_p2p_dev_addr))
+ return NULL;
+
+ value = os_malloc(20);
+ if (value == NULL)
+ return NULL;
+ res = os_snprintf(value, 20, MACSTR, MAC2STR(ssid->go_p2p_dev_addr));
+ if (os_snprintf_error(20, res)) {
+ os_free(value);
+ return NULL;
+ }
+ value[20 - 1] = '\0';
+ return value;
+}
+#endif /* NO_CONFIG_WRITE */
+
+
+static int wpa_config_parse_p2p_client_list(const struct parse_data *data,
+ struct wpa_ssid *ssid, int line,
+ const char *value)
+{
+ return wpa_config_parse_addr_list(data, line, value,
+ &ssid->p2p_client_list,
+ &ssid->num_p2p_clients,
+ "p2p_client_list", 0, 0);
+}
+
+
+#ifndef NO_CONFIG_WRITE
+static char * wpa_config_write_p2p_client_list(const struct parse_data *data,
+ struct wpa_ssid *ssid)
+{
+ return wpa_config_write_addr_list(data, ssid->p2p_client_list,
+ ssid->num_p2p_clients,
+ "p2p_client_list");
+}
+#endif /* NO_CONFIG_WRITE */
+
+
+static int wpa_config_parse_psk_list(const struct parse_data *data,
+ struct wpa_ssid *ssid, int line,
+ const char *value)
+{
+ struct psk_list_entry *p;
+ const char *pos;
+
+ p = os_zalloc(sizeof(*p));
+ if (p == NULL)
+ return -1;
+
+ pos = value;
+ if (os_strncmp(pos, "P2P-", 4) == 0) {
+ p->p2p = 1;
+ pos += 4;
+ }
+
+ if (hwaddr_aton(pos, p->addr)) {
+ wpa_printf(MSG_ERROR, "Line %d: Invalid psk_list address '%s'",
+ line, pos);
+ os_free(p);
+ return -1;
+ }
+ pos += 17;
+ if (*pos != '-') {
+ wpa_printf(MSG_ERROR, "Line %d: Invalid psk_list '%s'",
+ line, pos);
+ os_free(p);
+ return -1;
+ }
+ pos++;
+
+ if (hexstr2bin(pos, p->psk, PMK_LEN) || pos[PMK_LEN * 2] != '\0') {
+ wpa_printf(MSG_ERROR, "Line %d: Invalid psk_list PSK '%s'",
+ line, pos);
+ os_free(p);
+ return -1;
+ }
+
+ dl_list_add(&ssid->psk_list, &p->list);
+
+ return 0;
+}
+
+
+#ifndef NO_CONFIG_WRITE
+static char * wpa_config_write_psk_list(const struct parse_data *data,
+ struct wpa_ssid *ssid)
+{
+ return NULL;
+}
+#endif /* NO_CONFIG_WRITE */
+
+#endif /* CONFIG_P2P */
+
+
+#ifdef CONFIG_MESH
+
+static int wpa_config_parse_mesh_basic_rates(const struct parse_data *data,
+ struct wpa_ssid *ssid, int line,
+ const char *value)
+{
+ int *rates = wpa_config_parse_int_array(value);
+
+ if (rates == NULL) {
+ wpa_printf(MSG_ERROR, "Line %d: Invalid mesh_basic_rates '%s'",
+ line, value);
+ return -1;
+ }
+ if (rates[0] == 0) {
+ os_free(rates);
+ rates = NULL;
+ }
+
+ os_free(ssid->mesh_basic_rates);
+ ssid->mesh_basic_rates = rates;
+
+ return 0;
+}
+
+
+#ifndef NO_CONFIG_WRITE
+
+static char * wpa_config_write_mesh_basic_rates(const struct parse_data *data,
+ struct wpa_ssid *ssid)
+{
+ return wpa_config_write_freqs(data, ssid->mesh_basic_rates);
+}
+
+#endif /* NO_CONFIG_WRITE */
+
+#endif /* CONFIG_MESH */
+
+
+/* Helper macros for network block parser */
+
+#ifdef OFFSET
+#undef OFFSET
+#endif /* OFFSET */
+/* OFFSET: Get offset of a variable within the wpa_ssid structure */
+#define OFFSET(v) ((void *) &((struct wpa_ssid *) 0)->v)
+
+/* STR: Define a string variable for an ASCII string; f = field name */
+#ifdef NO_CONFIG_WRITE
+#define _STR(f) #f, wpa_config_parse_str, OFFSET(f)
+#define _STRe(f) #f, wpa_config_parse_str, OFFSET(eap.f)
+#else /* NO_CONFIG_WRITE */
+#define _STR(f) #f, wpa_config_parse_str, wpa_config_write_str, OFFSET(f)
+#define _STRe(f) #f, wpa_config_parse_str, wpa_config_write_str, OFFSET(eap.f)
+#endif /* NO_CONFIG_WRITE */
+#define STR(f) _STR(f), NULL, NULL, NULL, 0
+#define STRe(f) _STRe(f), NULL, NULL, NULL, 0
+#define STR_KEY(f) _STR(f), NULL, NULL, NULL, 1
+#define STR_KEYe(f) _STRe(f), NULL, NULL, NULL, 1
+
+/* STR_LEN: Define a string variable with a separate variable for storing the
+ * data length. Unlike STR(), this can be used to store arbitrary binary data
+ * (i.e., even nul termination character). */
+#define _STR_LEN(f) _STR(f), OFFSET(f ## _len)
+#define _STR_LENe(f) _STRe(f), OFFSET(eap.f ## _len)
+#define STR_LEN(f) _STR_LEN(f), NULL, NULL, 0
+#define STR_LENe(f) _STR_LENe(f), NULL, NULL, 0
+#define STR_LEN_KEY(f) _STR_LEN(f), NULL, NULL, 1
+
+/* STR_RANGE: Like STR_LEN(), but with minimum and maximum allowed length
+ * explicitly specified. */
+#define _STR_RANGE(f, min, max) _STR_LEN(f), (void *) (min), (void *) (max)
+#define STR_RANGE(f, min, max) _STR_RANGE(f, min, max), 0
+#define STR_RANGE_KEY(f, min, max) _STR_RANGE(f, min, max), 1
+
+#ifdef NO_CONFIG_WRITE
+#define _INT(f) #f, wpa_config_parse_int, OFFSET(f), (void *) 0
+#define _INTe(f) #f, wpa_config_parse_int, OFFSET(eap.f), (void *) 0
+#else /* NO_CONFIG_WRITE */
+#define _INT(f) #f, wpa_config_parse_int, wpa_config_write_int, \
+ OFFSET(f), (void *) 0
+#define _INTe(f) #f, wpa_config_parse_int, wpa_config_write_int, \
+ OFFSET(eap.f), (void *) 0
+#endif /* NO_CONFIG_WRITE */
+
+/* INT: Define an integer variable */
+#define INT(f) _INT(f), NULL, NULL, 0
+#define INTe(f) _INTe(f), NULL, NULL, 0
+
+/* INT_RANGE: Define an integer variable with allowed value range */
+#define INT_RANGE(f, min, max) _INT(f), (void *) (min), (void *) (max), 0
+
+/* FUNC: Define a configuration variable that uses a custom function for
+ * parsing and writing the value. */
+#ifdef NO_CONFIG_WRITE
+#define _FUNC(f) #f, wpa_config_parse_ ## f, NULL, NULL, NULL, NULL
+#else /* NO_CONFIG_WRITE */
+#define _FUNC(f) #f, wpa_config_parse_ ## f, wpa_config_write_ ## f, \
+ NULL, NULL, NULL, NULL
+#endif /* NO_CONFIG_WRITE */
+#define FUNC(f) _FUNC(f), 0
+#define FUNC_KEY(f) _FUNC(f), 1
+
+/*
+ * Table of network configuration variables. This table is used to parse each
+ * network configuration variable, e.g., each line in wpa_supplicant.conf file
+ * that is inside a network block.
+ *
+ * This table is generated using the helper macros defined above and with
+ * generous help from the C pre-processor. The field name is stored as a string
+ * into .name and for STR and INT types, the offset of the target buffer within
+ * struct wpa_ssid is stored in .param1. .param2 (if not NULL) is similar
+ * offset to the field containing the length of the configuration variable.
+ * .param3 and .param4 can be used to mark the allowed range (length for STR
+ * and value for INT).
+ *
+ * For each configuration line in wpa_supplicant.conf, the parser goes through
+ * this table and select the entry that matches with the field name. The parser
+ * function (.parser) is then called to parse the actual value of the field.
+ *
+ * This kind of mechanism makes it easy to add new configuration parameters,
+ * since only one line needs to be added into this table and into the
+ * struct wpa_ssid definition if the new variable is either a string or
+ * integer. More complex types will need to use their own parser and writer
+ * functions.
+ */
+static const struct parse_data ssid_fields[] = {
+ { STR_RANGE(ssid, 0, SSID_MAX_LEN) },
+ { INT_RANGE(scan_ssid, 0, 1) },
+ { FUNC(bssid) },
+ { FUNC(bssid_blacklist) },
+ { FUNC(bssid_whitelist) },
+ { FUNC_KEY(psk) },
+ { INT(mem_only_psk) },
+ { FUNC(proto) },
+ { FUNC(key_mgmt) },
+ { INT(bg_scan_period) },
+ { FUNC(pairwise) },
+ { FUNC(group) },
+ { FUNC(auth_alg) },
+ { FUNC(scan_freq) },
+ { FUNC(freq_list) },
+#ifdef IEEE8021X_EAPOL
+ { FUNC(eap) },
+ { STR_LENe(identity) },
+ { STR_LENe(anonymous_identity) },
+ { FUNC_KEY(password) },
+ { STRe(ca_cert) },
+ { STRe(ca_path) },
+ { STRe(client_cert) },
+ { STRe(private_key) },
+ { STR_KEYe(private_key_passwd) },
+ { STRe(dh_file) },
+ { STRe(subject_match) },
+ { STRe(altsubject_match) },
+ { STRe(domain_suffix_match) },
+ { STRe(domain_match) },
+ { STRe(ca_cert2) },
+ { STRe(ca_path2) },
+ { STRe(client_cert2) },
+ { STRe(private_key2) },
+ { STR_KEYe(private_key2_passwd) },
+ { STRe(dh_file2) },
+ { STRe(subject_match2) },
+ { STRe(altsubject_match2) },
+ { STRe(domain_suffix_match2) },
+ { STRe(domain_match2) },
+ { STRe(phase1) },
+ { STRe(phase2) },
+ { STRe(pcsc) },
+ { STR_KEYe(pin) },
+ { STRe(engine_id) },
+ { STRe(key_id) },
+ { STRe(cert_id) },
+ { STRe(ca_cert_id) },
+ { STR_KEYe(pin2) },
+ { STRe(engine2_id) },
+ { STRe(key2_id) },
+ { STRe(cert2_id) },
+ { STRe(ca_cert2_id) },
+ { INTe(engine) },
+ { INTe(engine2) },
+ { INT(eapol_flags) },
+ { INTe(sim_num) },
+ { STRe(openssl_ciphers) },
+ { INTe(erp) },
+#endif /* IEEE8021X_EAPOL */
+ { FUNC_KEY(wep_key0) },
+ { FUNC_KEY(wep_key1) },
+ { FUNC_KEY(wep_key2) },
+ { FUNC_KEY(wep_key3) },
+ { INT(wep_tx_keyidx) },
+ { INT(priority) },
+#ifdef IEEE8021X_EAPOL
+ { INT(eap_workaround) },
+ { STRe(pac_file) },
+ { INTe(fragment_size) },
+ { INTe(ocsp) },
+#endif /* IEEE8021X_EAPOL */
+#ifdef CONFIG_MESH
+ { INT_RANGE(mode, 0, 5) },
+ { INT_RANGE(no_auto_peer, 0, 1) },
+#else /* CONFIG_MESH */
+ { INT_RANGE(mode, 0, 4) },
+#endif /* CONFIG_MESH */
+ { INT_RANGE(proactive_key_caching, 0, 1) },
+ { INT_RANGE(disabled, 0, 2) },
+ { STR(id_str) },
+#ifdef CONFIG_IEEE80211W
+ { INT_RANGE(ieee80211w, 0, 2) },
+#endif /* CONFIG_IEEE80211W */
+ { INT_RANGE(peerkey, 0, 1) },
+ { INT_RANGE(mixed_cell, 0, 1) },
+ { INT_RANGE(frequency, 0, 65000) },
+ { INT_RANGE(fixed_freq, 0, 1) },
+#ifdef CONFIG_MESH
+ { FUNC(mesh_basic_rates) },
+ { INT(dot11MeshMaxRetries) },
+ { INT(dot11MeshRetryTimeout) },
+ { INT(dot11MeshConfirmTimeout) },
+ { INT(dot11MeshHoldingTimeout) },
+#endif /* CONFIG_MESH */
+ { INT(wpa_ptk_rekey) },
+ { STR(bgscan) },
+ { INT_RANGE(ignore_broadcast_ssid, 0, 2) },
+#ifdef CONFIG_P2P
+ { FUNC(go_p2p_dev_addr) },
+ { FUNC(p2p_client_list) },
+ { FUNC(psk_list) },
+#endif /* CONFIG_P2P */
+#ifdef CONFIG_HT_OVERRIDES
+ { INT_RANGE(disable_ht, 0, 1) },
+ { INT_RANGE(disable_ht40, -1, 1) },
+ { INT_RANGE(disable_sgi, 0, 1) },
+ { INT_RANGE(disable_ldpc, 0, 1) },
+ { INT_RANGE(ht40_intolerant, 0, 1) },
+ { INT_RANGE(disable_max_amsdu, -1, 1) },
+ { INT_RANGE(ampdu_factor, -1, 3) },
+ { INT_RANGE(ampdu_density, -1, 7) },
+ { STR(ht_mcs) },
+#endif /* CONFIG_HT_OVERRIDES */
+#ifdef CONFIG_VHT_OVERRIDES
+ { INT_RANGE(disable_vht, 0, 1) },
+ { INT(vht_capa) },
+ { INT(vht_capa_mask) },
+ { INT_RANGE(vht_rx_mcs_nss_1, -1, 3) },
+ { INT_RANGE(vht_rx_mcs_nss_2, -1, 3) },
+ { INT_RANGE(vht_rx_mcs_nss_3, -1, 3) },
+ { INT_RANGE(vht_rx_mcs_nss_4, -1, 3) },
+ { INT_RANGE(vht_rx_mcs_nss_5, -1, 3) },
+ { INT_RANGE(vht_rx_mcs_nss_6, -1, 3) },
+ { INT_RANGE(vht_rx_mcs_nss_7, -1, 3) },
+ { INT_RANGE(vht_rx_mcs_nss_8, -1, 3) },
+ { INT_RANGE(vht_tx_mcs_nss_1, -1, 3) },
+ { INT_RANGE(vht_tx_mcs_nss_2, -1, 3) },
+ { INT_RANGE(vht_tx_mcs_nss_3, -1, 3) },
+ { INT_RANGE(vht_tx_mcs_nss_4, -1, 3) },
+ { INT_RANGE(vht_tx_mcs_nss_5, -1, 3) },
+ { INT_RANGE(vht_tx_mcs_nss_6, -1, 3) },
+ { INT_RANGE(vht_tx_mcs_nss_7, -1, 3) },
+ { INT_RANGE(vht_tx_mcs_nss_8, -1, 3) },
+#endif /* CONFIG_VHT_OVERRIDES */
+ { INT(ap_max_inactivity) },
+ { INT(dtim_period) },
+ { INT(beacon_int) },
+#ifdef CONFIG_MACSEC
+ { INT_RANGE(macsec_policy, 0, 1) },
+#endif /* CONFIG_MACSEC */
+#ifdef CONFIG_HS20
+ { INT(update_identifier) },
+#endif /* CONFIG_HS20 */
+ { INT_RANGE(mac_addr, 0, 2) },
+};
+
+#undef OFFSET
+#undef _STR
+#undef STR
+#undef STR_KEY
+#undef _STR_LEN
+#undef STR_LEN
+#undef STR_LEN_KEY
+#undef _STR_RANGE
+#undef STR_RANGE
+#undef STR_RANGE_KEY
+#undef _INT
+#undef INT
+#undef INT_RANGE
+#undef _FUNC
+#undef FUNC
+#undef FUNC_KEY
+#define NUM_SSID_FIELDS ARRAY_SIZE(ssid_fields)
+
+
+/**
+ * wpa_config_add_prio_network - Add a network to priority lists
+ * @config: Configuration data from wpa_config_read()
+ * @ssid: Pointer to the network configuration to be added to the list
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function is used to add a network block to the priority list of
+ * networks. This must be called for each network when reading in the full
+ * configuration. In addition, this can be used indirectly when updating
+ * priorities by calling wpa_config_update_prio_list().
+ */
+int wpa_config_add_prio_network(struct wpa_config *config,
+ struct wpa_ssid *ssid)
+{
+ int prio;
+ struct wpa_ssid *prev, **nlist;
+
+ /*
+ * Add to an existing priority list if one is available for the
+ * configured priority level for this network.
+ */
+ for (prio = 0; prio < config->num_prio; prio++) {
+ prev = config->pssid[prio];
+ if (prev->priority == ssid->priority) {
+ while (prev->pnext)
+ prev = prev->pnext;
+ prev->pnext = ssid;
+ return 0;
+ }
+ }
+
+ /* First network for this priority - add a new priority list */
+ nlist = os_realloc_array(config->pssid, config->num_prio + 1,
+ sizeof(struct wpa_ssid *));
+ if (nlist == NULL)
+ return -1;
+
+ for (prio = 0; prio < config->num_prio; prio++) {
+ if (nlist[prio]->priority < ssid->priority) {
+ os_memmove(&nlist[prio + 1], &nlist[prio],
+ (config->num_prio - prio) *
+ sizeof(struct wpa_ssid *));
+ break;
+ }
+ }
+
+ nlist[prio] = ssid;
+ config->num_prio++;
+ config->pssid = nlist;
+
+ return 0;
+}
+
+
+/**
+ * wpa_config_update_prio_list - Update network priority list
+ * @config: Configuration data from wpa_config_read()
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function is called to update the priority list of networks in the
+ * configuration when a network is being added or removed. This is also called
+ * if a priority for a network is changed.
+ */
+int wpa_config_update_prio_list(struct wpa_config *config)
+{
+ struct wpa_ssid *ssid;
+ int ret = 0;
+
+ os_free(config->pssid);
+ config->pssid = NULL;
+ config->num_prio = 0;
+
+ ssid = config->ssid;
+ while (ssid) {
+ ssid->pnext = NULL;
+ if (wpa_config_add_prio_network(config, ssid) < 0)
+ ret = -1;
+ ssid = ssid->next;
+ }
+
+ return ret;
+}
+
+
+#ifdef IEEE8021X_EAPOL
+static void eap_peer_config_free(struct eap_peer_config *eap)
+{
+ os_free(eap->eap_methods);
+ bin_clear_free(eap->identity, eap->identity_len);
+ os_free(eap->anonymous_identity);
+ bin_clear_free(eap->password, eap->password_len);
+ os_free(eap->ca_cert);
+ os_free(eap->ca_path);
+ os_free(eap->client_cert);
+ os_free(eap->private_key);
+ str_clear_free(eap->private_key_passwd);
+ os_free(eap->dh_file);
+ os_free(eap->subject_match);
+ os_free(eap->altsubject_match);
+ os_free(eap->domain_suffix_match);
+ os_free(eap->domain_match);
+ os_free(eap->ca_cert2);
+ os_free(eap->ca_path2);
+ os_free(eap->client_cert2);
+ os_free(eap->private_key2);
+ str_clear_free(eap->private_key2_passwd);
+ os_free(eap->dh_file2);
+ os_free(eap->subject_match2);
+ os_free(eap->altsubject_match2);
+ os_free(eap->domain_suffix_match2);
+ os_free(eap->domain_match2);
+ os_free(eap->phase1);
+ os_free(eap->phase2);
+ os_free(eap->pcsc);
+ str_clear_free(eap->pin);
+ os_free(eap->engine_id);
+ os_free(eap->key_id);
+ os_free(eap->cert_id);
+ os_free(eap->ca_cert_id);
+ os_free(eap->key2_id);
+ os_free(eap->cert2_id);
+ os_free(eap->ca_cert2_id);
+ str_clear_free(eap->pin2);
+ os_free(eap->engine2_id);
+ os_free(eap->otp);
+ os_free(eap->pending_req_otp);
+ os_free(eap->pac_file);
+ bin_clear_free(eap->new_password, eap->new_password_len);
+ str_clear_free(eap->external_sim_resp);
+ os_free(eap->openssl_ciphers);
+}
+#endif /* IEEE8021X_EAPOL */
+
+
+/**
+ * wpa_config_free_ssid - Free network/ssid configuration data
+ * @ssid: Configuration data for the network
+ *
+ * This function frees all resources allocated for the network configuration
+ * data.
+ */
+void wpa_config_free_ssid(struct wpa_ssid *ssid)
+{
+ struct psk_list_entry *psk;
+
+ os_free(ssid->ssid);
+ str_clear_free(ssid->passphrase);
+ os_free(ssid->ext_psk);
+#ifdef IEEE8021X_EAPOL
+ eap_peer_config_free(&ssid->eap);
+#endif /* IEEE8021X_EAPOL */
+ os_free(ssid->id_str);
+ os_free(ssid->scan_freq);
+ os_free(ssid->freq_list);
+ os_free(ssid->bgscan);
+ os_free(ssid->p2p_client_list);
+ os_free(ssid->bssid_blacklist);
+ os_free(ssid->bssid_whitelist);
+#ifdef CONFIG_HT_OVERRIDES
+ os_free(ssid->ht_mcs);
+#endif /* CONFIG_HT_OVERRIDES */
+#ifdef CONFIG_MESH
+ os_free(ssid->mesh_basic_rates);
+#endif /* CONFIG_MESH */
+ while ((psk = dl_list_first(&ssid->psk_list, struct psk_list_entry,
+ list))) {
+ dl_list_del(&psk->list);
+ bin_clear_free(psk, sizeof(*psk));
+ }
+ bin_clear_free(ssid, sizeof(*ssid));
+}
+
+
+void wpa_config_free_cred(struct wpa_cred *cred)
+{
+ size_t i;
+
+ os_free(cred->realm);
+ str_clear_free(cred->username);
+ str_clear_free(cred->password);
+ os_free(cred->ca_cert);
+ os_free(cred->client_cert);
+ os_free(cred->private_key);
+ str_clear_free(cred->private_key_passwd);
+ os_free(cred->imsi);
+ str_clear_free(cred->milenage);
+ for (i = 0; i < cred->num_domain; i++)
+ os_free(cred->domain[i]);
+ os_free(cred->domain);
+ os_free(cred->domain_suffix_match);
+ os_free(cred->eap_method);
+ os_free(cred->phase1);
+ os_free(cred->phase2);
+ os_free(cred->excluded_ssid);
+ os_free(cred->roaming_partner);
+ os_free(cred->provisioning_sp);
+ for (i = 0; i < cred->num_req_conn_capab; i++)
+ os_free(cred->req_conn_capab_port[i]);
+ os_free(cred->req_conn_capab_port);
+ os_free(cred->req_conn_capab_proto);
+ os_free(cred);
+}
+
+
+void wpa_config_flush_blobs(struct wpa_config *config)
+{
+#ifndef CONFIG_NO_CONFIG_BLOBS
+ struct wpa_config_blob *blob, *prev;
+
+ blob = config->blobs;
+ config->blobs = NULL;
+ while (blob) {
+ prev = blob;
+ blob = blob->next;
+ wpa_config_free_blob(prev);
+ }
+#endif /* CONFIG_NO_CONFIG_BLOBS */
+}
+
+
+/**
+ * wpa_config_free - Free configuration data
+ * @config: Configuration data from wpa_config_read()
+ *
+ * This function frees all resources allocated for the configuration data by
+ * wpa_config_read().
+ */
+void wpa_config_free(struct wpa_config *config)
+{
+ struct wpa_ssid *ssid, *prev = NULL;
+ struct wpa_cred *cred, *cprev;
+ int i;
+
+ ssid = config->ssid;
+ while (ssid) {
+ prev = ssid;
+ ssid = ssid->next;
+ wpa_config_free_ssid(prev);
+ }
+
+ cred = config->cred;
+ while (cred) {
+ cprev = cred;
+ cred = cred->next;
+ wpa_config_free_cred(cprev);
+ }
+
+ wpa_config_flush_blobs(config);
+
+ wpabuf_free(config->wps_vendor_ext_m1);
+ for (i = 0; i < MAX_WPS_VENDOR_EXT; i++)
+ wpabuf_free(config->wps_vendor_ext[i]);
+ os_free(config->ctrl_interface);
+ os_free(config->ctrl_interface_group);
+ os_free(config->opensc_engine_path);
+ os_free(config->pkcs11_engine_path);
+ os_free(config->pkcs11_module_path);
+ os_free(config->openssl_ciphers);
+ os_free(config->pcsc_reader);
+ str_clear_free(config->pcsc_pin);
+ os_free(config->driver_param);
+ os_free(config->device_name);
+ os_free(config->manufacturer);
+ os_free(config->model_name);
+ os_free(config->model_number);
+ os_free(config->serial_number);
+ os_free(config->config_methods);
+ os_free(config->p2p_ssid_postfix);
+ os_free(config->pssid);
+ os_free(config->p2p_pref_chan);
+ os_free(config->p2p_no_go_freq.range);
+ os_free(config->autoscan);
+ os_free(config->freq_list);
+ wpabuf_free(config->wps_nfc_dh_pubkey);
+ wpabuf_free(config->wps_nfc_dh_privkey);
+ wpabuf_free(config->wps_nfc_dev_pw);
+ os_free(config->ext_password_backend);
+ os_free(config->sae_groups);
+ wpabuf_free(config->ap_vendor_elements);
+ os_free(config->osu_dir);
+ os_free(config->bgscan);
+ os_free(config->wowlan_triggers);
+ os_free(config->fst_group_id);
+ os_free(config);
+}
+
+
+/**
+ * wpa_config_foreach_network - Iterate over each configured network
+ * @config: Configuration data from wpa_config_read()
+ * @func: Callback function to process each network
+ * @arg: Opaque argument to pass to callback function
+ *
+ * Iterate over the set of configured networks calling the specified
+ * function for each item. We guard against callbacks removing the
+ * supplied network.
+ */
+void wpa_config_foreach_network(struct wpa_config *config,
+ void (*func)(void *, struct wpa_ssid *),
+ void *arg)
+{
+ struct wpa_ssid *ssid, *next;
+
+ ssid = config->ssid;
+ while (ssid) {
+ next = ssid->next;
+ func(arg, ssid);
+ ssid = next;
+ }
+}
+
+
+/**
+ * wpa_config_get_network - Get configured network based on id
+ * @config: Configuration data from wpa_config_read()
+ * @id: Unique network id to search for
+ * Returns: Network configuration or %NULL if not found
+ */
+struct wpa_ssid * wpa_config_get_network(struct wpa_config *config, int id)
+{
+ struct wpa_ssid *ssid;
+
+ ssid = config->ssid;
+ while (ssid) {
+ if (id == ssid->id)
+ break;
+ ssid = ssid->next;
+ }
+
+ return ssid;
+}
+
+
+/**
+ * wpa_config_add_network - Add a new network with empty configuration
+ * @config: Configuration data from wpa_config_read()
+ * Returns: The new network configuration or %NULL if operation failed
+ */
+struct wpa_ssid * wpa_config_add_network(struct wpa_config *config)
+{
+ int id;
+ struct wpa_ssid *ssid, *last = NULL;
+
+ id = -1;
+ ssid = config->ssid;
+ while (ssid) {
+ if (ssid->id > id)
+ id = ssid->id;
+ last = ssid;
+ ssid = ssid->next;
+ }
+ id++;
+
+ ssid = os_zalloc(sizeof(*ssid));
+ if (ssid == NULL)
+ return NULL;
+ ssid->id = id;
+ dl_list_init(&ssid->psk_list);
+ if (last)
+ last->next = ssid;
+ else
+ config->ssid = ssid;
+
+ wpa_config_update_prio_list(config);
+
+ return ssid;
+}
+
+
+/**
+ * wpa_config_remove_network - Remove a configured network based on id
+ * @config: Configuration data from wpa_config_read()
+ * @id: Unique network id to search for
+ * Returns: 0 on success, or -1 if the network was not found
+ */
+int wpa_config_remove_network(struct wpa_config *config, int id)
+{
+ struct wpa_ssid *ssid, *prev = NULL;
+
+ ssid = config->ssid;
+ while (ssid) {
+ if (id == ssid->id)
+ break;
+ prev = ssid;
+ ssid = ssid->next;
+ }
+
+ if (ssid == NULL)
+ return -1;
+
+ if (prev)
+ prev->next = ssid->next;
+ else
+ config->ssid = ssid->next;
+
+ wpa_config_update_prio_list(config);
+ wpa_config_free_ssid(ssid);
+ return 0;
+}
+
+
+/**
+ * wpa_config_set_network_defaults - Set network default values
+ * @ssid: Pointer to network configuration data
+ */
+void wpa_config_set_network_defaults(struct wpa_ssid *ssid)
+{
+ ssid->proto = DEFAULT_PROTO;
+ ssid->pairwise_cipher = DEFAULT_PAIRWISE;
+ ssid->group_cipher = DEFAULT_GROUP;
+ ssid->key_mgmt = DEFAULT_KEY_MGMT;
+ ssid->bg_scan_period = DEFAULT_BG_SCAN_PERIOD;
+#ifdef IEEE8021X_EAPOL
+ ssid->eapol_flags = DEFAULT_EAPOL_FLAGS;
+ ssid->eap_workaround = DEFAULT_EAP_WORKAROUND;
+ ssid->eap.fragment_size = DEFAULT_FRAGMENT_SIZE;
+ ssid->eap.sim_num = DEFAULT_USER_SELECTED_SIM;
+#endif /* IEEE8021X_EAPOL */
+#ifdef CONFIG_MESH
+ ssid->dot11MeshMaxRetries = DEFAULT_MESH_MAX_RETRIES;
+ ssid->dot11MeshRetryTimeout = DEFAULT_MESH_RETRY_TIMEOUT;
+ ssid->dot11MeshConfirmTimeout = DEFAULT_MESH_CONFIRM_TIMEOUT;
+ ssid->dot11MeshHoldingTimeout = DEFAULT_MESH_HOLDING_TIMEOUT;
+#endif /* CONFIG_MESH */
+#ifdef CONFIG_HT_OVERRIDES
+ ssid->disable_ht = DEFAULT_DISABLE_HT;
+ ssid->disable_ht40 = DEFAULT_DISABLE_HT40;
+ ssid->disable_sgi = DEFAULT_DISABLE_SGI;
+ ssid->disable_ldpc = DEFAULT_DISABLE_LDPC;
+ ssid->disable_max_amsdu = DEFAULT_DISABLE_MAX_AMSDU;
+ ssid->ampdu_factor = DEFAULT_AMPDU_FACTOR;
+ ssid->ampdu_density = DEFAULT_AMPDU_DENSITY;
+#endif /* CONFIG_HT_OVERRIDES */
+#ifdef CONFIG_VHT_OVERRIDES
+ ssid->vht_rx_mcs_nss_1 = -1;
+ ssid->vht_rx_mcs_nss_2 = -1;
+ ssid->vht_rx_mcs_nss_3 = -1;
+ ssid->vht_rx_mcs_nss_4 = -1;
+ ssid->vht_rx_mcs_nss_5 = -1;
+ ssid->vht_rx_mcs_nss_6 = -1;
+ ssid->vht_rx_mcs_nss_7 = -1;
+ ssid->vht_rx_mcs_nss_8 = -1;
+ ssid->vht_tx_mcs_nss_1 = -1;
+ ssid->vht_tx_mcs_nss_2 = -1;
+ ssid->vht_tx_mcs_nss_3 = -1;
+ ssid->vht_tx_mcs_nss_4 = -1;
+ ssid->vht_tx_mcs_nss_5 = -1;
+ ssid->vht_tx_mcs_nss_6 = -1;
+ ssid->vht_tx_mcs_nss_7 = -1;
+ ssid->vht_tx_mcs_nss_8 = -1;
+#endif /* CONFIG_VHT_OVERRIDES */
+ ssid->proactive_key_caching = -1;
+#ifdef CONFIG_IEEE80211W
+ ssid->ieee80211w = MGMT_FRAME_PROTECTION_DEFAULT;
+#endif /* CONFIG_IEEE80211W */
+ ssid->mac_addr = -1;
+}
+
+
+/**
+ * wpa_config_set - Set a variable in network configuration
+ * @ssid: Pointer to network configuration data
+ * @var: Variable name, e.g., "ssid"
+ * @value: Variable value
+ * @line: Line number in configuration file or 0 if not used
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function can be used to set network configuration variables based on
+ * both the configuration file and management interface input. The value
+ * parameter must be in the same format as the text-based configuration file is
+ * using. For example, strings are using double quotation marks.
+ */
+int wpa_config_set(struct wpa_ssid *ssid, const char *var, const char *value,
+ int line)
+{
+ size_t i;
+ int ret = 0;
+
+ if (ssid == NULL || var == NULL || value == NULL)
+ return -1;
+
+ for (i = 0; i < NUM_SSID_FIELDS; i++) {
+ const struct parse_data *field = &ssid_fields[i];
+ if (os_strcmp(var, field->name) != 0)
+ continue;
+
+ if (field->parser(field, ssid, line, value)) {
+ if (line) {
+ wpa_printf(MSG_ERROR, "Line %d: failed to "
+ "parse %s '%s'.", line, var, value);
+ }
+ ret = -1;
+ }
+ break;
+ }
+ if (i == NUM_SSID_FIELDS) {
+ if (line) {
+ wpa_printf(MSG_ERROR, "Line %d: unknown network field "
+ "'%s'.", line, var);
+ }
+ ret = -1;
+ }
+
+ return ret;
+}
+
+
+int wpa_config_set_quoted(struct wpa_ssid *ssid, const char *var,
+ const char *value)
+{
+ size_t len;
+ char *buf;
+ int ret;
+
+ len = os_strlen(value);
+ buf = os_malloc(len + 3);
+ if (buf == NULL)
+ return -1;
+ buf[0] = '"';
+ os_memcpy(buf + 1, value, len);
+ buf[len + 1] = '"';
+ buf[len + 2] = '\0';
+ ret = wpa_config_set(ssid, var, buf, 0);
+ os_free(buf);
+ return ret;
+}
+
+
+/**
+ * wpa_config_get_all - Get all options from network configuration
+ * @ssid: Pointer to network configuration data
+ * @get_keys: Determines if keys/passwords will be included in returned list
+ * (if they may be exported)
+ * Returns: %NULL terminated list of all set keys and their values in the form
+ * of [key1, val1, key2, val2, ... , NULL]
+ *
+ * This function can be used to get list of all configured network properties.
+ * The caller is responsible for freeing the returned list and all its
+ * elements.
+ */
+char ** wpa_config_get_all(struct wpa_ssid *ssid, int get_keys)
+{
+#ifdef NO_CONFIG_WRITE
+ return NULL;
+#else /* NO_CONFIG_WRITE */
+ const struct parse_data *field;
+ char *key, *value;
+ size_t i;
+ char **props;
+ int fields_num;
+
+ get_keys = get_keys && ssid->export_keys;
+
+ props = os_calloc(2 * NUM_SSID_FIELDS + 1, sizeof(char *));
+ if (!props)
+ return NULL;
+
+ fields_num = 0;
+ for (i = 0; i < NUM_SSID_FIELDS; i++) {
+ field = &ssid_fields[i];
+ if (field->key_data && !get_keys)
+ continue;
+ value = field->writer(field, ssid);
+ if (value == NULL)
+ continue;
+ if (os_strlen(value) == 0) {
+ os_free(value);
+ continue;
+ }
+
+ key = os_strdup(field->name);
+ if (key == NULL) {
+ os_free(value);
+ goto err;
+ }
+
+ props[fields_num * 2] = key;
+ props[fields_num * 2 + 1] = value;
+
+ fields_num++;
+ }
+
+ return props;
+
+err:
+ value = *props;
+ while (value)
+ os_free(value++);
+ os_free(props);
+ return NULL;
+#endif /* NO_CONFIG_WRITE */
+}
+
+
+#ifndef NO_CONFIG_WRITE
+/**
+ * wpa_config_get - Get a variable in network configuration
+ * @ssid: Pointer to network configuration data
+ * @var: Variable name, e.g., "ssid"
+ * Returns: Value of the variable or %NULL on failure
+ *
+ * This function can be used to get network configuration variables. The
+ * returned value is a copy of the configuration variable in text format, i.e,.
+ * the same format that the text-based configuration file and wpa_config_set()
+ * are using for the value. The caller is responsible for freeing the returned
+ * value.
+ */
+char * wpa_config_get(struct wpa_ssid *ssid, const char *var)
+{
+ size_t i;
+
+ if (ssid == NULL || var == NULL)
+ return NULL;
+
+ for (i = 0; i < NUM_SSID_FIELDS; i++) {
+ const struct parse_data *field = &ssid_fields[i];
+ if (os_strcmp(var, field->name) == 0)
+ return field->writer(field, ssid);
+ }
+
+ return NULL;
+}
+
+
+/**
+ * wpa_config_get_no_key - Get a variable in network configuration (no keys)
+ * @ssid: Pointer to network configuration data
+ * @var: Variable name, e.g., "ssid"
+ * Returns: Value of the variable or %NULL on failure
+ *
+ * This function can be used to get network configuration variable like
+ * wpa_config_get(). The only difference is that this functions does not expose
+ * key/password material from the configuration. In case a key/password field
+ * is requested, the returned value is an empty string or %NULL if the variable
+ * is not set or "*" if the variable is set (regardless of its value). The
+ * returned value is a copy of the configuration variable in text format, i.e,.
+ * the same format that the text-based configuration file and wpa_config_set()
+ * are using for the value. The caller is responsible for freeing the returned
+ * value.
+ */
+char * wpa_config_get_no_key(struct wpa_ssid *ssid, const char *var)
+{
+ size_t i;
+
+ if (ssid == NULL || var == NULL)
+ return NULL;
+
+ for (i = 0; i < NUM_SSID_FIELDS; i++) {
+ const struct parse_data *field = &ssid_fields[i];
+ if (os_strcmp(var, field->name) == 0) {
+ char *res = field->writer(field, ssid);
+ if (field->key_data) {
+ if (res && res[0]) {
+ wpa_printf(MSG_DEBUG, "Do not allow "
+ "key_data field to be "
+ "exposed");
+ str_clear_free(res);
+ return os_strdup("*");
+ }
+
+ os_free(res);
+ return NULL;
+ }
+ return res;
+ }
+ }
+
+ return NULL;
+}
+#endif /* NO_CONFIG_WRITE */
+
+
+/**
+ * wpa_config_update_psk - Update WPA PSK based on passphrase and SSID
+ * @ssid: Pointer to network configuration data
+ *
+ * This function must be called to update WPA PSK when either SSID or the
+ * passphrase has changed for the network configuration.
+ */
+void wpa_config_update_psk(struct wpa_ssid *ssid)
+{
+#ifndef CONFIG_NO_PBKDF2
+ pbkdf2_sha1(ssid->passphrase, ssid->ssid, ssid->ssid_len, 4096,
+ ssid->psk, PMK_LEN);
+ wpa_hexdump_key(MSG_MSGDUMP, "PSK (from passphrase)",
+ ssid->psk, PMK_LEN);
+ ssid->psk_set = 1;
+#endif /* CONFIG_NO_PBKDF2 */
+}
+
+
+static int wpa_config_set_cred_req_conn_capab(struct wpa_cred *cred,
+ const char *value)
+{
+ u8 *proto;
+ int **port;
+ int *ports, *nports;
+ const char *pos;
+ unsigned int num_ports;
+
+ proto = os_realloc_array(cred->req_conn_capab_proto,
+ cred->num_req_conn_capab + 1, sizeof(u8));
+ if (proto == NULL)
+ return -1;
+ cred->req_conn_capab_proto = proto;
+
+ port = os_realloc_array(cred->req_conn_capab_port,
+ cred->num_req_conn_capab + 1, sizeof(int *));
+ if (port == NULL)
+ return -1;
+ cred->req_conn_capab_port = port;
+
+ proto[cred->num_req_conn_capab] = atoi(value);
+
+ pos = os_strchr(value, ':');
+ if (pos == NULL) {
+ port[cred->num_req_conn_capab] = NULL;
+ cred->num_req_conn_capab++;
+ return 0;
+ }
+ pos++;
+
+ ports = NULL;
+ num_ports = 0;
+
+ while (*pos) {
+ nports = os_realloc_array(ports, num_ports + 1, sizeof(int));
+ if (nports == NULL) {
+ os_free(ports);
+ return -1;
+ }
+ ports = nports;
+ ports[num_ports++] = atoi(pos);
+
+ pos = os_strchr(pos, ',');
+ if (pos == NULL)
+ break;
+ pos++;
+ }
+
+ nports = os_realloc_array(ports, num_ports + 1, sizeof(int));
+ if (nports == NULL) {
+ os_free(ports);
+ return -1;
+ }
+ ports = nports;
+ ports[num_ports] = -1;
+
+ port[cred->num_req_conn_capab] = ports;
+ cred->num_req_conn_capab++;
+ return 0;
+}
+
+
+int wpa_config_set_cred(struct wpa_cred *cred, const char *var,
+ const char *value, int line)
+{
+ char *val;
+ size_t len;
+
+ if (os_strcmp(var, "temporary") == 0) {
+ cred->temporary = atoi(value);
+ return 0;
+ }
+
+ if (os_strcmp(var, "priority") == 0) {
+ cred->priority = atoi(value);
+ return 0;
+ }
+
+ if (os_strcmp(var, "sp_priority") == 0) {
+ int prio = atoi(value);
+ if (prio < 0 || prio > 255)
+ return -1;
+ cred->sp_priority = prio;
+ return 0;
+ }
+
+ if (os_strcmp(var, "pcsc") == 0) {
+ cred->pcsc = atoi(value);
+ return 0;
+ }
+
+ if (os_strcmp(var, "eap") == 0) {
+ struct eap_method_type method;
+ method.method = eap_peer_get_type(value, &method.vendor);
+ if (method.vendor == EAP_VENDOR_IETF &&
+ method.method == EAP_TYPE_NONE) {
+ wpa_printf(MSG_ERROR, "Line %d: unknown EAP type '%s' "
+ "for a credential", line, value);
+ return -1;
+ }
+ os_free(cred->eap_method);
+ cred->eap_method = os_malloc(sizeof(*cred->eap_method));
+ if (cred->eap_method == NULL)
+ return -1;
+ os_memcpy(cred->eap_method, &method, sizeof(method));
+ return 0;
+ }
+
+ if (os_strcmp(var, "password") == 0 &&
+ os_strncmp(value, "ext:", 4) == 0) {
+ str_clear_free(cred->password);
+ cred->password = os_strdup(value);
+ cred->ext_password = 1;
+ return 0;
+ }
+
+ if (os_strcmp(var, "update_identifier") == 0) {
+ cred->update_identifier = atoi(value);
+ return 0;
+ }
+
+ if (os_strcmp(var, "min_dl_bandwidth_home") == 0) {
+ cred->min_dl_bandwidth_home = atoi(value);
+ return 0;
+ }
+
+ if (os_strcmp(var, "min_ul_bandwidth_home") == 0) {
+ cred->min_ul_bandwidth_home = atoi(value);
+ return 0;
+ }
+
+ if (os_strcmp(var, "min_dl_bandwidth_roaming") == 0) {
+ cred->min_dl_bandwidth_roaming = atoi(value);
+ return 0;
+ }
+
+ if (os_strcmp(var, "min_ul_bandwidth_roaming") == 0) {
+ cred->min_ul_bandwidth_roaming = atoi(value);
+ return 0;
+ }
+
+ if (os_strcmp(var, "max_bss_load") == 0) {
+ cred->max_bss_load = atoi(value);
+ return 0;
+ }
+
+ if (os_strcmp(var, "req_conn_capab") == 0)
+ return wpa_config_set_cred_req_conn_capab(cred, value);
+
+ if (os_strcmp(var, "ocsp") == 0) {
+ cred->ocsp = atoi(value);
+ return 0;
+ }
+
+ if (os_strcmp(var, "sim_num") == 0) {
+ cred->sim_num = atoi(value);
+ return 0;
+ }
+
+ val = wpa_config_parse_string(value, &len);
+ if (val == NULL) {
+ wpa_printf(MSG_ERROR, "Line %d: invalid field '%s' string "
+ "value '%s'.", line, var, value);
+ return -1;
+ }
+
+ if (os_strcmp(var, "realm") == 0) {
+ os_free(cred->realm);
+ cred->realm = val;
+ return 0;
+ }
+
+ if (os_strcmp(var, "username") == 0) {
+ str_clear_free(cred->username);
+ cred->username = val;
+ return 0;
+ }
+
+ if (os_strcmp(var, "password") == 0) {
+ str_clear_free(cred->password);
+ cred->password = val;
+ cred->ext_password = 0;
+ return 0;
+ }
+
+ if (os_strcmp(var, "ca_cert") == 0) {
+ os_free(cred->ca_cert);
+ cred->ca_cert = val;
+ return 0;
+ }
+
+ if (os_strcmp(var, "client_cert") == 0) {
+ os_free(cred->client_cert);
+ cred->client_cert = val;
+ return 0;
+ }
+
+ if (os_strcmp(var, "private_key") == 0) {
+ os_free(cred->private_key);
+ cred->private_key = val;
+ return 0;
+ }
+
+ if (os_strcmp(var, "private_key_passwd") == 0) {
+ str_clear_free(cred->private_key_passwd);
+ cred->private_key_passwd = val;
+ return 0;
+ }
+
+ if (os_strcmp(var, "imsi") == 0) {
+ os_free(cred->imsi);
+ cred->imsi = val;
+ return 0;
+ }
+
+ if (os_strcmp(var, "milenage") == 0) {
+ str_clear_free(cred->milenage);
+ cred->milenage = val;
+ return 0;
+ }
+
+ if (os_strcmp(var, "domain_suffix_match") == 0) {
+ os_free(cred->domain_suffix_match);
+ cred->domain_suffix_match = val;
+ return 0;
+ }
+
+ if (os_strcmp(var, "domain") == 0) {
+ char **new_domain;
+ new_domain = os_realloc_array(cred->domain,
+ cred->num_domain + 1,
+ sizeof(char *));
+ if (new_domain == NULL) {
+ os_free(val);
+ return -1;
+ }
+ new_domain[cred->num_domain++] = val;
+ cred->domain = new_domain;
+ return 0;
+ }
+
+ if (os_strcmp(var, "phase1") == 0) {
+ os_free(cred->phase1);
+ cred->phase1 = val;
+ return 0;
+ }
+
+ if (os_strcmp(var, "phase2") == 0) {
+ os_free(cred->phase2);
+ cred->phase2 = val;
+ return 0;
+ }
+
+ if (os_strcmp(var, "roaming_consortium") == 0) {
+ if (len < 3 || len > sizeof(cred->roaming_consortium)) {
+ wpa_printf(MSG_ERROR, "Line %d: invalid "
+ "roaming_consortium length %d (3..15 "
+ "expected)", line, (int) len);
+ os_free(val);
+ return -1;
+ }
+ os_memcpy(cred->roaming_consortium, val, len);
+ cred->roaming_consortium_len = len;
+ os_free(val);
+ return 0;
+ }
+
+ if (os_strcmp(var, "required_roaming_consortium") == 0) {
+ if (len < 3 || len > sizeof(cred->required_roaming_consortium))
+ {
+ wpa_printf(MSG_ERROR, "Line %d: invalid "
+ "required_roaming_consortium length %d "
+ "(3..15 expected)", line, (int) len);
+ os_free(val);
+ return -1;
+ }
+ os_memcpy(cred->required_roaming_consortium, val, len);
+ cred->required_roaming_consortium_len = len;
+ os_free(val);
+ return 0;
+ }
+
+ if (os_strcmp(var, "excluded_ssid") == 0) {
+ struct excluded_ssid *e;
+
+ if (len > SSID_MAX_LEN) {
+ wpa_printf(MSG_ERROR, "Line %d: invalid "
+ "excluded_ssid length %d", line, (int) len);
+ os_free(val);
+ return -1;
+ }
+
+ e = os_realloc_array(cred->excluded_ssid,
+ cred->num_excluded_ssid + 1,
+ sizeof(struct excluded_ssid));
+ if (e == NULL) {
+ os_free(val);
+ return -1;
+ }
+ cred->excluded_ssid = e;
+
+ e = &cred->excluded_ssid[cred->num_excluded_ssid++];
+ os_memcpy(e->ssid, val, len);
+ e->ssid_len = len;
+
+ os_free(val);
+
+ return 0;
+ }
+
+ if (os_strcmp(var, "roaming_partner") == 0) {
+ struct roaming_partner *p;
+ char *pos;
+
+ p = os_realloc_array(cred->roaming_partner,
+ cred->num_roaming_partner + 1,
+ sizeof(struct roaming_partner));
+ if (p == NULL) {
+ os_free(val);
+ return -1;
+ }
+ cred->roaming_partner = p;
+
+ p = &cred->roaming_partner[cred->num_roaming_partner];
+
+ pos = os_strchr(val, ',');
+ if (pos == NULL) {
+ os_free(val);
+ return -1;
+ }
+ *pos++ = '\0';
+ if (pos - val - 1 >= (int) sizeof(p->fqdn)) {
+ os_free(val);
+ return -1;
+ }
+ os_memcpy(p->fqdn, val, pos - val);
+
+ p->exact_match = atoi(pos);
+
+ pos = os_strchr(pos, ',');
+ if (pos == NULL) {
+ os_free(val);
+ return -1;
+ }
+ *pos++ = '\0';
+
+ p->priority = atoi(pos);
+
+ pos = os_strchr(pos, ',');
+ if (pos == NULL) {
+ os_free(val);
+ return -1;
+ }
+ *pos++ = '\0';
+
+ if (os_strlen(pos) >= sizeof(p->country)) {
+ os_free(val);
+ return -1;
+ }
+ os_memcpy(p->country, pos, os_strlen(pos) + 1);
+
+ cred->num_roaming_partner++;
+ os_free(val);
+
+ return 0;
+ }
+
+ if (os_strcmp(var, "provisioning_sp") == 0) {
+ os_free(cred->provisioning_sp);
+ cred->provisioning_sp = val;
+ return 0;
+ }
+
+ if (line) {
+ wpa_printf(MSG_ERROR, "Line %d: unknown cred field '%s'.",
+ line, var);
+ }
+
+ os_free(val);
+
+ return -1;
+}
+
+
+static char * alloc_int_str(int val)
+{
+ const unsigned int bufsize = 20;
+ char *buf;
+ int res;
+
+ buf = os_malloc(bufsize);
+ if (buf == NULL)
+ return NULL;
+ res = os_snprintf(buf, bufsize, "%d", val);
+ if (os_snprintf_error(bufsize, res)) {
+ os_free(buf);
+ buf = NULL;
+ }
+ return buf;
+}
+
+
+static char * alloc_strdup(const char *str)
+{
+ if (str == NULL)
+ return NULL;
+ return os_strdup(str);
+}
+
+
+char * wpa_config_get_cred_no_key(struct wpa_cred *cred, const char *var)
+{
+ if (os_strcmp(var, "temporary") == 0)
+ return alloc_int_str(cred->temporary);
+
+ if (os_strcmp(var, "priority") == 0)
+ return alloc_int_str(cred->priority);
+
+ if (os_strcmp(var, "sp_priority") == 0)
+ return alloc_int_str(cred->sp_priority);
+
+ if (os_strcmp(var, "pcsc") == 0)
+ return alloc_int_str(cred->pcsc);
+
+ if (os_strcmp(var, "eap") == 0) {
+ if (!cred->eap_method)
+ return NULL;
+ return alloc_strdup(eap_get_name(cred->eap_method[0].vendor,
+ cred->eap_method[0].method));
+ }
+
+ if (os_strcmp(var, "update_identifier") == 0)
+ return alloc_int_str(cred->update_identifier);
+
+ if (os_strcmp(var, "min_dl_bandwidth_home") == 0)
+ return alloc_int_str(cred->min_dl_bandwidth_home);
+
+ if (os_strcmp(var, "min_ul_bandwidth_home") == 0)
+ return alloc_int_str(cred->min_ul_bandwidth_home);
+
+ if (os_strcmp(var, "min_dl_bandwidth_roaming") == 0)
+ return alloc_int_str(cred->min_dl_bandwidth_roaming);
+
+ if (os_strcmp(var, "min_ul_bandwidth_roaming") == 0)
+ return alloc_int_str(cred->min_ul_bandwidth_roaming);
+
+ if (os_strcmp(var, "max_bss_load") == 0)
+ return alloc_int_str(cred->max_bss_load);
+
+ if (os_strcmp(var, "req_conn_capab") == 0) {
+ unsigned int i;
+ char *buf, *end, *pos;
+ int ret;
+
+ if (!cred->num_req_conn_capab)
+ return NULL;
+
+ buf = os_malloc(4000);
+ if (buf == NULL)
+ return NULL;
+ pos = buf;
+ end = pos + 4000;
+ for (i = 0; i < cred->num_req_conn_capab; i++) {
+ int *ports;
+
+ ret = os_snprintf(pos, end - pos, "%s%u",
+ i > 0 ? "\n" : "",
+ cred->req_conn_capab_proto[i]);
+ if (os_snprintf_error(end - pos, ret))
+ return buf;
+ pos += ret;
+
+ ports = cred->req_conn_capab_port[i];
+ if (ports) {
+ int j;
+ for (j = 0; ports[j] != -1; j++) {
+ ret = os_snprintf(pos, end - pos,
+ "%s%d",
+ j > 0 ? "," : ":",
+ ports[j]);
+ if (os_snprintf_error(end - pos, ret))
+ return buf;
+ pos += ret;
+ }
+ }
+ }
+
+ return buf;
+ }
+
+ if (os_strcmp(var, "ocsp") == 0)
+ return alloc_int_str(cred->ocsp);
+
+ if (os_strcmp(var, "realm") == 0)
+ return alloc_strdup(cred->realm);
+
+ if (os_strcmp(var, "username") == 0)
+ return alloc_strdup(cred->username);
+
+ if (os_strcmp(var, "password") == 0) {
+ if (!cred->password)
+ return NULL;
+ return alloc_strdup("*");
+ }
+
+ if (os_strcmp(var, "ca_cert") == 0)
+ return alloc_strdup(cred->ca_cert);
+
+ if (os_strcmp(var, "client_cert") == 0)
+ return alloc_strdup(cred->client_cert);
+
+ if (os_strcmp(var, "private_key") == 0)
+ return alloc_strdup(cred->private_key);
+
+ if (os_strcmp(var, "private_key_passwd") == 0) {
+ if (!cred->private_key_passwd)
+ return NULL;
+ return alloc_strdup("*");
+ }
+
+ if (os_strcmp(var, "imsi") == 0)
+ return alloc_strdup(cred->imsi);
+
+ if (os_strcmp(var, "milenage") == 0) {
+ if (!(cred->milenage))
+ return NULL;
+ return alloc_strdup("*");
+ }
+
+ if (os_strcmp(var, "domain_suffix_match") == 0)
+ return alloc_strdup(cred->domain_suffix_match);
+
+ if (os_strcmp(var, "domain") == 0) {
+ unsigned int i;
+ char *buf, *end, *pos;
+ int ret;
+
+ if (!cred->num_domain)
+ return NULL;
+
+ buf = os_malloc(4000);
+ if (buf == NULL)
+ return NULL;
+ pos = buf;
+ end = pos + 4000;
+
+ for (i = 0; i < cred->num_domain; i++) {
+ ret = os_snprintf(pos, end - pos, "%s%s",
+ i > 0 ? "\n" : "", cred->domain[i]);
+ if (os_snprintf_error(end - pos, ret))
+ return buf;
+ pos += ret;
+ }
+
+ return buf;
+ }
+
+ if (os_strcmp(var, "phase1") == 0)
+ return alloc_strdup(cred->phase1);
+
+ if (os_strcmp(var, "phase2") == 0)
+ return alloc_strdup(cred->phase2);
+
+ if (os_strcmp(var, "roaming_consortium") == 0) {
+ size_t buflen;
+ char *buf;
+
+ if (!cred->roaming_consortium_len)
+ return NULL;
+ buflen = cred->roaming_consortium_len * 2 + 1;
+ buf = os_malloc(buflen);
+ if (buf == NULL)
+ return NULL;
+ wpa_snprintf_hex(buf, buflen, cred->roaming_consortium,
+ cred->roaming_consortium_len);
+ return buf;
+ }
+
+ if (os_strcmp(var, "required_roaming_consortium") == 0) {
+ size_t buflen;
+ char *buf;
+
+ if (!cred->required_roaming_consortium_len)
+ return NULL;
+ buflen = cred->required_roaming_consortium_len * 2 + 1;
+ buf = os_malloc(buflen);
+ if (buf == NULL)
+ return NULL;
+ wpa_snprintf_hex(buf, buflen, cred->required_roaming_consortium,
+ cred->required_roaming_consortium_len);
+ return buf;
+ }
+
+ if (os_strcmp(var, "excluded_ssid") == 0) {
+ unsigned int i;
+ char *buf, *end, *pos;
+
+ if (!cred->num_excluded_ssid)
+ return NULL;
+
+ buf = os_malloc(4000);
+ if (buf == NULL)
+ return NULL;
+ pos = buf;
+ end = pos + 4000;
+
+ for (i = 0; i < cred->num_excluded_ssid; i++) {
+ struct excluded_ssid *e;
+ int ret;
+
+ e = &cred->excluded_ssid[i];
+ ret = os_snprintf(pos, end - pos, "%s%s",
+ i > 0 ? "\n" : "",
+ wpa_ssid_txt(e->ssid, e->ssid_len));
+ if (os_snprintf_error(end - pos, ret))
+ return buf;
+ pos += ret;
+ }
+
+ return buf;
+ }
+
+ if (os_strcmp(var, "roaming_partner") == 0) {
+ unsigned int i;
+ char *buf, *end, *pos;
+
+ if (!cred->num_roaming_partner)
+ return NULL;
+
+ buf = os_malloc(4000);
+ if (buf == NULL)
+ return NULL;
+ pos = buf;
+ end = pos + 4000;
+
+ for (i = 0; i < cred->num_roaming_partner; i++) {
+ struct roaming_partner *p;
+ int ret;
+
+ p = &cred->roaming_partner[i];
+ ret = os_snprintf(pos, end - pos, "%s%s,%d,%u,%s",
+ i > 0 ? "\n" : "",
+ p->fqdn, p->exact_match, p->priority,
+ p->country);
+ if (os_snprintf_error(end - pos, ret))
+ return buf;
+ pos += ret;
+ }
+
+ return buf;
+ }
+
+ if (os_strcmp(var, "provisioning_sp") == 0)
+ return alloc_strdup(cred->provisioning_sp);
+
+ return NULL;
+}
+
+
+struct wpa_cred * wpa_config_get_cred(struct wpa_config *config, int id)
+{
+ struct wpa_cred *cred;
+
+ cred = config->cred;
+ while (cred) {
+ if (id == cred->id)
+ break;
+ cred = cred->next;
+ }
+
+ return cred;
+}
+
+
+struct wpa_cred * wpa_config_add_cred(struct wpa_config *config)
+{
+ int id;
+ struct wpa_cred *cred, *last = NULL;
+
+ id = -1;
+ cred = config->cred;
+ while (cred) {
+ if (cred->id > id)
+ id = cred->id;
+ last = cred;
+ cred = cred->next;
+ }
+ id++;
+
+ cred = os_zalloc(sizeof(*cred));
+ if (cred == NULL)
+ return NULL;
+ cred->id = id;
+ cred->sim_num = DEFAULT_USER_SELECTED_SIM;
+ if (last)
+ last->next = cred;
+ else
+ config->cred = cred;
+
+ return cred;
+}
+
+
+int wpa_config_remove_cred(struct wpa_config *config, int id)
+{
+ struct wpa_cred *cred, *prev = NULL;
+
+ cred = config->cred;
+ while (cred) {
+ if (id == cred->id)
+ break;
+ prev = cred;
+ cred = cred->next;
+ }
+
+ if (cred == NULL)
+ return -1;
+
+ if (prev)
+ prev->next = cred->next;
+ else
+ config->cred = cred->next;
+
+ wpa_config_free_cred(cred);
+ return 0;
+}
+
+
+#ifndef CONFIG_NO_CONFIG_BLOBS
+/**
+ * wpa_config_get_blob - Get a named configuration blob
+ * @config: Configuration data from wpa_config_read()
+ * @name: Name of the blob
+ * Returns: Pointer to blob data or %NULL if not found
+ */
+const struct wpa_config_blob * wpa_config_get_blob(struct wpa_config *config,
+ const char *name)
+{
+ struct wpa_config_blob *blob = config->blobs;
+
+ while (blob) {
+ if (os_strcmp(blob->name, name) == 0)
+ return blob;
+ blob = blob->next;
+ }
+ return NULL;
+}
+
+
+/**
+ * wpa_config_set_blob - Set or add a named configuration blob
+ * @config: Configuration data from wpa_config_read()
+ * @blob: New value for the blob
+ *
+ * Adds a new configuration blob or replaces the current value of an existing
+ * blob.
+ */
+void wpa_config_set_blob(struct wpa_config *config,
+ struct wpa_config_blob *blob)
+{
+ wpa_config_remove_blob(config, blob->name);
+ blob->next = config->blobs;
+ config->blobs = blob;
+}
+
+
+/**
+ * wpa_config_free_blob - Free blob data
+ * @blob: Pointer to blob to be freed
+ */
+void wpa_config_free_blob(struct wpa_config_blob *blob)
+{
+ if (blob) {
+ os_free(blob->name);
+ bin_clear_free(blob->data, blob->len);
+ os_free(blob);
+ }
+}
+
+
+/**
+ * wpa_config_remove_blob - Remove a named configuration blob
+ * @config: Configuration data from wpa_config_read()
+ * @name: Name of the blob to remove
+ * Returns: 0 if blob was removed or -1 if blob was not found
+ */
+int wpa_config_remove_blob(struct wpa_config *config, const char *name)
+{
+ struct wpa_config_blob *pos = config->blobs, *prev = NULL;
+
+ while (pos) {
+ if (os_strcmp(pos->name, name) == 0) {
+ if (prev)
+ prev->next = pos->next;
+ else
+ config->blobs = pos->next;
+ wpa_config_free_blob(pos);
+ return 0;
+ }
+ prev = pos;
+ pos = pos->next;
+ }
+
+ return -1;
+}
+#endif /* CONFIG_NO_CONFIG_BLOBS */
+
+
+/**
+ * wpa_config_alloc_empty - Allocate an empty configuration
+ * @ctrl_interface: Control interface parameters, e.g., path to UNIX domain
+ * socket
+ * @driver_param: Driver parameters
+ * Returns: Pointer to allocated configuration data or %NULL on failure
+ */
+struct wpa_config * wpa_config_alloc_empty(const char *ctrl_interface,
+ const char *driver_param)
+{
+ struct wpa_config *config;
+ const int aCWmin = 4, aCWmax = 10;
+ const struct hostapd_wmm_ac_params ac_bk =
+ { aCWmin, aCWmax, 7, 0, 0 }; /* background traffic */
+ const struct hostapd_wmm_ac_params ac_be =
+ { aCWmin, aCWmax, 3, 0, 0 }; /* best effort traffic */
+ const struct hostapd_wmm_ac_params ac_vi = /* video traffic */
+ { aCWmin - 1, aCWmin, 2, 3000 / 32, 0 };
+ const struct hostapd_wmm_ac_params ac_vo = /* voice traffic */
+ { aCWmin - 2, aCWmin - 1, 2, 1500 / 32, 0 };
+
+ config = os_zalloc(sizeof(*config));
+ if (config == NULL)
+ return NULL;
+ config->eapol_version = DEFAULT_EAPOL_VERSION;
+ config->ap_scan = DEFAULT_AP_SCAN;
+ config->user_mpm = DEFAULT_USER_MPM;
+ config->max_peer_links = DEFAULT_MAX_PEER_LINKS;
+ config->mesh_max_inactivity = DEFAULT_MESH_MAX_INACTIVITY;
+ config->dot11RSNASAERetransPeriod =
+ DEFAULT_DOT11_RSNA_SAE_RETRANS_PERIOD;
+ config->fast_reauth = DEFAULT_FAST_REAUTH;
+ config->p2p_go_intent = DEFAULT_P2P_GO_INTENT;
+ config->p2p_intra_bss = DEFAULT_P2P_INTRA_BSS;
+ config->p2p_go_freq_change_policy = DEFAULT_P2P_GO_FREQ_MOVE;
+ config->p2p_go_max_inactivity = DEFAULT_P2P_GO_MAX_INACTIVITY;
+ config->p2p_optimize_listen_chan = DEFAULT_P2P_OPTIMIZE_LISTEN_CHAN;
+ config->p2p_go_ctwindow = DEFAULT_P2P_GO_CTWINDOW;
+ config->bss_max_count = DEFAULT_BSS_MAX_COUNT;
+ config->bss_expiration_age = DEFAULT_BSS_EXPIRATION_AGE;
+ config->bss_expiration_scan_count = DEFAULT_BSS_EXPIRATION_SCAN_COUNT;
+ config->max_num_sta = DEFAULT_MAX_NUM_STA;
+ config->access_network_type = DEFAULT_ACCESS_NETWORK_TYPE;
+ config->scan_cur_freq = DEFAULT_SCAN_CUR_FREQ;
+ config->wmm_ac_params[0] = ac_be;
+ config->wmm_ac_params[1] = ac_bk;
+ config->wmm_ac_params[2] = ac_vi;
+ config->wmm_ac_params[3] = ac_vo;
+ config->p2p_search_delay = DEFAULT_P2P_SEARCH_DELAY;
+ config->rand_addr_lifetime = DEFAULT_RAND_ADDR_LIFETIME;
+ config->key_mgmt_offload = DEFAULT_KEY_MGMT_OFFLOAD;
+ config->cert_in_cb = DEFAULT_CERT_IN_CB;
+
+ if (ctrl_interface)
+ config->ctrl_interface = os_strdup(ctrl_interface);
+ if (driver_param)
+ config->driver_param = os_strdup(driver_param);
+
+ return config;
+}
+
+
+#ifndef CONFIG_NO_STDOUT_DEBUG
+/**
+ * wpa_config_debug_dump_networks - Debug dump of configured networks
+ * @config: Configuration data from wpa_config_read()
+ */
+void wpa_config_debug_dump_networks(struct wpa_config *config)
+{
+ int prio;
+ struct wpa_ssid *ssid;
+
+ for (prio = 0; prio < config->num_prio; prio++) {
+ ssid = config->pssid[prio];
+ wpa_printf(MSG_DEBUG, "Priority group %d",
+ ssid->priority);
+ while (ssid) {
+ wpa_printf(MSG_DEBUG, " id=%d ssid='%s'",
+ ssid->id,
+ wpa_ssid_txt(ssid->ssid, ssid->ssid_len));
+ ssid = ssid->pnext;
+ }
+ }
+}
+#endif /* CONFIG_NO_STDOUT_DEBUG */
+
+
+struct global_parse_data {
+ char *name;
+ int (*parser)(const struct global_parse_data *data,
+ struct wpa_config *config, int line, const char *value);
+ int (*get)(const char *name, struct wpa_config *config, long offset,
+ char *buf, size_t buflen, int pretty_print);
+ void *param1, *param2, *param3;
+ unsigned int changed_flag;
+};
+
+
+static int wpa_global_config_parse_int(const struct global_parse_data *data,
+ struct wpa_config *config, int line,
+ const char *pos)
+{
+ int val, *dst;
+ char *end;
+
+ dst = (int *) (((u8 *) config) + (long) data->param1);
+ val = strtol(pos, &end, 0);
+ if (*end) {
+ wpa_printf(MSG_ERROR, "Line %d: invalid number \"%s\"",
+ line, pos);
+ return -1;
+ }
+ *dst = val;
+
+ wpa_printf(MSG_DEBUG, "%s=%d", data->name, *dst);
+
+ if (data->param2 && *dst < (long) data->param2) {
+ wpa_printf(MSG_ERROR, "Line %d: too small %s (value=%d "
+ "min_value=%ld)", line, data->name, *dst,
+ (long) data->param2);
+ *dst = (long) data->param2;
+ return -1;
+ }
+
+ if (data->param3 && *dst > (long) data->param3) {
+ wpa_printf(MSG_ERROR, "Line %d: too large %s (value=%d "
+ "max_value=%ld)", line, data->name, *dst,
+ (long) data->param3);
+ *dst = (long) data->param3;
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static int wpa_global_config_parse_str(const struct global_parse_data *data,
+ struct wpa_config *config, int line,
+ const char *pos)
+{
+ size_t len;
+ char **dst, *tmp;
+
+ len = os_strlen(pos);
+ if (data->param2 && len < (size_t) data->param2) {
+ wpa_printf(MSG_ERROR, "Line %d: too short %s (len=%lu "
+ "min_len=%ld)", line, data->name,
+ (unsigned long) len, (long) data->param2);
+ return -1;
+ }
+
+ if (data->param3 && len > (size_t) data->param3) {
+ wpa_printf(MSG_ERROR, "Line %d: too long %s (len=%lu "
+ "max_len=%ld)", line, data->name,
+ (unsigned long) len, (long) data->param3);
+ return -1;
+ }
+
+ tmp = os_strdup(pos);
+ if (tmp == NULL)
+ return -1;
+
+ dst = (char **) (((u8 *) config) + (long) data->param1);
+ os_free(*dst);
+ *dst = tmp;
+ wpa_printf(MSG_DEBUG, "%s='%s'", data->name, *dst);
+
+ return 0;
+}
+
+
+static int wpa_config_process_bgscan(const struct global_parse_data *data,
+ struct wpa_config *config, int line,
+ const char *pos)
+{
+ size_t len;
+ char *tmp;
+ int res;
+
+ tmp = wpa_config_parse_string(pos, &len);
+ if (tmp == NULL) {
+ wpa_printf(MSG_ERROR, "Line %d: failed to parse %s",
+ line, data->name);
+ return -1;
+ }
+
+ res = wpa_global_config_parse_str(data, config, line, tmp);
+ os_free(tmp);
+ return res;
+}
+
+
+static int wpa_global_config_parse_bin(const struct global_parse_data *data,
+ struct wpa_config *config, int line,
+ const char *pos)
+{
+ size_t len;
+ struct wpabuf **dst, *tmp;
+
+ len = os_strlen(pos);
+ if (len & 0x01)
+ return -1;
+
+ tmp = wpabuf_alloc(len / 2);
+ if (tmp == NULL)
+ return -1;
+
+ if (hexstr2bin(pos, wpabuf_put(tmp, len / 2), len / 2)) {
+ wpabuf_free(tmp);
+ return -1;
+ }
+
+ dst = (struct wpabuf **) (((u8 *) config) + (long) data->param1);
+ wpabuf_free(*dst);
+ *dst = tmp;
+ wpa_printf(MSG_DEBUG, "%s", data->name);
+
+ return 0;
+}
+
+
+static int wpa_config_process_freq_list(const struct global_parse_data *data,
+ struct wpa_config *config, int line,
+ const char *value)
+{
+ int *freqs;
+
+ freqs = wpa_config_parse_int_array(value);
+ if (freqs == NULL)
+ return -1;
+ if (freqs[0] == 0) {
+ os_free(freqs);
+ freqs = NULL;
+ }
+ os_free(config->freq_list);
+ config->freq_list = freqs;
+ return 0;
+}
+
+
+#ifdef CONFIG_P2P
+static int wpa_global_config_parse_ipv4(const struct global_parse_data *data,
+ struct wpa_config *config, int line,
+ const char *pos)
+{
+ u32 *dst;
+ struct hostapd_ip_addr addr;
+
+ if (hostapd_parse_ip_addr(pos, &addr) < 0)
+ return -1;
+ if (addr.af != AF_INET)
+ return -1;
+
+ dst = (u32 *) (((u8 *) config) + (long) data->param1);
+ os_memcpy(dst, &addr.u.v4.s_addr, 4);
+ wpa_printf(MSG_DEBUG, "%s = 0x%x", data->name,
+ WPA_GET_BE32((u8 *) dst));
+
+ return 0;
+}
+#endif /* CONFIG_P2P */
+
+
+static int wpa_config_process_country(const struct global_parse_data *data,
+ struct wpa_config *config, int line,
+ const char *pos)
+{
+ if (!pos[0] || !pos[1]) {
+ wpa_printf(MSG_DEBUG, "Invalid country set");
+ return -1;
+ }
+ config->country[0] = pos[0];
+ config->country[1] = pos[1];
+ wpa_printf(MSG_DEBUG, "country='%c%c'",
+ config->country[0], config->country[1]);
+ return 0;
+}
+
+
+static int wpa_config_process_load_dynamic_eap(
+ const struct global_parse_data *data, struct wpa_config *config,
+ int line, const char *so)
+{
+ int ret;
+ wpa_printf(MSG_DEBUG, "load_dynamic_eap=%s", so);
+ ret = eap_peer_method_load(so);
+ if (ret == -2) {
+ wpa_printf(MSG_DEBUG, "This EAP type was already loaded - not "
+ "reloading.");
+ } else if (ret) {
+ wpa_printf(MSG_ERROR, "Line %d: Failed to load dynamic EAP "
+ "method '%s'.", line, so);
+ return -1;
+ }
+
+ return 0;
+}
+
+
+#ifdef CONFIG_WPS
+
+static int wpa_config_process_uuid(const struct global_parse_data *data,
+ struct wpa_config *config, int line,
+ const char *pos)
+{
+ char buf[40];
+ if (uuid_str2bin(pos, config->uuid)) {
+ wpa_printf(MSG_ERROR, "Line %d: invalid UUID", line);
+ return -1;
+ }
+ uuid_bin2str(config->uuid, buf, sizeof(buf));
+ wpa_printf(MSG_DEBUG, "uuid=%s", buf);
+ return 0;
+}
+
+
+static int wpa_config_process_device_type(
+ const struct global_parse_data *data,
+ struct wpa_config *config, int line, const char *pos)
+{
+ return wps_dev_type_str2bin(pos, config->device_type);
+}
+
+
+static int wpa_config_process_os_version(const struct global_parse_data *data,
+ struct wpa_config *config, int line,
+ const char *pos)
+{
+ if (hexstr2bin(pos, config->os_version, 4)) {
+ wpa_printf(MSG_ERROR, "Line %d: invalid os_version", line);
+ return -1;
+ }
+ wpa_printf(MSG_DEBUG, "os_version=%08x",
+ WPA_GET_BE32(config->os_version));
+ return 0;
+}
+
+
+static int wpa_config_process_wps_vendor_ext_m1(
+ const struct global_parse_data *data,
+ struct wpa_config *config, int line, const char *pos)
+{
+ struct wpabuf *tmp;
+ int len = os_strlen(pos) / 2;
+ u8 *p;
+
+ if (!len) {
+ wpa_printf(MSG_ERROR, "Line %d: "
+ "invalid wps_vendor_ext_m1", line);
+ return -1;
+ }
+
+ tmp = wpabuf_alloc(len);
+ if (tmp) {
+ p = wpabuf_put(tmp, len);
+
+ if (hexstr2bin(pos, p, len)) {
+ wpa_printf(MSG_ERROR, "Line %d: "
+ "invalid wps_vendor_ext_m1", line);
+ wpabuf_free(tmp);
+ return -1;
+ }
+
+ wpabuf_free(config->wps_vendor_ext_m1);
+ config->wps_vendor_ext_m1 = tmp;
+ } else {
+ wpa_printf(MSG_ERROR, "Can not allocate "
+ "memory for wps_vendor_ext_m1");
+ return -1;
+ }
+
+ return 0;
+}
+
+#endif /* CONFIG_WPS */
+
+#ifdef CONFIG_P2P
+static int wpa_config_process_sec_device_type(
+ const struct global_parse_data *data,
+ struct wpa_config *config, int line, const char *pos)
+{
+ int idx;
+
+ if (config->num_sec_device_types >= MAX_SEC_DEVICE_TYPES) {
+ wpa_printf(MSG_ERROR, "Line %d: too many sec_device_type "
+ "items", line);
+ return -1;
+ }
+
+ idx = config->num_sec_device_types;
+
+ if (wps_dev_type_str2bin(pos, config->sec_device_type[idx]))
+ return -1;
+
+ config->num_sec_device_types++;
+ return 0;
+}
+
+
+static int wpa_config_process_p2p_pref_chan(
+ const struct global_parse_data *data,
+ struct wpa_config *config, int line, const char *pos)
+{
+ struct p2p_channel *pref = NULL, *n;
+ unsigned int num = 0;
+ const char *pos2;
+ u8 op_class, chan;
+
+ /* format: class:chan,class:chan,... */
+
+ while (*pos) {
+ op_class = atoi(pos);
+ pos2 = os_strchr(pos, ':');
+ if (pos2 == NULL)
+ goto fail;
+ pos2++;
+ chan = atoi(pos2);
+
+ n = os_realloc_array(pref, num + 1,
+ sizeof(struct p2p_channel));
+ if (n == NULL)
+ goto fail;
+ pref = n;
+ pref[num].op_class = op_class;
+ pref[num].chan = chan;
+ num++;
+
+ pos = os_strchr(pos2, ',');
+ if (pos == NULL)
+ break;
+ pos++;
+ }
+
+ os_free(config->p2p_pref_chan);
+ config->p2p_pref_chan = pref;
+ config->num_p2p_pref_chan = num;
+ wpa_hexdump(MSG_DEBUG, "P2P: Preferred class/channel pairs",
+ (u8 *) config->p2p_pref_chan,
+ config->num_p2p_pref_chan * sizeof(struct p2p_channel));
+
+ return 0;
+
+fail:
+ os_free(pref);
+ wpa_printf(MSG_ERROR, "Line %d: Invalid p2p_pref_chan list", line);
+ return -1;
+}
+
+
+static int wpa_config_process_p2p_no_go_freq(
+ const struct global_parse_data *data,
+ struct wpa_config *config, int line, const char *pos)
+{
+ int ret;
+
+ ret = freq_range_list_parse(&config->p2p_no_go_freq, pos);
+ if (ret < 0) {
+ wpa_printf(MSG_ERROR, "Line %d: Invalid p2p_no_go_freq", line);
+ return -1;
+ }
+
+ wpa_printf(MSG_DEBUG, "P2P: p2p_no_go_freq with %u items",
+ config->p2p_no_go_freq.num);
+
+ return 0;
+}
+
+#endif /* CONFIG_P2P */
+
+
+static int wpa_config_process_hessid(
+ const struct global_parse_data *data,
+ struct wpa_config *config, int line, const char *pos)
+{
+ if (hwaddr_aton2(pos, config->hessid) < 0) {
+ wpa_printf(MSG_ERROR, "Line %d: Invalid hessid '%s'",
+ line, pos);
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static int wpa_config_process_sae_groups(
+ const struct global_parse_data *data,
+ struct wpa_config *config, int line, const char *pos)
+{
+ int *groups = wpa_config_parse_int_array(pos);
+ if (groups == NULL) {
+ wpa_printf(MSG_ERROR, "Line %d: Invalid sae_groups '%s'",
+ line, pos);
+ return -1;
+ }
+
+ os_free(config->sae_groups);
+ config->sae_groups = groups;
+
+ return 0;
+}
+
+
+static int wpa_config_process_ap_vendor_elements(
+ const struct global_parse_data *data,
+ struct wpa_config *config, int line, const char *pos)
+{
+ struct wpabuf *tmp;
+ int len = os_strlen(pos) / 2;
+ u8 *p;
+
+ if (!len) {
+ wpa_printf(MSG_ERROR, "Line %d: invalid ap_vendor_elements",
+ line);
+ return -1;
+ }
+
+ tmp = wpabuf_alloc(len);
+ if (tmp) {
+ p = wpabuf_put(tmp, len);
+
+ if (hexstr2bin(pos, p, len)) {
+ wpa_printf(MSG_ERROR, "Line %d: invalid "
+ "ap_vendor_elements", line);
+ wpabuf_free(tmp);
+ return -1;
+ }
+
+ wpabuf_free(config->ap_vendor_elements);
+ config->ap_vendor_elements = tmp;
+ } else {
+ wpa_printf(MSG_ERROR, "Cannot allocate memory for "
+ "ap_vendor_elements");
+ return -1;
+ }
+
+ return 0;
+}
+
+
+#ifdef CONFIG_CTRL_IFACE
+static int wpa_config_process_no_ctrl_interface(
+ const struct global_parse_data *data,
+ struct wpa_config *config, int line, const char *pos)
+{
+ wpa_printf(MSG_DEBUG, "no_ctrl_interface -> ctrl_interface=NULL");
+ os_free(config->ctrl_interface);
+ config->ctrl_interface = NULL;
+ return 0;
+}
+#endif /* CONFIG_CTRL_IFACE */
+
+
+static int wpa_config_get_int(const char *name, struct wpa_config *config,
+ long offset, char *buf, size_t buflen,
+ int pretty_print)
+{
+ int *val = (int *) (((u8 *) config) + (long) offset);
+
+ if (pretty_print)
+ return os_snprintf(buf, buflen, "%s=%d\n", name, *val);
+ return os_snprintf(buf, buflen, "%d", *val);
+}
+
+
+static int wpa_config_get_str(const char *name, struct wpa_config *config,
+ long offset, char *buf, size_t buflen,
+ int pretty_print)
+{
+ char **val = (char **) (((u8 *) config) + (long) offset);
+ int res;
+
+ if (pretty_print)
+ res = os_snprintf(buf, buflen, "%s=%s\n", name,
+ *val ? *val : "null");
+ else if (!*val)
+ return -1;
+ else
+ res = os_snprintf(buf, buflen, "%s", *val);
+ if (os_snprintf_error(buflen, res))
+ res = -1;
+
+ return res;
+}
+
+
+#ifdef CONFIG_P2P
+static int wpa_config_get_ipv4(const char *name, struct wpa_config *config,
+ long offset, char *buf, size_t buflen,
+ int pretty_print)
+{
+ void *val = ((u8 *) config) + (long) offset;
+ int res;
+ char addr[INET_ADDRSTRLEN];
+
+ if (!val || !inet_ntop(AF_INET, val, addr, sizeof(addr)))
+ return -1;
+
+ if (pretty_print)
+ res = os_snprintf(buf, buflen, "%s=%s\n", name, addr);
+ else
+ res = os_snprintf(buf, buflen, "%s", addr);
+
+ if (os_snprintf_error(buflen, res))
+ res = -1;
+
+ return res;
+}
+#endif /* CONFIG_P2P */
+
+
+#ifdef OFFSET
+#undef OFFSET
+#endif /* OFFSET */
+/* OFFSET: Get offset of a variable within the wpa_config structure */
+#define OFFSET(v) ((void *) &((struct wpa_config *) 0)->v)
+
+#define FUNC(f) #f, wpa_config_process_ ## f, NULL, OFFSET(f), NULL, NULL
+#define FUNC_NO_VAR(f) #f, wpa_config_process_ ## f, NULL, NULL, NULL, NULL
+#define _INT(f) #f, wpa_global_config_parse_int, wpa_config_get_int, OFFSET(f)
+#define INT(f) _INT(f), NULL, NULL
+#define INT_RANGE(f, min, max) _INT(f), (void *) min, (void *) max
+#define _STR(f) #f, wpa_global_config_parse_str, wpa_config_get_str, OFFSET(f)
+#define STR(f) _STR(f), NULL, NULL
+#define STR_RANGE(f, min, max) _STR(f), (void *) min, (void *) max
+#define BIN(f) #f, wpa_global_config_parse_bin, NULL, OFFSET(f), NULL, NULL
+#define IPV4(f) #f, wpa_global_config_parse_ipv4, wpa_config_get_ipv4, \
+ OFFSET(f), NULL, NULL
+
+static const struct global_parse_data global_fields[] = {
+#ifdef CONFIG_CTRL_IFACE
+ { STR(ctrl_interface), 0 },
+ { FUNC_NO_VAR(no_ctrl_interface), 0 },
+ { STR(ctrl_interface_group), 0 } /* deprecated */,
+#endif /* CONFIG_CTRL_IFACE */
+#ifdef CONFIG_MACSEC
+ { INT_RANGE(eapol_version, 1, 3), 0 },
+#else /* CONFIG_MACSEC */
+ { INT_RANGE(eapol_version, 1, 2), 0 },
+#endif /* CONFIG_MACSEC */
+ { INT(ap_scan), 0 },
+ { FUNC(bgscan), 0 },
+#ifdef CONFIG_MESH
+ { INT(user_mpm), 0 },
+ { INT_RANGE(max_peer_links, 0, 255), 0 },
+ { INT(mesh_max_inactivity), 0 },
+ { INT(dot11RSNASAERetransPeriod), 0 },
+#endif /* CONFIG_MESH */
+ { INT(disable_scan_offload), 0 },
+ { INT(fast_reauth), 0 },
+ { STR(opensc_engine_path), 0 },
+ { STR(pkcs11_engine_path), 0 },
+ { STR(pkcs11_module_path), 0 },
+ { STR(openssl_ciphers), 0 },
+ { STR(pcsc_reader), 0 },
+ { STR(pcsc_pin), 0 },
+ { INT(external_sim), 0 },
+ { STR(driver_param), 0 },
+ { INT(dot11RSNAConfigPMKLifetime), 0 },
+ { INT(dot11RSNAConfigPMKReauthThreshold), 0 },
+ { INT(dot11RSNAConfigSATimeout), 0 },
+#ifndef CONFIG_NO_CONFIG_WRITE
+ { INT(update_config), 0 },
+#endif /* CONFIG_NO_CONFIG_WRITE */
+ { FUNC_NO_VAR(load_dynamic_eap), 0 },
+#ifdef CONFIG_WPS
+ { FUNC(uuid), CFG_CHANGED_UUID },
+ { STR_RANGE(device_name, 0, WPS_DEV_NAME_MAX_LEN),
+ CFG_CHANGED_DEVICE_NAME },
+ { STR_RANGE(manufacturer, 0, 64), CFG_CHANGED_WPS_STRING },
+ { STR_RANGE(model_name, 0, 32), CFG_CHANGED_WPS_STRING },
+ { STR_RANGE(model_number, 0, 32), CFG_CHANGED_WPS_STRING },
+ { STR_RANGE(serial_number, 0, 32), CFG_CHANGED_WPS_STRING },
+ { FUNC(device_type), CFG_CHANGED_DEVICE_TYPE },
+ { FUNC(os_version), CFG_CHANGED_OS_VERSION },
+ { STR(config_methods), CFG_CHANGED_CONFIG_METHODS },
+ { INT_RANGE(wps_cred_processing, 0, 2), 0 },
+ { FUNC(wps_vendor_ext_m1), CFG_CHANGED_VENDOR_EXTENSION },
+#endif /* CONFIG_WPS */
+#ifdef CONFIG_P2P
+ { FUNC(sec_device_type), CFG_CHANGED_SEC_DEVICE_TYPE },
+ { INT(p2p_listen_reg_class), CFG_CHANGED_P2P_LISTEN_CHANNEL },
+ { INT(p2p_listen_channel), CFG_CHANGED_P2P_LISTEN_CHANNEL },
+ { INT(p2p_oper_reg_class), CFG_CHANGED_P2P_OPER_CHANNEL },
+ { INT(p2p_oper_channel), CFG_CHANGED_P2P_OPER_CHANNEL },
+ { INT_RANGE(p2p_go_intent, 0, 15), 0 },
+ { STR(p2p_ssid_postfix), CFG_CHANGED_P2P_SSID_POSTFIX },
+ { INT_RANGE(persistent_reconnect, 0, 1), 0 },
+ { INT_RANGE(p2p_intra_bss, 0, 1), CFG_CHANGED_P2P_INTRA_BSS },
+ { INT(p2p_group_idle), 0 },
+ { INT_RANGE(p2p_go_freq_change_policy, 0, P2P_GO_FREQ_MOVE_MAX), 0 },
+ { INT_RANGE(p2p_passphrase_len, 8, 63),
+ CFG_CHANGED_P2P_PASSPHRASE_LEN },
+ { FUNC(p2p_pref_chan), CFG_CHANGED_P2P_PREF_CHAN },
+ { FUNC(p2p_no_go_freq), CFG_CHANGED_P2P_PREF_CHAN },
+ { INT_RANGE(p2p_add_cli_chan, 0, 1), 0 },
+ { INT_RANGE(p2p_optimize_listen_chan, 0, 1), 0 },
+ { INT(p2p_go_ht40), 0 },
+ { INT(p2p_go_vht), 0 },
+ { INT(p2p_disabled), 0 },
+ { INT_RANGE(p2p_go_ctwindow, 0, 127), 0 },
+ { INT(p2p_no_group_iface), 0 },
+ { INT_RANGE(p2p_ignore_shared_freq, 0, 1), 0 },
+ { IPV4(ip_addr_go), 0 },
+ { IPV4(ip_addr_mask), 0 },
+ { IPV4(ip_addr_start), 0 },
+ { IPV4(ip_addr_end), 0 },
+ { INT_RANGE(p2p_cli_probe, 0, 1), 0 },
+#endif /* CONFIG_P2P */
+ { FUNC(country), CFG_CHANGED_COUNTRY },
+ { INT(bss_max_count), 0 },
+ { INT(bss_expiration_age), 0 },
+ { INT(bss_expiration_scan_count), 0 },
+ { INT_RANGE(filter_ssids, 0, 1), 0 },
+ { INT_RANGE(filter_rssi, -100, 0), 0 },
+ { INT(max_num_sta), 0 },
+ { INT_RANGE(disassoc_low_ack, 0, 1), 0 },
+#ifdef CONFIG_HS20
+ { INT_RANGE(hs20, 0, 1), 0 },
+#endif /* CONFIG_HS20 */
+ { INT_RANGE(interworking, 0, 1), 0 },
+ { FUNC(hessid), 0 },
+ { INT_RANGE(access_network_type, 0, 15), 0 },
+ { INT_RANGE(pbc_in_m1, 0, 1), 0 },
+ { STR(autoscan), 0 },
+ { INT_RANGE(wps_nfc_dev_pw_id, 0x10, 0xffff),
+ CFG_CHANGED_NFC_PASSWORD_TOKEN },
+ { BIN(wps_nfc_dh_pubkey), CFG_CHANGED_NFC_PASSWORD_TOKEN },
+ { BIN(wps_nfc_dh_privkey), CFG_CHANGED_NFC_PASSWORD_TOKEN },
+ { BIN(wps_nfc_dev_pw), CFG_CHANGED_NFC_PASSWORD_TOKEN },
+ { STR(ext_password_backend), CFG_CHANGED_EXT_PW_BACKEND },
+ { INT(p2p_go_max_inactivity), 0 },
+ { INT_RANGE(auto_interworking, 0, 1), 0 },
+ { INT(okc), 0 },
+ { INT(pmf), 0 },
+ { FUNC(sae_groups), 0 },
+ { INT(dtim_period), 0 },
+ { INT(beacon_int), 0 },
+ { FUNC(ap_vendor_elements), 0 },
+ { INT_RANGE(ignore_old_scan_res, 0, 1), 0 },
+ { FUNC(freq_list), 0 },
+ { INT(scan_cur_freq), 0 },
+ { INT(sched_scan_interval), 0 },
+ { INT(tdls_external_control), 0},
+ { STR(osu_dir), 0 },
+ { STR(wowlan_triggers), 0 },
+ { INT(p2p_search_delay), 0},
+ { INT(mac_addr), 0 },
+ { INT(rand_addr_lifetime), 0 },
+ { INT(preassoc_mac_addr), 0 },
+ { INT(key_mgmt_offload), 0},
+ { INT(passive_scan), 0 },
+ { INT(reassoc_same_bss_optim), 0 },
+ { INT(wps_priority), 0},
+#ifdef CONFIG_FST
+ { STR_RANGE(fst_group_id, 1, FST_MAX_GROUP_ID_LEN), 0 },
+ { INT_RANGE(fst_priority, 1, FST_MAX_PRIO_VALUE), 0 },
+ { INT_RANGE(fst_llt, 1, FST_MAX_LLT_MS), 0 },
+#endif /* CONFIG_FST */
+};
+
+#undef FUNC
+#undef _INT
+#undef INT
+#undef INT_RANGE
+#undef _STR
+#undef STR
+#undef STR_RANGE
+#undef BIN
+#undef IPV4
+#define NUM_GLOBAL_FIELDS ARRAY_SIZE(global_fields)
+
+
+int wpa_config_dump_values(struct wpa_config *config, char *buf, size_t buflen)
+{
+ int result = 0;
+ size_t i;
+
+ for (i = 0; i < NUM_GLOBAL_FIELDS; i++) {
+ const struct global_parse_data *field = &global_fields[i];
+ int tmp;
+
+ if (!field->get)
+ continue;
+
+ tmp = field->get(field->name, config, (long) field->param1,
+ buf, buflen, 1);
+ if (tmp < 0)
+ return -1;
+ buf += tmp;
+ buflen -= tmp;
+ result += tmp;
+ }
+ return result;
+}
+
+
+int wpa_config_get_value(const char *name, struct wpa_config *config,
+ char *buf, size_t buflen)
+{
+ size_t i;
+
+ for (i = 0; i < NUM_GLOBAL_FIELDS; i++) {
+ const struct global_parse_data *field = &global_fields[i];
+
+ if (os_strcmp(name, field->name) != 0)
+ continue;
+ if (!field->get)
+ break;
+ return field->get(name, config, (long) field->param1,
+ buf, buflen, 0);
+ }
+
+ return -1;
+}
+
+
+int wpa_config_process_global(struct wpa_config *config, char *pos, int line)
+{
+ size_t i;
+ int ret = 0;
+
+ for (i = 0; i < NUM_GLOBAL_FIELDS; i++) {
+ const struct global_parse_data *field = &global_fields[i];
+ size_t flen = os_strlen(field->name);
+ if (os_strncmp(pos, field->name, flen) != 0 ||
+ pos[flen] != '=')
+ continue;
+
+ if (field->parser(field, config, line, pos + flen + 1)) {
+ wpa_printf(MSG_ERROR, "Line %d: failed to "
+ "parse '%s'.", line, pos);
+ ret = -1;
+ }
+ if (field->changed_flag == CFG_CHANGED_NFC_PASSWORD_TOKEN)
+ config->wps_nfc_pw_from_config = 1;
+ config->changed_parameters |= field->changed_flag;
+ break;
+ }
+ if (i == NUM_GLOBAL_FIELDS) {
+#ifdef CONFIG_AP
+ if (os_strncmp(pos, "wmm_ac_", 7) == 0) {
+ char *tmp = os_strchr(pos, '=');
+ if (tmp == NULL) {
+ if (line < 0)
+ return -1;
+ wpa_printf(MSG_ERROR, "Line %d: invalid line "
+ "'%s'", line, pos);
+ return -1;
+ }
+ *tmp++ = '\0';
+ if (hostapd_config_wmm_ac(config->wmm_ac_params, pos,
+ tmp)) {
+ wpa_printf(MSG_ERROR, "Line %d: invalid WMM "
+ "AC item", line);
+ return -1;
+ }
+ }
+#endif /* CONFIG_AP */
+ if (line < 0)
+ return -1;
+ wpa_printf(MSG_ERROR, "Line %d: unknown global field '%s'.",
+ line, pos);
+ ret = -1;
+ }
+
+ return ret;
+}
diff --git a/freebsd/contrib/wpa/wpa_supplicant/config.h b/freebsd/contrib/wpa/wpa_supplicant/config.h
new file mode 100644
index 00000000..627f38b6
--- /dev/null
+++ b/freebsd/contrib/wpa/wpa_supplicant/config.h
@@ -0,0 +1,1342 @@
+/*
+ * WPA Supplicant / Configuration file structures
+ * Copyright (c) 2003-2012, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef CONFIG_H
+#define CONFIG_H
+
+#define DEFAULT_EAPOL_VERSION 1
+#ifdef CONFIG_NO_SCAN_PROCESSING
+#define DEFAULT_AP_SCAN 2
+#else /* CONFIG_NO_SCAN_PROCESSING */
+#define DEFAULT_AP_SCAN 1
+#endif /* CONFIG_NO_SCAN_PROCESSING */
+#define DEFAULT_USER_MPM 1
+#define DEFAULT_MAX_PEER_LINKS 99
+#define DEFAULT_MESH_MAX_INACTIVITY 300
+/*
+ * The default dot11RSNASAERetransPeriod is defined as 40 ms in the standard,
+ * but use 1000 ms in practice to avoid issues on low power CPUs.
+ */
+#define DEFAULT_DOT11_RSNA_SAE_RETRANS_PERIOD 1000
+#define DEFAULT_FAST_REAUTH 1
+#define DEFAULT_P2P_GO_INTENT 7
+#define DEFAULT_P2P_INTRA_BSS 1
+#define DEFAULT_P2P_GO_MAX_INACTIVITY (5 * 60)
+#define DEFAULT_P2P_OPTIMIZE_LISTEN_CHAN 0
+#define DEFAULT_BSS_MAX_COUNT 200
+#define DEFAULT_BSS_EXPIRATION_AGE 180
+#define DEFAULT_BSS_EXPIRATION_SCAN_COUNT 2
+#define DEFAULT_MAX_NUM_STA 128
+#define DEFAULT_ACCESS_NETWORK_TYPE 15
+#define DEFAULT_SCAN_CUR_FREQ 0
+#define DEFAULT_P2P_SEARCH_DELAY 500
+#define DEFAULT_RAND_ADDR_LIFETIME 60
+#define DEFAULT_KEY_MGMT_OFFLOAD 1
+#define DEFAULT_CERT_IN_CB 1
+#define DEFAULT_P2P_GO_CTWINDOW 0
+
+#include "config_ssid.h"
+#include "wps/wps.h"
+#include "common/ieee802_11_defs.h"
+#include "common/ieee802_11_common.h"
+
+
+struct wpa_cred {
+ /**
+ * next - Next credential in the list
+ *
+ * This pointer can be used to iterate over all credentials. The head
+ * of this list is stored in the cred field of struct wpa_config.
+ */
+ struct wpa_cred *next;
+
+ /**
+ * id - Unique id for the credential
+ *
+ * This identifier is used as a unique identifier for each credential
+ * block when using the control interface. Each credential is allocated
+ * an id when it is being created, either when reading the
+ * configuration file or when a new credential is added through the
+ * control interface.
+ */
+ int id;
+
+ /**
+ * temporary - Whether this credential is temporary and not to be saved
+ */
+ int temporary;
+
+ /**
+ * priority - Priority group
+ *
+ * By default, all networks and credentials get the same priority group
+ * (0). This field can be used to give higher priority for credentials
+ * (and similarly in struct wpa_ssid for network blocks) to change the
+ * Interworking automatic networking selection behavior. The matching
+ * network (based on either an enabled network block or a credential)
+ * with the highest priority value will be selected.
+ */
+ int priority;
+
+ /**
+ * pcsc - Use PC/SC and SIM/USIM card
+ */
+ int pcsc;
+
+ /**
+ * realm - Home Realm for Interworking
+ */
+ char *realm;
+
+ /**
+ * username - Username for Interworking network selection
+ */
+ char *username;
+
+ /**
+ * password - Password for Interworking network selection
+ */
+ char *password;
+
+ /**
+ * ext_password - Whether password is a name for external storage
+ */
+ int ext_password;
+
+ /**
+ * ca_cert - CA certificate for Interworking network selection
+ */
+ char *ca_cert;
+
+ /**
+ * client_cert - File path to client certificate file (PEM/DER)
+ *
+ * This field is used with Interworking networking selection for a case
+ * where client certificate/private key is used for authentication
+ * (EAP-TLS). Full path to the file should be used since working
+ * directory may change when wpa_supplicant is run in the background.
+ *
+ * Alternatively, a named configuration blob can be used by setting
+ * this to blob://blob_name.
+ */
+ char *client_cert;
+
+ /**
+ * private_key - File path to client private key file (PEM/DER/PFX)
+ *
+ * When PKCS#12/PFX file (.p12/.pfx) is used, client_cert should be
+ * commented out. Both the private key and certificate will be read
+ * from the PKCS#12 file in this case. Full path to the file should be
+ * used since working directory may change when wpa_supplicant is run
+ * in the background.
+ *
+ * Windows certificate store can be used by leaving client_cert out and
+ * configuring private_key in one of the following formats:
+ *
+ * cert://substring_to_match
+ *
+ * hash://certificate_thumbprint_in_hex
+ *
+ * For example: private_key="hash://63093aa9c47f56ae88334c7b65a4"
+ *
+ * Note that when running wpa_supplicant as an application, the user
+ * certificate store (My user account) is used, whereas computer store
+ * (Computer account) is used when running wpasvc as a service.
+ *
+ * Alternatively, a named configuration blob can be used by setting
+ * this to blob://blob_name.
+ */
+ char *private_key;
+
+ /**
+ * private_key_passwd - Password for private key file
+ */
+ char *private_key_passwd;
+
+ /**
+ * imsi - IMSI in <MCC> | <MNC> | '-' | <MSIN> format
+ */
+ char *imsi;
+
+ /**
+ * milenage - Milenage parameters for SIM/USIM simulator in
+ * <Ki>:<OPc>:<SQN> format
+ */
+ char *milenage;
+
+ /**
+ * domain_suffix_match - Constraint for server domain name
+ *
+ * If set, this FQDN is used as a suffix match requirement for the AAA
+ * server certificate in SubjectAltName dNSName element(s). If a
+ * matching dNSName is found, this constraint is met. If no dNSName
+ * values are present, this constraint is matched against SubjectName CN
+ * using same suffix match comparison. Suffix match here means that the
+ * host/domain name is compared one label at a time starting from the
+ * top-level domain and all the labels in @domain_suffix_match shall be
+ * included in the certificate. The certificate may include additional
+ * sub-level labels in addition to the required labels.
+ *
+ * For example, domain_suffix_match=example.com would match
+ * test.example.com but would not match test-example.com.
+ */
+ char *domain_suffix_match;
+
+ /**
+ * domain - Home service provider FQDN(s)
+ *
+ * This is used to compare against the Domain Name List to figure out
+ * whether the AP is operated by the Home SP. Multiple domain entries
+ * can be used to configure alternative FQDNs that will be considered
+ * home networks.
+ */
+ char **domain;
+
+ /**
+ * num_domain - Number of FQDNs in the domain array
+ */
+ size_t num_domain;
+
+ /**
+ * roaming_consortium - Roaming Consortium OI
+ *
+ * If roaming_consortium_len is non-zero, this field contains the
+ * Roaming Consortium OI that can be used to determine which access
+ * points support authentication with this credential. This is an
+ * alternative to the use of the realm parameter. When using Roaming
+ * Consortium to match the network, the EAP parameters need to be
+ * pre-configured with the credential since the NAI Realm information
+ * may not be available or fetched.
+ */
+ u8 roaming_consortium[15];
+
+ /**
+ * roaming_consortium_len - Length of roaming_consortium
+ */
+ size_t roaming_consortium_len;
+
+ u8 required_roaming_consortium[15];
+ size_t required_roaming_consortium_len;
+
+ /**
+ * eap_method - EAP method to use
+ *
+ * Pre-configured EAP method to use with this credential or %NULL to
+ * indicate no EAP method is selected, i.e., the method will be
+ * selected automatically based on ANQP information.
+ */
+ struct eap_method_type *eap_method;
+
+ /**
+ * phase1 - Phase 1 (outer authentication) parameters
+ *
+ * Pre-configured EAP parameters or %NULL.
+ */
+ char *phase1;
+
+ /**
+ * phase2 - Phase 2 (inner authentication) parameters
+ *
+ * Pre-configured EAP parameters or %NULL.
+ */
+ char *phase2;
+
+ struct excluded_ssid {
+ u8 ssid[SSID_MAX_LEN];
+ size_t ssid_len;
+ } *excluded_ssid;
+ size_t num_excluded_ssid;
+
+ struct roaming_partner {
+ char fqdn[128];
+ int exact_match;
+ u8 priority;
+ char country[3];
+ } *roaming_partner;
+ size_t num_roaming_partner;
+
+ int update_identifier;
+
+ /**
+ * provisioning_sp - FQDN of the SP that provisioned the credential
+ */
+ char *provisioning_sp;
+
+ /**
+ * sp_priority - Credential priority within a provisioning SP
+ *
+ * This is the priority of the credential among all credentials
+ * provisionined by the same SP (i.e., for entries that have identical
+ * provisioning_sp value). The range of this priority is 0-255 with 0
+ * being the highest and 255 the lower priority.
+ */
+ int sp_priority;
+
+ unsigned int min_dl_bandwidth_home;
+ unsigned int min_ul_bandwidth_home;
+ unsigned int min_dl_bandwidth_roaming;
+ unsigned int min_ul_bandwidth_roaming;
+
+ /**
+ * max_bss_load - Maximum BSS Load Channel Utilization (1..255)
+ * This value is used as the maximum channel utilization for network
+ * selection purposes for home networks. If the AP does not advertise
+ * BSS Load or if the limit would prevent any connection, this
+ * constraint will be ignored.
+ */
+ unsigned int max_bss_load;
+
+ unsigned int num_req_conn_capab;
+ u8 *req_conn_capab_proto;
+ int **req_conn_capab_port;
+
+ /**
+ * ocsp - Whether to use/require OCSP to check server certificate
+ *
+ * 0 = do not use OCSP stapling (TLS certificate status extension)
+ * 1 = try to use OCSP stapling, but not require response
+ * 2 = require valid OCSP stapling response
+ */
+ int ocsp;
+
+ /**
+ * sim_num - User selected SIM identifier
+ *
+ * This variable is used for identifying which SIM is used if the system
+ * has more than one.
+ */
+ int sim_num;
+};
+
+
+#define CFG_CHANGED_DEVICE_NAME BIT(0)
+#define CFG_CHANGED_CONFIG_METHODS BIT(1)
+#define CFG_CHANGED_DEVICE_TYPE BIT(2)
+#define CFG_CHANGED_OS_VERSION BIT(3)
+#define CFG_CHANGED_UUID BIT(4)
+#define CFG_CHANGED_COUNTRY BIT(5)
+#define CFG_CHANGED_SEC_DEVICE_TYPE BIT(6)
+#define CFG_CHANGED_P2P_SSID_POSTFIX BIT(7)
+#define CFG_CHANGED_WPS_STRING BIT(8)
+#define CFG_CHANGED_P2P_INTRA_BSS BIT(9)
+#define CFG_CHANGED_VENDOR_EXTENSION BIT(10)
+#define CFG_CHANGED_P2P_LISTEN_CHANNEL BIT(11)
+#define CFG_CHANGED_P2P_OPER_CHANNEL BIT(12)
+#define CFG_CHANGED_P2P_PREF_CHAN BIT(13)
+#define CFG_CHANGED_EXT_PW_BACKEND BIT(14)
+#define CFG_CHANGED_NFC_PASSWORD_TOKEN BIT(15)
+#define CFG_CHANGED_P2P_PASSPHRASE_LEN BIT(16)
+
+/**
+ * struct wpa_config - wpa_supplicant configuration data
+ *
+ * This data structure is presents the per-interface (radio) configuration
+ * data. In many cases, there is only one struct wpa_config instance, but if
+ * more than one network interface is being controlled, one instance is used
+ * for each.
+ */
+struct wpa_config {
+ /**
+ * ssid - Head of the global network list
+ *
+ * This is the head for the list of all the configured networks.
+ */
+ struct wpa_ssid *ssid;
+
+ /**
+ * pssid - Per-priority network lists (in priority order)
+ */
+ struct wpa_ssid **pssid;
+
+ /**
+ * num_prio - Number of different priorities used in the pssid lists
+ *
+ * This indicates how many per-priority network lists are included in
+ * pssid.
+ */
+ int num_prio;
+
+ /**
+ * cred - Head of the credential list
+ *
+ * This is the head for the list of all the configured credentials.
+ */
+ struct wpa_cred *cred;
+
+ /**
+ * eapol_version - IEEE 802.1X/EAPOL version number
+ *
+ * wpa_supplicant is implemented based on IEEE Std 802.1X-2004 which
+ * defines EAPOL version 2. However, there are many APs that do not
+ * handle the new version number correctly (they seem to drop the
+ * frames completely). In order to make wpa_supplicant interoperate
+ * with these APs, the version number is set to 1 by default. This
+ * configuration value can be used to set it to the new version (2).
+ */
+ int eapol_version;
+
+ /**
+ * ap_scan - AP scanning/selection
+ *
+ * By default, wpa_supplicant requests driver to perform AP
+ * scanning and then uses the scan results to select a
+ * suitable AP. Another alternative is to allow the driver to
+ * take care of AP scanning and selection and use
+ * wpa_supplicant just to process EAPOL frames based on IEEE
+ * 802.11 association information from the driver.
+ *
+ * 1: wpa_supplicant initiates scanning and AP selection (default).
+ *
+ * 0: Driver takes care of scanning, AP selection, and IEEE 802.11
+ * association parameters (e.g., WPA IE generation); this mode can
+ * also be used with non-WPA drivers when using IEEE 802.1X mode;
+ * do not try to associate with APs (i.e., external program needs
+ * to control association). This mode must also be used when using
+ * wired Ethernet drivers.
+ *
+ * 2: like 0, but associate with APs using security policy and SSID
+ * (but not BSSID); this can be used, e.g., with ndiswrapper and NDIS
+ * drivers to enable operation with hidden SSIDs and optimized roaming;
+ * in this mode, the network blocks in the configuration are tried
+ * one by one until the driver reports successful association; each
+ * network block should have explicit security policy (i.e., only one
+ * option in the lists) for key_mgmt, pairwise, group, proto variables.
+ *
+ * Note: ap_scan=2 should not be used with the nl80211 driver interface
+ * (the current Linux interface). ap_scan=1 is optimized work working
+ * with nl80211. For finding networks using hidden SSID, scan_ssid=1 in
+ * the network block can be used with nl80211.
+ */
+ int ap_scan;
+
+ /**
+ * bgscan - Background scan and roaming parameters or %NULL if none
+ *
+ * This is an optional set of parameters for background scanning and
+ * roaming within a network (ESS). For more detailed information see
+ * ssid block documentation.
+ *
+ * The variable defines default bgscan behavior for all BSS station
+ * networks except for those which have their own bgscan configuration.
+ */
+ char *bgscan;
+
+ /**
+ * disable_scan_offload - Disable automatic offloading of scan requests
+ *
+ * By default, %wpa_supplicant tries to offload scanning if the driver
+ * indicates support for this (sched_scan). This configuration
+ * parameter can be used to disable this offloading mechanism.
+ */
+ int disable_scan_offload;
+
+ /**
+ * ctrl_interface - Parameters for the control interface
+ *
+ * If this is specified, %wpa_supplicant will open a control interface
+ * that is available for external programs to manage %wpa_supplicant.
+ * The meaning of this string depends on which control interface
+ * mechanism is used. For all cases, the existence of this parameter
+ * in configuration is used to determine whether the control interface
+ * is enabled.
+ *
+ * For UNIX domain sockets (default on Linux and BSD): This is a
+ * directory that will be created for UNIX domain sockets for listening
+ * to requests from external programs (CLI/GUI, etc.) for status
+ * information and configuration. The socket file will be named based
+ * on the interface name, so multiple %wpa_supplicant processes can be
+ * run at the same time if more than one interface is used.
+ * /var/run/wpa_supplicant is the recommended directory for sockets and
+ * by default, wpa_cli will use it when trying to connect with
+ * %wpa_supplicant.
+ *
+ * Access control for the control interface can be configured
+ * by setting the directory to allow only members of a group
+ * to use sockets. This way, it is possible to run
+ * %wpa_supplicant as root (since it needs to change network
+ * configuration and open raw sockets) and still allow GUI/CLI
+ * components to be run as non-root users. However, since the
+ * control interface can be used to change the network
+ * configuration, this access needs to be protected in many
+ * cases. By default, %wpa_supplicant is configured to use gid
+ * 0 (root). If you want to allow non-root users to use the
+ * control interface, add a new group and change this value to
+ * match with that group. Add users that should have control
+ * interface access to this group.
+ *
+ * When configuring both the directory and group, use following format:
+ * DIR=/var/run/wpa_supplicant GROUP=wheel
+ * DIR=/var/run/wpa_supplicant GROUP=0
+ * (group can be either group name or gid)
+ *
+ * For UDP connections (default on Windows): The value will be ignored.
+ * This variable is just used to select that the control interface is
+ * to be created. The value can be set to, e.g., udp
+ * (ctrl_interface=udp).
+ *
+ * For Windows Named Pipe: This value can be used to set the security
+ * descriptor for controlling access to the control interface. Security
+ * descriptor can be set using Security Descriptor String Format (see
+ * http://msdn.microsoft.com/library/default.asp?url=/library/en-us/secauthz/security/security_descriptor_string_format.asp).
+ * The descriptor string needs to be prefixed with SDDL=. For example,
+ * ctrl_interface=SDDL=D: would set an empty DACL (which will reject
+ * all connections).
+ */
+ char *ctrl_interface;
+
+ /**
+ * ctrl_interface_group - Control interface group (DEPRECATED)
+ *
+ * This variable is only used for backwards compatibility. Group for
+ * UNIX domain sockets should now be specified using GROUP=group in
+ * ctrl_interface variable.
+ */
+ char *ctrl_interface_group;
+
+ /**
+ * fast_reauth - EAP fast re-authentication (session resumption)
+ *
+ * By default, fast re-authentication is enabled for all EAP methods
+ * that support it. This variable can be used to disable fast
+ * re-authentication (by setting fast_reauth=0). Normally, there is no
+ * need to disable fast re-authentication.
+ */
+ int fast_reauth;
+
+ /**
+ * opensc_engine_path - Path to the OpenSSL engine for opensc
+ *
+ * This is an OpenSSL specific configuration option for loading OpenSC
+ * engine (engine_opensc.so); if %NULL, this engine is not loaded.
+ */
+ char *opensc_engine_path;
+
+ /**
+ * pkcs11_engine_path - Path to the OpenSSL engine for PKCS#11
+ *
+ * This is an OpenSSL specific configuration option for loading PKCS#11
+ * engine (engine_pkcs11.so); if %NULL, this engine is not loaded.
+ */
+ char *pkcs11_engine_path;
+
+ /**
+ * pkcs11_module_path - Path to the OpenSSL OpenSC/PKCS#11 module
+ *
+ * This is an OpenSSL specific configuration option for configuring
+ * path to OpenSC/PKCS#11 engine (opensc-pkcs11.so); if %NULL, this
+ * module is not loaded.
+ */
+ char *pkcs11_module_path;
+
+ /**
+ * openssl_ciphers - OpenSSL cipher string
+ *
+ * This is an OpenSSL specific configuration option for configuring the
+ * default ciphers. If not set, "DEFAULT:!EXP:!LOW" is used as the
+ * default.
+ */
+ char *openssl_ciphers;
+
+ /**
+ * pcsc_reader - PC/SC reader name prefix
+ *
+ * If not %NULL, PC/SC reader with a name that matches this prefix is
+ * initialized for SIM/USIM access. Empty string can be used to match
+ * the first available reader.
+ */
+ char *pcsc_reader;
+
+ /**
+ * pcsc_pin - PIN for USIM, GSM SIM, and smartcards
+ *
+ * This field is used to configure PIN for SIM/USIM for EAP-SIM and
+ * EAP-AKA. If left out, this will be asked through control interface.
+ */
+ char *pcsc_pin;
+
+ /**
+ * external_sim - Use external processing for SIM/USIM operations
+ */
+ int external_sim;
+
+ /**
+ * driver_param - Driver interface parameters
+ *
+ * This text string is passed to the selected driver interface with the
+ * optional struct wpa_driver_ops::set_param() handler. This can be
+ * used to configure driver specific options without having to add new
+ * driver interface functionality.
+ */
+ char *driver_param;
+
+ /**
+ * dot11RSNAConfigPMKLifetime - Maximum lifetime of a PMK
+ *
+ * dot11 MIB variable for the maximum lifetime of a PMK in the PMK
+ * cache (unit: seconds).
+ */
+ unsigned int dot11RSNAConfigPMKLifetime;
+
+ /**
+ * dot11RSNAConfigPMKReauthThreshold - PMK re-authentication threshold
+ *
+ * dot11 MIB variable for the percentage of the PMK lifetime
+ * that should expire before an IEEE 802.1X reauthentication occurs.
+ */
+ unsigned int dot11RSNAConfigPMKReauthThreshold;
+
+ /**
+ * dot11RSNAConfigSATimeout - Security association timeout
+ *
+ * dot11 MIB variable for the maximum time a security association
+ * shall take to set up (unit: seconds).
+ */
+ unsigned int dot11RSNAConfigSATimeout;
+
+ /**
+ * update_config - Is wpa_supplicant allowed to update configuration
+ *
+ * This variable control whether wpa_supplicant is allow to re-write
+ * its configuration with wpa_config_write(). If this is zero,
+ * configuration data is only changed in memory and the external data
+ * is not overriden. If this is non-zero, wpa_supplicant will update
+ * the configuration data (e.g., a file) whenever configuration is
+ * changed. This update may replace the old configuration which can
+ * remove comments from it in case of a text file configuration.
+ */
+ int update_config;
+
+ /**
+ * blobs - Configuration blobs
+ */
+ struct wpa_config_blob *blobs;
+
+ /**
+ * uuid - Universally Unique IDentifier (UUID; see RFC 4122) for WPS
+ */
+ u8 uuid[16];
+
+ /**
+ * device_name - Device Name (WPS)
+ * User-friendly description of device; up to 32 octets encoded in
+ * UTF-8
+ */
+ char *device_name;
+
+ /**
+ * manufacturer - Manufacturer (WPS)
+ * The manufacturer of the device (up to 64 ASCII characters)
+ */
+ char *manufacturer;
+
+ /**
+ * model_name - Model Name (WPS)
+ * Model of the device (up to 32 ASCII characters)
+ */
+ char *model_name;
+
+ /**
+ * model_number - Model Number (WPS)
+ * Additional device description (up to 32 ASCII characters)
+ */
+ char *model_number;
+
+ /**
+ * serial_number - Serial Number (WPS)
+ * Serial number of the device (up to 32 characters)
+ */
+ char *serial_number;
+
+ /**
+ * device_type - Primary Device Type (WPS)
+ */
+ u8 device_type[WPS_DEV_TYPE_LEN];
+
+ /**
+ * config_methods - Config Methods
+ *
+ * This is a space-separated list of supported WPS configuration
+ * methods. For example, "label virtual_display virtual_push_button
+ * keypad".
+ * Available methods: usba ethernet label display ext_nfc_token
+ * int_nfc_token nfc_interface push_button keypad
+ * virtual_display physical_display
+ * virtual_push_button physical_push_button.
+ */
+ char *config_methods;
+
+ /**
+ * os_version - OS Version (WPS)
+ * 4-octet operating system version number
+ */
+ u8 os_version[4];
+
+ /**
+ * country - Country code
+ *
+ * This is the ISO/IEC alpha2 country code for which we are operating
+ * in
+ */
+ char country[2];
+
+ /**
+ * wps_cred_processing - Credential processing
+ *
+ * 0 = process received credentials internally
+ * 1 = do not process received credentials; just pass them over
+ * ctrl_iface to external program(s)
+ * 2 = process received credentials internally and pass them over
+ * ctrl_iface to external program(s)
+ */
+ int wps_cred_processing;
+
+#define MAX_SEC_DEVICE_TYPES 5
+ /**
+ * sec_device_types - Secondary Device Types (P2P)
+ */
+ u8 sec_device_type[MAX_SEC_DEVICE_TYPES][WPS_DEV_TYPE_LEN];
+ int num_sec_device_types;
+
+ int p2p_listen_reg_class;
+ int p2p_listen_channel;
+ int p2p_oper_reg_class;
+ int p2p_oper_channel;
+ int p2p_go_intent;
+ char *p2p_ssid_postfix;
+ int persistent_reconnect;
+ int p2p_intra_bss;
+ unsigned int num_p2p_pref_chan;
+ struct p2p_channel *p2p_pref_chan;
+ struct wpa_freq_range_list p2p_no_go_freq;
+ int p2p_add_cli_chan;
+ int p2p_ignore_shared_freq;
+ int p2p_optimize_listen_chan;
+
+ struct wpabuf *wps_vendor_ext_m1;
+
+#define MAX_WPS_VENDOR_EXT 10
+ /**
+ * wps_vendor_ext - Vendor extension attributes in WPS
+ */
+ struct wpabuf *wps_vendor_ext[MAX_WPS_VENDOR_EXT];
+
+ /**
+ * p2p_group_idle - Maximum idle time in seconds for P2P group
+ *
+ * This value controls how long a P2P group is maintained after there
+ * is no other members in the group. As a GO, this means no associated
+ * stations in the group. As a P2P client, this means no GO seen in
+ * scan results. The maximum idle time is specified in seconds with 0
+ * indicating no time limit, i.e., the P2P group remains in active
+ * state indefinitely until explicitly removed. As a P2P client, the
+ * maximum idle time of P2P_MAX_CLIENT_IDLE seconds is enforced, i.e.,
+ * this parameter is mainly meant for GO use and for P2P client, it can
+ * only be used to reduce the default timeout to smaller value. A
+ * special value -1 can be used to configure immediate removal of the
+ * group for P2P client role on any disconnection after the data
+ * connection has been established.
+ */
+ int p2p_group_idle;
+
+ /**
+ * p2p_go_freq_change_policy - The GO frequency change policy
+ *
+ * This controls the behavior of the GO when there is a change in the
+ * map of the currently used frequencies in case more than one channel
+ * is supported.
+ *
+ * @P2P_GO_FREQ_MOVE_SCM: Prefer working in a single channel mode if
+ * possible. In case the GO is the only interface using its frequency
+ * and there are other station interfaces on other frequencies, the GO
+ * will migrate to one of these frequencies.
+ *
+ * @P2P_GO_FREQ_MOVE_SCM_PEER_SUPPORTS: Same as P2P_GO_FREQ_MOVE_SCM,
+ * but a transition is possible only in case one of the other used
+ * frequencies is one of the frequencies in the intersection of the
+ * frequency list of the local device and the peer device.
+ *
+ * @P2P_GO_FREQ_MOVE_STAY: Prefer to stay on the current frequency.
+ */
+ enum {
+ P2P_GO_FREQ_MOVE_SCM = 0,
+ P2P_GO_FREQ_MOVE_SCM_PEER_SUPPORTS = 1,
+ P2P_GO_FREQ_MOVE_STAY = 2,
+ P2P_GO_FREQ_MOVE_MAX = P2P_GO_FREQ_MOVE_STAY,
+ } p2p_go_freq_change_policy;
+
+#define DEFAULT_P2P_GO_FREQ_MOVE P2P_GO_FREQ_MOVE_STAY
+
+ /**
+ * p2p_passphrase_len - Passphrase length (8..63) for P2P GO
+ *
+ * This parameter controls the length of the random passphrase that is
+ * generated at the GO.
+ */
+ unsigned int p2p_passphrase_len;
+
+ /**
+ * bss_max_count - Maximum number of BSS entries to keep in memory
+ */
+ unsigned int bss_max_count;
+
+ /**
+ * bss_expiration_age - BSS entry age after which it can be expired
+ *
+ * This value controls the time in seconds after which a BSS entry
+ * gets removed if it has not been updated or is not in use.
+ */
+ unsigned int bss_expiration_age;
+
+ /**
+ * bss_expiration_scan_count - Expire BSS after number of scans
+ *
+ * If the BSS entry has not been seen in this many scans, it will be
+ * removed. A value of 1 means that entry is removed after the first
+ * scan in which the BSSID is not seen. Larger values can be used
+ * to avoid BSS entries disappearing if they are not visible in
+ * every scan (e.g., low signal quality or interference).
+ */
+ unsigned int bss_expiration_scan_count;
+
+ /**
+ * filter_ssids - SSID-based scan result filtering
+ *
+ * 0 = do not filter scan results
+ * 1 = only include configured SSIDs in scan results/BSS table
+ */
+ int filter_ssids;
+
+ /**
+ * filter_rssi - RSSI-based scan result filtering
+ *
+ * 0 = do not filter scan results
+ * -n = filter scan results below -n dBm
+ */
+ int filter_rssi;
+
+ /**
+ * max_num_sta - Maximum number of STAs in an AP/P2P GO
+ */
+ unsigned int max_num_sta;
+
+ /**
+ * freq_list - Array of allowed scan frequencies or %NULL for all
+ *
+ * This is an optional zero-terminated array of frequencies in
+ * megahertz (MHz) to allow for narrowing scanning range.
+ */
+ int *freq_list;
+
+ /**
+ * scan_cur_freq - Whether to scan only the current channel
+ *
+ * If true, attempt to scan only the current channel if any other
+ * VIFs on this radio are already associated on a particular channel.
+ */
+ int scan_cur_freq;
+
+ /**
+ * changed_parameters - Bitmap of changed parameters since last update
+ */
+ unsigned int changed_parameters;
+
+ /**
+ * disassoc_low_ack - Disassocicate stations with massive packet loss
+ */
+ int disassoc_low_ack;
+
+ /**
+ * interworking - Whether Interworking (IEEE 802.11u) is enabled
+ */
+ int interworking;
+
+ /**
+ * access_network_type - Access Network Type
+ *
+ * When Interworking is enabled, scans will be limited to APs that
+ * advertise the specified Access Network Type (0..15; with 15
+ * indicating wildcard match).
+ */
+ int access_network_type;
+
+ /**
+ * hessid - Homogenous ESS identifier
+ *
+ * If this is set (any octet is non-zero), scans will be used to
+ * request response only from BSSes belonging to the specified
+ * Homogeneous ESS. This is used only if interworking is enabled.
+ */
+ u8 hessid[ETH_ALEN];
+
+ /**
+ * hs20 - Hotspot 2.0
+ */
+ int hs20;
+
+ /**
+ * pbc_in_m1 - AP mode WPS probing workaround for PBC with Windows 7
+ *
+ * Windows 7 uses incorrect way of figuring out AP's WPS capabilities
+ * by acting as a Registrar and using M1 from the AP. The config
+ * methods attribute in that message is supposed to indicate only the
+ * configuration method supported by the AP in Enrollee role, i.e., to
+ * add an external Registrar. For that case, PBC shall not be used and
+ * as such, the PushButton config method is removed from M1 by default.
+ * If pbc_in_m1=1 is included in the configuration file, the PushButton
+ * config method is left in M1 (if included in config_methods
+ * parameter) to allow Windows 7 to use PBC instead of PIN (e.g., from
+ * a label in the AP).
+ */
+ int pbc_in_m1;
+
+ /**
+ * autoscan - Automatic scan parameters or %NULL if none
+ *
+ * This is an optional set of parameters for automatic scanning
+ * within an interface in following format:
+ * <autoscan module name>:<module parameters>
+ */
+ char *autoscan;
+
+ /**
+ * wps_nfc_pw_from_config - NFC Device Password was read from config
+ *
+ * This parameter can be determined whether the NFC Device Password was
+ * included in the configuration (1) or generated dynamically (0). Only
+ * the former case is re-written back to the configuration file.
+ */
+ int wps_nfc_pw_from_config;
+
+ /**
+ * wps_nfc_dev_pw_id - NFC Device Password ID for password token
+ */
+ int wps_nfc_dev_pw_id;
+
+ /**
+ * wps_nfc_dh_pubkey - NFC DH Public Key for password token
+ */
+ struct wpabuf *wps_nfc_dh_pubkey;
+
+ /**
+ * wps_nfc_dh_privkey - NFC DH Private Key for password token
+ */
+ struct wpabuf *wps_nfc_dh_privkey;
+
+ /**
+ * wps_nfc_dev_pw - NFC Device Password for password token
+ */
+ struct wpabuf *wps_nfc_dev_pw;
+
+ /**
+ * ext_password_backend - External password backend or %NULL if none
+ *
+ * format: <backend name>[:<optional backend parameters>]
+ */
+ char *ext_password_backend;
+
+ /*
+ * p2p_go_max_inactivity - Timeout in seconds to detect STA inactivity
+ *
+ * This timeout value is used in P2P GO mode to clean up
+ * inactive stations.
+ * By default: 300 seconds.
+ */
+ int p2p_go_max_inactivity;
+
+ struct hostapd_wmm_ac_params wmm_ac_params[4];
+
+ /**
+ * auto_interworking - Whether to use network selection automatically
+ *
+ * 0 = do not automatically go through Interworking network selection
+ * (i.e., require explicit interworking_select command for this)
+ * 1 = perform Interworking network selection if one or more
+ * credentials have been configured and scan did not find a
+ * matching network block
+ */
+ int auto_interworking;
+
+ /**
+ * p2p_go_ht40 - Default mode for HT40 enable when operating as GO.
+ *
+ * This will take effect for p2p_group_add, p2p_connect, and p2p_invite.
+ * Note that regulatory constraints and driver capabilities are
+ * consulted anyway, so setting it to 1 can't do real harm.
+ * By default: 0 (disabled)
+ */
+ int p2p_go_ht40;
+
+ /**
+ * p2p_go_vht - Default mode for VHT enable when operating as GO
+ *
+ * This will take effect for p2p_group_add, p2p_connect, and p2p_invite.
+ * Note that regulatory constraints and driver capabilities are
+ * consulted anyway, so setting it to 1 can't do real harm.
+ * By default: 0 (disabled)
+ */
+ int p2p_go_vht;
+
+ /**
+ * p2p_go_ctwindow - CTWindow to use when operating as GO
+ *
+ * By default: 0 (no CTWindow). Values 0-127 can be used to indicate
+ * the length of the CTWindow in TUs.
+ */
+ int p2p_go_ctwindow;
+
+ /**
+ * p2p_disabled - Whether P2P operations are disabled for this interface
+ */
+ int p2p_disabled;
+
+ /**
+ * p2p_no_group_iface - Whether group interfaces can be used
+ *
+ * By default, wpa_supplicant will create a separate interface for P2P
+ * group operations if the driver supports this. This functionality can
+ * be disabled by setting this parameter to 1. In that case, the same
+ * interface that was used for the P2P management operations is used
+ * also for the group operation.
+ */
+ int p2p_no_group_iface;
+
+ /**
+ * p2p_cli_probe - Enable/disable P2P CLI probe request handling
+ *
+ * If this parameter is set to 1, a connected P2P Client will receive
+ * and handle Probe Request frames. Setting this parameter to 0
+ * disables this option. Default value: 0.
+ *
+ * Note: Setting this property at run time takes effect on the following
+ * interface state transition to/from the WPA_COMPLETED state.
+ */
+ int p2p_cli_probe;
+
+ /**
+ * okc - Whether to enable opportunistic key caching by default
+ *
+ * By default, OKC is disabled unless enabled by the per-network
+ * proactive_key_caching=1 parameter. okc=1 can be used to change this
+ * default behavior.
+ */
+ int okc;
+
+ /**
+ * pmf - Whether to enable/require PMF by default
+ *
+ * By default, PMF is disabled unless enabled by the per-network
+ * ieee80211w=1 or ieee80211w=2 parameter. pmf=1/2 can be used to change
+ * this default behavior.
+ */
+ enum mfp_options pmf;
+
+ /**
+ * sae_groups - Preference list of enabled groups for SAE
+ *
+ * By default (if this parameter is not set), the mandatory group 19
+ * (ECC group defined over a 256-bit prime order field) is preferred,
+ * but other groups are also enabled. If this parameter is set, the
+ * groups will be tried in the indicated order.
+ */
+ int *sae_groups;
+
+ /**
+ * dtim_period - Default DTIM period in Beacon intervals
+ *
+ * This parameter can be used to set the default value for network
+ * blocks that do not specify dtim_period.
+ */
+ int dtim_period;
+
+ /**
+ * beacon_int - Default Beacon interval in TU
+ *
+ * This parameter can be used to set the default value for network
+ * blocks that do not specify beacon_int.
+ */
+ int beacon_int;
+
+ /**
+ * ap_vendor_elements: Vendor specific elements for Beacon/ProbeResp
+ *
+ * This parameter can be used to define additional vendor specific
+ * elements for Beacon and Probe Response frames in AP/P2P GO mode. The
+ * format for these element(s) is a hexdump of the raw information
+ * elements (id+len+payload for one or more elements).
+ */
+ struct wpabuf *ap_vendor_elements;
+
+ /**
+ * ignore_old_scan_res - Ignore scan results older than request
+ *
+ * The driver may have a cache of scan results that makes it return
+ * information that is older than our scan trigger. This parameter can
+ * be used to configure such old information to be ignored instead of
+ * allowing it to update the internal BSS table.
+ */
+ int ignore_old_scan_res;
+
+ /**
+ * sched_scan_interval - schedule scan interval
+ */
+ unsigned int sched_scan_interval;
+
+ /**
+ * tdls_external_control - External control for TDLS setup requests
+ *
+ * Enable TDLS mode where external programs are given the control
+ * to specify the TDLS link to get established to the driver. The
+ * driver requests the TDLS setup to the supplicant only for the
+ * specified TDLS peers.
+ */
+ int tdls_external_control;
+
+ u8 ip_addr_go[4];
+ u8 ip_addr_mask[4];
+ u8 ip_addr_start[4];
+ u8 ip_addr_end[4];
+
+ /**
+ * osu_dir - OSU provider information directory
+ *
+ * If set, allow FETCH_OSU control interface command to be used to fetch
+ * OSU provider information into all APs and store the results in this
+ * directory.
+ */
+ char *osu_dir;
+
+ /**
+ * wowlan_triggers - Wake-on-WLAN triggers
+ *
+ * If set, these wowlan triggers will be configured.
+ */
+ char *wowlan_triggers;
+
+ /**
+ * p2p_search_delay - Extra delay between concurrent search iterations
+ *
+ * Add extra delay (in milliseconds) between search iterations when
+ * there is a concurrent operation to make p2p_find friendlier to
+ * concurrent operations by avoiding it from taking 100% of radio
+ * resources.
+ */
+ unsigned int p2p_search_delay;
+
+ /**
+ * mac_addr - MAC address policy default
+ *
+ * 0 = use permanent MAC address
+ * 1 = use random MAC address for each ESS connection
+ * 2 = like 1, but maintain OUI (with local admin bit set)
+ *
+ * By default, permanent MAC address is used unless policy is changed by
+ * the per-network mac_addr parameter. Global mac_addr=1 can be used to
+ * change this default behavior.
+ */
+ int mac_addr;
+
+ /**
+ * rand_addr_lifetime - Lifetime of random MAC address in seconds
+ */
+ unsigned int rand_addr_lifetime;
+
+ /**
+ * preassoc_mac_addr - Pre-association MAC address policy
+ *
+ * 0 = use permanent MAC address
+ * 1 = use random MAC address
+ * 2 = like 1, but maintain OUI (with local admin bit set)
+ */
+ int preassoc_mac_addr;
+
+ /**
+ * key_mgmt_offload - Use key management offload
+ *
+ * Key management offload should be used if the device supports it.
+ * Key management offload is the capability of a device operating as
+ * a station to do the exchange necessary to establish temporal keys
+ * during initial RSN connection, after roaming, or during a PTK
+ * rekeying operation.
+ */
+ int key_mgmt_offload;
+
+ /**
+ * user_mpm - MPM residency
+ *
+ * 0: MPM lives in driver.
+ * 1: wpa_supplicant handles peering and station allocation.
+ *
+ * If AMPE or SAE is enabled, the MPM is always in userspace.
+ */
+ int user_mpm;
+
+ /**
+ * max_peer_links - Maximum number of peer links
+ *
+ * Maximum number of mesh peering currently maintained by the STA.
+ */
+ int max_peer_links;
+
+ /**
+ * cert_in_cb - Whether to include a peer certificate dump in events
+ *
+ * This controls whether peer certificates for authentication server and
+ * its certificate chain are included in EAP peer certificate events.
+ */
+ int cert_in_cb;
+
+ /**
+ * mesh_max_inactivity - Timeout in seconds to detect STA inactivity
+ *
+ * This timeout value is used in mesh STA to clean up inactive stations.
+ * By default: 300 seconds.
+ */
+ int mesh_max_inactivity;
+
+ /**
+ * dot11RSNASAERetransPeriod - Timeout to retransmit SAE Auth frame
+ *
+ * This timeout value is used in mesh STA to retransmit
+ * SAE Authentication frame.
+ * By default: 1000 milliseconds.
+ */
+ int dot11RSNASAERetransPeriod;
+
+ /**
+ * passive_scan - Whether to force passive scan for network connection
+ *
+ * This parameter can be used to force only passive scanning to be used
+ * for network connection cases. It should be noted that this will slow
+ * down scan operations and reduce likelihood of finding the AP. In
+ * addition, some use cases will override this due to functional
+ * requirements, e.g., for finding an AP that uses hidden SSID
+ * (scan_ssid=1) or P2P device discovery.
+ */
+ int passive_scan;
+
+ /**
+ * reassoc_same_bss_optim - Whether to optimize reassoc-to-same-BSS
+ */
+ int reassoc_same_bss_optim;
+
+ /**
+ * wps_priority - Priority for the networks added through WPS
+ *
+ * This priority value will be set to each network profile that is added
+ * by executing the WPS protocol.
+ */
+ int wps_priority;
+
+ /**
+ * fst_group_id - FST group ID
+ */
+ char *fst_group_id;
+
+ /**
+ * fst_priority - priority of the interface within the FST group
+ */
+ int fst_priority;
+
+ /**
+ * fst_llt - default FST LLT (Link-Lost Timeout) to be used for the
+ * interface.
+ */
+ int fst_llt;
+};
+
+
+/* Prototypes for common functions from config.c */
+
+void wpa_config_free(struct wpa_config *ssid);
+void wpa_config_free_ssid(struct wpa_ssid *ssid);
+void wpa_config_foreach_network(struct wpa_config *config,
+ void (*func)(void *, struct wpa_ssid *),
+ void *arg);
+struct wpa_ssid * wpa_config_get_network(struct wpa_config *config, int id);
+struct wpa_ssid * wpa_config_add_network(struct wpa_config *config);
+int wpa_config_remove_network(struct wpa_config *config, int id);
+void wpa_config_set_network_defaults(struct wpa_ssid *ssid);
+int wpa_config_set(struct wpa_ssid *ssid, const char *var, const char *value,
+ int line);
+int wpa_config_set_quoted(struct wpa_ssid *ssid, const char *var,
+ const char *value);
+int wpa_config_dump_values(struct wpa_config *config, char *buf,
+ size_t buflen);
+int wpa_config_get_value(const char *name, struct wpa_config *config,
+ char *buf, size_t buflen);
+
+char ** wpa_config_get_all(struct wpa_ssid *ssid, int get_keys);
+char * wpa_config_get(struct wpa_ssid *ssid, const char *var);
+char * wpa_config_get_no_key(struct wpa_ssid *ssid, const char *var);
+void wpa_config_update_psk(struct wpa_ssid *ssid);
+int wpa_config_add_prio_network(struct wpa_config *config,
+ struct wpa_ssid *ssid);
+int wpa_config_update_prio_list(struct wpa_config *config);
+const struct wpa_config_blob * wpa_config_get_blob(struct wpa_config *config,
+ const char *name);
+void wpa_config_set_blob(struct wpa_config *config,
+ struct wpa_config_blob *blob);
+void wpa_config_free_blob(struct wpa_config_blob *blob);
+int wpa_config_remove_blob(struct wpa_config *config, const char *name);
+void wpa_config_flush_blobs(struct wpa_config *config);
+
+struct wpa_cred * wpa_config_get_cred(struct wpa_config *config, int id);
+struct wpa_cred * wpa_config_add_cred(struct wpa_config *config);
+int wpa_config_remove_cred(struct wpa_config *config, int id);
+void wpa_config_free_cred(struct wpa_cred *cred);
+int wpa_config_set_cred(struct wpa_cred *cred, const char *var,
+ const char *value, int line);
+char * wpa_config_get_cred_no_key(struct wpa_cred *cred, const char *var);
+
+struct wpa_config * wpa_config_alloc_empty(const char *ctrl_interface,
+ const char *driver_param);
+#ifndef CONFIG_NO_STDOUT_DEBUG
+void wpa_config_debug_dump_networks(struct wpa_config *config);
+#else /* CONFIG_NO_STDOUT_DEBUG */
+#define wpa_config_debug_dump_networks(c) do { } while (0)
+#endif /* CONFIG_NO_STDOUT_DEBUG */
+
+
+/* Prototypes for common functions from config.c */
+int wpa_config_process_global(struct wpa_config *config, char *pos, int line);
+
+
+/* Prototypes for backend specific functions from the selected config_*.c */
+
+/**
+ * wpa_config_read - Read and parse configuration database
+ * @name: Name of the configuration (e.g., path and file name for the
+ * configuration file)
+ * @cfgp: Pointer to previously allocated configuration data or %NULL if none
+ * Returns: Pointer to allocated configuration data or %NULL on failure
+ *
+ * This function reads configuration data, parses its contents, and allocates
+ * data structures needed for storing configuration information. The allocated
+ * data can be freed with wpa_config_free().
+ *
+ * Each configuration backend needs to implement this function.
+ */
+struct wpa_config * wpa_config_read(const char *name, struct wpa_config *cfgp);
+
+/**
+ * wpa_config_write - Write or update configuration data
+ * @name: Name of the configuration (e.g., path and file name for the
+ * configuration file)
+ * @config: Configuration data from wpa_config_read()
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function write all configuration data into an external database (e.g.,
+ * a text file) in a format that can be read with wpa_config_read(). This can
+ * be used to allow wpa_supplicant to update its configuration, e.g., when a
+ * new network is added or a password is changed.
+ *
+ * Each configuration backend needs to implement this function.
+ */
+int wpa_config_write(const char *name, struct wpa_config *config);
+
+#endif /* CONFIG_H */
diff --git a/freebsd/contrib/wpa/wpa_supplicant/config_file.c b/freebsd/contrib/wpa/wpa_supplicant/config_file.c
new file mode 100644
index 00000000..8466fc39
--- /dev/null
+++ b/freebsd/contrib/wpa/wpa_supplicant/config_file.c
@@ -0,0 +1,1389 @@
+#include <machine/rtems-bsd-user-space.h>
+
+/*
+ * WPA Supplicant / Configuration backend: text file
+ * Copyright (c) 2003-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 a configuration backend for text files. All the
+ * configuration information is stored in a text file that uses a format
+ * described in the sample configuration file, wpa_supplicant.conf.
+ */
+
+#include "includes.h"
+#ifdef ANDROID
+#include <sys/stat.h>
+#endif /* ANDROID */
+
+#include "common.h"
+#include "config.h"
+#include "base64.h"
+#include "uuid.h"
+#include "p2p/p2p.h"
+#include "eap_peer/eap_methods.h"
+#include "eap_peer/eap.h"
+
+
+static int newline_terminated(const char *buf, size_t buflen)
+{
+ size_t len = os_strlen(buf);
+ if (len == 0)
+ return 0;
+ if (len == buflen - 1 && buf[buflen - 1] != '\r' &&
+ buf[len - 1] != '\n')
+ return 0;
+ return 1;
+}
+
+
+static void skip_line_end(FILE *stream)
+{
+ char buf[100];
+ while (fgets(buf, sizeof(buf), stream)) {
+ buf[sizeof(buf) - 1] = '\0';
+ if (newline_terminated(buf, sizeof(buf)))
+ return;
+ }
+}
+
+
+/**
+ * wpa_config_get_line - Read the next configuration file line
+ * @s: Buffer for the line
+ * @size: The buffer length
+ * @stream: File stream to read from
+ * @line: Pointer to a variable storing the file line number
+ * @_pos: Buffer for the pointer to the beginning of data on the text line or
+ * %NULL if not needed (returned value used instead)
+ * Returns: Pointer to the beginning of data on the text line or %NULL if no
+ * more text lines are available.
+ *
+ * This function reads the next non-empty line from the configuration file and
+ * removes comments. The returned string is guaranteed to be null-terminated.
+ */
+static char * wpa_config_get_line(char *s, int size, FILE *stream, int *line,
+ char **_pos)
+{
+ char *pos, *end, *sstart;
+
+ while (fgets(s, size, stream)) {
+ (*line)++;
+ s[size - 1] = '\0';
+ if (!newline_terminated(s, size)) {
+ /*
+ * The line was truncated - skip rest of it to avoid
+ * confusing error messages.
+ */
+ wpa_printf(MSG_INFO, "Long line in configuration file "
+ "truncated");
+ skip_line_end(stream);
+ }
+ pos = s;
+
+ /* Skip white space from the beginning of line. */
+ while (*pos == ' ' || *pos == '\t' || *pos == '\r')
+ pos++;
+
+ /* Skip comment lines and empty lines */
+ if (*pos == '#' || *pos == '\n' || *pos == '\0')
+ continue;
+
+ /*
+ * Remove # comments unless they are within a double quoted
+ * string.
+ */
+ sstart = os_strchr(pos, '"');
+ if (sstart)
+ sstart = os_strrchr(sstart + 1, '"');
+ if (!sstart)
+ sstart = pos;
+ end = os_strchr(sstart, '#');
+ if (end)
+ *end-- = '\0';
+ else
+ end = pos + os_strlen(pos) - 1;
+
+ /* Remove trailing white space. */
+ while (end > pos &&
+ (*end == '\n' || *end == ' ' || *end == '\t' ||
+ *end == '\r'))
+ *end-- = '\0';
+
+ if (*pos == '\0')
+ continue;
+
+ if (_pos)
+ *_pos = pos;
+ return pos;
+ }
+
+ if (_pos)
+ *_pos = NULL;
+ return NULL;
+}
+
+
+static int wpa_config_validate_network(struct wpa_ssid *ssid, int line)
+{
+ int errors = 0;
+
+ if (ssid->passphrase) {
+ if (ssid->psk_set) {
+ wpa_printf(MSG_ERROR, "Line %d: both PSK and "
+ "passphrase configured.", line);
+ errors++;
+ }
+ wpa_config_update_psk(ssid);
+ }
+
+ if ((ssid->group_cipher & WPA_CIPHER_CCMP) &&
+ !(ssid->pairwise_cipher & WPA_CIPHER_CCMP) &&
+ !(ssid->pairwise_cipher & WPA_CIPHER_NONE)) {
+ /* Group cipher cannot be stronger than the pairwise cipher. */
+ wpa_printf(MSG_DEBUG, "Line %d: removed CCMP from group cipher"
+ " list since it was not allowed for pairwise "
+ "cipher", line);
+ ssid->group_cipher &= ~WPA_CIPHER_CCMP;
+ }
+
+ if (ssid->mode == WPAS_MODE_MESH &&
+ (ssid->key_mgmt != WPA_KEY_MGMT_NONE &&
+ ssid->key_mgmt != WPA_KEY_MGMT_SAE)) {
+ wpa_printf(MSG_ERROR,
+ "Line %d: key_mgmt for mesh network should be open or SAE",
+ line);
+ errors++;
+ }
+
+ return errors;
+}
+
+
+static struct wpa_ssid * wpa_config_read_network(FILE *f, int *line, int id)
+{
+ struct wpa_ssid *ssid;
+ int errors = 0, end = 0;
+ char buf[2000], *pos, *pos2;
+
+ wpa_printf(MSG_MSGDUMP, "Line: %d - start of a new network block",
+ *line);
+ ssid = os_zalloc(sizeof(*ssid));
+ if (ssid == NULL)
+ return NULL;
+ dl_list_init(&ssid->psk_list);
+ ssid->id = id;
+
+ wpa_config_set_network_defaults(ssid);
+
+ while (wpa_config_get_line(buf, sizeof(buf), f, line, &pos)) {
+ if (os_strcmp(pos, "}") == 0) {
+ end = 1;
+ break;
+ }
+
+ pos2 = os_strchr(pos, '=');
+ if (pos2 == NULL) {
+ wpa_printf(MSG_ERROR, "Line %d: Invalid SSID line "
+ "'%s'.", *line, pos);
+ errors++;
+ continue;
+ }
+
+ *pos2++ = '\0';
+ if (*pos2 == '"') {
+ if (os_strchr(pos2 + 1, '"') == NULL) {
+ wpa_printf(MSG_ERROR, "Line %d: invalid "
+ "quotation '%s'.", *line, pos2);
+ errors++;
+ continue;
+ }
+ }
+
+ if (wpa_config_set(ssid, pos, pos2, *line) < 0)
+ errors++;
+ }
+
+ if (!end) {
+ wpa_printf(MSG_ERROR, "Line %d: network block was not "
+ "terminated properly.", *line);
+ errors++;
+ }
+
+ errors += wpa_config_validate_network(ssid, *line);
+
+ if (errors) {
+ wpa_config_free_ssid(ssid);
+ ssid = NULL;
+ }
+
+ return ssid;
+}
+
+
+static struct wpa_cred * wpa_config_read_cred(FILE *f, int *line, int id)
+{
+ struct wpa_cred *cred;
+ int errors = 0, end = 0;
+ char buf[256], *pos, *pos2;
+
+ wpa_printf(MSG_MSGDUMP, "Line: %d - start of a new cred block", *line);
+ cred = os_zalloc(sizeof(*cred));
+ if (cred == NULL)
+ return NULL;
+ cred->id = id;
+ cred->sim_num = DEFAULT_USER_SELECTED_SIM;
+
+ while (wpa_config_get_line(buf, sizeof(buf), f, line, &pos)) {
+ if (os_strcmp(pos, "}") == 0) {
+ end = 1;
+ break;
+ }
+
+ pos2 = os_strchr(pos, '=');
+ if (pos2 == NULL) {
+ wpa_printf(MSG_ERROR, "Line %d: Invalid cred line "
+ "'%s'.", *line, pos);
+ errors++;
+ continue;
+ }
+
+ *pos2++ = '\0';
+ if (*pos2 == '"') {
+ if (os_strchr(pos2 + 1, '"') == NULL) {
+ wpa_printf(MSG_ERROR, "Line %d: invalid "
+ "quotation '%s'.", *line, pos2);
+ errors++;
+ continue;
+ }
+ }
+
+ if (wpa_config_set_cred(cred, pos, pos2, *line) < 0)
+ errors++;
+ }
+
+ if (!end) {
+ wpa_printf(MSG_ERROR, "Line %d: cred block was not "
+ "terminated properly.", *line);
+ errors++;
+ }
+
+ if (errors) {
+ wpa_config_free_cred(cred);
+ cred = NULL;
+ }
+
+ return cred;
+}
+
+
+#ifndef CONFIG_NO_CONFIG_BLOBS
+static struct wpa_config_blob * wpa_config_read_blob(FILE *f, int *line,
+ const char *name)
+{
+ struct wpa_config_blob *blob;
+ char buf[256], *pos;
+ unsigned char *encoded = NULL, *nencoded;
+ int end = 0;
+ size_t encoded_len = 0, len;
+
+ wpa_printf(MSG_MSGDUMP, "Line: %d - start of a new named blob '%s'",
+ *line, name);
+
+ while (wpa_config_get_line(buf, sizeof(buf), f, line, &pos)) {
+ if (os_strcmp(pos, "}") == 0) {
+ end = 1;
+ break;
+ }
+
+ len = os_strlen(pos);
+ nencoded = os_realloc(encoded, encoded_len + len);
+ if (nencoded == NULL) {
+ wpa_printf(MSG_ERROR, "Line %d: not enough memory for "
+ "blob", *line);
+ os_free(encoded);
+ return NULL;
+ }
+ encoded = nencoded;
+ os_memcpy(encoded + encoded_len, pos, len);
+ encoded_len += len;
+ }
+
+ if (!end) {
+ wpa_printf(MSG_ERROR, "Line %d: blob was not terminated "
+ "properly", *line);
+ os_free(encoded);
+ return NULL;
+ }
+
+ blob = os_zalloc(sizeof(*blob));
+ if (blob == NULL) {
+ os_free(encoded);
+ return NULL;
+ }
+ blob->name = os_strdup(name);
+ blob->data = base64_decode(encoded, encoded_len, &blob->len);
+ os_free(encoded);
+
+ if (blob->name == NULL || blob->data == NULL) {
+ wpa_config_free_blob(blob);
+ return NULL;
+ }
+
+ return blob;
+}
+
+
+static int wpa_config_process_blob(struct wpa_config *config, FILE *f,
+ int *line, char *bname)
+{
+ char *name_end;
+ struct wpa_config_blob *blob;
+
+ name_end = os_strchr(bname, '=');
+ if (name_end == NULL) {
+ wpa_printf(MSG_ERROR, "Line %d: no blob name terminator",
+ *line);
+ return -1;
+ }
+ *name_end = '\0';
+
+ blob = wpa_config_read_blob(f, line, bname);
+ if (blob == NULL) {
+ wpa_printf(MSG_ERROR, "Line %d: failed to read blob %s",
+ *line, bname);
+ return -1;
+ }
+ wpa_config_set_blob(config, blob);
+ return 0;
+}
+#endif /* CONFIG_NO_CONFIG_BLOBS */
+
+
+struct wpa_config * wpa_config_read(const char *name, struct wpa_config *cfgp)
+{
+ FILE *f;
+ char buf[512], *pos;
+ int errors = 0, line = 0;
+ struct wpa_ssid *ssid, *tail, *head;
+ struct wpa_cred *cred, *cred_tail, *cred_head;
+ struct wpa_config *config;
+ int id = 0;
+ int cred_id = 0;
+
+ if (name == NULL)
+ return NULL;
+ if (cfgp)
+ config = cfgp;
+ else
+ config = wpa_config_alloc_empty(NULL, NULL);
+ if (config == NULL) {
+ wpa_printf(MSG_ERROR, "Failed to allocate config file "
+ "structure");
+ return NULL;
+ }
+ tail = head = config->ssid;
+ while (tail && tail->next)
+ tail = tail->next;
+ cred_tail = cred_head = config->cred;
+ while (cred_tail && cred_tail->next)
+ cred_tail = cred_tail->next;
+
+ wpa_printf(MSG_DEBUG, "Reading configuration file '%s'", name);
+ f = fopen(name, "r");
+ if (f == NULL) {
+ wpa_printf(MSG_ERROR, "Failed to open config file '%s', "
+ "error: %s", name, strerror(errno));
+ os_free(config);
+ return NULL;
+ }
+
+ while (wpa_config_get_line(buf, sizeof(buf), f, &line, &pos)) {
+ if (os_strcmp(pos, "network={") == 0) {
+ ssid = wpa_config_read_network(f, &line, id++);
+ if (ssid == NULL) {
+ wpa_printf(MSG_ERROR, "Line %d: failed to "
+ "parse network block.", line);
+ errors++;
+ continue;
+ }
+ if (head == NULL) {
+ head = tail = ssid;
+ } else {
+ tail->next = ssid;
+ tail = ssid;
+ }
+ if (wpa_config_add_prio_network(config, ssid)) {
+ wpa_printf(MSG_ERROR, "Line %d: failed to add "
+ "network block to priority list.",
+ line);
+ errors++;
+ continue;
+ }
+ } else if (os_strcmp(pos, "cred={") == 0) {
+ cred = wpa_config_read_cred(f, &line, cred_id++);
+ if (cred == NULL) {
+ wpa_printf(MSG_ERROR, "Line %d: failed to "
+ "parse cred block.", line);
+ errors++;
+ continue;
+ }
+ if (cred_head == NULL) {
+ cred_head = cred_tail = cred;
+ } else {
+ cred_tail->next = cred;
+ cred_tail = cred;
+ }
+#ifndef CONFIG_NO_CONFIG_BLOBS
+ } else if (os_strncmp(pos, "blob-base64-", 12) == 0) {
+ if (wpa_config_process_blob(config, f, &line, pos + 12)
+ < 0) {
+ wpa_printf(MSG_ERROR, "Line %d: failed to "
+ "process blob.", line);
+ errors++;
+ continue;
+ }
+#endif /* CONFIG_NO_CONFIG_BLOBS */
+ } else if (wpa_config_process_global(config, pos, line) < 0) {
+ wpa_printf(MSG_ERROR, "Line %d: Invalid configuration "
+ "line '%s'.", line, pos);
+ errors++;
+ continue;
+ }
+ }
+
+ fclose(f);
+
+ config->ssid = head;
+ wpa_config_debug_dump_networks(config);
+ config->cred = cred_head;
+
+#ifndef WPA_IGNORE_CONFIG_ERRORS
+ if (errors) {
+ wpa_config_free(config);
+ config = NULL;
+ head = NULL;
+ }
+#endif /* WPA_IGNORE_CONFIG_ERRORS */
+
+ return config;
+}
+
+
+#ifndef CONFIG_NO_CONFIG_WRITE
+
+static void write_str(FILE *f, const char *field, struct wpa_ssid *ssid)
+{
+ char *value = wpa_config_get(ssid, field);
+ if (value == NULL)
+ return;
+ fprintf(f, "\t%s=%s\n", field, value);
+ os_free(value);
+}
+
+
+static void write_int(FILE *f, const char *field, int value, int def)
+{
+ if (value == def)
+ return;
+ fprintf(f, "\t%s=%d\n", field, value);
+}
+
+
+static void write_bssid(FILE *f, struct wpa_ssid *ssid)
+{
+ char *value = wpa_config_get(ssid, "bssid");
+ if (value == NULL)
+ return;
+ fprintf(f, "\tbssid=%s\n", value);
+ os_free(value);
+}
+
+
+static void write_psk(FILE *f, struct wpa_ssid *ssid)
+{
+ char *value;
+
+ if (ssid->mem_only_psk)
+ return;
+
+ value = wpa_config_get(ssid, "psk");
+ if (value == NULL)
+ return;
+ fprintf(f, "\tpsk=%s\n", value);
+ os_free(value);
+}
+
+
+static void write_proto(FILE *f, struct wpa_ssid *ssid)
+{
+ char *value;
+
+ if (ssid->proto == DEFAULT_PROTO)
+ return;
+
+ value = wpa_config_get(ssid, "proto");
+ if (value == NULL)
+ return;
+ if (value[0])
+ fprintf(f, "\tproto=%s\n", value);
+ os_free(value);
+}
+
+
+static void write_key_mgmt(FILE *f, struct wpa_ssid *ssid)
+{
+ char *value;
+
+ if (ssid->key_mgmt == DEFAULT_KEY_MGMT)
+ return;
+
+ value = wpa_config_get(ssid, "key_mgmt");
+ if (value == NULL)
+ return;
+ if (value[0])
+ fprintf(f, "\tkey_mgmt=%s\n", value);
+ os_free(value);
+}
+
+
+static void write_pairwise(FILE *f, struct wpa_ssid *ssid)
+{
+ char *value;
+
+ if (ssid->pairwise_cipher == DEFAULT_PAIRWISE)
+ return;
+
+ value = wpa_config_get(ssid, "pairwise");
+ if (value == NULL)
+ return;
+ if (value[0])
+ fprintf(f, "\tpairwise=%s\n", value);
+ os_free(value);
+}
+
+
+static void write_group(FILE *f, struct wpa_ssid *ssid)
+{
+ char *value;
+
+ if (ssid->group_cipher == DEFAULT_GROUP)
+ return;
+
+ value = wpa_config_get(ssid, "group");
+ if (value == NULL)
+ return;
+ if (value[0])
+ fprintf(f, "\tgroup=%s\n", value);
+ os_free(value);
+}
+
+
+static void write_auth_alg(FILE *f, struct wpa_ssid *ssid)
+{
+ char *value;
+
+ if (ssid->auth_alg == 0)
+ return;
+
+ value = wpa_config_get(ssid, "auth_alg");
+ if (value == NULL)
+ return;
+ if (value[0])
+ fprintf(f, "\tauth_alg=%s\n", value);
+ os_free(value);
+}
+
+
+#ifdef IEEE8021X_EAPOL
+static void write_eap(FILE *f, struct wpa_ssid *ssid)
+{
+ char *value;
+
+ value = wpa_config_get(ssid, "eap");
+ if (value == NULL)
+ return;
+
+ if (value[0])
+ fprintf(f, "\teap=%s\n", value);
+ os_free(value);
+}
+#endif /* IEEE8021X_EAPOL */
+
+
+static void write_wep_key(FILE *f, int idx, struct wpa_ssid *ssid)
+{
+ char field[20], *value;
+ int res;
+
+ res = os_snprintf(field, sizeof(field), "wep_key%d", idx);
+ if (os_snprintf_error(sizeof(field), res))
+ return;
+ value = wpa_config_get(ssid, field);
+ if (value) {
+ fprintf(f, "\t%s=%s\n", field, value);
+ os_free(value);
+ }
+}
+
+
+#ifdef CONFIG_P2P
+
+static void write_go_p2p_dev_addr(FILE *f, struct wpa_ssid *ssid)
+{
+ char *value = wpa_config_get(ssid, "go_p2p_dev_addr");
+ if (value == NULL)
+ return;
+ fprintf(f, "\tgo_p2p_dev_addr=%s\n", value);
+ os_free(value);
+}
+
+static void write_p2p_client_list(FILE *f, struct wpa_ssid *ssid)
+{
+ char *value = wpa_config_get(ssid, "p2p_client_list");
+ if (value == NULL)
+ return;
+ fprintf(f, "\tp2p_client_list=%s\n", value);
+ os_free(value);
+}
+
+
+static void write_psk_list(FILE *f, struct wpa_ssid *ssid)
+{
+ struct psk_list_entry *psk;
+ char hex[32 * 2 + 1];
+
+ dl_list_for_each(psk, &ssid->psk_list, struct psk_list_entry, list) {
+ wpa_snprintf_hex(hex, sizeof(hex), psk->psk, sizeof(psk->psk));
+ fprintf(f, "\tpsk_list=%s" MACSTR "-%s\n",
+ psk->p2p ? "P2P-" : "", MAC2STR(psk->addr), hex);
+ }
+}
+
+#endif /* CONFIG_P2P */
+
+
+static void wpa_config_write_network(FILE *f, struct wpa_ssid *ssid)
+{
+ int i;
+
+#define STR(t) write_str(f, #t, ssid)
+#define INT(t) write_int(f, #t, ssid->t, 0)
+#define INTe(t) write_int(f, #t, ssid->eap.t, 0)
+#define INT_DEF(t, def) write_int(f, #t, ssid->t, def)
+#define INT_DEFe(t, def) write_int(f, #t, ssid->eap.t, def)
+
+ STR(ssid);
+ INT(scan_ssid);
+ write_bssid(f, ssid);
+ write_str(f, "bssid_blacklist", ssid);
+ write_str(f, "bssid_whitelist", ssid);
+ write_psk(f, ssid);
+ INT(mem_only_psk);
+ write_proto(f, ssid);
+ write_key_mgmt(f, ssid);
+ INT_DEF(bg_scan_period, DEFAULT_BG_SCAN_PERIOD);
+ write_pairwise(f, ssid);
+ write_group(f, ssid);
+ write_auth_alg(f, ssid);
+ STR(bgscan);
+ STR(autoscan);
+ STR(scan_freq);
+#ifdef IEEE8021X_EAPOL
+ write_eap(f, ssid);
+ STR(identity);
+ STR(anonymous_identity);
+ STR(password);
+ STR(ca_cert);
+ STR(ca_path);
+ STR(client_cert);
+ STR(private_key);
+ STR(private_key_passwd);
+ STR(dh_file);
+ STR(subject_match);
+ STR(altsubject_match);
+ STR(domain_suffix_match);
+ STR(domain_match);
+ STR(ca_cert2);
+ STR(ca_path2);
+ STR(client_cert2);
+ STR(private_key2);
+ STR(private_key2_passwd);
+ STR(dh_file2);
+ STR(subject_match2);
+ STR(altsubject_match2);
+ STR(domain_suffix_match2);
+ STR(domain_match2);
+ STR(phase1);
+ STR(phase2);
+ STR(pcsc);
+ STR(pin);
+ STR(engine_id);
+ STR(key_id);
+ STR(cert_id);
+ STR(ca_cert_id);
+ STR(key2_id);
+ STR(pin2);
+ STR(engine2_id);
+ STR(cert2_id);
+ STR(ca_cert2_id);
+ INTe(engine);
+ INTe(engine2);
+ INT_DEF(eapol_flags, DEFAULT_EAPOL_FLAGS);
+ STR(openssl_ciphers);
+ INTe(erp);
+#endif /* IEEE8021X_EAPOL */
+ for (i = 0; i < 4; i++)
+ write_wep_key(f, i, ssid);
+ INT(wep_tx_keyidx);
+ INT(priority);
+#ifdef IEEE8021X_EAPOL
+ INT_DEF(eap_workaround, DEFAULT_EAP_WORKAROUND);
+ STR(pac_file);
+ INT_DEFe(fragment_size, DEFAULT_FRAGMENT_SIZE);
+ INTe(ocsp);
+ INT_DEFe(sim_num, DEFAULT_USER_SELECTED_SIM);
+#endif /* IEEE8021X_EAPOL */
+ INT(mode);
+ INT(no_auto_peer);
+ INT(frequency);
+ INT(fixed_freq);
+ write_int(f, "proactive_key_caching", ssid->proactive_key_caching, -1);
+ INT(disabled);
+ INT(peerkey);
+ INT(mixed_cell);
+#ifdef CONFIG_IEEE80211W
+ write_int(f, "ieee80211w", ssid->ieee80211w,
+ MGMT_FRAME_PROTECTION_DEFAULT);
+#endif /* CONFIG_IEEE80211W */
+ STR(id_str);
+#ifdef CONFIG_P2P
+ write_go_p2p_dev_addr(f, ssid);
+ write_p2p_client_list(f, ssid);
+ write_psk_list(f, ssid);
+#endif /* CONFIG_P2P */
+ INT(ap_max_inactivity);
+ INT(dtim_period);
+ INT(beacon_int);
+#ifdef CONFIG_MACSEC
+ INT(macsec_policy);
+#endif /* CONFIG_MACSEC */
+#ifdef CONFIG_HS20
+ INT(update_identifier);
+#endif /* CONFIG_HS20 */
+ write_int(f, "mac_addr", ssid->mac_addr, -1);
+#ifdef CONFIG_MESH
+ STR(mesh_basic_rates);
+ INT_DEF(dot11MeshMaxRetries, DEFAULT_MESH_MAX_RETRIES);
+ INT_DEF(dot11MeshRetryTimeout, DEFAULT_MESH_RETRY_TIMEOUT);
+ INT_DEF(dot11MeshConfirmTimeout, DEFAULT_MESH_CONFIRM_TIMEOUT);
+ INT_DEF(dot11MeshHoldingTimeout, DEFAULT_MESH_HOLDING_TIMEOUT);
+#endif /* CONFIG_MESH */
+ INT(wpa_ptk_rekey);
+ INT(ignore_broadcast_ssid);
+#ifdef CONFIG_HT_OVERRIDES
+ INT_DEF(disable_ht, DEFAULT_DISABLE_HT);
+ INT_DEF(disable_ht40, DEFAULT_DISABLE_HT40);
+ INT_DEF(disable_sgi, DEFAULT_DISABLE_SGI);
+ INT_DEF(disable_ldpc, DEFAULT_DISABLE_LDPC);
+ INT(ht40_intolerant);
+ INT_DEF(disable_max_amsdu, DEFAULT_DISABLE_MAX_AMSDU);
+ INT_DEF(ampdu_factor, DEFAULT_AMPDU_FACTOR);
+ INT_DEF(ampdu_density, DEFAULT_AMPDU_DENSITY);
+ STR(ht_mcs);
+#endif /* CONFIG_HT_OVERRIDES */
+#ifdef CONFIG_VHT_OVERRIDES
+ INT(disable_vht);
+ INT(vht_capa);
+ INT(vht_capa_mask);
+ INT_DEF(vht_rx_mcs_nss_1, -1);
+ INT_DEF(vht_rx_mcs_nss_2, -1);
+ INT_DEF(vht_rx_mcs_nss_3, -1);
+ INT_DEF(vht_rx_mcs_nss_4, -1);
+ INT_DEF(vht_rx_mcs_nss_5, -1);
+ INT_DEF(vht_rx_mcs_nss_6, -1);
+ INT_DEF(vht_rx_mcs_nss_7, -1);
+ INT_DEF(vht_rx_mcs_nss_8, -1);
+ INT_DEF(vht_tx_mcs_nss_1, -1);
+ INT_DEF(vht_tx_mcs_nss_2, -1);
+ INT_DEF(vht_tx_mcs_nss_3, -1);
+ INT_DEF(vht_tx_mcs_nss_4, -1);
+ INT_DEF(vht_tx_mcs_nss_5, -1);
+ INT_DEF(vht_tx_mcs_nss_6, -1);
+ INT_DEF(vht_tx_mcs_nss_7, -1);
+ INT_DEF(vht_tx_mcs_nss_8, -1);
+#endif /* CONFIG_VHT_OVERRIDES */
+
+#undef STR
+#undef INT
+#undef INT_DEF
+}
+
+
+static void wpa_config_write_cred(FILE *f, struct wpa_cred *cred)
+{
+ size_t i;
+
+ if (cred->priority)
+ fprintf(f, "\tpriority=%d\n", cred->priority);
+ if (cred->pcsc)
+ fprintf(f, "\tpcsc=%d\n", cred->pcsc);
+ if (cred->realm)
+ fprintf(f, "\trealm=\"%s\"\n", cred->realm);
+ if (cred->username)
+ fprintf(f, "\tusername=\"%s\"\n", cred->username);
+ if (cred->password && cred->ext_password)
+ fprintf(f, "\tpassword=ext:%s\n", cred->password);
+ else if (cred->password)
+ fprintf(f, "\tpassword=\"%s\"\n", cred->password);
+ if (cred->ca_cert)
+ fprintf(f, "\tca_cert=\"%s\"\n", cred->ca_cert);
+ if (cred->client_cert)
+ fprintf(f, "\tclient_cert=\"%s\"\n", cred->client_cert);
+ if (cred->private_key)
+ fprintf(f, "\tprivate_key=\"%s\"\n", cred->private_key);
+ if (cred->private_key_passwd)
+ fprintf(f, "\tprivate_key_passwd=\"%s\"\n",
+ cred->private_key_passwd);
+ if (cred->imsi)
+ fprintf(f, "\timsi=\"%s\"\n", cred->imsi);
+ if (cred->milenage)
+ fprintf(f, "\tmilenage=\"%s\"\n", cred->milenage);
+ for (i = 0; i < cred->num_domain; i++)
+ fprintf(f, "\tdomain=\"%s\"\n", cred->domain[i]);
+ if (cred->domain_suffix_match)
+ fprintf(f, "\tdomain_suffix_match=\"%s\"\n",
+ cred->domain_suffix_match);
+ if (cred->roaming_consortium_len) {
+ fprintf(f, "\troaming_consortium=");
+ for (i = 0; i < cred->roaming_consortium_len; i++)
+ fprintf(f, "%02x", cred->roaming_consortium[i]);
+ fprintf(f, "\n");
+ }
+ if (cred->eap_method) {
+ const char *name;
+ name = eap_get_name(cred->eap_method[0].vendor,
+ cred->eap_method[0].method);
+ if (name)
+ fprintf(f, "\teap=%s\n", name);
+ }
+ if (cred->phase1)
+ fprintf(f, "\tphase1=\"%s\"\n", cred->phase1);
+ if (cred->phase2)
+ fprintf(f, "\tphase2=\"%s\"\n", cred->phase2);
+ if (cred->excluded_ssid) {
+ size_t j;
+ for (i = 0; i < cred->num_excluded_ssid; i++) {
+ struct excluded_ssid *e = &cred->excluded_ssid[i];
+ fprintf(f, "\texcluded_ssid=");
+ for (j = 0; j < e->ssid_len; j++)
+ fprintf(f, "%02x", e->ssid[j]);
+ fprintf(f, "\n");
+ }
+ }
+ if (cred->roaming_partner) {
+ for (i = 0; i < cred->num_roaming_partner; i++) {
+ struct roaming_partner *p = &cred->roaming_partner[i];
+ fprintf(f, "\troaming_partner=\"%s,%d,%u,%s\"\n",
+ p->fqdn, p->exact_match, p->priority,
+ p->country);
+ }
+ }
+ if (cred->update_identifier)
+ fprintf(f, "\tupdate_identifier=%d\n", cred->update_identifier);
+
+ if (cred->provisioning_sp)
+ fprintf(f, "\tprovisioning_sp=\"%s\"\n", cred->provisioning_sp);
+ if (cred->sp_priority)
+ fprintf(f, "\tsp_priority=%d\n", cred->sp_priority);
+
+ if (cred->min_dl_bandwidth_home)
+ fprintf(f, "\tmin_dl_bandwidth_home=%u\n",
+ cred->min_dl_bandwidth_home);
+ if (cred->min_ul_bandwidth_home)
+ fprintf(f, "\tmin_ul_bandwidth_home=%u\n",
+ cred->min_ul_bandwidth_home);
+ if (cred->min_dl_bandwidth_roaming)
+ fprintf(f, "\tmin_dl_bandwidth_roaming=%u\n",
+ cred->min_dl_bandwidth_roaming);
+ if (cred->min_ul_bandwidth_roaming)
+ fprintf(f, "\tmin_ul_bandwidth_roaming=%u\n",
+ cred->min_ul_bandwidth_roaming);
+
+ if (cred->max_bss_load)
+ fprintf(f, "\tmax_bss_load=%u\n",
+ cred->max_bss_load);
+
+ if (cred->ocsp)
+ fprintf(f, "\tocsp=%d\n", cred->ocsp);
+
+ if (cred->num_req_conn_capab) {
+ for (i = 0; i < cred->num_req_conn_capab; i++) {
+ int *ports;
+
+ fprintf(f, "\treq_conn_capab=%u",
+ cred->req_conn_capab_proto[i]);
+ ports = cred->req_conn_capab_port[i];
+ if (ports) {
+ int j;
+ for (j = 0; ports[j] != -1; j++) {
+ fprintf(f, "%s%d", j > 0 ? "," : ":",
+ ports[j]);
+ }
+ }
+ fprintf(f, "\n");
+ }
+ }
+
+ if (cred->required_roaming_consortium_len) {
+ fprintf(f, "\trequired_roaming_consortium=");
+ for (i = 0; i < cred->required_roaming_consortium_len; i++)
+ fprintf(f, "%02x",
+ cred->required_roaming_consortium[i]);
+ fprintf(f, "\n");
+ }
+
+ if (cred->sim_num != DEFAULT_USER_SELECTED_SIM)
+ fprintf(f, "\tsim_num=%d\n", cred->sim_num);
+}
+
+
+#ifndef CONFIG_NO_CONFIG_BLOBS
+static int wpa_config_write_blob(FILE *f, struct wpa_config_blob *blob)
+{
+ unsigned char *encoded;
+
+ encoded = base64_encode(blob->data, blob->len, NULL);
+ if (encoded == NULL)
+ return -1;
+
+ fprintf(f, "\nblob-base64-%s={\n%s}\n", blob->name, encoded);
+ os_free(encoded);
+ return 0;
+}
+#endif /* CONFIG_NO_CONFIG_BLOBS */
+
+
+static void write_global_bin(FILE *f, const char *field,
+ const struct wpabuf *val)
+{
+ size_t i;
+ const u8 *pos;
+
+ if (val == NULL)
+ return;
+
+ fprintf(f, "%s=", field);
+ pos = wpabuf_head(val);
+ for (i = 0; i < wpabuf_len(val); i++)
+ fprintf(f, "%02X", *pos++);
+ fprintf(f, "\n");
+}
+
+
+static void wpa_config_write_global(FILE *f, struct wpa_config *config)
+{
+#ifdef CONFIG_CTRL_IFACE
+ if (config->ctrl_interface)
+ fprintf(f, "ctrl_interface=%s\n", config->ctrl_interface);
+ if (config->ctrl_interface_group)
+ fprintf(f, "ctrl_interface_group=%s\n",
+ config->ctrl_interface_group);
+#endif /* CONFIG_CTRL_IFACE */
+ if (config->eapol_version != DEFAULT_EAPOL_VERSION)
+ fprintf(f, "eapol_version=%d\n", config->eapol_version);
+ if (config->ap_scan != DEFAULT_AP_SCAN)
+ fprintf(f, "ap_scan=%d\n", config->ap_scan);
+ if (config->disable_scan_offload)
+ fprintf(f, "disable_scan_offload=%d\n",
+ config->disable_scan_offload);
+ if (config->fast_reauth != DEFAULT_FAST_REAUTH)
+ fprintf(f, "fast_reauth=%d\n", config->fast_reauth);
+ if (config->opensc_engine_path)
+ fprintf(f, "opensc_engine_path=%s\n",
+ config->opensc_engine_path);
+ if (config->pkcs11_engine_path)
+ fprintf(f, "pkcs11_engine_path=%s\n",
+ config->pkcs11_engine_path);
+ if (config->pkcs11_module_path)
+ fprintf(f, "pkcs11_module_path=%s\n",
+ config->pkcs11_module_path);
+ if (config->openssl_ciphers)
+ fprintf(f, "openssl_ciphers=%s\n", config->openssl_ciphers);
+ if (config->pcsc_reader)
+ fprintf(f, "pcsc_reader=%s\n", config->pcsc_reader);
+ if (config->pcsc_pin)
+ fprintf(f, "pcsc_pin=%s\n", config->pcsc_pin);
+ if (config->driver_param)
+ fprintf(f, "driver_param=%s\n", config->driver_param);
+ if (config->dot11RSNAConfigPMKLifetime)
+ fprintf(f, "dot11RSNAConfigPMKLifetime=%u\n",
+ config->dot11RSNAConfigPMKLifetime);
+ if (config->dot11RSNAConfigPMKReauthThreshold)
+ fprintf(f, "dot11RSNAConfigPMKReauthThreshold=%u\n",
+ config->dot11RSNAConfigPMKReauthThreshold);
+ if (config->dot11RSNAConfigSATimeout)
+ fprintf(f, "dot11RSNAConfigSATimeout=%u\n",
+ config->dot11RSNAConfigSATimeout);
+ if (config->update_config)
+ fprintf(f, "update_config=%d\n", config->update_config);
+#ifdef CONFIG_WPS
+ if (!is_nil_uuid(config->uuid)) {
+ char buf[40];
+ uuid_bin2str(config->uuid, buf, sizeof(buf));
+ fprintf(f, "uuid=%s\n", buf);
+ }
+ if (config->device_name)
+ fprintf(f, "device_name=%s\n", config->device_name);
+ if (config->manufacturer)
+ fprintf(f, "manufacturer=%s\n", config->manufacturer);
+ if (config->model_name)
+ fprintf(f, "model_name=%s\n", config->model_name);
+ if (config->model_number)
+ fprintf(f, "model_number=%s\n", config->model_number);
+ if (config->serial_number)
+ fprintf(f, "serial_number=%s\n", config->serial_number);
+ {
+ char _buf[WPS_DEV_TYPE_BUFSIZE], *buf;
+ buf = wps_dev_type_bin2str(config->device_type,
+ _buf, sizeof(_buf));
+ if (os_strcmp(buf, "0-00000000-0") != 0)
+ fprintf(f, "device_type=%s\n", buf);
+ }
+ if (WPA_GET_BE32(config->os_version))
+ fprintf(f, "os_version=%08x\n",
+ WPA_GET_BE32(config->os_version));
+ if (config->config_methods)
+ fprintf(f, "config_methods=%s\n", config->config_methods);
+ if (config->wps_cred_processing)
+ fprintf(f, "wps_cred_processing=%d\n",
+ config->wps_cred_processing);
+ if (config->wps_vendor_ext_m1) {
+ int i, len = wpabuf_len(config->wps_vendor_ext_m1);
+ const u8 *p = wpabuf_head_u8(config->wps_vendor_ext_m1);
+ if (len > 0) {
+ fprintf(f, "wps_vendor_ext_m1=");
+ for (i = 0; i < len; i++)
+ fprintf(f, "%02x", *p++);
+ fprintf(f, "\n");
+ }
+ }
+#endif /* CONFIG_WPS */
+#ifdef CONFIG_P2P
+ if (config->p2p_listen_reg_class)
+ fprintf(f, "p2p_listen_reg_class=%d\n",
+ config->p2p_listen_reg_class);
+ if (config->p2p_listen_channel)
+ fprintf(f, "p2p_listen_channel=%d\n",
+ config->p2p_listen_channel);
+ if (config->p2p_oper_reg_class)
+ fprintf(f, "p2p_oper_reg_class=%d\n",
+ config->p2p_oper_reg_class);
+ if (config->p2p_oper_channel)
+ fprintf(f, "p2p_oper_channel=%d\n", config->p2p_oper_channel);
+ if (config->p2p_go_intent != DEFAULT_P2P_GO_INTENT)
+ fprintf(f, "p2p_go_intent=%d\n", config->p2p_go_intent);
+ if (config->p2p_ssid_postfix)
+ fprintf(f, "p2p_ssid_postfix=%s\n", config->p2p_ssid_postfix);
+ if (config->persistent_reconnect)
+ fprintf(f, "persistent_reconnect=%d\n",
+ config->persistent_reconnect);
+ if (config->p2p_intra_bss != DEFAULT_P2P_INTRA_BSS)
+ fprintf(f, "p2p_intra_bss=%d\n", config->p2p_intra_bss);
+ if (config->p2p_group_idle)
+ fprintf(f, "p2p_group_idle=%d\n", config->p2p_group_idle);
+ if (config->p2p_passphrase_len)
+ fprintf(f, "p2p_passphrase_len=%u\n",
+ config->p2p_passphrase_len);
+ if (config->p2p_pref_chan) {
+ unsigned int i;
+ fprintf(f, "p2p_pref_chan=");
+ for (i = 0; i < config->num_p2p_pref_chan; i++) {
+ fprintf(f, "%s%u:%u", i > 0 ? "," : "",
+ config->p2p_pref_chan[i].op_class,
+ config->p2p_pref_chan[i].chan);
+ }
+ fprintf(f, "\n");
+ }
+ if (config->p2p_no_go_freq.num) {
+ char *val = freq_range_list_str(&config->p2p_no_go_freq);
+ if (val) {
+ fprintf(f, "p2p_no_go_freq=%s\n", val);
+ os_free(val);
+ }
+ }
+ if (config->p2p_add_cli_chan)
+ fprintf(f, "p2p_add_cli_chan=%d\n", config->p2p_add_cli_chan);
+ if (config->p2p_optimize_listen_chan !=
+ DEFAULT_P2P_OPTIMIZE_LISTEN_CHAN)
+ fprintf(f, "p2p_optimize_listen_chan=%d\n",
+ config->p2p_optimize_listen_chan);
+ if (config->p2p_go_ht40)
+ fprintf(f, "p2p_go_ht40=%d\n", config->p2p_go_ht40);
+ if (config->p2p_go_vht)
+ fprintf(f, "p2p_go_vht=%d\n", config->p2p_go_vht);
+ if (config->p2p_go_ctwindow != DEFAULT_P2P_GO_CTWINDOW)
+ fprintf(f, "p2p_go_ctwindow=%d\n", config->p2p_go_ctwindow);
+ if (config->p2p_disabled)
+ fprintf(f, "p2p_disabled=%d\n", config->p2p_disabled);
+ if (config->p2p_no_group_iface)
+ fprintf(f, "p2p_no_group_iface=%d\n",
+ config->p2p_no_group_iface);
+ if (config->p2p_ignore_shared_freq)
+ fprintf(f, "p2p_ignore_shared_freq=%d\n",
+ config->p2p_ignore_shared_freq);
+ if (config->p2p_cli_probe)
+ fprintf(f, "p2p_cli_probe=%d\n", config->p2p_cli_probe);
+ if (config->p2p_go_freq_change_policy != DEFAULT_P2P_GO_FREQ_MOVE)
+ fprintf(f, "p2p_go_freq_change_policy=%u\n",
+ config->p2p_go_freq_change_policy);
+#endif /* CONFIG_P2P */
+ if (config->country[0] && config->country[1]) {
+ fprintf(f, "country=%c%c\n",
+ config->country[0], config->country[1]);
+ }
+ if (config->bss_max_count != DEFAULT_BSS_MAX_COUNT)
+ fprintf(f, "bss_max_count=%u\n", config->bss_max_count);
+ if (config->bss_expiration_age != DEFAULT_BSS_EXPIRATION_AGE)
+ fprintf(f, "bss_expiration_age=%u\n",
+ config->bss_expiration_age);
+ if (config->bss_expiration_scan_count !=
+ DEFAULT_BSS_EXPIRATION_SCAN_COUNT)
+ fprintf(f, "bss_expiration_scan_count=%u\n",
+ config->bss_expiration_scan_count);
+ if (config->filter_ssids)
+ fprintf(f, "filter_ssids=%d\n", config->filter_ssids);
+ if (config->max_num_sta != DEFAULT_MAX_NUM_STA)
+ fprintf(f, "max_num_sta=%u\n", config->max_num_sta);
+ if (config->disassoc_low_ack)
+ fprintf(f, "disassoc_low_ack=%d\n", config->disassoc_low_ack);
+#ifdef CONFIG_HS20
+ if (config->hs20)
+ fprintf(f, "hs20=1\n");
+#endif /* CONFIG_HS20 */
+#ifdef CONFIG_INTERWORKING
+ if (config->interworking)
+ fprintf(f, "interworking=%d\n", config->interworking);
+ if (!is_zero_ether_addr(config->hessid))
+ fprintf(f, "hessid=" MACSTR "\n", MAC2STR(config->hessid));
+ if (config->access_network_type != DEFAULT_ACCESS_NETWORK_TYPE)
+ fprintf(f, "access_network_type=%d\n",
+ config->access_network_type);
+#endif /* CONFIG_INTERWORKING */
+ if (config->pbc_in_m1)
+ fprintf(f, "pbc_in_m1=%d\n", config->pbc_in_m1);
+ if (config->wps_nfc_pw_from_config) {
+ if (config->wps_nfc_dev_pw_id)
+ fprintf(f, "wps_nfc_dev_pw_id=%d\n",
+ config->wps_nfc_dev_pw_id);
+ write_global_bin(f, "wps_nfc_dh_pubkey",
+ config->wps_nfc_dh_pubkey);
+ write_global_bin(f, "wps_nfc_dh_privkey",
+ config->wps_nfc_dh_privkey);
+ write_global_bin(f, "wps_nfc_dev_pw", config->wps_nfc_dev_pw);
+ }
+
+ if (config->ext_password_backend)
+ fprintf(f, "ext_password_backend=%s\n",
+ config->ext_password_backend);
+ if (config->p2p_go_max_inactivity != DEFAULT_P2P_GO_MAX_INACTIVITY)
+ fprintf(f, "p2p_go_max_inactivity=%d\n",
+ config->p2p_go_max_inactivity);
+ if (config->auto_interworking)
+ fprintf(f, "auto_interworking=%d\n",
+ config->auto_interworking);
+ if (config->okc)
+ fprintf(f, "okc=%d\n", config->okc);
+ if (config->pmf)
+ fprintf(f, "pmf=%d\n", config->pmf);
+ if (config->dtim_period)
+ fprintf(f, "dtim_period=%d\n", config->dtim_period);
+ if (config->beacon_int)
+ fprintf(f, "beacon_int=%d\n", config->beacon_int);
+
+ if (config->sae_groups) {
+ int i;
+ fprintf(f, "sae_groups=");
+ for (i = 0; config->sae_groups[i] >= 0; i++) {
+ fprintf(f, "%s%d", i > 0 ? " " : "",
+ config->sae_groups[i]);
+ }
+ fprintf(f, "\n");
+ }
+
+ if (config->ap_vendor_elements) {
+ int i, len = wpabuf_len(config->ap_vendor_elements);
+ const u8 *p = wpabuf_head_u8(config->ap_vendor_elements);
+ if (len > 0) {
+ fprintf(f, "ap_vendor_elements=");
+ for (i = 0; i < len; i++)
+ fprintf(f, "%02x", *p++);
+ fprintf(f, "\n");
+ }
+ }
+
+ if (config->ignore_old_scan_res)
+ fprintf(f, "ignore_old_scan_res=%d\n",
+ config->ignore_old_scan_res);
+
+ if (config->freq_list && config->freq_list[0]) {
+ int i;
+ fprintf(f, "freq_list=");
+ for (i = 0; config->freq_list[i]; i++) {
+ fprintf(f, "%s%d", i > 0 ? " " : "",
+ config->freq_list[i]);
+ }
+ fprintf(f, "\n");
+ }
+ if (config->scan_cur_freq != DEFAULT_SCAN_CUR_FREQ)
+ fprintf(f, "scan_cur_freq=%d\n", config->scan_cur_freq);
+
+ if (config->sched_scan_interval)
+ fprintf(f, "sched_scan_interval=%u\n",
+ config->sched_scan_interval);
+
+ if (config->external_sim)
+ fprintf(f, "external_sim=%d\n", config->external_sim);
+
+ if (config->tdls_external_control)
+ fprintf(f, "tdls_external_control=%d\n",
+ config->tdls_external_control);
+
+ if (config->wowlan_triggers)
+ fprintf(f, "wowlan_triggers=%s\n",
+ config->wowlan_triggers);
+
+ if (config->bgscan)
+ fprintf(f, "bgscan=\"%s\"\n", config->bgscan);
+
+ if (config->p2p_search_delay != DEFAULT_P2P_SEARCH_DELAY)
+ fprintf(f, "p2p_search_delay=%u\n",
+ config->p2p_search_delay);
+
+ if (config->mac_addr)
+ fprintf(f, "mac_addr=%d\n", config->mac_addr);
+
+ if (config->rand_addr_lifetime != DEFAULT_RAND_ADDR_LIFETIME)
+ fprintf(f, "rand_addr_lifetime=%u\n",
+ config->rand_addr_lifetime);
+
+ if (config->preassoc_mac_addr)
+ fprintf(f, "preassoc_mac_addr=%d\n", config->preassoc_mac_addr);
+
+ if (config->key_mgmt_offload != DEFAULT_KEY_MGMT_OFFLOAD)
+ fprintf(f, "key_mgmt_offload=%d\n", config->key_mgmt_offload);
+
+ if (config->user_mpm != DEFAULT_USER_MPM)
+ fprintf(f, "user_mpm=%d\n", config->user_mpm);
+
+ if (config->max_peer_links != DEFAULT_MAX_PEER_LINKS)
+ fprintf(f, "max_peer_links=%d\n", config->max_peer_links);
+
+ if (config->cert_in_cb != DEFAULT_CERT_IN_CB)
+ fprintf(f, "cert_in_cb=%d\n", config->cert_in_cb);
+
+ if (config->mesh_max_inactivity != DEFAULT_MESH_MAX_INACTIVITY)
+ fprintf(f, "mesh_max_inactivity=%d\n",
+ config->mesh_max_inactivity);
+
+ if (config->dot11RSNASAERetransPeriod !=
+ DEFAULT_DOT11_RSNA_SAE_RETRANS_PERIOD)
+ fprintf(f, "dot11RSNASAERetransPeriod=%d\n",
+ config->dot11RSNASAERetransPeriod);
+
+ if (config->passive_scan)
+ fprintf(f, "passive_scan=%d\n", config->passive_scan);
+
+ if (config->reassoc_same_bss_optim)
+ fprintf(f, "reassoc_same_bss_optim=%d\n",
+ config->reassoc_same_bss_optim);
+
+ if (config->wps_priority)
+ fprintf(f, "wps_priority=%d\n", config->wps_priority);
+}
+
+#endif /* CONFIG_NO_CONFIG_WRITE */
+
+
+int wpa_config_write(const char *name, struct wpa_config *config)
+{
+#ifndef CONFIG_NO_CONFIG_WRITE
+ FILE *f;
+ struct wpa_ssid *ssid;
+ struct wpa_cred *cred;
+#ifndef CONFIG_NO_CONFIG_BLOBS
+ struct wpa_config_blob *blob;
+#endif /* CONFIG_NO_CONFIG_BLOBS */
+ int ret = 0;
+ const char *orig_name = name;
+ int tmp_len = os_strlen(name) + 5; /* allow space for .tmp suffix */
+ char *tmp_name = os_malloc(tmp_len);
+
+ if (tmp_name) {
+ os_snprintf(tmp_name, tmp_len, "%s.tmp", name);
+ name = tmp_name;
+ }
+
+ wpa_printf(MSG_DEBUG, "Writing configuration file '%s'", name);
+
+ f = fopen(name, "w");
+ if (f == NULL) {
+ wpa_printf(MSG_DEBUG, "Failed to open '%s' for writing", name);
+ os_free(tmp_name);
+ return -1;
+ }
+
+ wpa_config_write_global(f, config);
+
+ for (cred = config->cred; cred; cred = cred->next) {
+ if (cred->temporary)
+ continue;
+ fprintf(f, "\ncred={\n");
+ wpa_config_write_cred(f, cred);
+ fprintf(f, "}\n");
+ }
+
+ for (ssid = config->ssid; ssid; ssid = ssid->next) {
+ if (ssid->key_mgmt == WPA_KEY_MGMT_WPS || ssid->temporary)
+ continue; /* do not save temporary networks */
+ if (wpa_key_mgmt_wpa_psk(ssid->key_mgmt) && !ssid->psk_set &&
+ !ssid->passphrase)
+ continue; /* do not save invalid network */
+ fprintf(f, "\nnetwork={\n");
+ wpa_config_write_network(f, ssid);
+ fprintf(f, "}\n");
+ }
+
+#ifndef CONFIG_NO_CONFIG_BLOBS
+ for (blob = config->blobs; blob; blob = blob->next) {
+ ret = wpa_config_write_blob(f, blob);
+ if (ret)
+ break;
+ }
+#endif /* CONFIG_NO_CONFIG_BLOBS */
+
+ os_fdatasync(f);
+
+ fclose(f);
+
+ if (tmp_name) {
+ int chmod_ret = 0;
+
+#ifdef ANDROID
+ chmod_ret = chmod(tmp_name,
+ S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP);
+#endif /* ANDROID */
+ if (chmod_ret != 0 || rename(tmp_name, orig_name) != 0)
+ ret = -1;
+
+ os_free(tmp_name);
+ }
+
+ wpa_printf(MSG_DEBUG, "Configuration file '%s' written %ssuccessfully",
+ orig_name, ret ? "un" : "");
+ return ret;
+#else /* CONFIG_NO_CONFIG_WRITE */
+ return -1;
+#endif /* CONFIG_NO_CONFIG_WRITE */
+}
diff --git a/freebsd/contrib/wpa/wpa_supplicant/config_ssid.h b/freebsd/contrib/wpa/wpa_supplicant/config_ssid.h
new file mode 100644
index 00000000..7ef326cf
--- /dev/null
+++ b/freebsd/contrib/wpa/wpa_supplicant/config_ssid.h
@@ -0,0 +1,724 @@
+/*
+ * WPA Supplicant / Network configuration structures
+ * Copyright (c) 2003-2013, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef CONFIG_SSID_H
+#define CONFIG_SSID_H
+
+#include "common/defs.h"
+#include "utils/list.h"
+#include "eap_peer/eap_config.h"
+
+
+#define DEFAULT_EAP_WORKAROUND ((unsigned int) -1)
+#define DEFAULT_EAPOL_FLAGS (EAPOL_FLAG_REQUIRE_KEY_UNICAST | \
+ EAPOL_FLAG_REQUIRE_KEY_BROADCAST)
+#define DEFAULT_PROTO (WPA_PROTO_WPA | WPA_PROTO_RSN)
+#define DEFAULT_KEY_MGMT (WPA_KEY_MGMT_PSK | WPA_KEY_MGMT_IEEE8021X)
+#define DEFAULT_PAIRWISE (WPA_CIPHER_CCMP | WPA_CIPHER_TKIP)
+#define DEFAULT_GROUP (WPA_CIPHER_CCMP | WPA_CIPHER_TKIP)
+#define DEFAULT_FRAGMENT_SIZE 1398
+
+#define DEFAULT_BG_SCAN_PERIOD -1
+#define DEFAULT_MESH_MAX_RETRIES 2
+#define DEFAULT_MESH_RETRY_TIMEOUT 40
+#define DEFAULT_MESH_CONFIRM_TIMEOUT 40
+#define DEFAULT_MESH_HOLDING_TIMEOUT 40
+#define DEFAULT_DISABLE_HT 0
+#define DEFAULT_DISABLE_HT40 0
+#define DEFAULT_DISABLE_SGI 0
+#define DEFAULT_DISABLE_LDPC 0
+#define DEFAULT_DISABLE_MAX_AMSDU -1 /* no change */
+#define DEFAULT_AMPDU_FACTOR -1 /* no change */
+#define DEFAULT_AMPDU_DENSITY -1 /* no change */
+#define DEFAULT_USER_SELECTED_SIM 1
+
+struct psk_list_entry {
+ struct dl_list list;
+ u8 addr[ETH_ALEN];
+ u8 psk[32];
+ u8 p2p;
+};
+
+/**
+ * struct wpa_ssid - Network configuration data
+ *
+ * This structure includes all the configuration variables for a network. This
+ * data is included in the per-interface configuration data as an element of
+ * the network list, struct wpa_config::ssid. Each network block in the
+ * configuration is mapped to a struct wpa_ssid instance.
+ */
+struct wpa_ssid {
+ /**
+ * next - Next network in global list
+ *
+ * This pointer can be used to iterate over all networks. The head of
+ * this list is stored in the ssid field of struct wpa_config.
+ */
+ struct wpa_ssid *next;
+
+ /**
+ * pnext - Next network in per-priority list
+ *
+ * This pointer can be used to iterate over all networks in the same
+ * priority class. The heads of these list are stored in the pssid
+ * fields of struct wpa_config.
+ */
+ struct wpa_ssid *pnext;
+
+ /**
+ * id - Unique id for the network
+ *
+ * This identifier is used as a unique identifier for each network
+ * block when using the control interface. Each network is allocated an
+ * id when it is being created, either when reading the configuration
+ * file or when a new network is added through the control interface.
+ */
+ int id;
+
+ /**
+ * priority - Priority group
+ *
+ * By default, all networks will get same priority group (0). If some
+ * of the networks are more desirable, this field can be used to change
+ * the order in which wpa_supplicant goes through the networks when
+ * selecting a BSS. The priority groups will be iterated in decreasing
+ * priority (i.e., the larger the priority value, the sooner the
+ * network is matched against the scan results). Within each priority
+ * group, networks will be selected based on security policy, signal
+ * strength, etc.
+ *
+ * Please note that AP scanning with scan_ssid=1 and ap_scan=2 mode are
+ * not using this priority to select the order for scanning. Instead,
+ * they try the networks in the order that used in the configuration
+ * file.
+ */
+ int priority;
+
+ /**
+ * ssid - Service set identifier (network name)
+ *
+ * This is the SSID for the network. For wireless interfaces, this is
+ * used to select which network will be used. If set to %NULL (or
+ * ssid_len=0), any SSID can be used. For wired interfaces, this must
+ * be set to %NULL. Note: SSID may contain any characters, even nul
+ * (ASCII 0) and as such, this should not be assumed to be a nul
+ * terminated string. ssid_len defines how many characters are valid
+ * and the ssid field is not guaranteed to be nul terminated.
+ */
+ u8 *ssid;
+
+ /**
+ * ssid_len - Length of the SSID
+ */
+ size_t ssid_len;
+
+ /**
+ * bssid - BSSID
+ *
+ * If set, this network block is used only when associating with the AP
+ * using the configured BSSID
+ *
+ * If this is a persistent P2P group (disabled == 2), this is the GO
+ * Device Address.
+ */
+ u8 bssid[ETH_ALEN];
+
+ /**
+ * bssid_blacklist - List of inacceptable BSSIDs
+ */
+ u8 *bssid_blacklist;
+ size_t num_bssid_blacklist;
+
+ /**
+ * bssid_blacklist - List of acceptable BSSIDs
+ */
+ u8 *bssid_whitelist;
+ size_t num_bssid_whitelist;
+
+ /**
+ * bssid_set - Whether BSSID is configured for this network
+ */
+ int bssid_set;
+
+ /**
+ * go_p2p_dev_addr - GO's P2P Device Address or all zeros if not set
+ */
+ u8 go_p2p_dev_addr[ETH_ALEN];
+
+ /**
+ * psk - WPA pre-shared key (256 bits)
+ */
+ u8 psk[32];
+
+ /**
+ * psk_set - Whether PSK field is configured
+ */
+ int psk_set;
+
+ /**
+ * passphrase - WPA ASCII passphrase
+ *
+ * If this is set, psk will be generated using the SSID and passphrase
+ * configured for the network. ASCII passphrase must be between 8 and
+ * 63 characters (inclusive).
+ */
+ char *passphrase;
+
+ /**
+ * ext_psk - PSK/passphrase name in external storage
+ *
+ * If this is set, PSK/passphrase will be fetched from external storage
+ * when requesting association with the network.
+ */
+ char *ext_psk;
+
+ /**
+ * mem_only_psk - Whether to keep PSK/passphrase only in memory
+ *
+ * 0 = allow psk/passphrase to be stored to the configuration file
+ * 1 = do not store psk/passphrase to the configuration file
+ */
+ int mem_only_psk;
+
+ /**
+ * pairwise_cipher - Bitfield of allowed pairwise ciphers, WPA_CIPHER_*
+ */
+ int pairwise_cipher;
+
+ /**
+ * group_cipher - Bitfield of allowed group ciphers, WPA_CIPHER_*
+ */
+ int group_cipher;
+
+ /**
+ * key_mgmt - Bitfield of allowed key management protocols
+ *
+ * WPA_KEY_MGMT_*
+ */
+ int key_mgmt;
+
+ /**
+ * bg_scan_period - Background scan period in seconds, 0 to disable, or
+ * -1 to indicate no change to default driver configuration
+ */
+ int bg_scan_period;
+
+ /**
+ * proto - Bitfield of allowed protocols, WPA_PROTO_*
+ */
+ int proto;
+
+ /**
+ * auth_alg - Bitfield of allowed authentication algorithms
+ *
+ * WPA_AUTH_ALG_*
+ */
+ int auth_alg;
+
+ /**
+ * scan_ssid - Scan this SSID with Probe Requests
+ *
+ * scan_ssid can be used to scan for APs using hidden SSIDs.
+ * Note: Many drivers do not support this. ap_mode=2 can be used with
+ * such drivers to use hidden SSIDs. Note2: Most nl80211-based drivers
+ * do support scan_ssid=1 and that should be used with them instead of
+ * ap_scan=2.
+ */
+ int scan_ssid;
+
+#ifdef IEEE8021X_EAPOL
+#define EAPOL_FLAG_REQUIRE_KEY_UNICAST BIT(0)
+#define EAPOL_FLAG_REQUIRE_KEY_BROADCAST BIT(1)
+ /**
+ * eapol_flags - Bit field of IEEE 802.1X/EAPOL options (EAPOL_FLAG_*)
+ */
+ int eapol_flags;
+
+ /**
+ * eap - EAP peer configuration for this network
+ */
+ struct eap_peer_config eap;
+#endif /* IEEE8021X_EAPOL */
+
+#define NUM_WEP_KEYS 4
+#define MAX_WEP_KEY_LEN 16
+ /**
+ * wep_key - WEP keys
+ */
+ u8 wep_key[NUM_WEP_KEYS][MAX_WEP_KEY_LEN];
+
+ /**
+ * wep_key_len - WEP key lengths
+ */
+ size_t wep_key_len[NUM_WEP_KEYS];
+
+ /**
+ * wep_tx_keyidx - Default key index for TX frames using WEP
+ */
+ int wep_tx_keyidx;
+
+ /**
+ * proactive_key_caching - Enable proactive key caching
+ *
+ * This field can be used to enable proactive key caching which is also
+ * known as opportunistic PMKSA caching for WPA2. This is disabled (0)
+ * by default unless default value is changed with the global okc=1
+ * parameter. Enable by setting this to 1.
+ *
+ * Proactive key caching is used to make supplicant assume that the APs
+ * are using the same PMK and generate PMKSA cache entries without
+ * doing RSN pre-authentication. This requires support from the AP side
+ * and is normally used with wireless switches that co-locate the
+ * authenticator.
+ *
+ * Internally, special value -1 is used to indicate that the parameter
+ * was not specified in the configuration (i.e., default behavior is
+ * followed).
+ */
+ int proactive_key_caching;
+
+ /**
+ * mixed_cell - Whether mixed cells are allowed
+ *
+ * This option can be used to configure whether so called mixed cells,
+ * i.e., networks that use both plaintext and encryption in the same
+ * SSID, are allowed. This is disabled (0) by default. Enable by
+ * setting this to 1.
+ */
+ int mixed_cell;
+
+#ifdef IEEE8021X_EAPOL
+
+ /**
+ * leap - Number of EAP methods using LEAP
+ *
+ * This field should be set to 1 if LEAP is enabled. This is used to
+ * select IEEE 802.11 authentication algorithm.
+ */
+ int leap;
+
+ /**
+ * non_leap - Number of EAP methods not using LEAP
+ *
+ * This field should be set to >0 if any EAP method other than LEAP is
+ * enabled. This is used to select IEEE 802.11 authentication
+ * algorithm.
+ */
+ int non_leap;
+
+ /**
+ * eap_workaround - EAP workarounds enabled
+ *
+ * wpa_supplicant supports number of "EAP workarounds" to work around
+ * interoperability issues with incorrectly behaving authentication
+ * servers. This is recommended to be enabled by default because some
+ * of the issues are present in large number of authentication servers.
+ *
+ * Strict EAP conformance mode can be configured by disabling
+ * workarounds with eap_workaround = 0.
+ */
+ unsigned int eap_workaround;
+
+#endif /* IEEE8021X_EAPOL */
+
+ /**
+ * mode - IEEE 802.11 operation mode (Infrastucture/IBSS)
+ *
+ * 0 = infrastructure (Managed) mode, i.e., associate with an AP.
+ *
+ * 1 = IBSS (ad-hoc, peer-to-peer)
+ *
+ * 2 = AP (access point)
+ *
+ * 3 = P2P Group Owner (can be set in the configuration file)
+ *
+ * 4 = P2P Group Formation (used internally; not in configuration
+ * files)
+ *
+ * 5 = Mesh
+ *
+ * Note: IBSS can only be used with key_mgmt NONE (plaintext and static
+ * WEP) and WPA-PSK (with proto=RSN). In addition, key_mgmt=WPA-NONE
+ * (fixed group key TKIP/CCMP) is available for backwards compatibility,
+ * but its use is deprecated. WPA-None requires following network block
+ * options: proto=WPA, key_mgmt=WPA-NONE, pairwise=NONE, group=TKIP (or
+ * CCMP, but not both), and psk must also be set (either directly or
+ * using ASCII passphrase).
+ */
+ enum wpas_mode {
+ WPAS_MODE_INFRA = 0,
+ WPAS_MODE_IBSS = 1,
+ WPAS_MODE_AP = 2,
+ WPAS_MODE_P2P_GO = 3,
+ WPAS_MODE_P2P_GROUP_FORMATION = 4,
+ WPAS_MODE_MESH = 5,
+ } mode;
+
+ /**
+ * disabled - Whether this network is currently disabled
+ *
+ * 0 = this network can be used (default).
+ * 1 = this network block is disabled (can be enabled through
+ * ctrl_iface, e.g., with wpa_cli or wpa_gui).
+ * 2 = this network block includes parameters for a persistent P2P
+ * group (can be used with P2P ctrl_iface commands)
+ */
+ int disabled;
+
+ /**
+ * disabled_for_connect - Whether this network was temporarily disabled
+ *
+ * This flag is used to reenable all the temporarily disabled networks
+ * after either the success or failure of a WPS connection.
+ */
+ int disabled_for_connect;
+
+ /**
+ * peerkey - Whether PeerKey handshake for direct links is allowed
+ *
+ * This is only used when both RSN/WPA2 and IEEE 802.11e (QoS) are
+ * enabled.
+ *
+ * 0 = disabled (default)
+ * 1 = enabled
+ */
+ int peerkey;
+
+ /**
+ * id_str - Network identifier string for external scripts
+ *
+ * This value is passed to external ctrl_iface monitors in
+ * WPA_EVENT_CONNECTED event and wpa_cli sets this as WPA_ID_STR
+ * environment variable for action scripts.
+ */
+ char *id_str;
+
+#ifdef CONFIG_IEEE80211W
+ /**
+ * ieee80211w - Whether management frame protection is enabled
+ *
+ * This value is used to configure policy for management frame
+ * protection (IEEE 802.11w). 0 = disabled, 1 = optional, 2 = required.
+ * This is disabled by default unless the default value has been changed
+ * with the global pmf=1/2 parameter.
+ *
+ * Internally, special value 3 is used to indicate that the parameter
+ * was not specified in the configuration (i.e., default behavior is
+ * followed).
+ */
+ enum mfp_options ieee80211w;
+#endif /* CONFIG_IEEE80211W */
+
+ /**
+ * frequency - Channel frequency in megahertz (MHz) for IBSS
+ *
+ * This value is used to configure the initial channel for IBSS (adhoc)
+ * networks, e.g., 2412 = IEEE 802.11b/g channel 1. It is ignored in
+ * the infrastructure mode. In addition, this value is only used by the
+ * station that creates the IBSS. If an IBSS network with the
+ * configured SSID is already present, the frequency of the network
+ * will be used instead of this configured value.
+ */
+ int frequency;
+
+ /**
+ * fixed_freq - Use fixed frequency for IBSS
+ */
+ int fixed_freq;
+
+ /**
+ * mesh_basic_rates - BSS Basic rate set for mesh network
+ *
+ */
+ int *mesh_basic_rates;
+
+ /**
+ * Mesh network plink parameters
+ */
+ int dot11MeshMaxRetries;
+ int dot11MeshRetryTimeout; /* msec */
+ int dot11MeshConfirmTimeout; /* msec */
+ int dot11MeshHoldingTimeout; /* msec */
+
+ int ht40;
+
+ int vht;
+
+ /**
+ * wpa_ptk_rekey - Maximum lifetime for PTK in seconds
+ *
+ * This value can be used to enforce rekeying of PTK to mitigate some
+ * attacks against TKIP deficiencies.
+ */
+ int wpa_ptk_rekey;
+
+ /**
+ * scan_freq - Array of frequencies to scan or %NULL for all
+ *
+ * This is an optional zero-terminated array of frequencies in
+ * megahertz (MHz) to include in scan requests when searching for this
+ * network. This can be used to speed up scanning when the network is
+ * known to not use all possible channels.
+ */
+ int *scan_freq;
+
+ /**
+ * bgscan - Background scan and roaming parameters or %NULL if none
+ *
+ * This is an optional set of parameters for background scanning and
+ * roaming within a network (ESS) in following format:
+ * <bgscan module name>:<module parameters>
+ */
+ char *bgscan;
+
+ /**
+ * ignore_broadcast_ssid - Hide SSID in AP mode
+ *
+ * Send empty SSID in beacons and ignore probe request frames that do
+ * not specify full SSID, i.e., require stations to know SSID.
+ * default: disabled (0)
+ * 1 = send empty (length=0) SSID in beacon and ignore probe request
+ * for broadcast SSID
+ * 2 = clear SSID (ASCII 0), but keep the original length (this may be
+ * required with some clients that do not support empty SSID) and
+ * ignore probe requests for broadcast SSID
+ */
+ int ignore_broadcast_ssid;
+
+ /**
+ * freq_list - Array of allowed frequencies or %NULL for all
+ *
+ * This is an optional zero-terminated array of frequencies in
+ * megahertz (MHz) to allow for selecting the BSS. If set, scan results
+ * that do not match any of the specified frequencies are not
+ * considered when selecting a BSS.
+ */
+ int *freq_list;
+
+ /**
+ * p2p_client_list - List of P2P Clients in a persistent group (GO)
+ *
+ * This is a list of P2P Clients (P2P Device Address) that have joined
+ * the persistent group. This is maintained on the GO for persistent
+ * group entries (disabled == 2).
+ */
+ u8 *p2p_client_list;
+
+ /**
+ * num_p2p_clients - Number of entries in p2p_client_list
+ */
+ size_t num_p2p_clients;
+
+#ifndef P2P_MAX_STORED_CLIENTS
+#define P2P_MAX_STORED_CLIENTS 100
+#endif /* P2P_MAX_STORED_CLIENTS */
+
+ /**
+ * psk_list - Per-client PSKs (struct psk_list_entry)
+ */
+ struct dl_list psk_list;
+
+ /**
+ * p2p_group - Network generated as a P2P group (used internally)
+ */
+ int p2p_group;
+
+ /**
+ * p2p_persistent_group - Whether this is a persistent group
+ */
+ int p2p_persistent_group;
+
+ /**
+ * temporary - Whether this network is temporary and not to be saved
+ */
+ int temporary;
+
+ /**
+ * export_keys - Whether keys may be exported
+ *
+ * This attribute will be set when keys are determined through
+ * WPS or similar so that they may be exported.
+ */
+ int export_keys;
+
+#ifdef CONFIG_HT_OVERRIDES
+ /**
+ * disable_ht - Disable HT (IEEE 802.11n) for this network
+ *
+ * By default, use it if it is available, but this can be configured
+ * to 1 to have it disabled.
+ */
+ int disable_ht;
+
+ /**
+ * disable_ht40 - Disable HT40 for this network
+ *
+ * By default, use it if it is available, but this can be configured
+ * to 1 to have it disabled.
+ */
+ int disable_ht40;
+
+ /**
+ * disable_sgi - Disable SGI (Short Guard Interval) for this network
+ *
+ * By default, use it if it is available, but this can be configured
+ * to 1 to have it disabled.
+ */
+ int disable_sgi;
+
+ /**
+ * disable_ldpc - Disable LDPC for this network
+ *
+ * By default, use it if it is available, but this can be configured
+ * to 1 to have it disabled.
+ */
+ int disable_ldpc;
+
+ /**
+ * ht40_intolerant - Indicate 40 MHz intolerant for this network
+ */
+ int ht40_intolerant;
+
+ /**
+ * disable_max_amsdu - Disable MAX A-MSDU
+ *
+ * A-MDSU will be 3839 bytes when disabled, or 7935
+ * when enabled (assuming it is otherwise supported)
+ * -1 (default) means do not apply any settings to the kernel.
+ */
+ int disable_max_amsdu;
+
+ /**
+ * ampdu_factor - Maximum A-MPDU Length Exponent
+ *
+ * Value: 0-3, see 7.3.2.56.3 in IEEE Std 802.11n-2009.
+ */
+ int ampdu_factor;
+
+ /**
+ * ampdu_density - Minimum A-MPDU Start Spacing
+ *
+ * Value: 0-7, see 7.3.2.56.3 in IEEE Std 802.11n-2009.
+ */
+ int ampdu_density;
+
+ /**
+ * ht_mcs - Allowed HT-MCS rates, in ASCII hex: ffff0000...
+ *
+ * By default (empty string): Use whatever the OS has configured.
+ */
+ char *ht_mcs;
+#endif /* CONFIG_HT_OVERRIDES */
+
+#ifdef CONFIG_VHT_OVERRIDES
+ /**
+ * disable_vht - Disable VHT (IEEE 802.11ac) for this network
+ *
+ * By default, use it if it is available, but this can be configured
+ * to 1 to have it disabled.
+ */
+ int disable_vht;
+
+ /**
+ * vht_capa - VHT capabilities to use
+ */
+ unsigned int vht_capa;
+
+ /**
+ * vht_capa_mask - mask for VHT capabilities
+ */
+ unsigned int vht_capa_mask;
+
+ int vht_rx_mcs_nss_1, vht_rx_mcs_nss_2,
+ vht_rx_mcs_nss_3, vht_rx_mcs_nss_4,
+ vht_rx_mcs_nss_5, vht_rx_mcs_nss_6,
+ vht_rx_mcs_nss_7, vht_rx_mcs_nss_8;
+ int vht_tx_mcs_nss_1, vht_tx_mcs_nss_2,
+ vht_tx_mcs_nss_3, vht_tx_mcs_nss_4,
+ vht_tx_mcs_nss_5, vht_tx_mcs_nss_6,
+ vht_tx_mcs_nss_7, vht_tx_mcs_nss_8;
+#endif /* CONFIG_VHT_OVERRIDES */
+
+ /**
+ * ap_max_inactivity - Timeout in seconds to detect STA's inactivity
+ *
+ * This timeout value is used in AP mode to clean up inactive stations.
+ * By default: 300 seconds.
+ */
+ int ap_max_inactivity;
+
+ /**
+ * dtim_period - DTIM period in Beacon intervals
+ * By default: 2
+ */
+ int dtim_period;
+
+ /**
+ * beacon_int - Beacon interval (default: 100 TU)
+ */
+ int beacon_int;
+
+ /**
+ * auth_failures - Number of consecutive authentication failures
+ */
+ unsigned int auth_failures;
+
+ /**
+ * disabled_until - Network block disabled until this time if non-zero
+ */
+ struct os_reltime disabled_until;
+
+ /**
+ * parent_cred - Pointer to parent wpa_cred entry
+ *
+ * This pointer can be used to delete temporary networks when a wpa_cred
+ * that was used to create them is removed. This pointer should not be
+ * dereferences since it may not be updated in all cases.
+ */
+ void *parent_cred;
+
+#ifdef CONFIG_MACSEC
+ /**
+ * macsec_policy - Determines the policy for MACsec secure session
+ *
+ * 0: MACsec not in use (default)
+ * 1: MACsec enabled - Should secure, accept key server's advice to
+ * determine whether to use a secure session or not.
+ */
+ int macsec_policy;
+#endif /* CONFIG_MACSEC */
+
+#ifdef CONFIG_HS20
+ int update_identifier;
+#endif /* CONFIG_HS20 */
+
+ unsigned int wps_run;
+
+ /**
+ * mac_addr - MAC address policy
+ *
+ * 0 = use permanent MAC address
+ * 1 = use random MAC address for each ESS connection
+ * 2 = like 1, but maintain OUI (with local admin bit set)
+ *
+ * Internally, special value -1 is used to indicate that the parameter
+ * was not specified in the configuration (i.e., default behavior is
+ * followed).
+ */
+ int mac_addr;
+
+ /**
+ * no_auto_peer - Do not automatically peer with compatible mesh peers
+ *
+ * When unset, the reception of a beacon from a another mesh peer in
+ * this MBSS will trigger a peering attempt.
+ */
+ int no_auto_peer;
+};
+
+#endif /* CONFIG_SSID_H */
diff --git a/freebsd/contrib/wpa/wpa_supplicant/ctrl_iface.c b/freebsd/contrib/wpa/wpa_supplicant/ctrl_iface.c
new file mode 100644
index 00000000..b4da3f0a
--- /dev/null
+++ b/freebsd/contrib/wpa/wpa_supplicant/ctrl_iface.c
@@ -0,0 +1,9480 @@
+#include <machine/rtems-bsd-user-space.h>
+
+/*
+ * WPA Supplicant / Control interface (shared code for all backends)
+ * Copyright (c) 2004-2015, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+#ifdef CONFIG_TESTING_OPTIONS
+#include <net/ethernet.h>
+#include <netinet/ip.h>
+#endif /* CONFIG_TESTING_OPTIONS */
+
+#include "utils/common.h"
+#include "utils/eloop.h"
+#include "utils/uuid.h"
+#include "common/version.h"
+#include "common/ieee802_11_defs.h"
+#include "common/ieee802_11_common.h"
+#include "common/wpa_ctrl.h"
+#include "crypto/tls.h"
+#include "ap/hostapd.h"
+#include "eap_peer/eap.h"
+#include "eapol_supp/eapol_supp_sm.h"
+#include "rsn_supp/wpa.h"
+#include "rsn_supp/preauth.h"
+#include "rsn_supp/pmksa_cache.h"
+#include "l2_packet/l2_packet.h"
+#include "wps/wps.h"
+#include "fst/fst.h"
+#include "fst/fst_ctrl_iface.h"
+#include "config.h"
+#include "wpa_supplicant_i.h"
+#include "driver_i.h"
+#include "wps_supplicant.h"
+#include "ibss_rsn.h"
+#include "ap.h"
+#include "p2p_supplicant.h"
+#include "p2p/p2p.h"
+#include "hs20_supplicant.h"
+#include "wifi_display.h"
+#include "notify.h"
+#include "bss.h"
+#include "scan.h"
+#include "ctrl_iface.h"
+#include "interworking.h"
+#include "blacklist.h"
+#include "autoscan.h"
+#include "wnm_sta.h"
+#include "offchannel.h"
+#include "drivers/driver.h"
+#include "mesh.h"
+
+static int wpa_supplicant_global_iface_list(struct wpa_global *global,
+ char *buf, int len);
+static int wpa_supplicant_global_iface_interfaces(struct wpa_global *global,
+ char *buf, int len);
+static int * freq_range_to_channel_list(struct wpa_supplicant *wpa_s,
+ char *val);
+
+static int set_bssid_filter(struct wpa_supplicant *wpa_s, char *val)
+{
+ char *pos;
+ u8 addr[ETH_ALEN], *filter = NULL, *n;
+ size_t count = 0;
+
+ pos = val;
+ while (pos) {
+ if (*pos == '\0')
+ break;
+ if (hwaddr_aton(pos, addr)) {
+ os_free(filter);
+ return -1;
+ }
+ n = os_realloc_array(filter, count + 1, ETH_ALEN);
+ if (n == NULL) {
+ os_free(filter);
+ return -1;
+ }
+ filter = n;
+ os_memcpy(filter + count * ETH_ALEN, addr, ETH_ALEN);
+ count++;
+
+ pos = os_strchr(pos, ' ');
+ if (pos)
+ pos++;
+ }
+
+ wpa_hexdump(MSG_DEBUG, "bssid_filter", filter, count * ETH_ALEN);
+ os_free(wpa_s->bssid_filter);
+ wpa_s->bssid_filter = filter;
+ wpa_s->bssid_filter_count = count;
+
+ return 0;
+}
+
+
+static int set_disallow_aps(struct wpa_supplicant *wpa_s, char *val)
+{
+ char *pos;
+ u8 addr[ETH_ALEN], *bssid = NULL, *n;
+ struct wpa_ssid_value *ssid = NULL, *ns;
+ size_t count = 0, ssid_count = 0;
+ struct wpa_ssid *c;
+
+ /*
+ * disallow_list ::= <ssid_spec> | <bssid_spec> | <disallow_list> | ""
+ * SSID_SPEC ::= ssid <SSID_HEX>
+ * BSSID_SPEC ::= bssid <BSSID_HEX>
+ */
+
+ pos = val;
+ while (pos) {
+ if (*pos == '\0')
+ break;
+ if (os_strncmp(pos, "bssid ", 6) == 0) {
+ int res;
+ pos += 6;
+ res = hwaddr_aton2(pos, addr);
+ if (res < 0) {
+ os_free(ssid);
+ os_free(bssid);
+ wpa_printf(MSG_DEBUG, "Invalid disallow_aps "
+ "BSSID value '%s'", pos);
+ return -1;
+ }
+ pos += res;
+ n = os_realloc_array(bssid, count + 1, ETH_ALEN);
+ if (n == NULL) {
+ os_free(ssid);
+ os_free(bssid);
+ return -1;
+ }
+ bssid = n;
+ os_memcpy(bssid + count * ETH_ALEN, addr, ETH_ALEN);
+ count++;
+ } else if (os_strncmp(pos, "ssid ", 5) == 0) {
+ char *end;
+ pos += 5;
+
+ end = pos;
+ while (*end) {
+ if (*end == '\0' || *end == ' ')
+ break;
+ end++;
+ }
+
+ ns = os_realloc_array(ssid, ssid_count + 1,
+ sizeof(struct wpa_ssid_value));
+ if (ns == NULL) {
+ os_free(ssid);
+ os_free(bssid);
+ return -1;
+ }
+ ssid = ns;
+
+ if ((end - pos) & 0x01 ||
+ end - pos > 2 * SSID_MAX_LEN ||
+ hexstr2bin(pos, ssid[ssid_count].ssid,
+ (end - pos) / 2) < 0) {
+ os_free(ssid);
+ os_free(bssid);
+ wpa_printf(MSG_DEBUG, "Invalid disallow_aps "
+ "SSID value '%s'", pos);
+ return -1;
+ }
+ ssid[ssid_count].ssid_len = (end - pos) / 2;
+ wpa_hexdump_ascii(MSG_DEBUG, "disallow_aps SSID",
+ ssid[ssid_count].ssid,
+ ssid[ssid_count].ssid_len);
+ ssid_count++;
+ pos = end;
+ } else {
+ wpa_printf(MSG_DEBUG, "Unexpected disallow_aps value "
+ "'%s'", pos);
+ os_free(ssid);
+ os_free(bssid);
+ return -1;
+ }
+
+ pos = os_strchr(pos, ' ');
+ if (pos)
+ pos++;
+ }
+
+ wpa_hexdump(MSG_DEBUG, "disallow_aps_bssid", bssid, count * ETH_ALEN);
+ os_free(wpa_s->disallow_aps_bssid);
+ wpa_s->disallow_aps_bssid = bssid;
+ wpa_s->disallow_aps_bssid_count = count;
+
+ wpa_printf(MSG_DEBUG, "disallow_aps_ssid_count %d", (int) ssid_count);
+ os_free(wpa_s->disallow_aps_ssid);
+ wpa_s->disallow_aps_ssid = ssid;
+ wpa_s->disallow_aps_ssid_count = ssid_count;
+
+ if (!wpa_s->current_ssid || wpa_s->wpa_state < WPA_AUTHENTICATING)
+ return 0;
+
+ c = wpa_s->current_ssid;
+ if (c->mode != WPAS_MODE_INFRA && c->mode != WPAS_MODE_IBSS)
+ return 0;
+
+ if (!disallowed_bssid(wpa_s, wpa_s->bssid) &&
+ !disallowed_ssid(wpa_s, c->ssid, c->ssid_len))
+ return 0;
+
+ wpa_printf(MSG_DEBUG, "Disconnect and try to find another network "
+ "because current AP was marked disallowed");
+
+#ifdef CONFIG_SME
+ wpa_s->sme.prev_bssid_set = 0;
+#endif /* CONFIG_SME */
+ wpa_s->reassociate = 1;
+ wpa_s->own_disconnect_req = 1;
+ wpa_supplicant_deauthenticate(wpa_s, WLAN_REASON_DEAUTH_LEAVING);
+ wpa_supplicant_req_scan(wpa_s, 0, 0);
+
+ return 0;
+}
+
+
+#ifndef CONFIG_NO_CONFIG_BLOBS
+static int wpas_ctrl_set_blob(struct wpa_supplicant *wpa_s, char *pos)
+{
+ char *name = pos;
+ struct wpa_config_blob *blob;
+ size_t len;
+
+ pos = os_strchr(pos, ' ');
+ if (pos == NULL)
+ return -1;
+ *pos++ = '\0';
+ len = os_strlen(pos);
+ if (len & 1)
+ return -1;
+
+ wpa_printf(MSG_DEBUG, "CTRL: Set blob '%s'", name);
+ blob = os_zalloc(sizeof(*blob));
+ if (blob == NULL)
+ return -1;
+ blob->name = os_strdup(name);
+ blob->data = os_malloc(len / 2);
+ if (blob->name == NULL || blob->data == NULL) {
+ wpa_config_free_blob(blob);
+ return -1;
+ }
+
+ if (hexstr2bin(pos, blob->data, len / 2) < 0) {
+ wpa_printf(MSG_DEBUG, "CTRL: Invalid blob hex data");
+ wpa_config_free_blob(blob);
+ return -1;
+ }
+ blob->len = len / 2;
+
+ wpa_config_set_blob(wpa_s->conf, blob);
+
+ return 0;
+}
+#endif /* CONFIG_NO_CONFIG_BLOBS */
+
+
+static int wpas_ctrl_pno(struct wpa_supplicant *wpa_s, char *cmd)
+{
+ char *params;
+ char *pos;
+ int *freqs = NULL;
+ int ret;
+
+ if (atoi(cmd)) {
+ params = os_strchr(cmd, ' ');
+ os_free(wpa_s->manual_sched_scan_freqs);
+ if (params) {
+ params++;
+ pos = os_strstr(params, "freq=");
+ if (pos)
+ freqs = freq_range_to_channel_list(wpa_s,
+ pos + 5);
+ }
+ wpa_s->manual_sched_scan_freqs = freqs;
+ ret = wpas_start_pno(wpa_s);
+ } else {
+ ret = wpas_stop_pno(wpa_s);
+ }
+ return ret;
+}
+
+
+static int wpas_ctrl_set_band(struct wpa_supplicant *wpa_s, char *band)
+{
+ union wpa_event_data event;
+
+ if (os_strcmp(band, "AUTO") == 0)
+ wpa_s->setband = WPA_SETBAND_AUTO;
+ else if (os_strcmp(band, "5G") == 0)
+ wpa_s->setband = WPA_SETBAND_5G;
+ else if (os_strcmp(band, "2G") == 0)
+ wpa_s->setband = WPA_SETBAND_2G;
+ else
+ return -1;
+
+ if (wpa_drv_setband(wpa_s, wpa_s->setband) == 0) {
+ os_memset(&event, 0, sizeof(event));
+ event.channel_list_changed.initiator = REGDOM_SET_BY_USER;
+ event.channel_list_changed.type = REGDOM_TYPE_UNKNOWN;
+ wpa_supplicant_event(wpa_s, EVENT_CHANNEL_LIST_CHANGED, &event);
+ }
+
+ return 0;
+}
+
+
+static int wpa_supplicant_ctrl_iface_set(struct wpa_supplicant *wpa_s,
+ char *cmd)
+{
+ char *value;
+ int ret = 0;
+
+ value = os_strchr(cmd, ' ');
+ if (value == NULL)
+ return -1;
+ *value++ = '\0';
+
+ wpa_printf(MSG_DEBUG, "CTRL_IFACE SET '%s'='%s'", cmd, value);
+ if (os_strcasecmp(cmd, "EAPOL::heldPeriod") == 0) {
+ eapol_sm_configure(wpa_s->eapol,
+ atoi(value), -1, -1, -1);
+ } else if (os_strcasecmp(cmd, "EAPOL::authPeriod") == 0) {
+ eapol_sm_configure(wpa_s->eapol,
+ -1, atoi(value), -1, -1);
+ } else if (os_strcasecmp(cmd, "EAPOL::startPeriod") == 0) {
+ eapol_sm_configure(wpa_s->eapol,
+ -1, -1, atoi(value), -1);
+ } else if (os_strcasecmp(cmd, "EAPOL::maxStart") == 0) {
+ eapol_sm_configure(wpa_s->eapol,
+ -1, -1, -1, atoi(value));
+ } else if (os_strcasecmp(cmd, "dot11RSNAConfigPMKLifetime") == 0) {
+ if (wpa_sm_set_param(wpa_s->wpa, RSNA_PMK_LIFETIME,
+ atoi(value)))
+ ret = -1;
+ } else if (os_strcasecmp(cmd, "dot11RSNAConfigPMKReauthThreshold") ==
+ 0) {
+ if (wpa_sm_set_param(wpa_s->wpa, RSNA_PMK_REAUTH_THRESHOLD,
+ atoi(value)))
+ ret = -1;
+ } else if (os_strcasecmp(cmd, "dot11RSNAConfigSATimeout") == 0) {
+ if (wpa_sm_set_param(wpa_s->wpa, RSNA_SA_TIMEOUT, atoi(value)))
+ ret = -1;
+ } else if (os_strcasecmp(cmd, "wps_fragment_size") == 0) {
+ wpa_s->wps_fragment_size = atoi(value);
+#ifdef CONFIG_WPS_TESTING
+ } else if (os_strcasecmp(cmd, "wps_version_number") == 0) {
+ long int val;
+ val = strtol(value, NULL, 0);
+ if (val < 0 || val > 0xff) {
+ ret = -1;
+ wpa_printf(MSG_DEBUG, "WPS: Invalid "
+ "wps_version_number %ld", val);
+ } else {
+ wps_version_number = val;
+ wpa_printf(MSG_DEBUG, "WPS: Testing - force WPS "
+ "version %u.%u",
+ (wps_version_number & 0xf0) >> 4,
+ wps_version_number & 0x0f);
+ }
+ } else if (os_strcasecmp(cmd, "wps_testing_dummy_cred") == 0) {
+ wps_testing_dummy_cred = atoi(value);
+ wpa_printf(MSG_DEBUG, "WPS: Testing - dummy_cred=%d",
+ wps_testing_dummy_cred);
+ } else if (os_strcasecmp(cmd, "wps_corrupt_pkhash") == 0) {
+ wps_corrupt_pkhash = atoi(value);
+ wpa_printf(MSG_DEBUG, "WPS: Testing - wps_corrupt_pkhash=%d",
+ wps_corrupt_pkhash);
+#endif /* CONFIG_WPS_TESTING */
+ } else if (os_strcasecmp(cmd, "ampdu") == 0) {
+ if (wpa_drv_ampdu(wpa_s, atoi(value)) < 0)
+ ret = -1;
+#ifdef CONFIG_TDLS
+#ifdef CONFIG_TDLS_TESTING
+ } else if (os_strcasecmp(cmd, "tdls_testing") == 0) {
+ extern unsigned int tdls_testing;
+ tdls_testing = strtol(value, NULL, 0);
+ wpa_printf(MSG_DEBUG, "TDLS: tdls_testing=0x%x", tdls_testing);
+#endif /* CONFIG_TDLS_TESTING */
+ } else if (os_strcasecmp(cmd, "tdls_disabled") == 0) {
+ int disabled = atoi(value);
+ wpa_printf(MSG_DEBUG, "TDLS: tdls_disabled=%d", disabled);
+ if (disabled) {
+ if (wpa_drv_tdls_oper(wpa_s, TDLS_DISABLE, NULL) < 0)
+ ret = -1;
+ } else if (wpa_drv_tdls_oper(wpa_s, TDLS_ENABLE, NULL) < 0)
+ ret = -1;
+ wpa_tdls_enable(wpa_s->wpa, !disabled);
+#endif /* CONFIG_TDLS */
+ } else if (os_strcasecmp(cmd, "pno") == 0) {
+ ret = wpas_ctrl_pno(wpa_s, value);
+ } else if (os_strcasecmp(cmd, "radio_disabled") == 0) {
+ int disabled = atoi(value);
+ if (wpa_drv_radio_disable(wpa_s, disabled) < 0)
+ ret = -1;
+ else if (disabled)
+ wpa_supplicant_set_state(wpa_s, WPA_INACTIVE);
+ } else if (os_strcasecmp(cmd, "uapsd") == 0) {
+ if (os_strcmp(value, "disable") == 0)
+ wpa_s->set_sta_uapsd = 0;
+ else {
+ int be, bk, vi, vo;
+ char *pos;
+ /* format: BE,BK,VI,VO;max SP Length */
+ be = atoi(value);
+ pos = os_strchr(value, ',');
+ if (pos == NULL)
+ return -1;
+ pos++;
+ bk = atoi(pos);
+ pos = os_strchr(pos, ',');
+ if (pos == NULL)
+ return -1;
+ pos++;
+ vi = atoi(pos);
+ pos = os_strchr(pos, ',');
+ if (pos == NULL)
+ return -1;
+ pos++;
+ vo = atoi(pos);
+ /* ignore max SP Length for now */
+
+ wpa_s->set_sta_uapsd = 1;
+ wpa_s->sta_uapsd = 0;
+ if (be)
+ wpa_s->sta_uapsd |= BIT(0);
+ if (bk)
+ wpa_s->sta_uapsd |= BIT(1);
+ if (vi)
+ wpa_s->sta_uapsd |= BIT(2);
+ if (vo)
+ wpa_s->sta_uapsd |= BIT(3);
+ }
+ } else if (os_strcasecmp(cmd, "ps") == 0) {
+ ret = wpa_drv_set_p2p_powersave(wpa_s, atoi(value), -1, -1);
+#ifdef CONFIG_WIFI_DISPLAY
+ } else if (os_strcasecmp(cmd, "wifi_display") == 0) {
+ int enabled = !!atoi(value);
+ if (enabled && !wpa_s->global->p2p)
+ ret = -1;
+ else
+ wifi_display_enable(wpa_s->global, enabled);
+#endif /* CONFIG_WIFI_DISPLAY */
+ } else if (os_strcasecmp(cmd, "bssid_filter") == 0) {
+ ret = set_bssid_filter(wpa_s, value);
+ } else if (os_strcasecmp(cmd, "disallow_aps") == 0) {
+ ret = set_disallow_aps(wpa_s, value);
+ } else if (os_strcasecmp(cmd, "no_keep_alive") == 0) {
+ wpa_s->no_keep_alive = !!atoi(value);
+#ifdef CONFIG_TESTING_OPTIONS
+ } else if (os_strcasecmp(cmd, "ext_mgmt_frame_handling") == 0) {
+ wpa_s->ext_mgmt_frame_handling = !!atoi(value);
+ } else if (os_strcasecmp(cmd, "ext_eapol_frame_io") == 0) {
+ wpa_s->ext_eapol_frame_io = !!atoi(value);
+#ifdef CONFIG_AP
+ if (wpa_s->ap_iface) {
+ wpa_s->ap_iface->bss[0]->ext_eapol_frame_io =
+ wpa_s->ext_eapol_frame_io;
+ }
+#endif /* CONFIG_AP */
+ } else if (os_strcasecmp(cmd, "extra_roc_dur") == 0) {
+ wpa_s->extra_roc_dur = atoi(value);
+ } else if (os_strcasecmp(cmd, "test_failure") == 0) {
+ wpa_s->test_failure = atoi(value);
+#endif /* CONFIG_TESTING_OPTIONS */
+#ifndef CONFIG_NO_CONFIG_BLOBS
+ } else if (os_strcmp(cmd, "blob") == 0) {
+ ret = wpas_ctrl_set_blob(wpa_s, value);
+#endif /* CONFIG_NO_CONFIG_BLOBS */
+ } else if (os_strcasecmp(cmd, "setband") == 0) {
+ ret = wpas_ctrl_set_band(wpa_s, value);
+ } else {
+ value[-1] = '=';
+ ret = wpa_config_process_global(wpa_s->conf, cmd, -1);
+ if (ret == 0)
+ wpa_supplicant_update_config(wpa_s);
+ }
+
+ return ret;
+}
+
+
+static int wpa_supplicant_ctrl_iface_get(struct wpa_supplicant *wpa_s,
+ char *cmd, char *buf, size_t buflen)
+{
+ int res = -1;
+
+ wpa_printf(MSG_DEBUG, "CTRL_IFACE GET '%s'", cmd);
+
+ if (os_strcmp(cmd, "version") == 0) {
+ res = os_snprintf(buf, buflen, "%s", VERSION_STR);
+ } else if (os_strcasecmp(cmd, "country") == 0) {
+ if (wpa_s->conf->country[0] && wpa_s->conf->country[1])
+ res = os_snprintf(buf, buflen, "%c%c",
+ wpa_s->conf->country[0],
+ wpa_s->conf->country[1]);
+#ifdef CONFIG_WIFI_DISPLAY
+ } else if (os_strcasecmp(cmd, "wifi_display") == 0) {
+ int enabled;
+ if (wpa_s->global->p2p == NULL ||
+ wpa_s->global->p2p_disabled)
+ enabled = 0;
+ else
+ enabled = wpa_s->global->wifi_display;
+ res = os_snprintf(buf, buflen, "%d", enabled);
+#endif /* CONFIG_WIFI_DISPLAY */
+#ifdef CONFIG_TESTING_GET_GTK
+ } else if (os_strcmp(cmd, "gtk") == 0) {
+ if (wpa_s->last_gtk_len == 0)
+ return -1;
+ res = wpa_snprintf_hex(buf, buflen, wpa_s->last_gtk,
+ wpa_s->last_gtk_len);
+ return res;
+#endif /* CONFIG_TESTING_GET_GTK */
+ } else if (os_strcmp(cmd, "tls_library") == 0) {
+ res = tls_get_library_version(buf, buflen);
+ } else {
+ res = wpa_config_get_value(cmd, wpa_s->conf, buf, buflen);
+ }
+
+ if (os_snprintf_error(buflen, res))
+ return -1;
+ return res;
+}
+
+
+#ifdef IEEE8021X_EAPOL
+static int wpa_supplicant_ctrl_iface_preauth(struct wpa_supplicant *wpa_s,
+ char *addr)
+{
+ u8 bssid[ETH_ALEN];
+ struct wpa_ssid *ssid = wpa_s->current_ssid;
+
+ if (hwaddr_aton(addr, bssid)) {
+ wpa_printf(MSG_DEBUG, "CTRL_IFACE PREAUTH: invalid address "
+ "'%s'", addr);
+ return -1;
+ }
+
+ wpa_printf(MSG_DEBUG, "CTRL_IFACE PREAUTH " MACSTR, MAC2STR(bssid));
+ rsn_preauth_deinit(wpa_s->wpa);
+ if (rsn_preauth_init(wpa_s->wpa, bssid, ssid ? &ssid->eap : NULL))
+ return -1;
+
+ return 0;
+}
+#endif /* IEEE8021X_EAPOL */
+
+
+#ifdef CONFIG_PEERKEY
+/* MLME-STKSTART.request(peer) */
+static int wpa_supplicant_ctrl_iface_stkstart(
+ struct wpa_supplicant *wpa_s, char *addr)
+{
+ u8 peer[ETH_ALEN];
+
+ if (hwaddr_aton(addr, peer)) {
+ wpa_printf(MSG_DEBUG, "CTRL_IFACE STKSTART: invalid "
+ "address '%s'", addr);
+ return -1;
+ }
+
+ wpa_printf(MSG_DEBUG, "CTRL_IFACE STKSTART " MACSTR,
+ MAC2STR(peer));
+
+ return wpa_sm_stkstart(wpa_s->wpa, peer);
+}
+#endif /* CONFIG_PEERKEY */
+
+
+#ifdef CONFIG_TDLS
+
+static int wpa_supplicant_ctrl_iface_tdls_discover(
+ struct wpa_supplicant *wpa_s, char *addr)
+{
+ u8 peer[ETH_ALEN];
+ int ret;
+
+ if (hwaddr_aton(addr, peer)) {
+ wpa_printf(MSG_DEBUG, "CTRL_IFACE TDLS_DISCOVER: invalid "
+ "address '%s'", addr);
+ return -1;
+ }
+
+ wpa_printf(MSG_DEBUG, "CTRL_IFACE TDLS_DISCOVER " MACSTR,
+ MAC2STR(peer));
+
+ if (wpa_tdls_is_external_setup(wpa_s->wpa))
+ ret = wpa_tdls_send_discovery_request(wpa_s->wpa, peer);
+ else
+ ret = wpa_drv_tdls_oper(wpa_s, TDLS_DISCOVERY_REQ, peer);
+
+ return ret;
+}
+
+
+static int wpa_supplicant_ctrl_iface_tdls_setup(
+ struct wpa_supplicant *wpa_s, char *addr)
+{
+ u8 peer[ETH_ALEN];
+ int ret;
+
+ if (hwaddr_aton(addr, peer)) {
+ wpa_printf(MSG_DEBUG, "CTRL_IFACE TDLS_SETUP: invalid "
+ "address '%s'", addr);
+ return -1;
+ }
+
+ wpa_printf(MSG_DEBUG, "CTRL_IFACE TDLS_SETUP " MACSTR,
+ MAC2STR(peer));
+
+ if ((wpa_s->conf->tdls_external_control) &&
+ wpa_tdls_is_external_setup(wpa_s->wpa))
+ return wpa_drv_tdls_oper(wpa_s, TDLS_SETUP, peer);
+
+ wpa_tdls_remove(wpa_s->wpa, peer);
+
+ if (wpa_tdls_is_external_setup(wpa_s->wpa))
+ ret = wpa_tdls_start(wpa_s->wpa, peer);
+ else
+ ret = wpa_drv_tdls_oper(wpa_s, TDLS_SETUP, peer);
+
+ return ret;
+}
+
+
+static int wpa_supplicant_ctrl_iface_tdls_teardown(
+ struct wpa_supplicant *wpa_s, char *addr)
+{
+ u8 peer[ETH_ALEN];
+ int ret;
+
+ if (os_strcmp(addr, "*") == 0) {
+ /* remove everyone */
+ wpa_printf(MSG_DEBUG, "CTRL_IFACE TDLS_TEARDOWN *");
+ wpa_tdls_teardown_peers(wpa_s->wpa);
+ return 0;
+ }
+
+ if (hwaddr_aton(addr, peer)) {
+ wpa_printf(MSG_DEBUG, "CTRL_IFACE TDLS_TEARDOWN: invalid "
+ "address '%s'", addr);
+ return -1;
+ }
+
+ wpa_printf(MSG_DEBUG, "CTRL_IFACE TDLS_TEARDOWN " MACSTR,
+ MAC2STR(peer));
+
+ if ((wpa_s->conf->tdls_external_control) &&
+ wpa_tdls_is_external_setup(wpa_s->wpa))
+ return wpa_drv_tdls_oper(wpa_s, TDLS_TEARDOWN, peer);
+
+ if (wpa_tdls_is_external_setup(wpa_s->wpa))
+ ret = wpa_tdls_teardown_link(
+ wpa_s->wpa, peer,
+ WLAN_REASON_TDLS_TEARDOWN_UNSPECIFIED);
+ else
+ ret = wpa_drv_tdls_oper(wpa_s, TDLS_TEARDOWN, peer);
+
+ return ret;
+}
+
+
+static int ctrl_iface_get_capability_tdls(
+ struct wpa_supplicant *wpa_s, char *buf, size_t buflen)
+{
+ int ret;
+
+ ret = os_snprintf(buf, buflen, "%s\n",
+ wpa_s->drv_flags & WPA_DRIVER_FLAGS_TDLS_SUPPORT ?
+ (wpa_s->drv_flags &
+ WPA_DRIVER_FLAGS_TDLS_EXTERNAL_SETUP ?
+ "EXTERNAL" : "INTERNAL") : "UNSUPPORTED");
+ if (os_snprintf_error(buflen, ret))
+ return -1;
+ return ret;
+}
+
+
+static int wpa_supplicant_ctrl_iface_tdls_chan_switch(
+ struct wpa_supplicant *wpa_s, char *cmd)
+{
+ u8 peer[ETH_ALEN];
+ struct hostapd_freq_params freq_params;
+ u8 oper_class;
+ char *pos, *end;
+
+ if (!wpa_tdls_is_external_setup(wpa_s->wpa)) {
+ wpa_printf(MSG_INFO,
+ "tdls_chanswitch: Only supported with external setup");
+ return -1;
+ }
+
+ os_memset(&freq_params, 0, sizeof(freq_params));
+
+ pos = os_strchr(cmd, ' ');
+ if (pos == NULL)
+ return -1;
+ *pos++ = '\0';
+
+ oper_class = strtol(pos, &end, 10);
+ if (pos == end) {
+ wpa_printf(MSG_INFO,
+ "tdls_chanswitch: Invalid op class provided");
+ return -1;
+ }
+
+ pos = end;
+ freq_params.freq = atoi(pos);
+ if (freq_params.freq == 0) {
+ wpa_printf(MSG_INFO, "tdls_chanswitch: Invalid freq provided");
+ return -1;
+ }
+
+#define SET_FREQ_SETTING(str) \
+ do { \
+ const char *pos2 = os_strstr(pos, " " #str "="); \
+ if (pos2) { \
+ pos2 += sizeof(" " #str "=") - 1; \
+ freq_params.str = atoi(pos2); \
+ } \
+ } while (0)
+
+ SET_FREQ_SETTING(center_freq1);
+ SET_FREQ_SETTING(center_freq2);
+ SET_FREQ_SETTING(bandwidth);
+ SET_FREQ_SETTING(sec_channel_offset);
+#undef SET_FREQ_SETTING
+
+ freq_params.ht_enabled = !!os_strstr(pos, " ht");
+ freq_params.vht_enabled = !!os_strstr(pos, " vht");
+
+ if (hwaddr_aton(cmd, peer)) {
+ wpa_printf(MSG_DEBUG,
+ "CTRL_IFACE TDLS_CHAN_SWITCH: Invalid address '%s'",
+ cmd);
+ return -1;
+ }
+
+ wpa_printf(MSG_DEBUG, "CTRL_IFACE TDLS_CHAN_SWITCH " MACSTR
+ " OP CLASS %d FREQ %d CENTER1 %d CENTER2 %d BW %d SEC_OFFSET %d%s%s",
+ MAC2STR(peer), oper_class, freq_params.freq,
+ freq_params.center_freq1, freq_params.center_freq2,
+ freq_params.bandwidth, freq_params.sec_channel_offset,
+ freq_params.ht_enabled ? " HT" : "",
+ freq_params.vht_enabled ? " VHT" : "");
+
+ return wpa_tdls_enable_chan_switch(wpa_s->wpa, peer, oper_class,
+ &freq_params);
+}
+
+
+static int wpa_supplicant_ctrl_iface_tdls_cancel_chan_switch(
+ struct wpa_supplicant *wpa_s, char *cmd)
+{
+ u8 peer[ETH_ALEN];
+
+ if (!wpa_tdls_is_external_setup(wpa_s->wpa)) {
+ wpa_printf(MSG_INFO,
+ "tdls_chanswitch: Only supported with external setup");
+ return -1;
+ }
+
+ if (hwaddr_aton(cmd, peer)) {
+ wpa_printf(MSG_DEBUG,
+ "CTRL_IFACE TDLS_CANCEL_CHAN_SWITCH: Invalid address '%s'",
+ cmd);
+ return -1;
+ }
+
+ wpa_printf(MSG_DEBUG, "CTRL_IFACE TDLS_CANCEL_CHAN_SWITCH " MACSTR,
+ MAC2STR(peer));
+
+ return wpa_tdls_disable_chan_switch(wpa_s->wpa, peer);
+}
+
+
+static int wpa_supplicant_ctrl_iface_tdls_link_status(
+ struct wpa_supplicant *wpa_s, const char *addr,
+ char *buf, size_t buflen)
+{
+ u8 peer[ETH_ALEN];
+ const char *tdls_status;
+ int ret;
+
+ if (hwaddr_aton(addr, peer)) {
+ wpa_printf(MSG_DEBUG,
+ "CTRL_IFACE TDLS_LINK_STATUS: Invalid address '%s'",
+ addr);
+ return -1;
+ }
+ wpa_printf(MSG_DEBUG, "CTRL_IFACE TDLS_LINK_STATUS " MACSTR,
+ MAC2STR(peer));
+
+ tdls_status = wpa_tdls_get_link_status(wpa_s->wpa, peer);
+ wpa_printf(MSG_DEBUG, "CTRL_IFACE TDLS_LINK_STATUS: %s", tdls_status);
+ ret = os_snprintf(buf, buflen, "TDLS link status: %s\n", tdls_status);
+ if (os_snprintf_error(buflen, ret))
+ return -1;
+
+ return ret;
+}
+
+#endif /* CONFIG_TDLS */
+
+
+static int wmm_ac_ctrl_addts(struct wpa_supplicant *wpa_s, char *cmd)
+{
+ char *token, *context = NULL;
+ struct wmm_ac_ts_setup_params params = {
+ .tsid = 0xff,
+ .direction = 0xff,
+ };
+
+ while ((token = str_token(cmd, " ", &context))) {
+ if (sscanf(token, "tsid=%i", &params.tsid) == 1 ||
+ sscanf(token, "up=%i", &params.user_priority) == 1 ||
+ sscanf(token, "nominal_msdu_size=%i",
+ &params.nominal_msdu_size) == 1 ||
+ sscanf(token, "mean_data_rate=%i",
+ &params.mean_data_rate) == 1 ||
+ sscanf(token, "min_phy_rate=%i",
+ &params.minimum_phy_rate) == 1 ||
+ sscanf(token, "sba=%i",
+ &params.surplus_bandwidth_allowance) == 1)
+ continue;
+
+ if (os_strcasecmp(token, "downlink") == 0) {
+ params.direction = WMM_TSPEC_DIRECTION_DOWNLINK;
+ } else if (os_strcasecmp(token, "uplink") == 0) {
+ params.direction = WMM_TSPEC_DIRECTION_UPLINK;
+ } else if (os_strcasecmp(token, "bidi") == 0) {
+ params.direction = WMM_TSPEC_DIRECTION_BI_DIRECTIONAL;
+ } else if (os_strcasecmp(token, "fixed_nominal_msdu") == 0) {
+ params.fixed_nominal_msdu = 1;
+ } else {
+ wpa_printf(MSG_DEBUG,
+ "CTRL: Invalid WMM_AC_ADDTS parameter: '%s'",
+ token);
+ return -1;
+ }
+
+ }
+
+ return wpas_wmm_ac_addts(wpa_s, &params);
+}
+
+
+static int wmm_ac_ctrl_delts(struct wpa_supplicant *wpa_s, char *cmd)
+{
+ u8 tsid = atoi(cmd);
+
+ return wpas_wmm_ac_delts(wpa_s, tsid);
+}
+
+
+#ifdef CONFIG_IEEE80211R
+static int wpa_supplicant_ctrl_iface_ft_ds(
+ struct wpa_supplicant *wpa_s, char *addr)
+{
+ u8 target_ap[ETH_ALEN];
+ struct wpa_bss *bss;
+ const u8 *mdie;
+
+ if (hwaddr_aton(addr, target_ap)) {
+ wpa_printf(MSG_DEBUG, "CTRL_IFACE FT_DS: invalid "
+ "address '%s'", addr);
+ return -1;
+ }
+
+ wpa_printf(MSG_DEBUG, "CTRL_IFACE FT_DS " MACSTR, MAC2STR(target_ap));
+
+ bss = wpa_bss_get_bssid(wpa_s, target_ap);
+ if (bss)
+ mdie = wpa_bss_get_ie(bss, WLAN_EID_MOBILITY_DOMAIN);
+ else
+ mdie = NULL;
+
+ return wpa_ft_start_over_ds(wpa_s->wpa, target_ap, mdie);
+}
+#endif /* CONFIG_IEEE80211R */
+
+
+#ifdef CONFIG_WPS
+static int wpa_supplicant_ctrl_iface_wps_pbc(struct wpa_supplicant *wpa_s,
+ char *cmd)
+{
+ u8 bssid[ETH_ALEN], *_bssid = bssid;
+#ifdef CONFIG_P2P
+ u8 p2p_dev_addr[ETH_ALEN];
+#endif /* CONFIG_P2P */
+#ifdef CONFIG_AP
+ u8 *_p2p_dev_addr = NULL;
+#endif /* CONFIG_AP */
+
+ if (cmd == NULL || os_strcmp(cmd, "any") == 0) {
+ _bssid = NULL;
+#ifdef CONFIG_P2P
+ } else if (os_strncmp(cmd, "p2p_dev_addr=", 13) == 0) {
+ if (hwaddr_aton(cmd + 13, p2p_dev_addr)) {
+ wpa_printf(MSG_DEBUG, "CTRL_IFACE WPS_PBC: invalid "
+ "P2P Device Address '%s'",
+ cmd + 13);
+ return -1;
+ }
+ _p2p_dev_addr = p2p_dev_addr;
+#endif /* CONFIG_P2P */
+ } else if (hwaddr_aton(cmd, bssid)) {
+ wpa_printf(MSG_DEBUG, "CTRL_IFACE WPS_PBC: invalid BSSID '%s'",
+ cmd);
+ return -1;
+ }
+
+#ifdef CONFIG_AP
+ if (wpa_s->ap_iface)
+ return wpa_supplicant_ap_wps_pbc(wpa_s, _bssid, _p2p_dev_addr);
+#endif /* CONFIG_AP */
+
+ return wpas_wps_start_pbc(wpa_s, _bssid, 0);
+}
+
+
+static int wpa_supplicant_ctrl_iface_wps_pin(struct wpa_supplicant *wpa_s,
+ char *cmd, char *buf,
+ size_t buflen)
+{
+ u8 bssid[ETH_ALEN], *_bssid = bssid;
+ char *pin;
+ int ret;
+
+ pin = os_strchr(cmd, ' ');
+ if (pin)
+ *pin++ = '\0';
+
+ if (os_strcmp(cmd, "any") == 0)
+ _bssid = NULL;
+ else if (os_strcmp(cmd, "get") == 0) {
+ ret = wps_generate_pin();
+ goto done;
+ } else if (hwaddr_aton(cmd, bssid)) {
+ wpa_printf(MSG_DEBUG, "CTRL_IFACE WPS_PIN: invalid BSSID '%s'",
+ cmd);
+ return -1;
+ }
+
+#ifdef CONFIG_AP
+ if (wpa_s->ap_iface) {
+ int timeout = 0;
+ char *pos;
+
+ if (pin) {
+ pos = os_strchr(pin, ' ');
+ if (pos) {
+ *pos++ = '\0';
+ timeout = atoi(pos);
+ }
+ }
+
+ return wpa_supplicant_ap_wps_pin(wpa_s, _bssid, pin,
+ buf, buflen, timeout);
+ }
+#endif /* CONFIG_AP */
+
+ if (pin) {
+ ret = wpas_wps_start_pin(wpa_s, _bssid, pin, 0,
+ DEV_PW_DEFAULT);
+ if (ret < 0)
+ return -1;
+ ret = os_snprintf(buf, buflen, "%s", pin);
+ if (os_snprintf_error(buflen, ret))
+ return -1;
+ return ret;
+ }
+
+ ret = wpas_wps_start_pin(wpa_s, _bssid, NULL, 0, DEV_PW_DEFAULT);
+ if (ret < 0)
+ return -1;
+
+done:
+ /* Return the generated PIN */
+ ret = os_snprintf(buf, buflen, "%08d", ret);
+ if (os_snprintf_error(buflen, ret))
+ return -1;
+ return ret;
+}
+
+
+static int wpa_supplicant_ctrl_iface_wps_check_pin(
+ struct wpa_supplicant *wpa_s, char *cmd, char *buf, size_t buflen)
+{
+ char pin[9];
+ size_t len;
+ char *pos;
+ int ret;
+
+ wpa_hexdump_ascii_key(MSG_DEBUG, "WPS_CHECK_PIN",
+ (u8 *) cmd, os_strlen(cmd));
+ for (pos = cmd, len = 0; *pos != '\0'; pos++) {
+ if (*pos < '0' || *pos > '9')
+ continue;
+ pin[len++] = *pos;
+ if (len == 9) {
+ wpa_printf(MSG_DEBUG, "WPS: Too long PIN");
+ return -1;
+ }
+ }
+ if (len != 4 && len != 8) {
+ wpa_printf(MSG_DEBUG, "WPS: Invalid PIN length %d", (int) len);
+ return -1;
+ }
+ pin[len] = '\0';
+
+ if (len == 8) {
+ unsigned int pin_val;
+ pin_val = atoi(pin);
+ if (!wps_pin_valid(pin_val)) {
+ wpa_printf(MSG_DEBUG, "WPS: Invalid checksum digit");
+ ret = os_snprintf(buf, buflen, "FAIL-CHECKSUM\n");
+ if (os_snprintf_error(buflen, ret))
+ return -1;
+ return ret;
+ }
+ }
+
+ ret = os_snprintf(buf, buflen, "%s", pin);
+ if (os_snprintf_error(buflen, ret))
+ return -1;
+
+ return ret;
+}
+
+
+#ifdef CONFIG_WPS_NFC
+
+static int wpa_supplicant_ctrl_iface_wps_nfc(struct wpa_supplicant *wpa_s,
+ char *cmd)
+{
+ u8 bssid[ETH_ALEN], *_bssid = bssid;
+
+ if (cmd == NULL || cmd[0] == '\0')
+ _bssid = NULL;
+ else if (hwaddr_aton(cmd, bssid))
+ return -1;
+
+ return wpas_wps_start_nfc(wpa_s, NULL, _bssid, NULL, 0, 0, NULL, NULL,
+ 0, 0);
+}
+
+
+static int wpa_supplicant_ctrl_iface_wps_nfc_config_token(
+ struct wpa_supplicant *wpa_s, char *cmd, char *reply, size_t max_len)
+{
+ int ndef;
+ struct wpabuf *buf;
+ int res;
+ char *pos;
+
+ pos = os_strchr(cmd, ' ');
+ if (pos)
+ *pos++ = '\0';
+ if (os_strcmp(cmd, "WPS") == 0)
+ ndef = 0;
+ else if (os_strcmp(cmd, "NDEF") == 0)
+ ndef = 1;
+ else
+ return -1;
+
+ buf = wpas_wps_nfc_config_token(wpa_s, ndef, pos);
+ if (buf == NULL)
+ return -1;
+
+ res = wpa_snprintf_hex_uppercase(reply, max_len, wpabuf_head(buf),
+ wpabuf_len(buf));
+ reply[res++] = '\n';
+ reply[res] = '\0';
+
+ wpabuf_free(buf);
+
+ return res;
+}
+
+
+static int wpa_supplicant_ctrl_iface_wps_nfc_token(
+ struct wpa_supplicant *wpa_s, char *cmd, char *reply, size_t max_len)
+{
+ int ndef;
+ struct wpabuf *buf;
+ int res;
+
+ if (os_strcmp(cmd, "WPS") == 0)
+ ndef = 0;
+ else if (os_strcmp(cmd, "NDEF") == 0)
+ ndef = 1;
+ else
+ return -1;
+
+ buf = wpas_wps_nfc_token(wpa_s, ndef);
+ if (buf == NULL)
+ return -1;
+
+ res = wpa_snprintf_hex_uppercase(reply, max_len, wpabuf_head(buf),
+ wpabuf_len(buf));
+ reply[res++] = '\n';
+ reply[res] = '\0';
+
+ wpabuf_free(buf);
+
+ return res;
+}
+
+
+static int wpa_supplicant_ctrl_iface_wps_nfc_tag_read(
+ struct wpa_supplicant *wpa_s, char *pos)
+{
+ size_t len;
+ struct wpabuf *buf;
+ int ret;
+ char *freq;
+ int forced_freq = 0;
+
+ freq = strstr(pos, " freq=");
+ if (freq) {
+ *freq = '\0';
+ freq += 6;
+ forced_freq = atoi(freq);
+ }
+
+ len = os_strlen(pos);
+ if (len & 0x01)
+ return -1;
+ len /= 2;
+
+ buf = wpabuf_alloc(len);
+ if (buf == NULL)
+ return -1;
+ if (hexstr2bin(pos, wpabuf_put(buf, len), len) < 0) {
+ wpabuf_free(buf);
+ return -1;
+ }
+
+ ret = wpas_wps_nfc_tag_read(wpa_s, buf, forced_freq);
+ wpabuf_free(buf);
+
+ return ret;
+}
+
+
+static int wpas_ctrl_nfc_get_handover_req_wps(struct wpa_supplicant *wpa_s,
+ char *reply, size_t max_len,
+ int ndef)
+{
+ struct wpabuf *buf;
+ int res;
+
+ buf = wpas_wps_nfc_handover_req(wpa_s, ndef);
+ if (buf == NULL)
+ return -1;
+
+ res = wpa_snprintf_hex_uppercase(reply, max_len, wpabuf_head(buf),
+ wpabuf_len(buf));
+ reply[res++] = '\n';
+ reply[res] = '\0';
+
+ wpabuf_free(buf);
+
+ return res;
+}
+
+
+#ifdef CONFIG_P2P
+static int wpas_ctrl_nfc_get_handover_req_p2p(struct wpa_supplicant *wpa_s,
+ char *reply, size_t max_len,
+ int ndef)
+{
+ struct wpabuf *buf;
+ int res;
+
+ buf = wpas_p2p_nfc_handover_req(wpa_s, ndef);
+ if (buf == NULL) {
+ wpa_printf(MSG_DEBUG, "P2P: Could not generate NFC handover request");
+ return -1;
+ }
+
+ res = wpa_snprintf_hex_uppercase(reply, max_len, wpabuf_head(buf),
+ wpabuf_len(buf));
+ reply[res++] = '\n';
+ reply[res] = '\0';
+
+ wpabuf_free(buf);
+
+ return res;
+}
+#endif /* CONFIG_P2P */
+
+
+static int wpas_ctrl_nfc_get_handover_req(struct wpa_supplicant *wpa_s,
+ char *cmd, char *reply,
+ size_t max_len)
+{
+ char *pos;
+ int ndef;
+
+ pos = os_strchr(cmd, ' ');
+ if (pos == NULL)
+ return -1;
+ *pos++ = '\0';
+
+ if (os_strcmp(cmd, "WPS") == 0)
+ ndef = 0;
+ else if (os_strcmp(cmd, "NDEF") == 0)
+ ndef = 1;
+ else
+ return -1;
+
+ if (os_strcmp(pos, "WPS") == 0 || os_strcmp(pos, "WPS-CR") == 0) {
+ if (!ndef)
+ return -1;
+ return wpas_ctrl_nfc_get_handover_req_wps(
+ wpa_s, reply, max_len, ndef);
+ }
+
+#ifdef CONFIG_P2P
+ if (os_strcmp(pos, "P2P-CR") == 0) {
+ return wpas_ctrl_nfc_get_handover_req_p2p(
+ wpa_s, reply, max_len, ndef);
+ }
+#endif /* CONFIG_P2P */
+
+ return -1;
+}
+
+
+static int wpas_ctrl_nfc_get_handover_sel_wps(struct wpa_supplicant *wpa_s,
+ char *reply, size_t max_len,
+ int ndef, int cr, char *uuid)
+{
+ struct wpabuf *buf;
+ int res;
+
+ buf = wpas_wps_nfc_handover_sel(wpa_s, ndef, cr, uuid);
+ if (buf == NULL)
+ return -1;
+
+ res = wpa_snprintf_hex_uppercase(reply, max_len, wpabuf_head(buf),
+ wpabuf_len(buf));
+ reply[res++] = '\n';
+ reply[res] = '\0';
+
+ wpabuf_free(buf);
+
+ return res;
+}
+
+
+#ifdef CONFIG_P2P
+static int wpas_ctrl_nfc_get_handover_sel_p2p(struct wpa_supplicant *wpa_s,
+ char *reply, size_t max_len,
+ int ndef, int tag)
+{
+ struct wpabuf *buf;
+ int res;
+
+ buf = wpas_p2p_nfc_handover_sel(wpa_s, ndef, tag);
+ if (buf == NULL)
+ return -1;
+
+ res = wpa_snprintf_hex_uppercase(reply, max_len, wpabuf_head(buf),
+ wpabuf_len(buf));
+ reply[res++] = '\n';
+ reply[res] = '\0';
+
+ wpabuf_free(buf);
+
+ return res;
+}
+#endif /* CONFIG_P2P */
+
+
+static int wpas_ctrl_nfc_get_handover_sel(struct wpa_supplicant *wpa_s,
+ char *cmd, char *reply,
+ size_t max_len)
+{
+ char *pos, *pos2;
+ int ndef;
+
+ pos = os_strchr(cmd, ' ');
+ if (pos == NULL)
+ return -1;
+ *pos++ = '\0';
+
+ if (os_strcmp(cmd, "WPS") == 0)
+ ndef = 0;
+ else if (os_strcmp(cmd, "NDEF") == 0)
+ ndef = 1;
+ else
+ return -1;
+
+ pos2 = os_strchr(pos, ' ');
+ if (pos2)
+ *pos2++ = '\0';
+ if (os_strcmp(pos, "WPS") == 0 || os_strcmp(pos, "WPS-CR") == 0) {
+ if (!ndef)
+ return -1;
+ return wpas_ctrl_nfc_get_handover_sel_wps(
+ wpa_s, reply, max_len, ndef,
+ os_strcmp(pos, "WPS-CR") == 0, pos2);
+ }
+
+#ifdef CONFIG_P2P
+ if (os_strcmp(pos, "P2P-CR") == 0) {
+ return wpas_ctrl_nfc_get_handover_sel_p2p(
+ wpa_s, reply, max_len, ndef, 0);
+ }
+
+ if (os_strcmp(pos, "P2P-CR-TAG") == 0) {
+ return wpas_ctrl_nfc_get_handover_sel_p2p(
+ wpa_s, reply, max_len, ndef, 1);
+ }
+#endif /* CONFIG_P2P */
+
+ return -1;
+}
+
+
+static int wpas_ctrl_nfc_report_handover(struct wpa_supplicant *wpa_s,
+ char *cmd)
+{
+ size_t len;
+ struct wpabuf *req, *sel;
+ int ret;
+ char *pos, *role, *type, *pos2;
+#ifdef CONFIG_P2P
+ char *freq;
+ int forced_freq = 0;
+
+ freq = strstr(cmd, " freq=");
+ if (freq) {
+ *freq = '\0';
+ freq += 6;
+ forced_freq = atoi(freq);
+ }
+#endif /* CONFIG_P2P */
+
+ role = cmd;
+ pos = os_strchr(role, ' ');
+ if (pos == NULL) {
+ wpa_printf(MSG_DEBUG, "NFC: Missing type in handover report");
+ return -1;
+ }
+ *pos++ = '\0';
+
+ type = pos;
+ pos = os_strchr(type, ' ');
+ if (pos == NULL) {
+ wpa_printf(MSG_DEBUG, "NFC: Missing request message in handover report");
+ return -1;
+ }
+ *pos++ = '\0';
+
+ pos2 = os_strchr(pos, ' ');
+ if (pos2 == NULL) {
+ wpa_printf(MSG_DEBUG, "NFC: Missing select message in handover report");
+ return -1;
+ }
+ *pos2++ = '\0';
+
+ len = os_strlen(pos);
+ if (len & 0x01) {
+ wpa_printf(MSG_DEBUG, "NFC: Invalid request message length in handover report");
+ return -1;
+ }
+ len /= 2;
+
+ req = wpabuf_alloc(len);
+ if (req == NULL) {
+ wpa_printf(MSG_DEBUG, "NFC: Failed to allocate memory for request message");
+ return -1;
+ }
+ if (hexstr2bin(pos, wpabuf_put(req, len), len) < 0) {
+ wpa_printf(MSG_DEBUG, "NFC: Invalid request message hexdump in handover report");
+ wpabuf_free(req);
+ return -1;
+ }
+
+ len = os_strlen(pos2);
+ if (len & 0x01) {
+ wpa_printf(MSG_DEBUG, "NFC: Invalid select message length in handover report");
+ wpabuf_free(req);
+ return -1;
+ }
+ len /= 2;
+
+ sel = wpabuf_alloc(len);
+ if (sel == NULL) {
+ wpa_printf(MSG_DEBUG, "NFC: Failed to allocate memory for select message");
+ wpabuf_free(req);
+ return -1;
+ }
+ if (hexstr2bin(pos2, wpabuf_put(sel, len), len) < 0) {
+ wpa_printf(MSG_DEBUG, "NFC: Invalid select message hexdump in handover report");
+ wpabuf_free(req);
+ wpabuf_free(sel);
+ return -1;
+ }
+
+ wpa_printf(MSG_DEBUG, "NFC: Connection handover reported - role=%s type=%s req_len=%d sel_len=%d",
+ role, type, (int) wpabuf_len(req), (int) wpabuf_len(sel));
+
+ if (os_strcmp(role, "INIT") == 0 && os_strcmp(type, "WPS") == 0) {
+ ret = wpas_wps_nfc_report_handover(wpa_s, req, sel);
+#ifdef CONFIG_AP
+ } else if (os_strcmp(role, "RESP") == 0 && os_strcmp(type, "WPS") == 0)
+ {
+ ret = wpas_ap_wps_nfc_report_handover(wpa_s, req, sel);
+ if (ret < 0)
+ ret = wpas_er_wps_nfc_report_handover(wpa_s, req, sel);
+#endif /* CONFIG_AP */
+#ifdef CONFIG_P2P
+ } else if (os_strcmp(role, "INIT") == 0 && os_strcmp(type, "P2P") == 0)
+ {
+ ret = wpas_p2p_nfc_report_handover(wpa_s, 1, req, sel, 0);
+ } else if (os_strcmp(role, "RESP") == 0 && os_strcmp(type, "P2P") == 0)
+ {
+ ret = wpas_p2p_nfc_report_handover(wpa_s, 0, req, sel,
+ forced_freq);
+#endif /* CONFIG_P2P */
+ } else {
+ wpa_printf(MSG_DEBUG, "NFC: Unsupported connection handover "
+ "reported: role=%s type=%s", role, type);
+ ret = -1;
+ }
+ wpabuf_free(req);
+ wpabuf_free(sel);
+
+ if (ret)
+ wpa_printf(MSG_DEBUG, "NFC: Failed to process reported handover messages");
+
+ return ret;
+}
+
+#endif /* CONFIG_WPS_NFC */
+
+
+static int wpa_supplicant_ctrl_iface_wps_reg(struct wpa_supplicant *wpa_s,
+ char *cmd)
+{
+ u8 bssid[ETH_ALEN];
+ char *pin;
+ char *new_ssid;
+ char *new_auth;
+ char *new_encr;
+ char *new_key;
+ struct wps_new_ap_settings ap;
+
+ pin = os_strchr(cmd, ' ');
+ if (pin == NULL)
+ return -1;
+ *pin++ = '\0';
+
+ if (hwaddr_aton(cmd, bssid)) {
+ wpa_printf(MSG_DEBUG, "CTRL_IFACE WPS_REG: invalid BSSID '%s'",
+ cmd);
+ return -1;
+ }
+
+ new_ssid = os_strchr(pin, ' ');
+ if (new_ssid == NULL)
+ return wpas_wps_start_reg(wpa_s, bssid, pin, NULL);
+ *new_ssid++ = '\0';
+
+ new_auth = os_strchr(new_ssid, ' ');
+ if (new_auth == NULL)
+ return -1;
+ *new_auth++ = '\0';
+
+ new_encr = os_strchr(new_auth, ' ');
+ if (new_encr == NULL)
+ return -1;
+ *new_encr++ = '\0';
+
+ new_key = os_strchr(new_encr, ' ');
+ if (new_key == NULL)
+ return -1;
+ *new_key++ = '\0';
+
+ os_memset(&ap, 0, sizeof(ap));
+ ap.ssid_hex = new_ssid;
+ ap.auth = new_auth;
+ ap.encr = new_encr;
+ ap.key_hex = new_key;
+ return wpas_wps_start_reg(wpa_s, bssid, pin, &ap);
+}
+
+
+#ifdef CONFIG_AP
+static int wpa_supplicant_ctrl_iface_wps_ap_pin(struct wpa_supplicant *wpa_s,
+ char *cmd, char *buf,
+ size_t buflen)
+{
+ int timeout = 300;
+ char *pos;
+ const char *pin_txt;
+
+ if (!wpa_s->ap_iface)
+ return -1;
+
+ pos = os_strchr(cmd, ' ');
+ if (pos)
+ *pos++ = '\0';
+
+ if (os_strcmp(cmd, "disable") == 0) {
+ wpas_wps_ap_pin_disable(wpa_s);
+ return os_snprintf(buf, buflen, "OK\n");
+ }
+
+ if (os_strcmp(cmd, "random") == 0) {
+ if (pos)
+ timeout = atoi(pos);
+ pin_txt = wpas_wps_ap_pin_random(wpa_s, timeout);
+ if (pin_txt == NULL)
+ return -1;
+ return os_snprintf(buf, buflen, "%s", pin_txt);
+ }
+
+ if (os_strcmp(cmd, "get") == 0) {
+ pin_txt = wpas_wps_ap_pin_get(wpa_s);
+ if (pin_txt == NULL)
+ return -1;
+ return os_snprintf(buf, buflen, "%s", pin_txt);
+ }
+
+ if (os_strcmp(cmd, "set") == 0) {
+ char *pin;
+ if (pos == NULL)
+ return -1;
+ pin = pos;
+ pos = os_strchr(pos, ' ');
+ if (pos) {
+ *pos++ = '\0';
+ timeout = atoi(pos);
+ }
+ if (os_strlen(pin) > buflen)
+ return -1;
+ if (wpas_wps_ap_pin_set(wpa_s, pin, timeout) < 0)
+ return -1;
+ return os_snprintf(buf, buflen, "%s", pin);
+ }
+
+ return -1;
+}
+#endif /* CONFIG_AP */
+
+
+#ifdef CONFIG_WPS_ER
+static int wpa_supplicant_ctrl_iface_wps_er_pin(struct wpa_supplicant *wpa_s,
+ char *cmd)
+{
+ char *uuid = cmd, *pin, *pos;
+ u8 addr_buf[ETH_ALEN], *addr = NULL;
+ pin = os_strchr(uuid, ' ');
+ if (pin == NULL)
+ return -1;
+ *pin++ = '\0';
+ pos = os_strchr(pin, ' ');
+ if (pos) {
+ *pos++ = '\0';
+ if (hwaddr_aton(pos, addr_buf) == 0)
+ addr = addr_buf;
+ }
+ return wpas_wps_er_add_pin(wpa_s, addr, uuid, pin);
+}
+
+
+static int wpa_supplicant_ctrl_iface_wps_er_learn(struct wpa_supplicant *wpa_s,
+ char *cmd)
+{
+ char *uuid = cmd, *pin;
+ pin = os_strchr(uuid, ' ');
+ if (pin == NULL)
+ return -1;
+ *pin++ = '\0';
+ return wpas_wps_er_learn(wpa_s, uuid, pin);
+}
+
+
+static int wpa_supplicant_ctrl_iface_wps_er_set_config(
+ struct wpa_supplicant *wpa_s, char *cmd)
+{
+ char *uuid = cmd, *id;
+ id = os_strchr(uuid, ' ');
+ if (id == NULL)
+ return -1;
+ *id++ = '\0';
+ return wpas_wps_er_set_config(wpa_s, uuid, atoi(id));
+}
+
+
+static int wpa_supplicant_ctrl_iface_wps_er_config(
+ struct wpa_supplicant *wpa_s, char *cmd)
+{
+ char *pin;
+ char *new_ssid;
+ char *new_auth;
+ char *new_encr;
+ char *new_key;
+ struct wps_new_ap_settings ap;
+
+ pin = os_strchr(cmd, ' ');
+ if (pin == NULL)
+ return -1;
+ *pin++ = '\0';
+
+ new_ssid = os_strchr(pin, ' ');
+ if (new_ssid == NULL)
+ return -1;
+ *new_ssid++ = '\0';
+
+ new_auth = os_strchr(new_ssid, ' ');
+ if (new_auth == NULL)
+ return -1;
+ *new_auth++ = '\0';
+
+ new_encr = os_strchr(new_auth, ' ');
+ if (new_encr == NULL)
+ return -1;
+ *new_encr++ = '\0';
+
+ new_key = os_strchr(new_encr, ' ');
+ if (new_key == NULL)
+ return -1;
+ *new_key++ = '\0';
+
+ os_memset(&ap, 0, sizeof(ap));
+ ap.ssid_hex = new_ssid;
+ ap.auth = new_auth;
+ ap.encr = new_encr;
+ ap.key_hex = new_key;
+ return wpas_wps_er_config(wpa_s, cmd, pin, &ap);
+}
+
+
+#ifdef CONFIG_WPS_NFC
+static int wpa_supplicant_ctrl_iface_wps_er_nfc_config_token(
+ struct wpa_supplicant *wpa_s, char *cmd, char *reply, size_t max_len)
+{
+ int ndef;
+ struct wpabuf *buf;
+ int res;
+ char *uuid;
+
+ uuid = os_strchr(cmd, ' ');
+ if (uuid == NULL)
+ return -1;
+ *uuid++ = '\0';
+
+ if (os_strcmp(cmd, "WPS") == 0)
+ ndef = 0;
+ else if (os_strcmp(cmd, "NDEF") == 0)
+ ndef = 1;
+ else
+ return -1;
+
+ buf = wpas_wps_er_nfc_config_token(wpa_s, ndef, uuid);
+ if (buf == NULL)
+ return -1;
+
+ res = wpa_snprintf_hex_uppercase(reply, max_len, wpabuf_head(buf),
+ wpabuf_len(buf));
+ reply[res++] = '\n';
+ reply[res] = '\0';
+
+ wpabuf_free(buf);
+
+ return res;
+}
+#endif /* CONFIG_WPS_NFC */
+#endif /* CONFIG_WPS_ER */
+
+#endif /* CONFIG_WPS */
+
+
+#ifdef CONFIG_IBSS_RSN
+static int wpa_supplicant_ctrl_iface_ibss_rsn(
+ struct wpa_supplicant *wpa_s, char *addr)
+{
+ u8 peer[ETH_ALEN];
+
+ if (hwaddr_aton(addr, peer)) {
+ wpa_printf(MSG_DEBUG, "CTRL_IFACE IBSS_RSN: invalid "
+ "address '%s'", addr);
+ return -1;
+ }
+
+ wpa_printf(MSG_DEBUG, "CTRL_IFACE IBSS_RSN " MACSTR,
+ MAC2STR(peer));
+
+ return ibss_rsn_start(wpa_s->ibss_rsn, peer);
+}
+#endif /* CONFIG_IBSS_RSN */
+
+
+static int wpa_supplicant_ctrl_iface_ctrl_rsp(struct wpa_supplicant *wpa_s,
+ char *rsp)
+{
+#ifdef IEEE8021X_EAPOL
+ char *pos, *id_pos;
+ int id;
+ struct wpa_ssid *ssid;
+
+ pos = os_strchr(rsp, '-');
+ if (pos == NULL)
+ return -1;
+ *pos++ = '\0';
+ id_pos = pos;
+ pos = os_strchr(pos, ':');
+ if (pos == NULL)
+ return -1;
+ *pos++ = '\0';
+ id = atoi(id_pos);
+ wpa_printf(MSG_DEBUG, "CTRL_IFACE: field=%s id=%d", rsp, id);
+ wpa_hexdump_ascii_key(MSG_DEBUG, "CTRL_IFACE: value",
+ (u8 *) pos, os_strlen(pos));
+
+ ssid = wpa_config_get_network(wpa_s->conf, id);
+ if (ssid == NULL) {
+ wpa_printf(MSG_DEBUG, "CTRL_IFACE: Could not find SSID id=%d "
+ "to update", id);
+ return -1;
+ }
+
+ return wpa_supplicant_ctrl_iface_ctrl_rsp_handle(wpa_s, ssid, rsp,
+ pos);
+#else /* IEEE8021X_EAPOL */
+ wpa_printf(MSG_DEBUG, "CTRL_IFACE: 802.1X not included");
+ return -1;
+#endif /* IEEE8021X_EAPOL */
+}
+
+
+static int wpa_supplicant_ctrl_iface_status(struct wpa_supplicant *wpa_s,
+ const char *params,
+ char *buf, size_t buflen)
+{
+ char *pos, *end, tmp[30];
+ int res, verbose, wps, ret;
+#ifdef CONFIG_HS20
+ const u8 *hs20;
+#endif /* CONFIG_HS20 */
+ const u8 *sess_id;
+ size_t sess_id_len;
+
+ if (os_strcmp(params, "-DRIVER") == 0)
+ return wpa_drv_status(wpa_s, buf, buflen);
+ verbose = os_strcmp(params, "-VERBOSE") == 0;
+ wps = os_strcmp(params, "-WPS") == 0;
+ pos = buf;
+ end = buf + buflen;
+ if (wpa_s->wpa_state >= WPA_ASSOCIATED) {
+ struct wpa_ssid *ssid = wpa_s->current_ssid;
+ ret = os_snprintf(pos, end - pos, "bssid=" MACSTR "\n",
+ MAC2STR(wpa_s->bssid));
+ if (os_snprintf_error(end - pos, ret))
+ return pos - buf;
+ pos += ret;
+ ret = os_snprintf(pos, end - pos, "freq=%u\n",
+ wpa_s->assoc_freq);
+ if (os_snprintf_error(end - pos, ret))
+ return pos - buf;
+ pos += ret;
+ if (ssid) {
+ u8 *_ssid = ssid->ssid;
+ size_t ssid_len = ssid->ssid_len;
+ u8 ssid_buf[SSID_MAX_LEN];
+ if (ssid_len == 0) {
+ int _res = wpa_drv_get_ssid(wpa_s, ssid_buf);
+ if (_res < 0)
+ ssid_len = 0;
+ else
+ ssid_len = _res;
+ _ssid = ssid_buf;
+ }
+ ret = os_snprintf(pos, end - pos, "ssid=%s\nid=%d\n",
+ wpa_ssid_txt(_ssid, ssid_len),
+ ssid->id);
+ if (os_snprintf_error(end - pos, ret))
+ return pos - buf;
+ pos += ret;
+
+ if (wps && ssid->passphrase &&
+ wpa_key_mgmt_wpa_psk(ssid->key_mgmt) &&
+ (ssid->mode == WPAS_MODE_AP ||
+ ssid->mode == WPAS_MODE_P2P_GO)) {
+ ret = os_snprintf(pos, end - pos,
+ "passphrase=%s\n",
+ ssid->passphrase);
+ if (os_snprintf_error(end - pos, ret))
+ return pos - buf;
+ pos += ret;
+ }
+ if (ssid->id_str) {
+ ret = os_snprintf(pos, end - pos,
+ "id_str=%s\n",
+ ssid->id_str);
+ if (os_snprintf_error(end - pos, ret))
+ return pos - buf;
+ pos += ret;
+ }
+
+ switch (ssid->mode) {
+ case WPAS_MODE_INFRA:
+ ret = os_snprintf(pos, end - pos,
+ "mode=station\n");
+ break;
+ case WPAS_MODE_IBSS:
+ ret = os_snprintf(pos, end - pos,
+ "mode=IBSS\n");
+ break;
+ case WPAS_MODE_AP:
+ ret = os_snprintf(pos, end - pos,
+ "mode=AP\n");
+ break;
+ case WPAS_MODE_P2P_GO:
+ ret = os_snprintf(pos, end - pos,
+ "mode=P2P GO\n");
+ break;
+ case WPAS_MODE_P2P_GROUP_FORMATION:
+ ret = os_snprintf(pos, end - pos,
+ "mode=P2P GO - group "
+ "formation\n");
+ break;
+ default:
+ ret = 0;
+ break;
+ }
+ if (os_snprintf_error(end - pos, ret))
+ return pos - buf;
+ pos += ret;
+ }
+
+#ifdef CONFIG_AP
+ if (wpa_s->ap_iface) {
+ pos += ap_ctrl_iface_wpa_get_status(wpa_s, pos,
+ end - pos,
+ verbose);
+ } else
+#endif /* CONFIG_AP */
+ pos += wpa_sm_get_status(wpa_s->wpa, pos, end - pos, verbose);
+ }
+#ifdef CONFIG_SAE
+ if (wpa_s->wpa_state >= WPA_ASSOCIATED &&
+#ifdef CONFIG_AP
+ !wpa_s->ap_iface &&
+#endif /* CONFIG_AP */
+ wpa_s->sme.sae.state == SAE_ACCEPTED) {
+ ret = os_snprintf(pos, end - pos, "sae_group=%d\n",
+ wpa_s->sme.sae.group);
+ if (os_snprintf_error(end - pos, ret))
+ return pos - buf;
+ pos += ret;
+ }
+#endif /* CONFIG_SAE */
+ ret = os_snprintf(pos, end - pos, "wpa_state=%s\n",
+ wpa_supplicant_state_txt(wpa_s->wpa_state));
+ if (os_snprintf_error(end - pos, ret))
+ return pos - buf;
+ pos += ret;
+
+ if (wpa_s->l2 &&
+ l2_packet_get_ip_addr(wpa_s->l2, tmp, sizeof(tmp)) >= 0) {
+ ret = os_snprintf(pos, end - pos, "ip_address=%s\n", tmp);
+ if (os_snprintf_error(end - pos, ret))
+ return pos - buf;
+ pos += ret;
+ }
+
+#ifdef CONFIG_P2P
+ if (wpa_s->global->p2p) {
+ ret = os_snprintf(pos, end - pos, "p2p_device_address=" MACSTR
+ "\n", MAC2STR(wpa_s->global->p2p_dev_addr));
+ if (os_snprintf_error(end - pos, ret))
+ return pos - buf;
+ pos += ret;
+ }
+#endif /* CONFIG_P2P */
+
+ ret = os_snprintf(pos, end - pos, "address=" MACSTR "\n",
+ MAC2STR(wpa_s->own_addr));
+ if (os_snprintf_error(end - pos, ret))
+ return pos - buf;
+ pos += ret;
+
+#ifdef CONFIG_HS20
+ if (wpa_s->current_bss &&
+ (hs20 = wpa_bss_get_vendor_ie(wpa_s->current_bss,
+ HS20_IE_VENDOR_TYPE)) &&
+ wpa_s->wpa_proto == WPA_PROTO_RSN &&
+ wpa_key_mgmt_wpa_ieee8021x(wpa_s->key_mgmt)) {
+ int release = 1;
+ if (hs20[1] >= 5) {
+ u8 rel_num = (hs20[6] & 0xf0) >> 4;
+ release = rel_num + 1;
+ }
+ ret = os_snprintf(pos, end - pos, "hs20=%d\n", release);
+ if (os_snprintf_error(end - pos, ret))
+ return pos - buf;
+ pos += ret;
+ }
+
+ if (wpa_s->current_ssid) {
+ struct wpa_cred *cred;
+ char *type;
+
+ for (cred = wpa_s->conf->cred; cred; cred = cred->next) {
+ size_t i;
+
+ if (wpa_s->current_ssid->parent_cred != cred)
+ continue;
+
+ if (cred->provisioning_sp) {
+ ret = os_snprintf(pos, end - pos,
+ "provisioning_sp=%s\n",
+ cred->provisioning_sp);
+ if (os_snprintf_error(end - pos, ret))
+ return pos - buf;
+ pos += ret;
+ }
+
+ if (!cred->domain)
+ goto no_domain;
+
+ i = 0;
+ if (wpa_s->current_bss && wpa_s->current_bss->anqp) {
+ struct wpabuf *names =
+ wpa_s->current_bss->anqp->domain_name;
+ for (i = 0; names && i < cred->num_domain; i++)
+ {
+ if (domain_name_list_contains(
+ names, cred->domain[i], 1))
+ break;
+ }
+ if (i == cred->num_domain)
+ i = 0; /* show first entry by default */
+ }
+ ret = os_snprintf(pos, end - pos, "home_sp=%s\n",
+ cred->domain[i]);
+ if (os_snprintf_error(end - pos, ret))
+ return pos - buf;
+ pos += ret;
+
+ no_domain:
+ if (wpa_s->current_bss == NULL ||
+ wpa_s->current_bss->anqp == NULL)
+ res = -1;
+ else
+ res = interworking_home_sp_cred(
+ wpa_s, cred,
+ wpa_s->current_bss->anqp->domain_name);
+ if (res > 0)
+ type = "home";
+ else if (res == 0)
+ type = "roaming";
+ else
+ type = "unknown";
+
+ ret = os_snprintf(pos, end - pos, "sp_type=%s\n", type);
+ if (os_snprintf_error(end - pos, ret))
+ return pos - buf;
+ pos += ret;
+
+ break;
+ }
+ }
+#endif /* CONFIG_HS20 */
+
+ if (wpa_key_mgmt_wpa_ieee8021x(wpa_s->key_mgmt) ||
+ wpa_s->key_mgmt == WPA_KEY_MGMT_IEEE8021X_NO_WPA) {
+ res = eapol_sm_get_status(wpa_s->eapol, pos, end - pos,
+ verbose);
+ if (res >= 0)
+ pos += res;
+ }
+
+ sess_id = eapol_sm_get_session_id(wpa_s->eapol, &sess_id_len);
+ if (sess_id) {
+ char *start = pos;
+
+ ret = os_snprintf(pos, end - pos, "eap_session_id=");
+ if (os_snprintf_error(end - pos, ret))
+ return start - buf;
+ pos += ret;
+ ret = wpa_snprintf_hex(pos, end - pos, sess_id, sess_id_len);
+ if (ret <= 0)
+ return start - buf;
+ pos += ret;
+ ret = os_snprintf(pos, end - pos, "\n");
+ if (os_snprintf_error(end - pos, ret))
+ return start - buf;
+ pos += ret;
+ }
+
+ res = rsn_preauth_get_status(wpa_s->wpa, pos, end - pos, verbose);
+ if (res >= 0)
+ pos += res;
+
+#ifdef CONFIG_WPS
+ {
+ char uuid_str[100];
+ uuid_bin2str(wpa_s->wps->uuid, uuid_str, sizeof(uuid_str));
+ ret = os_snprintf(pos, end - pos, "uuid=%s\n", uuid_str);
+ if (os_snprintf_error(end - pos, ret))
+ return pos - buf;
+ pos += ret;
+ }
+#endif /* CONFIG_WPS */
+
+#ifdef ANDROID
+ /*
+ * Allow using the STATUS command with default behavior, say for debug,
+ * i.e., don't generate a "fake" CONNECTION and SUPPLICANT_STATE_CHANGE
+ * events with STATUS-NO_EVENTS.
+ */
+ if (os_strcmp(params, "-NO_EVENTS")) {
+ wpa_msg_ctrl(wpa_s, MSG_INFO, WPA_EVENT_STATE_CHANGE
+ "id=%d state=%d BSSID=" MACSTR " SSID=%s",
+ wpa_s->current_ssid ? wpa_s->current_ssid->id : -1,
+ wpa_s->wpa_state,
+ MAC2STR(wpa_s->bssid),
+ wpa_s->current_ssid && wpa_s->current_ssid->ssid ?
+ wpa_ssid_txt(wpa_s->current_ssid->ssid,
+ wpa_s->current_ssid->ssid_len) : "");
+ if (wpa_s->wpa_state == WPA_COMPLETED) {
+ struct wpa_ssid *ssid = wpa_s->current_ssid;
+ wpa_msg_ctrl(wpa_s, MSG_INFO, WPA_EVENT_CONNECTED
+ "- connection to " MACSTR
+ " completed %s [id=%d id_str=%s]",
+ MAC2STR(wpa_s->bssid), "(auth)",
+ ssid ? ssid->id : -1,
+ ssid && ssid->id_str ? ssid->id_str : "");
+ }
+ }
+#endif /* ANDROID */
+
+ return pos - buf;
+}
+
+
+static int wpa_supplicant_ctrl_iface_bssid(struct wpa_supplicant *wpa_s,
+ char *cmd)
+{
+ char *pos;
+ int id;
+ struct wpa_ssid *ssid;
+ u8 bssid[ETH_ALEN];
+
+ /* cmd: "<network id> <BSSID>" */
+ pos = os_strchr(cmd, ' ');
+ if (pos == NULL)
+ return -1;
+ *pos++ = '\0';
+ id = atoi(cmd);
+ wpa_printf(MSG_DEBUG, "CTRL_IFACE: id=%d bssid='%s'", id, pos);
+ if (hwaddr_aton(pos, bssid)) {
+ wpa_printf(MSG_DEBUG ,"CTRL_IFACE: invalid BSSID '%s'", pos);
+ return -1;
+ }
+
+ ssid = wpa_config_get_network(wpa_s->conf, id);
+ if (ssid == NULL) {
+ wpa_printf(MSG_DEBUG, "CTRL_IFACE: Could not find SSID id=%d "
+ "to update", id);
+ return -1;
+ }
+
+ os_memcpy(ssid->bssid, bssid, ETH_ALEN);
+ ssid->bssid_set = !is_zero_ether_addr(bssid);
+
+ return 0;
+}
+
+
+static int wpa_supplicant_ctrl_iface_blacklist(struct wpa_supplicant *wpa_s,
+ char *cmd, char *buf,
+ size_t buflen)
+{
+ u8 bssid[ETH_ALEN];
+ struct wpa_blacklist *e;
+ char *pos, *end;
+ int ret;
+
+ /* cmd: "BLACKLIST [<BSSID>]" */
+ if (*cmd == '\0') {
+ pos = buf;
+ end = buf + buflen;
+ e = wpa_s->blacklist;
+ while (e) {
+ ret = os_snprintf(pos, end - pos, MACSTR "\n",
+ MAC2STR(e->bssid));
+ if (os_snprintf_error(end - pos, ret))
+ return pos - buf;
+ pos += ret;
+ e = e->next;
+ }
+ return pos - buf;
+ }
+
+ cmd++;
+ if (os_strncmp(cmd, "clear", 5) == 0) {
+ wpa_blacklist_clear(wpa_s);
+ os_memcpy(buf, "OK\n", 3);
+ return 3;
+ }
+
+ wpa_printf(MSG_DEBUG, "CTRL_IFACE: BLACKLIST bssid='%s'", cmd);
+ if (hwaddr_aton(cmd, bssid)) {
+ wpa_printf(MSG_DEBUG, "CTRL_IFACE: invalid BSSID '%s'", cmd);
+ return -1;
+ }
+
+ /*
+ * Add the BSSID twice, so its count will be 2, causing it to be
+ * skipped when processing scan results.
+ */
+ ret = wpa_blacklist_add(wpa_s, bssid);
+ if (ret < 0)
+ return -1;
+ ret = wpa_blacklist_add(wpa_s, bssid);
+ if (ret < 0)
+ return -1;
+ os_memcpy(buf, "OK\n", 3);
+ return 3;
+}
+
+
+static int wpa_supplicant_ctrl_iface_log_level(struct wpa_supplicant *wpa_s,
+ char *cmd, char *buf,
+ size_t buflen)
+{
+ char *pos, *end, *stamp;
+ int ret;
+
+ /* cmd: "LOG_LEVEL [<level>]" */
+ if (*cmd == '\0') {
+ pos = buf;
+ end = buf + buflen;
+ ret = os_snprintf(pos, end - pos, "Current level: %s\n"
+ "Timestamp: %d\n",
+ debug_level_str(wpa_debug_level),
+ wpa_debug_timestamp);
+ if (os_snprintf_error(end - pos, ret))
+ ret = 0;
+
+ return ret;
+ }
+
+ while (*cmd == ' ')
+ cmd++;
+
+ stamp = os_strchr(cmd, ' ');
+ if (stamp) {
+ *stamp++ = '\0';
+ while (*stamp == ' ') {
+ stamp++;
+ }
+ }
+
+ if (os_strlen(cmd)) {
+ int level = str_to_debug_level(cmd);
+ if (level < 0)
+ return -1;
+ wpa_debug_level = level;
+ }
+
+ if (stamp && os_strlen(stamp))
+ wpa_debug_timestamp = atoi(stamp);
+
+ os_memcpy(buf, "OK\n", 3);
+ return 3;
+}
+
+
+static int wpa_supplicant_ctrl_iface_list_networks(
+ struct wpa_supplicant *wpa_s, char *cmd, char *buf, size_t buflen)
+{
+ char *pos, *end, *prev;
+ struct wpa_ssid *ssid;
+ int ret;
+
+ pos = buf;
+ end = buf + buflen;
+ ret = os_snprintf(pos, end - pos,
+ "network id / ssid / bssid / flags\n");
+ if (os_snprintf_error(end - pos, ret))
+ return pos - buf;
+ pos += ret;
+
+ ssid = wpa_s->conf->ssid;
+
+ /* skip over ssids until we find next one */
+ if (cmd != NULL && os_strncmp(cmd, "LAST_ID=", 8) == 0) {
+ int last_id = atoi(cmd + 8);
+ if (last_id != -1) {
+ while (ssid != NULL && ssid->id <= last_id) {
+ ssid = ssid->next;
+ }
+ }
+ }
+
+ while (ssid) {
+ prev = pos;
+ ret = os_snprintf(pos, end - pos, "%d\t%s",
+ ssid->id,
+ wpa_ssid_txt(ssid->ssid, ssid->ssid_len));
+ if (os_snprintf_error(end - pos, ret))
+ return prev - buf;
+ pos += ret;
+ if (ssid->bssid_set) {
+ ret = os_snprintf(pos, end - pos, "\t" MACSTR,
+ MAC2STR(ssid->bssid));
+ } else {
+ ret = os_snprintf(pos, end - pos, "\tany");
+ }
+ if (os_snprintf_error(end - pos, ret))
+ return prev - buf;
+ pos += ret;
+ ret = os_snprintf(pos, end - pos, "\t%s%s%s%s",
+ ssid == wpa_s->current_ssid ?
+ "[CURRENT]" : "",
+ ssid->disabled ? "[DISABLED]" : "",
+ ssid->disabled_until.sec ?
+ "[TEMP-DISABLED]" : "",
+ ssid->disabled == 2 ? "[P2P-PERSISTENT]" :
+ "");
+ if (os_snprintf_error(end - pos, ret))
+ return prev - buf;
+ pos += ret;
+ ret = os_snprintf(pos, end - pos, "\n");
+ if (os_snprintf_error(end - pos, ret))
+ return prev - buf;
+ pos += ret;
+
+ ssid = ssid->next;
+ }
+
+ return pos - buf;
+}
+
+
+static char * wpa_supplicant_cipher_txt(char *pos, char *end, int cipher)
+{
+ int ret;
+ ret = os_snprintf(pos, end - pos, "-");
+ if (os_snprintf_error(end - pos, ret))
+ return pos;
+ pos += ret;
+ ret = wpa_write_ciphers(pos, end, cipher, "+");
+ if (ret < 0)
+ return pos;
+ pos += ret;
+ return pos;
+}
+
+
+static char * wpa_supplicant_ie_txt(char *pos, char *end, const char *proto,
+ const u8 *ie, size_t ie_len)
+{
+ struct wpa_ie_data data;
+ char *start;
+ int ret;
+
+ ret = os_snprintf(pos, end - pos, "[%s-", proto);
+ if (os_snprintf_error(end - pos, ret))
+ return pos;
+ pos += ret;
+
+ if (wpa_parse_wpa_ie(ie, ie_len, &data) < 0) {
+ ret = os_snprintf(pos, end - pos, "?]");
+ if (os_snprintf_error(end - pos, ret))
+ return pos;
+ pos += ret;
+ return pos;
+ }
+
+ start = pos;
+ if (data.key_mgmt & WPA_KEY_MGMT_IEEE8021X) {
+ ret = os_snprintf(pos, end - pos, "%sEAP",
+ pos == start ? "" : "+");
+ if (os_snprintf_error(end - pos, ret))
+ return pos;
+ pos += ret;
+ }
+ if (data.key_mgmt & WPA_KEY_MGMT_PSK) {
+ ret = os_snprintf(pos, end - pos, "%sPSK",
+ pos == start ? "" : "+");
+ if (os_snprintf_error(end - pos, ret))
+ return pos;
+ pos += ret;
+ }
+ if (data.key_mgmt & WPA_KEY_MGMT_WPA_NONE) {
+ ret = os_snprintf(pos, end - pos, "%sNone",
+ pos == start ? "" : "+");
+ if (os_snprintf_error(end - pos, ret))
+ return pos;
+ pos += ret;
+ }
+ if (data.key_mgmt & WPA_KEY_MGMT_SAE) {
+ ret = os_snprintf(pos, end - pos, "%sSAE",
+ pos == start ? "" : "+");
+ if (os_snprintf_error(end - pos, ret))
+ return pos;
+ pos += ret;
+ }
+#ifdef CONFIG_IEEE80211R
+ if (data.key_mgmt & WPA_KEY_MGMT_FT_IEEE8021X) {
+ ret = os_snprintf(pos, end - pos, "%sFT/EAP",
+ pos == start ? "" : "+");
+ if (os_snprintf_error(end - pos, ret))
+ return pos;
+ pos += ret;
+ }
+ if (data.key_mgmt & WPA_KEY_MGMT_FT_PSK) {
+ ret = os_snprintf(pos, end - pos, "%sFT/PSK",
+ pos == start ? "" : "+");
+ if (os_snprintf_error(end - pos, ret))
+ return pos;
+ pos += ret;
+ }
+ if (data.key_mgmt & WPA_KEY_MGMT_FT_SAE) {
+ ret = os_snprintf(pos, end - pos, "%sFT/SAE",
+ pos == start ? "" : "+");
+ if (os_snprintf_error(end - pos, ret))
+ return pos;
+ pos += ret;
+ }
+#endif /* CONFIG_IEEE80211R */
+#ifdef CONFIG_IEEE80211W
+ if (data.key_mgmt & WPA_KEY_MGMT_IEEE8021X_SHA256) {
+ ret = os_snprintf(pos, end - pos, "%sEAP-SHA256",
+ pos == start ? "" : "+");
+ if (os_snprintf_error(end - pos, ret))
+ return pos;
+ pos += ret;
+ }
+ if (data.key_mgmt & WPA_KEY_MGMT_PSK_SHA256) {
+ ret = os_snprintf(pos, end - pos, "%sPSK-SHA256",
+ pos == start ? "" : "+");
+ if (os_snprintf_error(end - pos, ret))
+ return pos;
+ pos += ret;
+ }
+#endif /* CONFIG_IEEE80211W */
+
+#ifdef CONFIG_SUITEB
+ if (data.key_mgmt & WPA_KEY_MGMT_IEEE8021X_SUITE_B) {
+ ret = os_snprintf(pos, end - pos, "%sEAP-SUITE-B",
+ pos == start ? "" : "+");
+ if (os_snprintf_error(end - pos, ret))
+ return pos;
+ pos += ret;
+ }
+#endif /* CONFIG_SUITEB */
+
+#ifdef CONFIG_SUITEB192
+ if (data.key_mgmt & WPA_KEY_MGMT_IEEE8021X_SUITE_B_192) {
+ ret = os_snprintf(pos, end - pos, "%sEAP-SUITE-B-192",
+ pos == start ? "" : "+");
+ if (os_snprintf_error(end - pos, ret))
+ return pos;
+ pos += ret;
+ }
+#endif /* CONFIG_SUITEB192 */
+
+ if (data.key_mgmt & WPA_KEY_MGMT_OSEN) {
+ ret = os_snprintf(pos, end - pos, "%sOSEN",
+ pos == start ? "" : "+");
+ if (os_snprintf_error(end - pos, ret))
+ return pos;
+ pos += ret;
+ }
+
+ pos = wpa_supplicant_cipher_txt(pos, end, data.pairwise_cipher);
+
+ if (data.capabilities & WPA_CAPABILITY_PREAUTH) {
+ ret = os_snprintf(pos, end - pos, "-preauth");
+ if (os_snprintf_error(end - pos, ret))
+ return pos;
+ pos += ret;
+ }
+
+ ret = os_snprintf(pos, end - pos, "]");
+ if (os_snprintf_error(end - pos, ret))
+ return pos;
+ pos += ret;
+
+ return pos;
+}
+
+
+#ifdef CONFIG_WPS
+static char * wpa_supplicant_wps_ie_txt_buf(struct wpa_supplicant *wpa_s,
+ char *pos, char *end,
+ struct wpabuf *wps_ie)
+{
+ int ret;
+ const char *txt;
+
+ if (wps_ie == NULL)
+ return pos;
+ if (wps_is_selected_pbc_registrar(wps_ie))
+ txt = "[WPS-PBC]";
+ else if (wps_is_addr_authorized(wps_ie, wpa_s->own_addr, 0))
+ txt = "[WPS-AUTH]";
+ else if (wps_is_selected_pin_registrar(wps_ie))
+ txt = "[WPS-PIN]";
+ else
+ txt = "[WPS]";
+
+ ret = os_snprintf(pos, end - pos, "%s", txt);
+ if (!os_snprintf_error(end - pos, ret))
+ pos += ret;
+ wpabuf_free(wps_ie);
+ return pos;
+}
+#endif /* CONFIG_WPS */
+
+
+static char * wpa_supplicant_wps_ie_txt(struct wpa_supplicant *wpa_s,
+ char *pos, char *end,
+ const struct wpa_bss *bss)
+{
+#ifdef CONFIG_WPS
+ struct wpabuf *wps_ie;
+ wps_ie = wpa_bss_get_vendor_ie_multi(bss, WPS_IE_VENDOR_TYPE);
+ return wpa_supplicant_wps_ie_txt_buf(wpa_s, pos, end, wps_ie);
+#else /* CONFIG_WPS */
+ return pos;
+#endif /* CONFIG_WPS */
+}
+
+
+/* Format one result on one text line into a buffer. */
+static int wpa_supplicant_ctrl_iface_scan_result(
+ struct wpa_supplicant *wpa_s,
+ const struct wpa_bss *bss, char *buf, size_t buflen)
+{
+ char *pos, *end;
+ int ret;
+ const u8 *ie, *ie2, *osen_ie, *p2p, *mesh;
+
+ mesh = wpa_bss_get_ie(bss, WLAN_EID_MESH_ID);
+ p2p = wpa_bss_get_vendor_ie(bss, P2P_IE_VENDOR_TYPE);
+ if (!p2p)
+ p2p = wpa_bss_get_vendor_ie_beacon(bss, P2P_IE_VENDOR_TYPE);
+ if (p2p && bss->ssid_len == P2P_WILDCARD_SSID_LEN &&
+ os_memcmp(bss->ssid, P2P_WILDCARD_SSID, P2P_WILDCARD_SSID_LEN) ==
+ 0)
+ return 0; /* Do not show P2P listen discovery results here */
+
+ pos = buf;
+ end = buf + buflen;
+
+ ret = os_snprintf(pos, end - pos, MACSTR "\t%d\t%d\t",
+ MAC2STR(bss->bssid), bss->freq, bss->level);
+ if (os_snprintf_error(end - pos, ret))
+ return -1;
+ pos += ret;
+ ie = wpa_bss_get_vendor_ie(bss, WPA_IE_VENDOR_TYPE);
+ if (ie)
+ pos = wpa_supplicant_ie_txt(pos, end, "WPA", ie, 2 + ie[1]);
+ ie2 = wpa_bss_get_ie(bss, WLAN_EID_RSN);
+ if (ie2) {
+ pos = wpa_supplicant_ie_txt(pos, end, mesh ? "RSN" : "WPA2",
+ ie2, 2 + ie2[1]);
+ }
+ osen_ie = wpa_bss_get_vendor_ie(bss, OSEN_IE_VENDOR_TYPE);
+ if (osen_ie)
+ pos = wpa_supplicant_ie_txt(pos, end, "OSEN",
+ osen_ie, 2 + osen_ie[1]);
+ pos = wpa_supplicant_wps_ie_txt(wpa_s, pos, end, bss);
+ if (!ie && !ie2 && !osen_ie && (bss->caps & IEEE80211_CAP_PRIVACY)) {
+ ret = os_snprintf(pos, end - pos, "[WEP]");
+ if (os_snprintf_error(end - pos, ret))
+ return -1;
+ pos += ret;
+ }
+ if (mesh) {
+ ret = os_snprintf(pos, end - pos, "[MESH]");
+ if (os_snprintf_error(end - pos, ret))
+ return -1;
+ pos += ret;
+ }
+ if (bss_is_dmg(bss)) {
+ const char *s;
+ ret = os_snprintf(pos, end - pos, "[DMG]");
+ if (os_snprintf_error(end - pos, ret))
+ return -1;
+ pos += ret;
+ switch (bss->caps & IEEE80211_CAP_DMG_MASK) {
+ case IEEE80211_CAP_DMG_IBSS:
+ s = "[IBSS]";
+ break;
+ case IEEE80211_CAP_DMG_AP:
+ s = "[ESS]";
+ break;
+ case IEEE80211_CAP_DMG_PBSS:
+ s = "[PBSS]";
+ break;
+ default:
+ s = "";
+ break;
+ }
+ ret = os_snprintf(pos, end - pos, "%s", s);
+ if (os_snprintf_error(end - pos, ret))
+ return -1;
+ pos += ret;
+ } else {
+ if (bss->caps & IEEE80211_CAP_IBSS) {
+ ret = os_snprintf(pos, end - pos, "[IBSS]");
+ if (os_snprintf_error(end - pos, ret))
+ return -1;
+ pos += ret;
+ }
+ if (bss->caps & IEEE80211_CAP_ESS) {
+ ret = os_snprintf(pos, end - pos, "[ESS]");
+ if (os_snprintf_error(end - pos, ret))
+ return -1;
+ pos += ret;
+ }
+ }
+ if (p2p) {
+ ret = os_snprintf(pos, end - pos, "[P2P]");
+ if (os_snprintf_error(end - pos, ret))
+ return -1;
+ pos += ret;
+ }
+#ifdef CONFIG_HS20
+ if (wpa_bss_get_vendor_ie(bss, HS20_IE_VENDOR_TYPE) && ie2) {
+ ret = os_snprintf(pos, end - pos, "[HS20]");
+ if (os_snprintf_error(end - pos, ret))
+ return -1;
+ pos += ret;
+ }
+#endif /* CONFIG_HS20 */
+#ifdef CONFIG_FST
+ if (wpa_bss_get_ie(bss, WLAN_EID_MULTI_BAND)) {
+ ret = os_snprintf(pos, end - pos, "[FST]");
+ if (os_snprintf_error(end - pos, ret))
+ return -1;
+ pos += ret;
+ }
+#endif /* CONFIG_FST */
+
+ ret = os_snprintf(pos, end - pos, "\t%s",
+ wpa_ssid_txt(bss->ssid, bss->ssid_len));
+ if (os_snprintf_error(end - pos, ret))
+ return -1;
+ pos += ret;
+
+ ret = os_snprintf(pos, end - pos, "\n");
+ if (os_snprintf_error(end - pos, ret))
+ return -1;
+ pos += ret;
+
+ return pos - buf;
+}
+
+
+static int wpa_supplicant_ctrl_iface_scan_results(
+ struct wpa_supplicant *wpa_s, char *buf, size_t buflen)
+{
+ char *pos, *end;
+ struct wpa_bss *bss;
+ int ret;
+
+ pos = buf;
+ end = buf + buflen;
+ ret = os_snprintf(pos, end - pos, "bssid / frequency / signal level / "
+ "flags / ssid\n");
+ if (os_snprintf_error(end - pos, ret))
+ return pos - buf;
+ pos += ret;
+
+ dl_list_for_each(bss, &wpa_s->bss_id, struct wpa_bss, list_id) {
+ ret = wpa_supplicant_ctrl_iface_scan_result(wpa_s, bss, pos,
+ end - pos);
+ if (ret < 0 || ret >= end - pos)
+ return pos - buf;
+ pos += ret;
+ }
+
+ return pos - buf;
+}
+
+
+#ifdef CONFIG_MESH
+
+static int wpa_supplicant_ctrl_iface_mesh_interface_add(
+ struct wpa_supplicant *wpa_s, char *cmd, char *reply, size_t max_len)
+{
+ char *pos, ifname[IFNAMSIZ + 1];
+
+ ifname[0] = '\0';
+
+ pos = os_strstr(cmd, "ifname=");
+ if (pos) {
+ pos += 7;
+ os_strlcpy(ifname, pos, sizeof(ifname));
+ }
+
+ if (wpas_mesh_add_interface(wpa_s, ifname, sizeof(ifname)) < 0)
+ return -1;
+
+ os_strlcpy(reply, ifname, max_len);
+ return os_strlen(ifname);
+}
+
+
+static int wpa_supplicant_ctrl_iface_mesh_group_add(
+ struct wpa_supplicant *wpa_s, char *cmd)
+{
+ int id;
+ struct wpa_ssid *ssid;
+
+ id = atoi(cmd);
+ wpa_printf(MSG_DEBUG, "CTRL_IFACE: MESH_GROUP_ADD id=%d", id);
+
+ ssid = wpa_config_get_network(wpa_s->conf, id);
+ if (ssid == NULL) {
+ wpa_printf(MSG_DEBUG,
+ "CTRL_IFACE: Could not find network id=%d", id);
+ return -1;
+ }
+ if (ssid->mode != WPAS_MODE_MESH) {
+ wpa_printf(MSG_DEBUG,
+ "CTRL_IFACE: Cannot use MESH_GROUP_ADD on a non mesh network");
+ return -1;
+ }
+ if (ssid->key_mgmt != WPA_KEY_MGMT_NONE &&
+ ssid->key_mgmt != WPA_KEY_MGMT_SAE) {
+ wpa_printf(MSG_ERROR,
+ "CTRL_IFACE: key_mgmt for mesh network should be open or SAE");
+ return -1;
+ }
+
+ /*
+ * TODO: If necessary write our own group_add function,
+ * for now we can reuse select_network
+ */
+ wpa_supplicant_select_network(wpa_s, ssid);
+
+ return 0;
+}
+
+
+static int wpa_supplicant_ctrl_iface_mesh_group_remove(
+ struct wpa_supplicant *wpa_s, char *cmd)
+{
+ struct wpa_supplicant *orig;
+ struct wpa_global *global;
+ int found = 0;
+
+ wpa_printf(MSG_DEBUG, "CTRL_IFACE: MESH_GROUP_REMOVE ifname=%s", cmd);
+
+ global = wpa_s->global;
+ orig = wpa_s;
+
+ for (wpa_s = global->ifaces; wpa_s; wpa_s = wpa_s->next) {
+ if (os_strcmp(wpa_s->ifname, cmd) == 0) {
+ found = 1;
+ break;
+ }
+ }
+ if (!found) {
+ wpa_printf(MSG_ERROR,
+ "CTRL_IFACE: MESH_GROUP_REMOVE ifname=%s not found",
+ cmd);
+ return -1;
+ }
+ if (wpa_s->mesh_if_created && wpa_s == orig) {
+ wpa_printf(MSG_ERROR,
+ "CTRL_IFACE: MESH_GROUP_REMOVE can't remove itself");
+ return -1;
+ }
+
+ wpa_s->reassociate = 0;
+ wpa_s->disconnected = 1;
+ wpa_supplicant_cancel_sched_scan(wpa_s);
+ wpa_supplicant_cancel_scan(wpa_s);
+
+ /*
+ * TODO: If necessary write our own group_remove function,
+ * for now we can reuse deauthenticate
+ */
+ wpa_supplicant_deauthenticate(wpa_s, WLAN_REASON_DEAUTH_LEAVING);
+
+ if (wpa_s->mesh_if_created)
+ wpa_supplicant_remove_iface(global, wpa_s, 0);
+
+ return 0;
+}
+
+#endif /* CONFIG_MESH */
+
+
+static int wpa_supplicant_ctrl_iface_select_network(
+ struct wpa_supplicant *wpa_s, char *cmd)
+{
+ int id;
+ struct wpa_ssid *ssid;
+ char *pos;
+
+ /* cmd: "<network id>" or "any" */
+ if (os_strncmp(cmd, "any", 3) == 0) {
+ wpa_printf(MSG_DEBUG, "CTRL_IFACE: SELECT_NETWORK any");
+ ssid = NULL;
+ } else {
+ id = atoi(cmd);
+ wpa_printf(MSG_DEBUG, "CTRL_IFACE: SELECT_NETWORK id=%d", id);
+
+ ssid = wpa_config_get_network(wpa_s->conf, id);
+ if (ssid == NULL) {
+ wpa_printf(MSG_DEBUG, "CTRL_IFACE: Could not find "
+ "network id=%d", id);
+ return -1;
+ }
+ if (ssid->disabled == 2) {
+ wpa_printf(MSG_DEBUG, "CTRL_IFACE: Cannot use "
+ "SELECT_NETWORK with persistent P2P group");
+ return -1;
+ }
+ }
+
+ pos = os_strstr(cmd, " freq=");
+ if (pos) {
+ int *freqs = freq_range_to_channel_list(wpa_s, pos + 6);
+ if (freqs) {
+ wpa_s->scan_req = MANUAL_SCAN_REQ;
+ os_free(wpa_s->manual_scan_freqs);
+ wpa_s->manual_scan_freqs = freqs;
+ }
+ }
+
+ wpa_s->scan_min_time.sec = 0;
+ wpa_s->scan_min_time.usec = 0;
+ wpa_supplicant_select_network(wpa_s, ssid);
+
+ return 0;
+}
+
+
+static int wpa_supplicant_ctrl_iface_enable_network(
+ struct wpa_supplicant *wpa_s, char *cmd)
+{
+ int id;
+ struct wpa_ssid *ssid;
+
+ /* cmd: "<network id>" or "all" */
+ if (os_strcmp(cmd, "all") == 0) {
+ wpa_printf(MSG_DEBUG, "CTRL_IFACE: ENABLE_NETWORK all");
+ ssid = NULL;
+ } else {
+ id = atoi(cmd);
+ wpa_printf(MSG_DEBUG, "CTRL_IFACE: ENABLE_NETWORK id=%d", id);
+
+ ssid = wpa_config_get_network(wpa_s->conf, id);
+ if (ssid == NULL) {
+ wpa_printf(MSG_DEBUG, "CTRL_IFACE: Could not find "
+ "network id=%d", id);
+ return -1;
+ }
+ if (ssid->disabled == 2) {
+ wpa_printf(MSG_DEBUG, "CTRL_IFACE: Cannot use "
+ "ENABLE_NETWORK with persistent P2P group");
+ return -1;
+ }
+
+ if (os_strstr(cmd, " no-connect")) {
+ ssid->disabled = 0;
+ return 0;
+ }
+ }
+ wpa_s->scan_min_time.sec = 0;
+ wpa_s->scan_min_time.usec = 0;
+ wpa_supplicant_enable_network(wpa_s, ssid);
+
+ return 0;
+}
+
+
+static int wpa_supplicant_ctrl_iface_disable_network(
+ struct wpa_supplicant *wpa_s, char *cmd)
+{
+ int id;
+ struct wpa_ssid *ssid;
+
+ /* cmd: "<network id>" or "all" */
+ if (os_strcmp(cmd, "all") == 0) {
+ wpa_printf(MSG_DEBUG, "CTRL_IFACE: DISABLE_NETWORK all");
+ ssid = NULL;
+ } else {
+ id = atoi(cmd);
+ wpa_printf(MSG_DEBUG, "CTRL_IFACE: DISABLE_NETWORK id=%d", id);
+
+ ssid = wpa_config_get_network(wpa_s->conf, id);
+ if (ssid == NULL) {
+ wpa_printf(MSG_DEBUG, "CTRL_IFACE: Could not find "
+ "network id=%d", id);
+ return -1;
+ }
+ if (ssid->disabled == 2) {
+ wpa_printf(MSG_DEBUG, "CTRL_IFACE: Cannot use "
+ "DISABLE_NETWORK with persistent P2P "
+ "group");
+ return -1;
+ }
+ }
+ wpa_supplicant_disable_network(wpa_s, ssid);
+
+ return 0;
+}
+
+
+static int wpa_supplicant_ctrl_iface_add_network(
+ struct wpa_supplicant *wpa_s, char *buf, size_t buflen)
+{
+ struct wpa_ssid *ssid;
+ int ret;
+
+ wpa_printf(MSG_DEBUG, "CTRL_IFACE: ADD_NETWORK");
+
+ ssid = wpa_config_add_network(wpa_s->conf);
+ if (ssid == NULL)
+ return -1;
+
+ wpas_notify_network_added(wpa_s, ssid);
+
+ ssid->disabled = 1;
+ wpa_config_set_network_defaults(ssid);
+
+ ret = os_snprintf(buf, buflen, "%d\n", ssid->id);
+ if (os_snprintf_error(buflen, ret))
+ return -1;
+ return ret;
+}
+
+
+static int wpa_supplicant_ctrl_iface_remove_network(
+ struct wpa_supplicant *wpa_s, char *cmd)
+{
+ int id;
+ struct wpa_ssid *ssid;
+ int was_disabled;
+
+ /* cmd: "<network id>" or "all" */
+ if (os_strcmp(cmd, "all") == 0) {
+ wpa_printf(MSG_DEBUG, "CTRL_IFACE: REMOVE_NETWORK all");
+ if (wpa_s->sched_scanning)
+ wpa_supplicant_cancel_sched_scan(wpa_s);
+
+ eapol_sm_invalidate_cached_session(wpa_s->eapol);
+ if (wpa_s->current_ssid) {
+#ifdef CONFIG_SME
+ wpa_s->sme.prev_bssid_set = 0;
+#endif /* CONFIG_SME */
+ wpa_sm_set_config(wpa_s->wpa, NULL);
+ eapol_sm_notify_config(wpa_s->eapol, NULL, NULL);
+ if (wpa_s->wpa_state >= WPA_AUTHENTICATING)
+ wpa_s->own_disconnect_req = 1;
+ wpa_supplicant_deauthenticate(
+ wpa_s, WLAN_REASON_DEAUTH_LEAVING);
+ }
+ ssid = wpa_s->conf->ssid;
+ while (ssid) {
+ struct wpa_ssid *remove_ssid = ssid;
+ id = ssid->id;
+ ssid = ssid->next;
+ if (wpa_s->last_ssid == remove_ssid)
+ wpa_s->last_ssid = NULL;
+ wpas_notify_network_removed(wpa_s, remove_ssid);
+ wpa_config_remove_network(wpa_s->conf, id);
+ }
+ return 0;
+ }
+
+ id = atoi(cmd);
+ wpa_printf(MSG_DEBUG, "CTRL_IFACE: REMOVE_NETWORK id=%d", id);
+
+ ssid = wpa_config_get_network(wpa_s->conf, id);
+ if (ssid)
+ wpas_notify_network_removed(wpa_s, ssid);
+ if (ssid == NULL) {
+ wpa_printf(MSG_DEBUG, "CTRL_IFACE: Could not find network "
+ "id=%d", id);
+ return -1;
+ }
+
+ if (wpa_s->last_ssid == ssid)
+ wpa_s->last_ssid = NULL;
+
+ if (ssid == wpa_s->current_ssid || wpa_s->current_ssid == NULL) {
+#ifdef CONFIG_SME
+ wpa_s->sme.prev_bssid_set = 0;
+#endif /* CONFIG_SME */
+ /*
+ * Invalidate the EAP session cache if the current or
+ * previously used network is removed.
+ */
+ eapol_sm_invalidate_cached_session(wpa_s->eapol);
+ }
+
+ if (ssid == wpa_s->current_ssid) {
+ wpa_sm_set_config(wpa_s->wpa, NULL);
+ eapol_sm_notify_config(wpa_s->eapol, NULL, NULL);
+
+ if (wpa_s->wpa_state >= WPA_AUTHENTICATING)
+ wpa_s->own_disconnect_req = 1;
+ wpa_supplicant_deauthenticate(wpa_s,
+ WLAN_REASON_DEAUTH_LEAVING);
+ }
+
+ was_disabled = ssid->disabled;
+
+ if (wpa_config_remove_network(wpa_s->conf, id) < 0) {
+ wpa_printf(MSG_DEBUG, "CTRL_IFACE: Not able to remove the "
+ "network id=%d", id);
+ return -1;
+ }
+
+ if (!was_disabled && wpa_s->sched_scanning) {
+ wpa_printf(MSG_DEBUG, "Stop ongoing sched_scan to remove "
+ "network from filters");
+ wpa_supplicant_cancel_sched_scan(wpa_s);
+ wpa_supplicant_req_scan(wpa_s, 0, 0);
+ }
+
+ return 0;
+}
+
+
+static int wpa_supplicant_ctrl_iface_update_network(
+ struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid,
+ char *name, char *value)
+{
+ if (wpa_config_set(ssid, name, value, 0) < 0) {
+ wpa_printf(MSG_DEBUG, "CTRL_IFACE: Failed to set network "
+ "variable '%s'", name);
+ return -1;
+ }
+
+ if (os_strcmp(name, "bssid") != 0 &&
+ os_strcmp(name, "priority") != 0)
+ wpa_sm_pmksa_cache_flush(wpa_s->wpa, ssid);
+
+ if (wpa_s->current_ssid == ssid || wpa_s->current_ssid == NULL) {
+ /*
+ * Invalidate the EAP session cache if anything in the current
+ * or previously used configuration changes.
+ */
+ eapol_sm_invalidate_cached_session(wpa_s->eapol);
+ }
+
+ if ((os_strcmp(name, "psk") == 0 &&
+ value[0] == '"' && ssid->ssid_len) ||
+ (os_strcmp(name, "ssid") == 0 && ssid->passphrase))
+ wpa_config_update_psk(ssid);
+ else if (os_strcmp(name, "priority") == 0)
+ wpa_config_update_prio_list(wpa_s->conf);
+
+ return 0;
+}
+
+
+static int wpa_supplicant_ctrl_iface_set_network(
+ struct wpa_supplicant *wpa_s, char *cmd)
+{
+ int id, ret, prev_bssid_set, prev_disabled;
+ struct wpa_ssid *ssid;
+ char *name, *value;
+ u8 prev_bssid[ETH_ALEN];
+
+ /* cmd: "<network id> <variable name> <value>" */
+ name = os_strchr(cmd, ' ');
+ if (name == NULL)
+ return -1;
+ *name++ = '\0';
+
+ value = os_strchr(name, ' ');
+ if (value == NULL)
+ return -1;
+ *value++ = '\0';
+
+ id = atoi(cmd);
+ wpa_printf(MSG_DEBUG, "CTRL_IFACE: SET_NETWORK id=%d name='%s'",
+ id, name);
+ wpa_hexdump_ascii_key(MSG_DEBUG, "CTRL_IFACE: value",
+ (u8 *) value, os_strlen(value));
+
+ ssid = wpa_config_get_network(wpa_s->conf, id);
+ if (ssid == NULL) {
+ wpa_printf(MSG_DEBUG, "CTRL_IFACE: Could not find network "
+ "id=%d", id);
+ return -1;
+ }
+
+ prev_bssid_set = ssid->bssid_set;
+ prev_disabled = ssid->disabled;
+ os_memcpy(prev_bssid, ssid->bssid, ETH_ALEN);
+ ret = wpa_supplicant_ctrl_iface_update_network(wpa_s, ssid, name,
+ value);
+ if (ret == 0 &&
+ (ssid->bssid_set != prev_bssid_set ||
+ os_memcmp(ssid->bssid, prev_bssid, ETH_ALEN) != 0))
+ wpas_notify_network_bssid_set_changed(wpa_s, ssid);
+
+ if (prev_disabled != ssid->disabled &&
+ (prev_disabled == 2 || ssid->disabled == 2))
+ wpas_notify_network_type_changed(wpa_s, ssid);
+
+ return ret;
+}
+
+
+static int wpa_supplicant_ctrl_iface_get_network(
+ struct wpa_supplicant *wpa_s, char *cmd, char *buf, size_t buflen)
+{
+ int id;
+ size_t res;
+ struct wpa_ssid *ssid;
+ char *name, *value;
+
+ /* cmd: "<network id> <variable name>" */
+ name = os_strchr(cmd, ' ');
+ if (name == NULL || buflen == 0)
+ return -1;
+ *name++ = '\0';
+
+ id = atoi(cmd);
+ wpa_printf(MSG_EXCESSIVE, "CTRL_IFACE: GET_NETWORK id=%d name='%s'",
+ id, name);
+
+ ssid = wpa_config_get_network(wpa_s->conf, id);
+ if (ssid == NULL) {
+ wpa_printf(MSG_EXCESSIVE, "CTRL_IFACE: Could not find network "
+ "id=%d", id);
+ return -1;
+ }
+
+ value = wpa_config_get_no_key(ssid, name);
+ if (value == NULL) {
+ wpa_printf(MSG_EXCESSIVE, "CTRL_IFACE: Failed to get network "
+ "variable '%s'", name);
+ return -1;
+ }
+
+ res = os_strlcpy(buf, value, buflen);
+ if (res >= buflen) {
+ os_free(value);
+ return -1;
+ }
+
+ os_free(value);
+
+ return res;
+}
+
+
+static int wpa_supplicant_ctrl_iface_dup_network(
+ struct wpa_supplicant *wpa_s, char *cmd,
+ struct wpa_supplicant *dst_wpa_s)
+{
+ struct wpa_ssid *ssid_s, *ssid_d;
+ char *name, *id, *value;
+ int id_s, id_d, ret;
+
+ /* cmd: "<src network id> <dst network id> <variable name>" */
+ id = os_strchr(cmd, ' ');
+ if (id == NULL)
+ return -1;
+ *id++ = '\0';
+
+ name = os_strchr(id, ' ');
+ if (name == NULL)
+ return -1;
+ *name++ = '\0';
+
+ id_s = atoi(cmd);
+ id_d = atoi(id);
+
+ wpa_printf(MSG_DEBUG,
+ "CTRL_IFACE: DUP_NETWORK ifname=%s->%s id=%d->%d name='%s'",
+ wpa_s->ifname, dst_wpa_s->ifname, id_s, id_d, name);
+
+ ssid_s = wpa_config_get_network(wpa_s->conf, id_s);
+ if (ssid_s == NULL) {
+ wpa_printf(MSG_DEBUG, "CTRL_IFACE: Could not find "
+ "network id=%d", id_s);
+ return -1;
+ }
+
+ ssid_d = wpa_config_get_network(dst_wpa_s->conf, id_d);
+ if (ssid_d == NULL) {
+ wpa_printf(MSG_DEBUG, "CTRL_IFACE: Could not find "
+ "network id=%d", id_d);
+ return -1;
+ }
+
+ value = wpa_config_get(ssid_s, name);
+ if (value == NULL) {
+ wpa_printf(MSG_DEBUG, "CTRL_IFACE: Failed to get network "
+ "variable '%s'", name);
+ return -1;
+ }
+
+ ret = wpa_supplicant_ctrl_iface_update_network(dst_wpa_s, ssid_d, name,
+ value);
+
+ os_free(value);
+
+ return ret;
+}
+
+
+static int wpa_supplicant_ctrl_iface_list_creds(struct wpa_supplicant *wpa_s,
+ char *buf, size_t buflen)
+{
+ char *pos, *end;
+ struct wpa_cred *cred;
+ int ret;
+
+ pos = buf;
+ end = buf + buflen;
+ ret = os_snprintf(pos, end - pos,
+ "cred id / realm / username / domain / imsi\n");
+ if (os_snprintf_error(end - pos, ret))
+ return pos - buf;
+ pos += ret;
+
+ cred = wpa_s->conf->cred;
+ while (cred) {
+ ret = os_snprintf(pos, end - pos, "%d\t%s\t%s\t%s\t%s\n",
+ cred->id, cred->realm ? cred->realm : "",
+ cred->username ? cred->username : "",
+ cred->domain ? cred->domain[0] : "",
+ cred->imsi ? cred->imsi : "");
+ if (os_snprintf_error(end - pos, ret))
+ return pos - buf;
+ pos += ret;
+
+ cred = cred->next;
+ }
+
+ return pos - buf;
+}
+
+
+static int wpa_supplicant_ctrl_iface_add_cred(struct wpa_supplicant *wpa_s,
+ char *buf, size_t buflen)
+{
+ struct wpa_cred *cred;
+ int ret;
+
+ wpa_printf(MSG_DEBUG, "CTRL_IFACE: ADD_CRED");
+
+ cred = wpa_config_add_cred(wpa_s->conf);
+ if (cred == NULL)
+ return -1;
+
+ wpa_msg(wpa_s, MSG_INFO, CRED_ADDED "%d", cred->id);
+
+ ret = os_snprintf(buf, buflen, "%d\n", cred->id);
+ if (os_snprintf_error(buflen, ret))
+ return -1;
+ return ret;
+}
+
+
+static int wpas_ctrl_remove_cred(struct wpa_supplicant *wpa_s,
+ struct wpa_cred *cred)
+{
+ struct wpa_ssid *ssid;
+ char str[20];
+ int id;
+
+ if (cred == NULL) {
+ wpa_printf(MSG_DEBUG, "CTRL_IFACE: Could not find cred");
+ return -1;
+ }
+
+ id = cred->id;
+ if (wpa_config_remove_cred(wpa_s->conf, id) < 0) {
+ wpa_printf(MSG_DEBUG, "CTRL_IFACE: Could not find cred");
+ return -1;
+ }
+
+ wpa_msg(wpa_s, MSG_INFO, CRED_REMOVED "%d", id);
+
+ /* Remove any network entry created based on the removed credential */
+ ssid = wpa_s->conf->ssid;
+ while (ssid) {
+ if (ssid->parent_cred == cred) {
+ int res;
+
+ wpa_printf(MSG_DEBUG, "Remove network id %d since it "
+ "used the removed credential", ssid->id);
+ res = os_snprintf(str, sizeof(str), "%d", ssid->id);
+ if (os_snprintf_error(sizeof(str), res))
+ str[sizeof(str) - 1] = '\0';
+ ssid = ssid->next;
+ wpa_supplicant_ctrl_iface_remove_network(wpa_s, str);
+ } else
+ ssid = ssid->next;
+ }
+
+ return 0;
+}
+
+
+static int wpa_supplicant_ctrl_iface_remove_cred(struct wpa_supplicant *wpa_s,
+ char *cmd)
+{
+ int id;
+ struct wpa_cred *cred, *prev;
+
+ /* cmd: "<cred id>", "all", "sp_fqdn=<FQDN>", or
+ * "provisioning_sp=<FQDN> */
+ if (os_strcmp(cmd, "all") == 0) {
+ wpa_printf(MSG_DEBUG, "CTRL_IFACE: REMOVE_CRED all");
+ cred = wpa_s->conf->cred;
+ while (cred) {
+ prev = cred;
+ cred = cred->next;
+ wpas_ctrl_remove_cred(wpa_s, prev);
+ }
+ return 0;
+ }
+
+ if (os_strncmp(cmd, "sp_fqdn=", 8) == 0) {
+ wpa_printf(MSG_DEBUG, "CTRL_IFACE: REMOVE_CRED SP FQDN '%s'",
+ cmd + 8);
+ cred = wpa_s->conf->cred;
+ while (cred) {
+ prev = cred;
+ cred = cred->next;
+ if (prev->domain) {
+ size_t i;
+ for (i = 0; i < prev->num_domain; i++) {
+ if (os_strcmp(prev->domain[i], cmd + 8)
+ != 0)
+ continue;
+ wpas_ctrl_remove_cred(wpa_s, prev);
+ break;
+ }
+ }
+ }
+ return 0;
+ }
+
+ if (os_strncmp(cmd, "provisioning_sp=", 16) == 0) {
+ wpa_printf(MSG_DEBUG, "CTRL_IFACE: REMOVE_CRED provisioning SP FQDN '%s'",
+ cmd + 16);
+ cred = wpa_s->conf->cred;
+ while (cred) {
+ prev = cred;
+ cred = cred->next;
+ if (prev->provisioning_sp &&
+ os_strcmp(prev->provisioning_sp, cmd + 16) == 0)
+ wpas_ctrl_remove_cred(wpa_s, prev);
+ }
+ return 0;
+ }
+
+ id = atoi(cmd);
+ wpa_printf(MSG_DEBUG, "CTRL_IFACE: REMOVE_CRED id=%d", id);
+
+ cred = wpa_config_get_cred(wpa_s->conf, id);
+ return wpas_ctrl_remove_cred(wpa_s, cred);
+}
+
+
+static int wpa_supplicant_ctrl_iface_set_cred(struct wpa_supplicant *wpa_s,
+ char *cmd)
+{
+ int id;
+ struct wpa_cred *cred;
+ char *name, *value;
+
+ /* cmd: "<cred id> <variable name> <value>" */
+ name = os_strchr(cmd, ' ');
+ if (name == NULL)
+ return -1;
+ *name++ = '\0';
+
+ value = os_strchr(name, ' ');
+ if (value == NULL)
+ return -1;
+ *value++ = '\0';
+
+ id = atoi(cmd);
+ wpa_printf(MSG_DEBUG, "CTRL_IFACE: SET_CRED id=%d name='%s'",
+ id, name);
+ wpa_hexdump_ascii_key(MSG_DEBUG, "CTRL_IFACE: value",
+ (u8 *) value, os_strlen(value));
+
+ cred = wpa_config_get_cred(wpa_s->conf, id);
+ if (cred == NULL) {
+ wpa_printf(MSG_DEBUG, "CTRL_IFACE: Could not find cred id=%d",
+ id);
+ return -1;
+ }
+
+ if (wpa_config_set_cred(cred, name, value, 0) < 0) {
+ wpa_printf(MSG_DEBUG, "CTRL_IFACE: Failed to set cred "
+ "variable '%s'", name);
+ return -1;
+ }
+
+ wpa_msg(wpa_s, MSG_INFO, CRED_MODIFIED "%d %s", cred->id, name);
+
+ return 0;
+}
+
+
+static int wpa_supplicant_ctrl_iface_get_cred(struct wpa_supplicant *wpa_s,
+ char *cmd, char *buf,
+ size_t buflen)
+{
+ int id;
+ size_t res;
+ struct wpa_cred *cred;
+ char *name, *value;
+
+ /* cmd: "<cred id> <variable name>" */
+ name = os_strchr(cmd, ' ');
+ if (name == NULL)
+ return -1;
+ *name++ = '\0';
+
+ id = atoi(cmd);
+ wpa_printf(MSG_DEBUG, "CTRL_IFACE: GET_CRED id=%d name='%s'",
+ id, name);
+
+ cred = wpa_config_get_cred(wpa_s->conf, id);
+ if (cred == NULL) {
+ wpa_printf(MSG_DEBUG, "CTRL_IFACE: Could not find cred id=%d",
+ id);
+ return -1;
+ }
+
+ value = wpa_config_get_cred_no_key(cred, name);
+ if (value == NULL) {
+ wpa_printf(MSG_DEBUG, "CTRL_IFACE: Failed to get cred variable '%s'",
+ name);
+ return -1;
+ }
+
+ res = os_strlcpy(buf, value, buflen);
+ if (res >= buflen) {
+ os_free(value);
+ return -1;
+ }
+
+ os_free(value);
+
+ return res;
+}
+
+
+#ifndef CONFIG_NO_CONFIG_WRITE
+static int wpa_supplicant_ctrl_iface_save_config(struct wpa_supplicant *wpa_s)
+{
+ int ret;
+
+ if (!wpa_s->conf->update_config) {
+ wpa_printf(MSG_DEBUG, "CTRL_IFACE: SAVE_CONFIG - Not allowed "
+ "to update configuration (update_config=0)");
+ return -1;
+ }
+
+ ret = wpa_config_write(wpa_s->confname, wpa_s->conf);
+ if (ret) {
+ wpa_printf(MSG_DEBUG, "CTRL_IFACE: SAVE_CONFIG - Failed to "
+ "update configuration");
+ } else {
+ wpa_printf(MSG_DEBUG, "CTRL_IFACE: SAVE_CONFIG - Configuration"
+ " updated");
+ }
+
+ return ret;
+}
+#endif /* CONFIG_NO_CONFIG_WRITE */
+
+
+struct cipher_info {
+ unsigned int capa;
+ const char *name;
+ int group_only;
+};
+
+static const struct cipher_info ciphers[] = {
+ { WPA_DRIVER_CAPA_ENC_CCMP_256, "CCMP-256", 0 },
+ { WPA_DRIVER_CAPA_ENC_GCMP_256, "GCMP-256", 0 },
+ { WPA_DRIVER_CAPA_ENC_CCMP, "CCMP", 0 },
+ { WPA_DRIVER_CAPA_ENC_GCMP, "GCMP", 0 },
+ { WPA_DRIVER_CAPA_ENC_TKIP, "TKIP", 0 },
+ { WPA_DRIVER_CAPA_KEY_MGMT_WPA_NONE, "NONE", 0 },
+ { WPA_DRIVER_CAPA_ENC_WEP104, "WEP104", 1 },
+ { WPA_DRIVER_CAPA_ENC_WEP40, "WEP40", 1 }
+};
+
+static const struct cipher_info ciphers_group_mgmt[] = {
+ { WPA_DRIVER_CAPA_ENC_BIP, "AES-128-CMAC", 1 },
+ { WPA_DRIVER_CAPA_ENC_BIP_GMAC_128, "BIP-GMAC-128", 1 },
+ { WPA_DRIVER_CAPA_ENC_BIP_GMAC_256, "BIP-GMAC-256", 1 },
+ { WPA_DRIVER_CAPA_ENC_BIP_CMAC_256, "BIP-CMAC-256", 1 },
+};
+
+
+static int ctrl_iface_get_capability_pairwise(int res, char *strict,
+ struct wpa_driver_capa *capa,
+ char *buf, size_t buflen)
+{
+ int ret;
+ char *pos, *end;
+ size_t len;
+ unsigned int i;
+
+ pos = buf;
+ end = pos + buflen;
+
+ if (res < 0) {
+ if (strict)
+ return 0;
+ len = os_strlcpy(buf, "CCMP TKIP NONE", buflen);
+ if (len >= buflen)
+ return -1;
+ return len;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(ciphers); i++) {
+ if (!ciphers[i].group_only && capa->enc & ciphers[i].capa) {
+ ret = os_snprintf(pos, end - pos, "%s%s",
+ pos == buf ? "" : " ",
+ ciphers[i].name);
+ if (os_snprintf_error(end - pos, ret))
+ return pos - buf;
+ pos += ret;
+ }
+ }
+
+ return pos - buf;
+}
+
+
+static int ctrl_iface_get_capability_group(int res, char *strict,
+ struct wpa_driver_capa *capa,
+ char *buf, size_t buflen)
+{
+ int ret;
+ char *pos, *end;
+ size_t len;
+ unsigned int i;
+
+ pos = buf;
+ end = pos + buflen;
+
+ if (res < 0) {
+ if (strict)
+ return 0;
+ len = os_strlcpy(buf, "CCMP TKIP WEP104 WEP40", buflen);
+ if (len >= buflen)
+ return -1;
+ return len;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(ciphers); i++) {
+ if (capa->enc & ciphers[i].capa) {
+ ret = os_snprintf(pos, end - pos, "%s%s",
+ pos == buf ? "" : " ",
+ ciphers[i].name);
+ if (os_snprintf_error(end - pos, ret))
+ return pos - buf;
+ pos += ret;
+ }
+ }
+
+ return pos - buf;
+}
+
+
+static int ctrl_iface_get_capability_group_mgmt(int res, char *strict,
+ struct wpa_driver_capa *capa,
+ char *buf, size_t buflen)
+{
+ int ret;
+ char *pos, *end;
+ unsigned int i;
+
+ pos = buf;
+ end = pos + buflen;
+
+ if (res < 0)
+ return 0;
+
+ for (i = 0; i < ARRAY_SIZE(ciphers_group_mgmt); i++) {
+ if (capa->enc & ciphers_group_mgmt[i].capa) {
+ ret = os_snprintf(pos, end - pos, "%s%s",
+ pos == buf ? "" : " ",
+ ciphers_group_mgmt[i].name);
+ if (os_snprintf_error(end - pos, ret))
+ return pos - buf;
+ pos += ret;
+ }
+ }
+
+ return pos - buf;
+}
+
+
+static int ctrl_iface_get_capability_key_mgmt(int res, char *strict,
+ struct wpa_driver_capa *capa,
+ char *buf, size_t buflen)
+{
+ int ret;
+ char *pos, *end;
+ size_t len;
+
+ pos = buf;
+ end = pos + buflen;
+
+ if (res < 0) {
+ if (strict)
+ return 0;
+ len = os_strlcpy(buf, "WPA-PSK WPA-EAP IEEE8021X WPA-NONE "
+ "NONE", buflen);
+ if (len >= buflen)
+ return -1;
+ return len;
+ }
+
+ ret = os_snprintf(pos, end - pos, "NONE IEEE8021X");
+ if (os_snprintf_error(end - pos, ret))
+ return pos - buf;
+ pos += ret;
+
+ if (capa->key_mgmt & (WPA_DRIVER_CAPA_KEY_MGMT_WPA |
+ WPA_DRIVER_CAPA_KEY_MGMT_WPA2)) {
+ ret = os_snprintf(pos, end - pos, " WPA-EAP");
+ if (os_snprintf_error(end - pos, ret))
+ return pos - buf;
+ pos += ret;
+ }
+
+ if (capa->key_mgmt & (WPA_DRIVER_CAPA_KEY_MGMT_WPA_PSK |
+ WPA_DRIVER_CAPA_KEY_MGMT_WPA2_PSK)) {
+ ret = os_snprintf(pos, end - pos, " WPA-PSK");
+ if (os_snprintf_error(end - pos, ret))
+ return pos - buf;
+ pos += ret;
+ }
+
+ if (capa->key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_WPA_NONE) {
+ ret = os_snprintf(pos, end - pos, " WPA-NONE");
+ if (os_snprintf_error(end - pos, ret))
+ return pos - buf;
+ pos += ret;
+ }
+
+#ifdef CONFIG_SUITEB
+ if (capa->key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_SUITE_B) {
+ ret = os_snprintf(pos, end - pos, " WPA-EAP-SUITE-B");
+ if (os_snprintf_error(end - pos, ret))
+ return pos - buf;
+ pos += ret;
+ }
+#endif /* CONFIG_SUITEB */
+#ifdef CONFIG_SUITEB192
+ if (capa->key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_SUITE_B_192) {
+ ret = os_snprintf(pos, end - pos, " WPA-EAP-SUITE-B-192");
+ if (os_snprintf_error(end - pos, ret))
+ return pos - buf;
+ pos += ret;
+ }
+#endif /* CONFIG_SUITEB192 */
+
+ return pos - buf;
+}
+
+
+static int ctrl_iface_get_capability_proto(int res, char *strict,
+ struct wpa_driver_capa *capa,
+ char *buf, size_t buflen)
+{
+ int ret;
+ char *pos, *end;
+ size_t len;
+
+ pos = buf;
+ end = pos + buflen;
+
+ if (res < 0) {
+ if (strict)
+ return 0;
+ len = os_strlcpy(buf, "RSN WPA", buflen);
+ if (len >= buflen)
+ return -1;
+ return len;
+ }
+
+ if (capa->key_mgmt & (WPA_DRIVER_CAPA_KEY_MGMT_WPA2 |
+ WPA_DRIVER_CAPA_KEY_MGMT_WPA2_PSK)) {
+ ret = os_snprintf(pos, end - pos, "%sRSN",
+ pos == buf ? "" : " ");
+ if (os_snprintf_error(end - pos, ret))
+ return pos - buf;
+ pos += ret;
+ }
+
+ if (capa->key_mgmt & (WPA_DRIVER_CAPA_KEY_MGMT_WPA |
+ WPA_DRIVER_CAPA_KEY_MGMT_WPA_PSK)) {
+ ret = os_snprintf(pos, end - pos, "%sWPA",
+ pos == buf ? "" : " ");
+ if (os_snprintf_error(end - pos, ret))
+ return pos - buf;
+ pos += ret;
+ }
+
+ return pos - buf;
+}
+
+
+static int ctrl_iface_get_capability_auth_alg(struct wpa_supplicant *wpa_s,
+ int res, char *strict,
+ struct wpa_driver_capa *capa,
+ char *buf, size_t buflen)
+{
+ int ret;
+ char *pos, *end;
+ size_t len;
+
+ pos = buf;
+ end = pos + buflen;
+
+ if (res < 0) {
+ if (strict)
+ return 0;
+ len = os_strlcpy(buf, "OPEN SHARED LEAP", buflen);
+ if (len >= buflen)
+ return -1;
+ return len;
+ }
+
+ if (capa->auth & (WPA_DRIVER_AUTH_OPEN)) {
+ ret = os_snprintf(pos, end - pos, "%sOPEN",
+ pos == buf ? "" : " ");
+ if (os_snprintf_error(end - pos, ret))
+ return pos - buf;
+ pos += ret;
+ }
+
+ if (capa->auth & (WPA_DRIVER_AUTH_SHARED)) {
+ ret = os_snprintf(pos, end - pos, "%sSHARED",
+ pos == buf ? "" : " ");
+ if (os_snprintf_error(end - pos, ret))
+ return pos - buf;
+ pos += ret;
+ }
+
+ if (capa->auth & (WPA_DRIVER_AUTH_LEAP)) {
+ ret = os_snprintf(pos, end - pos, "%sLEAP",
+ pos == buf ? "" : " ");
+ if (os_snprintf_error(end - pos, ret))
+ return pos - buf;
+ pos += ret;
+ }
+
+#ifdef CONFIG_SAE
+ if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_SAE) {
+ ret = os_snprintf(pos, end - pos, "%sSAE",
+ pos == buf ? "" : " ");
+ if (os_snprintf_error(end - pos, ret))
+ return pos - buf;
+ pos += ret;
+ }
+#endif /* CONFIG_SAE */
+
+ return pos - buf;
+}
+
+
+static int ctrl_iface_get_capability_modes(int res, char *strict,
+ struct wpa_driver_capa *capa,
+ char *buf, size_t buflen)
+{
+ int ret;
+ char *pos, *end;
+ size_t len;
+
+ pos = buf;
+ end = pos + buflen;
+
+ if (res < 0) {
+ if (strict)
+ return 0;
+ len = os_strlcpy(buf, "IBSS AP", buflen);
+ if (len >= buflen)
+ return -1;
+ return len;
+ }
+
+ if (capa->flags & WPA_DRIVER_FLAGS_IBSS) {
+ ret = os_snprintf(pos, end - pos, "%sIBSS",
+ pos == buf ? "" : " ");
+ if (os_snprintf_error(end - pos, ret))
+ return pos - buf;
+ pos += ret;
+ }
+
+ if (capa->flags & WPA_DRIVER_FLAGS_AP) {
+ ret = os_snprintf(pos, end - pos, "%sAP",
+ pos == buf ? "" : " ");
+ if (os_snprintf_error(end - pos, ret))
+ return pos - buf;
+ pos += ret;
+ }
+
+#ifdef CONFIG_MESH
+ if (capa->flags & WPA_DRIVER_FLAGS_MESH) {
+ ret = os_snprintf(pos, end - pos, "%sMESH",
+ pos == buf ? "" : " ");
+ if (os_snprintf_error(end - pos, ret))
+ return pos - buf;
+ pos += ret;
+ }
+#endif /* CONFIG_MESH */
+
+ return pos - buf;
+}
+
+
+static int ctrl_iface_get_capability_channels(struct wpa_supplicant *wpa_s,
+ char *buf, size_t buflen)
+{
+ struct hostapd_channel_data *chnl;
+ int ret, i, j;
+ char *pos, *end, *hmode;
+
+ pos = buf;
+ end = pos + buflen;
+
+ for (j = 0; j < wpa_s->hw.num_modes; j++) {
+ switch (wpa_s->hw.modes[j].mode) {
+ case HOSTAPD_MODE_IEEE80211B:
+ hmode = "B";
+ break;
+ case HOSTAPD_MODE_IEEE80211G:
+ hmode = "G";
+ break;
+ case HOSTAPD_MODE_IEEE80211A:
+ hmode = "A";
+ break;
+ case HOSTAPD_MODE_IEEE80211AD:
+ hmode = "AD";
+ break;
+ default:
+ continue;
+ }
+ ret = os_snprintf(pos, end - pos, "Mode[%s] Channels:", hmode);
+ if (os_snprintf_error(end - pos, ret))
+ return pos - buf;
+ pos += ret;
+ chnl = wpa_s->hw.modes[j].channels;
+ for (i = 0; i < wpa_s->hw.modes[j].num_channels; i++) {
+ if (chnl[i].flag & HOSTAPD_CHAN_DISABLED)
+ continue;
+ ret = os_snprintf(pos, end - pos, " %d", chnl[i].chan);
+ if (os_snprintf_error(end - pos, ret))
+ return pos - buf;
+ pos += ret;
+ }
+ ret = os_snprintf(pos, end - pos, "\n");
+ if (os_snprintf_error(end - pos, ret))
+ return pos - buf;
+ pos += ret;
+ }
+
+ return pos - buf;
+}
+
+
+static int ctrl_iface_get_capability_freq(struct wpa_supplicant *wpa_s,
+ char *buf, size_t buflen)
+{
+ struct hostapd_channel_data *chnl;
+ int ret, i, j;
+ char *pos, *end, *hmode;
+
+ pos = buf;
+ end = pos + buflen;
+
+ for (j = 0; j < wpa_s->hw.num_modes; j++) {
+ switch (wpa_s->hw.modes[j].mode) {
+ case HOSTAPD_MODE_IEEE80211B:
+ hmode = "B";
+ break;
+ case HOSTAPD_MODE_IEEE80211G:
+ hmode = "G";
+ break;
+ case HOSTAPD_MODE_IEEE80211A:
+ hmode = "A";
+ break;
+ case HOSTAPD_MODE_IEEE80211AD:
+ hmode = "AD";
+ break;
+ default:
+ continue;
+ }
+ ret = os_snprintf(pos, end - pos, "Mode[%s] Channels:\n",
+ hmode);
+ if (os_snprintf_error(end - pos, ret))
+ return pos - buf;
+ pos += ret;
+ chnl = wpa_s->hw.modes[j].channels;
+ for (i = 0; i < wpa_s->hw.modes[j].num_channels; i++) {
+ if (chnl[i].flag & HOSTAPD_CHAN_DISABLED)
+ continue;
+ ret = os_snprintf(pos, end - pos, " %d = %d MHz%s%s\n",
+ chnl[i].chan, chnl[i].freq,
+ chnl[i].flag & HOSTAPD_CHAN_NO_IR ?
+ " (NO_IR)" : "",
+ chnl[i].flag & HOSTAPD_CHAN_RADAR ?
+ " (DFS)" : "");
+
+ if (os_snprintf_error(end - pos, ret))
+ return pos - buf;
+ pos += ret;
+ }
+ ret = os_snprintf(pos, end - pos, "\n");
+ if (os_snprintf_error(end - pos, ret))
+ return pos - buf;
+ pos += ret;
+ }
+
+ return pos - buf;
+}
+
+
+static int wpa_supplicant_ctrl_iface_get_capability(
+ struct wpa_supplicant *wpa_s, const char *_field, char *buf,
+ size_t buflen)
+{
+ struct wpa_driver_capa capa;
+ int res;
+ char *strict;
+ char field[30];
+ size_t len;
+
+ /* Determine whether or not strict checking was requested */
+ len = os_strlcpy(field, _field, sizeof(field));
+ if (len >= sizeof(field))
+ return -1;
+ strict = os_strchr(field, ' ');
+ if (strict != NULL) {
+ *strict++ = '\0';
+ if (os_strcmp(strict, "strict") != 0)
+ return -1;
+ }
+
+ wpa_printf(MSG_DEBUG, "CTRL_IFACE: GET_CAPABILITY '%s' %s",
+ field, strict ? strict : "");
+
+ if (os_strcmp(field, "eap") == 0) {
+ return eap_get_names(buf, buflen);
+ }
+
+ res = wpa_drv_get_capa(wpa_s, &capa);
+
+ if (os_strcmp(field, "pairwise") == 0)
+ return ctrl_iface_get_capability_pairwise(res, strict, &capa,
+ buf, buflen);
+
+ if (os_strcmp(field, "group") == 0)
+ return ctrl_iface_get_capability_group(res, strict, &capa,
+ buf, buflen);
+
+ if (os_strcmp(field, "group_mgmt") == 0)
+ return ctrl_iface_get_capability_group_mgmt(res, strict, &capa,
+ buf, buflen);
+
+ if (os_strcmp(field, "key_mgmt") == 0)
+ return ctrl_iface_get_capability_key_mgmt(res, strict, &capa,
+ buf, buflen);
+
+ if (os_strcmp(field, "proto") == 0)
+ return ctrl_iface_get_capability_proto(res, strict, &capa,
+ buf, buflen);
+
+ if (os_strcmp(field, "auth_alg") == 0)
+ return ctrl_iface_get_capability_auth_alg(wpa_s, res, strict,
+ &capa, buf, buflen);
+
+ if (os_strcmp(field, "modes") == 0)
+ return ctrl_iface_get_capability_modes(res, strict, &capa,
+ buf, buflen);
+
+ if (os_strcmp(field, "channels") == 0)
+ return ctrl_iface_get_capability_channels(wpa_s, buf, buflen);
+
+ if (os_strcmp(field, "freq") == 0)
+ return ctrl_iface_get_capability_freq(wpa_s, buf, buflen);
+
+#ifdef CONFIG_TDLS
+ if (os_strcmp(field, "tdls") == 0)
+ return ctrl_iface_get_capability_tdls(wpa_s, buf, buflen);
+#endif /* CONFIG_TDLS */
+
+#ifdef CONFIG_ERP
+ if (os_strcmp(field, "erp") == 0) {
+ res = os_snprintf(buf, buflen, "ERP");
+ if (os_snprintf_error(buflen, res))
+ return -1;
+ return res;
+ }
+#endif /* CONFIG_EPR */
+
+#ifdef CONFIG_FIPS
+ if (os_strcmp(field, "fips") == 0) {
+ res = os_snprintf(buf, buflen, "FIPS");
+ if (os_snprintf_error(buflen, res))
+ return -1;
+ return res;
+ }
+#endif /* CONFIG_FIPS */
+
+ wpa_printf(MSG_DEBUG, "CTRL_IFACE: Unknown GET_CAPABILITY field '%s'",
+ field);
+
+ return -1;
+}
+
+
+#ifdef CONFIG_INTERWORKING
+static char * anqp_add_hex(char *pos, char *end, const char *title,
+ struct wpabuf *data)
+{
+ char *start = pos;
+ size_t i;
+ int ret;
+ const u8 *d;
+
+ if (data == NULL)
+ return start;
+
+ ret = os_snprintf(pos, end - pos, "%s=", title);
+ if (os_snprintf_error(end - pos, ret))
+ return start;
+ pos += ret;
+
+ d = wpabuf_head_u8(data);
+ for (i = 0; i < wpabuf_len(data); i++) {
+ ret = os_snprintf(pos, end - pos, "%02x", *d++);
+ if (os_snprintf_error(end - pos, ret))
+ return start;
+ pos += ret;
+ }
+
+ ret = os_snprintf(pos, end - pos, "\n");
+ if (os_snprintf_error(end - pos, ret))
+ return start;
+ pos += ret;
+
+ return pos;
+}
+#endif /* CONFIG_INTERWORKING */
+
+
+static int print_bss_info(struct wpa_supplicant *wpa_s, struct wpa_bss *bss,
+ unsigned long mask, char *buf, size_t buflen)
+{
+ size_t i;
+ int ret;
+ char *pos, *end;
+ const u8 *ie, *ie2, *osen_ie;
+
+ pos = buf;
+ end = buf + buflen;
+
+ if (mask & WPA_BSS_MASK_ID) {
+ ret = os_snprintf(pos, end - pos, "id=%u\n", bss->id);
+ if (os_snprintf_error(end - pos, ret))
+ return 0;
+ pos += ret;
+ }
+
+ if (mask & WPA_BSS_MASK_BSSID) {
+ ret = os_snprintf(pos, end - pos, "bssid=" MACSTR "\n",
+ MAC2STR(bss->bssid));
+ if (os_snprintf_error(end - pos, ret))
+ return 0;
+ pos += ret;
+ }
+
+ if (mask & WPA_BSS_MASK_FREQ) {
+ ret = os_snprintf(pos, end - pos, "freq=%d\n", bss->freq);
+ if (os_snprintf_error(end - pos, ret))
+ return 0;
+ pos += ret;
+ }
+
+ if (mask & WPA_BSS_MASK_BEACON_INT) {
+ ret = os_snprintf(pos, end - pos, "beacon_int=%d\n",
+ bss->beacon_int);
+ if (os_snprintf_error(end - pos, ret))
+ return 0;
+ pos += ret;
+ }
+
+ if (mask & WPA_BSS_MASK_CAPABILITIES) {
+ ret = os_snprintf(pos, end - pos, "capabilities=0x%04x\n",
+ bss->caps);
+ if (os_snprintf_error(end - pos, ret))
+ return 0;
+ pos += ret;
+ }
+
+ if (mask & WPA_BSS_MASK_QUAL) {
+ ret = os_snprintf(pos, end - pos, "qual=%d\n", bss->qual);
+ if (os_snprintf_error(end - pos, ret))
+ return 0;
+ pos += ret;
+ }
+
+ if (mask & WPA_BSS_MASK_NOISE) {
+ ret = os_snprintf(pos, end - pos, "noise=%d\n", bss->noise);
+ if (os_snprintf_error(end - pos, ret))
+ return 0;
+ pos += ret;
+ }
+
+ if (mask & WPA_BSS_MASK_LEVEL) {
+ ret = os_snprintf(pos, end - pos, "level=%d\n", bss->level);
+ if (os_snprintf_error(end - pos, ret))
+ return 0;
+ pos += ret;
+ }
+
+ if (mask & WPA_BSS_MASK_TSF) {
+ ret = os_snprintf(pos, end - pos, "tsf=%016llu\n",
+ (unsigned long long) bss->tsf);
+ if (os_snprintf_error(end - pos, ret))
+ return 0;
+ pos += ret;
+ }
+
+ if (mask & WPA_BSS_MASK_AGE) {
+ struct os_reltime now;
+
+ os_get_reltime(&now);
+ ret = os_snprintf(pos, end - pos, "age=%d\n",
+ (int) (now.sec - bss->last_update.sec));
+ if (os_snprintf_error(end - pos, ret))
+ return 0;
+ pos += ret;
+ }
+
+ if (mask & WPA_BSS_MASK_IE) {
+ ret = os_snprintf(pos, end - pos, "ie=");
+ if (os_snprintf_error(end - pos, ret))
+ return 0;
+ pos += ret;
+
+ ie = (const u8 *) (bss + 1);
+ for (i = 0; i < bss->ie_len; i++) {
+ ret = os_snprintf(pos, end - pos, "%02x", *ie++);
+ if (os_snprintf_error(end - pos, ret))
+ return 0;
+ pos += ret;
+ }
+
+ ret = os_snprintf(pos, end - pos, "\n");
+ if (os_snprintf_error(end - pos, ret))
+ return 0;
+ pos += ret;
+ }
+
+ if (mask & WPA_BSS_MASK_FLAGS) {
+ ret = os_snprintf(pos, end - pos, "flags=");
+ if (os_snprintf_error(end - pos, ret))
+ return 0;
+ pos += ret;
+
+ ie = wpa_bss_get_vendor_ie(bss, WPA_IE_VENDOR_TYPE);
+ if (ie)
+ pos = wpa_supplicant_ie_txt(pos, end, "WPA", ie,
+ 2 + ie[1]);
+ ie2 = wpa_bss_get_ie(bss, WLAN_EID_RSN);
+ if (ie2)
+ pos = wpa_supplicant_ie_txt(pos, end, "WPA2", ie2,
+ 2 + ie2[1]);
+ osen_ie = wpa_bss_get_vendor_ie(bss, OSEN_IE_VENDOR_TYPE);
+ if (osen_ie)
+ pos = wpa_supplicant_ie_txt(pos, end, "OSEN",
+ osen_ie, 2 + osen_ie[1]);
+ pos = wpa_supplicant_wps_ie_txt(wpa_s, pos, end, bss);
+ if (!ie && !ie2 && !osen_ie &&
+ (bss->caps & IEEE80211_CAP_PRIVACY)) {
+ ret = os_snprintf(pos, end - pos, "[WEP]");
+ if (os_snprintf_error(end - pos, ret))
+ return 0;
+ pos += ret;
+ }
+ if (bss_is_dmg(bss)) {
+ const char *s;
+ ret = os_snprintf(pos, end - pos, "[DMG]");
+ if (os_snprintf_error(end - pos, ret))
+ return 0;
+ pos += ret;
+ switch (bss->caps & IEEE80211_CAP_DMG_MASK) {
+ case IEEE80211_CAP_DMG_IBSS:
+ s = "[IBSS]";
+ break;
+ case IEEE80211_CAP_DMG_AP:
+ s = "[ESS]";
+ break;
+ case IEEE80211_CAP_DMG_PBSS:
+ s = "[PBSS]";
+ break;
+ default:
+ s = "";
+ break;
+ }
+ ret = os_snprintf(pos, end - pos, "%s", s);
+ if (os_snprintf_error(end - pos, ret))
+ return 0;
+ pos += ret;
+ } else {
+ if (bss->caps & IEEE80211_CAP_IBSS) {
+ ret = os_snprintf(pos, end - pos, "[IBSS]");
+ if (os_snprintf_error(end - pos, ret))
+ return 0;
+ pos += ret;
+ }
+ if (bss->caps & IEEE80211_CAP_ESS) {
+ ret = os_snprintf(pos, end - pos, "[ESS]");
+ if (os_snprintf_error(end - pos, ret))
+ return 0;
+ pos += ret;
+ }
+ }
+ if (wpa_bss_get_vendor_ie(bss, P2P_IE_VENDOR_TYPE) ||
+ wpa_bss_get_vendor_ie_beacon(bss, P2P_IE_VENDOR_TYPE)) {
+ ret = os_snprintf(pos, end - pos, "[P2P]");
+ if (os_snprintf_error(end - pos, ret))
+ return 0;
+ pos += ret;
+ }
+#ifdef CONFIG_HS20
+ if (wpa_bss_get_vendor_ie(bss, HS20_IE_VENDOR_TYPE)) {
+ ret = os_snprintf(pos, end - pos, "[HS20]");
+ if (os_snprintf_error(end - pos, ret))
+ return 0;
+ pos += ret;
+ }
+#endif /* CONFIG_HS20 */
+
+ ret = os_snprintf(pos, end - pos, "\n");
+ if (os_snprintf_error(end - pos, ret))
+ return 0;
+ pos += ret;
+ }
+
+ if (mask & WPA_BSS_MASK_SSID) {
+ ret = os_snprintf(pos, end - pos, "ssid=%s\n",
+ wpa_ssid_txt(bss->ssid, bss->ssid_len));
+ if (os_snprintf_error(end - pos, ret))
+ return 0;
+ pos += ret;
+ }
+
+#ifdef CONFIG_WPS
+ if (mask & WPA_BSS_MASK_WPS_SCAN) {
+ ie = (const u8 *) (bss + 1);
+ ret = wpas_wps_scan_result_text(ie, bss->ie_len, pos, end);
+ if (ret >= end - pos)
+ return 0;
+ if (ret > 0)
+ pos += ret;
+ }
+#endif /* CONFIG_WPS */
+
+#ifdef CONFIG_P2P
+ if (mask & WPA_BSS_MASK_P2P_SCAN) {
+ ie = (const u8 *) (bss + 1);
+ ret = wpas_p2p_scan_result_text(ie, bss->ie_len, pos, end);
+ if (ret < 0 || ret >= end - pos)
+ return 0;
+ pos += ret;
+ }
+#endif /* CONFIG_P2P */
+
+#ifdef CONFIG_WIFI_DISPLAY
+ if (mask & WPA_BSS_MASK_WIFI_DISPLAY) {
+ struct wpabuf *wfd;
+ ie = (const u8 *) (bss + 1);
+ wfd = ieee802_11_vendor_ie_concat(ie, bss->ie_len,
+ WFD_IE_VENDOR_TYPE);
+ if (wfd) {
+ ret = os_snprintf(pos, end - pos, "wfd_subelems=");
+ if (os_snprintf_error(end - pos, ret)) {
+ wpabuf_free(wfd);
+ return 0;
+ }
+ pos += ret;
+
+ pos += wpa_snprintf_hex(pos, end - pos,
+ wpabuf_head(wfd),
+ wpabuf_len(wfd));
+ wpabuf_free(wfd);
+
+ ret = os_snprintf(pos, end - pos, "\n");
+ if (os_snprintf_error(end - pos, ret))
+ return 0;
+ pos += ret;
+ }
+ }
+#endif /* CONFIG_WIFI_DISPLAY */
+
+#ifdef CONFIG_INTERWORKING
+ if ((mask & WPA_BSS_MASK_INTERNETW) && bss->anqp) {
+ struct wpa_bss_anqp *anqp = bss->anqp;
+ pos = anqp_add_hex(pos, end, "anqp_capability_list",
+ anqp->capability_list);
+ pos = anqp_add_hex(pos, end, "anqp_venue_name",
+ anqp->venue_name);
+ pos = anqp_add_hex(pos, end, "anqp_network_auth_type",
+ anqp->network_auth_type);
+ pos = anqp_add_hex(pos, end, "anqp_roaming_consortium",
+ anqp->roaming_consortium);
+ pos = anqp_add_hex(pos, end, "anqp_ip_addr_type_availability",
+ anqp->ip_addr_type_availability);
+ pos = anqp_add_hex(pos, end, "anqp_nai_realm",
+ anqp->nai_realm);
+ pos = anqp_add_hex(pos, end, "anqp_3gpp", anqp->anqp_3gpp);
+ pos = anqp_add_hex(pos, end, "anqp_domain_name",
+ anqp->domain_name);
+#ifdef CONFIG_HS20
+ pos = anqp_add_hex(pos, end, "hs20_capability_list",
+ anqp->hs20_capability_list);
+ pos = anqp_add_hex(pos, end, "hs20_operator_friendly_name",
+ anqp->hs20_operator_friendly_name);
+ pos = anqp_add_hex(pos, end, "hs20_wan_metrics",
+ anqp->hs20_wan_metrics);
+ pos = anqp_add_hex(pos, end, "hs20_connection_capability",
+ anqp->hs20_connection_capability);
+ pos = anqp_add_hex(pos, end, "hs20_operating_class",
+ anqp->hs20_operating_class);
+ pos = anqp_add_hex(pos, end, "hs20_osu_providers_list",
+ anqp->hs20_osu_providers_list);
+#endif /* CONFIG_HS20 */
+ }
+#endif /* CONFIG_INTERWORKING */
+
+#ifdef CONFIG_MESH
+ if (mask & WPA_BSS_MASK_MESH_SCAN) {
+ ie = (const u8 *) (bss + 1);
+ ret = wpas_mesh_scan_result_text(ie, bss->ie_len, pos, end);
+ if (ret < 0 || ret >= end - pos)
+ return 0;
+ pos += ret;
+ }
+#endif /* CONFIG_MESH */
+
+ if (mask & WPA_BSS_MASK_SNR) {
+ ret = os_snprintf(pos, end - pos, "snr=%d\n", bss->snr);
+ if (os_snprintf_error(end - pos, ret))
+ return 0;
+ pos += ret;
+ }
+
+ if (mask & WPA_BSS_MASK_EST_THROUGHPUT) {
+ ret = os_snprintf(pos, end - pos, "est_throughput=%d\n",
+ bss->est_throughput);
+ if (os_snprintf_error(end - pos, ret))
+ return 0;
+ pos += ret;
+ }
+
+#ifdef CONFIG_FST
+ if (mask & WPA_BSS_MASK_FST) {
+ ret = fst_ctrl_iface_mb_info(bss->bssid, pos, end - pos);
+ if (ret < 0 || ret >= end - pos)
+ return 0;
+ pos += ret;
+ }
+#endif /* CONFIG_FST */
+
+ if (mask & WPA_BSS_MASK_DELIM) {
+ ret = os_snprintf(pos, end - pos, "====\n");
+ if (os_snprintf_error(end - pos, ret))
+ return 0;
+ pos += ret;
+ }
+
+ return pos - buf;
+}
+
+
+static int wpa_supplicant_ctrl_iface_bss(struct wpa_supplicant *wpa_s,
+ const char *cmd, char *buf,
+ size_t buflen)
+{
+ u8 bssid[ETH_ALEN];
+ size_t i;
+ struct wpa_bss *bss;
+ struct wpa_bss *bsslast = NULL;
+ struct dl_list *next;
+ int ret = 0;
+ int len;
+ char *ctmp, *end = buf + buflen;
+ unsigned long mask = WPA_BSS_MASK_ALL;
+
+ if (os_strncmp(cmd, "RANGE=", 6) == 0) {
+ if (os_strncmp(cmd + 6, "ALL", 3) == 0) {
+ bss = dl_list_first(&wpa_s->bss_id, struct wpa_bss,
+ list_id);
+ bsslast = dl_list_last(&wpa_s->bss_id, struct wpa_bss,
+ list_id);
+ } else { /* N1-N2 */
+ unsigned int id1, id2;
+
+ if ((ctmp = os_strchr(cmd + 6, '-')) == NULL) {
+ wpa_printf(MSG_INFO, "Wrong BSS range "
+ "format");
+ return 0;
+ }
+
+ if (*(cmd + 6) == '-')
+ id1 = 0;
+ else
+ id1 = atoi(cmd + 6);
+ ctmp++;
+ if (*ctmp >= '0' && *ctmp <= '9')
+ id2 = atoi(ctmp);
+ else
+ id2 = (unsigned int) -1;
+ bss = wpa_bss_get_id_range(wpa_s, id1, id2);
+ if (id2 == (unsigned int) -1)
+ bsslast = dl_list_last(&wpa_s->bss_id,
+ struct wpa_bss,
+ list_id);
+ else {
+ bsslast = wpa_bss_get_id(wpa_s, id2);
+ if (bsslast == NULL && bss && id2 > id1) {
+ struct wpa_bss *tmp = bss;
+ for (;;) {
+ next = tmp->list_id.next;
+ if (next == &wpa_s->bss_id)
+ break;
+ tmp = dl_list_entry(
+ next, struct wpa_bss,
+ list_id);
+ if (tmp->id > id2)
+ break;
+ bsslast = tmp;
+ }
+ }
+ }
+ }
+ } else if (os_strncmp(cmd, "FIRST", 5) == 0)
+ bss = dl_list_first(&wpa_s->bss_id, struct wpa_bss, list_id);
+ else if (os_strncmp(cmd, "LAST", 4) == 0)
+ bss = dl_list_last(&wpa_s->bss_id, struct wpa_bss, list_id);
+ else if (os_strncmp(cmd, "ID-", 3) == 0) {
+ i = atoi(cmd + 3);
+ bss = wpa_bss_get_id(wpa_s, i);
+ } else if (os_strncmp(cmd, "NEXT-", 5) == 0) {
+ i = atoi(cmd + 5);
+ bss = wpa_bss_get_id(wpa_s, i);
+ if (bss) {
+ next = bss->list_id.next;
+ if (next == &wpa_s->bss_id)
+ bss = NULL;
+ else
+ bss = dl_list_entry(next, struct wpa_bss,
+ list_id);
+ }
+#ifdef CONFIG_P2P
+ } else if (os_strncmp(cmd, "p2p_dev_addr=", 13) == 0) {
+ if (hwaddr_aton(cmd + 13, bssid) == 0)
+ bss = wpa_bss_get_p2p_dev_addr(wpa_s, bssid);
+ else
+ bss = NULL;
+#endif /* CONFIG_P2P */
+ } else if (hwaddr_aton(cmd, bssid) == 0)
+ bss = wpa_bss_get_bssid(wpa_s, bssid);
+ else {
+ struct wpa_bss *tmp;
+ i = atoi(cmd);
+ bss = NULL;
+ dl_list_for_each(tmp, &wpa_s->bss_id, struct wpa_bss, list_id)
+ {
+ if (i-- == 0) {
+ bss = tmp;
+ break;
+ }
+ }
+ }
+
+ if ((ctmp = os_strstr(cmd, "MASK=")) != NULL) {
+ mask = strtoul(ctmp + 5, NULL, 0x10);
+ if (mask == 0)
+ mask = WPA_BSS_MASK_ALL;
+ }
+
+ if (bss == NULL)
+ return 0;
+
+ if (bsslast == NULL)
+ bsslast = bss;
+ do {
+ len = print_bss_info(wpa_s, bss, mask, buf, buflen);
+ ret += len;
+ buf += len;
+ buflen -= len;
+ if (bss == bsslast) {
+ if ((mask & WPA_BSS_MASK_DELIM) && len &&
+ (bss == dl_list_last(&wpa_s->bss_id,
+ struct wpa_bss, list_id))) {
+ int res;
+
+ res = os_snprintf(buf - 5, end - buf + 5,
+ "####\n");
+ if (os_snprintf_error(end - buf + 5, res)) {
+ wpa_printf(MSG_DEBUG,
+ "Could not add end delim");
+ }
+ }
+ break;
+ }
+ next = bss->list_id.next;
+ if (next == &wpa_s->bss_id)
+ break;
+ bss = dl_list_entry(next, struct wpa_bss, list_id);
+ } while (bss && len);
+
+ return ret;
+}
+
+
+static int wpa_supplicant_ctrl_iface_ap_scan(
+ struct wpa_supplicant *wpa_s, char *cmd)
+{
+ int ap_scan = atoi(cmd);
+ return wpa_supplicant_set_ap_scan(wpa_s, ap_scan);
+}
+
+
+static int wpa_supplicant_ctrl_iface_scan_interval(
+ struct wpa_supplicant *wpa_s, char *cmd)
+{
+ int scan_int = atoi(cmd);
+ return wpa_supplicant_set_scan_interval(wpa_s, scan_int);
+}
+
+
+static int wpa_supplicant_ctrl_iface_bss_expire_age(
+ struct wpa_supplicant *wpa_s, char *cmd)
+{
+ int expire_age = atoi(cmd);
+ return wpa_supplicant_set_bss_expiration_age(wpa_s, expire_age);
+}
+
+
+static int wpa_supplicant_ctrl_iface_bss_expire_count(
+ struct wpa_supplicant *wpa_s, char *cmd)
+{
+ int expire_count = atoi(cmd);
+ return wpa_supplicant_set_bss_expiration_count(wpa_s, expire_count);
+}
+
+
+static void wpa_supplicant_ctrl_iface_bss_flush(
+ struct wpa_supplicant *wpa_s, char *cmd)
+{
+ int flush_age = atoi(cmd);
+
+ if (flush_age == 0)
+ wpa_bss_flush(wpa_s);
+ else
+ wpa_bss_flush_by_age(wpa_s, flush_age);
+}
+
+
+#ifdef CONFIG_TESTING_OPTIONS
+static void wpa_supplicant_ctrl_iface_drop_sa(struct wpa_supplicant *wpa_s)
+{
+ wpa_printf(MSG_DEBUG, "Dropping SA without deauthentication");
+ /* MLME-DELETEKEYS.request */
+ wpa_drv_set_key(wpa_s, WPA_ALG_NONE, NULL, 0, 0, NULL, 0, NULL, 0);
+ wpa_drv_set_key(wpa_s, WPA_ALG_NONE, NULL, 1, 0, NULL, 0, NULL, 0);
+ wpa_drv_set_key(wpa_s, WPA_ALG_NONE, NULL, 2, 0, NULL, 0, NULL, 0);
+ wpa_drv_set_key(wpa_s, WPA_ALG_NONE, NULL, 3, 0, NULL, 0, NULL, 0);
+#ifdef CONFIG_IEEE80211W
+ wpa_drv_set_key(wpa_s, WPA_ALG_NONE, NULL, 4, 0, NULL, 0, NULL, 0);
+ wpa_drv_set_key(wpa_s, WPA_ALG_NONE, NULL, 5, 0, NULL, 0, NULL, 0);
+#endif /* CONFIG_IEEE80211W */
+
+ wpa_drv_set_key(wpa_s, WPA_ALG_NONE, wpa_s->bssid, 0, 0, NULL, 0, NULL,
+ 0);
+ /* MLME-SETPROTECTION.request(None) */
+ wpa_drv_mlme_setprotection(wpa_s, wpa_s->bssid,
+ MLME_SETPROTECTION_PROTECT_TYPE_NONE,
+ MLME_SETPROTECTION_KEY_TYPE_PAIRWISE);
+ wpa_sm_drop_sa(wpa_s->wpa);
+}
+#endif /* CONFIG_TESTING_OPTIONS */
+
+
+static int wpa_supplicant_ctrl_iface_roam(struct wpa_supplicant *wpa_s,
+ char *addr)
+{
+#ifdef CONFIG_NO_SCAN_PROCESSING
+ return -1;
+#else /* CONFIG_NO_SCAN_PROCESSING */
+ u8 bssid[ETH_ALEN];
+ struct wpa_bss *bss;
+ struct wpa_ssid *ssid = wpa_s->current_ssid;
+
+ if (hwaddr_aton(addr, bssid)) {
+ wpa_printf(MSG_DEBUG, "CTRL_IFACE ROAM: invalid "
+ "address '%s'", addr);
+ return -1;
+ }
+
+ wpa_printf(MSG_DEBUG, "CTRL_IFACE ROAM " MACSTR, MAC2STR(bssid));
+
+ if (!ssid) {
+ wpa_printf(MSG_DEBUG, "CTRL_IFACE ROAM: No network "
+ "configuration known for the target AP");
+ return -1;
+ }
+
+ bss = wpa_bss_get(wpa_s, bssid, ssid->ssid, ssid->ssid_len);
+ if (!bss) {
+ wpa_printf(MSG_DEBUG, "CTRL_IFACE ROAM: Target AP not found "
+ "from BSS table");
+ return -1;
+ }
+
+ /*
+ * TODO: Find best network configuration block from configuration to
+ * allow roaming to other networks
+ */
+
+ wpa_s->reassociate = 1;
+ wpa_supplicant_connect(wpa_s, bss, ssid);
+
+ return 0;
+#endif /* CONFIG_NO_SCAN_PROCESSING */
+}
+
+
+#ifdef CONFIG_P2P
+static int p2p_ctrl_find(struct wpa_supplicant *wpa_s, char *cmd)
+{
+ unsigned int timeout = atoi(cmd);
+ enum p2p_discovery_type type = P2P_FIND_START_WITH_FULL;
+ u8 dev_id[ETH_ALEN], *_dev_id = NULL;
+ u8 dev_type[WPS_DEV_TYPE_LEN], *_dev_type = NULL;
+ char *pos;
+ unsigned int search_delay;
+ const char *_seek[P2P_MAX_QUERY_HASH + 1], **seek = NULL;
+ u8 seek_count = 0;
+ int freq = 0;
+
+ if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED) {
+ wpa_dbg(wpa_s, MSG_INFO,
+ "Reject P2P_FIND since interface is disabled");
+ return -1;
+ }
+ if (os_strstr(cmd, "type=social"))
+ type = P2P_FIND_ONLY_SOCIAL;
+ else if (os_strstr(cmd, "type=progressive"))
+ type = P2P_FIND_PROGRESSIVE;
+
+ pos = os_strstr(cmd, "dev_id=");
+ if (pos) {
+ pos += 7;
+ if (hwaddr_aton(pos, dev_id))
+ return -1;
+ _dev_id = dev_id;
+ }
+
+ pos = os_strstr(cmd, "dev_type=");
+ if (pos) {
+ pos += 9;
+ if (wps_dev_type_str2bin(pos, dev_type) < 0)
+ return -1;
+ _dev_type = dev_type;
+ }
+
+ pos = os_strstr(cmd, "delay=");
+ if (pos) {
+ pos += 6;
+ search_delay = atoi(pos);
+ } else
+ search_delay = wpas_p2p_search_delay(wpa_s);
+
+ pos = os_strstr(cmd, "freq=");
+ if (pos) {
+ pos += 5;
+ freq = atoi(pos);
+ if (freq <= 0)
+ return -1;
+ }
+
+ /* Must be searched for last, because it adds nul termination */
+ pos = os_strstr(cmd, " seek=");
+ if (pos)
+ pos += 6;
+ while (pos && seek_count < P2P_MAX_QUERY_HASH + 1) {
+ char *term;
+
+ _seek[seek_count++] = pos;
+ seek = _seek;
+ term = os_strchr(pos, ' ');
+ if (!term)
+ break;
+ *term = '\0';
+ pos = os_strstr(term + 1, "seek=");
+ if (pos)
+ pos += 5;
+ }
+ if (seek_count > P2P_MAX_QUERY_HASH) {
+ seek[0] = NULL;
+ seek_count = 1;
+ }
+
+ return wpas_p2p_find(wpa_s, timeout, type, _dev_type != NULL, _dev_type,
+ _dev_id, search_delay, seek_count, seek, freq);
+}
+
+
+static int p2ps_ctrl_parse_cpt_priority(const char *pos, u8 *cpt)
+{
+ const char *last = NULL;
+ const char *token;
+ long int token_len;
+ unsigned int i;
+
+ /* Expected predefined CPT names delimited by ':' */
+ for (i = 0; (token = cstr_token(pos, ": \t", &last)); i++) {
+ if (i >= P2PS_FEATURE_CAPAB_CPT_MAX) {
+ wpa_printf(MSG_ERROR,
+ "P2PS: CPT name list is too long, expected up to %d names",
+ P2PS_FEATURE_CAPAB_CPT_MAX);
+ cpt[0] = 0;
+ return -1;
+ }
+
+ token_len = last - token;
+
+ if (token_len == 3 &&
+ os_memcmp(token, "UDP", token_len) == 0) {
+ cpt[i] = P2PS_FEATURE_CAPAB_UDP_TRANSPORT;
+ } else if (token_len == 3 &&
+ os_memcmp(token, "MAC", token_len) == 0) {
+ cpt[i] = P2PS_FEATURE_CAPAB_MAC_TRANSPORT;
+ } else {
+ wpa_printf(MSG_ERROR,
+ "P2PS: Unsupported CPT name '%s'", token);
+ cpt[0] = 0;
+ return -1;
+ }
+
+ if (isblank(*last)) {
+ i++;
+ break;
+ }
+ }
+ cpt[i] = 0;
+ return 0;
+}
+
+
+static struct p2ps_provision * p2p_parse_asp_provision_cmd(const char *cmd)
+{
+ struct p2ps_provision *p2ps_prov;
+ char *pos;
+ size_t info_len = 0;
+ char *info = NULL;
+ u8 role = P2PS_SETUP_NONE;
+ long long unsigned val;
+ int i;
+
+ pos = os_strstr(cmd, "info=");
+ if (pos) {
+ pos += 5;
+ info_len = os_strlen(pos);
+
+ if (info_len) {
+ info = os_malloc(info_len + 1);
+ if (info) {
+ info_len = utf8_unescape(pos, info_len,
+ info, info_len + 1);
+ } else
+ info_len = 0;
+ }
+ }
+
+ p2ps_prov = os_zalloc(sizeof(struct p2ps_provision) + info_len + 1);
+ if (p2ps_prov == NULL) {
+ os_free(info);
+ return NULL;
+ }
+
+ if (info) {
+ os_memcpy(p2ps_prov->info, info, info_len);
+ p2ps_prov->info[info_len] = '\0';
+ os_free(info);
+ }
+
+ pos = os_strstr(cmd, "status=");
+ if (pos)
+ p2ps_prov->status = atoi(pos + 7);
+ else
+ p2ps_prov->status = -1;
+
+ pos = os_strstr(cmd, "adv_id=");
+ if (!pos || sscanf(pos + 7, "%llx", &val) != 1 || val > 0xffffffffULL)
+ goto invalid_args;
+ p2ps_prov->adv_id = val;
+
+ pos = os_strstr(cmd, "method=");
+ if (pos)
+ p2ps_prov->method = strtol(pos + 7, NULL, 16);
+ else
+ p2ps_prov->method = 0;
+
+ pos = os_strstr(cmd, "session=");
+ if (!pos || sscanf(pos + 8, "%llx", &val) != 1 || val > 0xffffffffULL)
+ goto invalid_args;
+ p2ps_prov->session_id = val;
+
+ pos = os_strstr(cmd, "adv_mac=");
+ if (!pos || hwaddr_aton(pos + 8, p2ps_prov->adv_mac))
+ goto invalid_args;
+
+ pos = os_strstr(cmd, "session_mac=");
+ if (!pos || hwaddr_aton(pos + 12, p2ps_prov->session_mac))
+ goto invalid_args;
+
+ pos = os_strstr(cmd, "cpt=");
+ if (pos) {
+ if (p2ps_ctrl_parse_cpt_priority(pos + 4,
+ p2ps_prov->cpt_priority))
+ goto invalid_args;
+ } else {
+ p2ps_prov->cpt_priority[0] = P2PS_FEATURE_CAPAB_UDP_TRANSPORT;
+ }
+
+ for (i = 0; p2ps_prov->cpt_priority[i]; i++)
+ p2ps_prov->cpt_mask |= p2ps_prov->cpt_priority[i];
+
+ /* force conncap with tstCap (no sanity checks) */
+ pos = os_strstr(cmd, "tstCap=");
+ if (pos) {
+ role = strtol(pos + 7, NULL, 16);
+ } else {
+ pos = os_strstr(cmd, "role=");
+ if (pos) {
+ role = strtol(pos + 5, NULL, 16);
+ if (role != P2PS_SETUP_CLIENT &&
+ role != P2PS_SETUP_GROUP_OWNER)
+ role = P2PS_SETUP_NONE;
+ }
+ }
+ p2ps_prov->role = role;
+
+ return p2ps_prov;
+
+invalid_args:
+ os_free(p2ps_prov);
+ return NULL;
+}
+
+
+static int p2p_ctrl_asp_provision_resp(struct wpa_supplicant *wpa_s, char *cmd)
+{
+ u8 addr[ETH_ALEN];
+ struct p2ps_provision *p2ps_prov;
+ char *pos;
+
+ /* <addr> id=<adv_id> [role=<conncap>] [info=<infodata>] */
+
+ wpa_printf(MSG_DEBUG, "%s: %s", __func__, cmd);
+
+ if (hwaddr_aton(cmd, addr))
+ return -1;
+
+ pos = cmd + 17;
+ if (*pos != ' ')
+ return -1;
+
+ p2ps_prov = p2p_parse_asp_provision_cmd(pos);
+ if (!p2ps_prov)
+ return -1;
+
+ if (p2ps_prov->status < 0) {
+ os_free(p2ps_prov);
+ return -1;
+ }
+
+ return wpas_p2p_prov_disc(wpa_s, addr, NULL, WPAS_P2P_PD_FOR_ASP,
+ p2ps_prov);
+}
+
+
+static int p2p_ctrl_asp_provision(struct wpa_supplicant *wpa_s, char *cmd)
+{
+ u8 addr[ETH_ALEN];
+ struct p2ps_provision *p2ps_prov;
+ char *pos;
+
+ /* <addr> id=<adv_id> adv_mac=<adv_mac> conncap=<conncap>
+ * session=<ses_id> mac=<ses_mac> [info=<infodata>]
+ */
+
+ wpa_printf(MSG_DEBUG, "%s: %s", __func__, cmd);
+ if (hwaddr_aton(cmd, addr))
+ return -1;
+
+ pos = cmd + 17;
+ if (*pos != ' ')
+ return -1;
+
+ p2ps_prov = p2p_parse_asp_provision_cmd(pos);
+ if (!p2ps_prov)
+ return -1;
+
+ p2ps_prov->pd_seeker = 1;
+
+ return wpas_p2p_prov_disc(wpa_s, addr, NULL, WPAS_P2P_PD_FOR_ASP,
+ p2ps_prov);
+}
+
+
+static int p2p_ctrl_connect(struct wpa_supplicant *wpa_s, char *cmd,
+ char *buf, size_t buflen)
+{
+ u8 addr[ETH_ALEN];
+ char *pos, *pos2;
+ char *pin = NULL;
+ enum p2p_wps_method wps_method;
+ int new_pin;
+ int ret;
+ int persistent_group, persistent_id = -1;
+ int join;
+ int auth;
+ int automatic;
+ int go_intent = -1;
+ int freq = 0;
+ int pd;
+ int ht40, vht;
+
+ if (!wpa_s->global->p2p_init_wpa_s)
+ return -1;
+ if (wpa_s->global->p2p_init_wpa_s != wpa_s) {
+ wpa_dbg(wpa_s, MSG_DEBUG, "Direct P2P_CONNECT command to %s",
+ wpa_s->global->p2p_init_wpa_s->ifname);
+ wpa_s = wpa_s->global->p2p_init_wpa_s;
+ }
+
+ /* <addr> <"pbc" | "pin" | PIN> [label|display|keypad|p2ps]
+ * [persistent|persistent=<network id>]
+ * [join] [auth] [go_intent=<0..15>] [freq=<in MHz>] [provdisc]
+ * [ht40] [vht] [auto] */
+
+ if (hwaddr_aton(cmd, addr))
+ return -1;
+
+ pos = cmd + 17;
+ if (*pos != ' ')
+ return -1;
+ pos++;
+
+ persistent_group = os_strstr(pos, " persistent") != NULL;
+ pos2 = os_strstr(pos, " persistent=");
+ if (pos2) {
+ struct wpa_ssid *ssid;
+ persistent_id = atoi(pos2 + 12);
+ ssid = wpa_config_get_network(wpa_s->conf, persistent_id);
+ if (ssid == NULL || ssid->disabled != 2 ||
+ ssid->mode != WPAS_MODE_P2P_GO) {
+ wpa_printf(MSG_DEBUG, "CTRL_IFACE: Could not find "
+ "SSID id=%d for persistent P2P group (GO)",
+ persistent_id);
+ return -1;
+ }
+ }
+ join = os_strstr(pos, " join") != NULL;
+ auth = os_strstr(pos, " auth") != NULL;
+ automatic = os_strstr(pos, " auto") != NULL;
+ pd = os_strstr(pos, " provdisc") != NULL;
+ vht = (os_strstr(cmd, " vht") != NULL) || wpa_s->conf->p2p_go_vht;
+ ht40 = (os_strstr(cmd, " ht40") != NULL) || wpa_s->conf->p2p_go_ht40 ||
+ vht;
+
+ pos2 = os_strstr(pos, " go_intent=");
+ if (pos2) {
+ pos2 += 11;
+ go_intent = atoi(pos2);
+ if (go_intent < 0 || go_intent > 15)
+ return -1;
+ }
+
+ pos2 = os_strstr(pos, " freq=");
+ if (pos2) {
+ pos2 += 6;
+ freq = atoi(pos2);
+ if (freq <= 0)
+ return -1;
+ }
+
+ if (os_strncmp(pos, "pin", 3) == 0) {
+ /* Request random PIN (to be displayed) and enable the PIN */
+ wps_method = WPS_PIN_DISPLAY;
+ } else if (os_strncmp(pos, "pbc", 3) == 0) {
+ wps_method = WPS_PBC;
+ } else {
+ pin = pos;
+ pos = os_strchr(pin, ' ');
+ wps_method = WPS_PIN_KEYPAD;
+ if (pos) {
+ *pos++ = '\0';
+ if (os_strncmp(pos, "display", 7) == 0)
+ wps_method = WPS_PIN_DISPLAY;
+ else if (os_strncmp(pos, "p2ps", 4) == 0)
+ wps_method = WPS_P2PS;
+ }
+ if (!wps_pin_str_valid(pin)) {
+ os_memcpy(buf, "FAIL-INVALID-PIN\n", 17);
+ return 17;
+ }
+ }
+
+ new_pin = wpas_p2p_connect(wpa_s, addr, pin, wps_method,
+ persistent_group, automatic, join,
+ auth, go_intent, freq, persistent_id, pd,
+ ht40, vht);
+ if (new_pin == -2) {
+ os_memcpy(buf, "FAIL-CHANNEL-UNAVAILABLE\n", 25);
+ return 25;
+ }
+ if (new_pin == -3) {
+ os_memcpy(buf, "FAIL-CHANNEL-UNSUPPORTED\n", 25);
+ return 25;
+ }
+ if (new_pin < 0)
+ return -1;
+ if (wps_method == WPS_PIN_DISPLAY && pin == NULL) {
+ ret = os_snprintf(buf, buflen, "%08d", new_pin);
+ if (os_snprintf_error(buflen, ret))
+ return -1;
+ return ret;
+ }
+
+ os_memcpy(buf, "OK\n", 3);
+ return 3;
+}
+
+
+static int p2p_ctrl_listen(struct wpa_supplicant *wpa_s, char *cmd)
+{
+ unsigned int timeout = atoi(cmd);
+ if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED) {
+ wpa_dbg(wpa_s, MSG_INFO,
+ "Reject P2P_LISTEN since interface is disabled");
+ return -1;
+ }
+ return wpas_p2p_listen(wpa_s, timeout);
+}
+
+
+static int p2p_ctrl_prov_disc(struct wpa_supplicant *wpa_s, char *cmd)
+{
+ u8 addr[ETH_ALEN];
+ char *pos;
+ enum wpas_p2p_prov_disc_use use = WPAS_P2P_PD_FOR_GO_NEG;
+
+ /* <addr> <config method> [join|auto] */
+
+ if (hwaddr_aton(cmd, addr))
+ return -1;
+
+ pos = cmd + 17;
+ if (*pos != ' ')
+ return -1;
+ pos++;
+
+ if (os_strstr(pos, " join") != NULL)
+ use = WPAS_P2P_PD_FOR_JOIN;
+ else if (os_strstr(pos, " auto") != NULL)
+ use = WPAS_P2P_PD_AUTO;
+
+ return wpas_p2p_prov_disc(wpa_s, addr, pos, use, NULL);
+}
+
+
+static int p2p_get_passphrase(struct wpa_supplicant *wpa_s, char *buf,
+ size_t buflen)
+{
+ struct wpa_ssid *ssid = wpa_s->current_ssid;
+
+ if (ssid == NULL || ssid->mode != WPAS_MODE_P2P_GO ||
+ ssid->passphrase == NULL)
+ return -1;
+
+ os_strlcpy(buf, ssid->passphrase, buflen);
+ return os_strlen(buf);
+}
+
+
+static int p2p_ctrl_serv_disc_req(struct wpa_supplicant *wpa_s, char *cmd,
+ char *buf, size_t buflen)
+{
+ u64 ref;
+ int res;
+ u8 dst_buf[ETH_ALEN], *dst;
+ struct wpabuf *tlvs;
+ char *pos;
+ size_t len;
+
+ if (hwaddr_aton(cmd, dst_buf))
+ return -1;
+ dst = dst_buf;
+ if (dst[0] == 0 && dst[1] == 0 && dst[2] == 0 &&
+ dst[3] == 0 && dst[4] == 0 && dst[5] == 0)
+ dst = NULL;
+ pos = cmd + 17;
+ if (*pos != ' ')
+ return -1;
+ pos++;
+
+ if (os_strncmp(pos, "upnp ", 5) == 0) {
+ u8 version;
+ pos += 5;
+ if (hexstr2bin(pos, &version, 1) < 0)
+ return -1;
+ pos += 2;
+ if (*pos != ' ')
+ return -1;
+ pos++;
+ ref = wpas_p2p_sd_request_upnp(wpa_s, dst, version, pos);
+#ifdef CONFIG_WIFI_DISPLAY
+ } else if (os_strncmp(pos, "wifi-display ", 13) == 0) {
+ ref = wpas_p2p_sd_request_wifi_display(wpa_s, dst, pos + 13);
+#endif /* CONFIG_WIFI_DISPLAY */
+ } else if (os_strncmp(pos, "asp ", 4) == 0) {
+ char *svc_str;
+ char *svc_info = NULL;
+ u32 id;
+
+ pos += 4;
+ if (sscanf(pos, "%x", &id) != 1 || id > 0xff)
+ return -1;
+
+ pos = os_strchr(pos, ' ');
+ if (pos == NULL || pos[1] == '\0' || pos[1] == ' ')
+ return -1;
+
+ svc_str = pos + 1;
+
+ pos = os_strchr(svc_str, ' ');
+
+ if (pos)
+ *pos++ = '\0';
+
+ /* All remaining data is the svc_info string */
+ if (pos && pos[0] && pos[0] != ' ') {
+ len = os_strlen(pos);
+
+ /* Unescape in place */
+ len = utf8_unescape(pos, len, pos, len);
+ if (len > 0xff)
+ return -1;
+
+ svc_info = pos;
+ }
+
+ ref = wpas_p2p_sd_request_asp(wpa_s, dst, (u8) id,
+ svc_str, svc_info);
+ } else {
+ len = os_strlen(pos);
+ if (len & 1)
+ return -1;
+ len /= 2;
+ tlvs = wpabuf_alloc(len);
+ if (tlvs == NULL)
+ return -1;
+ if (hexstr2bin(pos, wpabuf_put(tlvs, len), len) < 0) {
+ wpabuf_free(tlvs);
+ return -1;
+ }
+
+ ref = wpas_p2p_sd_request(wpa_s, dst, tlvs);
+ wpabuf_free(tlvs);
+ }
+ if (ref == 0)
+ return -1;
+ res = os_snprintf(buf, buflen, "%llx", (long long unsigned) ref);
+ if (os_snprintf_error(buflen, res))
+ return -1;
+ return res;
+}
+
+
+static int p2p_ctrl_serv_disc_cancel_req(struct wpa_supplicant *wpa_s,
+ char *cmd)
+{
+ long long unsigned val;
+ u64 req;
+ if (sscanf(cmd, "%llx", &val) != 1)
+ return -1;
+ req = val;
+ return wpas_p2p_sd_cancel_request(wpa_s, req);
+}
+
+
+static int p2p_ctrl_serv_disc_resp(struct wpa_supplicant *wpa_s, char *cmd)
+{
+ int freq;
+ u8 dst[ETH_ALEN];
+ u8 dialog_token;
+ struct wpabuf *resp_tlvs;
+ char *pos, *pos2;
+ size_t len;
+
+ pos = os_strchr(cmd, ' ');
+ if (pos == NULL)
+ return -1;
+ *pos++ = '\0';
+ freq = atoi(cmd);
+ if (freq == 0)
+ return -1;
+
+ if (hwaddr_aton(pos, dst))
+ return -1;
+ pos += 17;
+ if (*pos != ' ')
+ return -1;
+ pos++;
+
+ pos2 = os_strchr(pos, ' ');
+ if (pos2 == NULL)
+ return -1;
+ *pos2++ = '\0';
+ dialog_token = atoi(pos);
+
+ len = os_strlen(pos2);
+ if (len & 1)
+ return -1;
+ len /= 2;
+ resp_tlvs = wpabuf_alloc(len);
+ if (resp_tlvs == NULL)
+ return -1;
+ if (hexstr2bin(pos2, wpabuf_put(resp_tlvs, len), len) < 0) {
+ wpabuf_free(resp_tlvs);
+ return -1;
+ }
+
+ wpas_p2p_sd_response(wpa_s, freq, dst, dialog_token, resp_tlvs);
+ wpabuf_free(resp_tlvs);
+ return 0;
+}
+
+
+static int p2p_ctrl_serv_disc_external(struct wpa_supplicant *wpa_s,
+ char *cmd)
+{
+ if (os_strcmp(cmd, "0") && os_strcmp(cmd, "1"))
+ return -1;
+ wpa_s->p2p_sd_over_ctrl_iface = atoi(cmd);
+ return 0;
+}
+
+
+static int p2p_ctrl_service_add_bonjour(struct wpa_supplicant *wpa_s,
+ char *cmd)
+{
+ char *pos;
+ size_t len;
+ struct wpabuf *query, *resp;
+
+ pos = os_strchr(cmd, ' ');
+ if (pos == NULL)
+ return -1;
+ *pos++ = '\0';
+
+ len = os_strlen(cmd);
+ if (len & 1)
+ return -1;
+ len /= 2;
+ query = wpabuf_alloc(len);
+ if (query == NULL)
+ return -1;
+ if (hexstr2bin(cmd, wpabuf_put(query, len), len) < 0) {
+ wpabuf_free(query);
+ return -1;
+ }
+
+ len = os_strlen(pos);
+ if (len & 1) {
+ wpabuf_free(query);
+ return -1;
+ }
+ len /= 2;
+ resp = wpabuf_alloc(len);
+ if (resp == NULL) {
+ wpabuf_free(query);
+ return -1;
+ }
+ if (hexstr2bin(pos, wpabuf_put(resp, len), len) < 0) {
+ wpabuf_free(query);
+ wpabuf_free(resp);
+ return -1;
+ }
+
+ if (wpas_p2p_service_add_bonjour(wpa_s, query, resp) < 0) {
+ wpabuf_free(query);
+ wpabuf_free(resp);
+ return -1;
+ }
+ return 0;
+}
+
+
+static int p2p_ctrl_service_add_upnp(struct wpa_supplicant *wpa_s, char *cmd)
+{
+ char *pos;
+ u8 version;
+
+ pos = os_strchr(cmd, ' ');
+ if (pos == NULL)
+ return -1;
+ *pos++ = '\0';
+
+ if (hexstr2bin(cmd, &version, 1) < 0)
+ return -1;
+
+ return wpas_p2p_service_add_upnp(wpa_s, version, pos);
+}
+
+
+static int p2p_ctrl_service_add_asp(struct wpa_supplicant *wpa_s,
+ u8 replace, char *cmd)
+{
+ char *pos;
+ char *adv_str;
+ u32 auto_accept, adv_id, svc_state, config_methods;
+ char *svc_info = NULL;
+ char *cpt_prio_str;
+ u8 cpt_prio[P2PS_FEATURE_CAPAB_CPT_MAX + 1];
+
+ pos = os_strchr(cmd, ' ');
+ if (pos == NULL)
+ return -1;
+ *pos++ = '\0';
+
+ /* Auto-Accept value is mandatory, and must be one of the
+ * single values (0, 1, 2, 4) */
+ auto_accept = atoi(cmd);
+ switch (auto_accept) {
+ case P2PS_SETUP_NONE: /* No auto-accept */
+ case P2PS_SETUP_NEW:
+ case P2PS_SETUP_CLIENT:
+ case P2PS_SETUP_GROUP_OWNER:
+ break;
+ default:
+ return -1;
+ }
+
+ /* Advertisement ID is mandatory */
+ cmd = pos;
+ pos = os_strchr(cmd, ' ');
+ if (pos == NULL)
+ return -1;
+ *pos++ = '\0';
+
+ /* Handle Adv_ID == 0 (wildcard "org.wi-fi.wfds") internally. */
+ if (sscanf(cmd, "%x", &adv_id) != 1 || adv_id == 0)
+ return -1;
+
+ /* Only allow replacements if exist, and adds if not */
+ if (wpas_p2p_service_p2ps_id_exists(wpa_s, adv_id)) {
+ if (!replace)
+ return -1;
+ } else {
+ if (replace)
+ return -1;
+ }
+
+ /* svc_state between 0 - 0xff is mandatory */
+ if (sscanf(pos, "%x", &svc_state) != 1 || svc_state > 0xff)
+ return -1;
+
+ pos = os_strchr(pos, ' ');
+ if (pos == NULL)
+ return -1;
+
+ /* config_methods is mandatory */
+ pos++;
+ if (sscanf(pos, "%x", &config_methods) != 1)
+ return -1;
+
+ if (!(config_methods &
+ (WPS_CONFIG_DISPLAY | WPS_CONFIG_KEYPAD | WPS_CONFIG_P2PS)))
+ return -1;
+
+ pos = os_strchr(pos, ' ');
+ if (pos == NULL)
+ return -1;
+
+ pos++;
+ adv_str = pos;
+
+ /* Advertisement string is mandatory */
+ if (!pos[0] || pos[0] == ' ')
+ return -1;
+
+ /* Terminate svc string */
+ pos = os_strchr(pos, ' ');
+ if (pos != NULL)
+ *pos++ = '\0';
+
+ cpt_prio_str = (pos && pos[0]) ? os_strstr(pos, "cpt=") : NULL;
+ if (cpt_prio_str) {
+ pos = os_strchr(pos, ' ');
+ if (pos != NULL)
+ *pos++ = '\0';
+
+ if (p2ps_ctrl_parse_cpt_priority(cpt_prio_str + 4, cpt_prio))
+ return -1;
+ } else {
+ cpt_prio[0] = P2PS_FEATURE_CAPAB_UDP_TRANSPORT;
+ cpt_prio[1] = 0;
+ }
+
+ /* Service and Response Information are optional */
+ if (pos && pos[0]) {
+ size_t len;
+
+ /* Note the bare ' included, which cannot exist legally
+ * in unescaped string. */
+ svc_info = os_strstr(pos, "svc_info='");
+
+ if (svc_info) {
+ svc_info += 9;
+ len = os_strlen(svc_info);
+ utf8_unescape(svc_info, len, svc_info, len);
+ }
+ }
+
+ return wpas_p2p_service_add_asp(wpa_s, auto_accept, adv_id, adv_str,
+ (u8) svc_state, (u16) config_methods,
+ svc_info, cpt_prio);
+}
+
+
+static int p2p_ctrl_service_add(struct wpa_supplicant *wpa_s, char *cmd)
+{
+ char *pos;
+
+ pos = os_strchr(cmd, ' ');
+ if (pos == NULL)
+ return -1;
+ *pos++ = '\0';
+
+ if (os_strcmp(cmd, "bonjour") == 0)
+ return p2p_ctrl_service_add_bonjour(wpa_s, pos);
+ if (os_strcmp(cmd, "upnp") == 0)
+ return p2p_ctrl_service_add_upnp(wpa_s, pos);
+ if (os_strcmp(cmd, "asp") == 0)
+ return p2p_ctrl_service_add_asp(wpa_s, 0, pos);
+ wpa_printf(MSG_DEBUG, "Unknown service '%s'", cmd);
+ return -1;
+}
+
+
+static int p2p_ctrl_service_del_bonjour(struct wpa_supplicant *wpa_s,
+ char *cmd)
+{
+ size_t len;
+ struct wpabuf *query;
+ int ret;
+
+ len = os_strlen(cmd);
+ if (len & 1)
+ return -1;
+ len /= 2;
+ query = wpabuf_alloc(len);
+ if (query == NULL)
+ return -1;
+ if (hexstr2bin(cmd, wpabuf_put(query, len), len) < 0) {
+ wpabuf_free(query);
+ return -1;
+ }
+
+ ret = wpas_p2p_service_del_bonjour(wpa_s, query);
+ wpabuf_free(query);
+ return ret;
+}
+
+
+static int p2p_ctrl_service_del_upnp(struct wpa_supplicant *wpa_s, char *cmd)
+{
+ char *pos;
+ u8 version;
+
+ pos = os_strchr(cmd, ' ');
+ if (pos == NULL)
+ return -1;
+ *pos++ = '\0';
+
+ if (hexstr2bin(cmd, &version, 1) < 0)
+ return -1;
+
+ return wpas_p2p_service_del_upnp(wpa_s, version, pos);
+}
+
+
+static int p2p_ctrl_service_del_asp(struct wpa_supplicant *wpa_s, char *cmd)
+{
+ u32 adv_id;
+
+ if (os_strcmp(cmd, "all") == 0) {
+ wpas_p2p_service_flush_asp(wpa_s);
+ return 0;
+ }
+
+ if (sscanf(cmd, "%x", &adv_id) != 1)
+ return -1;
+
+ return wpas_p2p_service_del_asp(wpa_s, adv_id);
+}
+
+
+static int p2p_ctrl_service_del(struct wpa_supplicant *wpa_s, char *cmd)
+{
+ char *pos;
+
+ pos = os_strchr(cmd, ' ');
+ if (pos == NULL)
+ return -1;
+ *pos++ = '\0';
+
+ if (os_strcmp(cmd, "bonjour") == 0)
+ return p2p_ctrl_service_del_bonjour(wpa_s, pos);
+ if (os_strcmp(cmd, "upnp") == 0)
+ return p2p_ctrl_service_del_upnp(wpa_s, pos);
+ if (os_strcmp(cmd, "asp") == 0)
+ return p2p_ctrl_service_del_asp(wpa_s, pos);
+ wpa_printf(MSG_DEBUG, "Unknown service '%s'", cmd);
+ return -1;
+}
+
+
+static int p2p_ctrl_service_replace(struct wpa_supplicant *wpa_s, char *cmd)
+{
+ char *pos;
+
+ pos = os_strchr(cmd, ' ');
+ if (pos == NULL)
+ return -1;
+ *pos++ = '\0';
+
+ if (os_strcmp(cmd, "asp") == 0)
+ return p2p_ctrl_service_add_asp(wpa_s, 1, pos);
+
+ wpa_printf(MSG_DEBUG, "Unknown service '%s'", cmd);
+ return -1;
+}
+
+
+static int p2p_ctrl_reject(struct wpa_supplicant *wpa_s, char *cmd)
+{
+ u8 addr[ETH_ALEN];
+
+ /* <addr> */
+
+ if (hwaddr_aton(cmd, addr))
+ return -1;
+
+ return wpas_p2p_reject(wpa_s, addr);
+}
+
+
+static int p2p_ctrl_invite_persistent(struct wpa_supplicant *wpa_s, char *cmd)
+{
+ char *pos;
+ int id;
+ struct wpa_ssid *ssid;
+ u8 *_peer = NULL, peer[ETH_ALEN];
+ int freq = 0, pref_freq = 0;
+ int ht40, vht;
+
+ id = atoi(cmd);
+ pos = os_strstr(cmd, " peer=");
+ if (pos) {
+ pos += 6;
+ if (hwaddr_aton(pos, peer))
+ return -1;
+ _peer = peer;
+ }
+ ssid = wpa_config_get_network(wpa_s->conf, id);
+ if (ssid == NULL || ssid->disabled != 2) {
+ wpa_printf(MSG_DEBUG, "CTRL_IFACE: Could not find SSID id=%d "
+ "for persistent P2P group",
+ id);
+ return -1;
+ }
+
+ pos = os_strstr(cmd, " freq=");
+ if (pos) {
+ pos += 6;
+ freq = atoi(pos);
+ if (freq <= 0)
+ return -1;
+ }
+
+ pos = os_strstr(cmd, " pref=");
+ if (pos) {
+ pos += 6;
+ pref_freq = atoi(pos);
+ if (pref_freq <= 0)
+ return -1;
+ }
+
+ vht = (os_strstr(cmd, " vht") != NULL) || wpa_s->conf->p2p_go_vht;
+ ht40 = (os_strstr(cmd, " ht40") != NULL) || wpa_s->conf->p2p_go_ht40 ||
+ vht;
+
+ return wpas_p2p_invite(wpa_s, _peer, ssid, NULL, freq, ht40, vht,
+ pref_freq);
+}
+
+
+static int p2p_ctrl_invite_group(struct wpa_supplicant *wpa_s, char *cmd)
+{
+ char *pos;
+ u8 peer[ETH_ALEN], go_dev_addr[ETH_ALEN], *go_dev = NULL;
+
+ pos = os_strstr(cmd, " peer=");
+ if (!pos)
+ return -1;
+
+ *pos = '\0';
+ pos += 6;
+ if (hwaddr_aton(pos, peer)) {
+ wpa_printf(MSG_DEBUG, "P2P: Invalid MAC address '%s'", pos);
+ return -1;
+ }
+
+ pos = os_strstr(pos, " go_dev_addr=");
+ if (pos) {
+ pos += 13;
+ if (hwaddr_aton(pos, go_dev_addr)) {
+ wpa_printf(MSG_DEBUG, "P2P: Invalid MAC address '%s'",
+ pos);
+ return -1;
+ }
+ go_dev = go_dev_addr;
+ }
+
+ return wpas_p2p_invite_group(wpa_s, cmd, peer, go_dev);
+}
+
+
+static int p2p_ctrl_invite(struct wpa_supplicant *wpa_s, char *cmd)
+{
+ if (os_strncmp(cmd, "persistent=", 11) == 0)
+ return p2p_ctrl_invite_persistent(wpa_s, cmd + 11);
+ if (os_strncmp(cmd, "group=", 6) == 0)
+ return p2p_ctrl_invite_group(wpa_s, cmd + 6);
+
+ return -1;
+}
+
+
+static int p2p_ctrl_group_add_persistent(struct wpa_supplicant *wpa_s,
+ int id, int freq, int ht40, int vht)
+{
+ struct wpa_ssid *ssid;
+
+ ssid = wpa_config_get_network(wpa_s->conf, id);
+ if (ssid == NULL || ssid->disabled != 2) {
+ wpa_printf(MSG_DEBUG, "CTRL_IFACE: Could not find SSID id=%d "
+ "for persistent P2P group",
+ id);
+ return -1;
+ }
+
+ return wpas_p2p_group_add_persistent(wpa_s, ssid, 0, freq, 0, ht40, vht,
+ NULL, 0, 0);
+}
+
+
+static int p2p_ctrl_group_add(struct wpa_supplicant *wpa_s, char *cmd)
+{
+ int freq = 0, persistent = 0, group_id = -1;
+ int vht = wpa_s->conf->p2p_go_vht;
+ int ht40 = wpa_s->conf->p2p_go_ht40 || vht;
+ char *token, *context = NULL;
+
+ while ((token = str_token(cmd, " ", &context))) {
+ if (sscanf(token, "freq=%d", &freq) == 1 ||
+ sscanf(token, "persistent=%d", &group_id) == 1) {
+ continue;
+ } else if (os_strcmp(token, "ht40") == 0) {
+ ht40 = 1;
+ } else if (os_strcmp(token, "vht") == 0) {
+ vht = 1;
+ ht40 = 1;
+ } else if (os_strcmp(token, "persistent") == 0) {
+ persistent = 1;
+ } else {
+ wpa_printf(MSG_DEBUG,
+ "CTRL: Invalid P2P_GROUP_ADD parameter: '%s'",
+ token);
+ return -1;
+ }
+ }
+
+ if (group_id >= 0)
+ return p2p_ctrl_group_add_persistent(wpa_s, group_id,
+ freq, ht40, vht);
+
+ return wpas_p2p_group_add(wpa_s, persistent, freq, ht40, vht);
+}
+
+
+static int p2p_ctrl_peer(struct wpa_supplicant *wpa_s, char *cmd,
+ char *buf, size_t buflen)
+{
+ u8 addr[ETH_ALEN], *addr_ptr;
+ int next, res;
+ const struct p2p_peer_info *info;
+ char *pos, *end;
+ char devtype[WPS_DEV_TYPE_BUFSIZE];
+ struct wpa_ssid *ssid;
+ size_t i;
+
+ if (!wpa_s->global->p2p)
+ return -1;
+
+ if (os_strcmp(cmd, "FIRST") == 0) {
+ addr_ptr = NULL;
+ next = 0;
+ } else if (os_strncmp(cmd, "NEXT-", 5) == 0) {
+ if (hwaddr_aton(cmd + 5, addr) < 0)
+ return -1;
+ addr_ptr = addr;
+ next = 1;
+ } else {
+ if (hwaddr_aton(cmd, addr) < 0)
+ return -1;
+ addr_ptr = addr;
+ next = 0;
+ }
+
+ info = p2p_get_peer_info(wpa_s->global->p2p, addr_ptr, next);
+ if (info == NULL)
+ return -1;
+
+ pos = buf;
+ end = buf + buflen;
+
+ res = os_snprintf(pos, end - pos, MACSTR "\n"
+ "pri_dev_type=%s\n"
+ "device_name=%s\n"
+ "manufacturer=%s\n"
+ "model_name=%s\n"
+ "model_number=%s\n"
+ "serial_number=%s\n"
+ "config_methods=0x%x\n"
+ "dev_capab=0x%x\n"
+ "group_capab=0x%x\n"
+ "level=%d\n",
+ MAC2STR(info->p2p_device_addr),
+ wps_dev_type_bin2str(info->pri_dev_type,
+ devtype, sizeof(devtype)),
+ info->device_name,
+ info->manufacturer,
+ info->model_name,
+ info->model_number,
+ info->serial_number,
+ info->config_methods,
+ info->dev_capab,
+ info->group_capab,
+ info->level);
+ if (os_snprintf_error(end - pos, res))
+ return pos - buf;
+ pos += res;
+
+ for (i = 0; i < info->wps_sec_dev_type_list_len / WPS_DEV_TYPE_LEN; i++)
+ {
+ const u8 *t;
+ t = &info->wps_sec_dev_type_list[i * WPS_DEV_TYPE_LEN];
+ res = os_snprintf(pos, end - pos, "sec_dev_type=%s\n",
+ wps_dev_type_bin2str(t, devtype,
+ sizeof(devtype)));
+ if (os_snprintf_error(end - pos, res))
+ return pos - buf;
+ pos += res;
+ }
+
+ ssid = wpas_p2p_get_persistent(wpa_s, info->p2p_device_addr, NULL, 0);
+ if (ssid) {
+ res = os_snprintf(pos, end - pos, "persistent=%d\n", ssid->id);
+ if (os_snprintf_error(end - pos, res))
+ return pos - buf;
+ pos += res;
+ }
+
+ res = p2p_get_peer_info_txt(info, pos, end - pos);
+ if (res < 0)
+ return pos - buf;
+ pos += res;
+
+ if (info->vendor_elems) {
+ res = os_snprintf(pos, end - pos, "vendor_elems=");
+ if (os_snprintf_error(end - pos, res))
+ return pos - buf;
+ pos += res;
+
+ pos += wpa_snprintf_hex(pos, end - pos,
+ wpabuf_head(info->vendor_elems),
+ wpabuf_len(info->vendor_elems));
+
+ res = os_snprintf(pos, end - pos, "\n");
+ if (os_snprintf_error(end - pos, res))
+ return pos - buf;
+ pos += res;
+ }
+
+ return pos - buf;
+}
+
+
+static int p2p_ctrl_disallow_freq(struct wpa_supplicant *wpa_s,
+ const char *param)
+{
+ unsigned int i;
+
+ if (wpa_s->global->p2p == NULL)
+ return -1;
+
+ if (freq_range_list_parse(&wpa_s->global->p2p_disallow_freq, param) < 0)
+ return -1;
+
+ for (i = 0; i < wpa_s->global->p2p_disallow_freq.num; i++) {
+ struct wpa_freq_range *freq;
+ freq = &wpa_s->global->p2p_disallow_freq.range[i];
+ wpa_printf(MSG_DEBUG, "P2P: Disallowed frequency range %u-%u",
+ freq->min, freq->max);
+ }
+
+ wpas_p2p_update_channel_list(wpa_s, WPAS_P2P_CHANNEL_UPDATE_DISALLOW);
+ return 0;
+}
+
+
+static int p2p_ctrl_set(struct wpa_supplicant *wpa_s, char *cmd)
+{
+ char *param;
+
+ if (wpa_s->global->p2p == NULL)
+ return -1;
+
+ param = os_strchr(cmd, ' ');
+ if (param == NULL)
+ return -1;
+ *param++ = '\0';
+
+ if (os_strcmp(cmd, "discoverability") == 0) {
+ p2p_set_client_discoverability(wpa_s->global->p2p,
+ atoi(param));
+ return 0;
+ }
+
+ if (os_strcmp(cmd, "managed") == 0) {
+ p2p_set_managed_oper(wpa_s->global->p2p, atoi(param));
+ return 0;
+ }
+
+ if (os_strcmp(cmd, "listen_channel") == 0) {
+ return p2p_set_listen_channel(wpa_s->global->p2p, 81,
+ atoi(param), 1);
+ }
+
+ if (os_strcmp(cmd, "ssid_postfix") == 0) {
+ return p2p_set_ssid_postfix(wpa_s->global->p2p, (u8 *) param,
+ os_strlen(param));
+ }
+
+ if (os_strcmp(cmd, "noa") == 0) {
+ char *pos;
+ int count, start, duration;
+ /* GO NoA parameters: count,start_offset(ms),duration(ms) */
+ count = atoi(param);
+ pos = os_strchr(param, ',');
+ if (pos == NULL)
+ return -1;
+ pos++;
+ start = atoi(pos);
+ pos = os_strchr(pos, ',');
+ if (pos == NULL)
+ return -1;
+ pos++;
+ duration = atoi(pos);
+ if (count < 0 || count > 255 || start < 0 || duration < 0)
+ return -1;
+ if (count == 0 && duration > 0)
+ return -1;
+ wpa_printf(MSG_DEBUG, "CTRL_IFACE: P2P_SET GO NoA: count=%d "
+ "start=%d duration=%d", count, start, duration);
+ return wpas_p2p_set_noa(wpa_s, count, start, duration);
+ }
+
+ if (os_strcmp(cmd, "ps") == 0)
+ return wpa_drv_set_p2p_powersave(wpa_s, atoi(param), -1, -1);
+
+ if (os_strcmp(cmd, "oppps") == 0)
+ return wpa_drv_set_p2p_powersave(wpa_s, -1, atoi(param), -1);
+
+ if (os_strcmp(cmd, "ctwindow") == 0)
+ return wpa_drv_set_p2p_powersave(wpa_s, -1, -1, atoi(param));
+
+ if (os_strcmp(cmd, "disabled") == 0) {
+ wpa_s->global->p2p_disabled = atoi(param);
+ wpa_printf(MSG_DEBUG, "P2P functionality %s",
+ wpa_s->global->p2p_disabled ?
+ "disabled" : "enabled");
+ if (wpa_s->global->p2p_disabled) {
+ wpas_p2p_stop_find(wpa_s);
+ os_memset(wpa_s->p2p_auth_invite, 0, ETH_ALEN);
+ p2p_flush(wpa_s->global->p2p);
+ }
+ return 0;
+ }
+
+ if (os_strcmp(cmd, "conc_pref") == 0) {
+ if (os_strcmp(param, "sta") == 0)
+ wpa_s->global->conc_pref = WPA_CONC_PREF_STA;
+ else if (os_strcmp(param, "p2p") == 0)
+ wpa_s->global->conc_pref = WPA_CONC_PREF_P2P;
+ else {
+ wpa_printf(MSG_INFO, "Invalid conc_pref value");
+ return -1;
+ }
+ wpa_printf(MSG_DEBUG, "Single channel concurrency preference: "
+ "%s", param);
+ return 0;
+ }
+
+ if (os_strcmp(cmd, "force_long_sd") == 0) {
+ wpa_s->force_long_sd = atoi(param);
+ return 0;
+ }
+
+ if (os_strcmp(cmd, "peer_filter") == 0) {
+ u8 addr[ETH_ALEN];
+ if (hwaddr_aton(param, addr))
+ return -1;
+ p2p_set_peer_filter(wpa_s->global->p2p, addr);
+ return 0;
+ }
+
+ if (os_strcmp(cmd, "cross_connect") == 0)
+ return wpas_p2p_set_cross_connect(wpa_s, atoi(param));
+
+ if (os_strcmp(cmd, "go_apsd") == 0) {
+ if (os_strcmp(param, "disable") == 0)
+ wpa_s->set_ap_uapsd = 0;
+ else {
+ wpa_s->set_ap_uapsd = 1;
+ wpa_s->ap_uapsd = atoi(param);
+ }
+ return 0;
+ }
+
+ if (os_strcmp(cmd, "client_apsd") == 0) {
+ if (os_strcmp(param, "disable") == 0)
+ wpa_s->set_sta_uapsd = 0;
+ else {
+ int be, bk, vi, vo;
+ char *pos;
+ /* format: BE,BK,VI,VO;max SP Length */
+ be = atoi(param);
+ pos = os_strchr(param, ',');
+ if (pos == NULL)
+ return -1;
+ pos++;
+ bk = atoi(pos);
+ pos = os_strchr(pos, ',');
+ if (pos == NULL)
+ return -1;
+ pos++;
+ vi = atoi(pos);
+ pos = os_strchr(pos, ',');
+ if (pos == NULL)
+ return -1;
+ pos++;
+ vo = atoi(pos);
+ /* ignore max SP Length for now */
+
+ wpa_s->set_sta_uapsd = 1;
+ wpa_s->sta_uapsd = 0;
+ if (be)
+ wpa_s->sta_uapsd |= BIT(0);
+ if (bk)
+ wpa_s->sta_uapsd |= BIT(1);
+ if (vi)
+ wpa_s->sta_uapsd |= BIT(2);
+ if (vo)
+ wpa_s->sta_uapsd |= BIT(3);
+ }
+ return 0;
+ }
+
+ if (os_strcmp(cmd, "disallow_freq") == 0)
+ return p2p_ctrl_disallow_freq(wpa_s, param);
+
+ if (os_strcmp(cmd, "disc_int") == 0) {
+ int min_disc_int, max_disc_int, max_disc_tu;
+ char *pos;
+
+ pos = param;
+
+ min_disc_int = atoi(pos);
+ pos = os_strchr(pos, ' ');
+ if (pos == NULL)
+ return -1;
+ *pos++ = '\0';
+
+ max_disc_int = atoi(pos);
+ pos = os_strchr(pos, ' ');
+ if (pos == NULL)
+ return -1;
+ *pos++ = '\0';
+
+ max_disc_tu = atoi(pos);
+
+ return p2p_set_disc_int(wpa_s->global->p2p, min_disc_int,
+ max_disc_int, max_disc_tu);
+ }
+
+ if (os_strcmp(cmd, "per_sta_psk") == 0) {
+ wpa_s->global->p2p_per_sta_psk = !!atoi(param);
+ return 0;
+ }
+
+#ifdef CONFIG_WPS_NFC
+ if (os_strcmp(cmd, "nfc_tag") == 0)
+ return wpas_p2p_nfc_tag_enabled(wpa_s, !!atoi(param));
+#endif /* CONFIG_WPS_NFC */
+
+ if (os_strcmp(cmd, "disable_ip_addr_req") == 0) {
+ wpa_s->p2p_disable_ip_addr_req = !!atoi(param);
+ return 0;
+ }
+
+ wpa_printf(MSG_DEBUG, "CTRL_IFACE: Unknown P2P_SET field value '%s'",
+ cmd);
+
+ return -1;
+}
+
+
+static void p2p_ctrl_flush(struct wpa_supplicant *wpa_s)
+{
+ os_memset(wpa_s->p2p_auth_invite, 0, ETH_ALEN);
+ wpa_s->force_long_sd = 0;
+ wpas_p2p_stop_find(wpa_s);
+ wpa_s->parent->p2ps_method_config_any = 0;
+ if (wpa_s->global->p2p)
+ p2p_flush(wpa_s->global->p2p);
+}
+
+
+static int p2p_ctrl_presence_req(struct wpa_supplicant *wpa_s, char *cmd)
+{
+ char *pos, *pos2;
+ unsigned int dur1 = 0, int1 = 0, dur2 = 0, int2 = 0;
+
+ if (cmd[0]) {
+ pos = os_strchr(cmd, ' ');
+ if (pos == NULL)
+ return -1;
+ *pos++ = '\0';
+ dur1 = atoi(cmd);
+
+ pos2 = os_strchr(pos, ' ');
+ if (pos2)
+ *pos2++ = '\0';
+ int1 = atoi(pos);
+ } else
+ pos2 = NULL;
+
+ if (pos2) {
+ pos = os_strchr(pos2, ' ');
+ if (pos == NULL)
+ return -1;
+ *pos++ = '\0';
+ dur2 = atoi(pos2);
+ int2 = atoi(pos);
+ }
+
+ return wpas_p2p_presence_req(wpa_s, dur1, int1, dur2, int2);
+}
+
+
+static int p2p_ctrl_ext_listen(struct wpa_supplicant *wpa_s, char *cmd)
+{
+ char *pos;
+ unsigned int period = 0, interval = 0;
+
+ if (cmd[0]) {
+ pos = os_strchr(cmd, ' ');
+ if (pos == NULL)
+ return -1;
+ *pos++ = '\0';
+ period = atoi(cmd);
+ interval = atoi(pos);
+ }
+
+ return wpas_p2p_ext_listen(wpa_s, period, interval);
+}
+
+
+static int p2p_ctrl_remove_client(struct wpa_supplicant *wpa_s, const char *cmd)
+{
+ const char *pos;
+ u8 peer[ETH_ALEN];
+ int iface_addr = 0;
+
+ pos = cmd;
+ if (os_strncmp(pos, "iface=", 6) == 0) {
+ iface_addr = 1;
+ pos += 6;
+ }
+ if (hwaddr_aton(pos, peer))
+ return -1;
+
+ wpas_p2p_remove_client(wpa_s, peer, iface_addr);
+ return 0;
+}
+
+#endif /* CONFIG_P2P */
+
+
+static int * freq_range_to_channel_list(struct wpa_supplicant *wpa_s, char *val)
+{
+ struct wpa_freq_range_list ranges;
+ int *freqs = NULL;
+ struct hostapd_hw_modes *mode;
+ u16 i;
+
+ if (wpa_s->hw.modes == NULL)
+ return NULL;
+
+ os_memset(&ranges, 0, sizeof(ranges));
+ if (freq_range_list_parse(&ranges, val) < 0)
+ return NULL;
+
+ for (i = 0; i < wpa_s->hw.num_modes; i++) {
+ int j;
+
+ mode = &wpa_s->hw.modes[i];
+ for (j = 0; j < mode->num_channels; j++) {
+ unsigned int freq;
+
+ if (mode->channels[j].flag & HOSTAPD_CHAN_DISABLED)
+ continue;
+
+ freq = mode->channels[j].freq;
+ if (!freq_range_list_includes(&ranges, freq))
+ continue;
+
+ int_array_add_unique(&freqs, freq);
+ }
+ }
+
+ os_free(ranges.range);
+ return freqs;
+}
+
+
+#ifdef CONFIG_INTERWORKING
+
+static int ctrl_interworking_select(struct wpa_supplicant *wpa_s, char *param)
+{
+ int auto_sel = 0;
+ int *freqs = NULL;
+
+ if (param) {
+ char *pos;
+
+ auto_sel = os_strstr(param, "auto") != NULL;
+
+ pos = os_strstr(param, "freq=");
+ if (pos) {
+ freqs = freq_range_to_channel_list(wpa_s, pos + 5);
+ if (freqs == NULL)
+ return -1;
+ }
+
+ }
+
+ return interworking_select(wpa_s, auto_sel, freqs);
+}
+
+
+static int ctrl_interworking_connect(struct wpa_supplicant *wpa_s, char *dst,
+ int only_add)
+{
+ u8 bssid[ETH_ALEN];
+ struct wpa_bss *bss;
+
+ if (hwaddr_aton(dst, bssid)) {
+ wpa_printf(MSG_DEBUG, "Invalid BSSID '%s'", dst);
+ return -1;
+ }
+
+ bss = wpa_bss_get_bssid(wpa_s, bssid);
+ if (bss == NULL) {
+ wpa_printf(MSG_DEBUG, "Could not find BSS " MACSTR,
+ MAC2STR(bssid));
+ return -1;
+ }
+
+ if (bss->ssid_len == 0) {
+ int found = 0;
+
+ wpa_printf(MSG_DEBUG, "Selected BSS entry for " MACSTR
+ " does not have SSID information", MAC2STR(bssid));
+
+ dl_list_for_each_reverse(bss, &wpa_s->bss, struct wpa_bss,
+ list) {
+ if (os_memcmp(bss->bssid, bssid, ETH_ALEN) == 0 &&
+ bss->ssid_len > 0) {
+ found = 1;
+ break;
+ }
+ }
+
+ if (!found)
+ return -1;
+ wpa_printf(MSG_DEBUG,
+ "Found another matching BSS entry with SSID");
+ }
+
+ return interworking_connect(wpa_s, bss, only_add);
+}
+
+
+static int get_anqp(struct wpa_supplicant *wpa_s, char *dst)
+{
+ u8 dst_addr[ETH_ALEN];
+ int used;
+ char *pos;
+#define MAX_ANQP_INFO_ID 100
+ u16 id[MAX_ANQP_INFO_ID];
+ size_t num_id = 0;
+ u32 subtypes = 0;
+
+ used = hwaddr_aton2(dst, dst_addr);
+ if (used < 0)
+ return -1;
+ pos = dst + used;
+ if (*pos == ' ')
+ pos++;
+ while (num_id < MAX_ANQP_INFO_ID) {
+ if (os_strncmp(pos, "hs20:", 5) == 0) {
+#ifdef CONFIG_HS20
+ int num = atoi(pos + 5);
+ if (num <= 0 || num > 31)
+ return -1;
+ subtypes |= BIT(num);
+#else /* CONFIG_HS20 */
+ return -1;
+#endif /* CONFIG_HS20 */
+ } else {
+ id[num_id] = atoi(pos);
+ if (id[num_id])
+ num_id++;
+ }
+ pos = os_strchr(pos + 1, ',');
+ if (pos == NULL)
+ break;
+ pos++;
+ }
+
+ if (num_id == 0)
+ return -1;
+
+ return anqp_send_req(wpa_s, dst_addr, id, num_id, subtypes);
+}
+
+
+static int gas_request(struct wpa_supplicant *wpa_s, char *cmd)
+{
+ u8 dst_addr[ETH_ALEN];
+ struct wpabuf *advproto, *query = NULL;
+ int used, ret = -1;
+ char *pos, *end;
+ size_t len;
+
+ used = hwaddr_aton2(cmd, dst_addr);
+ if (used < 0)
+ return -1;
+
+ pos = cmd + used;
+ while (*pos == ' ')
+ pos++;
+
+ /* Advertisement Protocol ID */
+ end = os_strchr(pos, ' ');
+ if (end)
+ len = end - pos;
+ else
+ len = os_strlen(pos);
+ if (len & 0x01)
+ return -1;
+ len /= 2;
+ if (len == 0)
+ return -1;
+ advproto = wpabuf_alloc(len);
+ if (advproto == NULL)
+ return -1;
+ if (hexstr2bin(pos, wpabuf_put(advproto, len), len) < 0)
+ goto fail;
+
+ if (end) {
+ /* Optional Query Request */
+ pos = end + 1;
+ while (*pos == ' ')
+ pos++;
+
+ len = os_strlen(pos);
+ if (len) {
+ if (len & 0x01)
+ goto fail;
+ len /= 2;
+ if (len == 0)
+ goto fail;
+ query = wpabuf_alloc(len);
+ if (query == NULL)
+ goto fail;
+ if (hexstr2bin(pos, wpabuf_put(query, len), len) < 0)
+ goto fail;
+ }
+ }
+
+ ret = gas_send_request(wpa_s, dst_addr, advproto, query);
+
+fail:
+ wpabuf_free(advproto);
+ wpabuf_free(query);
+
+ return ret;
+}
+
+
+static int gas_response_get(struct wpa_supplicant *wpa_s, char *cmd, char *buf,
+ size_t buflen)
+{
+ u8 addr[ETH_ALEN];
+ int dialog_token;
+ int used;
+ char *pos;
+ size_t resp_len, start, requested_len;
+ struct wpabuf *resp;
+ int ret;
+
+ used = hwaddr_aton2(cmd, addr);
+ if (used < 0)
+ return -1;
+
+ pos = cmd + used;
+ while (*pos == ' ')
+ pos++;
+ dialog_token = atoi(pos);
+
+ if (wpa_s->last_gas_resp &&
+ os_memcmp(addr, wpa_s->last_gas_addr, ETH_ALEN) == 0 &&
+ dialog_token == wpa_s->last_gas_dialog_token)
+ resp = wpa_s->last_gas_resp;
+ else if (wpa_s->prev_gas_resp &&
+ os_memcmp(addr, wpa_s->prev_gas_addr, ETH_ALEN) == 0 &&
+ dialog_token == wpa_s->prev_gas_dialog_token)
+ resp = wpa_s->prev_gas_resp;
+ else
+ return -1;
+
+ resp_len = wpabuf_len(resp);
+ start = 0;
+ requested_len = resp_len;
+
+ pos = os_strchr(pos, ' ');
+ if (pos) {
+ start = atoi(pos);
+ if (start > resp_len)
+ return os_snprintf(buf, buflen, "FAIL-Invalid range");
+ pos = os_strchr(pos, ',');
+ if (pos == NULL)
+ return -1;
+ pos++;
+ requested_len = atoi(pos);
+ if (start + requested_len > resp_len)
+ return os_snprintf(buf, buflen, "FAIL-Invalid range");
+ }
+
+ if (requested_len * 2 + 1 > buflen)
+ return os_snprintf(buf, buflen, "FAIL-Too long response");
+
+ ret = wpa_snprintf_hex(buf, buflen, wpabuf_head_u8(resp) + start,
+ requested_len);
+
+ if (start + requested_len == resp_len) {
+ /*
+ * Free memory by dropping the response after it has been
+ * fetched.
+ */
+ if (resp == wpa_s->prev_gas_resp) {
+ wpabuf_free(wpa_s->prev_gas_resp);
+ wpa_s->prev_gas_resp = NULL;
+ } else {
+ wpabuf_free(wpa_s->last_gas_resp);
+ wpa_s->last_gas_resp = NULL;
+ }
+ }
+
+ return ret;
+}
+#endif /* CONFIG_INTERWORKING */
+
+
+#ifdef CONFIG_HS20
+
+static int get_hs20_anqp(struct wpa_supplicant *wpa_s, char *dst)
+{
+ u8 dst_addr[ETH_ALEN];
+ int used;
+ char *pos;
+ u32 subtypes = 0;
+
+ used = hwaddr_aton2(dst, dst_addr);
+ if (used < 0)
+ return -1;
+ pos = dst + used;
+ if (*pos == ' ')
+ pos++;
+ for (;;) {
+ int num = atoi(pos);
+ if (num <= 0 || num > 31)
+ return -1;
+ subtypes |= BIT(num);
+ pos = os_strchr(pos + 1, ',');
+ if (pos == NULL)
+ break;
+ pos++;
+ }
+
+ if (subtypes == 0)
+ return -1;
+
+ return hs20_anqp_send_req(wpa_s, dst_addr, subtypes, NULL, 0);
+}
+
+
+static int hs20_nai_home_realm_list(struct wpa_supplicant *wpa_s,
+ const u8 *addr, const char *realm)
+{
+ u8 *buf;
+ size_t rlen, len;
+ int ret;
+
+ rlen = os_strlen(realm);
+ len = 3 + rlen;
+ buf = os_malloc(len);
+ if (buf == NULL)
+ return -1;
+ buf[0] = 1; /* NAI Home Realm Count */
+ buf[1] = 0; /* Formatted in accordance with RFC 4282 */
+ buf[2] = rlen;
+ os_memcpy(buf + 3, realm, rlen);
+
+ ret = hs20_anqp_send_req(wpa_s, addr,
+ BIT(HS20_STYPE_NAI_HOME_REALM_QUERY),
+ buf, len);
+
+ os_free(buf);
+
+ return ret;
+}
+
+
+static int hs20_get_nai_home_realm_list(struct wpa_supplicant *wpa_s,
+ char *dst)
+{
+ struct wpa_cred *cred = wpa_s->conf->cred;
+ u8 dst_addr[ETH_ALEN];
+ int used;
+ u8 *buf;
+ size_t len;
+ int ret;
+
+ used = hwaddr_aton2(dst, dst_addr);
+ if (used < 0)
+ return -1;
+
+ while (dst[used] == ' ')
+ used++;
+ if (os_strncmp(dst + used, "realm=", 6) == 0)
+ return hs20_nai_home_realm_list(wpa_s, dst_addr,
+ dst + used + 6);
+
+ len = os_strlen(dst + used);
+
+ if (len == 0 && cred && cred->realm)
+ return hs20_nai_home_realm_list(wpa_s, dst_addr, cred->realm);
+
+ if (len & 1)
+ return -1;
+ len /= 2;
+ buf = os_malloc(len);
+ if (buf == NULL)
+ return -1;
+ if (hexstr2bin(dst + used, buf, len) < 0) {
+ os_free(buf);
+ return -1;
+ }
+
+ ret = hs20_anqp_send_req(wpa_s, dst_addr,
+ BIT(HS20_STYPE_NAI_HOME_REALM_QUERY),
+ buf, len);
+ os_free(buf);
+
+ return ret;
+}
+
+
+static int hs20_icon_request(struct wpa_supplicant *wpa_s, char *cmd)
+{
+ u8 dst_addr[ETH_ALEN];
+ int used;
+ char *icon;
+
+ used = hwaddr_aton2(cmd, dst_addr);
+ if (used < 0)
+ return -1;
+
+ while (cmd[used] == ' ')
+ used++;
+ icon = &cmd[used];
+
+ wpa_s->fetch_osu_icon_in_progress = 0;
+ return hs20_anqp_send_req(wpa_s, dst_addr, BIT(HS20_STYPE_ICON_REQUEST),
+ (u8 *) icon, os_strlen(icon));
+}
+
+#endif /* CONFIG_HS20 */
+
+
+#ifdef CONFIG_AUTOSCAN
+
+static int wpa_supplicant_ctrl_iface_autoscan(struct wpa_supplicant *wpa_s,
+ char *cmd)
+{
+ enum wpa_states state = wpa_s->wpa_state;
+ char *new_params = NULL;
+
+ if (os_strlen(cmd) > 0) {
+ new_params = os_strdup(cmd);
+ if (new_params == NULL)
+ return -1;
+ }
+
+ os_free(wpa_s->conf->autoscan);
+ wpa_s->conf->autoscan = new_params;
+
+ if (wpa_s->conf->autoscan == NULL)
+ autoscan_deinit(wpa_s);
+ else if (state == WPA_DISCONNECTED || state == WPA_INACTIVE)
+ autoscan_init(wpa_s, 1);
+ else if (state == WPA_SCANNING)
+ wpa_supplicant_reinit_autoscan(wpa_s);
+
+ return 0;
+}
+
+#endif /* CONFIG_AUTOSCAN */
+
+
+#ifdef CONFIG_WNM
+
+static int wpas_ctrl_iface_wnm_sleep(struct wpa_supplicant *wpa_s, char *cmd)
+{
+ int enter;
+ int intval = 0;
+ char *pos;
+ int ret;
+ struct wpabuf *tfs_req = NULL;
+
+ if (os_strncmp(cmd, "enter", 5) == 0)
+ enter = 1;
+ else if (os_strncmp(cmd, "exit", 4) == 0)
+ enter = 0;
+ else
+ return -1;
+
+ pos = os_strstr(cmd, " interval=");
+ if (pos)
+ intval = atoi(pos + 10);
+
+ pos = os_strstr(cmd, " tfs_req=");
+ if (pos) {
+ char *end;
+ size_t len;
+ pos += 9;
+ end = os_strchr(pos, ' ');
+ if (end)
+ len = end - pos;
+ else
+ len = os_strlen(pos);
+ if (len & 1)
+ return -1;
+ len /= 2;
+ tfs_req = wpabuf_alloc(len);
+ if (tfs_req == NULL)
+ return -1;
+ if (hexstr2bin(pos, wpabuf_put(tfs_req, len), len) < 0) {
+ wpabuf_free(tfs_req);
+ return -1;
+ }
+ }
+
+ ret = ieee802_11_send_wnmsleep_req(wpa_s, enter ? WNM_SLEEP_MODE_ENTER :
+ WNM_SLEEP_MODE_EXIT, intval,
+ tfs_req);
+ wpabuf_free(tfs_req);
+
+ return ret;
+}
+
+
+static int wpas_ctrl_iface_wnm_bss_query(struct wpa_supplicant *wpa_s, char *cmd)
+{
+ int query_reason;
+
+ query_reason = atoi(cmd);
+
+ wpa_printf(MSG_DEBUG, "CTRL_IFACE: WNM_BSS_QUERY query_reason=%d",
+ query_reason);
+
+ return wnm_send_bss_transition_mgmt_query(wpa_s, query_reason);
+}
+
+#endif /* CONFIG_WNM */
+
+
+static int wpa_supplicant_signal_poll(struct wpa_supplicant *wpa_s, char *buf,
+ size_t buflen)
+{
+ struct wpa_signal_info si;
+ int ret;
+ char *pos, *end;
+
+ ret = wpa_drv_signal_poll(wpa_s, &si);
+ if (ret)
+ return -1;
+
+ pos = buf;
+ end = buf + buflen;
+
+ ret = os_snprintf(pos, end - pos, "RSSI=%d\nLINKSPEED=%d\n"
+ "NOISE=%d\nFREQUENCY=%u\n",
+ si.current_signal, si.current_txrate / 1000,
+ si.current_noise, si.frequency);
+ if (os_snprintf_error(end - pos, ret))
+ return -1;
+ pos += ret;
+
+ if (si.chanwidth != CHAN_WIDTH_UNKNOWN) {
+ ret = os_snprintf(pos, end - pos, "WIDTH=%s\n",
+ channel_width_to_string(si.chanwidth));
+ if (os_snprintf_error(end - pos, ret))
+ return -1;
+ pos += ret;
+ }
+
+ if (si.center_frq1 > 0 && si.center_frq2 > 0) {
+ ret = os_snprintf(pos, end - pos,
+ "CENTER_FRQ1=%d\nCENTER_FRQ2=%d\n",
+ si.center_frq1, si.center_frq2);
+ if (os_snprintf_error(end - pos, ret))
+ return -1;
+ pos += ret;
+ }
+
+ if (si.avg_signal) {
+ ret = os_snprintf(pos, end - pos,
+ "AVG_RSSI=%d\n", si.avg_signal);
+ if (os_snprintf_error(end - pos, ret))
+ return -1;
+ pos += ret;
+ }
+
+ if (si.avg_beacon_signal) {
+ ret = os_snprintf(pos, end - pos,
+ "AVG_BEACON_RSSI=%d\n", si.avg_beacon_signal);
+ if (os_snprintf_error(end - pos, ret))
+ return -1;
+ pos += ret;
+ }
+
+ return pos - buf;
+}
+
+
+static int wpas_ctrl_iface_get_pref_freq_list(
+ struct wpa_supplicant *wpa_s, char *cmd, char *buf, size_t buflen)
+{
+ unsigned int freq_list[100], num = 100, i;
+ int ret;
+ enum wpa_driver_if_type iface_type;
+ char *pos, *end;
+
+ pos = buf;
+ end = buf + buflen;
+
+ /* buf: "<interface_type>" */
+ if (os_strcmp(cmd, "STATION") == 0)
+ iface_type = WPA_IF_STATION;
+ else if (os_strcmp(cmd, "AP") == 0)
+ iface_type = WPA_IF_AP_BSS;
+ else if (os_strcmp(cmd, "P2P_GO") == 0)
+ iface_type = WPA_IF_P2P_GO;
+ else if (os_strcmp(cmd, "P2P_CLIENT") == 0)
+ iface_type = WPA_IF_P2P_CLIENT;
+ else if (os_strcmp(cmd, "IBSS") == 0)
+ iface_type = WPA_IF_IBSS;
+ else if (os_strcmp(cmd, "TDLS") == 0)
+ iface_type = WPA_IF_TDLS;
+ else
+ return -1;
+
+ wpa_printf(MSG_DEBUG,
+ "CTRL_IFACE: GET_PREF_FREQ_LIST iface_type=%d (%s)",
+ iface_type, buf);
+
+ ret = wpa_drv_get_pref_freq_list(wpa_s, iface_type, &num, freq_list);
+ if (ret)
+ return -1;
+
+ for (i = 0; i < num; i++) {
+ ret = os_snprintf(pos, end - pos, "%s%u",
+ i > 0 ? "," : "", freq_list[i]);
+ if (os_snprintf_error(end - pos, ret))
+ return -1;
+ pos += ret;
+ }
+
+ return pos - buf;
+}
+
+
+static int wpa_supplicant_pktcnt_poll(struct wpa_supplicant *wpa_s, char *buf,
+ size_t buflen)
+{
+ struct hostap_sta_driver_data sta;
+ int ret;
+
+ ret = wpa_drv_pktcnt_poll(wpa_s, &sta);
+ if (ret)
+ return -1;
+
+ ret = os_snprintf(buf, buflen, "TXGOOD=%lu\nTXBAD=%lu\nRXGOOD=%lu\n",
+ sta.tx_packets, sta.tx_retry_failed, sta.rx_packets);
+ if (os_snprintf_error(buflen, ret))
+ return -1;
+ return ret;
+}
+
+
+#ifdef ANDROID
+static int wpa_supplicant_driver_cmd(struct wpa_supplicant *wpa_s, char *cmd,
+ char *buf, size_t buflen)
+{
+ int ret;
+
+ ret = wpa_drv_driver_cmd(wpa_s, cmd, buf, buflen);
+ if (ret == 0) {
+ if (os_strncasecmp(cmd, "COUNTRY", 7) == 0) {
+ struct p2p_data *p2p = wpa_s->global->p2p;
+ if (p2p) {
+ char country[3];
+ country[0] = cmd[8];
+ country[1] = cmd[9];
+ country[2] = 0x04;
+ p2p_set_country(p2p, country);
+ }
+ }
+ ret = os_snprintf(buf, buflen, "%s\n", "OK");
+ if (os_snprintf_error(buflen, ret))
+ ret = -1;
+ }
+ return ret;
+}
+#endif /* ANDROID */
+
+
+static int wpa_supplicant_vendor_cmd(struct wpa_supplicant *wpa_s, char *cmd,
+ char *buf, size_t buflen)
+{
+ int ret;
+ char *pos;
+ u8 *data = NULL;
+ unsigned int vendor_id, subcmd;
+ struct wpabuf *reply;
+ size_t data_len = 0;
+
+ /* cmd: <vendor id> <subcommand id> [<hex formatted data>] */
+ vendor_id = strtoul(cmd, &pos, 16);
+ if (!isblank(*pos))
+ return -EINVAL;
+
+ subcmd = strtoul(pos, &pos, 10);
+
+ if (*pos != '\0') {
+ if (!isblank(*pos++))
+ return -EINVAL;
+ data_len = os_strlen(pos);
+ }
+
+ if (data_len) {
+ data_len /= 2;
+ data = os_malloc(data_len);
+ if (!data)
+ return -1;
+
+ if (hexstr2bin(pos, data, data_len)) {
+ wpa_printf(MSG_DEBUG,
+ "Vendor command: wrong parameter format");
+ os_free(data);
+ return -EINVAL;
+ }
+ }
+
+ reply = wpabuf_alloc((buflen - 1) / 2);
+ if (!reply) {
+ os_free(data);
+ return -1;
+ }
+
+ ret = wpa_drv_vendor_cmd(wpa_s, vendor_id, subcmd, data, data_len,
+ reply);
+
+ if (ret == 0)
+ ret = wpa_snprintf_hex(buf, buflen, wpabuf_head_u8(reply),
+ wpabuf_len(reply));
+
+ wpabuf_free(reply);
+ os_free(data);
+
+ return ret;
+}
+
+
+static void wpa_supplicant_ctrl_iface_flush(struct wpa_supplicant *wpa_s)
+{
+#ifdef CONFIG_P2P
+ struct wpa_supplicant *p2p_wpa_s = wpa_s->global->p2p_init_wpa_s ?
+ wpa_s->global->p2p_init_wpa_s : wpa_s;
+#endif /* CONFIG_P2P */
+
+ wpa_dbg(wpa_s, MSG_DEBUG, "Flush all wpa_supplicant state");
+
+#ifdef CONFIG_P2P
+ wpas_p2p_cancel(p2p_wpa_s);
+ p2p_ctrl_flush(p2p_wpa_s);
+ wpas_p2p_group_remove(p2p_wpa_s, "*");
+ wpas_p2p_service_flush(p2p_wpa_s);
+ p2p_wpa_s->global->p2p_disabled = 0;
+ p2p_wpa_s->global->p2p_per_sta_psk = 0;
+ p2p_wpa_s->conf->num_sec_device_types = 0;
+ p2p_wpa_s->p2p_disable_ip_addr_req = 0;
+ os_free(p2p_wpa_s->global->p2p_go_avoid_freq.range);
+ p2p_wpa_s->global->p2p_go_avoid_freq.range = NULL;
+ p2p_wpa_s->global->p2p_go_avoid_freq.num = 0;
+ p2p_wpa_s->global->pending_p2ps_group = 0;
+#endif /* CONFIG_P2P */
+
+#ifdef CONFIG_WPS_TESTING
+ wps_version_number = 0x20;
+ wps_testing_dummy_cred = 0;
+ wps_corrupt_pkhash = 0;
+#endif /* CONFIG_WPS_TESTING */
+#ifdef CONFIG_WPS
+ wpa_s->wps_fragment_size = 0;
+ wpas_wps_cancel(wpa_s);
+ wps_registrar_flush(wpa_s->wps->registrar);
+#endif /* CONFIG_WPS */
+ wpa_s->after_wps = 0;
+ wpa_s->known_wps_freq = 0;
+
+#ifdef CONFIG_TDLS
+#ifdef CONFIG_TDLS_TESTING
+ extern unsigned int tdls_testing;
+ tdls_testing = 0;
+#endif /* CONFIG_TDLS_TESTING */
+ wpa_drv_tdls_oper(wpa_s, TDLS_ENABLE, NULL);
+ wpa_tdls_enable(wpa_s->wpa, 1);
+#endif /* CONFIG_TDLS */
+
+ eloop_cancel_timeout(wpa_supplicant_stop_countermeasures, wpa_s, NULL);
+ wpa_supplicant_stop_countermeasures(wpa_s, NULL);
+
+ wpa_s->no_keep_alive = 0;
+ wpa_s->own_disconnect_req = 0;
+
+ os_free(wpa_s->disallow_aps_bssid);
+ wpa_s->disallow_aps_bssid = NULL;
+ wpa_s->disallow_aps_bssid_count = 0;
+ os_free(wpa_s->disallow_aps_ssid);
+ wpa_s->disallow_aps_ssid = NULL;
+ wpa_s->disallow_aps_ssid_count = 0;
+
+ wpa_s->set_sta_uapsd = 0;
+ wpa_s->sta_uapsd = 0;
+
+ wpa_drv_radio_disable(wpa_s, 0);
+ wpa_blacklist_clear(wpa_s);
+ wpa_s->extra_blacklist_count = 0;
+ wpa_supplicant_ctrl_iface_remove_network(wpa_s, "all");
+ wpa_supplicant_ctrl_iface_remove_cred(wpa_s, "all");
+ wpa_config_flush_blobs(wpa_s->conf);
+ wpa_s->conf->auto_interworking = 0;
+ wpa_s->conf->okc = 0;
+
+ wpa_sm_pmksa_cache_flush(wpa_s->wpa, NULL);
+ rsn_preauth_deinit(wpa_s->wpa);
+
+ wpa_sm_set_param(wpa_s->wpa, RSNA_PMK_LIFETIME, 43200);
+ wpa_sm_set_param(wpa_s->wpa, RSNA_PMK_REAUTH_THRESHOLD, 70);
+ wpa_sm_set_param(wpa_s->wpa, RSNA_SA_TIMEOUT, 60);
+ eapol_sm_notify_logoff(wpa_s->eapol, FALSE);
+
+ radio_remove_works(wpa_s, NULL, 1);
+ wpa_s->ext_work_in_progress = 0;
+
+ wpa_s->next_ssid = NULL;
+
+#ifdef CONFIG_INTERWORKING
+ hs20_cancel_fetch_osu(wpa_s);
+#endif /* CONFIG_INTERWORKING */
+
+ wpa_s->ext_mgmt_frame_handling = 0;
+ wpa_s->ext_eapol_frame_io = 0;
+#ifdef CONFIG_TESTING_OPTIONS
+ wpa_s->extra_roc_dur = 0;
+ wpa_s->test_failure = WPAS_TEST_FAILURE_NONE;
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ wpa_s->disconnected = 0;
+ os_free(wpa_s->next_scan_freqs);
+ wpa_s->next_scan_freqs = NULL;
+
+ wpa_bss_flush(wpa_s);
+ if (!dl_list_empty(&wpa_s->bss)) {
+ wpa_printf(MSG_DEBUG,
+ "BSS table not empty after flush: %u entries, current_bss=%p bssid="
+ MACSTR " pending_bssid=" MACSTR,
+ dl_list_len(&wpa_s->bss), wpa_s->current_bss,
+ MAC2STR(wpa_s->bssid),
+ MAC2STR(wpa_s->pending_bssid));
+ }
+
+ eloop_cancel_timeout(wpas_network_reenabled, wpa_s, NULL);
+}
+
+
+static int wpas_ctrl_radio_work_show(struct wpa_supplicant *wpa_s,
+ char *buf, size_t buflen)
+{
+ struct wpa_radio_work *work;
+ char *pos, *end;
+ struct os_reltime now, diff;
+
+ pos = buf;
+ end = buf + buflen;
+
+ os_get_reltime(&now);
+
+ dl_list_for_each(work, &wpa_s->radio->work, struct wpa_radio_work, list)
+ {
+ int ret;
+
+ os_reltime_sub(&now, &work->time, &diff);
+ ret = os_snprintf(pos, end - pos, "%s@%s:%u:%u:%ld.%06ld\n",
+ work->type, work->wpa_s->ifname, work->freq,
+ work->started, diff.sec, diff.usec);
+ if (os_snprintf_error(end - pos, ret))
+ break;
+ pos += ret;
+ }
+
+ return pos - buf;
+}
+
+
+static void wpas_ctrl_radio_work_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+ struct wpa_radio_work *work = eloop_ctx;
+ struct wpa_external_work *ework = work->ctx;
+
+ wpa_dbg(work->wpa_s, MSG_DEBUG,
+ "Timing out external radio work %u (%s)",
+ ework->id, work->type);
+ wpa_msg(work->wpa_s, MSG_INFO, EXT_RADIO_WORK_TIMEOUT "%u", ework->id);
+ work->wpa_s->ext_work_in_progress = 0;
+ radio_work_done(work);
+ os_free(ework);
+}
+
+
+static void wpas_ctrl_radio_work_cb(struct wpa_radio_work *work, int deinit)
+{
+ struct wpa_external_work *ework = work->ctx;
+
+ if (deinit) {
+ if (work->started)
+ eloop_cancel_timeout(wpas_ctrl_radio_work_timeout,
+ work, NULL);
+
+ os_free(ework);
+ return;
+ }
+
+ wpa_dbg(work->wpa_s, MSG_DEBUG, "Starting external radio work %u (%s)",
+ ework->id, ework->type);
+ wpa_msg(work->wpa_s, MSG_INFO, EXT_RADIO_WORK_START "%u", ework->id);
+ work->wpa_s->ext_work_in_progress = 1;
+ if (!ework->timeout)
+ ework->timeout = 10;
+ eloop_register_timeout(ework->timeout, 0, wpas_ctrl_radio_work_timeout,
+ work, NULL);
+}
+
+
+static int wpas_ctrl_radio_work_add(struct wpa_supplicant *wpa_s, char *cmd,
+ char *buf, size_t buflen)
+{
+ struct wpa_external_work *ework;
+ char *pos, *pos2;
+ size_t type_len;
+ int ret;
+ unsigned int freq = 0;
+
+ /* format: <name> [freq=<MHz>] [timeout=<seconds>] */
+
+ ework = os_zalloc(sizeof(*ework));
+ if (ework == NULL)
+ return -1;
+
+ pos = os_strchr(cmd, ' ');
+ if (pos) {
+ type_len = pos - cmd;
+ pos++;
+
+ pos2 = os_strstr(pos, "freq=");
+ if (pos2)
+ freq = atoi(pos2 + 5);
+
+ pos2 = os_strstr(pos, "timeout=");
+ if (pos2)
+ ework->timeout = atoi(pos2 + 8);
+ } else {
+ type_len = os_strlen(cmd);
+ }
+ if (4 + type_len >= sizeof(ework->type))
+ type_len = sizeof(ework->type) - 4 - 1;
+ os_strlcpy(ework->type, "ext:", sizeof(ework->type));
+ os_memcpy(ework->type + 4, cmd, type_len);
+ ework->type[4 + type_len] = '\0';
+
+ wpa_s->ext_work_id++;
+ if (wpa_s->ext_work_id == 0)
+ wpa_s->ext_work_id++;
+ ework->id = wpa_s->ext_work_id;
+
+ if (radio_add_work(wpa_s, freq, ework->type, 0, wpas_ctrl_radio_work_cb,
+ ework) < 0) {
+ os_free(ework);
+ return -1;
+ }
+
+ ret = os_snprintf(buf, buflen, "%u", ework->id);
+ if (os_snprintf_error(buflen, ret))
+ return -1;
+ return ret;
+}
+
+
+static int wpas_ctrl_radio_work_done(struct wpa_supplicant *wpa_s, char *cmd)
+{
+ struct wpa_radio_work *work;
+ unsigned int id = atoi(cmd);
+
+ dl_list_for_each(work, &wpa_s->radio->work, struct wpa_radio_work, list)
+ {
+ struct wpa_external_work *ework;
+
+ if (os_strncmp(work->type, "ext:", 4) != 0)
+ continue;
+ ework = work->ctx;
+ if (id && ework->id != id)
+ continue;
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ "Completed external radio work %u (%s)",
+ ework->id, ework->type);
+ eloop_cancel_timeout(wpas_ctrl_radio_work_timeout, work, NULL);
+ wpa_s->ext_work_in_progress = 0;
+ radio_work_done(work);
+ os_free(ework);
+ return 3; /* "OK\n" */
+ }
+
+ return -1;
+}
+
+
+static int wpas_ctrl_radio_work(struct wpa_supplicant *wpa_s, char *cmd,
+ char *buf, size_t buflen)
+{
+ if (os_strcmp(cmd, "show") == 0)
+ return wpas_ctrl_radio_work_show(wpa_s, buf, buflen);
+ if (os_strncmp(cmd, "add ", 4) == 0)
+ return wpas_ctrl_radio_work_add(wpa_s, cmd + 4, buf, buflen);
+ if (os_strncmp(cmd, "done ", 5) == 0)
+ return wpas_ctrl_radio_work_done(wpa_s, cmd + 4);
+ return -1;
+}
+
+
+void wpas_ctrl_radio_work_flush(struct wpa_supplicant *wpa_s)
+{
+ struct wpa_radio_work *work, *tmp;
+
+ if (!wpa_s || !wpa_s->radio)
+ return;
+
+ dl_list_for_each_safe(work, tmp, &wpa_s->radio->work,
+ struct wpa_radio_work, list) {
+ struct wpa_external_work *ework;
+
+ if (os_strncmp(work->type, "ext:", 4) != 0)
+ continue;
+ ework = work->ctx;
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ "Flushing%s external radio work %u (%s)",
+ work->started ? " started" : "", ework->id,
+ ework->type);
+ if (work->started)
+ eloop_cancel_timeout(wpas_ctrl_radio_work_timeout,
+ work, NULL);
+ radio_work_done(work);
+ os_free(ework);
+ }
+}
+
+
+static void wpas_ctrl_eapol_response(void *eloop_ctx, void *timeout_ctx)
+{
+ struct wpa_supplicant *wpa_s = eloop_ctx;
+ eapol_sm_notify_ctrl_response(wpa_s->eapol);
+}
+
+
+static int scan_id_list_parse(struct wpa_supplicant *wpa_s, const char *value,
+ unsigned int *scan_id_count, int scan_id[])
+{
+ const char *pos = value;
+
+ while (pos) {
+ if (*pos == ' ' || *pos == '\0')
+ break;
+ if (*scan_id_count == MAX_SCAN_ID)
+ return -1;
+ scan_id[(*scan_id_count)++] = atoi(pos);
+ pos = os_strchr(pos, ',');
+ if (pos)
+ pos++;
+ }
+
+ return 0;
+}
+
+
+static void wpas_ctrl_scan(struct wpa_supplicant *wpa_s, char *params,
+ char *reply, int reply_size, int *reply_len)
+{
+ char *pos;
+ unsigned int manual_scan_passive = 0;
+ unsigned int manual_scan_use_id = 0;
+ unsigned int manual_scan_only_new = 0;
+ unsigned int scan_only = 0;
+ unsigned int scan_id_count = 0;
+ int scan_id[MAX_SCAN_ID];
+ void (*scan_res_handler)(struct wpa_supplicant *wpa_s,
+ struct wpa_scan_results *scan_res);
+ int *manual_scan_freqs = NULL;
+ struct wpa_ssid_value *ssid = NULL, *ns;
+ unsigned int ssid_count = 0;
+
+ if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED) {
+ *reply_len = -1;
+ return;
+ }
+
+ if (radio_work_pending(wpa_s, "scan")) {
+ wpa_printf(MSG_DEBUG,
+ "Pending scan scheduled - reject new request");
+ *reply_len = os_snprintf(reply, reply_size, "FAIL-BUSY\n");
+ return;
+ }
+
+#ifdef CONFIG_INTERWORKING
+ if (wpa_s->fetch_anqp_in_progress || wpa_s->network_select) {
+ wpa_printf(MSG_DEBUG,
+ "Interworking select in progress - reject new scan");
+ *reply_len = os_snprintf(reply, reply_size, "FAIL-BUSY\n");
+ return;
+ }
+#endif /* CONFIG_INTERWORKING */
+
+ if (params) {
+ if (os_strncasecmp(params, "TYPE=ONLY", 9) == 0)
+ scan_only = 1;
+
+ pos = os_strstr(params, "freq=");
+ if (pos) {
+ manual_scan_freqs = freq_range_to_channel_list(wpa_s,
+ pos + 5);
+ if (manual_scan_freqs == NULL) {
+ *reply_len = -1;
+ goto done;
+ }
+ }
+
+ pos = os_strstr(params, "passive=");
+ if (pos)
+ manual_scan_passive = !!atoi(pos + 8);
+
+ pos = os_strstr(params, "use_id=");
+ if (pos)
+ manual_scan_use_id = atoi(pos + 7);
+
+ pos = os_strstr(params, "only_new=1");
+ if (pos)
+ manual_scan_only_new = 1;
+
+ pos = os_strstr(params, "scan_id=");
+ if (pos && scan_id_list_parse(wpa_s, pos + 8, &scan_id_count,
+ scan_id) < 0) {
+ *reply_len = -1;
+ goto done;
+ }
+
+ pos = params;
+ while (pos && *pos != '\0') {
+ if (os_strncmp(pos, "ssid ", 5) == 0) {
+ char *end;
+
+ pos += 5;
+ end = pos;
+ while (*end) {
+ if (*end == '\0' || *end == ' ')
+ break;
+ end++;
+ }
+
+ ns = os_realloc_array(
+ ssid, ssid_count + 1,
+ sizeof(struct wpa_ssid_value));
+ if (ns == NULL) {
+ *reply_len = -1;
+ goto done;
+ }
+ ssid = ns;
+
+ if ((end - pos) & 0x01 ||
+ end - pos > 2 * SSID_MAX_LEN ||
+ hexstr2bin(pos, ssid[ssid_count].ssid,
+ (end - pos) / 2) < 0) {
+ wpa_printf(MSG_DEBUG,
+ "Invalid SSID value '%s'",
+ pos);
+ *reply_len = -1;
+ goto done;
+ }
+ ssid[ssid_count].ssid_len = (end - pos) / 2;
+ wpa_hexdump_ascii(MSG_DEBUG, "scan SSID",
+ ssid[ssid_count].ssid,
+ ssid[ssid_count].ssid_len);
+ ssid_count++;
+ pos = end;
+ }
+
+ pos = os_strchr(pos, ' ');
+ if (pos)
+ pos++;
+ }
+ }
+
+ wpa_s->num_ssids_from_scan_req = ssid_count;
+ os_free(wpa_s->ssids_from_scan_req);
+ if (ssid_count) {
+ wpa_s->ssids_from_scan_req = ssid;
+ ssid = NULL;
+ } else {
+ wpa_s->ssids_from_scan_req = NULL;
+ }
+
+ if (scan_only)
+ scan_res_handler = scan_only_handler;
+ else if (wpa_s->scan_res_handler == scan_only_handler)
+ scan_res_handler = NULL;
+ else
+ scan_res_handler = wpa_s->scan_res_handler;
+
+ if (!wpa_s->sched_scanning && !wpa_s->scanning &&
+ ((wpa_s->wpa_state <= WPA_SCANNING) ||
+ (wpa_s->wpa_state == WPA_COMPLETED))) {
+ wpa_s->manual_scan_passive = manual_scan_passive;
+ wpa_s->manual_scan_use_id = manual_scan_use_id;
+ wpa_s->manual_scan_only_new = manual_scan_only_new;
+ wpa_s->scan_id_count = scan_id_count;
+ os_memcpy(wpa_s->scan_id, scan_id, scan_id_count * sizeof(int));
+ wpa_s->scan_res_handler = scan_res_handler;
+ os_free(wpa_s->manual_scan_freqs);
+ wpa_s->manual_scan_freqs = manual_scan_freqs;
+ manual_scan_freqs = NULL;
+
+ wpa_s->normal_scans = 0;
+ wpa_s->scan_req = MANUAL_SCAN_REQ;
+ wpa_s->after_wps = 0;
+ wpa_s->known_wps_freq = 0;
+ wpa_supplicant_req_scan(wpa_s, 0, 0);
+ if (wpa_s->manual_scan_use_id) {
+ wpa_s->manual_scan_id++;
+ wpa_dbg(wpa_s, MSG_DEBUG, "Assigned scan id %u",
+ wpa_s->manual_scan_id);
+ *reply_len = os_snprintf(reply, reply_size, "%u\n",
+ wpa_s->manual_scan_id);
+ }
+ } else if (wpa_s->sched_scanning) {
+ wpa_s->manual_scan_passive = manual_scan_passive;
+ wpa_s->manual_scan_use_id = manual_scan_use_id;
+ wpa_s->manual_scan_only_new = manual_scan_only_new;
+ wpa_s->scan_id_count = scan_id_count;
+ os_memcpy(wpa_s->scan_id, scan_id, scan_id_count * sizeof(int));
+ wpa_s->scan_res_handler = scan_res_handler;
+ os_free(wpa_s->manual_scan_freqs);
+ wpa_s->manual_scan_freqs = manual_scan_freqs;
+ manual_scan_freqs = NULL;
+
+ wpa_printf(MSG_DEBUG, "Stop ongoing sched_scan to allow requested full scan to proceed");
+ wpa_supplicant_cancel_sched_scan(wpa_s);
+ wpa_s->scan_req = MANUAL_SCAN_REQ;
+ wpa_supplicant_req_scan(wpa_s, 0, 0);
+ if (wpa_s->manual_scan_use_id) {
+ wpa_s->manual_scan_id++;
+ *reply_len = os_snprintf(reply, reply_size, "%u\n",
+ wpa_s->manual_scan_id);
+ wpa_dbg(wpa_s, MSG_DEBUG, "Assigned scan id %u",
+ wpa_s->manual_scan_id);
+ }
+ } else {
+ wpa_printf(MSG_DEBUG, "Ongoing scan action - reject new request");
+ *reply_len = os_snprintf(reply, reply_size, "FAIL-BUSY\n");
+ }
+
+done:
+ os_free(manual_scan_freqs);
+ os_free(ssid);
+}
+
+
+#ifdef CONFIG_TESTING_OPTIONS
+
+static void wpas_ctrl_iface_mgmt_tx_cb(struct wpa_supplicant *wpa_s,
+ unsigned int freq, const u8 *dst,
+ const u8 *src, const u8 *bssid,
+ const u8 *data, size_t data_len,
+ enum offchannel_send_action_result
+ result)
+{
+ wpa_msg(wpa_s, MSG_INFO, "MGMT-TX-STATUS freq=%u dst=" MACSTR
+ " src=" MACSTR " bssid=" MACSTR " result=%s",
+ freq, MAC2STR(dst), MAC2STR(src), MAC2STR(bssid),
+ result == OFFCHANNEL_SEND_ACTION_SUCCESS ?
+ "SUCCESS" : (result == OFFCHANNEL_SEND_ACTION_NO_ACK ?
+ "NO_ACK" : "FAILED"));
+}
+
+
+static int wpas_ctrl_iface_mgmt_tx(struct wpa_supplicant *wpa_s, char *cmd)
+{
+ char *pos, *param;
+ size_t len;
+ u8 *buf, da[ETH_ALEN], bssid[ETH_ALEN];
+ int res, used;
+ int freq = 0, no_cck = 0, wait_time = 0;
+
+ /* <DA> <BSSID> [freq=<MHz>] [wait_time=<ms>] [no_cck=1]
+ * <action=Action frame payload> */
+
+ wpa_printf(MSG_DEBUG, "External MGMT TX: %s", cmd);
+
+ pos = cmd;
+ used = hwaddr_aton2(pos, da);
+ if (used < 0)
+ return -1;
+ pos += used;
+ while (*pos == ' ')
+ pos++;
+ used = hwaddr_aton2(pos, bssid);
+ if (used < 0)
+ return -1;
+ pos += used;
+
+ param = os_strstr(pos, " freq=");
+ if (param) {
+ param += 6;
+ freq = atoi(param);
+ }
+
+ param = os_strstr(pos, " no_cck=");
+ if (param) {
+ param += 8;
+ no_cck = atoi(param);
+ }
+
+ param = os_strstr(pos, " wait_time=");
+ if (param) {
+ param += 11;
+ wait_time = atoi(param);
+ }
+
+ param = os_strstr(pos, " action=");
+ if (param == NULL)
+ return -1;
+ param += 8;
+
+ len = os_strlen(param);
+ if (len & 1)
+ return -1;
+ len /= 2;
+
+ buf = os_malloc(len);
+ if (buf == NULL)
+ return -1;
+
+ if (hexstr2bin(param, buf, len) < 0) {
+ os_free(buf);
+ return -1;
+ }
+
+ res = offchannel_send_action(wpa_s, freq, da, wpa_s->own_addr, bssid,
+ buf, len, wait_time,
+ wpas_ctrl_iface_mgmt_tx_cb, no_cck);
+ os_free(buf);
+ return res;
+}
+
+
+static void wpas_ctrl_iface_mgmt_tx_done(struct wpa_supplicant *wpa_s)
+{
+ wpa_printf(MSG_DEBUG, "External MGMT TX - done waiting");
+ offchannel_send_action_done(wpa_s);
+}
+
+
+static int wpas_ctrl_iface_driver_event(struct wpa_supplicant *wpa_s, char *cmd)
+{
+ char *pos, *param;
+ union wpa_event_data event;
+ enum wpa_event_type ev;
+
+ /* <event name> [parameters..] */
+
+ wpa_dbg(wpa_s, MSG_DEBUG, "Testing - external driver event: %s", cmd);
+
+ pos = cmd;
+ param = os_strchr(pos, ' ');
+ if (param)
+ *param++ = '\0';
+
+ os_memset(&event, 0, sizeof(event));
+
+ if (os_strcmp(cmd, "INTERFACE_ENABLED") == 0) {
+ ev = EVENT_INTERFACE_ENABLED;
+ } else if (os_strcmp(cmd, "INTERFACE_DISABLED") == 0) {
+ ev = EVENT_INTERFACE_DISABLED;
+ } else if (os_strcmp(cmd, "AVOID_FREQUENCIES") == 0) {
+ ev = EVENT_AVOID_FREQUENCIES;
+ if (param == NULL)
+ param = "";
+ if (freq_range_list_parse(&event.freq_range, param) < 0)
+ return -1;
+ wpa_supplicant_event(wpa_s, ev, &event);
+ os_free(event.freq_range.range);
+ return 0;
+ } else {
+ wpa_dbg(wpa_s, MSG_DEBUG, "Testing - unknown driver event: %s",
+ cmd);
+ return -1;
+ }
+
+ wpa_supplicant_event(wpa_s, ev, &event);
+
+ return 0;
+}
+
+
+static int wpas_ctrl_iface_eapol_rx(struct wpa_supplicant *wpa_s, char *cmd)
+{
+ char *pos;
+ u8 src[ETH_ALEN], *buf;
+ int used;
+ size_t len;
+
+ wpa_printf(MSG_DEBUG, "External EAPOL RX: %s", cmd);
+
+ pos = cmd;
+ used = hwaddr_aton2(pos, src);
+ if (used < 0)
+ return -1;
+ pos += used;
+ while (*pos == ' ')
+ pos++;
+
+ len = os_strlen(pos);
+ if (len & 1)
+ return -1;
+ len /= 2;
+
+ buf = os_malloc(len);
+ if (buf == NULL)
+ return -1;
+
+ if (hexstr2bin(pos, buf, len) < 0) {
+ os_free(buf);
+ return -1;
+ }
+
+ wpa_supplicant_rx_eapol(wpa_s, src, buf, len);
+ os_free(buf);
+
+ return 0;
+}
+
+
+static u16 ipv4_hdr_checksum(const void *buf, size_t len)
+{
+ size_t i;
+ u32 sum = 0;
+ const u16 *pos = buf;
+
+ for (i = 0; i < len / 2; i++)
+ sum += *pos++;
+
+ while (sum >> 16)
+ sum = (sum & 0xffff) + (sum >> 16);
+
+ return sum ^ 0xffff;
+}
+
+
+#define HWSIM_PACKETLEN 1500
+#define HWSIM_IP_LEN (HWSIM_PACKETLEN - sizeof(struct ether_header))
+
+void wpas_data_test_rx(void *ctx, const u8 *src_addr, const u8 *buf, size_t len)
+{
+ struct wpa_supplicant *wpa_s = ctx;
+ const struct ether_header *eth;
+ struct iphdr ip;
+ const u8 *pos;
+ unsigned int i;
+
+ if (len != HWSIM_PACKETLEN)
+ return;
+
+ eth = (const struct ether_header *) buf;
+ os_memcpy(&ip, eth + 1, sizeof(ip));
+ pos = &buf[sizeof(*eth) + sizeof(ip)];
+
+ if (ip.ihl != 5 || ip.version != 4 ||
+ ntohs(ip.tot_len) != HWSIM_IP_LEN)
+ return;
+
+ for (i = 0; i < HWSIM_IP_LEN - sizeof(ip); i++) {
+ if (*pos != (u8) i)
+ return;
+ pos++;
+ }
+
+ wpa_msg(wpa_s, MSG_INFO, "DATA-TEST-RX " MACSTR " " MACSTR,
+ MAC2STR(eth->ether_dhost), MAC2STR(eth->ether_shost));
+}
+
+
+static int wpas_ctrl_iface_data_test_config(struct wpa_supplicant *wpa_s,
+ char *cmd)
+{
+ int enabled = atoi(cmd);
+
+ if (!enabled) {
+ if (wpa_s->l2_test) {
+ l2_packet_deinit(wpa_s->l2_test);
+ wpa_s->l2_test = NULL;
+ wpa_dbg(wpa_s, MSG_DEBUG, "test data: Disabled");
+ }
+ return 0;
+ }
+
+ if (wpa_s->l2_test)
+ return 0;
+
+ wpa_s->l2_test = l2_packet_init(wpa_s->ifname, wpa_s->own_addr,
+ ETHERTYPE_IP, wpas_data_test_rx,
+ wpa_s, 1);
+ if (wpa_s->l2_test == NULL)
+ return -1;
+
+ wpa_dbg(wpa_s, MSG_DEBUG, "test data: Enabled");
+
+ return 0;
+}
+
+
+static int wpas_ctrl_iface_data_test_tx(struct wpa_supplicant *wpa_s, char *cmd)
+{
+ u8 dst[ETH_ALEN], src[ETH_ALEN];
+ char *pos;
+ int used;
+ long int val;
+ u8 tos;
+ u8 buf[2 + HWSIM_PACKETLEN];
+ struct ether_header *eth;
+ struct iphdr *ip;
+ u8 *dpos;
+ unsigned int i;
+
+ if (wpa_s->l2_test == NULL)
+ return -1;
+
+ /* format: <dst> <src> <tos> */
+
+ pos = cmd;
+ used = hwaddr_aton2(pos, dst);
+ if (used < 0)
+ return -1;
+ pos += used;
+ while (*pos == ' ')
+ pos++;
+ used = hwaddr_aton2(pos, src);
+ if (used < 0)
+ return -1;
+ pos += used;
+
+ val = strtol(pos, NULL, 0);
+ if (val < 0 || val > 0xff)
+ return -1;
+ tos = val;
+
+ eth = (struct ether_header *) &buf[2];
+ os_memcpy(eth->ether_dhost, dst, ETH_ALEN);
+ os_memcpy(eth->ether_shost, src, ETH_ALEN);
+ eth->ether_type = htons(ETHERTYPE_IP);
+ ip = (struct iphdr *) (eth + 1);
+ os_memset(ip, 0, sizeof(*ip));
+ ip->ihl = 5;
+ ip->version = 4;
+ ip->ttl = 64;
+ ip->tos = tos;
+ ip->tot_len = htons(HWSIM_IP_LEN);
+ ip->protocol = 1;
+ ip->saddr = htonl(192U << 24 | 168 << 16 | 1 << 8 | 1);
+ ip->daddr = htonl(192U << 24 | 168 << 16 | 1 << 8 | 2);
+ ip->check = ipv4_hdr_checksum(ip, sizeof(*ip));
+ dpos = (u8 *) (ip + 1);
+ for (i = 0; i < HWSIM_IP_LEN - sizeof(*ip); i++)
+ *dpos++ = i;
+
+ if (l2_packet_send(wpa_s->l2_test, dst, ETHERTYPE_IP, &buf[2],
+ HWSIM_PACKETLEN) < 0)
+ return -1;
+
+ wpa_dbg(wpa_s, MSG_DEBUG, "test data: TX dst=" MACSTR " src=" MACSTR
+ " tos=0x%x", MAC2STR(dst), MAC2STR(src), tos);
+
+ return 0;
+}
+
+
+static int wpas_ctrl_iface_data_test_frame(struct wpa_supplicant *wpa_s,
+ char *cmd)
+{
+ u8 *buf;
+ struct ether_header *eth;
+ struct l2_packet_data *l2 = NULL;
+ size_t len;
+ u16 ethertype;
+ int res = -1;
+
+ len = os_strlen(cmd);
+ if (len & 1 || len < ETH_HLEN * 2)
+ return -1;
+ len /= 2;
+
+ buf = os_malloc(len);
+ if (buf == NULL)
+ return -1;
+
+ if (hexstr2bin(cmd, buf, len) < 0)
+ goto done;
+
+ eth = (struct ether_header *) buf;
+ ethertype = ntohs(eth->ether_type);
+
+ l2 = l2_packet_init(wpa_s->ifname, wpa_s->own_addr, ethertype,
+ wpas_data_test_rx, wpa_s, 1);
+ if (l2 == NULL)
+ goto done;
+
+ res = l2_packet_send(l2, eth->ether_dhost, ethertype, buf, len);
+ wpa_dbg(wpa_s, MSG_DEBUG, "test data: TX frame res=%d", res);
+done:
+ if (l2)
+ l2_packet_deinit(l2);
+ os_free(buf);
+
+ return res < 0 ? -1 : 0;
+}
+
+
+static int wpas_ctrl_test_alloc_fail(struct wpa_supplicant *wpa_s, char *cmd)
+{
+#ifdef WPA_TRACE_BFD
+ extern char wpa_trace_fail_func[256];
+ extern unsigned int wpa_trace_fail_after;
+ char *pos;
+
+ wpa_trace_fail_after = atoi(cmd);
+ pos = os_strchr(cmd, ':');
+ if (pos) {
+ pos++;
+ os_strlcpy(wpa_trace_fail_func, pos,
+ sizeof(wpa_trace_fail_func));
+ } else {
+ wpa_trace_fail_after = 0;
+ }
+ return 0;
+#else /* WPA_TRACE_BFD */
+ return -1;
+#endif /* WPA_TRACE_BFD */
+}
+
+
+static int wpas_ctrl_get_alloc_fail(struct wpa_supplicant *wpa_s,
+ char *buf, size_t buflen)
+{
+#ifdef WPA_TRACE_BFD
+ extern char wpa_trace_fail_func[256];
+ extern unsigned int wpa_trace_fail_after;
+
+ return os_snprintf(buf, buflen, "%u:%s", wpa_trace_fail_after,
+ wpa_trace_fail_func);
+#else /* WPA_TRACE_BFD */
+ return -1;
+#endif /* WPA_TRACE_BFD */
+}
+
+
+static int wpas_ctrl_test_fail(struct wpa_supplicant *wpa_s, char *cmd)
+{
+#ifdef WPA_TRACE_BFD
+ extern char wpa_trace_test_fail_func[256];
+ extern unsigned int wpa_trace_test_fail_after;
+ char *pos;
+
+ wpa_trace_test_fail_after = atoi(cmd);
+ pos = os_strchr(cmd, ':');
+ if (pos) {
+ pos++;
+ os_strlcpy(wpa_trace_test_fail_func, pos,
+ sizeof(wpa_trace_test_fail_func));
+ } else {
+ wpa_trace_test_fail_after = 0;
+ }
+ return 0;
+#else /* WPA_TRACE_BFD */
+ return -1;
+#endif /* WPA_TRACE_BFD */
+}
+
+
+static int wpas_ctrl_get_fail(struct wpa_supplicant *wpa_s,
+ char *buf, size_t buflen)
+{
+#ifdef WPA_TRACE_BFD
+ extern char wpa_trace_test_fail_func[256];
+ extern unsigned int wpa_trace_test_fail_after;
+
+ return os_snprintf(buf, buflen, "%u:%s", wpa_trace_test_fail_after,
+ wpa_trace_test_fail_func);
+#else /* WPA_TRACE_BFD */
+ return -1;
+#endif /* WPA_TRACE_BFD */
+}
+
+#endif /* CONFIG_TESTING_OPTIONS */
+
+
+static void wpas_ctrl_vendor_elem_update(struct wpa_supplicant *wpa_s)
+{
+ unsigned int i;
+ char buf[30];
+
+ wpa_printf(MSG_DEBUG, "Update vendor elements");
+
+ for (i = 0; i < NUM_VENDOR_ELEM_FRAMES; i++) {
+ if (wpa_s->vendor_elem[i]) {
+ int res;
+
+ res = os_snprintf(buf, sizeof(buf), "frame[%u]", i);
+ if (!os_snprintf_error(sizeof(buf), res)) {
+ wpa_hexdump_buf(MSG_DEBUG, buf,
+ wpa_s->vendor_elem[i]);
+ }
+ }
+ }
+
+#ifdef CONFIG_P2P
+ if (wpa_s->parent == wpa_s &&
+ wpa_s->global->p2p &&
+ !wpa_s->global->p2p_disabled)
+ p2p_set_vendor_elems(wpa_s->global->p2p, wpa_s->vendor_elem);
+#endif /* CONFIG_P2P */
+}
+
+
+static struct wpa_supplicant *
+wpas_ctrl_vendor_elem_iface(struct wpa_supplicant *wpa_s,
+ enum wpa_vendor_elem_frame frame)
+{
+ switch (frame) {
+#ifdef CONFIG_P2P
+ case VENDOR_ELEM_PROBE_REQ_P2P:
+ case VENDOR_ELEM_PROBE_RESP_P2P:
+ case VENDOR_ELEM_PROBE_RESP_P2P_GO:
+ case VENDOR_ELEM_BEACON_P2P_GO:
+ case VENDOR_ELEM_P2P_PD_REQ:
+ case VENDOR_ELEM_P2P_PD_RESP:
+ case VENDOR_ELEM_P2P_GO_NEG_REQ:
+ case VENDOR_ELEM_P2P_GO_NEG_RESP:
+ case VENDOR_ELEM_P2P_GO_NEG_CONF:
+ case VENDOR_ELEM_P2P_INV_REQ:
+ case VENDOR_ELEM_P2P_INV_RESP:
+ case VENDOR_ELEM_P2P_ASSOC_REQ:
+ return wpa_s->parent;
+#endif /* CONFIG_P2P */
+ default:
+ return wpa_s;
+ }
+}
+
+
+static int wpas_ctrl_vendor_elem_add(struct wpa_supplicant *wpa_s, char *cmd)
+{
+ char *pos = cmd;
+ int frame;
+ size_t len;
+ struct wpabuf *buf;
+ struct ieee802_11_elems elems;
+
+ frame = atoi(pos);
+ if (frame < 0 || frame >= NUM_VENDOR_ELEM_FRAMES)
+ return -1;
+ wpa_s = wpas_ctrl_vendor_elem_iface(wpa_s, frame);
+
+ pos = os_strchr(pos, ' ');
+ if (pos == NULL)
+ return -1;
+ pos++;
+
+ len = os_strlen(pos);
+ if (len == 0)
+ return 0;
+ if (len & 1)
+ return -1;
+ len /= 2;
+
+ buf = wpabuf_alloc(len);
+ if (buf == NULL)
+ return -1;
+
+ if (hexstr2bin(pos, wpabuf_put(buf, len), len) < 0) {
+ wpabuf_free(buf);
+ return -1;
+ }
+
+ if (ieee802_11_parse_elems(wpabuf_head_u8(buf), len, &elems, 0) ==
+ ParseFailed) {
+ wpabuf_free(buf);
+ return -1;
+ }
+
+ if (wpa_s->vendor_elem[frame] == NULL) {
+ wpa_s->vendor_elem[frame] = buf;
+ wpas_ctrl_vendor_elem_update(wpa_s);
+ return 0;
+ }
+
+ if (wpabuf_resize(&wpa_s->vendor_elem[frame], len) < 0) {
+ wpabuf_free(buf);
+ return -1;
+ }
+
+ wpabuf_put_buf(wpa_s->vendor_elem[frame], buf);
+ wpabuf_free(buf);
+ wpas_ctrl_vendor_elem_update(wpa_s);
+
+ return 0;
+}
+
+
+static int wpas_ctrl_vendor_elem_get(struct wpa_supplicant *wpa_s, char *cmd,
+ char *buf, size_t buflen)
+{
+ int frame = atoi(cmd);
+
+ if (frame < 0 || frame >= NUM_VENDOR_ELEM_FRAMES)
+ return -1;
+ wpa_s = wpas_ctrl_vendor_elem_iface(wpa_s, frame);
+
+ if (wpa_s->vendor_elem[frame] == NULL)
+ return 0;
+
+ return wpa_snprintf_hex(buf, buflen,
+ wpabuf_head_u8(wpa_s->vendor_elem[frame]),
+ wpabuf_len(wpa_s->vendor_elem[frame]));
+}
+
+
+static int wpas_ctrl_vendor_elem_remove(struct wpa_supplicant *wpa_s, char *cmd)
+{
+ char *pos = cmd;
+ int frame;
+ size_t len;
+ u8 *buf;
+ struct ieee802_11_elems elems;
+ u8 *ie, *end;
+
+ frame = atoi(pos);
+ if (frame < 0 || frame >= NUM_VENDOR_ELEM_FRAMES)
+ return -1;
+ wpa_s = wpas_ctrl_vendor_elem_iface(wpa_s, frame);
+
+ pos = os_strchr(pos, ' ');
+ if (pos == NULL)
+ return -1;
+ pos++;
+
+ if (*pos == '*') {
+ wpabuf_free(wpa_s->vendor_elem[frame]);
+ wpa_s->vendor_elem[frame] = NULL;
+ wpas_ctrl_vendor_elem_update(wpa_s);
+ return 0;
+ }
+
+ if (wpa_s->vendor_elem[frame] == NULL)
+ return -1;
+
+ len = os_strlen(pos);
+ if (len == 0)
+ return 0;
+ if (len & 1)
+ return -1;
+ len /= 2;
+
+ buf = os_malloc(len);
+ if (buf == NULL)
+ return -1;
+
+ if (hexstr2bin(pos, buf, len) < 0) {
+ os_free(buf);
+ return -1;
+ }
+
+ if (ieee802_11_parse_elems(buf, len, &elems, 0) == ParseFailed) {
+ os_free(buf);
+ return -1;
+ }
+
+ ie = wpabuf_mhead_u8(wpa_s->vendor_elem[frame]);
+ end = ie + wpabuf_len(wpa_s->vendor_elem[frame]);
+
+ for (; ie + 1 < end; ie += 2 + ie[1]) {
+ if (ie + len > end)
+ break;
+ if (os_memcmp(ie, buf, len) != 0)
+ continue;
+
+ if (wpabuf_len(wpa_s->vendor_elem[frame]) == len) {
+ wpabuf_free(wpa_s->vendor_elem[frame]);
+ wpa_s->vendor_elem[frame] = NULL;
+ } else {
+ os_memmove(ie, ie + len,
+ end - (ie + len));
+ wpa_s->vendor_elem[frame]->used -= len;
+ }
+ os_free(buf);
+ wpas_ctrl_vendor_elem_update(wpa_s);
+ return 0;
+ }
+
+ os_free(buf);
+
+ return -1;
+}
+
+
+static void wpas_ctrl_neighbor_rep_cb(void *ctx, struct wpabuf *neighbor_rep)
+{
+ struct wpa_supplicant *wpa_s = ctx;
+
+ if (neighbor_rep) {
+ wpa_msg_ctrl(wpa_s, MSG_INFO, RRM_EVENT_NEIGHBOR_REP_RXED
+ "length=%u",
+ (unsigned int) wpabuf_len(neighbor_rep));
+ wpabuf_free(neighbor_rep);
+ } else {
+ wpa_msg_ctrl(wpa_s, MSG_INFO, RRM_EVENT_NEIGHBOR_REP_FAILED);
+ }
+}
+
+
+static int wpas_ctrl_iface_send_neigbor_rep(struct wpa_supplicant *wpa_s,
+ char *cmd)
+{
+ struct wpa_ssid ssid;
+ struct wpa_ssid *ssid_p = NULL;
+ int ret = 0;
+
+ if (os_strncmp(cmd, " ssid=", 6) == 0) {
+ ssid.ssid_len = os_strlen(cmd + 6);
+ if (ssid.ssid_len > SSID_MAX_LEN)
+ return -1;
+ ssid.ssid = (u8 *) (cmd + 6);
+ ssid_p = &ssid;
+ }
+
+ ret = wpas_rrm_send_neighbor_rep_request(wpa_s, ssid_p,
+ wpas_ctrl_neighbor_rep_cb,
+ wpa_s);
+
+ return ret;
+}
+
+
+static int wpas_ctrl_iface_erp_flush(struct wpa_supplicant *wpa_s)
+{
+ eapol_sm_erp_flush(wpa_s->eapol);
+ return 0;
+}
+
+
+static int wpas_ctrl_iface_mac_rand_scan(struct wpa_supplicant *wpa_s,
+ char *cmd)
+{
+ char *token, *context = NULL;
+ unsigned int enable = ~0, type = 0;
+ u8 _addr[ETH_ALEN], _mask[ETH_ALEN];
+ u8 *addr = NULL, *mask = NULL;
+
+ while ((token = str_token(cmd, " ", &context))) {
+ if (os_strcasecmp(token, "scan") == 0) {
+ type |= MAC_ADDR_RAND_SCAN;
+ } else if (os_strcasecmp(token, "sched") == 0) {
+ type |= MAC_ADDR_RAND_SCHED_SCAN;
+ } else if (os_strcasecmp(token, "pno") == 0) {
+ type |= MAC_ADDR_RAND_PNO;
+ } else if (os_strcasecmp(token, "all") == 0) {
+ type = wpa_s->mac_addr_rand_supported;
+ } else if (os_strncasecmp(token, "enable=", 7) == 0) {
+ enable = atoi(token + 7);
+ } else if (os_strncasecmp(token, "addr=", 5) == 0) {
+ addr = _addr;
+ if (hwaddr_aton(token + 5, addr)) {
+ wpa_printf(MSG_INFO,
+ "CTRL: Invalid MAC address: %s",
+ token);
+ return -1;
+ }
+ } else if (os_strncasecmp(token, "mask=", 5) == 0) {
+ mask = _mask;
+ if (hwaddr_aton(token + 5, mask)) {
+ wpa_printf(MSG_INFO,
+ "CTRL: Invalid MAC address mask: %s",
+ token);
+ return -1;
+ }
+ } else {
+ wpa_printf(MSG_INFO,
+ "CTRL: Invalid MAC_RAND_SCAN parameter: %s",
+ token);
+ return -1;
+ }
+ }
+
+ if (!type) {
+ wpa_printf(MSG_INFO, "CTRL: MAC_RAND_SCAN no type specified");
+ return -1;
+ }
+
+ if ((wpa_s->mac_addr_rand_supported & type) != type) {
+ wpa_printf(MSG_INFO,
+ "CTRL: MAC_RAND_SCAN types=%u != supported=%u",
+ type, wpa_s->mac_addr_rand_supported);
+ return -1;
+ }
+
+ if (enable > 1) {
+ wpa_printf(MSG_INFO,
+ "CTRL: MAC_RAND_SCAN enable=<0/1> not specified");
+ return -1;
+ }
+
+ if (!enable) {
+ wpas_mac_addr_rand_scan_clear(wpa_s, type);
+ if (wpa_s->pno) {
+ if (type & MAC_ADDR_RAND_PNO) {
+ wpas_stop_pno(wpa_s);
+ wpas_start_pno(wpa_s);
+ }
+ } else if (wpa_s->sched_scanning &&
+ (type & MAC_ADDR_RAND_SCHED_SCAN)) {
+ /* simulate timeout to restart the sched scan */
+ wpa_s->sched_scan_timed_out = 1;
+ wpa_s->prev_sched_ssid = NULL;
+ wpa_supplicant_cancel_sched_scan(wpa_s);
+ }
+ return 0;
+ }
+
+ if ((addr && !mask) || (!addr && mask)) {
+ wpa_printf(MSG_INFO,
+ "CTRL: MAC_RAND_SCAN invalid addr/mask combination");
+ return -1;
+ }
+
+ if (addr && mask && (!(mask[0] & 0x01) || (addr[0] & 0x01))) {
+ wpa_printf(MSG_INFO,
+ "CTRL: MAC_RAND_SCAN cannot allow multicast address");
+ return -1;
+ }
+
+ if (type & MAC_ADDR_RAND_SCAN) {
+ wpas_mac_addr_rand_scan_set(wpa_s, MAC_ADDR_RAND_SCAN,
+ addr, mask);
+ }
+
+ if (type & MAC_ADDR_RAND_SCHED_SCAN) {
+ wpas_mac_addr_rand_scan_set(wpa_s, MAC_ADDR_RAND_SCHED_SCAN,
+ addr, mask);
+
+ if (wpa_s->sched_scanning && !wpa_s->pno) {
+ /* simulate timeout to restart the sched scan */
+ wpa_s->sched_scan_timed_out = 1;
+ wpa_s->prev_sched_ssid = NULL;
+ wpa_supplicant_cancel_sched_scan(wpa_s);
+ }
+ }
+
+ if (type & MAC_ADDR_RAND_PNO) {
+ wpas_mac_addr_rand_scan_set(wpa_s, MAC_ADDR_RAND_PNO,
+ addr, mask);
+ if (wpa_s->pno) {
+ wpas_stop_pno(wpa_s);
+ wpas_start_pno(wpa_s);
+ }
+ }
+
+ return 0;
+}
+
+
+static int wpas_ctrl_cmd_debug_level(const char *cmd)
+{
+ if (os_strcmp(cmd, "PING") == 0 ||
+ os_strncmp(cmd, "BSS ", 4) == 0 ||
+ os_strncmp(cmd, "GET_NETWORK ", 12) == 0 ||
+ os_strncmp(cmd, "STATUS", 6) == 0 ||
+ os_strncmp(cmd, "STA ", 4) == 0 ||
+ os_strncmp(cmd, "STA-", 4) == 0)
+ return MSG_EXCESSIVE;
+ return MSG_DEBUG;
+}
+
+
+char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s,
+ char *buf, size_t *resp_len)
+{
+ char *reply;
+ const int reply_size = 4096;
+ int reply_len;
+
+ if (os_strncmp(buf, WPA_CTRL_RSP, os_strlen(WPA_CTRL_RSP)) == 0 ||
+ os_strncmp(buf, "SET_NETWORK ", 12) == 0) {
+ if (wpa_debug_show_keys)
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ "Control interface command '%s'", buf);
+ else
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ "Control interface command '%s [REMOVED]'",
+ os_strncmp(buf, WPA_CTRL_RSP,
+ os_strlen(WPA_CTRL_RSP)) == 0 ?
+ WPA_CTRL_RSP : "SET_NETWORK");
+ } else if (os_strncmp(buf, "WPS_NFC_TAG_READ", 16) == 0 ||
+ os_strncmp(buf, "NFC_REPORT_HANDOVER", 19) == 0) {
+ wpa_hexdump_ascii_key(MSG_DEBUG, "RX ctrl_iface",
+ (const u8 *) buf, os_strlen(buf));
+ } else {
+ int level = wpas_ctrl_cmd_debug_level(buf);
+ wpa_dbg(wpa_s, level, "Control interface command '%s'", buf);
+ }
+
+ reply = os_malloc(reply_size);
+ if (reply == NULL) {
+ *resp_len = 1;
+ return NULL;
+ }
+
+ os_memcpy(reply, "OK\n", 3);
+ reply_len = 3;
+
+ if (os_strcmp(buf, "PING") == 0) {
+ os_memcpy(reply, "PONG\n", 5);
+ reply_len = 5;
+ } else if (os_strcmp(buf, "IFNAME") == 0) {
+ reply_len = os_strlen(wpa_s->ifname);
+ os_memcpy(reply, wpa_s->ifname, reply_len);
+ } else if (os_strncmp(buf, "RELOG", 5) == 0) {
+ if (wpa_debug_reopen_file() < 0)
+ reply_len = -1;
+ } else if (os_strncmp(buf, "NOTE ", 5) == 0) {
+ wpa_printf(MSG_INFO, "NOTE: %s", buf + 5);
+ } else if (os_strcmp(buf, "MIB") == 0) {
+ reply_len = wpa_sm_get_mib(wpa_s->wpa, reply, reply_size);
+ if (reply_len >= 0) {
+ reply_len += eapol_sm_get_mib(wpa_s->eapol,
+ reply + reply_len,
+ reply_size - reply_len);
+ }
+ } else if (os_strncmp(buf, "STATUS", 6) == 0) {
+ reply_len = wpa_supplicant_ctrl_iface_status(
+ wpa_s, buf + 6, reply, reply_size);
+ } else if (os_strcmp(buf, "PMKSA") == 0) {
+ reply_len = wpa_sm_pmksa_cache_list(wpa_s->wpa, reply,
+ reply_size);
+ } else if (os_strcmp(buf, "PMKSA_FLUSH") == 0) {
+ wpa_sm_pmksa_cache_flush(wpa_s->wpa, NULL);
+ } else if (os_strncmp(buf, "SET ", 4) == 0) {
+ if (wpa_supplicant_ctrl_iface_set(wpa_s, buf + 4))
+ reply_len = -1;
+ } else if (os_strncmp(buf, "DUMP", 4) == 0) {
+ reply_len = wpa_config_dump_values(wpa_s->conf,
+ reply, reply_size);
+ } else if (os_strncmp(buf, "GET ", 4) == 0) {
+ reply_len = wpa_supplicant_ctrl_iface_get(wpa_s, buf + 4,
+ reply, reply_size);
+ } else if (os_strcmp(buf, "LOGON") == 0) {
+ eapol_sm_notify_logoff(wpa_s->eapol, FALSE);
+ } else if (os_strcmp(buf, "LOGOFF") == 0) {
+ eapol_sm_notify_logoff(wpa_s->eapol, TRUE);
+ } else if (os_strcmp(buf, "REASSOCIATE") == 0) {
+ if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED)
+ reply_len = -1;
+ else
+ wpas_request_connection(wpa_s);
+ } else if (os_strcmp(buf, "REATTACH") == 0) {
+ if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED ||
+ !wpa_s->current_ssid)
+ reply_len = -1;
+ else {
+ wpa_s->reattach = 1;
+ wpas_request_connection(wpa_s);
+ }
+ } else if (os_strcmp(buf, "RECONNECT") == 0) {
+ if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED)
+ reply_len = -1;
+ else if (wpa_s->disconnected)
+ wpas_request_connection(wpa_s);
+#ifdef IEEE8021X_EAPOL
+ } else if (os_strncmp(buf, "PREAUTH ", 8) == 0) {
+ if (wpa_supplicant_ctrl_iface_preauth(wpa_s, buf + 8))
+ reply_len = -1;
+#endif /* IEEE8021X_EAPOL */
+#ifdef CONFIG_PEERKEY
+ } else if (os_strncmp(buf, "STKSTART ", 9) == 0) {
+ if (wpa_supplicant_ctrl_iface_stkstart(wpa_s, buf + 9))
+ reply_len = -1;
+#endif /* CONFIG_PEERKEY */
+#ifdef CONFIG_IEEE80211R
+ } else if (os_strncmp(buf, "FT_DS ", 6) == 0) {
+ if (wpa_supplicant_ctrl_iface_ft_ds(wpa_s, buf + 6))
+ reply_len = -1;
+#endif /* CONFIG_IEEE80211R */
+#ifdef CONFIG_WPS
+ } else if (os_strcmp(buf, "WPS_PBC") == 0) {
+ int res = wpa_supplicant_ctrl_iface_wps_pbc(wpa_s, NULL);
+ if (res == -2) {
+ os_memcpy(reply, "FAIL-PBC-OVERLAP\n", 17);
+ reply_len = 17;
+ } else if (res)
+ reply_len = -1;
+ } else if (os_strncmp(buf, "WPS_PBC ", 8) == 0) {
+ int res = wpa_supplicant_ctrl_iface_wps_pbc(wpa_s, buf + 8);
+ if (res == -2) {
+ os_memcpy(reply, "FAIL-PBC-OVERLAP\n", 17);
+ reply_len = 17;
+ } else if (res)
+ reply_len = -1;
+ } else if (os_strncmp(buf, "WPS_PIN ", 8) == 0) {
+ reply_len = wpa_supplicant_ctrl_iface_wps_pin(wpa_s, buf + 8,
+ reply,
+ reply_size);
+ } else if (os_strncmp(buf, "WPS_CHECK_PIN ", 14) == 0) {
+ reply_len = wpa_supplicant_ctrl_iface_wps_check_pin(
+ wpa_s, buf + 14, reply, reply_size);
+ } else if (os_strcmp(buf, "WPS_CANCEL") == 0) {
+ if (wpas_wps_cancel(wpa_s))
+ reply_len = -1;
+#ifdef CONFIG_WPS_NFC
+ } else if (os_strcmp(buf, "WPS_NFC") == 0) {
+ if (wpa_supplicant_ctrl_iface_wps_nfc(wpa_s, NULL))
+ reply_len = -1;
+ } else if (os_strncmp(buf, "WPS_NFC ", 8) == 0) {
+ if (wpa_supplicant_ctrl_iface_wps_nfc(wpa_s, buf + 8))
+ reply_len = -1;
+ } else if (os_strncmp(buf, "WPS_NFC_CONFIG_TOKEN ", 21) == 0) {
+ reply_len = wpa_supplicant_ctrl_iface_wps_nfc_config_token(
+ wpa_s, buf + 21, reply, reply_size);
+ } else if (os_strncmp(buf, "WPS_NFC_TOKEN ", 14) == 0) {
+ reply_len = wpa_supplicant_ctrl_iface_wps_nfc_token(
+ wpa_s, buf + 14, reply, reply_size);
+ } else if (os_strncmp(buf, "WPS_NFC_TAG_READ ", 17) == 0) {
+ if (wpa_supplicant_ctrl_iface_wps_nfc_tag_read(wpa_s,
+ buf + 17))
+ reply_len = -1;
+ } else if (os_strncmp(buf, "NFC_GET_HANDOVER_REQ ", 21) == 0) {
+ reply_len = wpas_ctrl_nfc_get_handover_req(
+ wpa_s, buf + 21, reply, reply_size);
+ } else if (os_strncmp(buf, "NFC_GET_HANDOVER_SEL ", 21) == 0) {
+ reply_len = wpas_ctrl_nfc_get_handover_sel(
+ wpa_s, buf + 21, reply, reply_size);
+ } else if (os_strncmp(buf, "NFC_REPORT_HANDOVER ", 20) == 0) {
+ if (wpas_ctrl_nfc_report_handover(wpa_s, buf + 20))
+ reply_len = -1;
+#endif /* CONFIG_WPS_NFC */
+ } else if (os_strncmp(buf, "WPS_REG ", 8) == 0) {
+ if (wpa_supplicant_ctrl_iface_wps_reg(wpa_s, buf + 8))
+ reply_len = -1;
+#ifdef CONFIG_AP
+ } else if (os_strncmp(buf, "WPS_AP_PIN ", 11) == 0) {
+ reply_len = wpa_supplicant_ctrl_iface_wps_ap_pin(
+ wpa_s, buf + 11, reply, reply_size);
+#endif /* CONFIG_AP */
+#ifdef CONFIG_WPS_ER
+ } else if (os_strcmp(buf, "WPS_ER_START") == 0) {
+ if (wpas_wps_er_start(wpa_s, NULL))
+ reply_len = -1;
+ } else if (os_strncmp(buf, "WPS_ER_START ", 13) == 0) {
+ if (wpas_wps_er_start(wpa_s, buf + 13))
+ reply_len = -1;
+ } else if (os_strcmp(buf, "WPS_ER_STOP") == 0) {
+ wpas_wps_er_stop(wpa_s);
+ } else if (os_strncmp(buf, "WPS_ER_PIN ", 11) == 0) {
+ if (wpa_supplicant_ctrl_iface_wps_er_pin(wpa_s, buf + 11))
+ reply_len = -1;
+ } else if (os_strncmp(buf, "WPS_ER_PBC ", 11) == 0) {
+ int ret = wpas_wps_er_pbc(wpa_s, buf + 11);
+ if (ret == -2) {
+ os_memcpy(reply, "FAIL-PBC-OVERLAP\n", 17);
+ reply_len = 17;
+ } else if (ret == -3) {
+ os_memcpy(reply, "FAIL-UNKNOWN-UUID\n", 18);
+ reply_len = 18;
+ } else if (ret == -4) {
+ os_memcpy(reply, "FAIL-NO-AP-SETTINGS\n", 20);
+ reply_len = 20;
+ } else if (ret)
+ reply_len = -1;
+ } else if (os_strncmp(buf, "WPS_ER_LEARN ", 13) == 0) {
+ if (wpa_supplicant_ctrl_iface_wps_er_learn(wpa_s, buf + 13))
+ reply_len = -1;
+ } else if (os_strncmp(buf, "WPS_ER_SET_CONFIG ", 18) == 0) {
+ if (wpa_supplicant_ctrl_iface_wps_er_set_config(wpa_s,
+ buf + 18))
+ reply_len = -1;
+ } else if (os_strncmp(buf, "WPS_ER_CONFIG ", 14) == 0) {
+ if (wpa_supplicant_ctrl_iface_wps_er_config(wpa_s, buf + 14))
+ reply_len = -1;
+#ifdef CONFIG_WPS_NFC
+ } else if (os_strncmp(buf, "WPS_ER_NFC_CONFIG_TOKEN ", 24) == 0) {
+ reply_len = wpa_supplicant_ctrl_iface_wps_er_nfc_config_token(
+ wpa_s, buf + 24, reply, reply_size);
+#endif /* CONFIG_WPS_NFC */
+#endif /* CONFIG_WPS_ER */
+#endif /* CONFIG_WPS */
+#ifdef CONFIG_IBSS_RSN
+ } else if (os_strncmp(buf, "IBSS_RSN ", 9) == 0) {
+ if (wpa_supplicant_ctrl_iface_ibss_rsn(wpa_s, buf + 9))
+ reply_len = -1;
+#endif /* CONFIG_IBSS_RSN */
+#ifdef CONFIG_MESH
+ } else if (os_strncmp(buf, "MESH_INTERFACE_ADD ", 19) == 0) {
+ reply_len = wpa_supplicant_ctrl_iface_mesh_interface_add(
+ wpa_s, buf + 19, reply, reply_size);
+ } else if (os_strcmp(buf, "MESH_INTERFACE_ADD") == 0) {
+ reply_len = wpa_supplicant_ctrl_iface_mesh_interface_add(
+ wpa_s, "", reply, reply_size);
+ } else if (os_strncmp(buf, "MESH_GROUP_ADD ", 15) == 0) {
+ if (wpa_supplicant_ctrl_iface_mesh_group_add(wpa_s, buf + 15))
+ reply_len = -1;
+ } else if (os_strncmp(buf, "MESH_GROUP_REMOVE ", 18) == 0) {
+ if (wpa_supplicant_ctrl_iface_mesh_group_remove(wpa_s,
+ buf + 18))
+ reply_len = -1;
+#endif /* CONFIG_MESH */
+#ifdef CONFIG_P2P
+ } else if (os_strncmp(buf, "P2P_FIND ", 9) == 0) {
+ if (p2p_ctrl_find(wpa_s, buf + 8))
+ reply_len = -1;
+ } else if (os_strcmp(buf, "P2P_FIND") == 0) {
+ if (p2p_ctrl_find(wpa_s, ""))
+ reply_len = -1;
+ } else if (os_strcmp(buf, "P2P_STOP_FIND") == 0) {
+ wpas_p2p_stop_find(wpa_s);
+ } else if (os_strncmp(buf, "P2P_ASP_PROVISION ", 18) == 0) {
+ if (p2p_ctrl_asp_provision(wpa_s, buf + 18))
+ reply_len = -1;
+ } else if (os_strncmp(buf, "P2P_ASP_PROVISION_RESP ", 23) == 0) {
+ if (p2p_ctrl_asp_provision_resp(wpa_s, buf + 23))
+ reply_len = -1;
+ } else if (os_strncmp(buf, "P2P_CONNECT ", 12) == 0) {
+ reply_len = p2p_ctrl_connect(wpa_s, buf + 12, reply,
+ reply_size);
+ } else if (os_strncmp(buf, "P2P_LISTEN ", 11) == 0) {
+ if (p2p_ctrl_listen(wpa_s, buf + 11))
+ reply_len = -1;
+ } else if (os_strcmp(buf, "P2P_LISTEN") == 0) {
+ if (p2p_ctrl_listen(wpa_s, ""))
+ reply_len = -1;
+ } else if (os_strncmp(buf, "P2P_GROUP_REMOVE ", 17) == 0) {
+ if (wpas_p2p_group_remove(wpa_s, buf + 17))
+ reply_len = -1;
+ } else if (os_strcmp(buf, "P2P_GROUP_ADD") == 0) {
+ if (p2p_ctrl_group_add(wpa_s, ""))
+ reply_len = -1;
+ } else if (os_strncmp(buf, "P2P_GROUP_ADD ", 14) == 0) {
+ if (p2p_ctrl_group_add(wpa_s, buf + 14))
+ reply_len = -1;
+ } else if (os_strncmp(buf, "P2P_PROV_DISC ", 14) == 0) {
+ if (p2p_ctrl_prov_disc(wpa_s, buf + 14))
+ reply_len = -1;
+ } else if (os_strcmp(buf, "P2P_GET_PASSPHRASE") == 0) {
+ reply_len = p2p_get_passphrase(wpa_s, reply, reply_size);
+ } else if (os_strncmp(buf, "P2P_SERV_DISC_REQ ", 18) == 0) {
+ reply_len = p2p_ctrl_serv_disc_req(wpa_s, buf + 18, reply,
+ reply_size);
+ } else if (os_strncmp(buf, "P2P_SERV_DISC_CANCEL_REQ ", 25) == 0) {
+ if (p2p_ctrl_serv_disc_cancel_req(wpa_s, buf + 25) < 0)
+ reply_len = -1;
+ } else if (os_strncmp(buf, "P2P_SERV_DISC_RESP ", 19) == 0) {
+ if (p2p_ctrl_serv_disc_resp(wpa_s, buf + 19) < 0)
+ reply_len = -1;
+ } else if (os_strcmp(buf, "P2P_SERVICE_UPDATE") == 0) {
+ wpas_p2p_sd_service_update(wpa_s);
+ } else if (os_strncmp(buf, "P2P_SERV_DISC_EXTERNAL ", 23) == 0) {
+ if (p2p_ctrl_serv_disc_external(wpa_s, buf + 23) < 0)
+ reply_len = -1;
+ } else if (os_strcmp(buf, "P2P_SERVICE_FLUSH") == 0) {
+ wpas_p2p_service_flush(wpa_s);
+ } else if (os_strncmp(buf, "P2P_SERVICE_ADD ", 16) == 0) {
+ if (p2p_ctrl_service_add(wpa_s, buf + 16) < 0)
+ reply_len = -1;
+ } else if (os_strncmp(buf, "P2P_SERVICE_DEL ", 16) == 0) {
+ if (p2p_ctrl_service_del(wpa_s, buf + 16) < 0)
+ reply_len = -1;
+ } else if (os_strncmp(buf, "P2P_SERVICE_REP ", 16) == 0) {
+ if (p2p_ctrl_service_replace(wpa_s, buf + 16) < 0)
+ reply_len = -1;
+ } else if (os_strncmp(buf, "P2P_REJECT ", 11) == 0) {
+ if (p2p_ctrl_reject(wpa_s, buf + 11) < 0)
+ reply_len = -1;
+ } else if (os_strncmp(buf, "P2P_INVITE ", 11) == 0) {
+ if (p2p_ctrl_invite(wpa_s, buf + 11) < 0)
+ reply_len = -1;
+ } else if (os_strncmp(buf, "P2P_PEER ", 9) == 0) {
+ reply_len = p2p_ctrl_peer(wpa_s, buf + 9, reply,
+ reply_size);
+ } else if (os_strncmp(buf, "P2P_SET ", 8) == 0) {
+ if (p2p_ctrl_set(wpa_s, buf + 8) < 0)
+ reply_len = -1;
+ } else if (os_strcmp(buf, "P2P_FLUSH") == 0) {
+ p2p_ctrl_flush(wpa_s);
+ } else if (os_strncmp(buf, "P2P_UNAUTHORIZE ", 16) == 0) {
+ if (wpas_p2p_unauthorize(wpa_s, buf + 16) < 0)
+ reply_len = -1;
+ } else if (os_strcmp(buf, "P2P_CANCEL") == 0) {
+ if (wpas_p2p_cancel(wpa_s))
+ reply_len = -1;
+ } else if (os_strncmp(buf, "P2P_PRESENCE_REQ ", 17) == 0) {
+ if (p2p_ctrl_presence_req(wpa_s, buf + 17) < 0)
+ reply_len = -1;
+ } else if (os_strcmp(buf, "P2P_PRESENCE_REQ") == 0) {
+ if (p2p_ctrl_presence_req(wpa_s, "") < 0)
+ reply_len = -1;
+ } else if (os_strncmp(buf, "P2P_EXT_LISTEN ", 15) == 0) {
+ if (p2p_ctrl_ext_listen(wpa_s, buf + 15) < 0)
+ reply_len = -1;
+ } else if (os_strcmp(buf, "P2P_EXT_LISTEN") == 0) {
+ if (p2p_ctrl_ext_listen(wpa_s, "") < 0)
+ reply_len = -1;
+ } else if (os_strncmp(buf, "P2P_REMOVE_CLIENT ", 18) == 0) {
+ if (p2p_ctrl_remove_client(wpa_s, buf + 18) < 0)
+ reply_len = -1;
+#endif /* CONFIG_P2P */
+#ifdef CONFIG_WIFI_DISPLAY
+ } else if (os_strncmp(buf, "WFD_SUBELEM_SET ", 16) == 0) {
+ if (wifi_display_subelem_set(wpa_s->global, buf + 16) < 0)
+ reply_len = -1;
+ } else if (os_strncmp(buf, "WFD_SUBELEM_GET ", 16) == 0) {
+ reply_len = wifi_display_subelem_get(wpa_s->global, buf + 16,
+ reply, reply_size);
+#endif /* CONFIG_WIFI_DISPLAY */
+#ifdef CONFIG_INTERWORKING
+ } else if (os_strcmp(buf, "FETCH_ANQP") == 0) {
+ if (interworking_fetch_anqp(wpa_s) < 0)
+ reply_len = -1;
+ } else if (os_strcmp(buf, "STOP_FETCH_ANQP") == 0) {
+ interworking_stop_fetch_anqp(wpa_s);
+ } else if (os_strcmp(buf, "INTERWORKING_SELECT") == 0) {
+ if (ctrl_interworking_select(wpa_s, NULL) < 0)
+ reply_len = -1;
+ } else if (os_strncmp(buf, "INTERWORKING_SELECT ", 20) == 0) {
+ if (ctrl_interworking_select(wpa_s, buf + 20) < 0)
+ reply_len = -1;
+ } else if (os_strncmp(buf, "INTERWORKING_CONNECT ", 21) == 0) {
+ if (ctrl_interworking_connect(wpa_s, buf + 21, 0) < 0)
+ reply_len = -1;
+ } else if (os_strncmp(buf, "INTERWORKING_ADD_NETWORK ", 25) == 0) {
+ int id;
+
+ id = ctrl_interworking_connect(wpa_s, buf + 25, 1);
+ if (id < 0)
+ reply_len = -1;
+ else {
+ reply_len = os_snprintf(reply, reply_size, "%d\n", id);
+ if (os_snprintf_error(reply_size, reply_len))
+ reply_len = -1;
+ }
+ } else if (os_strncmp(buf, "ANQP_GET ", 9) == 0) {
+ if (get_anqp(wpa_s, buf + 9) < 0)
+ reply_len = -1;
+ } else if (os_strncmp(buf, "GAS_REQUEST ", 12) == 0) {
+ if (gas_request(wpa_s, buf + 12) < 0)
+ reply_len = -1;
+ } else if (os_strncmp(buf, "GAS_RESPONSE_GET ", 17) == 0) {
+ reply_len = gas_response_get(wpa_s, buf + 17, reply,
+ reply_size);
+#endif /* CONFIG_INTERWORKING */
+#ifdef CONFIG_HS20
+ } else if (os_strncmp(buf, "HS20_ANQP_GET ", 14) == 0) {
+ if (get_hs20_anqp(wpa_s, buf + 14) < 0)
+ reply_len = -1;
+ } else if (os_strncmp(buf, "HS20_GET_NAI_HOME_REALM_LIST ", 29) == 0) {
+ if (hs20_get_nai_home_realm_list(wpa_s, buf + 29) < 0)
+ reply_len = -1;
+ } else if (os_strncmp(buf, "HS20_ICON_REQUEST ", 18) == 0) {
+ if (hs20_icon_request(wpa_s, buf + 18) < 0)
+ reply_len = -1;
+ } else if (os_strcmp(buf, "FETCH_OSU") == 0) {
+ if (hs20_fetch_osu(wpa_s) < 0)
+ reply_len = -1;
+ } else if (os_strcmp(buf, "CANCEL_FETCH_OSU") == 0) {
+ hs20_cancel_fetch_osu(wpa_s);
+#endif /* CONFIG_HS20 */
+ } else if (os_strncmp(buf, WPA_CTRL_RSP, os_strlen(WPA_CTRL_RSP)) == 0)
+ {
+ if (wpa_supplicant_ctrl_iface_ctrl_rsp(
+ wpa_s, buf + os_strlen(WPA_CTRL_RSP)))
+ reply_len = -1;
+ else {
+ /*
+ * Notify response from timeout to allow the control
+ * interface response to be sent first.
+ */
+ eloop_register_timeout(0, 0, wpas_ctrl_eapol_response,
+ wpa_s, NULL);
+ }
+ } else if (os_strcmp(buf, "RECONFIGURE") == 0) {
+ if (wpa_supplicant_reload_configuration(wpa_s))
+ reply_len = -1;
+ } else if (os_strcmp(buf, "TERMINATE") == 0) {
+ wpa_supplicant_terminate_proc(wpa_s->global);
+ } else if (os_strncmp(buf, "BSSID ", 6) == 0) {
+ if (wpa_supplicant_ctrl_iface_bssid(wpa_s, buf + 6))
+ reply_len = -1;
+ } else if (os_strncmp(buf, "BLACKLIST", 9) == 0) {
+ reply_len = wpa_supplicant_ctrl_iface_blacklist(
+ wpa_s, buf + 9, reply, reply_size);
+ } else if (os_strncmp(buf, "LOG_LEVEL", 9) == 0) {
+ reply_len = wpa_supplicant_ctrl_iface_log_level(
+ wpa_s, buf + 9, reply, reply_size);
+ } else if (os_strncmp(buf, "LIST_NETWORKS ", 14) == 0) {
+ reply_len = wpa_supplicant_ctrl_iface_list_networks(
+ wpa_s, buf + 14, reply, reply_size);
+ } else if (os_strcmp(buf, "LIST_NETWORKS") == 0) {
+ reply_len = wpa_supplicant_ctrl_iface_list_networks(
+ wpa_s, NULL, reply, reply_size);
+ } else if (os_strcmp(buf, "DISCONNECT") == 0) {
+#ifdef CONFIG_SME
+ wpa_s->sme.prev_bssid_set = 0;
+#endif /* CONFIG_SME */
+ wpa_s->reassociate = 0;
+ wpa_s->disconnected = 1;
+ wpa_supplicant_cancel_sched_scan(wpa_s);
+ wpa_supplicant_cancel_scan(wpa_s);
+ wpa_supplicant_deauthenticate(wpa_s,
+ WLAN_REASON_DEAUTH_LEAVING);
+ eloop_cancel_timeout(wpas_network_reenabled, wpa_s, NULL);
+ } else if (os_strcmp(buf, "SCAN") == 0) {
+ wpas_ctrl_scan(wpa_s, NULL, reply, reply_size, &reply_len);
+ } else if (os_strncmp(buf, "SCAN ", 5) == 0) {
+ wpas_ctrl_scan(wpa_s, buf + 5, reply, reply_size, &reply_len);
+ } else if (os_strcmp(buf, "SCAN_RESULTS") == 0) {
+ reply_len = wpa_supplicant_ctrl_iface_scan_results(
+ wpa_s, reply, reply_size);
+ } else if (os_strncmp(buf, "SELECT_NETWORK ", 15) == 0) {
+ if (wpa_supplicant_ctrl_iface_select_network(wpa_s, buf + 15))
+ reply_len = -1;
+ } else if (os_strncmp(buf, "ENABLE_NETWORK ", 15) == 0) {
+ if (wpa_supplicant_ctrl_iface_enable_network(wpa_s, buf + 15))
+ reply_len = -1;
+ } else if (os_strncmp(buf, "DISABLE_NETWORK ", 16) == 0) {
+ if (wpa_supplicant_ctrl_iface_disable_network(wpa_s, buf + 16))
+ reply_len = -1;
+ } else if (os_strcmp(buf, "ADD_NETWORK") == 0) {
+ reply_len = wpa_supplicant_ctrl_iface_add_network(
+ wpa_s, reply, reply_size);
+ } else if (os_strncmp(buf, "REMOVE_NETWORK ", 15) == 0) {
+ if (wpa_supplicant_ctrl_iface_remove_network(wpa_s, buf + 15))
+ reply_len = -1;
+ } else if (os_strncmp(buf, "SET_NETWORK ", 12) == 0) {
+ if (wpa_supplicant_ctrl_iface_set_network(wpa_s, buf + 12))
+ reply_len = -1;
+ } else if (os_strncmp(buf, "GET_NETWORK ", 12) == 0) {
+ reply_len = wpa_supplicant_ctrl_iface_get_network(
+ wpa_s, buf + 12, reply, reply_size);
+ } else if (os_strncmp(buf, "DUP_NETWORK ", 12) == 0) {
+ if (wpa_supplicant_ctrl_iface_dup_network(wpa_s, buf + 12,
+ wpa_s))
+ reply_len = -1;
+ } else if (os_strcmp(buf, "LIST_CREDS") == 0) {
+ reply_len = wpa_supplicant_ctrl_iface_list_creds(
+ wpa_s, reply, reply_size);
+ } else if (os_strcmp(buf, "ADD_CRED") == 0) {
+ reply_len = wpa_supplicant_ctrl_iface_add_cred(
+ wpa_s, reply, reply_size);
+ } else if (os_strncmp(buf, "REMOVE_CRED ", 12) == 0) {
+ if (wpa_supplicant_ctrl_iface_remove_cred(wpa_s, buf + 12))
+ reply_len = -1;
+ } else if (os_strncmp(buf, "SET_CRED ", 9) == 0) {
+ if (wpa_supplicant_ctrl_iface_set_cred(wpa_s, buf + 9))
+ reply_len = -1;
+ } else if (os_strncmp(buf, "GET_CRED ", 9) == 0) {
+ reply_len = wpa_supplicant_ctrl_iface_get_cred(wpa_s, buf + 9,
+ reply,
+ reply_size);
+#ifndef CONFIG_NO_CONFIG_WRITE
+ } else if (os_strcmp(buf, "SAVE_CONFIG") == 0) {
+ if (wpa_supplicant_ctrl_iface_save_config(wpa_s))
+ reply_len = -1;
+#endif /* CONFIG_NO_CONFIG_WRITE */
+ } else if (os_strncmp(buf, "GET_CAPABILITY ", 15) == 0) {
+ reply_len = wpa_supplicant_ctrl_iface_get_capability(
+ wpa_s, buf + 15, reply, reply_size);
+ } else if (os_strncmp(buf, "AP_SCAN ", 8) == 0) {
+ if (wpa_supplicant_ctrl_iface_ap_scan(wpa_s, buf + 8))
+ reply_len = -1;
+ } else if (os_strncmp(buf, "SCAN_INTERVAL ", 14) == 0) {
+ if (wpa_supplicant_ctrl_iface_scan_interval(wpa_s, buf + 14))
+ reply_len = -1;
+ } else if (os_strcmp(buf, "INTERFACE_LIST") == 0) {
+ reply_len = wpa_supplicant_global_iface_list(
+ wpa_s->global, reply, reply_size);
+ } else if (os_strcmp(buf, "INTERFACES") == 0) {
+ reply_len = wpa_supplicant_global_iface_interfaces(
+ wpa_s->global, reply, reply_size);
+ } else if (os_strncmp(buf, "BSS ", 4) == 0) {
+ reply_len = wpa_supplicant_ctrl_iface_bss(
+ wpa_s, buf + 4, reply, reply_size);
+#ifdef CONFIG_AP
+ } else if (os_strcmp(buf, "STA-FIRST") == 0) {
+ reply_len = ap_ctrl_iface_sta_first(wpa_s, reply, reply_size);
+ } else if (os_strncmp(buf, "STA ", 4) == 0) {
+ reply_len = ap_ctrl_iface_sta(wpa_s, buf + 4, reply,
+ reply_size);
+ } else if (os_strncmp(buf, "STA-NEXT ", 9) == 0) {
+ reply_len = ap_ctrl_iface_sta_next(wpa_s, buf + 9, reply,
+ reply_size);
+ } else if (os_strncmp(buf, "DEAUTHENTICATE ", 15) == 0) {
+ if (ap_ctrl_iface_sta_deauthenticate(wpa_s, buf + 15))
+ reply_len = -1;
+ } else if (os_strncmp(buf, "DISASSOCIATE ", 13) == 0) {
+ if (ap_ctrl_iface_sta_disassociate(wpa_s, buf + 13))
+ reply_len = -1;
+ } else if (os_strncmp(buf, "CHAN_SWITCH ", 12) == 0) {
+ if (ap_ctrl_iface_chanswitch(wpa_s, buf + 12))
+ reply_len = -1;
+ } else if (os_strcmp(buf, "STOP_AP") == 0) {
+ if (wpas_ap_stop_ap(wpa_s))
+ reply_len = -1;
+#endif /* CONFIG_AP */
+ } else if (os_strcmp(buf, "SUSPEND") == 0) {
+ wpas_notify_suspend(wpa_s->global);
+ } else if (os_strcmp(buf, "RESUME") == 0) {
+ wpas_notify_resume(wpa_s->global);
+#ifdef CONFIG_TESTING_OPTIONS
+ } else if (os_strcmp(buf, "DROP_SA") == 0) {
+ wpa_supplicant_ctrl_iface_drop_sa(wpa_s);
+#endif /* CONFIG_TESTING_OPTIONS */
+ } else if (os_strncmp(buf, "ROAM ", 5) == 0) {
+ if (wpa_supplicant_ctrl_iface_roam(wpa_s, buf + 5))
+ reply_len = -1;
+ } else if (os_strncmp(buf, "STA_AUTOCONNECT ", 16) == 0) {
+ wpa_s->auto_reconnect_disabled = atoi(buf + 16) == 0;
+ } else if (os_strncmp(buf, "BSS_EXPIRE_AGE ", 15) == 0) {
+ if (wpa_supplicant_ctrl_iface_bss_expire_age(wpa_s, buf + 15))
+ reply_len = -1;
+ } else if (os_strncmp(buf, "BSS_EXPIRE_COUNT ", 17) == 0) {
+ if (wpa_supplicant_ctrl_iface_bss_expire_count(wpa_s,
+ buf + 17))
+ reply_len = -1;
+ } else if (os_strncmp(buf, "BSS_FLUSH ", 10) == 0) {
+ wpa_supplicant_ctrl_iface_bss_flush(wpa_s, buf + 10);
+#ifdef CONFIG_TDLS
+ } else if (os_strncmp(buf, "TDLS_DISCOVER ", 14) == 0) {
+ if (wpa_supplicant_ctrl_iface_tdls_discover(wpa_s, buf + 14))
+ reply_len = -1;
+ } else if (os_strncmp(buf, "TDLS_SETUP ", 11) == 0) {
+ if (wpa_supplicant_ctrl_iface_tdls_setup(wpa_s, buf + 11))
+ reply_len = -1;
+ } else if (os_strncmp(buf, "TDLS_TEARDOWN ", 14) == 0) {
+ if (wpa_supplicant_ctrl_iface_tdls_teardown(wpa_s, buf + 14))
+ reply_len = -1;
+ } else if (os_strncmp(buf, "TDLS_CHAN_SWITCH ", 17) == 0) {
+ if (wpa_supplicant_ctrl_iface_tdls_chan_switch(wpa_s,
+ buf + 17))
+ reply_len = -1;
+ } else if (os_strncmp(buf, "TDLS_CANCEL_CHAN_SWITCH ", 24) == 0) {
+ if (wpa_supplicant_ctrl_iface_tdls_cancel_chan_switch(wpa_s,
+ buf + 24))
+ reply_len = -1;
+ } else if (os_strncmp(buf, "TDLS_LINK_STATUS ", 17) == 0) {
+ reply_len = wpa_supplicant_ctrl_iface_tdls_link_status(
+ wpa_s, buf + 17, reply, reply_size);
+#endif /* CONFIG_TDLS */
+ } else if (os_strcmp(buf, "WMM_AC_STATUS") == 0) {
+ reply_len = wpas_wmm_ac_status(wpa_s, reply, reply_size);
+ } else if (os_strncmp(buf, "WMM_AC_ADDTS ", 13) == 0) {
+ if (wmm_ac_ctrl_addts(wpa_s, buf + 13))
+ reply_len = -1;
+ } else if (os_strncmp(buf, "WMM_AC_DELTS ", 13) == 0) {
+ if (wmm_ac_ctrl_delts(wpa_s, buf + 13))
+ reply_len = -1;
+ } else if (os_strncmp(buf, "SIGNAL_POLL", 11) == 0) {
+ reply_len = wpa_supplicant_signal_poll(wpa_s, reply,
+ reply_size);
+ } else if (os_strncmp(buf, "PKTCNT_POLL", 11) == 0) {
+ reply_len = wpa_supplicant_pktcnt_poll(wpa_s, reply,
+ reply_size);
+#ifdef CONFIG_AUTOSCAN
+ } else if (os_strncmp(buf, "AUTOSCAN ", 9) == 0) {
+ if (wpa_supplicant_ctrl_iface_autoscan(wpa_s, buf + 9))
+ reply_len = -1;
+#endif /* CONFIG_AUTOSCAN */
+#ifdef ANDROID
+ } else if (os_strncmp(buf, "DRIVER ", 7) == 0) {
+ reply_len = wpa_supplicant_driver_cmd(wpa_s, buf + 7, reply,
+ reply_size);
+#endif /* ANDROID */
+ } else if (os_strncmp(buf, "VENDOR ", 7) == 0) {
+ reply_len = wpa_supplicant_vendor_cmd(wpa_s, buf + 7, reply,
+ reply_size);
+ } else if (os_strcmp(buf, "REAUTHENTICATE") == 0) {
+ pmksa_cache_clear_current(wpa_s->wpa);
+ eapol_sm_request_reauth(wpa_s->eapol);
+#ifdef CONFIG_WNM
+ } else if (os_strncmp(buf, "WNM_SLEEP ", 10) == 0) {
+ if (wpas_ctrl_iface_wnm_sleep(wpa_s, buf + 10))
+ reply_len = -1;
+ } else if (os_strncmp(buf, "WNM_BSS_QUERY ", 14) == 0) {
+ if (wpas_ctrl_iface_wnm_bss_query(wpa_s, buf + 14))
+ reply_len = -1;
+#endif /* CONFIG_WNM */
+ } else if (os_strcmp(buf, "FLUSH") == 0) {
+ wpa_supplicant_ctrl_iface_flush(wpa_s);
+ } else if (os_strncmp(buf, "RADIO_WORK ", 11) == 0) {
+ reply_len = wpas_ctrl_radio_work(wpa_s, buf + 11, reply,
+ reply_size);
+#ifdef CONFIG_TESTING_OPTIONS
+ } else if (os_strncmp(buf, "MGMT_TX ", 8) == 0) {
+ if (wpas_ctrl_iface_mgmt_tx(wpa_s, buf + 8) < 0)
+ reply_len = -1;
+ } else if (os_strcmp(buf, "MGMT_TX_DONE") == 0) {
+ wpas_ctrl_iface_mgmt_tx_done(wpa_s);
+ } else if (os_strncmp(buf, "DRIVER_EVENT ", 13) == 0) {
+ if (wpas_ctrl_iface_driver_event(wpa_s, buf + 13) < 0)
+ reply_len = -1;
+ } else if (os_strncmp(buf, "EAPOL_RX ", 9) == 0) {
+ if (wpas_ctrl_iface_eapol_rx(wpa_s, buf + 9) < 0)
+ reply_len = -1;
+ } else if (os_strncmp(buf, "DATA_TEST_CONFIG ", 17) == 0) {
+ if (wpas_ctrl_iface_data_test_config(wpa_s, buf + 17) < 0)
+ reply_len = -1;
+ } else if (os_strncmp(buf, "DATA_TEST_TX ", 13) == 0) {
+ if (wpas_ctrl_iface_data_test_tx(wpa_s, buf + 13) < 0)
+ reply_len = -1;
+ } else if (os_strncmp(buf, "DATA_TEST_FRAME ", 16) == 0) {
+ if (wpas_ctrl_iface_data_test_frame(wpa_s, buf + 16) < 0)
+ reply_len = -1;
+ } else if (os_strncmp(buf, "TEST_ALLOC_FAIL ", 16) == 0) {
+ if (wpas_ctrl_test_alloc_fail(wpa_s, buf + 16) < 0)
+ reply_len = -1;
+ } else if (os_strcmp(buf, "GET_ALLOC_FAIL") == 0) {
+ reply_len = wpas_ctrl_get_alloc_fail(wpa_s, reply, reply_size);
+ } else if (os_strncmp(buf, "TEST_FAIL ", 10) == 0) {
+ if (wpas_ctrl_test_fail(wpa_s, buf + 10) < 0)
+ reply_len = -1;
+ } else if (os_strcmp(buf, "GET_FAIL") == 0) {
+ reply_len = wpas_ctrl_get_fail(wpa_s, reply, reply_size);
+#endif /* CONFIG_TESTING_OPTIONS */
+ } else if (os_strncmp(buf, "VENDOR_ELEM_ADD ", 16) == 0) {
+ if (wpas_ctrl_vendor_elem_add(wpa_s, buf + 16) < 0)
+ reply_len = -1;
+ } else if (os_strncmp(buf, "VENDOR_ELEM_GET ", 16) == 0) {
+ reply_len = wpas_ctrl_vendor_elem_get(wpa_s, buf + 16, reply,
+ reply_size);
+ } else if (os_strncmp(buf, "VENDOR_ELEM_REMOVE ", 19) == 0) {
+ if (wpas_ctrl_vendor_elem_remove(wpa_s, buf + 19) < 0)
+ reply_len = -1;
+ } else if (os_strncmp(buf, "NEIGHBOR_REP_REQUEST", 20) == 0) {
+ if (wpas_ctrl_iface_send_neigbor_rep(wpa_s, buf + 20))
+ reply_len = -1;
+ } else if (os_strcmp(buf, "ERP_FLUSH") == 0) {
+ wpas_ctrl_iface_erp_flush(wpa_s);
+ } else if (os_strncmp(buf, "MAC_RAND_SCAN ", 14) == 0) {
+ if (wpas_ctrl_iface_mac_rand_scan(wpa_s, buf + 14))
+ reply_len = -1;
+ } else if (os_strncmp(buf, "GET_PREF_FREQ_LIST ", 19) == 0) {
+ reply_len = wpas_ctrl_iface_get_pref_freq_list(
+ wpa_s, buf + 19, reply, reply_size);
+ } else {
+ os_memcpy(reply, "UNKNOWN COMMAND\n", 16);
+ reply_len = 16;
+ }
+
+ if (reply_len < 0) {
+ os_memcpy(reply, "FAIL\n", 5);
+ reply_len = 5;
+ }
+
+ *resp_len = reply_len;
+ return reply;
+}
+
+
+static int wpa_supplicant_global_iface_add(struct wpa_global *global,
+ char *cmd)
+{
+ struct wpa_interface iface;
+ char *pos, *extra;
+ struct wpa_supplicant *wpa_s;
+ unsigned int create_iface = 0;
+ u8 mac_addr[ETH_ALEN];
+
+ /*
+ * <ifname>TAB<confname>TAB<driver>TAB<ctrl_interface>TAB<driver_param>
+ * TAB<bridge_ifname>[TAB<create>]
+ */
+ wpa_printf(MSG_DEBUG, "CTRL_IFACE GLOBAL INTERFACE_ADD '%s'", cmd);
+
+ os_memset(&iface, 0, sizeof(iface));
+
+ do {
+ iface.ifname = pos = cmd;
+ pos = os_strchr(pos, '\t');
+ if (pos)
+ *pos++ = '\0';
+ if (iface.ifname[0] == '\0')
+ return -1;
+ if (pos == NULL)
+ break;
+
+ iface.confname = pos;
+ pos = os_strchr(pos, '\t');
+ if (pos)
+ *pos++ = '\0';
+ if (iface.confname[0] == '\0')
+ iface.confname = NULL;
+ if (pos == NULL)
+ break;
+
+ iface.driver = pos;
+ pos = os_strchr(pos, '\t');
+ if (pos)
+ *pos++ = '\0';
+ if (iface.driver[0] == '\0')
+ iface.driver = NULL;
+ if (pos == NULL)
+ break;
+
+ iface.ctrl_interface = pos;
+ pos = os_strchr(pos, '\t');
+ if (pos)
+ *pos++ = '\0';
+ if (iface.ctrl_interface[0] == '\0')
+ iface.ctrl_interface = NULL;
+ if (pos == NULL)
+ break;
+
+ iface.driver_param = pos;
+ pos = os_strchr(pos, '\t');
+ if (pos)
+ *pos++ = '\0';
+ if (iface.driver_param[0] == '\0')
+ iface.driver_param = NULL;
+ if (pos == NULL)
+ break;
+
+ iface.bridge_ifname = pos;
+ pos = os_strchr(pos, '\t');
+ if (pos)
+ *pos++ = '\0';
+ if (iface.bridge_ifname[0] == '\0')
+ iface.bridge_ifname = NULL;
+ if (pos == NULL)
+ break;
+
+ extra = pos;
+ pos = os_strchr(pos, '\t');
+ if (pos)
+ *pos++ = '\0';
+ if (!extra[0])
+ break;
+
+ if (os_strcmp(extra, "create") == 0)
+ create_iface = 1;
+ else {
+ wpa_printf(MSG_DEBUG,
+ "INTERFACE_ADD unsupported extra parameter: '%s'",
+ extra);
+ return -1;
+ }
+ } while (0);
+
+ if (create_iface) {
+ wpa_printf(MSG_DEBUG, "CTRL_IFACE creating interface '%s'",
+ iface.ifname);
+ if (!global->ifaces)
+ return -1;
+ if (wpa_drv_if_add(global->ifaces, WPA_IF_STATION, iface.ifname,
+ NULL, NULL, NULL, mac_addr, NULL) < 0) {
+ wpa_printf(MSG_ERROR,
+ "CTRL_IFACE interface creation failed");
+ return -1;
+ }
+
+ wpa_printf(MSG_DEBUG,
+ "CTRL_IFACE interface '%s' created with MAC addr: "
+ MACSTR, iface.ifname, MAC2STR(mac_addr));
+ }
+
+ if (wpa_supplicant_get_iface(global, iface.ifname))
+ goto fail;
+
+ wpa_s = wpa_supplicant_add_iface(global, &iface, NULL);
+ if (!wpa_s)
+ goto fail;
+ wpa_s->added_vif = create_iface;
+ return 0;
+
+fail:
+ if (create_iface)
+ wpa_drv_if_remove(global->ifaces, WPA_IF_STATION, iface.ifname);
+ return -1;
+}
+
+
+static int wpa_supplicant_global_iface_remove(struct wpa_global *global,
+ char *cmd)
+{
+ struct wpa_supplicant *wpa_s;
+ int ret;
+ unsigned int delete_iface;
+
+ wpa_printf(MSG_DEBUG, "CTRL_IFACE GLOBAL INTERFACE_REMOVE '%s'", cmd);
+
+ wpa_s = wpa_supplicant_get_iface(global, cmd);
+ if (wpa_s == NULL)
+ return -1;
+ delete_iface = wpa_s->added_vif;
+ ret = wpa_supplicant_remove_iface(global, wpa_s, 0);
+ if (!ret && delete_iface) {
+ wpa_printf(MSG_DEBUG, "CTRL_IFACE deleting the interface '%s'",
+ cmd);
+ ret = wpa_drv_if_remove(global->ifaces, WPA_IF_STATION, cmd);
+ }
+ return ret;
+}
+
+
+static void wpa_free_iface_info(struct wpa_interface_info *iface)
+{
+ struct wpa_interface_info *prev;
+
+ while (iface) {
+ prev = iface;
+ iface = iface->next;
+
+ os_free(prev->ifname);
+ os_free(prev->desc);
+ os_free(prev);
+ }
+}
+
+
+static int wpa_supplicant_global_iface_list(struct wpa_global *global,
+ char *buf, int len)
+{
+ int i, res;
+ struct wpa_interface_info *iface = NULL, *last = NULL, *tmp;
+ char *pos, *end;
+
+ for (i = 0; wpa_drivers[i]; i++) {
+ const struct wpa_driver_ops *drv = wpa_drivers[i];
+ if (drv->get_interfaces == NULL)
+ continue;
+ tmp = drv->get_interfaces(global->drv_priv[i]);
+ if (tmp == NULL)
+ continue;
+
+ if (last == NULL)
+ iface = last = tmp;
+ else
+ last->next = tmp;
+ while (last->next)
+ last = last->next;
+ }
+
+ pos = buf;
+ end = buf + len;
+ for (tmp = iface; tmp; tmp = tmp->next) {
+ res = os_snprintf(pos, end - pos, "%s\t%s\t%s\n",
+ tmp->drv_name, tmp->ifname,
+ tmp->desc ? tmp->desc : "");
+ if (os_snprintf_error(end - pos, res)) {
+ *pos = '\0';
+ break;
+ }
+ pos += res;
+ }
+
+ wpa_free_iface_info(iface);
+
+ return pos - buf;
+}
+
+
+static int wpa_supplicant_global_iface_interfaces(struct wpa_global *global,
+ char *buf, int len)
+{
+ int res;
+ char *pos, *end;
+ struct wpa_supplicant *wpa_s;
+
+ wpa_s = global->ifaces;
+ pos = buf;
+ end = buf + len;
+
+ while (wpa_s) {
+ res = os_snprintf(pos, end - pos, "%s\n", wpa_s->ifname);
+ if (os_snprintf_error(end - pos, res)) {
+ *pos = '\0';
+ break;
+ }
+ pos += res;
+ wpa_s = wpa_s->next;
+ }
+ return pos - buf;
+}
+
+
+static char * wpas_global_ctrl_iface_ifname(struct wpa_global *global,
+ const char *ifname,
+ char *cmd, size_t *resp_len)
+{
+ struct wpa_supplicant *wpa_s;
+
+ for (wpa_s = global->ifaces; wpa_s; wpa_s = wpa_s->next) {
+ if (os_strcmp(ifname, wpa_s->ifname) == 0)
+ break;
+ }
+
+ if (wpa_s == NULL) {
+ char *resp = os_strdup("FAIL-NO-IFNAME-MATCH\n");
+ if (resp)
+ *resp_len = os_strlen(resp);
+ else
+ *resp_len = 1;
+ return resp;
+ }
+
+ return wpa_supplicant_ctrl_iface_process(wpa_s, cmd, resp_len);
+}
+
+
+static char * wpas_global_ctrl_iface_redir_p2p(struct wpa_global *global,
+ char *buf, size_t *resp_len)
+{
+#ifdef CONFIG_P2P
+ static const char * cmd[] = {
+ "LIST_NETWORKS",
+ "P2P_FIND",
+ "P2P_STOP_FIND",
+ "P2P_LISTEN",
+ "P2P_GROUP_ADD",
+ "P2P_GET_PASSPHRASE",
+ "P2P_SERVICE_UPDATE",
+ "P2P_SERVICE_FLUSH",
+ "P2P_FLUSH",
+ "P2P_CANCEL",
+ "P2P_PRESENCE_REQ",
+ "P2P_EXT_LISTEN",
+ NULL
+ };
+ static const char * prefix[] = {
+#ifdef ANDROID
+ "DRIVER ",
+#endif /* ANDROID */
+ "GET_NETWORK ",
+ "REMOVE_NETWORK ",
+ "P2P_FIND ",
+ "P2P_CONNECT ",
+ "P2P_LISTEN ",
+ "P2P_GROUP_REMOVE ",
+ "P2P_GROUP_ADD ",
+ "P2P_PROV_DISC ",
+ "P2P_SERV_DISC_REQ ",
+ "P2P_SERV_DISC_CANCEL_REQ ",
+ "P2P_SERV_DISC_RESP ",
+ "P2P_SERV_DISC_EXTERNAL ",
+ "P2P_SERVICE_ADD ",
+ "P2P_SERVICE_DEL ",
+ "P2P_SERVICE_REP ",
+ "P2P_REJECT ",
+ "P2P_INVITE ",
+ "P2P_PEER ",
+ "P2P_SET ",
+ "P2P_UNAUTHORIZE ",
+ "P2P_PRESENCE_REQ ",
+ "P2P_EXT_LISTEN ",
+ "P2P_REMOVE_CLIENT ",
+ "WPS_NFC_TOKEN ",
+ "WPS_NFC_TAG_READ ",
+ "NFC_GET_HANDOVER_SEL ",
+ "NFC_GET_HANDOVER_REQ ",
+ "NFC_REPORT_HANDOVER ",
+ "P2P_ASP_PROVISION ",
+ "P2P_ASP_PROVISION_RESP ",
+ NULL
+ };
+ int found = 0;
+ int i;
+
+ if (global->p2p_init_wpa_s == NULL)
+ return NULL;
+
+ for (i = 0; !found && cmd[i]; i++) {
+ if (os_strcmp(buf, cmd[i]) == 0)
+ found = 1;
+ }
+
+ for (i = 0; !found && prefix[i]; i++) {
+ if (os_strncmp(buf, prefix[i], os_strlen(prefix[i])) == 0)
+ found = 1;
+ }
+
+ if (found)
+ return wpa_supplicant_ctrl_iface_process(global->p2p_init_wpa_s,
+ buf, resp_len);
+#endif /* CONFIG_P2P */
+ return NULL;
+}
+
+
+static char * wpas_global_ctrl_iface_redir_wfd(struct wpa_global *global,
+ char *buf, size_t *resp_len)
+{
+#ifdef CONFIG_WIFI_DISPLAY
+ if (global->p2p_init_wpa_s == NULL)
+ return NULL;
+ if (os_strncmp(buf, "WFD_SUBELEM_SET ", 16) == 0 ||
+ os_strncmp(buf, "WFD_SUBELEM_GET ", 16) == 0)
+ return wpa_supplicant_ctrl_iface_process(global->p2p_init_wpa_s,
+ buf, resp_len);
+#endif /* CONFIG_WIFI_DISPLAY */
+ return NULL;
+}
+
+
+static char * wpas_global_ctrl_iface_redir(struct wpa_global *global,
+ char *buf, size_t *resp_len)
+{
+ char *ret;
+
+ ret = wpas_global_ctrl_iface_redir_p2p(global, buf, resp_len);
+ if (ret)
+ return ret;
+
+ ret = wpas_global_ctrl_iface_redir_wfd(global, buf, resp_len);
+ if (ret)
+ return ret;
+
+ return NULL;
+}
+
+
+static int wpas_global_ctrl_iface_set(struct wpa_global *global, char *cmd)
+{
+ char *value;
+
+ value = os_strchr(cmd, ' ');
+ if (value == NULL)
+ return -1;
+ *value++ = '\0';
+
+ wpa_printf(MSG_DEBUG, "GLOBAL_CTRL_IFACE SET '%s'='%s'", cmd, value);
+
+#ifdef CONFIG_WIFI_DISPLAY
+ if (os_strcasecmp(cmd, "wifi_display") == 0) {
+ wifi_display_enable(global, !!atoi(value));
+ return 0;
+ }
+#endif /* CONFIG_WIFI_DISPLAY */
+
+ /* Restore cmd to its original value to allow redirection */
+ value[-1] = ' ';
+
+ return -1;
+}
+
+
+static int wpas_global_ctrl_iface_dup_network(struct wpa_global *global,
+ char *cmd)
+{
+ struct wpa_supplicant *wpa_s[2]; /* src, dst */
+ char *p;
+ unsigned int i;
+
+ /* cmd: "<src ifname> <dst ifname> <src network id> <dst network id>
+ * <variable name> */
+
+ for (i = 0; i < ARRAY_SIZE(wpa_s) ; i++) {
+ p = os_strchr(cmd, ' ');
+ if (p == NULL)
+ return -1;
+ *p = '\0';
+
+ wpa_s[i] = global->ifaces;
+ for (; wpa_s[i]; wpa_s[i] = wpa_s[i]->next) {
+ if (os_strcmp(cmd, wpa_s[i]->ifname) == 0)
+ break;
+ }
+
+ if (!wpa_s[i]) {
+ wpa_printf(MSG_DEBUG,
+ "CTRL_IFACE: Could not find iface=%s", cmd);
+ return -1;
+ }
+
+ cmd = p + 1;
+ }
+
+ return wpa_supplicant_ctrl_iface_dup_network(wpa_s[0], cmd, wpa_s[1]);
+}
+
+
+#ifndef CONFIG_NO_CONFIG_WRITE
+static int wpas_global_ctrl_iface_save_config(struct wpa_global *global)
+{
+ int ret = 0, saved = 0;
+ struct wpa_supplicant *wpa_s;
+
+ for (wpa_s = global->ifaces; wpa_s; wpa_s = wpa_s->next) {
+ if (!wpa_s->conf->update_config) {
+ wpa_dbg(wpa_s, MSG_DEBUG, "CTRL_IFACE: SAVE_CONFIG - Not allowed to update configuration (update_config=0)");
+ continue;
+ }
+
+ if (wpa_config_write(wpa_s->confname, wpa_s->conf)) {
+ wpa_dbg(wpa_s, MSG_DEBUG, "CTRL_IFACE: SAVE_CONFIG - Failed to update configuration");
+ ret = 1;
+ } else {
+ wpa_dbg(wpa_s, MSG_DEBUG, "CTRL_IFACE: SAVE_CONFIG - Configuration updated");
+ saved++;
+ }
+ }
+
+ if (!saved && !ret) {
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ "CTRL_IFACE: SAVE_CONFIG - No configuration files could be updated");
+ ret = 1;
+ }
+
+ return ret;
+}
+#endif /* CONFIG_NO_CONFIG_WRITE */
+
+
+static int wpas_global_ctrl_iface_status(struct wpa_global *global,
+ char *buf, size_t buflen)
+{
+ char *pos, *end;
+ int ret;
+ struct wpa_supplicant *wpa_s;
+
+ pos = buf;
+ end = buf + buflen;
+
+#ifdef CONFIG_P2P
+ if (global->p2p && !global->p2p_disabled) {
+ ret = os_snprintf(pos, end - pos, "p2p_device_address=" MACSTR
+ "\n"
+ "p2p_state=%s\n",
+ MAC2STR(global->p2p_dev_addr),
+ p2p_get_state_txt(global->p2p));
+ if (os_snprintf_error(end - pos, ret))
+ return pos - buf;
+ pos += ret;
+ } else if (global->p2p) {
+ ret = os_snprintf(pos, end - pos, "p2p_state=DISABLED\n");
+ if (os_snprintf_error(end - pos, ret))
+ return pos - buf;
+ pos += ret;
+ }
+#endif /* CONFIG_P2P */
+
+#ifdef CONFIG_WIFI_DISPLAY
+ ret = os_snprintf(pos, end - pos, "wifi_display=%d\n",
+ !!global->wifi_display);
+ if (os_snprintf_error(end - pos, ret))
+ return pos - buf;
+ pos += ret;
+#endif /* CONFIG_WIFI_DISPLAY */
+
+ for (wpa_s = global->ifaces; wpa_s; wpa_s = wpa_s->next) {
+ ret = os_snprintf(pos, end - pos, "ifname=%s\n"
+ "address=" MACSTR "\n",
+ wpa_s->ifname, MAC2STR(wpa_s->own_addr));
+ if (os_snprintf_error(end - pos, ret))
+ return pos - buf;
+ pos += ret;
+ }
+
+ return pos - buf;
+}
+
+
+#ifdef CONFIG_FST
+
+static int wpas_global_ctrl_iface_fst_attach(struct wpa_global *global,
+ char *cmd, char *buf,
+ size_t reply_size)
+{
+ char ifname[IFNAMSIZ + 1];
+ struct fst_iface_cfg cfg;
+ struct wpa_supplicant *wpa_s;
+ struct fst_wpa_obj iface_obj;
+
+ if (!fst_parse_attach_command(cmd, ifname, sizeof(ifname), &cfg)) {
+ wpa_s = wpa_supplicant_get_iface(global, ifname);
+ if (wpa_s) {
+ if (wpa_s->fst) {
+ wpa_printf(MSG_INFO, "FST: Already attached");
+ return -1;
+ }
+ fst_wpa_supplicant_fill_iface_obj(wpa_s, &iface_obj);
+ wpa_s->fst = fst_attach(ifname, wpa_s->own_addr,
+ &iface_obj, &cfg);
+ if (wpa_s->fst)
+ return os_snprintf(buf, reply_size, "OK\n");
+ }
+ }
+
+ return -1;
+}
+
+
+static int wpas_global_ctrl_iface_fst_detach(struct wpa_global *global,
+ char *cmd, char *buf,
+ size_t reply_size)
+{
+ char ifname[IFNAMSIZ + 1];
+ struct wpa_supplicant *wpa_s;
+
+ if (!fst_parse_detach_command(cmd, ifname, sizeof(ifname))) {
+ wpa_s = wpa_supplicant_get_iface(global, ifname);
+ if (wpa_s) {
+ if (!fst_iface_detach(ifname)) {
+ wpa_s->fst = NULL;
+ return os_snprintf(buf, reply_size, "OK\n");
+ }
+ }
+ }
+
+ return -1;
+}
+
+#endif /* CONFIG_FST */
+
+
+char * wpa_supplicant_global_ctrl_iface_process(struct wpa_global *global,
+ char *buf, size_t *resp_len)
+{
+ char *reply;
+ const int reply_size = 2048;
+ int reply_len;
+ int level = MSG_DEBUG;
+
+ if (os_strncmp(buf, "IFNAME=", 7) == 0) {
+ char *pos = os_strchr(buf + 7, ' ');
+ if (pos) {
+ *pos++ = '\0';
+ return wpas_global_ctrl_iface_ifname(global,
+ buf + 7, pos,
+ resp_len);
+ }
+ }
+
+ reply = wpas_global_ctrl_iface_redir(global, buf, resp_len);
+ if (reply)
+ return reply;
+
+ if (os_strcmp(buf, "PING") == 0)
+ level = MSG_EXCESSIVE;
+ wpa_hexdump_ascii(level, "RX global ctrl_iface",
+ (const u8 *) buf, os_strlen(buf));
+
+ reply = os_malloc(reply_size);
+ if (reply == NULL) {
+ *resp_len = 1;
+ return NULL;
+ }
+
+ os_memcpy(reply, "OK\n", 3);
+ reply_len = 3;
+
+ if (os_strcmp(buf, "PING") == 0) {
+ os_memcpy(reply, "PONG\n", 5);
+ reply_len = 5;
+ } else if (os_strncmp(buf, "INTERFACE_ADD ", 14) == 0) {
+ if (wpa_supplicant_global_iface_add(global, buf + 14))
+ reply_len = -1;
+ } else if (os_strncmp(buf, "INTERFACE_REMOVE ", 17) == 0) {
+ if (wpa_supplicant_global_iface_remove(global, buf + 17))
+ reply_len = -1;
+ } else if (os_strcmp(buf, "INTERFACE_LIST") == 0) {
+ reply_len = wpa_supplicant_global_iface_list(
+ global, reply, reply_size);
+ } else if (os_strcmp(buf, "INTERFACES") == 0) {
+ reply_len = wpa_supplicant_global_iface_interfaces(
+ global, reply, reply_size);
+#ifdef CONFIG_FST
+ } else if (os_strncmp(buf, "FST-ATTACH ", 11) == 0) {
+ reply_len = wpas_global_ctrl_iface_fst_attach(global, buf + 11,
+ reply,
+ reply_size);
+ } else if (os_strncmp(buf, "FST-DETACH ", 11) == 0) {
+ reply_len = wpas_global_ctrl_iface_fst_detach(global, buf + 11,
+ reply,
+ reply_size);
+ } else if (os_strncmp(buf, "FST-MANAGER ", 12) == 0) {
+ reply_len = fst_ctrl_iface_receive(buf + 12, reply, reply_size);
+#endif /* CONFIG_FST */
+ } else if (os_strcmp(buf, "TERMINATE") == 0) {
+ wpa_supplicant_terminate_proc(global);
+ } else if (os_strcmp(buf, "SUSPEND") == 0) {
+ wpas_notify_suspend(global);
+ } else if (os_strcmp(buf, "RESUME") == 0) {
+ wpas_notify_resume(global);
+ } else if (os_strncmp(buf, "SET ", 4) == 0) {
+ if (wpas_global_ctrl_iface_set(global, buf + 4)) {
+#ifdef CONFIG_P2P
+ if (global->p2p_init_wpa_s) {
+ os_free(reply);
+ /* Check if P2P redirection would work for this
+ * command. */
+ return wpa_supplicant_ctrl_iface_process(
+ global->p2p_init_wpa_s,
+ buf, resp_len);
+ }
+#endif /* CONFIG_P2P */
+ reply_len = -1;
+ }
+ } else if (os_strncmp(buf, "DUP_NETWORK ", 12) == 0) {
+ if (wpas_global_ctrl_iface_dup_network(global, buf + 12))
+ reply_len = -1;
+#ifndef CONFIG_NO_CONFIG_WRITE
+ } else if (os_strcmp(buf, "SAVE_CONFIG") == 0) {
+ if (wpas_global_ctrl_iface_save_config(global))
+ reply_len = -1;
+#endif /* CONFIG_NO_CONFIG_WRITE */
+ } else if (os_strcmp(buf, "STATUS") == 0) {
+ reply_len = wpas_global_ctrl_iface_status(global, reply,
+ reply_size);
+#ifdef CONFIG_MODULE_TESTS
+ } else if (os_strcmp(buf, "MODULE_TESTS") == 0) {
+ int wpas_module_tests(void);
+ if (wpas_module_tests() < 0)
+ reply_len = -1;
+#endif /* CONFIG_MODULE_TESTS */
+ } else if (os_strncmp(buf, "RELOG", 5) == 0) {
+ if (wpa_debug_reopen_file() < 0)
+ reply_len = -1;
+ } else {
+ os_memcpy(reply, "UNKNOWN COMMAND\n", 16);
+ reply_len = 16;
+ }
+
+ if (reply_len < 0) {
+ os_memcpy(reply, "FAIL\n", 5);
+ reply_len = 5;
+ }
+
+ *resp_len = reply_len;
+ return reply;
+}
diff --git a/freebsd/contrib/wpa/wpa_supplicant/ctrl_iface.h b/freebsd/contrib/wpa/wpa_supplicant/ctrl_iface.h
new file mode 100644
index 00000000..d54cc076
--- /dev/null
+++ b/freebsd/contrib/wpa/wpa_supplicant/ctrl_iface.h
@@ -0,0 +1,159 @@
+/*
+ * WPA Supplicant / UNIX domain socket -based control interface
+ * Copyright (c) 2004-2005, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef CTRL_IFACE_H
+#define CTRL_IFACE_H
+
+#ifdef CONFIG_CTRL_IFACE
+
+/* Shared functions from ctrl_iface.c; to be called by ctrl_iface backends */
+
+/**
+ * wpa_supplicant_ctrl_iface_process - Process ctrl_iface command
+ * @wpa_s: Pointer to wpa_supplicant data
+ * @buf: Received command buffer (nul terminated string)
+ * @resp_len: Variable to be set to the response length
+ * Returns: Response (*resp_len bytes) or %NULL on failure
+ *
+ * Control interface backends call this function when receiving a message that
+ * they do not process internally, i.e., anything else than ATTACH, DETACH,
+ * and LEVEL. The return response value is then sent to the external program
+ * that sent the command. Caller is responsible for freeing the buffer after
+ * this. If %NULL is returned, *resp_len can be set to two special values:
+ * 1 = send "FAIL\n" response, 2 = send "OK\n" response. If *resp_len has any
+ * other value, no response is sent.
+ */
+char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s,
+ char *buf, size_t *resp_len);
+
+/**
+ * wpa_supplicant_global_ctrl_iface_process - Process global ctrl_iface command
+ * @global: Pointer to global data from wpa_supplicant_init()
+ * @buf: Received command buffer (nul terminated string)
+ * @resp_len: Variable to be set to the response length
+ * Returns: Response (*resp_len bytes) or %NULL on failure
+ *
+ * Control interface backends call this function when receiving a message from
+ * the global ctrl_iface connection. The return response value is then sent to
+ * the external program that sent the command. Caller is responsible for
+ * freeing the buffer after this. If %NULL is returned, *resp_len can be set to
+ * two special values: 1 = send "FAIL\n" response, 2 = send "OK\n" response. If
+ * *resp_len has any other value, no response is sent.
+ */
+char * wpa_supplicant_global_ctrl_iface_process(struct wpa_global *global,
+ char *buf, size_t *resp_len);
+
+
+/* Functions that each ctrl_iface backend must implement */
+
+/**
+ * wpa_supplicant_ctrl_iface_init - Initialize control interface
+ * @wpa_s: Pointer to wpa_supplicant data
+ * Returns: Pointer to private data on success, %NULL on failure
+ *
+ * Initialize the control interface and start receiving commands from external
+ * programs.
+ *
+ * Required to be implemented in each control interface backend.
+ */
+struct ctrl_iface_priv *
+wpa_supplicant_ctrl_iface_init(struct wpa_supplicant *wpa_s);
+
+/**
+ * wpa_supplicant_ctrl_iface_deinit - Deinitialize control interface
+ * @priv: Pointer to private data from wpa_supplicant_ctrl_iface_init()
+ *
+ * Deinitialize the control interface that was initialized with
+ * wpa_supplicant_ctrl_iface_init().
+ *
+ * Required to be implemented in each control interface backend.
+ */
+void wpa_supplicant_ctrl_iface_deinit(struct ctrl_iface_priv *priv);
+
+/**
+ * wpa_supplicant_ctrl_iface_wait - Wait for ctrl_iface monitor
+ * @priv: Pointer to private data from wpa_supplicant_ctrl_iface_init()
+ *
+ * Wait until the first message from an external program using the control
+ * interface is received. This function can be used to delay normal startup
+ * processing to allow control interface programs to attach with
+ * %wpa_supplicant before normal operations are started.
+ *
+ * Required to be implemented in each control interface backend.
+ */
+void wpa_supplicant_ctrl_iface_wait(struct ctrl_iface_priv *priv);
+
+/**
+ * wpa_supplicant_global_ctrl_iface_init - Initialize global control interface
+ * @global: Pointer to global data from wpa_supplicant_init()
+ * Returns: Pointer to private data on success, %NULL on failure
+ *
+ * Initialize the global control interface and start receiving commands from
+ * external programs.
+ *
+ * Required to be implemented in each control interface backend.
+ */
+struct ctrl_iface_global_priv *
+wpa_supplicant_global_ctrl_iface_init(struct wpa_global *global);
+
+/**
+ * wpa_supplicant_global_ctrl_iface_deinit - Deinitialize global ctrl interface
+ * @priv: Pointer to private data from wpa_supplicant_global_ctrl_iface_init()
+ *
+ * Deinitialize the global control interface that was initialized with
+ * wpa_supplicant_global_ctrl_iface_init().
+ *
+ * Required to be implemented in each control interface backend.
+ */
+void wpa_supplicant_global_ctrl_iface_deinit(
+ struct ctrl_iface_global_priv *priv);
+
+void wpas_ctrl_radio_work_flush(struct wpa_supplicant *wpa_s);
+
+#else /* CONFIG_CTRL_IFACE */
+
+static inline struct ctrl_iface_priv *
+wpa_supplicant_ctrl_iface_init(struct wpa_supplicant *wpa_s)
+{
+ return (void *) -1;
+}
+
+static inline void
+wpa_supplicant_ctrl_iface_deinit(struct ctrl_iface_priv *priv)
+{
+}
+
+static inline void
+wpa_supplicant_ctrl_iface_send(struct ctrl_iface_priv *priv, int level,
+ char *buf, size_t len)
+{
+}
+
+static inline void
+wpa_supplicant_ctrl_iface_wait(struct ctrl_iface_priv *priv)
+{
+}
+
+static inline struct ctrl_iface_global_priv *
+wpa_supplicant_global_ctrl_iface_init(struct wpa_global *global)
+{
+ return (void *) 1;
+}
+
+static inline void
+wpa_supplicant_global_ctrl_iface_deinit(struct ctrl_iface_global_priv *priv)
+{
+}
+
+static inline void wpas_ctrl_radio_work_flush(struct wpa_supplicant *wpa_s)
+{
+}
+
+#endif /* CONFIG_CTRL_IFACE */
+
+#endif /* CTRL_IFACE_H */
diff --git a/freebsd/contrib/wpa/wpa_supplicant/ctrl_iface_unix.c b/freebsd/contrib/wpa/wpa_supplicant/ctrl_iface_unix.c
new file mode 100644
index 00000000..259d2937
--- /dev/null
+++ b/freebsd/contrib/wpa/wpa_supplicant/ctrl_iface_unix.c
@@ -0,0 +1,1220 @@
+#include <machine/rtems-bsd-user-space.h>
+
+/*
+ * WPA Supplicant / UNIX domain socket -based control interface
+ * Copyright (c) 2004-2014, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+#include <sys/un.h>
+#include <sys/stat.h>
+#include <grp.h>
+#include <stddef.h>
+#include <unistd.h>
+#include <fcntl.h>
+#ifdef __linux__
+#include <sys/ioctl.h>
+#include <linux/sockios.h>
+#endif /* __linux__ */
+#ifdef ANDROID
+#include <cutils/sockets.h>
+#endif /* ANDROID */
+
+#include "utils/common.h"
+#include "utils/eloop.h"
+#include "utils/list.h"
+#include "eapol_supp/eapol_supp_sm.h"
+#include "config.h"
+#include "wpa_supplicant_i.h"
+#include "ctrl_iface.h"
+
+/* Per-interface ctrl_iface */
+
+/**
+ * struct wpa_ctrl_dst - Internal data structure of control interface monitors
+ *
+ * This structure is used to store information about registered control
+ * interface monitors into struct wpa_supplicant. This data is private to
+ * ctrl_iface_unix.c and should not be touched directly from other files.
+ */
+struct wpa_ctrl_dst {
+ struct dl_list list;
+ struct sockaddr_un addr;
+ socklen_t addrlen;
+ int debug_level;
+ int errors;
+};
+
+
+struct ctrl_iface_priv {
+ struct wpa_supplicant *wpa_s;
+ int sock;
+ struct dl_list ctrl_dst;
+ int android_control_socket;
+};
+
+
+struct ctrl_iface_global_priv {
+ struct wpa_global *global;
+ int sock;
+ struct dl_list ctrl_dst;
+ int android_control_socket;
+};
+
+
+static void wpa_supplicant_ctrl_iface_send(struct wpa_supplicant *wpa_s,
+ const char *ifname, int sock,
+ struct dl_list *ctrl_dst,
+ int level, const char *buf,
+ size_t len,
+ struct ctrl_iface_priv *priv,
+ struct ctrl_iface_global_priv *gp);
+static int wpas_ctrl_iface_reinit(struct wpa_supplicant *wpa_s,
+ struct ctrl_iface_priv *priv);
+static int wpas_ctrl_iface_global_reinit(struct wpa_global *global,
+ struct ctrl_iface_global_priv *priv);
+
+
+static void wpas_ctrl_sock_debug(const char *title, int sock, const char *buf,
+ size_t len)
+{
+#ifdef __linux__
+ socklen_t optlen;
+ int sndbuf, outq;
+ int level = MSG_MSGDUMP;
+
+ if (len >= 5 && os_strncmp(buf, "PONG\n", 5) == 0)
+ level = MSG_EXCESSIVE;
+
+ optlen = sizeof(sndbuf);
+ sndbuf = 0;
+ if (getsockopt(sock, SOL_SOCKET, SO_SNDBUF, &sndbuf, &optlen) < 0)
+ sndbuf = -1;
+
+ if (ioctl(sock, SIOCOUTQ, &outq) < 0)
+ outq = -1;
+
+ wpa_printf(level,
+ "CTRL-DEBUG: %s: sock=%d sndbuf=%d outq=%d send_len=%d",
+ title, sock, sndbuf, outq, (int) len);
+#endif /* __linux__ */
+}
+
+
+static int wpa_supplicant_ctrl_iface_attach(struct dl_list *ctrl_dst,
+ struct sockaddr_un *from,
+ socklen_t fromlen, int global)
+{
+ struct wpa_ctrl_dst *dst;
+ char addr_txt[200];
+
+ dst = os_zalloc(sizeof(*dst));
+ if (dst == NULL)
+ return -1;
+ os_memcpy(&dst->addr, from, sizeof(struct sockaddr_un));
+ dst->addrlen = fromlen;
+ dst->debug_level = MSG_INFO;
+ dl_list_add(ctrl_dst, &dst->list);
+ printf_encode(addr_txt, sizeof(addr_txt),
+ (u8 *) from->sun_path,
+ fromlen - offsetof(struct sockaddr_un, sun_path));
+ wpa_printf(MSG_DEBUG, "CTRL_IFACE %smonitor attached %s",
+ global ? "global " : "", addr_txt);
+ return 0;
+}
+
+
+static int wpa_supplicant_ctrl_iface_detach(struct dl_list *ctrl_dst,
+ struct sockaddr_un *from,
+ socklen_t fromlen)
+{
+ struct wpa_ctrl_dst *dst;
+
+ dl_list_for_each(dst, ctrl_dst, struct wpa_ctrl_dst, list) {
+ if (fromlen == dst->addrlen &&
+ os_memcmp(from->sun_path, dst->addr.sun_path,
+ fromlen - offsetof(struct sockaddr_un, sun_path))
+ == 0) {
+ char addr_txt[200];
+ printf_encode(addr_txt, sizeof(addr_txt),
+ (u8 *) from->sun_path,
+ fromlen -
+ offsetof(struct sockaddr_un, sun_path));
+ wpa_printf(MSG_DEBUG, "CTRL_IFACE monitor detached %s",
+ addr_txt);
+ dl_list_del(&dst->list);
+ os_free(dst);
+ return 0;
+ }
+ }
+ return -1;
+}
+
+
+static int wpa_supplicant_ctrl_iface_level(struct ctrl_iface_priv *priv,
+ struct sockaddr_un *from,
+ socklen_t fromlen,
+ char *level)
+{
+ struct wpa_ctrl_dst *dst;
+
+ wpa_printf(MSG_DEBUG, "CTRL_IFACE LEVEL %s", level);
+
+ dl_list_for_each(dst, &priv->ctrl_dst, struct wpa_ctrl_dst, list) {
+ if (fromlen == dst->addrlen &&
+ os_memcmp(from->sun_path, dst->addr.sun_path,
+ fromlen - offsetof(struct sockaddr_un, sun_path))
+ == 0) {
+ char addr_txt[200];
+ dst->debug_level = atoi(level);
+ printf_encode(addr_txt, sizeof(addr_txt),
+ (u8 *) from->sun_path, fromlen -
+ offsetof(struct sockaddr_un, sun_path));
+ wpa_printf(MSG_DEBUG, "CTRL_IFACE changed monitor level to %d for %s",
+ dst->debug_level, addr_txt);
+ return 0;
+ }
+ }
+
+ return -1;
+}
+
+
+static void wpa_supplicant_ctrl_iface_receive(int sock, void *eloop_ctx,
+ void *sock_ctx)
+{
+ struct wpa_supplicant *wpa_s = eloop_ctx;
+ struct ctrl_iface_priv *priv = sock_ctx;
+ char buf[4096];
+ int res;
+ struct sockaddr_un from;
+ socklen_t fromlen = sizeof(from);
+ char *reply = NULL, *reply_buf = NULL;
+ size_t reply_len = 0;
+ int new_attached = 0;
+
+ res = recvfrom(sock, buf, sizeof(buf) - 1, 0,
+ (struct sockaddr *) &from, &fromlen);
+ if (res < 0) {
+ wpa_printf(MSG_ERROR, "recvfrom(ctrl_iface): %s",
+ strerror(errno));
+ return;
+ }
+ buf[res] = '\0';
+
+ if (os_strcmp(buf, "ATTACH") == 0) {
+ if (wpa_supplicant_ctrl_iface_attach(&priv->ctrl_dst, &from,
+ fromlen, 0))
+ reply_len = 1;
+ else {
+ new_attached = 1;
+ reply_len = 2;
+ }
+ } else if (os_strcmp(buf, "DETACH") == 0) {
+ if (wpa_supplicant_ctrl_iface_detach(&priv->ctrl_dst, &from,
+ fromlen))
+ reply_len = 1;
+ else
+ reply_len = 2;
+ } else if (os_strncmp(buf, "LEVEL ", 6) == 0) {
+ if (wpa_supplicant_ctrl_iface_level(priv, &from, fromlen,
+ buf + 6))
+ reply_len = 1;
+ else
+ reply_len = 2;
+ } else {
+ reply_buf = wpa_supplicant_ctrl_iface_process(wpa_s, buf,
+ &reply_len);
+ reply = reply_buf;
+
+ /*
+ * There could be some password/key material in the command, so
+ * clear the buffer explicitly now that it is not needed
+ * anymore.
+ */
+ os_memset(buf, 0, res);
+ }
+
+ if (!reply && reply_len == 1) {
+ reply = "FAIL\n";
+ reply_len = 5;
+ } else if (!reply && reply_len == 2) {
+ reply = "OK\n";
+ reply_len = 3;
+ }
+
+ if (reply) {
+ wpas_ctrl_sock_debug("ctrl_sock-sendto", sock, reply,
+ reply_len);
+ if (sendto(sock, reply, reply_len, 0, (struct sockaddr *) &from,
+ fromlen) < 0) {
+ int _errno = errno;
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ "ctrl_iface sendto failed: %d - %s",
+ _errno, strerror(_errno));
+ if (_errno == ENOBUFS || _errno == EAGAIN) {
+ /*
+ * The socket send buffer could be full. This
+ * may happen if client programs are not
+ * receiving their pending messages. Close and
+ * reopen the socket as a workaround to avoid
+ * getting stuck being unable to send any new
+ * responses.
+ */
+ sock = wpas_ctrl_iface_reinit(wpa_s, priv);
+ if (sock < 0) {
+ wpa_dbg(wpa_s, MSG_DEBUG, "Failed to reinitialize ctrl_iface socket");
+ }
+ }
+ if (new_attached) {
+ wpa_dbg(wpa_s, MSG_DEBUG, "Failed to send response to ATTACH - detaching");
+ new_attached = 0;
+ wpa_supplicant_ctrl_iface_detach(
+ &priv->ctrl_dst, &from, fromlen);
+ }
+ }
+ }
+ os_free(reply_buf);
+
+ if (new_attached)
+ eapol_sm_notify_ctrl_attached(wpa_s->eapol);
+}
+
+
+static char * wpa_supplicant_ctrl_iface_path(struct wpa_supplicant *wpa_s)
+{
+ char *buf;
+ size_t len;
+ char *pbuf, *dir = NULL;
+ int res;
+
+ if (wpa_s->conf->ctrl_interface == NULL)
+ return NULL;
+
+ pbuf = os_strdup(wpa_s->conf->ctrl_interface);
+ if (pbuf == NULL)
+ return NULL;
+ if (os_strncmp(pbuf, "DIR=", 4) == 0) {
+ char *gid_str;
+ dir = pbuf + 4;
+ gid_str = os_strstr(dir, " GROUP=");
+ if (gid_str)
+ *gid_str = '\0';
+ } else
+ dir = pbuf;
+
+ len = os_strlen(dir) + os_strlen(wpa_s->ifname) + 2;
+ buf = os_malloc(len);
+ if (buf == NULL) {
+ os_free(pbuf);
+ return NULL;
+ }
+
+ res = os_snprintf(buf, len, "%s/%s", dir, wpa_s->ifname);
+ if (os_snprintf_error(len, res)) {
+ os_free(pbuf);
+ os_free(buf);
+ return NULL;
+ }
+#ifdef __CYGWIN__
+ {
+ /* Windows/WinPcap uses interface names that are not suitable
+ * as a file name - convert invalid chars to underscores */
+ char *pos = buf;
+ while (*pos) {
+ if (*pos == '\\')
+ *pos = '_';
+ pos++;
+ }
+ }
+#endif /* __CYGWIN__ */
+ os_free(pbuf);
+ return buf;
+}
+
+
+static void wpa_supplicant_ctrl_iface_msg_cb(void *ctx, int level,
+ enum wpa_msg_type type,
+ const char *txt, size_t len)
+{
+ struct wpa_supplicant *wpa_s = ctx;
+
+ if (wpa_s == NULL)
+ return;
+
+ if (type != WPA_MSG_NO_GLOBAL && wpa_s->global->ctrl_iface) {
+ struct ctrl_iface_global_priv *priv = wpa_s->global->ctrl_iface;
+ if (!dl_list_empty(&priv->ctrl_dst)) {
+ wpa_supplicant_ctrl_iface_send(
+ wpa_s,
+ type != WPA_MSG_PER_INTERFACE ?
+ NULL : wpa_s->ifname,
+ priv->sock, &priv->ctrl_dst, level, txt, len,
+ NULL, priv);
+ }
+ }
+
+ if (type == WPA_MSG_ONLY_GLOBAL || wpa_s->ctrl_iface == NULL)
+ return;
+ wpa_supplicant_ctrl_iface_send(wpa_s, NULL, wpa_s->ctrl_iface->sock,
+ &wpa_s->ctrl_iface->ctrl_dst,
+ level, txt, len, wpa_s->ctrl_iface,
+ NULL);
+}
+
+
+static int wpas_ctrl_iface_open_sock(struct wpa_supplicant *wpa_s,
+ struct ctrl_iface_priv *priv)
+{
+ struct sockaddr_un addr;
+ char *fname = NULL;
+ gid_t gid = 0;
+ int gid_set = 0;
+ char *buf, *dir = NULL, *gid_str = NULL;
+ struct group *grp;
+ char *endp;
+ int flags;
+
+ buf = os_strdup(wpa_s->conf->ctrl_interface);
+ if (buf == NULL)
+ goto fail;
+#ifdef ANDROID
+ os_snprintf(addr.sun_path, sizeof(addr.sun_path), "wpa_%s",
+ wpa_s->conf->ctrl_interface);
+ priv->sock = android_get_control_socket(addr.sun_path);
+ if (priv->sock >= 0) {
+ priv->android_control_socket = 1;
+ goto havesock;
+ }
+#endif /* ANDROID */
+ if (os_strncmp(buf, "DIR=", 4) == 0) {
+ dir = buf + 4;
+ gid_str = os_strstr(dir, " GROUP=");
+ if (gid_str) {
+ *gid_str = '\0';
+ gid_str += 7;
+ }
+ } else {
+ dir = buf;
+ gid_str = wpa_s->conf->ctrl_interface_group;
+ }
+
+ if (mkdir(dir, S_IRWXU | S_IRWXG) < 0) {
+ if (errno == EEXIST) {
+ wpa_printf(MSG_DEBUG, "Using existing control "
+ "interface directory.");
+ } else {
+ wpa_printf(MSG_ERROR, "mkdir[ctrl_interface=%s]: %s",
+ dir, strerror(errno));
+ goto fail;
+ }
+ }
+
+#ifdef ANDROID
+ /*
+ * wpa_supplicant is started from /init.*.rc on Android and that seems
+ * to be using umask 0077 which would leave the control interface
+ * directory without group access. This breaks things since Wi-Fi
+ * framework assumes that this directory can be accessed by other
+ * applications in the wifi group. Fix this by adding group access even
+ * if umask value would prevent this.
+ */
+ if (chmod(dir, S_IRWXU | S_IRWXG) < 0) {
+ wpa_printf(MSG_ERROR, "CTRL: Could not chmod directory: %s",
+ strerror(errno));
+ /* Try to continue anyway */
+ }
+#endif /* ANDROID */
+
+ if (gid_str) {
+ grp = getgrnam(gid_str);
+ if (grp) {
+ gid = grp->gr_gid;
+ gid_set = 1;
+ wpa_printf(MSG_DEBUG, "ctrl_interface_group=%d"
+ " (from group name '%s')",
+ (int) gid, gid_str);
+ } else {
+ /* Group name not found - try to parse this as gid */
+ gid = strtol(gid_str, &endp, 10);
+ if (*gid_str == '\0' || *endp != '\0') {
+ wpa_printf(MSG_ERROR, "CTRL: Invalid group "
+ "'%s'", gid_str);
+ goto fail;
+ }
+ gid_set = 1;
+ wpa_printf(MSG_DEBUG, "ctrl_interface_group=%d",
+ (int) gid);
+ }
+ }
+
+ if (gid_set && chown(dir, -1, gid) < 0) {
+ wpa_printf(MSG_ERROR, "chown[ctrl_interface=%s,gid=%d]: %s",
+ dir, (int) gid, strerror(errno));
+ goto fail;
+ }
+
+ /* Make sure the group can enter and read the directory */
+ if (gid_set &&
+ chmod(dir, S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IXGRP) < 0) {
+ wpa_printf(MSG_ERROR, "CTRL: chmod[ctrl_interface]: %s",
+ strerror(errno));
+ goto fail;
+ }
+
+ if (os_strlen(dir) + 1 + os_strlen(wpa_s->ifname) >=
+ sizeof(addr.sun_path)) {
+ wpa_printf(MSG_ERROR, "ctrl_iface path limit exceeded");
+ goto fail;
+ }
+
+ priv->sock = socket(PF_UNIX, SOCK_DGRAM, 0);
+ if (priv->sock < 0) {
+ wpa_printf(MSG_ERROR, "socket(PF_UNIX): %s", strerror(errno));
+ goto fail;
+ }
+
+ os_memset(&addr, 0, sizeof(addr));
+#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
+ addr.sun_len = sizeof(addr);
+#endif /* __FreeBSD__ */
+ addr.sun_family = AF_UNIX;
+ fname = wpa_supplicant_ctrl_iface_path(wpa_s);
+ if (fname == NULL)
+ goto fail;
+ os_strlcpy(addr.sun_path, fname, sizeof(addr.sun_path));
+ if (bind(priv->sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+ wpa_printf(MSG_DEBUG, "ctrl_iface bind(PF_UNIX) failed: %s",
+ strerror(errno));
+ if (connect(priv->sock, (struct sockaddr *) &addr,
+ sizeof(addr)) < 0) {
+ wpa_printf(MSG_DEBUG, "ctrl_iface exists, but does not"
+ " allow connections - assuming it was left"
+ "over from forced program termination");
+ if (unlink(fname) < 0) {
+ wpa_printf(MSG_ERROR,
+ "Could not unlink existing ctrl_iface socket '%s': %s",
+ fname, strerror(errno));
+ goto fail;
+ }
+ if (bind(priv->sock, (struct sockaddr *) &addr,
+ sizeof(addr)) < 0) {
+ wpa_printf(MSG_ERROR, "supp-ctrl-iface-init: bind(PF_UNIX): %s",
+ strerror(errno));
+ goto fail;
+ }
+ wpa_printf(MSG_DEBUG, "Successfully replaced leftover "
+ "ctrl_iface socket '%s'", fname);
+ } else {
+ wpa_printf(MSG_INFO, "ctrl_iface exists and seems to "
+ "be in use - cannot override it");
+ wpa_printf(MSG_INFO, "Delete '%s' manually if it is "
+ "not used anymore", fname);
+ os_free(fname);
+ fname = NULL;
+ goto fail;
+ }
+ }
+
+ if (gid_set && chown(fname, -1, gid) < 0) {
+ wpa_printf(MSG_ERROR, "chown[ctrl_interface=%s,gid=%d]: %s",
+ fname, (int) gid, strerror(errno));
+ goto fail;
+ }
+
+ if (chmod(fname, S_IRWXU | S_IRWXG) < 0) {
+ wpa_printf(MSG_ERROR, "chmod[ctrl_interface=%s]: %s",
+ fname, strerror(errno));
+ goto fail;
+ }
+ os_free(fname);
+
+#ifdef ANDROID
+havesock:
+#endif /* ANDROID */
+
+ /*
+ * Make socket non-blocking so that we don't hang forever if
+ * target dies unexpectedly.
+ */
+ flags = fcntl(priv->sock, F_GETFL);
+ if (flags >= 0) {
+ flags |= O_NONBLOCK;
+ if (fcntl(priv->sock, F_SETFL, flags) < 0) {
+ wpa_printf(MSG_INFO, "fcntl(ctrl, O_NONBLOCK): %s",
+ strerror(errno));
+ /* Not fatal, continue on.*/
+ }
+ }
+
+ eloop_register_read_sock(priv->sock, wpa_supplicant_ctrl_iface_receive,
+ wpa_s, priv);
+ wpa_msg_register_cb(wpa_supplicant_ctrl_iface_msg_cb);
+
+ os_free(buf);
+ return 0;
+
+fail:
+ if (priv->sock >= 0) {
+ close(priv->sock);
+ priv->sock = -1;
+ }
+ if (fname) {
+ unlink(fname);
+ os_free(fname);
+ }
+ os_free(buf);
+ return -1;
+}
+
+
+struct ctrl_iface_priv *
+wpa_supplicant_ctrl_iface_init(struct wpa_supplicant *wpa_s)
+{
+ struct ctrl_iface_priv *priv;
+
+ priv = os_zalloc(sizeof(*priv));
+ if (priv == NULL)
+ return NULL;
+ dl_list_init(&priv->ctrl_dst);
+ priv->wpa_s = wpa_s;
+ priv->sock = -1;
+
+ if (wpa_s->conf->ctrl_interface == NULL)
+ return priv;
+
+#ifdef ANDROID
+ if (wpa_s->global->params.ctrl_interface) {
+ int same = 0;
+
+ if (wpa_s->global->params.ctrl_interface[0] == '/') {
+ if (os_strcmp(wpa_s->global->params.ctrl_interface,
+ wpa_s->conf->ctrl_interface) == 0)
+ same = 1;
+ } else if (os_strncmp(wpa_s->global->params.ctrl_interface,
+ "@android:", 9) == 0 ||
+ os_strncmp(wpa_s->global->params.ctrl_interface,
+ "@abstract:", 10) == 0) {
+ char *pos;
+
+ /*
+ * Currently, Android uses @android:wpa_* as the naming
+ * convention for the global ctrl interface. This logic
+ * needs to be revisited if the above naming convention
+ * is modified.
+ */
+ pos = os_strchr(wpa_s->global->params.ctrl_interface,
+ '_');
+ if (pos &&
+ os_strcmp(pos + 1,
+ wpa_s->conf->ctrl_interface) == 0)
+ same = 1;
+ }
+
+ if (same) {
+ /*
+ * The invalid configuration combination might be
+ * possible to hit in an Android OTA upgrade case, so
+ * instead of refusing to start the wpa_supplicant
+ * process, do not open the per-interface ctrl_iface
+ * and continue with the global control interface that
+ * was set from the command line since the Wi-Fi
+ * framework will use it for operations.
+ */
+ wpa_printf(MSG_ERROR,
+ "global ctrl interface %s matches ctrl interface %s - do not open per-interface ctrl interface",
+ wpa_s->global->params.ctrl_interface,
+ wpa_s->conf->ctrl_interface);
+ return priv;
+ }
+ }
+#endif /* ANDROID */
+
+ if (wpas_ctrl_iface_open_sock(wpa_s, priv) < 0) {
+ os_free(priv);
+ return NULL;
+ }
+
+ return priv;
+}
+
+
+static int wpas_ctrl_iface_reinit(struct wpa_supplicant *wpa_s,
+ struct ctrl_iface_priv *priv)
+{
+ int res;
+
+ if (priv->sock <= 0)
+ return -1;
+
+ /*
+ * On Android, the control socket being used may be the socket
+ * that is created when wpa_supplicant is started as a /init.*.rc
+ * service. Such a socket is maintained as a key-value pair in
+ * Android's environment. Closing this control socket would leave us
+ * in a bad state with an invalid socket descriptor.
+ */
+ if (priv->android_control_socket)
+ return priv->sock;
+
+ eloop_unregister_read_sock(priv->sock);
+ close(priv->sock);
+ priv->sock = -1;
+ res = wpas_ctrl_iface_open_sock(wpa_s, priv);
+ if (res < 0)
+ return -1;
+ return priv->sock;
+}
+
+
+void wpa_supplicant_ctrl_iface_deinit(struct ctrl_iface_priv *priv)
+{
+ struct wpa_ctrl_dst *dst, *prev;
+
+ if (priv->sock > -1) {
+ char *fname;
+ char *buf, *dir = NULL;
+ eloop_unregister_read_sock(priv->sock);
+ if (!dl_list_empty(&priv->ctrl_dst)) {
+ /*
+ * Wait before closing the control socket if
+ * there are any attached monitors in order to allow
+ * them to receive any pending messages.
+ */
+ wpa_printf(MSG_DEBUG, "CTRL_IFACE wait for attached "
+ "monitors to receive messages");
+ os_sleep(0, 100000);
+ }
+ close(priv->sock);
+ priv->sock = -1;
+ fname = wpa_supplicant_ctrl_iface_path(priv->wpa_s);
+ if (fname) {
+ unlink(fname);
+ os_free(fname);
+ }
+
+ if (priv->wpa_s->conf->ctrl_interface == NULL)
+ goto free_dst;
+ buf = os_strdup(priv->wpa_s->conf->ctrl_interface);
+ if (buf == NULL)
+ goto free_dst;
+ if (os_strncmp(buf, "DIR=", 4) == 0) {
+ char *gid_str;
+ dir = buf + 4;
+ gid_str = os_strstr(dir, " GROUP=");
+ if (gid_str)
+ *gid_str = '\0';
+ } else
+ dir = buf;
+
+ if (rmdir(dir) < 0) {
+ if (errno == ENOTEMPTY) {
+ wpa_printf(MSG_DEBUG, "Control interface "
+ "directory not empty - leaving it "
+ "behind");
+ } else {
+ wpa_printf(MSG_ERROR,
+ "rmdir[ctrl_interface=%s]: %s",
+ dir, strerror(errno));
+ }
+ }
+ os_free(buf);
+ }
+
+free_dst:
+ dl_list_for_each_safe(dst, prev, &priv->ctrl_dst, struct wpa_ctrl_dst,
+ list)
+ os_free(dst);
+ os_free(priv);
+}
+
+
+/**
+ * wpa_supplicant_ctrl_iface_send - Send a control interface packet to monitors
+ * @ifname: Interface name for global control socket or %NULL
+ * @sock: Local socket fd
+ * @ctrl_dst: List of attached listeners
+ * @level: Priority level of the message
+ * @buf: Message data
+ * @len: Message length
+ *
+ * Send a packet to all monitor programs attached to the control interface.
+ */
+static void wpa_supplicant_ctrl_iface_send(struct wpa_supplicant *wpa_s,
+ const char *ifname, int sock,
+ struct dl_list *ctrl_dst,
+ int level, const char *buf,
+ size_t len,
+ struct ctrl_iface_priv *priv,
+ struct ctrl_iface_global_priv *gp)
+{
+ struct wpa_ctrl_dst *dst, *next;
+ char levelstr[10];
+ int idx, res;
+ struct msghdr msg;
+ struct iovec io[5];
+
+ if (sock < 0 || dl_list_empty(ctrl_dst))
+ return;
+
+ res = os_snprintf(levelstr, sizeof(levelstr), "<%d>", level);
+ if (os_snprintf_error(sizeof(levelstr), res))
+ return;
+ idx = 0;
+ if (ifname) {
+ io[idx].iov_base = "IFNAME=";
+ io[idx].iov_len = 7;
+ idx++;
+ io[idx].iov_base = (char *) ifname;
+ io[idx].iov_len = os_strlen(ifname);
+ idx++;
+ io[idx].iov_base = " ";
+ io[idx].iov_len = 1;
+ idx++;
+ }
+ io[idx].iov_base = levelstr;
+ io[idx].iov_len = os_strlen(levelstr);
+ idx++;
+ io[idx].iov_base = (char *) buf;
+ io[idx].iov_len = len;
+ idx++;
+ os_memset(&msg, 0, sizeof(msg));
+ msg.msg_iov = io;
+ msg.msg_iovlen = idx;
+
+ dl_list_for_each_safe(dst, next, ctrl_dst, struct wpa_ctrl_dst, list) {
+ int _errno;
+ char addr_txt[200];
+
+ if (level < dst->debug_level)
+ continue;
+
+ printf_encode(addr_txt, sizeof(addr_txt),
+ (u8 *) dst->addr.sun_path, dst->addrlen -
+ offsetof(struct sockaddr_un, sun_path));
+ msg.msg_name = (void *) &dst->addr;
+ msg.msg_namelen = dst->addrlen;
+ wpas_ctrl_sock_debug("ctrl_sock-sendmsg", sock, buf, len);
+ if (sendmsg(sock, &msg, MSG_DONTWAIT) >= 0) {
+ wpa_printf(MSG_MSGDUMP,
+ "CTRL_IFACE monitor sent successfully to %s",
+ addr_txt);
+ dst->errors = 0;
+ continue;
+ }
+
+ _errno = errno;
+ wpa_printf(MSG_DEBUG, "CTRL_IFACE monitor[%s]: %d - %s",
+ addr_txt, errno, strerror(errno));
+ dst->errors++;
+
+ if (dst->errors > 10 || _errno == ENOENT || _errno == EPERM) {
+ wpa_printf(MSG_INFO, "CTRL_IFACE: Detach monitor %s that cannot receive messages",
+ addr_txt);
+ wpa_supplicant_ctrl_iface_detach(ctrl_dst, &dst->addr,
+ dst->addrlen);
+ }
+
+ if (_errno == ENOBUFS || _errno == EAGAIN) {
+ /*
+ * The socket send buffer could be full. This may happen
+ * if client programs are not receiving their pending
+ * messages. Close and reopen the socket as a workaround
+ * to avoid getting stuck being unable to send any new
+ * responses.
+ */
+ if (priv)
+ sock = wpas_ctrl_iface_reinit(wpa_s, priv);
+ else if (gp)
+ sock = wpas_ctrl_iface_global_reinit(
+ wpa_s->global, gp);
+ else
+ break;
+ if (sock < 0) {
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ "Failed to reinitialize ctrl_iface socket");
+ break;
+ }
+ }
+ }
+}
+
+
+void wpa_supplicant_ctrl_iface_wait(struct ctrl_iface_priv *priv)
+{
+ char buf[256];
+ int res;
+ struct sockaddr_un from;
+ socklen_t fromlen = sizeof(from);
+
+ for (;;) {
+ wpa_printf(MSG_DEBUG, "CTRL_IFACE - %s - wait for monitor to "
+ "attach", priv->wpa_s->ifname);
+ eloop_wait_for_read_sock(priv->sock);
+
+ res = recvfrom(priv->sock, buf, sizeof(buf) - 1, 0,
+ (struct sockaddr *) &from, &fromlen);
+ if (res < 0) {
+ wpa_printf(MSG_ERROR, "recvfrom(ctrl_iface): %s",
+ strerror(errno));
+ continue;
+ }
+ buf[res] = '\0';
+
+ if (os_strcmp(buf, "ATTACH") == 0) {
+ /* handle ATTACH signal of first monitor interface */
+ if (!wpa_supplicant_ctrl_iface_attach(&priv->ctrl_dst,
+ &from, fromlen,
+ 0)) {
+ if (sendto(priv->sock, "OK\n", 3, 0,
+ (struct sockaddr *) &from, fromlen) <
+ 0) {
+ wpa_printf(MSG_DEBUG, "ctrl_iface sendto failed: %s",
+ strerror(errno));
+ }
+ /* OK to continue */
+ return;
+ } else {
+ if (sendto(priv->sock, "FAIL\n", 5, 0,
+ (struct sockaddr *) &from, fromlen) <
+ 0) {
+ wpa_printf(MSG_DEBUG, "ctrl_iface sendto failed: %s",
+ strerror(errno));
+ }
+ }
+ } else {
+ /* return FAIL for all other signals */
+ if (sendto(priv->sock, "FAIL\n", 5, 0,
+ (struct sockaddr *) &from, fromlen) < 0) {
+ wpa_printf(MSG_DEBUG,
+ "ctrl_iface sendto failed: %s",
+ strerror(errno));
+ }
+ }
+ }
+}
+
+
+/* Global ctrl_iface */
+
+static void wpa_supplicant_global_ctrl_iface_receive(int sock, void *eloop_ctx,
+ void *sock_ctx)
+{
+ struct wpa_global *global = eloop_ctx;
+ struct ctrl_iface_global_priv *priv = sock_ctx;
+ char buf[4096];
+ int res;
+ struct sockaddr_un from;
+ socklen_t fromlen = sizeof(from);
+ char *reply = NULL, *reply_buf = NULL;
+ size_t reply_len;
+
+ res = recvfrom(sock, buf, sizeof(buf) - 1, 0,
+ (struct sockaddr *) &from, &fromlen);
+ if (res < 0) {
+ wpa_printf(MSG_ERROR, "recvfrom(ctrl_iface): %s",
+ strerror(errno));
+ return;
+ }
+ buf[res] = '\0';
+
+ if (os_strcmp(buf, "ATTACH") == 0) {
+ if (wpa_supplicant_ctrl_iface_attach(&priv->ctrl_dst, &from,
+ fromlen, 1))
+ reply_len = 1;
+ else
+ reply_len = 2;
+ } else if (os_strcmp(buf, "DETACH") == 0) {
+ if (wpa_supplicant_ctrl_iface_detach(&priv->ctrl_dst, &from,
+ fromlen))
+ reply_len = 1;
+ else
+ reply_len = 2;
+ } else {
+ reply_buf = wpa_supplicant_global_ctrl_iface_process(
+ global, buf, &reply_len);
+ reply = reply_buf;
+
+ /*
+ * There could be some password/key material in the command, so
+ * clear the buffer explicitly now that it is not needed
+ * anymore.
+ */
+ os_memset(buf, 0, res);
+ }
+
+ if (!reply && reply_len == 1) {
+ reply = "FAIL\n";
+ reply_len = 5;
+ } else if (!reply && reply_len == 2) {
+ reply = "OK\n";
+ reply_len = 3;
+ }
+
+ if (reply) {
+ wpas_ctrl_sock_debug("global_ctrl_sock-sendto",
+ sock, reply, reply_len);
+ if (sendto(sock, reply, reply_len, 0, (struct sockaddr *) &from,
+ fromlen) < 0) {
+ wpa_printf(MSG_DEBUG, "ctrl_iface sendto failed: %s",
+ strerror(errno));
+ }
+ }
+ os_free(reply_buf);
+}
+
+
+static int wpas_global_ctrl_iface_open_sock(struct wpa_global *global,
+ struct ctrl_iface_global_priv *priv)
+{
+ struct sockaddr_un addr;
+ const char *ctrl = global->params.ctrl_interface;
+ int flags;
+
+ wpa_printf(MSG_DEBUG, "Global control interface '%s'", ctrl);
+
+#ifdef ANDROID
+ if (os_strncmp(ctrl, "@android:", 9) == 0) {
+ priv->sock = android_get_control_socket(ctrl + 9);
+ if (priv->sock < 0) {
+ wpa_printf(MSG_ERROR, "Failed to open Android control "
+ "socket '%s'", ctrl + 9);
+ goto fail;
+ }
+ wpa_printf(MSG_DEBUG, "Using Android control socket '%s'",
+ ctrl + 9);
+ priv->android_control_socket = 1;
+ goto havesock;
+ }
+
+ if (os_strncmp(ctrl, "@abstract:", 10) != 0) {
+ /*
+ * Backwards compatibility - try to open an Android control
+ * socket and if that fails, assume this was a UNIX domain
+ * socket instead.
+ */
+ priv->sock = android_get_control_socket(ctrl);
+ if (priv->sock >= 0) {
+ wpa_printf(MSG_DEBUG,
+ "Using Android control socket '%s'",
+ ctrl);
+ priv->android_control_socket = 1;
+ goto havesock;
+ }
+ }
+#endif /* ANDROID */
+
+ priv->sock = socket(PF_UNIX, SOCK_DGRAM, 0);
+ if (priv->sock < 0) {
+ wpa_printf(MSG_ERROR, "socket(PF_UNIX): %s", strerror(errno));
+ goto fail;
+ }
+
+ os_memset(&addr, 0, sizeof(addr));
+#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
+ addr.sun_len = sizeof(addr);
+#endif /* __FreeBSD__ */
+ addr.sun_family = AF_UNIX;
+
+ if (os_strncmp(ctrl, "@abstract:", 10) == 0) {
+ addr.sun_path[0] = '\0';
+ os_strlcpy(addr.sun_path + 1, ctrl + 10,
+ sizeof(addr.sun_path) - 1);
+ if (bind(priv->sock, (struct sockaddr *) &addr, sizeof(addr)) <
+ 0) {
+ wpa_printf(MSG_ERROR, "supp-global-ctrl-iface-init: "
+ "bind(PF_UNIX;%s) failed: %s",
+ ctrl, strerror(errno));
+ goto fail;
+ }
+ wpa_printf(MSG_DEBUG, "Using Abstract control socket '%s'",
+ ctrl + 10);
+ goto havesock;
+ }
+
+ os_strlcpy(addr.sun_path, ctrl, sizeof(addr.sun_path));
+ if (bind(priv->sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+ wpa_printf(MSG_INFO, "supp-global-ctrl-iface-init(%s) (will try fixup): bind(PF_UNIX): %s",
+ ctrl, strerror(errno));
+ if (connect(priv->sock, (struct sockaddr *) &addr,
+ sizeof(addr)) < 0) {
+ wpa_printf(MSG_DEBUG, "ctrl_iface exists, but does not"
+ " allow connections - assuming it was left"
+ "over from forced program termination");
+ if (unlink(ctrl) < 0) {
+ wpa_printf(MSG_ERROR,
+ "Could not unlink existing ctrl_iface socket '%s': %s",
+ ctrl, strerror(errno));
+ goto fail;
+ }
+ if (bind(priv->sock, (struct sockaddr *) &addr,
+ sizeof(addr)) < 0) {
+ wpa_printf(MSG_ERROR, "supp-glb-iface-init: bind(PF_UNIX;%s): %s",
+ ctrl, strerror(errno));
+ goto fail;
+ }
+ wpa_printf(MSG_DEBUG, "Successfully replaced leftover "
+ "ctrl_iface socket '%s'",
+ ctrl);
+ } else {
+ wpa_printf(MSG_INFO, "ctrl_iface exists and seems to "
+ "be in use - cannot override it");
+ wpa_printf(MSG_INFO, "Delete '%s' manually if it is "
+ "not used anymore",
+ ctrl);
+ goto fail;
+ }
+ }
+
+ wpa_printf(MSG_DEBUG, "Using UNIX control socket '%s'", ctrl);
+
+ if (global->params.ctrl_interface_group) {
+ char *gid_str = global->params.ctrl_interface_group;
+ gid_t gid = 0;
+ struct group *grp;
+ char *endp;
+
+ grp = getgrnam(gid_str);
+ if (grp) {
+ gid = grp->gr_gid;
+ wpa_printf(MSG_DEBUG, "ctrl_interface_group=%d"
+ " (from group name '%s')",
+ (int) gid, gid_str);
+ } else {
+ /* Group name not found - try to parse this as gid */
+ gid = strtol(gid_str, &endp, 10);
+ if (*gid_str == '\0' || *endp != '\0') {
+ wpa_printf(MSG_ERROR, "CTRL: Invalid group "
+ "'%s'", gid_str);
+ goto fail;
+ }
+ wpa_printf(MSG_DEBUG, "ctrl_interface_group=%d",
+ (int) gid);
+ }
+ if (chown(ctrl, -1, gid) < 0) {
+ wpa_printf(MSG_ERROR,
+ "chown[global_ctrl_interface=%s,gid=%d]: %s",
+ ctrl, (int) gid, strerror(errno));
+ goto fail;
+ }
+
+ if (chmod(ctrl, S_IRWXU | S_IRWXG) < 0) {
+ wpa_printf(MSG_ERROR,
+ "chmod[global_ctrl_interface=%s]: %s",
+ ctrl, strerror(errno));
+ goto fail;
+ }
+ } else {
+ if (chmod(ctrl, S_IRWXU) < 0) {
+ wpa_printf(MSG_DEBUG,
+ "chmod[global_ctrl_interface=%s](S_IRWXU): %s",
+ ctrl, strerror(errno));
+ /* continue anyway since group change was not required
+ */
+ }
+ }
+
+havesock:
+
+ /*
+ * Make socket non-blocking so that we don't hang forever if
+ * target dies unexpectedly.
+ */
+ flags = fcntl(priv->sock, F_GETFL);
+ if (flags >= 0) {
+ flags |= O_NONBLOCK;
+ if (fcntl(priv->sock, F_SETFL, flags) < 0) {
+ wpa_printf(MSG_INFO, "fcntl(ctrl, O_NONBLOCK): %s",
+ strerror(errno));
+ /* Not fatal, continue on.*/
+ }
+ }
+
+ eloop_register_read_sock(priv->sock,
+ wpa_supplicant_global_ctrl_iface_receive,
+ global, priv);
+
+ return 0;
+
+fail:
+ if (priv->sock >= 0) {
+ close(priv->sock);
+ priv->sock = -1;
+ }
+ return -1;
+}
+
+
+struct ctrl_iface_global_priv *
+wpa_supplicant_global_ctrl_iface_init(struct wpa_global *global)
+{
+ struct ctrl_iface_global_priv *priv;
+
+ priv = os_zalloc(sizeof(*priv));
+ if (priv == NULL)
+ return NULL;
+ dl_list_init(&priv->ctrl_dst);
+ priv->global = global;
+ priv->sock = -1;
+
+ if (global->params.ctrl_interface == NULL)
+ return priv;
+
+ if (wpas_global_ctrl_iface_open_sock(global, priv) < 0) {
+ os_free(priv);
+ return NULL;
+ }
+
+ wpa_msg_register_cb(wpa_supplicant_ctrl_iface_msg_cb);
+
+ return priv;
+}
+
+
+static int wpas_ctrl_iface_global_reinit(struct wpa_global *global,
+ struct ctrl_iface_global_priv *priv)
+{
+ int res;
+
+ if (priv->sock <= 0)
+ return -1;
+
+ /*
+ * On Android, the control socket being used may be the socket
+ * that is created when wpa_supplicant is started as a /init.*.rc
+ * service. Such a socket is maintained as a key-value pair in
+ * Android's environment. Closing this control socket would leave us
+ * in a bad state with an invalid socket descriptor.
+ */
+ if (priv->android_control_socket)
+ return priv->sock;
+
+ eloop_unregister_read_sock(priv->sock);
+ close(priv->sock);
+ priv->sock = -1;
+ res = wpas_global_ctrl_iface_open_sock(global, priv);
+ if (res < 0)
+ return -1;
+ return priv->sock;
+}
+
+
+void
+wpa_supplicant_global_ctrl_iface_deinit(struct ctrl_iface_global_priv *priv)
+{
+ struct wpa_ctrl_dst *dst, *prev;
+
+ if (priv->sock >= 0) {
+ eloop_unregister_read_sock(priv->sock);
+ close(priv->sock);
+ }
+ if (priv->global->params.ctrl_interface)
+ unlink(priv->global->params.ctrl_interface);
+ dl_list_for_each_safe(dst, prev, &priv->ctrl_dst, struct wpa_ctrl_dst,
+ list)
+ os_free(dst);
+ os_free(priv);
+}
diff --git a/freebsd/contrib/wpa/wpa_supplicant/dbus/dbus_common.h b/freebsd/contrib/wpa/wpa_supplicant/dbus/dbus_common.h
new file mode 100644
index 00000000..aea7db74
--- /dev/null
+++ b/freebsd/contrib/wpa/wpa_supplicant/dbus/dbus_common.h
@@ -0,0 +1,20 @@
+/*
+ * wpa_supplicant D-Bus control interface - common definitions
+ * Copyright (c) 2006, Dan Williams <dcbw@redhat.com> and Red Hat, Inc.
+ * Copyright (c) 2009, Witold Sowa <witold.sowa@gmail.com>
+ * 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.
+ */
+
+#ifndef DBUS_COMMON_H
+#define DBUS_COMMON_H
+
+struct wpas_dbus_priv;
+struct wpa_global;
+
+struct wpas_dbus_priv * wpas_dbus_init(struct wpa_global *global);
+void wpas_dbus_deinit(struct wpas_dbus_priv *priv);
+
+#endif /* DBUS_COMMON_H */
diff --git a/freebsd/contrib/wpa/wpa_supplicant/dbus/dbus_new.h b/freebsd/contrib/wpa/wpa_supplicant/dbus/dbus_new.h
new file mode 100644
index 00000000..6d240fff
--- /dev/null
+++ b/freebsd/contrib/wpa/wpa_supplicant/dbus/dbus_new.h
@@ -0,0 +1,557 @@
+/*
+ * WPA Supplicant / dbus-based control interface
+ * Copyright (c) 2006, Dan Williams <dcbw@redhat.com> and Red Hat, Inc.
+ * Copyright (c) 2009-2010, Witold Sowa <witold.sowa@gmail.com>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef CTRL_IFACE_DBUS_NEW_H
+#define CTRL_IFACE_DBUS_NEW_H
+
+#include "common/defs.h"
+#include "p2p/p2p.h"
+
+struct wpa_global;
+struct wpa_supplicant;
+struct wpa_ssid;
+struct wps_event_m2d;
+struct wps_event_fail;
+struct wps_credential;
+
+enum wpas_dbus_prop {
+ WPAS_DBUS_PROP_AP_SCAN,
+ WPAS_DBUS_PROP_SCANNING,
+ WPAS_DBUS_PROP_STATE,
+ WPAS_DBUS_PROP_CURRENT_BSS,
+ WPAS_DBUS_PROP_CURRENT_NETWORK,
+ WPAS_DBUS_PROP_CURRENT_AUTH_MODE,
+ WPAS_DBUS_PROP_BSSS,
+ WPAS_DBUS_PROP_DISCONNECT_REASON,
+};
+
+enum wpas_dbus_bss_prop {
+ WPAS_DBUS_BSS_PROP_SIGNAL,
+ WPAS_DBUS_BSS_PROP_FREQ,
+ WPAS_DBUS_BSS_PROP_MODE,
+ WPAS_DBUS_BSS_PROP_PRIVACY,
+ WPAS_DBUS_BSS_PROP_RATES,
+ WPAS_DBUS_BSS_PROP_WPA,
+ WPAS_DBUS_BSS_PROP_RSN,
+ WPAS_DBUS_BSS_PROP_WPS,
+ WPAS_DBUS_BSS_PROP_IES,
+ WPAS_DBUS_BSS_PROP_AGE,
+};
+
+#define WPAS_DBUS_OBJECT_PATH_MAX 150
+
+#define WPAS_DBUS_NEW_SERVICE "fi.w1.wpa_supplicant1"
+#define WPAS_DBUS_NEW_PATH "/fi/w1/wpa_supplicant1"
+#define WPAS_DBUS_NEW_INTERFACE "fi.w1.wpa_supplicant1"
+
+#define WPAS_DBUS_NEW_PATH_INTERFACES WPAS_DBUS_NEW_PATH "/Interfaces"
+#define WPAS_DBUS_NEW_IFACE_INTERFACE WPAS_DBUS_NEW_INTERFACE ".Interface"
+#define WPAS_DBUS_NEW_IFACE_WPS WPAS_DBUS_NEW_IFACE_INTERFACE ".WPS"
+
+#define WPAS_DBUS_NEW_NETWORKS_PART "Networks"
+#define WPAS_DBUS_NEW_IFACE_NETWORK WPAS_DBUS_NEW_INTERFACE ".Network"
+
+#define WPAS_DBUS_NEW_BSSIDS_PART "BSSs"
+#define WPAS_DBUS_NEW_IFACE_BSS WPAS_DBUS_NEW_INTERFACE ".BSS"
+
+#define WPAS_DBUS_NEW_IFACE_P2PDEVICE \
+ WPAS_DBUS_NEW_IFACE_INTERFACE ".P2PDevice"
+
+/*
+ * Groups correspond to P2P groups where this device is a GO (owner)
+ */
+#define WPAS_DBUS_NEW_P2P_GROUPS_PART "Groups"
+#define WPAS_DBUS_NEW_IFACE_P2P_GROUP WPAS_DBUS_NEW_INTERFACE ".Group"
+
+/*
+ * Different dbus object for persistent groups so they do not get confused
+ * with regular (configured) network objects.
+ */
+#define WPAS_DBUS_NEW_PERSISTENT_GROUPS_PART "PersistentGroups"
+#define WPAS_DBUS_NEW_IFACE_PERSISTENT_GROUP \
+ WPAS_DBUS_NEW_INTERFACE ".PersistentGroup"
+
+#define WPAS_DBUS_NEW_P2P_PEERS_PART "Peers"
+#define WPAS_DBUS_NEW_IFACE_P2P_PEER WPAS_DBUS_NEW_INTERFACE ".Peer"
+
+/* Top-level Errors */
+#define WPAS_DBUS_ERROR_UNKNOWN_ERROR \
+ WPAS_DBUS_NEW_INTERFACE ".UnknownError"
+#define WPAS_DBUS_ERROR_INVALID_ARGS \
+ WPAS_DBUS_NEW_INTERFACE ".InvalidArgs"
+
+#define WPAS_DBUS_ERROR_IFACE_EXISTS \
+ WPAS_DBUS_NEW_INTERFACE ".InterfaceExists"
+#define WPAS_DBUS_ERROR_IFACE_DISABLED \
+ WPAS_DBUS_NEW_INTERFACE ".InterfaceDisabled"
+#define WPAS_DBUS_ERROR_IFACE_UNKNOWN \
+ WPAS_DBUS_NEW_INTERFACE ".InterfaceUnknown"
+
+#define WPAS_DBUS_ERROR_NOT_CONNECTED \
+ WPAS_DBUS_NEW_INTERFACE ".NotConnected"
+#define WPAS_DBUS_ERROR_NETWORK_UNKNOWN \
+ WPAS_DBUS_NEW_INTERFACE ".NetworkUnknown"
+
+#define WPAS_DBUS_ERROR_CONNECT_CHANNEL_UNAVAILABLE \
+ WPAS_DBUS_NEW_INTERFACE ".ConnectChannelUnavailable"
+#define WPAS_DBUS_ERROR_CONNECT_CHANNEL_UNSUPPORTED \
+ WPAS_DBUS_NEW_INTERFACE ".ConnectChannelUnsupported"
+#define WPAS_DBUS_ERROR_CONNECT_UNSPECIFIED_ERROR \
+ WPAS_DBUS_NEW_INTERFACE ".ConnectUnspecifiedError"
+
+#define WPAS_DBUS_ERROR_BLOB_EXISTS \
+ WPAS_DBUS_NEW_INTERFACE ".BlobExists"
+#define WPAS_DBUS_ERROR_BLOB_UNKNOWN \
+ WPAS_DBUS_NEW_INTERFACE ".BlobUnknown"
+
+#define WPAS_DBUS_ERROR_SUBSCRIPTION_IN_USE \
+ WPAS_DBUS_NEW_INTERFACE ".SubscriptionInUse"
+#define WPAS_DBUS_ERROR_NO_SUBSCRIPTION \
+ WPAS_DBUS_NEW_INTERFACE ".NoSubscription"
+#define WPAS_DBUS_ERROR_SUBSCRIPTION_EPERM \
+ WPAS_DBUS_NEW_INTERFACE ".SubscriptionNotYou"
+
+/* Interface-level errors */
+#define WPAS_DBUS_ERROR_IFACE_SCAN_ERROR \
+ WPAS_DBUS_NEW_IFACE_INTERFACE ".ScanError"
+
+void wpas_dbus_subscribe_noc(struct wpas_dbus_priv *priv);
+void wpas_dbus_unsubscribe_noc(struct wpas_dbus_priv *priv);
+
+
+#ifdef CONFIG_CTRL_IFACE_DBUS_NEW
+
+int wpas_dbus_ctrl_iface_init(struct wpas_dbus_priv *priv);
+void wpas_dbus_ctrl_iface_deinit(struct wpas_dbus_priv *iface);
+
+int wpas_dbus_register_interface(struct wpa_supplicant *wpa_s);
+int wpas_dbus_unregister_interface(struct wpa_supplicant *wpa_s);
+void wpas_dbus_signal_prop_changed(struct wpa_supplicant *wpa_s,
+ enum wpas_dbus_prop property);
+void wpas_dbus_bss_signal_prop_changed(struct wpa_supplicant *wpa_s,
+ enum wpas_dbus_bss_prop property,
+ unsigned int id);
+void wpas_dbus_signal_network_enabled_changed(struct wpa_supplicant *wpa_s,
+ struct wpa_ssid *ssid);
+void wpas_dbus_signal_network_selected(struct wpa_supplicant *wpa_s, int id);
+void wpas_dbus_signal_network_request(struct wpa_supplicant *wpa_s,
+ struct wpa_ssid *ssid,
+ enum wpa_ctrl_req_type rtype,
+ const char *default_text);
+void wpas_dbus_signal_scan_done(struct wpa_supplicant *wpa_s, int success);
+void wpas_dbus_signal_wps_cred(struct wpa_supplicant *wpa_s,
+ const struct wps_credential *cred);
+void wpas_dbus_signal_wps_event_m2d(struct wpa_supplicant *wpa_s,
+ struct wps_event_m2d *m2d);
+void wpas_dbus_signal_wps_event_fail(struct wpa_supplicant *wpa_s,
+ struct wps_event_fail *fail);
+void wpas_dbus_signal_wps_event_success(struct wpa_supplicant *wpa_s);
+void wpas_dbus_signal_wps_event_pbc_overlap(struct wpa_supplicant *wpa_s);
+int wpas_dbus_register_network(struct wpa_supplicant *wpa_s,
+ struct wpa_ssid *ssid);
+int wpas_dbus_unregister_network(struct wpa_supplicant *wpa_s, int nid);
+int wpas_dbus_unregister_bss(struct wpa_supplicant *wpa_s,
+ u8 bssid[ETH_ALEN], unsigned int id);
+int wpas_dbus_register_bss(struct wpa_supplicant *wpa_s,
+ u8 bssid[ETH_ALEN], unsigned int id);
+void wpas_dbus_signal_blob_added(struct wpa_supplicant *wpa_s,
+ const char *name);
+void wpas_dbus_signal_blob_removed(struct wpa_supplicant *wpa_s,
+ const char *name);
+void wpas_dbus_signal_debug_level_changed(struct wpa_global *global);
+void wpas_dbus_signal_debug_timestamp_changed(struct wpa_global *global);
+void wpas_dbus_signal_debug_show_keys_changed(struct wpa_global *global);
+
+int wpas_dbus_register_peer(struct wpa_supplicant *wpa_s, const u8 *dev_addr);
+void wpas_dbus_signal_p2p_find_stopped(struct wpa_supplicant *wpa_s);
+void wpas_dbus_signal_peer_device_found(struct wpa_supplicant *wpa_s,
+ const u8 *dev_addr);
+int wpas_dbus_unregister_peer(struct wpa_supplicant *wpa_s,
+ const u8 *dev_addr);
+void wpas_dbus_signal_peer_device_lost(struct wpa_supplicant *wpa_s,
+ const u8 *dev_addr);
+void wpas_dbus_signal_peer_groups_changed(struct wpa_supplicant *wpa_s,
+ const u8 *dev_addr);
+void wpas_dbus_signal_p2p_group_removed(struct wpa_supplicant *wpa_s,
+ const char *role);
+void wpas_dbus_signal_p2p_provision_discovery(struct wpa_supplicant *wpa_s,
+ const u8 *dev_addr, int request,
+ enum p2p_prov_disc_status status,
+ u16 config_methods,
+ unsigned int generated_pin);
+void wpas_dbus_signal_p2p_go_neg_req(struct wpa_supplicant *wpa_s,
+ const u8 *src, u16 dev_passwd_id,
+ u8 go_intent);
+void wpas_dbus_signal_p2p_group_started(struct wpa_supplicant *wpa_s,
+ const struct wpa_ssid *ssid,
+ int client, int network_id);
+void wpas_dbus_signal_p2p_group_formation_failure(struct wpa_supplicant *wpa_s,
+ const char *reason);
+void wpas_dbus_register_p2p_group(struct wpa_supplicant *wpa_s,
+ struct wpa_ssid *ssid);
+void wpas_dbus_signal_p2p_go_neg_resp(struct wpa_supplicant *wpa_s,
+ struct p2p_go_neg_results *res);
+void wpas_dbus_unregister_p2p_group(struct wpa_supplicant *wpa_s,
+ const struct wpa_ssid *ssid);
+int wpas_dbus_register_persistent_group(struct wpa_supplicant *wpa_s,
+ struct wpa_ssid *ssid);
+int wpas_dbus_unregister_persistent_group(struct wpa_supplicant *wpa_s,
+ int nid);
+void wpas_dbus_signal_p2p_invitation_result(struct wpa_supplicant *wpa_s,
+ int status, const u8 *bssid);
+void wpas_dbus_signal_p2p_peer_disconnected(struct wpa_supplicant *wpa_s,
+ const u8 *member);
+void wpas_dbus_signal_p2p_sd_request(struct wpa_supplicant *wpa_s,
+ int freq, const u8 *sa, u8 dialog_token,
+ u16 update_indic, const u8 *tlvs,
+ size_t tlvs_len);
+void wpas_dbus_signal_p2p_sd_response(struct wpa_supplicant *wpa_s,
+ const u8 *sa, u16 update_indic,
+ const u8 *tlvs, size_t tlvs_len);
+void wpas_dbus_signal_p2p_peer_joined(struct wpa_supplicant *wpa_s,
+ const u8 *member);
+void wpas_dbus_signal_p2p_wps_failed(struct wpa_supplicant *wpa_s,
+ struct wps_event_fail *fail);
+void wpas_dbus_signal_certification(struct wpa_supplicant *wpa_s,
+ int depth, const char *subject,
+ const char *altsubject[],
+ int num_altsubject,
+ const char *cert_hash,
+ const struct wpabuf *cert);
+void wpas_dbus_signal_preq(struct wpa_supplicant *wpa_s,
+ const u8 *addr, const u8 *dst, const u8 *bssid,
+ const u8 *ie, size_t ie_len, u32 ssi_signal);
+void wpas_dbus_signal_eap_status(struct wpa_supplicant *wpa_s,
+ const char *status, const char *parameter);
+void wpas_dbus_signal_sta_authorized(struct wpa_supplicant *wpa_s,
+ const u8 *sta);
+void wpas_dbus_signal_sta_deauthorized(struct wpa_supplicant *wpa_s,
+ const u8 *sta);
+void wpas_dbus_signal_p2p_invitation_received(struct wpa_supplicant *wpa_s,
+ const u8 *sa, const u8 *dev_addr,
+ const u8 *bssid, int id,
+ int op_freq);
+
+#else /* CONFIG_CTRL_IFACE_DBUS_NEW */
+
+static inline int wpas_dbus_register_interface(struct wpa_supplicant *wpa_s)
+{
+ return 0;
+}
+
+static inline int wpas_dbus_unregister_interface(struct wpa_supplicant *wpa_s)
+{
+ return 0;
+}
+
+#define wpas_dbus_signal_state_changed(w, n, o) do { } while (0)
+
+static inline void wpas_dbus_signal_prop_changed(struct wpa_supplicant *wpa_s,
+ enum wpas_dbus_prop property)
+{
+}
+
+static inline void wpas_dbus_bss_signal_prop_changed(
+ struct wpa_supplicant *wpa_s, enum wpas_dbus_bss_prop property,
+ unsigned int id)
+{
+}
+
+static inline void wpas_dbus_signal_network_enabled_changed(
+ struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid)
+{
+}
+
+static inline void wpas_dbus_signal_network_selected(
+ struct wpa_supplicant *wpa_s, int id)
+{
+}
+
+static inline void wpas_dbus_signal_network_request(
+ struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid,
+ enum wpa_ctrl_req_type rtype, const char *default_txt)
+{
+}
+
+static inline void wpas_dbus_signal_scan_done(struct wpa_supplicant *wpa_s,
+ int success)
+{
+}
+
+static inline void wpas_dbus_signal_wps_cred(struct wpa_supplicant *wpa_s,
+ const struct wps_credential *cred)
+{
+}
+
+static inline void wpas_dbus_signal_wps_event_m2d(struct wpa_supplicant *wpa_s,
+ struct wps_event_m2d *m2d)
+{
+}
+
+static inline void wpas_dbus_signal_wps_event_fail(
+ struct wpa_supplicant *wpa_s, struct wps_event_fail *fail)
+{
+}
+
+static inline void wpas_dbus_signal_wps_event_success(
+ struct wpa_supplicant *wpa_s)
+{
+}
+
+static inline void wpas_dbus_signal_wps_event_pbc_overlap(
+ struct wpa_supplicant *wpa_s)
+{
+}
+
+static inline int wpas_dbus_register_network(struct wpa_supplicant *wpa_s,
+ struct wpa_ssid *ssid)
+{
+ return 0;
+}
+
+static inline int wpas_dbus_unregister_network(struct wpa_supplicant *wpa_s,
+ int nid)
+{
+ return 0;
+}
+
+static inline int wpas_dbus_unregister_bss(struct wpa_supplicant *wpa_s,
+ u8 bssid[ETH_ALEN], unsigned int id)
+{
+ return 0;
+}
+
+static inline int wpas_dbus_register_bss(struct wpa_supplicant *wpa_s,
+ u8 bssid[ETH_ALEN], unsigned int id)
+{
+ return 0;
+}
+
+static inline void wpas_dbus_signal_blob_added(struct wpa_supplicant *wpa_s,
+ const char *name)
+{
+}
+
+static inline void wpas_dbus_signal_blob_removed(struct wpa_supplicant *wpa_s,
+ const char *name)
+{
+}
+
+static inline void wpas_dbus_signal_debug_level_changed(
+ struct wpa_global *global)
+{
+}
+
+static inline void wpas_dbus_signal_debug_timestamp_changed(
+ struct wpa_global *global)
+{
+}
+
+static inline void wpas_dbus_signal_debug_show_keys_changed(
+ struct wpa_global *global)
+{
+}
+
+static inline int wpas_dbus_register_peer(struct wpa_supplicant *wpa_s,
+ const u8 *dev_addr)
+{
+ return 0;
+}
+
+static inline int wpas_dbus_unregister_peer(struct wpa_supplicant *wpa_s,
+ const u8 *dev_addr)
+{
+ return 0;
+}
+
+static inline void
+wpas_dbus_signal_peer_groups_changed(struct wpa_supplicant *wpa_s,
+ const u8 *dev_addr)
+{
+}
+
+static inline void
+wpas_dbus_signal_p2p_group_removed(struct wpa_supplicant *wpa_s,
+ const char *role)
+{
+}
+
+static inline void
+wpas_dbus_signal_p2p_provision_discovery(struct wpa_supplicant *wpa_s,
+ const u8 *dev_addr, int request,
+ enum p2p_prov_disc_status status,
+ u16 config_methods,
+ unsigned int generated_pin)
+{
+}
+
+static inline void wpas_dbus_signal_p2p_go_neg_req(struct wpa_supplicant *wpa_s,
+ const u8 *src,
+ u16 dev_passwd_id,
+ u8 go_intent)
+{
+}
+
+static inline void
+wpas_dbus_signal_p2p_group_started(struct wpa_supplicant *wpa_s,
+ const struct wpa_ssid *ssid,
+ int client, int network_id)
+{
+}
+
+static inline void
+wpas_dbus_signal_p2p_group_formation_failure(struct wpa_supplicant *wpa_s,
+ const char *reason)
+{
+}
+
+static inline void
+wpas_dbus_register_p2p_group(struct wpa_supplicant *wpa_s,
+ struct wpa_ssid *ssid)
+{
+}
+
+static inline int wpas_dbus_register_persistent_group(
+ struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid)
+{
+ return 0;
+}
+
+static inline int wpas_dbus_unregister_persistent_group(
+ struct wpa_supplicant *wpa_s, int nid)
+{
+ return 0;
+}
+
+static inline void
+wpas_dbus_signal_p2p_go_neg_resp(struct wpa_supplicant *wpa_s,
+ struct p2p_go_neg_results *res)
+{
+}
+
+static inline void
+wpas_dbus_unregister_p2p_group(struct wpa_supplicant *wpa_s,
+ const struct wpa_ssid *ssid)
+{
+}
+
+static inline void wpas_dbus_signal_p2p_invitation_result(
+ struct wpa_supplicant *wpa_s, int status,
+ const u8 *bssid)
+{
+}
+
+static inline void
+wpas_dbus_register_p2p_groupmember(struct wpa_supplicant *wpa_s,
+ const u8 *p2p_if_addr)
+{
+}
+
+static inline void
+wpas_dbus_signal_p2p_sd_request(struct wpa_supplicant *wpa_s, int freq,
+ const u8 *sa, u8 dialog_token, u16 update_indic,
+ const u8 *tlvs, size_t tlvs_len)
+{
+}
+
+static inline void
+wpas_dbus_signal_p2p_sd_response(struct wpa_supplicant *wpa_s,
+ const u8 *sa, u16 update_indic,
+ const u8 *tlvs, size_t tlvs_len)
+{
+}
+
+static inline void
+wpas_dbus_unregister_p2p_groupmember(struct wpa_supplicant *wpa_s,
+ const u8 *p2p_if_addr)
+{
+}
+
+static inline void
+wpas_dbus_signal_p2p_peer_joined(struct wpa_supplicant *wpa_s,
+ const u8 *member)
+{
+}
+
+static inline void
+wpas_dbus_signal_p2p_find_stopped(struct wpa_supplicant *wpa_s)
+{
+}
+
+static inline void
+wpas_dbus_signal_peer_device_found(struct wpa_supplicant *wpa_s,
+ const u8 *dev_addr)
+{
+}
+
+static inline void
+wpas_dbus_signal_peer_device_lost(struct wpa_supplicant *wpa_s,
+ const u8 *dev_addr)
+{
+}
+
+static inline void
+wpas_dbus_signal_p2p_peer_disconnected(struct wpa_supplicant *wpa_s,
+ const u8 *member)
+{
+}
+
+static inline void
+wpas_dbus_signal_p2p_wps_failed(struct wpa_supplicant *wpa_s,
+ struct wps_event_fail *fail)
+{
+}
+
+static inline void wpas_dbus_signal_certification(struct wpa_supplicant *wpa_s,
+ int depth,
+ const char *subject,
+ const char *altsubject[],
+ int num_altsubject,
+ const char *cert_hash,
+ const struct wpabuf *cert)
+{
+}
+
+static inline void wpas_dbus_signal_preq(struct wpa_supplicant *wpa_s,
+ const u8 *addr, const u8 *dst,
+ const u8 *bssid,
+ const u8 *ie, size_t ie_len,
+ u32 ssi_signal)
+{
+}
+
+static inline void wpas_dbus_signal_eap_status(struct wpa_supplicant *wpa_s,
+ const char *status,
+ const char *parameter)
+{
+}
+
+static inline
+void wpas_dbus_signal_sta_authorized(struct wpa_supplicant *wpa_s,
+ const u8 *sta)
+{
+}
+
+static inline
+void wpas_dbus_signal_sta_deauthorized(struct wpa_supplicant *wpa_s,
+ const u8 *sta)
+{
+}
+
+static inline
+void wpas_dbus_signal_p2p_invitation_received(struct wpa_supplicant *wpa_s,
+ const u8 *sa, const u8 *dev_addr,
+ const u8 *bssid, int id,
+ int op_freq)
+{
+}
+
+#endif /* CONFIG_CTRL_IFACE_DBUS_NEW */
+
+#endif /* CTRL_IFACE_DBUS_H_NEW */
diff --git a/freebsd/contrib/wpa/wpa_supplicant/dbus/dbus_old.h b/freebsd/contrib/wpa/wpa_supplicant/dbus/dbus_old.h
new file mode 100644
index 00000000..451a9f82
--- /dev/null
+++ b/freebsd/contrib/wpa/wpa_supplicant/dbus/dbus_old.h
@@ -0,0 +1,142 @@
+/*
+ * WPA Supplicant / dbus-based control interface
+ * Copyright (c) 2006, Dan Williams <dcbw@redhat.com> and Red Hat, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef CTRL_IFACE_DBUS_H
+#define CTRL_IFACE_DBUS_H
+
+struct wps_credential;
+
+#ifdef CONFIG_CTRL_IFACE_DBUS
+
+#define WPAS_DBUS_OBJECT_PATH_MAX 150
+
+#define WPAS_DBUS_SERVICE "fi.epitest.hostap.WPASupplicant"
+#define WPAS_DBUS_PATH "/fi/epitest/hostap/WPASupplicant"
+#define WPAS_DBUS_INTERFACE "fi.epitest.hostap.WPASupplicant"
+
+#define WPAS_DBUS_PATH_INTERFACES WPAS_DBUS_PATH "/Interfaces"
+#define WPAS_DBUS_IFACE_INTERFACE WPAS_DBUS_INTERFACE ".Interface"
+
+#define WPAS_DBUS_NETWORKS_PART "Networks"
+#define WPAS_DBUS_IFACE_NETWORK WPAS_DBUS_INTERFACE ".Network"
+
+#define WPAS_DBUS_BSSIDS_PART "BSSIDs"
+#define WPAS_DBUS_IFACE_BSSID WPAS_DBUS_INTERFACE ".BSSID"
+
+
+/* Errors */
+#define WPAS_ERROR_INVALID_NETWORK \
+ WPAS_DBUS_IFACE_INTERFACE ".InvalidNetwork"
+#define WPAS_ERROR_INVALID_BSSID \
+ WPAS_DBUS_IFACE_INTERFACE ".InvalidBSSID"
+
+#define WPAS_ERROR_INVALID_OPTS \
+ WPAS_DBUS_INTERFACE ".InvalidOptions"
+#define WPAS_ERROR_INVALID_IFACE \
+ WPAS_DBUS_INTERFACE ".InvalidInterface"
+
+#define WPAS_ERROR_ADD_ERROR \
+ WPAS_DBUS_INTERFACE ".AddError"
+#define WPAS_ERROR_EXISTS_ERROR \
+ WPAS_DBUS_INTERFACE ".ExistsError"
+#define WPAS_ERROR_REMOVE_ERROR \
+ WPAS_DBUS_INTERFACE ".RemoveError"
+
+#define WPAS_ERROR_SCAN_ERROR \
+ WPAS_DBUS_IFACE_INTERFACE ".ScanError"
+#define WPAS_ERROR_ADD_NETWORK_ERROR \
+ WPAS_DBUS_IFACE_INTERFACE ".AddNetworkError"
+#define WPAS_ERROR_INTERNAL_ERROR \
+ WPAS_DBUS_IFACE_INTERFACE ".InternalError"
+#define WPAS_ERROR_REMOVE_NETWORK_ERROR \
+ WPAS_DBUS_IFACE_INTERFACE ".RemoveNetworkError"
+
+#define WPAS_ERROR_WPS_PBC_ERROR \
+ WPAS_DBUS_IFACE_INTERFACE ".WpsPbcError"
+#define WPAS_ERROR_WPS_PIN_ERROR \
+ WPAS_DBUS_IFACE_INTERFACE ".WpsPinError"
+#define WPAS_ERROR_WPS_REG_ERROR \
+ WPAS_DBUS_IFACE_INTERFACE ".WpsRegError"
+
+#define WPAS_DBUS_BSSID_FORMAT "%02x%02x%02x%02x%02x%02x"
+
+struct wpa_global;
+struct wpa_supplicant;
+
+int wpa_supplicant_dbus_ctrl_iface_init(struct wpas_dbus_priv *iface);
+void wpa_supplicant_dbus_notify_scan_results(struct wpa_supplicant *wpa_s);
+void wpa_supplicant_dbus_notify_scanning(struct wpa_supplicant *wpa_s);
+void wpa_supplicant_dbus_notify_state_change(struct wpa_supplicant *wpa_s,
+ enum wpa_states new_state,
+ enum wpa_states old_state);
+void wpa_supplicant_dbus_notify_wps_cred(struct wpa_supplicant *wpa_s,
+ const struct wps_credential *cred);
+void wpa_supplicant_dbus_notify_certification(struct wpa_supplicant *wpa_s,
+ int depth, const char *subject,
+ const char *cert_hash,
+ const struct wpabuf *cert);
+
+char * wpas_dbus_decompose_object_path(const char *path, char **network,
+ char **bssid);
+
+int wpas_dbus_register_iface(struct wpa_supplicant *wpa_s);
+int wpas_dbus_unregister_iface(struct wpa_supplicant *wpa_s);
+
+
+/* Methods internal to the dbus control interface */
+struct wpa_supplicant * wpa_supplicant_get_iface_by_dbus_path(
+ struct wpa_global *global, const char *path);
+
+#else /* CONFIG_CTRL_IFACE_DBUS */
+
+static inline void
+wpa_supplicant_dbus_notify_scan_results(struct wpa_supplicant *wpa_s)
+{
+}
+
+static inline void
+wpa_supplicant_dbus_notify_scanning(struct wpa_supplicant *wpa_s)
+{
+}
+
+static inline void
+wpa_supplicant_dbus_notify_state_change(struct wpa_supplicant *wpa_s,
+ enum wpa_states new_state,
+ enum wpa_states old_state)
+{
+}
+
+static inline void
+wpa_supplicant_dbus_notify_wps_cred(struct wpa_supplicant *wpa_s,
+ const struct wps_credential *cred)
+{
+}
+
+static inline void
+wpa_supplicant_dbus_notify_certification(struct wpa_supplicant *wpa_s,
+ int depth, const char *subject,
+ const char *cert_hash,
+ const struct wpabuf *cert)
+{
+}
+
+static inline int
+wpas_dbus_register_iface(struct wpa_supplicant *wpa_s)
+{
+ return 0;
+}
+
+static inline int
+wpas_dbus_unregister_iface(struct wpa_supplicant *wpa_s)
+{
+ return 0;
+}
+
+#endif /* CONFIG_CTRL_IFACE_DBUS */
+
+#endif /* CTRL_IFACE_DBUS_H */
diff --git a/freebsd/contrib/wpa/wpa_supplicant/driver_i.h b/freebsd/contrib/wpa/wpa_supplicant/driver_i.h
new file mode 100644
index 00000000..73768c75
--- /dev/null
+++ b/freebsd/contrib/wpa/wpa_supplicant/driver_i.h
@@ -0,0 +1,915 @@
+/*
+ * wpa_supplicant - Internal driver interface wrappers
+ * Copyright (c) 2003-2015, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef DRIVER_I_H
+#define DRIVER_I_H
+
+#include "drivers/driver.h"
+
+/* driver_ops */
+static inline void * wpa_drv_init(struct wpa_supplicant *wpa_s,
+ const char *ifname)
+{
+ if (wpa_s->driver->init2)
+ return wpa_s->driver->init2(wpa_s, ifname,
+ wpa_s->global_drv_priv);
+ if (wpa_s->driver->init) {
+ return wpa_s->driver->init(wpa_s, ifname);
+ }
+ return NULL;
+}
+
+static inline void wpa_drv_deinit(struct wpa_supplicant *wpa_s)
+{
+ if (wpa_s->driver->deinit)
+ wpa_s->driver->deinit(wpa_s->drv_priv);
+}
+
+static inline int wpa_drv_set_param(struct wpa_supplicant *wpa_s,
+ const char *param)
+{
+ if (wpa_s->driver->set_param)
+ return wpa_s->driver->set_param(wpa_s->drv_priv, param);
+ return 0;
+}
+
+static inline int wpa_drv_set_countermeasures(struct wpa_supplicant *wpa_s,
+ int enabled)
+{
+ if (wpa_s->driver->set_countermeasures) {
+ return wpa_s->driver->set_countermeasures(wpa_s->drv_priv,
+ enabled);
+ }
+ return -1;
+}
+
+static inline int wpa_drv_authenticate(struct wpa_supplicant *wpa_s,
+ struct wpa_driver_auth_params *params)
+{
+ if (wpa_s->driver->authenticate)
+ return wpa_s->driver->authenticate(wpa_s->drv_priv, params);
+ return -1;
+}
+
+static inline int wpa_drv_associate(struct wpa_supplicant *wpa_s,
+ struct wpa_driver_associate_params *params)
+{
+ if (wpa_s->driver->associate) {
+ return wpa_s->driver->associate(wpa_s->drv_priv, params);
+ }
+ return -1;
+}
+
+static inline int wpa_drv_init_mesh(struct wpa_supplicant *wpa_s)
+{
+ if (wpa_s->driver->init_mesh)
+ return wpa_s->driver->init_mesh(wpa_s->drv_priv);
+ return -1;
+}
+
+static inline int wpa_drv_join_mesh(struct wpa_supplicant *wpa_s,
+ struct wpa_driver_mesh_join_params *params)
+{
+ if (wpa_s->driver->join_mesh)
+ return wpa_s->driver->join_mesh(wpa_s->drv_priv, params);
+ return -1;
+}
+
+static inline int wpa_drv_leave_mesh(struct wpa_supplicant *wpa_s)
+{
+ if (wpa_s->driver->leave_mesh)
+ return wpa_s->driver->leave_mesh(wpa_s->drv_priv);
+ return -1;
+}
+
+static inline int wpa_drv_scan(struct wpa_supplicant *wpa_s,
+ struct wpa_driver_scan_params *params)
+{
+#ifdef CONFIG_TESTING_OPTIONS
+ if (wpa_s->test_failure == WPAS_TEST_FAILURE_SCAN_TRIGGER)
+ return -EBUSY;
+#endif /* CONFIG_TESTING_OPTIONS */
+ if (wpa_s->driver->scan2)
+ return wpa_s->driver->scan2(wpa_s->drv_priv, params);
+ return -1;
+}
+
+static inline int wpa_drv_sched_scan(struct wpa_supplicant *wpa_s,
+ struct wpa_driver_scan_params *params,
+ u32 interval)
+{
+ if (wpa_s->driver->sched_scan)
+ return wpa_s->driver->sched_scan(wpa_s->drv_priv,
+ params, interval);
+ return -1;
+}
+
+static inline int wpa_drv_stop_sched_scan(struct wpa_supplicant *wpa_s)
+{
+ if (wpa_s->driver->stop_sched_scan)
+ return wpa_s->driver->stop_sched_scan(wpa_s->drv_priv);
+ return -1;
+}
+
+static inline struct wpa_scan_results * wpa_drv_get_scan_results2(
+ struct wpa_supplicant *wpa_s)
+{
+ if (wpa_s->driver->get_scan_results2)
+ return wpa_s->driver->get_scan_results2(wpa_s->drv_priv);
+ return NULL;
+}
+
+static inline int wpa_drv_get_bssid(struct wpa_supplicant *wpa_s, u8 *bssid)
+{
+ if (wpa_s->driver->get_bssid) {
+ return wpa_s->driver->get_bssid(wpa_s->drv_priv, bssid);
+ }
+ return -1;
+}
+
+static inline int wpa_drv_get_ssid(struct wpa_supplicant *wpa_s, u8 *ssid)
+{
+ if (wpa_s->driver->get_ssid) {
+ return wpa_s->driver->get_ssid(wpa_s->drv_priv, ssid);
+ }
+ return -1;
+}
+
+static inline int wpa_drv_set_key(struct wpa_supplicant *wpa_s,
+ enum wpa_alg alg, const u8 *addr,
+ int key_idx, int set_tx,
+ const u8 *seq, size_t seq_len,
+ const u8 *key, size_t key_len)
+{
+ if (alg != WPA_ALG_NONE) {
+ if (key_idx >= 0 && key_idx <= 6)
+ wpa_s->keys_cleared &= ~BIT(key_idx);
+ else
+ wpa_s->keys_cleared = 0;
+ }
+ if (wpa_s->driver->set_key) {
+ return wpa_s->driver->set_key(wpa_s->ifname, wpa_s->drv_priv,
+ alg, addr, key_idx, set_tx,
+ seq, seq_len, key, key_len);
+ }
+ return -1;
+}
+
+static inline int wpa_drv_sta_deauth(struct wpa_supplicant *wpa_s,
+ const u8 *addr, int reason_code)
+{
+ if (wpa_s->driver->sta_deauth) {
+ return wpa_s->driver->sta_deauth(wpa_s->drv_priv,
+ wpa_s->own_addr, addr,
+ reason_code);
+ }
+ return -1;
+}
+
+static inline int wpa_drv_deauthenticate(struct wpa_supplicant *wpa_s,
+ const u8 *addr, int reason_code)
+{
+ if (wpa_s->driver->deauthenticate) {
+ return wpa_s->driver->deauthenticate(wpa_s->drv_priv, addr,
+ reason_code);
+ }
+ return -1;
+}
+
+static inline int wpa_drv_add_pmkid(struct wpa_supplicant *wpa_s,
+ const u8 *bssid, const u8 *pmkid)
+{
+ if (wpa_s->driver->add_pmkid) {
+ return wpa_s->driver->add_pmkid(wpa_s->drv_priv, bssid, pmkid);
+ }
+ return -1;
+}
+
+static inline int wpa_drv_remove_pmkid(struct wpa_supplicant *wpa_s,
+ const u8 *bssid, const u8 *pmkid)
+{
+ if (wpa_s->driver->remove_pmkid) {
+ return wpa_s->driver->remove_pmkid(wpa_s->drv_priv, bssid,
+ pmkid);
+ }
+ return -1;
+}
+
+static inline int wpa_drv_flush_pmkid(struct wpa_supplicant *wpa_s)
+{
+ if (wpa_s->driver->flush_pmkid) {
+ return wpa_s->driver->flush_pmkid(wpa_s->drv_priv);
+ }
+ return -1;
+}
+
+static inline int wpa_drv_get_capa(struct wpa_supplicant *wpa_s,
+ struct wpa_driver_capa *capa)
+{
+ if (wpa_s->driver->get_capa) {
+ return wpa_s->driver->get_capa(wpa_s->drv_priv, capa);
+ }
+ return -1;
+}
+
+static inline void wpa_drv_poll(struct wpa_supplicant *wpa_s)
+{
+ if (wpa_s->driver->poll) {
+ wpa_s->driver->poll(wpa_s->drv_priv);
+ }
+}
+
+static inline const char * wpa_drv_get_ifname(struct wpa_supplicant *wpa_s)
+{
+ if (wpa_s->driver->get_ifname) {
+ return wpa_s->driver->get_ifname(wpa_s->drv_priv);
+ }
+ return NULL;
+}
+
+static inline const char *
+wpa_driver_get_radio_name(struct wpa_supplicant *wpa_s)
+{
+ if (wpa_s->driver->get_radio_name)
+ return wpa_s->driver->get_radio_name(wpa_s->drv_priv);
+ return NULL;
+}
+
+static inline const u8 * wpa_drv_get_mac_addr(struct wpa_supplicant *wpa_s)
+{
+ if (wpa_s->driver->get_mac_addr) {
+ return wpa_s->driver->get_mac_addr(wpa_s->drv_priv);
+ }
+ return NULL;
+}
+
+static inline int wpa_drv_set_operstate(struct wpa_supplicant *wpa_s,
+ int state)
+{
+ if (wpa_s->driver->set_operstate)
+ return wpa_s->driver->set_operstate(wpa_s->drv_priv, state);
+ return 0;
+}
+
+static inline int wpa_drv_mlme_setprotection(struct wpa_supplicant *wpa_s,
+ const u8 *addr, int protect_type,
+ int key_type)
+{
+ if (wpa_s->driver->mlme_setprotection)
+ return wpa_s->driver->mlme_setprotection(wpa_s->drv_priv, addr,
+ protect_type,
+ key_type);
+ return 0;
+}
+
+static inline struct hostapd_hw_modes *
+wpa_drv_get_hw_feature_data(struct wpa_supplicant *wpa_s, u16 *num_modes,
+ u16 *flags)
+{
+ if (wpa_s->driver->get_hw_feature_data)
+ return wpa_s->driver->get_hw_feature_data(wpa_s->drv_priv,
+ num_modes, flags);
+ return NULL;
+}
+
+static inline int wpa_drv_set_country(struct wpa_supplicant *wpa_s,
+ const char *alpha2)
+{
+ if (wpa_s->driver->set_country)
+ return wpa_s->driver->set_country(wpa_s->drv_priv, alpha2);
+ return 0;
+}
+
+static inline int wpa_drv_send_mlme(struct wpa_supplicant *wpa_s,
+ const u8 *data, size_t data_len, int noack,
+ unsigned int freq)
+{
+ if (wpa_s->driver->send_mlme)
+ return wpa_s->driver->send_mlme(wpa_s->drv_priv,
+ data, data_len, noack,
+ freq);
+ return -1;
+}
+
+static inline int wpa_drv_update_ft_ies(struct wpa_supplicant *wpa_s,
+ const u8 *md,
+ const u8 *ies, size_t ies_len)
+{
+ if (wpa_s->driver->update_ft_ies)
+ return wpa_s->driver->update_ft_ies(wpa_s->drv_priv, md,
+ ies, ies_len);
+ return -1;
+}
+
+static inline int wpa_drv_set_ap(struct wpa_supplicant *wpa_s,
+ struct wpa_driver_ap_params *params)
+{
+ if (wpa_s->driver->set_ap)
+ return wpa_s->driver->set_ap(wpa_s->drv_priv, params);
+ return -1;
+}
+
+static inline int wpa_drv_sta_add(struct wpa_supplicant *wpa_s,
+ struct hostapd_sta_add_params *params)
+{
+ if (wpa_s->driver->sta_add)
+ return wpa_s->driver->sta_add(wpa_s->drv_priv, params);
+ return -1;
+}
+
+static inline int wpa_drv_sta_remove(struct wpa_supplicant *wpa_s,
+ const u8 *addr)
+{
+ if (wpa_s->driver->sta_remove)
+ return wpa_s->driver->sta_remove(wpa_s->drv_priv, addr);
+ return -1;
+}
+
+static inline int wpa_drv_hapd_send_eapol(struct wpa_supplicant *wpa_s,
+ const u8 *addr, const u8 *data,
+ size_t data_len, int encrypt,
+ const u8 *own_addr, u32 flags)
+{
+ if (wpa_s->driver->hapd_send_eapol)
+ return wpa_s->driver->hapd_send_eapol(wpa_s->drv_priv, addr,
+ data, data_len, encrypt,
+ own_addr, flags);
+ return -1;
+}
+
+static inline int wpa_drv_sta_set_flags(struct wpa_supplicant *wpa_s,
+ const u8 *addr, int total_flags,
+ int flags_or, int flags_and)
+{
+ if (wpa_s->driver->sta_set_flags)
+ return wpa_s->driver->sta_set_flags(wpa_s->drv_priv, addr,
+ total_flags, flags_or,
+ flags_and);
+ return -1;
+}
+
+static inline int wpa_drv_set_supp_port(struct wpa_supplicant *wpa_s,
+ int authorized)
+{
+ if (wpa_s->driver->set_supp_port) {
+ return wpa_s->driver->set_supp_port(wpa_s->drv_priv,
+ authorized);
+ }
+ return 0;
+}
+
+static inline int wpa_drv_send_action(struct wpa_supplicant *wpa_s,
+ unsigned int freq,
+ unsigned int wait,
+ const u8 *dst, const u8 *src,
+ const u8 *bssid,
+ const u8 *data, size_t data_len,
+ int no_cck)
+{
+ if (wpa_s->driver->send_action)
+ return wpa_s->driver->send_action(wpa_s->drv_priv, freq,
+ wait, dst, src, bssid,
+ data, data_len, no_cck);
+ return -1;
+}
+
+static inline void wpa_drv_send_action_cancel_wait(struct wpa_supplicant *wpa_s)
+{
+ if (wpa_s->driver->send_action_cancel_wait)
+ wpa_s->driver->send_action_cancel_wait(wpa_s->drv_priv);
+}
+
+static inline int wpa_drv_set_freq(struct wpa_supplicant *wpa_s,
+ struct hostapd_freq_params *freq)
+{
+ if (wpa_s->driver->set_freq)
+ return wpa_s->driver->set_freq(wpa_s->drv_priv, freq);
+ return -1;
+}
+
+static inline int wpa_drv_if_add(struct wpa_supplicant *wpa_s,
+ enum wpa_driver_if_type type,
+ const char *ifname, const u8 *addr,
+ void *bss_ctx, char *force_ifname,
+ u8 *if_addr, const char *bridge)
+{
+ if (wpa_s->driver->if_add)
+ return wpa_s->driver->if_add(wpa_s->drv_priv, type, ifname,
+ addr, bss_ctx, NULL, force_ifname,
+ if_addr, bridge, 0);
+ return -1;
+}
+
+static inline int wpa_drv_if_remove(struct wpa_supplicant *wpa_s,
+ enum wpa_driver_if_type type,
+ const char *ifname)
+{
+ if (wpa_s->driver->if_remove)
+ return wpa_s->driver->if_remove(wpa_s->drv_priv, type, ifname);
+ return -1;
+}
+
+static inline int wpa_drv_remain_on_channel(struct wpa_supplicant *wpa_s,
+ unsigned int freq,
+ unsigned int duration)
+{
+ if (wpa_s->driver->remain_on_channel)
+ return wpa_s->driver->remain_on_channel(wpa_s->drv_priv, freq,
+ duration);
+ return -1;
+}
+
+static inline int wpa_drv_cancel_remain_on_channel(
+ struct wpa_supplicant *wpa_s)
+{
+ if (wpa_s->driver->cancel_remain_on_channel)
+ return wpa_s->driver->cancel_remain_on_channel(
+ wpa_s->drv_priv);
+ return -1;
+}
+
+static inline int wpa_drv_probe_req_report(struct wpa_supplicant *wpa_s,
+ int report)
+{
+ if (wpa_s->driver->probe_req_report)
+ return wpa_s->driver->probe_req_report(wpa_s->drv_priv,
+ report);
+ return -1;
+}
+
+static inline int wpa_drv_deinit_ap(struct wpa_supplicant *wpa_s)
+{
+ if (wpa_s->driver->deinit_ap)
+ return wpa_s->driver->deinit_ap(wpa_s->drv_priv);
+ return 0;
+}
+
+static inline int wpa_drv_deinit_p2p_cli(struct wpa_supplicant *wpa_s)
+{
+ if (wpa_s->driver->deinit_p2p_cli)
+ return wpa_s->driver->deinit_p2p_cli(wpa_s->drv_priv);
+ return 0;
+}
+
+static inline void wpa_drv_suspend(struct wpa_supplicant *wpa_s)
+{
+ if (wpa_s->driver->suspend)
+ wpa_s->driver->suspend(wpa_s->drv_priv);
+}
+
+static inline void wpa_drv_resume(struct wpa_supplicant *wpa_s)
+{
+ if (wpa_s->driver->resume)
+ wpa_s->driver->resume(wpa_s->drv_priv);
+}
+
+static inline int wpa_drv_signal_monitor(struct wpa_supplicant *wpa_s,
+ int threshold, int hysteresis)
+{
+ if (wpa_s->driver->signal_monitor)
+ return wpa_s->driver->signal_monitor(wpa_s->drv_priv,
+ threshold, hysteresis);
+ return -1;
+}
+
+static inline int wpa_drv_signal_poll(struct wpa_supplicant *wpa_s,
+ struct wpa_signal_info *si)
+{
+ if (wpa_s->driver->signal_poll)
+ return wpa_s->driver->signal_poll(wpa_s->drv_priv, si);
+ return -1;
+}
+
+static inline int wpa_drv_pktcnt_poll(struct wpa_supplicant *wpa_s,
+ struct hostap_sta_driver_data *sta)
+{
+ if (wpa_s->driver->read_sta_data)
+ return wpa_s->driver->read_sta_data(wpa_s->drv_priv, sta,
+ wpa_s->bssid);
+ return -1;
+}
+
+static inline int wpa_drv_set_ap_wps_ie(struct wpa_supplicant *wpa_s,
+ const struct wpabuf *beacon,
+ const struct wpabuf *proberesp,
+ const struct wpabuf *assocresp)
+{
+ if (!wpa_s->driver->set_ap_wps_ie)
+ return -1;
+ return wpa_s->driver->set_ap_wps_ie(wpa_s->drv_priv, beacon,
+ proberesp, assocresp);
+}
+
+static inline int wpa_drv_get_noa(struct wpa_supplicant *wpa_s,
+ u8 *buf, size_t buf_len)
+{
+ if (!wpa_s->driver->get_noa)
+ return -1;
+ return wpa_s->driver->get_noa(wpa_s->drv_priv, buf, buf_len);
+}
+
+static inline int wpa_drv_set_p2p_powersave(struct wpa_supplicant *wpa_s,
+ int legacy_ps, int opp_ps,
+ int ctwindow)
+{
+ if (!wpa_s->driver->set_p2p_powersave)
+ return -1;
+ return wpa_s->driver->set_p2p_powersave(wpa_s->drv_priv, legacy_ps,
+ opp_ps, ctwindow);
+}
+
+static inline int wpa_drv_ampdu(struct wpa_supplicant *wpa_s, int ampdu)
+{
+ if (!wpa_s->driver->ampdu)
+ return -1;
+ return wpa_s->driver->ampdu(wpa_s->drv_priv, ampdu);
+}
+
+static inline int wpa_drv_send_tdls_mgmt(struct wpa_supplicant *wpa_s,
+ const u8 *dst, u8 action_code,
+ u8 dialog_token, u16 status_code,
+ u32 peer_capab, int initiator,
+ const u8 *buf, size_t len)
+{
+ if (wpa_s->driver->send_tdls_mgmt) {
+ return wpa_s->driver->send_tdls_mgmt(wpa_s->drv_priv, dst,
+ action_code, dialog_token,
+ status_code, peer_capab,
+ initiator, buf, len);
+ }
+ return -1;
+}
+
+static inline int wpa_drv_tdls_oper(struct wpa_supplicant *wpa_s,
+ enum tdls_oper oper, const u8 *peer)
+{
+ if (!wpa_s->driver->tdls_oper)
+ return -1;
+ return wpa_s->driver->tdls_oper(wpa_s->drv_priv, oper, peer);
+}
+
+#ifdef ANDROID
+static inline int wpa_drv_driver_cmd(struct wpa_supplicant *wpa_s,
+ char *cmd, char *buf, size_t buf_len)
+{
+ if (!wpa_s->driver->driver_cmd)
+ return -1;
+ return wpa_s->driver->driver_cmd(wpa_s->drv_priv, cmd, buf, buf_len);
+}
+#endif /* ANDROID */
+
+static inline void wpa_drv_set_rekey_info(struct wpa_supplicant *wpa_s,
+ const u8 *kek, size_t kek_len,
+ const u8 *kck, size_t kck_len,
+ const u8 *replay_ctr)
+{
+ if (!wpa_s->driver->set_rekey_info)
+ return;
+ wpa_s->driver->set_rekey_info(wpa_s->drv_priv, kek, kek_len,
+ kck, kck_len, replay_ctr);
+}
+
+static inline int wpa_drv_radio_disable(struct wpa_supplicant *wpa_s,
+ int disabled)
+{
+ if (!wpa_s->driver->radio_disable)
+ return -1;
+ return wpa_s->driver->radio_disable(wpa_s->drv_priv, disabled);
+}
+
+static inline int wpa_drv_switch_channel(struct wpa_supplicant *wpa_s,
+ struct csa_settings *settings)
+{
+ if (!wpa_s->driver->switch_channel)
+ return -1;
+ return wpa_s->driver->switch_channel(wpa_s->drv_priv, settings);
+}
+
+static inline int wpa_drv_add_ts(struct wpa_supplicant *wpa_s, u8 tsid,
+ const u8 *address, u8 user_priority,
+ u16 admitted_time)
+{
+ if (!wpa_s->driver->add_tx_ts)
+ return -1;
+ return wpa_s->driver->add_tx_ts(wpa_s->drv_priv, tsid, address,
+ user_priority, admitted_time);
+}
+
+static inline int wpa_drv_del_ts(struct wpa_supplicant *wpa_s, u8 tid,
+ const u8 *address)
+{
+ if (!wpa_s->driver->del_tx_ts)
+ return -1;
+ return wpa_s->driver->del_tx_ts(wpa_s->drv_priv, tid, address);
+}
+
+static inline int wpa_drv_tdls_enable_channel_switch(
+ struct wpa_supplicant *wpa_s, const u8 *addr, u8 oper_class,
+ const struct hostapd_freq_params *freq_params)
+{
+ if (!wpa_s->driver->tdls_enable_channel_switch)
+ return -1;
+ return wpa_s->driver->tdls_enable_channel_switch(wpa_s->drv_priv, addr,
+ oper_class,
+ freq_params);
+}
+
+static inline int
+wpa_drv_tdls_disable_channel_switch(struct wpa_supplicant *wpa_s,
+ const u8 *addr)
+{
+ if (!wpa_s->driver->tdls_disable_channel_switch)
+ return -1;
+ return wpa_s->driver->tdls_disable_channel_switch(wpa_s->drv_priv,
+ addr);
+}
+
+static inline int wpa_drv_wnm_oper(struct wpa_supplicant *wpa_s,
+ enum wnm_oper oper, const u8 *peer,
+ u8 *buf, u16 *buf_len)
+{
+ if (!wpa_s->driver->wnm_oper)
+ return -1;
+ return wpa_s->driver->wnm_oper(wpa_s->drv_priv, oper, peer, buf,
+ buf_len);
+}
+
+static inline int wpa_drv_status(struct wpa_supplicant *wpa_s,
+ char *buf, size_t buflen)
+{
+ if (!wpa_s->driver->status)
+ return -1;
+ return wpa_s->driver->status(wpa_s->drv_priv, buf, buflen);
+}
+
+static inline int wpa_drv_set_qos_map(struct wpa_supplicant *wpa_s,
+ const u8 *qos_map_set, u8 qos_map_set_len)
+{
+ if (!wpa_s->driver->set_qos_map)
+ return -1;
+ return wpa_s->driver->set_qos_map(wpa_s->drv_priv, qos_map_set,
+ qos_map_set_len);
+}
+
+static inline int wpa_drv_wowlan(struct wpa_supplicant *wpa_s,
+ const struct wowlan_triggers *triggers)
+{
+ if (!wpa_s->driver->set_wowlan)
+ return -1;
+ return wpa_s->driver->set_wowlan(wpa_s->drv_priv, triggers);
+}
+
+static inline int wpa_drv_vendor_cmd(struct wpa_supplicant *wpa_s,
+ int vendor_id, int subcmd, const u8 *data,
+ size_t data_len, struct wpabuf *buf)
+{
+ if (!wpa_s->driver->vendor_cmd)
+ return -1;
+ return wpa_s->driver->vendor_cmd(wpa_s->drv_priv, vendor_id, subcmd,
+ data, data_len, buf);
+}
+
+static inline int wpa_drv_roaming(struct wpa_supplicant *wpa_s, int allowed,
+ const u8 *bssid)
+{
+ if (!wpa_s->driver->roaming)
+ return -1;
+ return wpa_s->driver->roaming(wpa_s->drv_priv, allowed, bssid);
+}
+
+static inline int wpa_drv_set_mac_addr(struct wpa_supplicant *wpa_s,
+ const u8 *addr)
+{
+ if (!wpa_s->driver->set_mac_addr)
+ return -1;
+ return wpa_s->driver->set_mac_addr(wpa_s->drv_priv, addr);
+}
+
+
+#ifdef CONFIG_MACSEC
+
+static inline int wpa_drv_macsec_init(struct wpa_supplicant *wpa_s,
+ struct macsec_init_params *params)
+{
+ if (!wpa_s->driver->macsec_init)
+ return -1;
+ return wpa_s->driver->macsec_init(wpa_s->drv_priv, params);
+}
+
+static inline int wpa_drv_macsec_deinit(struct wpa_supplicant *wpa_s)
+{
+ if (!wpa_s->driver->macsec_deinit)
+ return -1;
+ return wpa_s->driver->macsec_deinit(wpa_s->drv_priv);
+}
+
+static inline int wpa_drv_enable_protect_frames(struct wpa_supplicant *wpa_s,
+ Boolean enabled)
+{
+ if (!wpa_s->driver->enable_protect_frames)
+ return -1;
+ return wpa_s->driver->enable_protect_frames(wpa_s->drv_priv, enabled);
+}
+
+static inline int wpa_drv_set_replay_protect(struct wpa_supplicant *wpa_s,
+ Boolean enabled, u32 window)
+{
+ if (!wpa_s->driver->set_replay_protect)
+ return -1;
+ return wpa_s->driver->set_replay_protect(wpa_s->drv_priv, enabled,
+ window);
+}
+
+static inline int wpa_drv_set_current_cipher_suite(struct wpa_supplicant *wpa_s,
+ const u8 *cs, size_t cs_len)
+{
+ if (!wpa_s->driver->set_current_cipher_suite)
+ return -1;
+ return wpa_s->driver->set_current_cipher_suite(wpa_s->drv_priv, cs,
+ cs_len);
+}
+
+static inline int wpa_drv_enable_controlled_port(struct wpa_supplicant *wpa_s,
+ Boolean enabled)
+{
+ if (!wpa_s->driver->enable_controlled_port)
+ return -1;
+ return wpa_s->driver->enable_controlled_port(wpa_s->drv_priv, enabled);
+}
+
+static inline int wpa_drv_get_receive_lowest_pn(struct wpa_supplicant *wpa_s,
+ u32 channel, u8 an,
+ u32 *lowest_pn)
+{
+ if (!wpa_s->driver->get_receive_lowest_pn)
+ return -1;
+ return wpa_s->driver->get_receive_lowest_pn(wpa_s->drv_priv, channel,
+ an, lowest_pn);
+}
+
+static inline int wpa_drv_get_transmit_next_pn(struct wpa_supplicant *wpa_s,
+ u32 channel, u8 an,
+ u32 *next_pn)
+{
+ if (!wpa_s->driver->get_transmit_next_pn)
+ return -1;
+ return wpa_s->driver->get_transmit_next_pn(wpa_s->drv_priv, channel,
+ an, next_pn);
+}
+
+static inline int wpa_drv_set_transmit_next_pn(struct wpa_supplicant *wpa_s,
+ u32 channel, u8 an,
+ u32 next_pn)
+{
+ if (!wpa_s->driver->set_transmit_next_pn)
+ return -1;
+ return wpa_s->driver->set_transmit_next_pn(wpa_s->drv_priv, channel,
+ an, next_pn);
+}
+
+static inline int wpa_drv_get_available_receive_sc(struct wpa_supplicant *wpa_s,
+ u32 *channel)
+{
+ if (!wpa_s->driver->get_available_receive_sc)
+ return -1;
+ return wpa_s->driver->get_available_receive_sc(wpa_s->drv_priv,
+ channel);
+}
+
+static inline int
+wpa_drv_create_receive_sc(struct wpa_supplicant *wpa_s, u32 channel,
+ const u8 *sci_addr, u16 sci_port,
+ unsigned int conf_offset, int validation)
+{
+ if (!wpa_s->driver->create_receive_sc)
+ return -1;
+ return wpa_s->driver->create_receive_sc(wpa_s->drv_priv, channel,
+ sci_addr, sci_port, conf_offset,
+ validation);
+}
+
+static inline int wpa_drv_delete_receive_sc(struct wpa_supplicant *wpa_s,
+ u32 channel)
+{
+ if (!wpa_s->driver->delete_receive_sc)
+ return -1;
+ return wpa_s->driver->delete_receive_sc(wpa_s->drv_priv, channel);
+}
+
+static inline int wpa_drv_create_receive_sa(struct wpa_supplicant *wpa_s,
+ u32 channel, u8 an,
+ u32 lowest_pn, const u8 *sak)
+{
+ if (!wpa_s->driver->create_receive_sa)
+ return -1;
+ return wpa_s->driver->create_receive_sa(wpa_s->drv_priv, channel, an,
+ lowest_pn, sak);
+}
+
+static inline int wpa_drv_enable_receive_sa(struct wpa_supplicant *wpa_s,
+ u32 channel, u8 an)
+{
+ if (!wpa_s->driver->enable_receive_sa)
+ return -1;
+ return wpa_s->driver->enable_receive_sa(wpa_s->drv_priv, channel, an);
+}
+
+static inline int wpa_drv_disable_receive_sa(struct wpa_supplicant *wpa_s,
+ u32 channel, u8 an)
+{
+ if (!wpa_s->driver->disable_receive_sa)
+ return -1;
+ return wpa_s->driver->disable_receive_sa(wpa_s->drv_priv, channel, an);
+}
+
+static inline int
+wpa_drv_get_available_transmit_sc(struct wpa_supplicant *wpa_s, u32 *channel)
+{
+ if (!wpa_s->driver->get_available_transmit_sc)
+ return -1;
+ return wpa_s->driver->get_available_transmit_sc(wpa_s->drv_priv,
+ channel);
+}
+
+static inline int
+wpa_drv_create_transmit_sc(struct wpa_supplicant *wpa_s, u32 channel,
+ const u8 *sci_addr, u16 sci_port,
+ unsigned int conf_offset)
+{
+ if (!wpa_s->driver->create_transmit_sc)
+ return -1;
+ return wpa_s->driver->create_transmit_sc(wpa_s->drv_priv, channel,
+ sci_addr, sci_port,
+ conf_offset);
+}
+
+static inline int wpa_drv_delete_transmit_sc(struct wpa_supplicant *wpa_s,
+ u32 channel)
+{
+ if (!wpa_s->driver->delete_transmit_sc)
+ return -1;
+ return wpa_s->driver->delete_transmit_sc(wpa_s->drv_priv, channel);
+}
+
+static inline int wpa_drv_create_transmit_sa(struct wpa_supplicant *wpa_s,
+ u32 channel, u8 an,
+ u32 next_pn,
+ Boolean confidentiality,
+ const u8 *sak)
+{
+ if (!wpa_s->driver->create_transmit_sa)
+ return -1;
+ return wpa_s->driver->create_transmit_sa(wpa_s->drv_priv, channel, an,
+ next_pn, confidentiality, sak);
+}
+
+static inline int wpa_drv_enable_transmit_sa(struct wpa_supplicant *wpa_s,
+ u32 channel, u8 an)
+{
+ if (!wpa_s->driver->enable_transmit_sa)
+ return -1;
+ return wpa_s->driver->enable_transmit_sa(wpa_s->drv_priv, channel, an);
+}
+
+static inline int wpa_drv_disable_transmit_sa(struct wpa_supplicant *wpa_s,
+ u32 channel, u8 an)
+{
+ if (!wpa_s->driver->disable_transmit_sa)
+ return -1;
+ return wpa_s->driver->disable_transmit_sa(wpa_s->drv_priv, channel, an);
+}
+#endif /* CONFIG_MACSEC */
+
+static inline int wpa_drv_setband(struct wpa_supplicant *wpa_s,
+ enum set_band band)
+{
+ if (!wpa_s->driver->set_band)
+ return -1;
+ return wpa_s->driver->set_band(wpa_s->drv_priv, band);
+}
+
+static inline int wpa_drv_get_pref_freq_list(struct wpa_supplicant *wpa_s,
+ enum wpa_driver_if_type if_type,
+ unsigned int *num,
+ unsigned int *freq_list)
+{
+ if (!wpa_s->driver->get_pref_freq_list)
+ return -1;
+ return wpa_s->driver->get_pref_freq_list(wpa_s->drv_priv, if_type,
+ num, freq_list);
+}
+
+static inline int wpa_drv_set_prob_oper_freq(struct wpa_supplicant *wpa_s,
+ unsigned int freq)
+{
+ if (!wpa_s->driver->set_prob_oper_freq)
+ return 0;
+ return wpa_s->driver->set_prob_oper_freq(wpa_s->drv_priv, freq);
+}
+
+#endif /* DRIVER_I_H */
diff --git a/freebsd/contrib/wpa/wpa_supplicant/eap_register.c b/freebsd/contrib/wpa/wpa_supplicant/eap_register.c
new file mode 100644
index 00000000..ee31c519
--- /dev/null
+++ b/freebsd/contrib/wpa/wpa_supplicant/eap_register.c
@@ -0,0 +1,263 @@
+#include <machine/rtems-bsd-user-space.h>
+
+/*
+ * EAP method registration
+ * Copyright (c) 2004-2009, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "eap_peer/eap_methods.h"
+#include "eap_server/eap_methods.h"
+#include "wpa_supplicant_i.h"
+
+
+/**
+ * eap_register_methods - Register statically linked EAP methods
+ * Returns: 0 on success, -1 or -2 on failure
+ *
+ * This function is called at program initialization to register all EAP
+ * methods that were linked in statically.
+ */
+int eap_register_methods(void)
+{
+ int ret = 0;
+
+#ifdef EAP_MD5
+ if (ret == 0)
+ ret = eap_peer_md5_register();
+#endif /* EAP_MD5 */
+
+#ifdef EAP_TLS
+ if (ret == 0)
+ ret = eap_peer_tls_register();
+#endif /* EAP_TLS */
+
+#ifdef EAP_UNAUTH_TLS
+ if (ret == 0)
+ ret = eap_peer_unauth_tls_register();
+#endif /* EAP_UNAUTH_TLS */
+
+#ifdef EAP_TLS
+#ifdef CONFIG_HS20
+ if (ret == 0)
+ ret = eap_peer_wfa_unauth_tls_register();
+#endif /* CONFIG_HS20 */
+#endif /* EAP_TLS */
+
+#ifdef EAP_MSCHAPv2
+ if (ret == 0)
+ ret = eap_peer_mschapv2_register();
+#endif /* EAP_MSCHAPv2 */
+
+#ifdef EAP_PEAP
+ if (ret == 0)
+ ret = eap_peer_peap_register();
+#endif /* EAP_PEAP */
+
+#ifdef EAP_TTLS
+ if (ret == 0)
+ ret = eap_peer_ttls_register();
+#endif /* EAP_TTLS */
+
+#ifdef EAP_GTC
+ if (ret == 0)
+ ret = eap_peer_gtc_register();
+#endif /* EAP_GTC */
+
+#ifdef EAP_OTP
+ if (ret == 0)
+ ret = eap_peer_otp_register();
+#endif /* EAP_OTP */
+
+#ifdef EAP_SIM
+ if (ret == 0)
+ ret = eap_peer_sim_register();
+#endif /* EAP_SIM */
+
+#ifdef EAP_LEAP
+ if (ret == 0)
+ ret = eap_peer_leap_register();
+#endif /* EAP_LEAP */
+
+#ifdef EAP_PSK
+ if (ret == 0)
+ ret = eap_peer_psk_register();
+#endif /* EAP_PSK */
+
+#ifdef EAP_AKA
+ if (ret == 0)
+ ret = eap_peer_aka_register();
+#endif /* EAP_AKA */
+
+#ifdef EAP_AKA_PRIME
+ if (ret == 0)
+ ret = eap_peer_aka_prime_register();
+#endif /* EAP_AKA_PRIME */
+
+#ifdef EAP_FAST
+ if (ret == 0)
+ ret = eap_peer_fast_register();
+#endif /* EAP_FAST */
+
+#ifdef EAP_PAX
+ if (ret == 0)
+ ret = eap_peer_pax_register();
+#endif /* EAP_PAX */
+
+#ifdef EAP_SAKE
+ if (ret == 0)
+ ret = eap_peer_sake_register();
+#endif /* EAP_SAKE */
+
+#ifdef EAP_GPSK
+ if (ret == 0)
+ ret = eap_peer_gpsk_register();
+#endif /* EAP_GPSK */
+
+#ifdef EAP_WSC
+ if (ret == 0)
+ ret = eap_peer_wsc_register();
+#endif /* EAP_WSC */
+
+#ifdef EAP_IKEV2
+ if (ret == 0)
+ ret = eap_peer_ikev2_register();
+#endif /* EAP_IKEV2 */
+
+#ifdef EAP_VENDOR_TEST
+ if (ret == 0)
+ ret = eap_peer_vendor_test_register();
+#endif /* EAP_VENDOR_TEST */
+
+#ifdef EAP_TNC
+ if (ret == 0)
+ ret = eap_peer_tnc_register();
+#endif /* EAP_TNC */
+
+#ifdef EAP_PWD
+ if (ret == 0)
+ ret = eap_peer_pwd_register();
+#endif /* EAP_PWD */
+
+#ifdef EAP_EKE
+ if (ret == 0)
+ ret = eap_peer_eke_register();
+#endif /* EAP_EKE */
+
+#ifdef EAP_SERVER_IDENTITY
+ if (ret == 0)
+ ret = eap_server_identity_register();
+#endif /* EAP_SERVER_IDENTITY */
+
+#ifdef EAP_SERVER_MD5
+ if (ret == 0)
+ ret = eap_server_md5_register();
+#endif /* EAP_SERVER_MD5 */
+
+#ifdef EAP_SERVER_TLS
+ if (ret == 0)
+ ret = eap_server_tls_register();
+#endif /* EAP_SERVER_TLS */
+
+#ifdef EAP_SERVER_UNAUTH_TLS
+ if (ret == 0)
+ ret = eap_server_unauth_tls_register();
+#endif /* EAP_SERVER_UNAUTH_TLS */
+
+#ifdef EAP_SERVER_MSCHAPV2
+ if (ret == 0)
+ ret = eap_server_mschapv2_register();
+#endif /* EAP_SERVER_MSCHAPV2 */
+
+#ifdef EAP_SERVER_PEAP
+ if (ret == 0)
+ ret = eap_server_peap_register();
+#endif /* EAP_SERVER_PEAP */
+
+#ifdef EAP_SERVER_TLV
+ if (ret == 0)
+ ret = eap_server_tlv_register();
+#endif /* EAP_SERVER_TLV */
+
+#ifdef EAP_SERVER_GTC
+ if (ret == 0)
+ ret = eap_server_gtc_register();
+#endif /* EAP_SERVER_GTC */
+
+#ifdef EAP_SERVER_TTLS
+ if (ret == 0)
+ ret = eap_server_ttls_register();
+#endif /* EAP_SERVER_TTLS */
+
+#ifdef EAP_SERVER_SIM
+ if (ret == 0)
+ ret = eap_server_sim_register();
+#endif /* EAP_SERVER_SIM */
+
+#ifdef EAP_SERVER_AKA
+ if (ret == 0)
+ ret = eap_server_aka_register();
+#endif /* EAP_SERVER_AKA */
+
+#ifdef EAP_SERVER_AKA_PRIME
+ if (ret == 0)
+ ret = eap_server_aka_prime_register();
+#endif /* EAP_SERVER_AKA_PRIME */
+
+#ifdef EAP_SERVER_PAX
+ if (ret == 0)
+ ret = eap_server_pax_register();
+#endif /* EAP_SERVER_PAX */
+
+#ifdef EAP_SERVER_PSK
+ if (ret == 0)
+ ret = eap_server_psk_register();
+#endif /* EAP_SERVER_PSK */
+
+#ifdef EAP_SERVER_SAKE
+ if (ret == 0)
+ ret = eap_server_sake_register();
+#endif /* EAP_SERVER_SAKE */
+
+#ifdef EAP_SERVER_GPSK
+ if (ret == 0)
+ ret = eap_server_gpsk_register();
+#endif /* EAP_SERVER_GPSK */
+
+#ifdef EAP_SERVER_VENDOR_TEST
+ if (ret == 0)
+ ret = eap_server_vendor_test_register();
+#endif /* EAP_SERVER_VENDOR_TEST */
+
+#ifdef EAP_SERVER_FAST
+ if (ret == 0)
+ ret = eap_server_fast_register();
+#endif /* EAP_SERVER_FAST */
+
+#ifdef EAP_SERVER_WSC
+ if (ret == 0)
+ ret = eap_server_wsc_register();
+#endif /* EAP_SERVER_WSC */
+
+#ifdef EAP_SERVER_IKEV2
+ if (ret == 0)
+ ret = eap_server_ikev2_register();
+#endif /* EAP_SERVER_IKEV2 */
+
+#ifdef EAP_SERVER_TNC
+ if (ret == 0)
+ ret = eap_server_tnc_register();
+#endif /* EAP_SERVER_TNC */
+
+#ifdef EAP_SERVER_PWD
+ if (ret == 0)
+ ret = eap_server_pwd_register();
+#endif /* EAP_SERVER_PWD */
+
+ return ret;
+}
diff --git a/freebsd/contrib/wpa/wpa_supplicant/events.c b/freebsd/contrib/wpa/wpa_supplicant/events.c
new file mode 100644
index 00000000..b4d5d85b
--- /dev/null
+++ b/freebsd/contrib/wpa/wpa_supplicant/events.c
@@ -0,0 +1,3829 @@
+#include <machine/rtems-bsd-user-space.h>
+
+/*
+ * WPA Supplicant - Driver event processing
+ * Copyright (c) 2003-2015, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "eapol_supp/eapol_supp_sm.h"
+#include "rsn_supp/wpa.h"
+#include "eloop.h"
+#include "config.h"
+#include "l2_packet/l2_packet.h"
+#include "wpa_supplicant_i.h"
+#include "driver_i.h"
+#include "pcsc_funcs.h"
+#include "rsn_supp/preauth.h"
+#include "rsn_supp/pmksa_cache.h"
+#include "common/wpa_ctrl.h"
+#include "eap_peer/eap.h"
+#include "ap/hostapd.h"
+#include "p2p/p2p.h"
+#include "fst/fst.h"
+#include "wnm_sta.h"
+#include "notify.h"
+#include "common/ieee802_11_defs.h"
+#include "common/ieee802_11_common.h"
+#include "crypto/random.h"
+#include "blacklist.h"
+#include "wpas_glue.h"
+#include "wps_supplicant.h"
+#include "ibss_rsn.h"
+#include "sme.h"
+#include "gas_query.h"
+#include "p2p_supplicant.h"
+#include "bgscan.h"
+#include "autoscan.h"
+#include "ap.h"
+#include "bss.h"
+#include "scan.h"
+#include "offchannel.h"
+#include "interworking.h"
+#include "mesh.h"
+#include "mesh_mpm.h"
+#include "wmm_ac.h"
+
+
+#ifndef CONFIG_NO_SCAN_PROCESSING
+static int wpas_select_network_from_last_scan(struct wpa_supplicant *wpa_s,
+ int new_scan, int own_request);
+#endif /* CONFIG_NO_SCAN_PROCESSING */
+
+
+static int wpas_temp_disabled(struct wpa_supplicant *wpa_s,
+ struct wpa_ssid *ssid)
+{
+ struct os_reltime now;
+
+ if (ssid == NULL || ssid->disabled_until.sec == 0)
+ return 0;
+
+ os_get_reltime(&now);
+ if (ssid->disabled_until.sec > now.sec)
+ return ssid->disabled_until.sec - now.sec;
+
+ wpas_clear_temp_disabled(wpa_s, ssid, 0);
+
+ return 0;
+}
+
+
+/**
+ * wpas_reenabled_network_time - Time until first network is re-enabled
+ * @wpa_s: Pointer to wpa_supplicant data
+ * Returns: If all enabled networks are temporarily disabled, returns the time
+ * (in sec) until the first network is re-enabled. Otherwise returns 0.
+ *
+ * This function is used in case all enabled networks are temporarily disabled,
+ * in which case it returns the time (in sec) that the first network will be
+ * re-enabled. The function assumes that at least one network is enabled.
+ */
+static int wpas_reenabled_network_time(struct wpa_supplicant *wpa_s)
+{
+ struct wpa_ssid *ssid;
+ int disabled_for, res = 0;
+
+#ifdef CONFIG_INTERWORKING
+ if (wpa_s->conf->auto_interworking && wpa_s->conf->interworking &&
+ wpa_s->conf->cred)
+ return 0;
+#endif /* CONFIG_INTERWORKING */
+
+ for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next) {
+ if (ssid->disabled)
+ continue;
+
+ disabled_for = wpas_temp_disabled(wpa_s, ssid);
+ if (!disabled_for)
+ return 0;
+
+ if (!res || disabled_for < res)
+ res = disabled_for;
+ }
+
+ return res;
+}
+
+
+void wpas_network_reenabled(void *eloop_ctx, void *timeout_ctx)
+{
+ struct wpa_supplicant *wpa_s = eloop_ctx;
+
+ if (wpa_s->disconnected || wpa_s->wpa_state != WPA_SCANNING)
+ return;
+
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ "Try to associate due to network getting re-enabled");
+ if (wpa_supplicant_fast_associate(wpa_s) != 1) {
+ wpa_supplicant_cancel_sched_scan(wpa_s);
+ wpa_supplicant_req_scan(wpa_s, 0, 0);
+ }
+}
+
+
+static struct wpa_bss * wpa_supplicant_get_new_bss(
+ struct wpa_supplicant *wpa_s, const u8 *bssid)
+{
+ struct wpa_bss *bss = NULL;
+ struct wpa_ssid *ssid = wpa_s->current_ssid;
+
+ if (ssid->ssid_len > 0)
+ bss = wpa_bss_get(wpa_s, bssid, ssid->ssid, ssid->ssid_len);
+ if (!bss)
+ bss = wpa_bss_get_bssid(wpa_s, bssid);
+
+ return bss;
+}
+
+
+static void wpa_supplicant_update_current_bss(struct wpa_supplicant *wpa_s)
+{
+ struct wpa_bss *bss = wpa_supplicant_get_new_bss(wpa_s, wpa_s->bssid);
+
+ if (!bss) {
+ wpa_supplicant_update_scan_results(wpa_s);
+
+ /* Get the BSS from the new scan results */
+ bss = wpa_supplicant_get_new_bss(wpa_s, wpa_s->bssid);
+ }
+
+ if (bss)
+ wpa_s->current_bss = bss;
+}
+
+
+static int wpa_supplicant_select_config(struct wpa_supplicant *wpa_s)
+{
+ struct wpa_ssid *ssid, *old_ssid;
+ u8 drv_ssid[SSID_MAX_LEN];
+ size_t drv_ssid_len;
+ int res;
+
+ if (wpa_s->conf->ap_scan == 1 && wpa_s->current_ssid) {
+ wpa_supplicant_update_current_bss(wpa_s);
+
+ if (wpa_s->current_ssid->ssid_len == 0)
+ return 0; /* current profile still in use */
+ res = wpa_drv_get_ssid(wpa_s, drv_ssid);
+ if (res < 0) {
+ wpa_msg(wpa_s, MSG_INFO,
+ "Failed to read SSID from driver");
+ return 0; /* try to use current profile */
+ }
+ drv_ssid_len = res;
+
+ if (drv_ssid_len == wpa_s->current_ssid->ssid_len &&
+ os_memcmp(drv_ssid, wpa_s->current_ssid->ssid,
+ drv_ssid_len) == 0)
+ return 0; /* current profile still in use */
+
+ wpa_msg(wpa_s, MSG_DEBUG,
+ "Driver-initiated BSS selection changed the SSID to %s",
+ wpa_ssid_txt(drv_ssid, drv_ssid_len));
+ /* continue selecting a new network profile */
+ }
+
+ wpa_dbg(wpa_s, MSG_DEBUG, "Select network based on association "
+ "information");
+ ssid = wpa_supplicant_get_ssid(wpa_s);
+ if (ssid == NULL) {
+ wpa_msg(wpa_s, MSG_INFO,
+ "No network configuration found for the current AP");
+ return -1;
+ }
+
+ if (wpas_network_disabled(wpa_s, ssid)) {
+ wpa_dbg(wpa_s, MSG_DEBUG, "Selected network is disabled");
+ return -1;
+ }
+
+ if (disallowed_bssid(wpa_s, wpa_s->bssid) ||
+ disallowed_ssid(wpa_s, ssid->ssid, ssid->ssid_len)) {
+ wpa_dbg(wpa_s, MSG_DEBUG, "Selected BSS is disallowed");
+ return -1;
+ }
+
+ res = wpas_temp_disabled(wpa_s, ssid);
+ if (res > 0) {
+ wpa_dbg(wpa_s, MSG_DEBUG, "Selected network is temporarily "
+ "disabled for %d second(s)", res);
+ return -1;
+ }
+
+ wpa_dbg(wpa_s, MSG_DEBUG, "Network configuration found for the "
+ "current AP");
+ if (wpa_key_mgmt_wpa_any(ssid->key_mgmt)) {
+ u8 wpa_ie[80];
+ size_t wpa_ie_len = sizeof(wpa_ie);
+ if (wpa_supplicant_set_suites(wpa_s, NULL, ssid,
+ wpa_ie, &wpa_ie_len) < 0)
+ wpa_dbg(wpa_s, MSG_DEBUG, "Could not set WPA suites");
+ } else {
+ wpa_supplicant_set_non_wpa_policy(wpa_s, ssid);
+ }
+
+ if (wpa_s->current_ssid && wpa_s->current_ssid != ssid)
+ eapol_sm_invalidate_cached_session(wpa_s->eapol);
+ old_ssid = wpa_s->current_ssid;
+ wpa_s->current_ssid = ssid;
+
+ wpa_supplicant_update_current_bss(wpa_s);
+
+ wpa_supplicant_rsn_supp_set_config(wpa_s, wpa_s->current_ssid);
+ wpa_supplicant_initiate_eapol(wpa_s);
+ if (old_ssid != wpa_s->current_ssid)
+ wpas_notify_network_changed(wpa_s);
+
+ return 0;
+}
+
+
+void wpa_supplicant_stop_countermeasures(void *eloop_ctx, void *sock_ctx)
+{
+ struct wpa_supplicant *wpa_s = eloop_ctx;
+
+ if (wpa_s->countermeasures) {
+ wpa_s->countermeasures = 0;
+ wpa_drv_set_countermeasures(wpa_s, 0);
+ wpa_msg(wpa_s, MSG_INFO, "WPA: TKIP countermeasures stopped");
+
+ /*
+ * It is possible that the device is sched scanning, which means
+ * that a connection attempt will be done only when we receive
+ * scan results. However, in this case, it would be preferable
+ * to scan and connect immediately, so cancel the sched_scan and
+ * issue a regular scan flow.
+ */
+ wpa_supplicant_cancel_sched_scan(wpa_s);
+ wpa_supplicant_req_scan(wpa_s, 0, 0);
+ }
+}
+
+
+void wpa_supplicant_mark_disassoc(struct wpa_supplicant *wpa_s)
+{
+ int bssid_changed;
+
+ wnm_bss_keep_alive_deinit(wpa_s);
+
+#ifdef CONFIG_IBSS_RSN
+ ibss_rsn_deinit(wpa_s->ibss_rsn);
+ wpa_s->ibss_rsn = NULL;
+#endif /* CONFIG_IBSS_RSN */
+
+#ifdef CONFIG_AP
+ wpa_supplicant_ap_deinit(wpa_s);
+#endif /* CONFIG_AP */
+
+ if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED)
+ return;
+
+ wpa_supplicant_set_state(wpa_s, WPA_DISCONNECTED);
+ bssid_changed = !is_zero_ether_addr(wpa_s->bssid);
+ os_memset(wpa_s->bssid, 0, ETH_ALEN);
+ os_memset(wpa_s->pending_bssid, 0, ETH_ALEN);
+ sme_clear_on_disassoc(wpa_s);
+ wpa_s->current_bss = NULL;
+ wpa_s->assoc_freq = 0;
+
+ if (bssid_changed)
+ wpas_notify_bssid_changed(wpa_s);
+
+ eapol_sm_notify_portEnabled(wpa_s->eapol, FALSE);
+ eapol_sm_notify_portValid(wpa_s->eapol, FALSE);
+ if (wpa_key_mgmt_wpa_psk(wpa_s->key_mgmt))
+ eapol_sm_notify_eap_success(wpa_s->eapol, FALSE);
+ wpa_s->ap_ies_from_associnfo = 0;
+ wpa_s->current_ssid = NULL;
+ eapol_sm_notify_config(wpa_s->eapol, NULL, NULL);
+ wpa_s->key_mgmt = 0;
+
+ wpas_rrm_reset(wpa_s);
+}
+
+
+static void wpa_find_assoc_pmkid(struct wpa_supplicant *wpa_s)
+{
+ struct wpa_ie_data ie;
+ int pmksa_set = -1;
+ size_t i;
+
+ if (wpa_sm_parse_own_wpa_ie(wpa_s->wpa, &ie) < 0 ||
+ ie.pmkid == NULL)
+ return;
+
+ for (i = 0; i < ie.num_pmkid; i++) {
+ pmksa_set = pmksa_cache_set_current(wpa_s->wpa,
+ ie.pmkid + i * PMKID_LEN,
+ NULL, NULL, 0);
+ if (pmksa_set == 0) {
+ eapol_sm_notify_pmkid_attempt(wpa_s->eapol);
+ break;
+ }
+ }
+
+ wpa_dbg(wpa_s, MSG_DEBUG, "RSN: PMKID from assoc IE %sfound from "
+ "PMKSA cache", pmksa_set == 0 ? "" : "not ");
+}
+
+
+static void wpa_supplicant_event_pmkid_candidate(struct wpa_supplicant *wpa_s,
+ union wpa_event_data *data)
+{
+ if (data == NULL) {
+ wpa_dbg(wpa_s, MSG_DEBUG, "RSN: No data in PMKID candidate "
+ "event");
+ return;
+ }
+ wpa_dbg(wpa_s, MSG_DEBUG, "RSN: PMKID candidate event - bssid=" MACSTR
+ " index=%d preauth=%d",
+ MAC2STR(data->pmkid_candidate.bssid),
+ data->pmkid_candidate.index,
+ data->pmkid_candidate.preauth);
+
+ pmksa_candidate_add(wpa_s->wpa, data->pmkid_candidate.bssid,
+ data->pmkid_candidate.index,
+ data->pmkid_candidate.preauth);
+}
+
+
+static int wpa_supplicant_dynamic_keys(struct wpa_supplicant *wpa_s)
+{
+ if (wpa_s->key_mgmt == WPA_KEY_MGMT_NONE ||
+ wpa_s->key_mgmt == WPA_KEY_MGMT_WPA_NONE)
+ return 0;
+
+#ifdef IEEE8021X_EAPOL
+ if (wpa_s->key_mgmt == WPA_KEY_MGMT_IEEE8021X_NO_WPA &&
+ wpa_s->current_ssid &&
+ !(wpa_s->current_ssid->eapol_flags &
+ (EAPOL_FLAG_REQUIRE_KEY_UNICAST |
+ EAPOL_FLAG_REQUIRE_KEY_BROADCAST))) {
+ /* IEEE 802.1X, but not using dynamic WEP keys (i.e., either
+ * plaintext or static WEP keys). */
+ return 0;
+ }
+#endif /* IEEE8021X_EAPOL */
+
+ return 1;
+}
+
+
+/**
+ * wpa_supplicant_scard_init - Initialize SIM/USIM access with PC/SC
+ * @wpa_s: pointer to wpa_supplicant data
+ * @ssid: Configuration data for the network
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function is called when starting authentication with a network that is
+ * configured to use PC/SC for SIM/USIM access (EAP-SIM or EAP-AKA).
+ */
+int wpa_supplicant_scard_init(struct wpa_supplicant *wpa_s,
+ struct wpa_ssid *ssid)
+{
+#ifdef IEEE8021X_EAPOL
+#ifdef PCSC_FUNCS
+ int aka = 0, sim = 0;
+
+ if ((ssid != NULL && ssid->eap.pcsc == NULL) ||
+ wpa_s->scard != NULL || wpa_s->conf->external_sim)
+ return 0;
+
+ if (ssid == NULL || ssid->eap.eap_methods == NULL) {
+ sim = 1;
+ aka = 1;
+ } else {
+ struct eap_method_type *eap = ssid->eap.eap_methods;
+ while (eap->vendor != EAP_VENDOR_IETF ||
+ eap->method != EAP_TYPE_NONE) {
+ if (eap->vendor == EAP_VENDOR_IETF) {
+ if (eap->method == EAP_TYPE_SIM)
+ sim = 1;
+ else if (eap->method == EAP_TYPE_AKA ||
+ eap->method == EAP_TYPE_AKA_PRIME)
+ aka = 1;
+ }
+ eap++;
+ }
+ }
+
+ if (eap_peer_get_eap_method(EAP_VENDOR_IETF, EAP_TYPE_SIM) == NULL)
+ sim = 0;
+ if (eap_peer_get_eap_method(EAP_VENDOR_IETF, EAP_TYPE_AKA) == NULL &&
+ eap_peer_get_eap_method(EAP_VENDOR_IETF, EAP_TYPE_AKA_PRIME) ==
+ NULL)
+ aka = 0;
+
+ if (!sim && !aka) {
+ wpa_dbg(wpa_s, MSG_DEBUG, "Selected network is configured to "
+ "use SIM, but neither EAP-SIM nor EAP-AKA are "
+ "enabled");
+ return 0;
+ }
+
+ wpa_dbg(wpa_s, MSG_DEBUG, "Selected network is configured to use SIM "
+ "(sim=%d aka=%d) - initialize PCSC", sim, aka);
+
+ wpa_s->scard = scard_init(wpa_s->conf->pcsc_reader);
+ if (wpa_s->scard == NULL) {
+ wpa_msg(wpa_s, MSG_WARNING, "Failed to initialize SIM "
+ "(pcsc-lite)");
+ return -1;
+ }
+ wpa_sm_set_scard_ctx(wpa_s->wpa, wpa_s->scard);
+ eapol_sm_register_scard_ctx(wpa_s->eapol, wpa_s->scard);
+#endif /* PCSC_FUNCS */
+#endif /* IEEE8021X_EAPOL */
+
+ return 0;
+}
+
+
+#ifndef CONFIG_NO_SCAN_PROCESSING
+
+static int has_wep_key(struct wpa_ssid *ssid)
+{
+ int i;
+
+ for (i = 0; i < NUM_WEP_KEYS; i++) {
+ if (ssid->wep_key_len[i])
+ return 1;
+ }
+
+ return 0;
+}
+
+
+static int wpa_supplicant_match_privacy(struct wpa_bss *bss,
+ struct wpa_ssid *ssid)
+{
+ int privacy = 0;
+
+ if (ssid->mixed_cell)
+ return 1;
+
+#ifdef CONFIG_WPS
+ if (ssid->key_mgmt & WPA_KEY_MGMT_WPS)
+ return 1;
+#endif /* CONFIG_WPS */
+
+ if (has_wep_key(ssid))
+ privacy = 1;
+
+#ifdef IEEE8021X_EAPOL
+ if ((ssid->key_mgmt & WPA_KEY_MGMT_IEEE8021X_NO_WPA) &&
+ ssid->eapol_flags & (EAPOL_FLAG_REQUIRE_KEY_UNICAST |
+ EAPOL_FLAG_REQUIRE_KEY_BROADCAST))
+ privacy = 1;
+#endif /* IEEE8021X_EAPOL */
+
+ if (wpa_key_mgmt_wpa(ssid->key_mgmt))
+ privacy = 1;
+
+ if (ssid->key_mgmt & WPA_KEY_MGMT_OSEN)
+ privacy = 1;
+
+ if (bss->caps & IEEE80211_CAP_PRIVACY)
+ return privacy;
+ return !privacy;
+}
+
+
+static int wpa_supplicant_ssid_bss_match(struct wpa_supplicant *wpa_s,
+ struct wpa_ssid *ssid,
+ struct wpa_bss *bss)
+{
+ struct wpa_ie_data ie;
+ int proto_match = 0;
+ const u8 *rsn_ie, *wpa_ie;
+ int ret;
+ int wep_ok;
+
+ ret = wpas_wps_ssid_bss_match(wpa_s, ssid, bss);
+ if (ret >= 0)
+ return ret;
+
+ /* Allow TSN if local configuration accepts WEP use without WPA/WPA2 */
+ wep_ok = !wpa_key_mgmt_wpa(ssid->key_mgmt) &&
+ (((ssid->key_mgmt & WPA_KEY_MGMT_NONE) &&
+ ssid->wep_key_len[ssid->wep_tx_keyidx] > 0) ||
+ (ssid->key_mgmt & WPA_KEY_MGMT_IEEE8021X_NO_WPA));
+
+ rsn_ie = wpa_bss_get_ie(bss, WLAN_EID_RSN);
+ while ((ssid->proto & WPA_PROTO_RSN) && rsn_ie) {
+ proto_match++;
+
+ if (wpa_parse_wpa_ie(rsn_ie, 2 + rsn_ie[1], &ie)) {
+ wpa_dbg(wpa_s, MSG_DEBUG, " skip RSN IE - parse "
+ "failed");
+ break;
+ }
+
+ if (wep_ok &&
+ (ie.group_cipher & (WPA_CIPHER_WEP40 | WPA_CIPHER_WEP104)))
+ {
+ wpa_dbg(wpa_s, MSG_DEBUG, " selected based on TSN "
+ "in RSN IE");
+ return 1;
+ }
+
+ if (!(ie.proto & ssid->proto)) {
+ wpa_dbg(wpa_s, MSG_DEBUG, " skip RSN IE - proto "
+ "mismatch");
+ break;
+ }
+
+ if (!(ie.pairwise_cipher & ssid->pairwise_cipher)) {
+ wpa_dbg(wpa_s, MSG_DEBUG, " skip RSN IE - PTK "
+ "cipher mismatch");
+ break;
+ }
+
+ if (!(ie.group_cipher & ssid->group_cipher)) {
+ wpa_dbg(wpa_s, MSG_DEBUG, " skip RSN IE - GTK "
+ "cipher mismatch");
+ break;
+ }
+
+ if (!(ie.key_mgmt & ssid->key_mgmt)) {
+ wpa_dbg(wpa_s, MSG_DEBUG, " skip RSN IE - key mgmt "
+ "mismatch");
+ break;
+ }
+
+#ifdef CONFIG_IEEE80211W
+ if (!(ie.capabilities & WPA_CAPABILITY_MFPC) &&
+ wpas_get_ssid_pmf(wpa_s, ssid) ==
+ MGMT_FRAME_PROTECTION_REQUIRED) {
+ wpa_dbg(wpa_s, MSG_DEBUG, " skip RSN IE - no mgmt "
+ "frame protection");
+ break;
+ }
+#endif /* CONFIG_IEEE80211W */
+
+ wpa_dbg(wpa_s, MSG_DEBUG, " selected based on RSN IE");
+ return 1;
+ }
+
+ wpa_ie = wpa_bss_get_vendor_ie(bss, WPA_IE_VENDOR_TYPE);
+ while ((ssid->proto & WPA_PROTO_WPA) && wpa_ie) {
+ proto_match++;
+
+ if (wpa_parse_wpa_ie(wpa_ie, 2 + wpa_ie[1], &ie)) {
+ wpa_dbg(wpa_s, MSG_DEBUG, " skip WPA IE - parse "
+ "failed");
+ break;
+ }
+
+ if (wep_ok &&
+ (ie.group_cipher & (WPA_CIPHER_WEP40 | WPA_CIPHER_WEP104)))
+ {
+ wpa_dbg(wpa_s, MSG_DEBUG, " selected based on TSN "
+ "in WPA IE");
+ return 1;
+ }
+
+ if (!(ie.proto & ssid->proto)) {
+ wpa_dbg(wpa_s, MSG_DEBUG, " skip WPA IE - proto "
+ "mismatch");
+ break;
+ }
+
+ if (!(ie.pairwise_cipher & ssid->pairwise_cipher)) {
+ wpa_dbg(wpa_s, MSG_DEBUG, " skip WPA IE - PTK "
+ "cipher mismatch");
+ break;
+ }
+
+ if (!(ie.group_cipher & ssid->group_cipher)) {
+ wpa_dbg(wpa_s, MSG_DEBUG, " skip WPA IE - GTK "
+ "cipher mismatch");
+ break;
+ }
+
+ if (!(ie.key_mgmt & ssid->key_mgmt)) {
+ wpa_dbg(wpa_s, MSG_DEBUG, " skip WPA IE - key mgmt "
+ "mismatch");
+ break;
+ }
+
+ wpa_dbg(wpa_s, MSG_DEBUG, " selected based on WPA IE");
+ return 1;
+ }
+
+ if ((ssid->key_mgmt & WPA_KEY_MGMT_IEEE8021X_NO_WPA) && !wpa_ie &&
+ !rsn_ie) {
+ wpa_dbg(wpa_s, MSG_DEBUG, " allow for non-WPA IEEE 802.1X");
+ return 1;
+ }
+
+ if ((ssid->proto & (WPA_PROTO_WPA | WPA_PROTO_RSN)) &&
+ wpa_key_mgmt_wpa(ssid->key_mgmt) && proto_match == 0) {
+ wpa_dbg(wpa_s, MSG_DEBUG, " skip - no WPA/RSN proto match");
+ return 0;
+ }
+
+ if ((ssid->key_mgmt & WPA_KEY_MGMT_OSEN) &&
+ wpa_bss_get_vendor_ie(bss, OSEN_IE_VENDOR_TYPE)) {
+ wpa_dbg(wpa_s, MSG_DEBUG, " allow in OSEN");
+ return 1;
+ }
+
+ if (!wpa_key_mgmt_wpa(ssid->key_mgmt)) {
+ wpa_dbg(wpa_s, MSG_DEBUG, " allow in non-WPA/WPA2");
+ return 1;
+ }
+
+ wpa_dbg(wpa_s, MSG_DEBUG, " reject due to mismatch with "
+ "WPA/WPA2");
+
+ return 0;
+}
+
+
+static int freq_allowed(int *freqs, int freq)
+{
+ int i;
+
+ if (freqs == NULL)
+ return 1;
+
+ for (i = 0; freqs[i]; i++)
+ if (freqs[i] == freq)
+ return 1;
+ return 0;
+}
+
+
+static int rate_match(struct wpa_supplicant *wpa_s, struct wpa_bss *bss)
+{
+ const struct hostapd_hw_modes *mode = NULL, *modes;
+ const u8 scan_ie[2] = { WLAN_EID_SUPP_RATES, WLAN_EID_EXT_SUPP_RATES };
+ const u8 *rate_ie;
+ int i, j, k;
+
+ if (bss->freq == 0)
+ return 1; /* Cannot do matching without knowing band */
+
+ modes = wpa_s->hw.modes;
+ if (modes == NULL) {
+ /*
+ * The driver does not provide any additional information
+ * about the utilized hardware, so allow the connection attempt
+ * to continue.
+ */
+ return 1;
+ }
+
+ for (i = 0; i < wpa_s->hw.num_modes; i++) {
+ for (j = 0; j < modes[i].num_channels; j++) {
+ int freq = modes[i].channels[j].freq;
+ if (freq == bss->freq) {
+ if (mode &&
+ mode->mode == HOSTAPD_MODE_IEEE80211G)
+ break; /* do not allow 802.11b replace
+ * 802.11g */
+ mode = &modes[i];
+ break;
+ }
+ }
+ }
+
+ if (mode == NULL)
+ return 0;
+
+ for (i = 0; i < (int) sizeof(scan_ie); i++) {
+ rate_ie = wpa_bss_get_ie(bss, scan_ie[i]);
+ if (rate_ie == NULL)
+ continue;
+
+ for (j = 2; j < rate_ie[1] + 2; j++) {
+ int flagged = !!(rate_ie[j] & 0x80);
+ int r = (rate_ie[j] & 0x7f) * 5;
+
+ /*
+ * IEEE Std 802.11n-2009 7.3.2.2:
+ * The new BSS Membership selector value is encoded
+ * like a legacy basic rate, but it is not a rate and
+ * only indicates if the BSS members are required to
+ * support the mandatory features of Clause 20 [HT PHY]
+ * in order to join the BSS.
+ */
+ if (flagged && ((rate_ie[j] & 0x7f) ==
+ BSS_MEMBERSHIP_SELECTOR_HT_PHY)) {
+ if (!ht_supported(mode)) {
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ " hardware does not support "
+ "HT PHY");
+ return 0;
+ }
+ continue;
+ }
+
+ /* There's also a VHT selector for 802.11ac */
+ if (flagged && ((rate_ie[j] & 0x7f) ==
+ BSS_MEMBERSHIP_SELECTOR_VHT_PHY)) {
+ if (!vht_supported(mode)) {
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ " hardware does not support "
+ "VHT PHY");
+ return 0;
+ }
+ continue;
+ }
+
+ if (!flagged)
+ continue;
+
+ /* check for legacy basic rates */
+ for (k = 0; k < mode->num_rates; k++) {
+ if (mode->rates[k] == r)
+ break;
+ }
+ if (k == mode->num_rates) {
+ /*
+ * IEEE Std 802.11-2007 7.3.2.2 demands that in
+ * order to join a BSS all required rates
+ * have to be supported by the hardware.
+ */
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ " hardware does not support required rate %d.%d Mbps (freq=%d mode==%d num_rates=%d)",
+ r / 10, r % 10,
+ bss->freq, mode->mode, mode->num_rates);
+ return 0;
+ }
+ }
+ }
+
+ return 1;
+}
+
+
+/*
+ * Test whether BSS is in an ESS.
+ * This is done differently in DMG (60 GHz) and non-DMG bands
+ */
+static int bss_is_ess(struct wpa_bss *bss)
+{
+ if (bss_is_dmg(bss)) {
+ return (bss->caps & IEEE80211_CAP_DMG_MASK) ==
+ IEEE80211_CAP_DMG_AP;
+ }
+
+ return ((bss->caps & (IEEE80211_CAP_ESS | IEEE80211_CAP_IBSS)) ==
+ IEEE80211_CAP_ESS);
+}
+
+
+static int match_mac_mask(const u8 *addr_a, const u8 *addr_b, const u8 *mask)
+{
+ size_t i;
+
+ for (i = 0; i < ETH_ALEN; i++) {
+ if ((addr_a[i] & mask[i]) != (addr_b[i] & mask[i]))
+ return 0;
+ }
+ return 1;
+}
+
+
+static int addr_in_list(const u8 *addr, const u8 *list, size_t num)
+{
+ size_t i;
+
+ for (i = 0; i < num; i++) {
+ const u8 *a = list + i * ETH_ALEN * 2;
+ const u8 *m = a + ETH_ALEN;
+
+ if (match_mac_mask(a, addr, m))
+ return 1;
+ }
+ return 0;
+}
+
+
+static struct wpa_ssid * wpa_scan_res_match(struct wpa_supplicant *wpa_s,
+ int i, struct wpa_bss *bss,
+ struct wpa_ssid *group,
+ int only_first_ssid)
+{
+ u8 wpa_ie_len, rsn_ie_len;
+ int wpa;
+ struct wpa_blacklist *e;
+ const u8 *ie;
+ struct wpa_ssid *ssid;
+ int osen;
+
+ ie = wpa_bss_get_vendor_ie(bss, WPA_IE_VENDOR_TYPE);
+ wpa_ie_len = ie ? ie[1] : 0;
+
+ ie = wpa_bss_get_ie(bss, WLAN_EID_RSN);
+ rsn_ie_len = ie ? ie[1] : 0;
+
+ ie = wpa_bss_get_vendor_ie(bss, OSEN_IE_VENDOR_TYPE);
+ osen = ie != NULL;
+
+ wpa_dbg(wpa_s, MSG_DEBUG, "%d: " MACSTR " ssid='%s' "
+ "wpa_ie_len=%u rsn_ie_len=%u caps=0x%x level=%d freq=%d %s%s%s",
+ i, MAC2STR(bss->bssid), wpa_ssid_txt(bss->ssid, bss->ssid_len),
+ wpa_ie_len, rsn_ie_len, bss->caps, bss->level, bss->freq,
+ wpa_bss_get_vendor_ie(bss, WPS_IE_VENDOR_TYPE) ? " wps" : "",
+ (wpa_bss_get_vendor_ie(bss, P2P_IE_VENDOR_TYPE) ||
+ wpa_bss_get_vendor_ie_beacon(bss, P2P_IE_VENDOR_TYPE)) ?
+ " p2p" : "",
+ osen ? " osen=1" : "");
+
+ e = wpa_blacklist_get(wpa_s, bss->bssid);
+ if (e) {
+ int limit = 1;
+ if (wpa_supplicant_enabled_networks(wpa_s) == 1) {
+ /*
+ * When only a single network is enabled, we can
+ * trigger blacklisting on the first failure. This
+ * should not be done with multiple enabled networks to
+ * avoid getting forced to move into a worse ESS on
+ * single error if there are no other BSSes of the
+ * current ESS.
+ */
+ limit = 0;
+ }
+ if (e->count > limit) {
+ wpa_dbg(wpa_s, MSG_DEBUG, " skip - blacklisted "
+ "(count=%d limit=%d)", e->count, limit);
+ return NULL;
+ }
+ }
+
+ if (bss->ssid_len == 0) {
+ wpa_dbg(wpa_s, MSG_DEBUG, " skip - SSID not known");
+ return NULL;
+ }
+
+ if (disallowed_bssid(wpa_s, bss->bssid)) {
+ wpa_dbg(wpa_s, MSG_DEBUG, " skip - BSSID disallowed");
+ return NULL;
+ }
+
+ if (disallowed_ssid(wpa_s, bss->ssid, bss->ssid_len)) {
+ wpa_dbg(wpa_s, MSG_DEBUG, " skip - SSID disallowed");
+ return NULL;
+ }
+
+ wpa = wpa_ie_len > 0 || rsn_ie_len > 0;
+
+ for (ssid = group; ssid; ssid = only_first_ssid ? NULL : ssid->pnext) {
+ int check_ssid = wpa ? 1 : (ssid->ssid_len != 0);
+ int res;
+
+ if (wpas_network_disabled(wpa_s, ssid)) {
+ wpa_dbg(wpa_s, MSG_DEBUG, " skip - disabled");
+ continue;
+ }
+
+ res = wpas_temp_disabled(wpa_s, ssid);
+ if (res > 0) {
+ wpa_dbg(wpa_s, MSG_DEBUG, " skip - disabled "
+ "temporarily for %d second(s)", res);
+ continue;
+ }
+
+#ifdef CONFIG_WPS
+ if ((ssid->key_mgmt & WPA_KEY_MGMT_WPS) && e && e->count > 0) {
+ wpa_dbg(wpa_s, MSG_DEBUG, " skip - blacklisted "
+ "(WPS)");
+ continue;
+ }
+
+ if (wpa && ssid->ssid_len == 0 &&
+ wpas_wps_ssid_wildcard_ok(wpa_s, ssid, bss))
+ check_ssid = 0;
+
+ if (!wpa && (ssid->key_mgmt & WPA_KEY_MGMT_WPS)) {
+ /* Only allow wildcard SSID match if an AP
+ * advertises active WPS operation that matches
+ * with our mode. */
+ check_ssid = 1;
+ if (ssid->ssid_len == 0 &&
+ wpas_wps_ssid_wildcard_ok(wpa_s, ssid, bss))
+ check_ssid = 0;
+ }
+#endif /* CONFIG_WPS */
+
+ if (ssid->bssid_set && ssid->ssid_len == 0 &&
+ os_memcmp(bss->bssid, ssid->bssid, ETH_ALEN) == 0)
+ check_ssid = 0;
+
+ if (check_ssid &&
+ (bss->ssid_len != ssid->ssid_len ||
+ os_memcmp(bss->ssid, ssid->ssid, bss->ssid_len) != 0)) {
+ wpa_dbg(wpa_s, MSG_DEBUG, " skip - SSID mismatch");
+ continue;
+ }
+
+ if (ssid->bssid_set &&
+ os_memcmp(bss->bssid, ssid->bssid, ETH_ALEN) != 0) {
+ wpa_dbg(wpa_s, MSG_DEBUG, " skip - BSSID mismatch");
+ continue;
+ }
+
+ /* check blacklist */
+ if (ssid->num_bssid_blacklist &&
+ addr_in_list(bss->bssid, ssid->bssid_blacklist,
+ ssid->num_bssid_blacklist)) {
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ " skip - BSSID blacklisted");
+ continue;
+ }
+
+ /* if there is a whitelist, only accept those APs */
+ if (ssid->num_bssid_whitelist &&
+ !addr_in_list(bss->bssid, ssid->bssid_whitelist,
+ ssid->num_bssid_whitelist)) {
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ " skip - BSSID not in whitelist");
+ continue;
+ }
+
+ if (!wpa_supplicant_ssid_bss_match(wpa_s, ssid, bss))
+ continue;
+
+ if (!osen && !wpa &&
+ !(ssid->key_mgmt & WPA_KEY_MGMT_NONE) &&
+ !(ssid->key_mgmt & WPA_KEY_MGMT_WPS) &&
+ !(ssid->key_mgmt & WPA_KEY_MGMT_IEEE8021X_NO_WPA)) {
+ wpa_dbg(wpa_s, MSG_DEBUG, " skip - non-WPA network "
+ "not allowed");
+ continue;
+ }
+
+ if (wpa && !wpa_key_mgmt_wpa(ssid->key_mgmt) &&
+ has_wep_key(ssid)) {
+ wpa_dbg(wpa_s, MSG_DEBUG, " skip - ignore WPA/WPA2 AP for WEP network block");
+ continue;
+ }
+
+ if ((ssid->key_mgmt & WPA_KEY_MGMT_OSEN) && !osen) {
+ wpa_dbg(wpa_s, MSG_DEBUG, " skip - non-OSEN network "
+ "not allowed");
+ continue;
+ }
+
+ if (!wpa_supplicant_match_privacy(bss, ssid)) {
+ wpa_dbg(wpa_s, MSG_DEBUG, " skip - privacy "
+ "mismatch");
+ continue;
+ }
+
+ if (!bss_is_ess(bss)) {
+ wpa_dbg(wpa_s, MSG_DEBUG, " skip - not ESS network");
+ continue;
+ }
+
+ if (!freq_allowed(ssid->freq_list, bss->freq)) {
+ wpa_dbg(wpa_s, MSG_DEBUG, " skip - frequency not "
+ "allowed");
+ continue;
+ }
+
+ if (!rate_match(wpa_s, bss)) {
+ wpa_dbg(wpa_s, MSG_DEBUG, " skip - rate sets do "
+ "not match");
+ continue;
+ }
+
+#ifdef CONFIG_P2P
+ if (ssid->p2p_group &&
+ !wpa_bss_get_vendor_ie(bss, P2P_IE_VENDOR_TYPE) &&
+ !wpa_bss_get_vendor_ie_beacon(bss, P2P_IE_VENDOR_TYPE)) {
+ wpa_dbg(wpa_s, MSG_DEBUG, " skip - no P2P IE seen");
+ continue;
+ }
+
+ if (!is_zero_ether_addr(ssid->go_p2p_dev_addr)) {
+ struct wpabuf *p2p_ie;
+ u8 dev_addr[ETH_ALEN];
+
+ ie = wpa_bss_get_vendor_ie(bss, P2P_IE_VENDOR_TYPE);
+ if (ie == NULL) {
+ wpa_dbg(wpa_s, MSG_DEBUG, " skip - no P2P element");
+ continue;
+ }
+ p2p_ie = wpa_bss_get_vendor_ie_multi(
+ bss, P2P_IE_VENDOR_TYPE);
+ if (p2p_ie == NULL) {
+ wpa_dbg(wpa_s, MSG_DEBUG, " skip - could not fetch P2P element");
+ continue;
+ }
+
+ if (p2p_parse_dev_addr_in_p2p_ie(p2p_ie, dev_addr) < 0
+ || os_memcmp(dev_addr, ssid->go_p2p_dev_addr,
+ ETH_ALEN) != 0) {
+ wpa_dbg(wpa_s, MSG_DEBUG, " skip - no matching GO P2P Device Address in P2P element");
+ wpabuf_free(p2p_ie);
+ continue;
+ }
+ wpabuf_free(p2p_ie);
+ }
+
+ /*
+ * TODO: skip the AP if its P2P IE has Group Formation
+ * bit set in the P2P Group Capability Bitmap and we
+ * are not in Group Formation with that device.
+ */
+#endif /* CONFIG_P2P */
+
+ if (os_reltime_before(&bss->last_update, &wpa_s->scan_min_time))
+ {
+ struct os_reltime diff;
+
+ os_reltime_sub(&wpa_s->scan_min_time,
+ &bss->last_update, &diff);
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ " skip - scan result not recent enough (%u.%06u seconds too old)",
+ (unsigned int) diff.sec,
+ (unsigned int) diff.usec);
+ continue;
+ }
+
+ /* Matching configuration found */
+ return ssid;
+ }
+
+ /* No matching configuration found */
+ return NULL;
+}
+
+
+static struct wpa_bss *
+wpa_supplicant_select_bss(struct wpa_supplicant *wpa_s,
+ struct wpa_ssid *group,
+ struct wpa_ssid **selected_ssid,
+ int only_first_ssid)
+{
+ unsigned int i;
+
+ if (only_first_ssid)
+ wpa_dbg(wpa_s, MSG_DEBUG, "Try to find BSS matching pre-selected network id=%d",
+ group->id);
+ else
+ wpa_dbg(wpa_s, MSG_DEBUG, "Selecting BSS from priority group %d",
+ group->priority);
+
+ for (i = 0; i < wpa_s->last_scan_res_used; i++) {
+ struct wpa_bss *bss = wpa_s->last_scan_res[i];
+ *selected_ssid = wpa_scan_res_match(wpa_s, i, bss, group,
+ only_first_ssid);
+ if (!*selected_ssid)
+ continue;
+ wpa_dbg(wpa_s, MSG_DEBUG, " selected BSS " MACSTR
+ " ssid='%s'",
+ MAC2STR(bss->bssid),
+ wpa_ssid_txt(bss->ssid, bss->ssid_len));
+ return bss;
+ }
+
+ return NULL;
+}
+
+
+struct wpa_bss * wpa_supplicant_pick_network(struct wpa_supplicant *wpa_s,
+ struct wpa_ssid **selected_ssid)
+{
+ struct wpa_bss *selected = NULL;
+ int prio;
+ struct wpa_ssid *next_ssid = NULL;
+ struct wpa_ssid *ssid;
+
+ if (wpa_s->last_scan_res == NULL ||
+ wpa_s->last_scan_res_used == 0)
+ return NULL; /* no scan results from last update */
+
+ if (wpa_s->next_ssid) {
+ /* check that next_ssid is still valid */
+ for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next) {
+ if (ssid == wpa_s->next_ssid)
+ break;
+ }
+ next_ssid = ssid;
+ wpa_s->next_ssid = NULL;
+ }
+
+ while (selected == NULL) {
+ for (prio = 0; prio < wpa_s->conf->num_prio; prio++) {
+ if (next_ssid && next_ssid->priority ==
+ wpa_s->conf->pssid[prio]->priority) {
+ selected = wpa_supplicant_select_bss(
+ wpa_s, next_ssid, selected_ssid, 1);
+ if (selected)
+ break;
+ }
+ selected = wpa_supplicant_select_bss(
+ wpa_s, wpa_s->conf->pssid[prio],
+ selected_ssid, 0);
+ if (selected)
+ break;
+ }
+
+ if (selected == NULL && wpa_s->blacklist &&
+ !wpa_s->countermeasures) {
+ wpa_dbg(wpa_s, MSG_DEBUG, "No APs found - clear "
+ "blacklist and try again");
+ wpa_blacklist_clear(wpa_s);
+ wpa_s->blacklist_cleared++;
+ } else if (selected == NULL)
+ break;
+ }
+
+ ssid = *selected_ssid;
+ if (selected && ssid && ssid->mem_only_psk && !ssid->psk_set &&
+ !ssid->passphrase && !ssid->ext_psk) {
+ const char *field_name, *txt = NULL;
+
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ "PSK/passphrase not yet available for the selected network");
+
+ wpas_notify_network_request(wpa_s, ssid,
+ WPA_CTRL_REQ_PSK_PASSPHRASE, NULL);
+
+ field_name = wpa_supplicant_ctrl_req_to_string(
+ WPA_CTRL_REQ_PSK_PASSPHRASE, NULL, &txt);
+ if (field_name == NULL)
+ return NULL;
+
+ wpas_send_ctrl_req(wpa_s, ssid, field_name, txt);
+
+ selected = NULL;
+ }
+
+ return selected;
+}
+
+
+static void wpa_supplicant_req_new_scan(struct wpa_supplicant *wpa_s,
+ int timeout_sec, int timeout_usec)
+{
+ if (!wpa_supplicant_enabled_networks(wpa_s)) {
+ /*
+ * No networks are enabled; short-circuit request so
+ * we don't wait timeout seconds before transitioning
+ * to INACTIVE state.
+ */
+ wpa_dbg(wpa_s, MSG_DEBUG, "Short-circuit new scan request "
+ "since there are no enabled networks");
+ wpa_supplicant_set_state(wpa_s, WPA_INACTIVE);
+ return;
+ }
+
+ wpa_s->scan_for_connection = 1;
+ wpa_supplicant_req_scan(wpa_s, timeout_sec, timeout_usec);
+}
+
+
+int wpa_supplicant_connect(struct wpa_supplicant *wpa_s,
+ struct wpa_bss *selected,
+ struct wpa_ssid *ssid)
+{
+ if (wpas_wps_scan_pbc_overlap(wpa_s, selected, ssid)) {
+ wpa_msg(wpa_s, MSG_INFO, WPS_EVENT_OVERLAP
+ "PBC session overlap");
+ wpas_notify_wps_event_pbc_overlap(wpa_s);
+#ifdef CONFIG_P2P
+ if (wpa_s->p2p_group_interface == P2P_GROUP_INTERFACE_CLIENT ||
+ wpa_s->p2p_in_provisioning) {
+ eloop_register_timeout(0, 0, wpas_p2p_pbc_overlap_cb,
+ wpa_s, NULL);
+ return -1;
+ }
+#endif /* CONFIG_P2P */
+
+#ifdef CONFIG_WPS
+ wpas_wps_pbc_overlap(wpa_s);
+ wpas_wps_cancel(wpa_s);
+#endif /* CONFIG_WPS */
+ return -1;
+ }
+
+ wpa_msg(wpa_s, MSG_DEBUG,
+ "Considering connect request: reassociate: %d selected: "
+ MACSTR " bssid: " MACSTR " pending: " MACSTR
+ " wpa_state: %s ssid=%p current_ssid=%p",
+ wpa_s->reassociate, MAC2STR(selected->bssid),
+ MAC2STR(wpa_s->bssid), MAC2STR(wpa_s->pending_bssid),
+ wpa_supplicant_state_txt(wpa_s->wpa_state),
+ ssid, wpa_s->current_ssid);
+
+ /*
+ * Do not trigger new association unless the BSSID has changed or if
+ * reassociation is requested. If we are in process of associating with
+ * the selected BSSID, do not trigger new attempt.
+ */
+ if (wpa_s->reassociate ||
+ (os_memcmp(selected->bssid, wpa_s->bssid, ETH_ALEN) != 0 &&
+ ((wpa_s->wpa_state != WPA_ASSOCIATING &&
+ wpa_s->wpa_state != WPA_AUTHENTICATING) ||
+ (!is_zero_ether_addr(wpa_s->pending_bssid) &&
+ os_memcmp(selected->bssid, wpa_s->pending_bssid, ETH_ALEN) !=
+ 0) ||
+ (is_zero_ether_addr(wpa_s->pending_bssid) &&
+ ssid != wpa_s->current_ssid)))) {
+ if (wpa_supplicant_scard_init(wpa_s, ssid)) {
+ wpa_supplicant_req_new_scan(wpa_s, 10, 0);
+ return 0;
+ }
+ wpa_msg(wpa_s, MSG_DEBUG, "Request association with " MACSTR,
+ MAC2STR(selected->bssid));
+ wpa_supplicant_associate(wpa_s, selected, ssid);
+ } else {
+ wpa_dbg(wpa_s, MSG_DEBUG, "Already associated or trying to "
+ "connect with the selected AP");
+ }
+
+ return 0;
+}
+
+
+static struct wpa_ssid *
+wpa_supplicant_pick_new_network(struct wpa_supplicant *wpa_s)
+{
+ int prio;
+ struct wpa_ssid *ssid;
+
+ for (prio = 0; prio < wpa_s->conf->num_prio; prio++) {
+ for (ssid = wpa_s->conf->pssid[prio]; ssid; ssid = ssid->pnext)
+ {
+ if (wpas_network_disabled(wpa_s, ssid))
+ continue;
+ if (ssid->mode == IEEE80211_MODE_IBSS ||
+ ssid->mode == IEEE80211_MODE_AP ||
+ ssid->mode == IEEE80211_MODE_MESH)
+ return ssid;
+ }
+ }
+ return NULL;
+}
+
+
+/* TODO: move the rsn_preauth_scan_result*() to be called from notify.c based
+ * on BSS added and BSS changed events */
+static void wpa_supplicant_rsn_preauth_scan_results(
+ struct wpa_supplicant *wpa_s)
+{
+ struct wpa_bss *bss;
+
+ if (rsn_preauth_scan_results(wpa_s->wpa) < 0)
+ return;
+
+ dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) {
+ const u8 *ssid, *rsn;
+
+ ssid = wpa_bss_get_ie(bss, WLAN_EID_SSID);
+ if (ssid == NULL)
+ continue;
+
+ rsn = wpa_bss_get_ie(bss, WLAN_EID_RSN);
+ if (rsn == NULL)
+ continue;
+
+ rsn_preauth_scan_result(wpa_s->wpa, bss->bssid, ssid, rsn);
+ }
+
+}
+
+
+static int wpa_supplicant_need_to_roam(struct wpa_supplicant *wpa_s,
+ struct wpa_bss *selected,
+ struct wpa_ssid *ssid)
+{
+ struct wpa_bss *current_bss = NULL;
+#ifndef CONFIG_NO_ROAMING
+ int min_diff;
+#endif /* CONFIG_NO_ROAMING */
+
+ if (wpa_s->reassociate)
+ return 1; /* explicit request to reassociate */
+ if (wpa_s->wpa_state < WPA_ASSOCIATED)
+ return 1; /* we are not associated; continue */
+ if (wpa_s->current_ssid == NULL)
+ return 1; /* unknown current SSID */
+ if (wpa_s->current_ssid != ssid)
+ return 1; /* different network block */
+
+ if (wpas_driver_bss_selection(wpa_s))
+ return 0; /* Driver-based roaming */
+
+ if (wpa_s->current_ssid->ssid)
+ current_bss = wpa_bss_get(wpa_s, wpa_s->bssid,
+ wpa_s->current_ssid->ssid,
+ wpa_s->current_ssid->ssid_len);
+ if (!current_bss)
+ current_bss = wpa_bss_get_bssid(wpa_s, wpa_s->bssid);
+
+ if (!current_bss)
+ return 1; /* current BSS not seen in scan results */
+
+ if (current_bss == selected)
+ return 0;
+
+ if (selected->last_update_idx > current_bss->last_update_idx)
+ return 1; /* current BSS not seen in the last scan */
+
+#ifndef CONFIG_NO_ROAMING
+ wpa_dbg(wpa_s, MSG_DEBUG, "Considering within-ESS reassociation");
+ wpa_dbg(wpa_s, MSG_DEBUG, "Current BSS: " MACSTR
+ " level=%d snr=%d est_throughput=%u",
+ MAC2STR(current_bss->bssid), current_bss->level,
+ current_bss->snr, current_bss->est_throughput);
+ wpa_dbg(wpa_s, MSG_DEBUG, "Selected BSS: " MACSTR
+ " level=%d snr=%d est_throughput=%u",
+ MAC2STR(selected->bssid), selected->level,
+ selected->snr, selected->est_throughput);
+
+ if (wpa_s->current_ssid->bssid_set &&
+ os_memcmp(selected->bssid, wpa_s->current_ssid->bssid, ETH_ALEN) ==
+ 0) {
+ wpa_dbg(wpa_s, MSG_DEBUG, "Allow reassociation - selected BSS "
+ "has preferred BSSID");
+ return 1;
+ }
+
+ if (selected->est_throughput > current_bss->est_throughput + 5000) {
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ "Allow reassociation - selected BSS has better estimated throughput");
+ return 1;
+ }
+
+ if (current_bss->level < 0 && current_bss->level > selected->level) {
+ wpa_dbg(wpa_s, MSG_DEBUG, "Skip roam - Current BSS has better "
+ "signal level");
+ return 0;
+ }
+
+ min_diff = 2;
+ if (current_bss->level < 0) {
+ if (current_bss->level < -85)
+ min_diff = 1;
+ else if (current_bss->level < -80)
+ min_diff = 2;
+ else if (current_bss->level < -75)
+ min_diff = 3;
+ else if (current_bss->level < -70)
+ min_diff = 4;
+ else
+ min_diff = 5;
+ }
+ if (abs(current_bss->level - selected->level) < min_diff) {
+ wpa_dbg(wpa_s, MSG_DEBUG, "Skip roam - too small difference "
+ "in signal level");
+ return 0;
+ }
+
+ return 1;
+#else /* CONFIG_NO_ROAMING */
+ return 0;
+#endif /* CONFIG_NO_ROAMING */
+}
+
+
+/* Return != 0 if no scan results could be fetched or if scan results should not
+ * be shared with other virtual interfaces. */
+static int _wpa_supplicant_event_scan_results(struct wpa_supplicant *wpa_s,
+ union wpa_event_data *data,
+ int own_request)
+{
+ struct wpa_scan_results *scan_res = NULL;
+ int ret = 0;
+ int ap = 0;
+#ifndef CONFIG_NO_RANDOM_POOL
+ size_t i, num;
+#endif /* CONFIG_NO_RANDOM_POOL */
+
+#ifdef CONFIG_AP
+ if (wpa_s->ap_iface)
+ ap = 1;
+#endif /* CONFIG_AP */
+
+ wpa_supplicant_notify_scanning(wpa_s, 0);
+
+ scan_res = wpa_supplicant_get_scan_results(wpa_s,
+ data ? &data->scan_info :
+ NULL, 1);
+ if (scan_res == NULL) {
+ if (wpa_s->conf->ap_scan == 2 || ap ||
+ wpa_s->scan_res_handler == scan_only_handler)
+ return -1;
+ if (!own_request)
+ return -1;
+ wpa_dbg(wpa_s, MSG_DEBUG, "Failed to get scan results - try "
+ "scanning again");
+ wpa_supplicant_req_new_scan(wpa_s, 1, 0);
+ ret = -1;
+ goto scan_work_done;
+ }
+
+#ifndef CONFIG_NO_RANDOM_POOL
+ num = scan_res->num;
+ if (num > 10)
+ num = 10;
+ for (i = 0; i < num; i++) {
+ u8 buf[5];
+ struct wpa_scan_res *res = scan_res->res[i];
+ buf[0] = res->bssid[5];
+ buf[1] = res->qual & 0xff;
+ buf[2] = res->noise & 0xff;
+ buf[3] = res->level & 0xff;
+ buf[4] = res->tsf & 0xff;
+ random_add_randomness(buf, sizeof(buf));
+ }
+#endif /* CONFIG_NO_RANDOM_POOL */
+
+ if (own_request && wpa_s->scan_res_handler &&
+ (wpa_s->own_scan_running || !wpa_s->radio->external_scan_running)) {
+ void (*scan_res_handler)(struct wpa_supplicant *wpa_s,
+ struct wpa_scan_results *scan_res);
+
+ scan_res_handler = wpa_s->scan_res_handler;
+ wpa_s->scan_res_handler = NULL;
+ scan_res_handler(wpa_s, scan_res);
+ ret = -2;
+ goto scan_work_done;
+ }
+
+ if (ap) {
+ wpa_dbg(wpa_s, MSG_DEBUG, "Ignore scan results in AP mode");
+#ifdef CONFIG_AP
+ if (wpa_s->ap_iface->scan_cb)
+ wpa_s->ap_iface->scan_cb(wpa_s->ap_iface);
+#endif /* CONFIG_AP */
+ goto scan_work_done;
+ }
+
+ wpa_dbg(wpa_s, MSG_DEBUG, "New scan results available (own=%u ext=%u)",
+ wpa_s->own_scan_running, wpa_s->radio->external_scan_running);
+ if (wpa_s->last_scan_req == MANUAL_SCAN_REQ &&
+ wpa_s->manual_scan_use_id && wpa_s->own_scan_running) {
+ wpa_msg_ctrl(wpa_s, MSG_INFO, WPA_EVENT_SCAN_RESULTS "id=%u",
+ wpa_s->manual_scan_id);
+ wpa_s->manual_scan_use_id = 0;
+ } else {
+ wpa_msg_ctrl(wpa_s, MSG_INFO, WPA_EVENT_SCAN_RESULTS);
+ }
+ wpas_notify_scan_results(wpa_s);
+
+ wpas_notify_scan_done(wpa_s, 1);
+
+ if (!wpa_s->own_scan_running && wpa_s->radio->external_scan_running) {
+ wpa_dbg(wpa_s, MSG_DEBUG, "Do not use results from externally requested scan operation for network selection");
+ wpa_scan_results_free(scan_res);
+ return 0;
+ }
+
+ if (wnm_scan_process(wpa_s, 1) > 0)
+ goto scan_work_done;
+
+ if (sme_proc_obss_scan(wpa_s) > 0)
+ goto scan_work_done;
+
+ if ((wpa_s->conf->ap_scan == 2 && !wpas_wps_searching(wpa_s)))
+ goto scan_work_done;
+
+ if (autoscan_notify_scan(wpa_s, scan_res))
+ goto scan_work_done;
+
+ if (wpa_s->disconnected) {
+ wpa_supplicant_set_state(wpa_s, WPA_DISCONNECTED);
+ goto scan_work_done;
+ }
+
+ if (!wpas_driver_bss_selection(wpa_s) &&
+ bgscan_notify_scan(wpa_s, scan_res) == 1)
+ goto scan_work_done;
+
+ wpas_wps_update_ap_info(wpa_s, scan_res);
+
+ wpa_scan_results_free(scan_res);
+
+ if (wpa_s->scan_work) {
+ struct wpa_radio_work *work = wpa_s->scan_work;
+ wpa_s->scan_work = NULL;
+ radio_work_done(work);
+ }
+
+ return wpas_select_network_from_last_scan(wpa_s, 1, own_request);
+
+scan_work_done:
+ wpa_scan_results_free(scan_res);
+ if (wpa_s->scan_work) {
+ struct wpa_radio_work *work = wpa_s->scan_work;
+ wpa_s->scan_work = NULL;
+ radio_work_done(work);
+ }
+ return ret;
+}
+
+
+static int wpas_select_network_from_last_scan(struct wpa_supplicant *wpa_s,
+ int new_scan, int own_request)
+{
+ struct wpa_bss *selected;
+ struct wpa_ssid *ssid = NULL;
+ int time_to_reenable = wpas_reenabled_network_time(wpa_s);
+
+ if (time_to_reenable > 0) {
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ "Postpone network selection by %d seconds since all networks are disabled",
+ time_to_reenable);
+ eloop_cancel_timeout(wpas_network_reenabled, wpa_s, NULL);
+ eloop_register_timeout(time_to_reenable, 0,
+ wpas_network_reenabled, wpa_s, NULL);
+ return 0;
+ }
+
+ if (wpa_s->p2p_mgmt)
+ return 0; /* no normal connection on p2p_mgmt interface */
+
+ selected = wpa_supplicant_pick_network(wpa_s, &ssid);
+
+ if (selected) {
+ int skip;
+ skip = !wpa_supplicant_need_to_roam(wpa_s, selected, ssid);
+ if (skip) {
+ if (new_scan)
+ wpa_supplicant_rsn_preauth_scan_results(wpa_s);
+ return 0;
+ }
+
+ if (wpa_supplicant_connect(wpa_s, selected, ssid) < 0) {
+ wpa_dbg(wpa_s, MSG_DEBUG, "Connect failed");
+ return -1;
+ }
+ if (new_scan)
+ wpa_supplicant_rsn_preauth_scan_results(wpa_s);
+ /*
+ * Do not notify other virtual radios of scan results since we do not
+ * want them to start other associations at the same time.
+ */
+ return 1;
+ } else {
+#ifdef CONFIG_MESH
+ if (wpa_s->ifmsh) {
+ wpa_msg(wpa_s, MSG_INFO,
+ "Avoiding join because we already joined a mesh group");
+ return 0;
+ }
+#endif /* CONFIG_MESH */
+ wpa_dbg(wpa_s, MSG_DEBUG, "No suitable network found");
+ ssid = wpa_supplicant_pick_new_network(wpa_s);
+ if (ssid) {
+ wpa_dbg(wpa_s, MSG_DEBUG, "Setup a new network");
+ wpa_supplicant_associate(wpa_s, NULL, ssid);
+ if (new_scan)
+ wpa_supplicant_rsn_preauth_scan_results(wpa_s);
+ } else if (own_request) {
+ /*
+ * No SSID found. If SCAN results are as a result of
+ * own scan request and not due to a scan request on
+ * another shared interface, try another scan.
+ */
+ int timeout_sec = wpa_s->scan_interval;
+ int timeout_usec = 0;
+#ifdef CONFIG_P2P
+ int res;
+
+ res = wpas_p2p_scan_no_go_seen(wpa_s);
+ if (res == 2)
+ return 2;
+ if (res == 1)
+ return 0;
+
+ if (wpa_s->p2p_in_provisioning ||
+ wpa_s->show_group_started ||
+ wpa_s->p2p_in_invitation) {
+ /*
+ * Use shorter wait during P2P Provisioning
+ * state and during P2P join-a-group operation
+ * to speed up group formation.
+ */
+ timeout_sec = 0;
+ timeout_usec = 250000;
+ wpa_supplicant_req_new_scan(wpa_s, timeout_sec,
+ timeout_usec);
+ return 0;
+ }
+#endif /* CONFIG_P2P */
+#ifdef CONFIG_INTERWORKING
+ if (wpa_s->conf->auto_interworking &&
+ wpa_s->conf->interworking &&
+ wpa_s->conf->cred) {
+ wpa_dbg(wpa_s, MSG_DEBUG, "Interworking: "
+ "start ANQP fetch since no matching "
+ "networks found");
+ wpa_s->network_select = 1;
+ wpa_s->auto_network_select = 1;
+ interworking_start_fetch_anqp(wpa_s);
+ return 1;
+ }
+#endif /* CONFIG_INTERWORKING */
+#ifdef CONFIG_WPS
+ if (wpa_s->after_wps > 0 || wpas_wps_searching(wpa_s)) {
+ wpa_dbg(wpa_s, MSG_DEBUG, "Use shorter wait during WPS processing");
+ timeout_sec = 0;
+ timeout_usec = 500000;
+ wpa_supplicant_req_new_scan(wpa_s, timeout_sec,
+ timeout_usec);
+ return 0;
+ }
+#endif /* CONFIG_WPS */
+ if (wpa_supplicant_req_sched_scan(wpa_s))
+ wpa_supplicant_req_new_scan(wpa_s, timeout_sec,
+ timeout_usec);
+
+ wpa_msg_ctrl(wpa_s, MSG_INFO,
+ WPA_EVENT_NETWORK_NOT_FOUND);
+ }
+ }
+ return 0;
+}
+
+
+static int wpa_supplicant_event_scan_results(struct wpa_supplicant *wpa_s,
+ union wpa_event_data *data)
+{
+ struct wpa_supplicant *ifs;
+ int res;
+
+ res = _wpa_supplicant_event_scan_results(wpa_s, data, 1);
+ if (res == 2) {
+ /*
+ * Interface may have been removed, so must not dereference
+ * wpa_s after this.
+ */
+ return 1;
+ }
+ if (res != 0) {
+ /*
+ * If no scan results could be fetched, then no need to
+ * notify those interfaces that did not actually request
+ * this scan. Similarly, if scan results started a new operation on this
+ * interface, do not notify other interfaces to avoid concurrent
+ * operations during a connection attempt.
+ */
+ return 0;
+ }
+
+ /*
+ * Check other interfaces to see if they share the same radio. If
+ * so, they get updated with this same scan info.
+ */
+ dl_list_for_each(ifs, &wpa_s->radio->ifaces, struct wpa_supplicant,
+ radio_list) {
+ if (ifs != wpa_s) {
+ wpa_printf(MSG_DEBUG, "%s: Updating scan results from "
+ "sibling", ifs->ifname);
+ _wpa_supplicant_event_scan_results(ifs, data, 0);
+ }
+ }
+
+ return 0;
+}
+
+#endif /* CONFIG_NO_SCAN_PROCESSING */
+
+
+int wpa_supplicant_fast_associate(struct wpa_supplicant *wpa_s)
+{
+#ifdef CONFIG_NO_SCAN_PROCESSING
+ return -1;
+#else /* CONFIG_NO_SCAN_PROCESSING */
+ struct os_reltime now;
+
+ if (wpa_s->last_scan_res_used == 0)
+ return -1;
+
+ os_get_reltime(&now);
+ if (os_reltime_expired(&now, &wpa_s->last_scan, 5)) {
+ wpa_printf(MSG_DEBUG, "Fast associate: Old scan results");
+ return -1;
+ }
+
+ return wpas_select_network_from_last_scan(wpa_s, 0, 1);
+#endif /* CONFIG_NO_SCAN_PROCESSING */
+}
+
+#ifdef CONFIG_WNM
+
+static void wnm_bss_keep_alive(void *eloop_ctx, void *sock_ctx)
+{
+ struct wpa_supplicant *wpa_s = eloop_ctx;
+
+ if (wpa_s->wpa_state < WPA_ASSOCIATED)
+ return;
+
+ if (!wpa_s->no_keep_alive) {
+ wpa_printf(MSG_DEBUG, "WNM: Send keep-alive to AP " MACSTR,
+ MAC2STR(wpa_s->bssid));
+ /* TODO: could skip this if normal data traffic has been sent */
+ /* TODO: Consider using some more appropriate data frame for
+ * this */
+ if (wpa_s->l2)
+ l2_packet_send(wpa_s->l2, wpa_s->bssid, 0x0800,
+ (u8 *) "", 0);
+ }
+
+#ifdef CONFIG_SME
+ if (wpa_s->sme.bss_max_idle_period) {
+ unsigned int msec;
+ msec = wpa_s->sme.bss_max_idle_period * 1024; /* times 1000 */
+ if (msec > 100)
+ msec -= 100;
+ eloop_register_timeout(msec / 1000, msec % 1000 * 1000,
+ wnm_bss_keep_alive, wpa_s, NULL);
+ }
+#endif /* CONFIG_SME */
+}
+
+
+static void wnm_process_assoc_resp(struct wpa_supplicant *wpa_s,
+ const u8 *ies, size_t ies_len)
+{
+ struct ieee802_11_elems elems;
+
+ if (ies == NULL)
+ return;
+
+ if (ieee802_11_parse_elems(ies, ies_len, &elems, 1) == ParseFailed)
+ return;
+
+#ifdef CONFIG_SME
+ if (elems.bss_max_idle_period) {
+ unsigned int msec;
+ wpa_s->sme.bss_max_idle_period =
+ WPA_GET_LE16(elems.bss_max_idle_period);
+ wpa_printf(MSG_DEBUG, "WNM: BSS Max Idle Period: %u (* 1000 "
+ "TU)%s", wpa_s->sme.bss_max_idle_period,
+ (elems.bss_max_idle_period[2] & 0x01) ?
+ " (protected keep-live required)" : "");
+ if (wpa_s->sme.bss_max_idle_period == 0)
+ wpa_s->sme.bss_max_idle_period = 1;
+ if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME) {
+ eloop_cancel_timeout(wnm_bss_keep_alive, wpa_s, NULL);
+ /* msec times 1000 */
+ msec = wpa_s->sme.bss_max_idle_period * 1024;
+ if (msec > 100)
+ msec -= 100;
+ eloop_register_timeout(msec / 1000, msec % 1000 * 1000,
+ wnm_bss_keep_alive, wpa_s,
+ NULL);
+ }
+ }
+#endif /* CONFIG_SME */
+}
+
+#endif /* CONFIG_WNM */
+
+
+void wnm_bss_keep_alive_deinit(struct wpa_supplicant *wpa_s)
+{
+#ifdef CONFIG_WNM
+ eloop_cancel_timeout(wnm_bss_keep_alive, wpa_s, NULL);
+#endif /* CONFIG_WNM */
+}
+
+
+#ifdef CONFIG_INTERWORKING
+
+static int wpas_qos_map_set(struct wpa_supplicant *wpa_s, const u8 *qos_map,
+ size_t len)
+{
+ int res;
+
+ wpa_hexdump(MSG_DEBUG, "Interworking: QoS Map Set", qos_map, len);
+ res = wpa_drv_set_qos_map(wpa_s, qos_map, len);
+ if (res) {
+ wpa_printf(MSG_DEBUG, "Interworking: Failed to configure QoS Map Set to the driver");
+ }
+
+ return res;
+}
+
+
+static void interworking_process_assoc_resp(struct wpa_supplicant *wpa_s,
+ const u8 *ies, size_t ies_len)
+{
+ struct ieee802_11_elems elems;
+
+ if (ies == NULL)
+ return;
+
+ if (ieee802_11_parse_elems(ies, ies_len, &elems, 1) == ParseFailed)
+ return;
+
+ if (elems.qos_map_set) {
+ wpas_qos_map_set(wpa_s, elems.qos_map_set,
+ elems.qos_map_set_len);
+ }
+}
+
+#endif /* CONFIG_INTERWORKING */
+
+
+static int wpa_supplicant_event_associnfo(struct wpa_supplicant *wpa_s,
+ union wpa_event_data *data)
+{
+ int l, len, found = 0, wpa_found, rsn_found;
+ const u8 *p;
+#ifdef CONFIG_IEEE80211R
+ u8 bssid[ETH_ALEN];
+#endif /* CONFIG_IEEE80211R */
+
+ wpa_dbg(wpa_s, MSG_DEBUG, "Association info event");
+ if (data->assoc_info.req_ies)
+ wpa_hexdump(MSG_DEBUG, "req_ies", data->assoc_info.req_ies,
+ data->assoc_info.req_ies_len);
+ if (data->assoc_info.resp_ies) {
+ wpa_hexdump(MSG_DEBUG, "resp_ies", data->assoc_info.resp_ies,
+ data->assoc_info.resp_ies_len);
+#ifdef CONFIG_TDLS
+ wpa_tdls_assoc_resp_ies(wpa_s->wpa, data->assoc_info.resp_ies,
+ data->assoc_info.resp_ies_len);
+#endif /* CONFIG_TDLS */
+#ifdef CONFIG_WNM
+ wnm_process_assoc_resp(wpa_s, data->assoc_info.resp_ies,
+ data->assoc_info.resp_ies_len);
+#endif /* CONFIG_WNM */
+#ifdef CONFIG_INTERWORKING
+ interworking_process_assoc_resp(wpa_s, data->assoc_info.resp_ies,
+ data->assoc_info.resp_ies_len);
+#endif /* CONFIG_INTERWORKING */
+ }
+ if (data->assoc_info.beacon_ies)
+ wpa_hexdump(MSG_DEBUG, "beacon_ies",
+ data->assoc_info.beacon_ies,
+ data->assoc_info.beacon_ies_len);
+ if (data->assoc_info.freq)
+ wpa_dbg(wpa_s, MSG_DEBUG, "freq=%u MHz",
+ data->assoc_info.freq);
+
+ p = data->assoc_info.req_ies;
+ l = data->assoc_info.req_ies_len;
+
+ /* Go through the IEs and make a copy of the WPA/RSN IE, if present. */
+ while (p && l >= 2) {
+ len = p[1] + 2;
+ if (len > l) {
+ wpa_hexdump(MSG_DEBUG, "Truncated IE in assoc_info",
+ p, l);
+ break;
+ }
+ if ((p[0] == WLAN_EID_VENDOR_SPECIFIC && p[1] >= 6 &&
+ (os_memcmp(&p[2], "\x00\x50\xF2\x01\x01\x00", 6) == 0)) ||
+ (p[0] == WLAN_EID_RSN && p[1] >= 2)) {
+ if (wpa_sm_set_assoc_wpa_ie(wpa_s->wpa, p, len))
+ break;
+ found = 1;
+ wpa_find_assoc_pmkid(wpa_s);
+ break;
+ }
+ l -= len;
+ p += len;
+ }
+ if (!found && data->assoc_info.req_ies)
+ wpa_sm_set_assoc_wpa_ie(wpa_s->wpa, NULL, 0);
+
+#ifdef CONFIG_IEEE80211R
+#ifdef CONFIG_SME
+ if (wpa_s->sme.auth_alg == WPA_AUTH_ALG_FT) {
+ if (wpa_drv_get_bssid(wpa_s, bssid) < 0 ||
+ wpa_ft_validate_reassoc_resp(wpa_s->wpa,
+ data->assoc_info.resp_ies,
+ data->assoc_info.resp_ies_len,
+ bssid) < 0) {
+ wpa_dbg(wpa_s, MSG_DEBUG, "FT: Validation of "
+ "Reassociation Response failed");
+ wpa_supplicant_deauthenticate(
+ wpa_s, WLAN_REASON_INVALID_IE);
+ return -1;
+ }
+ }
+
+ p = data->assoc_info.resp_ies;
+ l = data->assoc_info.resp_ies_len;
+
+#ifdef CONFIG_WPS_STRICT
+ if (p && wpa_s->current_ssid &&
+ wpa_s->current_ssid->key_mgmt == WPA_KEY_MGMT_WPS) {
+ struct wpabuf *wps;
+ wps = ieee802_11_vendor_ie_concat(p, l, WPS_IE_VENDOR_TYPE);
+ if (wps == NULL) {
+ wpa_msg(wpa_s, MSG_INFO, "WPS-STRICT: AP did not "
+ "include WPS IE in (Re)Association Response");
+ return -1;
+ }
+
+ if (wps_validate_assoc_resp(wps) < 0) {
+ wpabuf_free(wps);
+ wpa_supplicant_deauthenticate(
+ wpa_s, WLAN_REASON_INVALID_IE);
+ return -1;
+ }
+ wpabuf_free(wps);
+ }
+#endif /* CONFIG_WPS_STRICT */
+
+ /* Go through the IEs and make a copy of the MDIE, if present. */
+ while (p && l >= 2) {
+ len = p[1] + 2;
+ if (len > l) {
+ wpa_hexdump(MSG_DEBUG, "Truncated IE in assoc_info",
+ p, l);
+ break;
+ }
+ if (p[0] == WLAN_EID_MOBILITY_DOMAIN &&
+ p[1] >= MOBILITY_DOMAIN_ID_LEN) {
+ wpa_s->sme.ft_used = 1;
+ os_memcpy(wpa_s->sme.mobility_domain, p + 2,
+ MOBILITY_DOMAIN_ID_LEN);
+ break;
+ }
+ l -= len;
+ p += len;
+ }
+#endif /* CONFIG_SME */
+
+ /* Process FT when SME is in the driver */
+ if (!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME) &&
+ wpa_ft_is_completed(wpa_s->wpa)) {
+ if (wpa_drv_get_bssid(wpa_s, bssid) < 0 ||
+ wpa_ft_validate_reassoc_resp(wpa_s->wpa,
+ data->assoc_info.resp_ies,
+ data->assoc_info.resp_ies_len,
+ bssid) < 0) {
+ wpa_dbg(wpa_s, MSG_DEBUG, "FT: Validation of "
+ "Reassociation Response failed");
+ wpa_supplicant_deauthenticate(
+ wpa_s, WLAN_REASON_INVALID_IE);
+ return -1;
+ }
+ wpa_dbg(wpa_s, MSG_DEBUG, "FT: Reassociation Response done");
+ }
+
+ wpa_sm_set_ft_params(wpa_s->wpa, data->assoc_info.resp_ies,
+ data->assoc_info.resp_ies_len);
+#endif /* CONFIG_IEEE80211R */
+
+ /* WPA/RSN IE from Beacon/ProbeResp */
+ p = data->assoc_info.beacon_ies;
+ l = data->assoc_info.beacon_ies_len;
+
+ /* Go through the IEs and make a copy of the WPA/RSN IEs, if present.
+ */
+ wpa_found = rsn_found = 0;
+ while (p && l >= 2) {
+ len = p[1] + 2;
+ if (len > l) {
+ wpa_hexdump(MSG_DEBUG, "Truncated IE in beacon_ies",
+ p, l);
+ break;
+ }
+ if (!wpa_found &&
+ p[0] == WLAN_EID_VENDOR_SPECIFIC && p[1] >= 6 &&
+ os_memcmp(&p[2], "\x00\x50\xF2\x01\x01\x00", 6) == 0) {
+ wpa_found = 1;
+ wpa_sm_set_ap_wpa_ie(wpa_s->wpa, p, len);
+ }
+
+ if (!rsn_found &&
+ p[0] == WLAN_EID_RSN && p[1] >= 2) {
+ rsn_found = 1;
+ wpa_sm_set_ap_rsn_ie(wpa_s->wpa, p, len);
+ }
+
+ l -= len;
+ p += len;
+ }
+
+ if (!wpa_found && data->assoc_info.beacon_ies)
+ wpa_sm_set_ap_wpa_ie(wpa_s->wpa, NULL, 0);
+ if (!rsn_found && data->assoc_info.beacon_ies)
+ wpa_sm_set_ap_rsn_ie(wpa_s->wpa, NULL, 0);
+ if (wpa_found || rsn_found)
+ wpa_s->ap_ies_from_associnfo = 1;
+
+#ifdef CONFIG_FST
+ wpabuf_free(wpa_s->received_mb_ies);
+ wpa_s->received_mb_ies = NULL;
+ if (wpa_s->fst) {
+ struct mb_ies_info mb_ies;
+
+ wpa_printf(MSG_DEBUG, "Looking for MB IE");
+ if (!mb_ies_info_by_ies(&mb_ies, data->assoc_info.resp_ies,
+ data->assoc_info.resp_ies_len))
+ wpa_s->received_mb_ies = mb_ies_by_info(&mb_ies);
+ }
+#endif /* CONFIG_FST */
+
+ if (wpa_s->assoc_freq && data->assoc_info.freq &&
+ wpa_s->assoc_freq != data->assoc_info.freq) {
+ wpa_printf(MSG_DEBUG, "Operating frequency changed from "
+ "%u to %u MHz",
+ wpa_s->assoc_freq, data->assoc_info.freq);
+ wpa_supplicant_update_scan_results(wpa_s);
+ }
+
+ wpa_s->assoc_freq = data->assoc_info.freq;
+
+ return 0;
+}
+
+
+static int wpa_supplicant_assoc_update_ie(struct wpa_supplicant *wpa_s)
+{
+ const u8 *bss_wpa = NULL, *bss_rsn = NULL;
+
+ if (!wpa_s->current_bss || !wpa_s->current_ssid)
+ return -1;
+
+ if (!wpa_key_mgmt_wpa_any(wpa_s->current_ssid->key_mgmt))
+ return 0;
+
+ bss_wpa = wpa_bss_get_vendor_ie(wpa_s->current_bss,
+ WPA_IE_VENDOR_TYPE);
+ bss_rsn = wpa_bss_get_ie(wpa_s->current_bss, WLAN_EID_RSN);
+
+ if (wpa_sm_set_ap_wpa_ie(wpa_s->wpa, bss_wpa,
+ bss_wpa ? 2 + bss_wpa[1] : 0) ||
+ wpa_sm_set_ap_rsn_ie(wpa_s->wpa, bss_rsn,
+ bss_rsn ? 2 + bss_rsn[1] : 0))
+ return -1;
+
+ return 0;
+}
+
+
+static void wpa_supplicant_event_assoc(struct wpa_supplicant *wpa_s,
+ union wpa_event_data *data)
+{
+ u8 bssid[ETH_ALEN];
+ int ft_completed;
+ int new_bss = 0;
+
+#ifdef CONFIG_AP
+ if (wpa_s->ap_iface) {
+ if (!data)
+ return;
+ hostapd_notif_assoc(wpa_s->ap_iface->bss[0],
+ data->assoc_info.addr,
+ data->assoc_info.req_ies,
+ data->assoc_info.req_ies_len,
+ data->assoc_info.reassoc);
+ return;
+ }
+#endif /* CONFIG_AP */
+
+ eloop_cancel_timeout(wpas_network_reenabled, wpa_s, NULL);
+
+ ft_completed = wpa_ft_is_completed(wpa_s->wpa);
+ if (data && wpa_supplicant_event_associnfo(wpa_s, data) < 0)
+ return;
+
+ if (wpa_drv_get_bssid(wpa_s, bssid) < 0) {
+ wpa_dbg(wpa_s, MSG_ERROR, "Failed to get BSSID");
+ wpa_supplicant_deauthenticate(
+ wpa_s, WLAN_REASON_DEAUTH_LEAVING);
+ return;
+ }
+
+ wpa_supplicant_set_state(wpa_s, WPA_ASSOCIATED);
+ if (os_memcmp(bssid, wpa_s->bssid, ETH_ALEN) != 0) {
+ wpa_dbg(wpa_s, MSG_DEBUG, "Associated to a new BSS: BSSID="
+ MACSTR, MAC2STR(bssid));
+ new_bss = 1;
+ random_add_randomness(bssid, ETH_ALEN);
+ os_memcpy(wpa_s->bssid, bssid, ETH_ALEN);
+ os_memset(wpa_s->pending_bssid, 0, ETH_ALEN);
+ wpas_notify_bssid_changed(wpa_s);
+
+ if (wpa_supplicant_dynamic_keys(wpa_s) && !ft_completed) {
+ wpa_clear_keys(wpa_s, bssid);
+ }
+ if (wpa_supplicant_select_config(wpa_s) < 0) {
+ wpa_supplicant_deauthenticate(
+ wpa_s, WLAN_REASON_DEAUTH_LEAVING);
+ return;
+ }
+ }
+
+ if (wpa_s->conf->ap_scan == 1 &&
+ wpa_s->drv_flags & WPA_DRIVER_FLAGS_BSS_SELECTION) {
+ if (wpa_supplicant_assoc_update_ie(wpa_s) < 0 && new_bss)
+ wpa_msg(wpa_s, MSG_WARNING,
+ "WPA/RSN IEs not updated");
+ }
+
+#ifdef CONFIG_SME
+ os_memcpy(wpa_s->sme.prev_bssid, bssid, ETH_ALEN);
+ wpa_s->sme.prev_bssid_set = 1;
+ wpa_s->sme.last_unprot_disconnect.sec = 0;
+#endif /* CONFIG_SME */
+
+ wpa_msg(wpa_s, MSG_INFO, "Associated with " MACSTR, MAC2STR(bssid));
+ if (wpa_s->current_ssid) {
+ /* When using scanning (ap_scan=1), SIM PC/SC interface can be
+ * initialized before association, but for other modes,
+ * initialize PC/SC here, if the current configuration needs
+ * smartcard or SIM/USIM. */
+ wpa_supplicant_scard_init(wpa_s, wpa_s->current_ssid);
+ }
+ wpa_sm_notify_assoc(wpa_s->wpa, bssid);
+ if (wpa_s->l2)
+ l2_packet_notify_auth_start(wpa_s->l2);
+
+ /*
+ * Set portEnabled first to FALSE in order to get EAP state machine out
+ * of the SUCCESS state and eapSuccess cleared. Without this, EAPOL PAE
+ * state machine may transit to AUTHENTICATING state based on obsolete
+ * eapSuccess and then trigger BE_AUTH to SUCCESS and PAE to
+ * AUTHENTICATED without ever giving chance to EAP state machine to
+ * reset the state.
+ */
+ if (!ft_completed) {
+ eapol_sm_notify_portEnabled(wpa_s->eapol, FALSE);
+ eapol_sm_notify_portValid(wpa_s->eapol, FALSE);
+ }
+ if (wpa_key_mgmt_wpa_psk(wpa_s->key_mgmt) || ft_completed)
+ eapol_sm_notify_eap_success(wpa_s->eapol, FALSE);
+ /* 802.1X::portControl = Auto */
+ eapol_sm_notify_portEnabled(wpa_s->eapol, TRUE);
+ wpa_s->eapol_received = 0;
+ if (wpa_s->key_mgmt == WPA_KEY_MGMT_NONE ||
+ wpa_s->key_mgmt == WPA_KEY_MGMT_WPA_NONE ||
+ (wpa_s->current_ssid &&
+ wpa_s->current_ssid->mode == IEEE80211_MODE_IBSS)) {
+ if (wpa_s->current_ssid &&
+ wpa_s->key_mgmt == WPA_KEY_MGMT_WPA_NONE &&
+ (wpa_s->drv_flags &
+ WPA_DRIVER_FLAGS_SET_KEYS_AFTER_ASSOC_DONE)) {
+ /*
+ * Set the key after having received joined-IBSS event
+ * from the driver.
+ */
+ wpa_supplicant_set_wpa_none_key(wpa_s,
+ wpa_s->current_ssid);
+ }
+ wpa_supplicant_cancel_auth_timeout(wpa_s);
+ wpa_supplicant_set_state(wpa_s, WPA_COMPLETED);
+ } else if (!ft_completed) {
+ /* Timeout for receiving the first EAPOL packet */
+ wpa_supplicant_req_auth_timeout(wpa_s, 10, 0);
+ }
+ wpa_supplicant_cancel_scan(wpa_s);
+
+ if ((wpa_s->drv_flags & WPA_DRIVER_FLAGS_4WAY_HANDSHAKE) &&
+ wpa_key_mgmt_wpa_psk(wpa_s->key_mgmt)) {
+ /*
+ * We are done; the driver will take care of RSN 4-way
+ * handshake.
+ */
+ wpa_supplicant_cancel_auth_timeout(wpa_s);
+ wpa_supplicant_set_state(wpa_s, WPA_COMPLETED);
+ eapol_sm_notify_portValid(wpa_s->eapol, TRUE);
+ eapol_sm_notify_eap_success(wpa_s->eapol, TRUE);
+ } else if ((wpa_s->drv_flags & WPA_DRIVER_FLAGS_4WAY_HANDSHAKE) &&
+ wpa_key_mgmt_wpa_ieee8021x(wpa_s->key_mgmt)) {
+ /*
+ * The driver will take care of RSN 4-way handshake, so we need
+ * to allow EAPOL supplicant to complete its work without
+ * waiting for WPA supplicant.
+ */
+ eapol_sm_notify_portValid(wpa_s->eapol, TRUE);
+ } else if (ft_completed) {
+ /*
+ * FT protocol completed - make sure EAPOL state machine ends
+ * up in authenticated.
+ */
+ wpa_supplicant_cancel_auth_timeout(wpa_s);
+ wpa_supplicant_set_state(wpa_s, WPA_COMPLETED);
+ eapol_sm_notify_portValid(wpa_s->eapol, TRUE);
+ eapol_sm_notify_eap_success(wpa_s->eapol, TRUE);
+ }
+
+ wpa_s->last_eapol_matches_bssid = 0;
+
+ if (wpa_s->pending_eapol_rx) {
+ struct os_reltime now, age;
+ os_get_reltime(&now);
+ os_reltime_sub(&now, &wpa_s->pending_eapol_rx_time, &age);
+ if (age.sec == 0 && age.usec < 100000 &&
+ os_memcmp(wpa_s->pending_eapol_rx_src, bssid, ETH_ALEN) ==
+ 0) {
+ wpa_dbg(wpa_s, MSG_DEBUG, "Process pending EAPOL "
+ "frame that was received just before "
+ "association notification");
+ wpa_supplicant_rx_eapol(
+ wpa_s, wpa_s->pending_eapol_rx_src,
+ wpabuf_head(wpa_s->pending_eapol_rx),
+ wpabuf_len(wpa_s->pending_eapol_rx));
+ }
+ wpabuf_free(wpa_s->pending_eapol_rx);
+ wpa_s->pending_eapol_rx = NULL;
+ }
+
+ if ((wpa_s->key_mgmt == WPA_KEY_MGMT_NONE ||
+ wpa_s->key_mgmt == WPA_KEY_MGMT_IEEE8021X_NO_WPA) &&
+ wpa_s->current_ssid &&
+ (wpa_s->drv_flags & WPA_DRIVER_FLAGS_SET_KEYS_AFTER_ASSOC_DONE)) {
+ /* Set static WEP keys again */
+ wpa_set_wep_keys(wpa_s, wpa_s->current_ssid);
+ }
+
+#ifdef CONFIG_IBSS_RSN
+ if (wpa_s->current_ssid &&
+ wpa_s->current_ssid->mode == WPAS_MODE_IBSS &&
+ wpa_s->key_mgmt != WPA_KEY_MGMT_NONE &&
+ wpa_s->key_mgmt != WPA_KEY_MGMT_WPA_NONE &&
+ wpa_s->ibss_rsn == NULL) {
+ wpa_s->ibss_rsn = ibss_rsn_init(wpa_s);
+ if (!wpa_s->ibss_rsn) {
+ wpa_msg(wpa_s, MSG_INFO, "Failed to init IBSS RSN");
+ wpa_supplicant_deauthenticate(
+ wpa_s, WLAN_REASON_DEAUTH_LEAVING);
+ return;
+ }
+
+ ibss_rsn_set_psk(wpa_s->ibss_rsn, wpa_s->current_ssid->psk);
+ }
+#endif /* CONFIG_IBSS_RSN */
+
+ wpas_wps_notify_assoc(wpa_s, bssid);
+
+ if (data) {
+ wmm_ac_notify_assoc(wpa_s, data->assoc_info.resp_ies,
+ data->assoc_info.resp_ies_len,
+ &data->assoc_info.wmm_params);
+
+ if (wpa_s->reassoc_same_bss)
+ wmm_ac_restore_tspecs(wpa_s);
+ }
+}
+
+
+static int disconnect_reason_recoverable(u16 reason_code)
+{
+ return reason_code == WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY ||
+ reason_code == WLAN_REASON_CLASS2_FRAME_FROM_NONAUTH_STA ||
+ reason_code == WLAN_REASON_CLASS3_FRAME_FROM_NONASSOC_STA;
+}
+
+
+static void wpa_supplicant_event_disassoc(struct wpa_supplicant *wpa_s,
+ u16 reason_code,
+ int locally_generated)
+{
+ const u8 *bssid;
+
+ if (wpa_s->key_mgmt == WPA_KEY_MGMT_WPA_NONE) {
+ /*
+ * At least Host AP driver and a Prism3 card seemed to be
+ * generating streams of disconnected events when configuring
+ * IBSS for WPA-None. Ignore them for now.
+ */
+ return;
+ }
+
+ bssid = wpa_s->bssid;
+ if (is_zero_ether_addr(bssid))
+ bssid = wpa_s->pending_bssid;
+
+ if (!is_zero_ether_addr(bssid) ||
+ wpa_s->wpa_state >= WPA_AUTHENTICATING) {
+ wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_DISCONNECTED "bssid=" MACSTR
+ " reason=%d%s",
+ MAC2STR(bssid), reason_code,
+ locally_generated ? " locally_generated=1" : "");
+ }
+}
+
+
+static int could_be_psk_mismatch(struct wpa_supplicant *wpa_s, u16 reason_code,
+ int locally_generated)
+{
+ if (wpa_s->wpa_state != WPA_4WAY_HANDSHAKE ||
+ !wpa_key_mgmt_wpa_psk(wpa_s->key_mgmt))
+ return 0; /* Not in 4-way handshake with PSK */
+
+ /*
+ * It looks like connection was lost while trying to go through PSK
+ * 4-way handshake. Filter out known disconnection cases that are caused
+ * by something else than PSK mismatch to avoid confusing reports.
+ */
+
+ if (locally_generated) {
+ if (reason_code == WLAN_REASON_IE_IN_4WAY_DIFFERS)
+ return 0;
+ }
+
+ return 1;
+}
+
+
+static void wpa_supplicant_event_disassoc_finish(struct wpa_supplicant *wpa_s,
+ u16 reason_code,
+ int locally_generated)
+{
+ const u8 *bssid;
+ int authenticating;
+ u8 prev_pending_bssid[ETH_ALEN];
+ struct wpa_bss *fast_reconnect = NULL;
+ struct wpa_ssid *fast_reconnect_ssid = NULL;
+ struct wpa_ssid *last_ssid;
+
+ authenticating = wpa_s->wpa_state == WPA_AUTHENTICATING;
+ os_memcpy(prev_pending_bssid, wpa_s->pending_bssid, ETH_ALEN);
+
+ if (wpa_s->key_mgmt == WPA_KEY_MGMT_WPA_NONE) {
+ /*
+ * At least Host AP driver and a Prism3 card seemed to be
+ * generating streams of disconnected events when configuring
+ * IBSS for WPA-None. Ignore them for now.
+ */
+ wpa_dbg(wpa_s, MSG_DEBUG, "Disconnect event - ignore in "
+ "IBSS/WPA-None mode");
+ return;
+ }
+
+ if (could_be_psk_mismatch(wpa_s, reason_code, locally_generated)) {
+ wpa_msg(wpa_s, MSG_INFO, "WPA: 4-Way Handshake failed - "
+ "pre-shared key may be incorrect");
+ if (wpas_p2p_4way_hs_failed(wpa_s) > 0)
+ return; /* P2P group removed */
+ wpas_auth_failed(wpa_s, "WRONG_KEY");
+ }
+ if (!wpa_s->disconnected &&
+ (!wpa_s->auto_reconnect_disabled ||
+ wpa_s->key_mgmt == WPA_KEY_MGMT_WPS ||
+ wpas_wps_searching(wpa_s))) {
+ wpa_dbg(wpa_s, MSG_DEBUG, "Auto connect enabled: try to "
+ "reconnect (wps=%d/%d wpa_state=%d)",
+ wpa_s->key_mgmt == WPA_KEY_MGMT_WPS,
+ wpas_wps_searching(wpa_s),
+ wpa_s->wpa_state);
+ if (wpa_s->wpa_state == WPA_COMPLETED &&
+ wpa_s->current_ssid &&
+ wpa_s->current_ssid->mode == WPAS_MODE_INFRA &&
+ !locally_generated &&
+ disconnect_reason_recoverable(reason_code)) {
+ /*
+ * It looks like the AP has dropped association with
+ * us, but could allow us to get back in. Try to
+ * reconnect to the same BSS without full scan to save
+ * time for some common cases.
+ */
+ fast_reconnect = wpa_s->current_bss;
+ fast_reconnect_ssid = wpa_s->current_ssid;
+ } else if (wpa_s->wpa_state >= WPA_ASSOCIATING)
+ wpa_supplicant_req_scan(wpa_s, 0, 100000);
+ else
+ wpa_dbg(wpa_s, MSG_DEBUG, "Do not request new "
+ "immediate scan");
+ } else {
+ wpa_dbg(wpa_s, MSG_DEBUG, "Auto connect disabled: do not "
+ "try to re-connect");
+ wpa_s->reassociate = 0;
+ wpa_s->disconnected = 1;
+ if (!wpa_s->pno)
+ wpa_supplicant_cancel_sched_scan(wpa_s);
+ }
+ bssid = wpa_s->bssid;
+ if (is_zero_ether_addr(bssid))
+ bssid = wpa_s->pending_bssid;
+ if (wpa_s->wpa_state >= WPA_AUTHENTICATING)
+ wpas_connection_failed(wpa_s, bssid);
+ wpa_sm_notify_disassoc(wpa_s->wpa);
+ if (locally_generated)
+ wpa_s->disconnect_reason = -reason_code;
+ else
+ wpa_s->disconnect_reason = reason_code;
+ wpas_notify_disconnect_reason(wpa_s);
+ if (wpa_supplicant_dynamic_keys(wpa_s)) {
+ wpa_dbg(wpa_s, MSG_DEBUG, "Disconnect event - remove keys");
+ wpa_clear_keys(wpa_s, wpa_s->bssid);
+ }
+ last_ssid = wpa_s->current_ssid;
+ wpa_supplicant_mark_disassoc(wpa_s);
+
+ if (authenticating && (wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME)) {
+ sme_disassoc_while_authenticating(wpa_s, prev_pending_bssid);
+ wpa_s->current_ssid = last_ssid;
+ }
+
+ if (fast_reconnect &&
+ !wpas_network_disabled(wpa_s, fast_reconnect_ssid) &&
+ !disallowed_bssid(wpa_s, fast_reconnect->bssid) &&
+ !disallowed_ssid(wpa_s, fast_reconnect->ssid,
+ fast_reconnect->ssid_len) &&
+ !wpas_temp_disabled(wpa_s, fast_reconnect_ssid)) {
+#ifndef CONFIG_NO_SCAN_PROCESSING
+ wpa_dbg(wpa_s, MSG_DEBUG, "Try to reconnect to the same BSS");
+ if (wpa_supplicant_connect(wpa_s, fast_reconnect,
+ fast_reconnect_ssid) < 0) {
+ /* Recover through full scan */
+ wpa_supplicant_req_scan(wpa_s, 0, 100000);
+ }
+#endif /* CONFIG_NO_SCAN_PROCESSING */
+ } else if (fast_reconnect) {
+ /*
+ * Could not reconnect to the same BSS due to network being
+ * disabled. Use a new scan to match the alternative behavior
+ * above, i.e., to continue automatic reconnection attempt in a
+ * way that enforces disabled network rules.
+ */
+ wpa_supplicant_req_scan(wpa_s, 0, 100000);
+ }
+}
+
+
+#ifdef CONFIG_DELAYED_MIC_ERROR_REPORT
+void wpa_supplicant_delayed_mic_error_report(void *eloop_ctx, void *sock_ctx)
+{
+ struct wpa_supplicant *wpa_s = eloop_ctx;
+
+ if (!wpa_s->pending_mic_error_report)
+ return;
+
+ wpa_dbg(wpa_s, MSG_DEBUG, "WPA: Sending pending MIC error report");
+ wpa_sm_key_request(wpa_s->wpa, 1, wpa_s->pending_mic_error_pairwise);
+ wpa_s->pending_mic_error_report = 0;
+}
+#endif /* CONFIG_DELAYED_MIC_ERROR_REPORT */
+
+
+static void
+wpa_supplicant_event_michael_mic_failure(struct wpa_supplicant *wpa_s,
+ union wpa_event_data *data)
+{
+ int pairwise;
+ struct os_reltime t;
+
+ wpa_msg(wpa_s, MSG_WARNING, "Michael MIC failure detected");
+ pairwise = (data && data->michael_mic_failure.unicast);
+ os_get_reltime(&t);
+ if ((wpa_s->last_michael_mic_error.sec &&
+ !os_reltime_expired(&t, &wpa_s->last_michael_mic_error, 60)) ||
+ wpa_s->pending_mic_error_report) {
+ if (wpa_s->pending_mic_error_report) {
+ /*
+ * Send the pending MIC error report immediately since
+ * we are going to start countermeasures and AP better
+ * do the same.
+ */
+ wpa_sm_key_request(wpa_s->wpa, 1,
+ wpa_s->pending_mic_error_pairwise);
+ }
+
+ /* Send the new MIC error report immediately since we are going
+ * to start countermeasures and AP better do the same.
+ */
+ wpa_sm_key_request(wpa_s->wpa, 1, pairwise);
+
+ /* initialize countermeasures */
+ wpa_s->countermeasures = 1;
+
+ wpa_blacklist_add(wpa_s, wpa_s->bssid);
+
+ wpa_msg(wpa_s, MSG_WARNING, "TKIP countermeasures started");
+
+ /*
+ * Need to wait for completion of request frame. We do not get
+ * any callback for the message completion, so just wait a
+ * short while and hope for the best. */
+ os_sleep(0, 10000);
+
+ wpa_drv_set_countermeasures(wpa_s, 1);
+ wpa_supplicant_deauthenticate(wpa_s,
+ WLAN_REASON_MICHAEL_MIC_FAILURE);
+ eloop_cancel_timeout(wpa_supplicant_stop_countermeasures,
+ wpa_s, NULL);
+ eloop_register_timeout(60, 0,
+ wpa_supplicant_stop_countermeasures,
+ wpa_s, NULL);
+ /* TODO: mark the AP rejected for 60 second. STA is
+ * allowed to associate with another AP.. */
+ } else {
+#ifdef CONFIG_DELAYED_MIC_ERROR_REPORT
+ if (wpa_s->mic_errors_seen) {
+ /*
+ * Reduce the effectiveness of Michael MIC error
+ * reports as a means for attacking against TKIP if
+ * more than one MIC failure is noticed with the same
+ * PTK. We delay the transmission of the reports by a
+ * random time between 0 and 60 seconds in order to
+ * force the attacker wait 60 seconds before getting
+ * the information on whether a frame resulted in a MIC
+ * failure.
+ */
+ u8 rval[4];
+ int sec;
+
+ if (os_get_random(rval, sizeof(rval)) < 0)
+ sec = os_random() % 60;
+ else
+ sec = WPA_GET_BE32(rval) % 60;
+ wpa_dbg(wpa_s, MSG_DEBUG, "WPA: Delay MIC error "
+ "report %d seconds", sec);
+ wpa_s->pending_mic_error_report = 1;
+ wpa_s->pending_mic_error_pairwise = pairwise;
+ eloop_cancel_timeout(
+ wpa_supplicant_delayed_mic_error_report,
+ wpa_s, NULL);
+ eloop_register_timeout(
+ sec, os_random() % 1000000,
+ wpa_supplicant_delayed_mic_error_report,
+ wpa_s, NULL);
+ } else {
+ wpa_sm_key_request(wpa_s->wpa, 1, pairwise);
+ }
+#else /* CONFIG_DELAYED_MIC_ERROR_REPORT */
+ wpa_sm_key_request(wpa_s->wpa, 1, pairwise);
+#endif /* CONFIG_DELAYED_MIC_ERROR_REPORT */
+ }
+ wpa_s->last_michael_mic_error = t;
+ wpa_s->mic_errors_seen++;
+}
+
+
+#ifdef CONFIG_TERMINATE_ONLASTIF
+static int any_interfaces(struct wpa_supplicant *head)
+{
+ struct wpa_supplicant *wpa_s;
+
+ for (wpa_s = head; wpa_s != NULL; wpa_s = wpa_s->next)
+ if (!wpa_s->interface_removed)
+ return 1;
+ return 0;
+}
+#endif /* CONFIG_TERMINATE_ONLASTIF */
+
+
+static void
+wpa_supplicant_event_interface_status(struct wpa_supplicant *wpa_s,
+ union wpa_event_data *data)
+{
+ if (os_strcmp(wpa_s->ifname, data->interface_status.ifname) != 0)
+ return;
+
+ switch (data->interface_status.ievent) {
+ case EVENT_INTERFACE_ADDED:
+ if (!wpa_s->interface_removed)
+ break;
+ wpa_s->interface_removed = 0;
+ wpa_dbg(wpa_s, MSG_DEBUG, "Configured interface was added");
+ if (wpa_supplicant_driver_init(wpa_s) < 0) {
+ wpa_msg(wpa_s, MSG_INFO, "Failed to initialize the "
+ "driver after interface was added");
+ }
+
+#ifdef CONFIG_P2P
+ if (!wpa_s->global->p2p &&
+ !wpa_s->global->p2p_disabled &&
+ !wpa_s->conf->p2p_disabled &&
+ (wpa_s->drv_flags &
+ WPA_DRIVER_FLAGS_DEDICATED_P2P_DEVICE) &&
+ wpas_p2p_add_p2pdev_interface(
+ wpa_s, wpa_s->global->params.conf_p2p_dev) < 0) {
+ wpa_printf(MSG_INFO,
+ "P2P: Failed to enable P2P Device interface");
+ /* Try to continue without. P2P will be disabled. */
+ }
+#endif /* CONFIG_P2P */
+
+ break;
+ case EVENT_INTERFACE_REMOVED:
+ wpa_dbg(wpa_s, MSG_DEBUG, "Configured interface was removed");
+ wpa_s->interface_removed = 1;
+ wpa_supplicant_mark_disassoc(wpa_s);
+ wpa_supplicant_set_state(wpa_s, WPA_INTERFACE_DISABLED);
+ l2_packet_deinit(wpa_s->l2);
+ wpa_s->l2 = NULL;
+
+#ifdef CONFIG_P2P
+ if (wpa_s->global->p2p &&
+ wpa_s->global->p2p_init_wpa_s->parent == wpa_s &&
+ (wpa_s->drv_flags &
+ WPA_DRIVER_FLAGS_DEDICATED_P2P_DEVICE)) {
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ "Removing P2P Device interface");
+ wpa_supplicant_remove_iface(
+ wpa_s->global, wpa_s->global->p2p_init_wpa_s,
+ 0);
+ wpa_s->global->p2p_init_wpa_s = NULL;
+ }
+#endif /* CONFIG_P2P */
+
+#ifdef CONFIG_TERMINATE_ONLASTIF
+ /* check if last interface */
+ if (!any_interfaces(wpa_s->global->ifaces))
+ eloop_terminate();
+#endif /* CONFIG_TERMINATE_ONLASTIF */
+ break;
+ }
+}
+
+
+#ifdef CONFIG_PEERKEY
+static void
+wpa_supplicant_event_stkstart(struct wpa_supplicant *wpa_s,
+ union wpa_event_data *data)
+{
+ if (data == NULL)
+ return;
+ wpa_sm_stkstart(wpa_s->wpa, data->stkstart.peer);
+}
+#endif /* CONFIG_PEERKEY */
+
+
+#ifdef CONFIG_TDLS
+static void wpa_supplicant_event_tdls(struct wpa_supplicant *wpa_s,
+ union wpa_event_data *data)
+{
+ if (data == NULL)
+ return;
+ switch (data->tdls.oper) {
+ case TDLS_REQUEST_SETUP:
+ wpa_tdls_remove(wpa_s->wpa, data->tdls.peer);
+ if (wpa_tdls_is_external_setup(wpa_s->wpa))
+ wpa_tdls_start(wpa_s->wpa, data->tdls.peer);
+ else
+ wpa_drv_tdls_oper(wpa_s, TDLS_SETUP, data->tdls.peer);
+ break;
+ case TDLS_REQUEST_TEARDOWN:
+ if (wpa_tdls_is_external_setup(wpa_s->wpa))
+ wpa_tdls_teardown_link(wpa_s->wpa, data->tdls.peer,
+ data->tdls.reason_code);
+ else
+ wpa_drv_tdls_oper(wpa_s, TDLS_TEARDOWN,
+ data->tdls.peer);
+ break;
+ case TDLS_REQUEST_DISCOVER:
+ wpa_tdls_send_discovery_request(wpa_s->wpa,
+ data->tdls.peer);
+ break;
+ }
+}
+#endif /* CONFIG_TDLS */
+
+
+#ifdef CONFIG_WNM
+static void wpa_supplicant_event_wnm(struct wpa_supplicant *wpa_s,
+ union wpa_event_data *data)
+{
+ if (data == NULL)
+ return;
+ switch (data->wnm.oper) {
+ case WNM_OPER_SLEEP:
+ wpa_printf(MSG_DEBUG, "Start sending WNM-Sleep Request "
+ "(action=%d, intval=%d)",
+ data->wnm.sleep_action, data->wnm.sleep_intval);
+ ieee802_11_send_wnmsleep_req(wpa_s, data->wnm.sleep_action,
+ data->wnm.sleep_intval, NULL);
+ break;
+ }
+}
+#endif /* CONFIG_WNM */
+
+
+#ifdef CONFIG_IEEE80211R
+static void
+wpa_supplicant_event_ft_response(struct wpa_supplicant *wpa_s,
+ union wpa_event_data *data)
+{
+ if (data == NULL)
+ return;
+
+ if (wpa_ft_process_response(wpa_s->wpa, data->ft_ies.ies,
+ data->ft_ies.ies_len,
+ data->ft_ies.ft_action,
+ data->ft_ies.target_ap,
+ data->ft_ies.ric_ies,
+ data->ft_ies.ric_ies_len) < 0) {
+ /* TODO: prevent MLME/driver from trying to associate? */
+ }
+}
+#endif /* CONFIG_IEEE80211R */
+
+
+#ifdef CONFIG_IBSS_RSN
+static void wpa_supplicant_event_ibss_rsn_start(struct wpa_supplicant *wpa_s,
+ union wpa_event_data *data)
+{
+ struct wpa_ssid *ssid;
+ if (wpa_s->wpa_state < WPA_ASSOCIATED)
+ return;
+ if (data == NULL)
+ return;
+ ssid = wpa_s->current_ssid;
+ if (ssid == NULL)
+ return;
+ if (ssid->mode != WPAS_MODE_IBSS || !wpa_key_mgmt_wpa(ssid->key_mgmt))
+ return;
+
+ ibss_rsn_start(wpa_s->ibss_rsn, data->ibss_rsn_start.peer);
+}
+
+
+static void wpa_supplicant_event_ibss_auth(struct wpa_supplicant *wpa_s,
+ union wpa_event_data *data)
+{
+ struct wpa_ssid *ssid = wpa_s->current_ssid;
+
+ if (ssid == NULL)
+ return;
+
+ /* check if the ssid is correctly configured as IBSS/RSN */
+ if (ssid->mode != WPAS_MODE_IBSS || !wpa_key_mgmt_wpa(ssid->key_mgmt))
+ return;
+
+ ibss_rsn_handle_auth(wpa_s->ibss_rsn, data->rx_mgmt.frame,
+ data->rx_mgmt.frame_len);
+}
+#endif /* CONFIG_IBSS_RSN */
+
+
+#ifdef CONFIG_IEEE80211R
+static void ft_rx_action(struct wpa_supplicant *wpa_s, const u8 *data,
+ size_t len)
+{
+ const u8 *sta_addr, *target_ap_addr;
+ u16 status;
+
+ wpa_hexdump(MSG_MSGDUMP, "FT: RX Action", data, len);
+ if (!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME))
+ return; /* only SME case supported for now */
+ if (len < 1 + 2 * ETH_ALEN + 2)
+ return;
+ if (data[0] != 2)
+ return; /* Only FT Action Response is supported for now */
+ sta_addr = data + 1;
+ target_ap_addr = data + 1 + ETH_ALEN;
+ status = WPA_GET_LE16(data + 1 + 2 * ETH_ALEN);
+ wpa_dbg(wpa_s, MSG_DEBUG, "FT: Received FT Action Response: STA "
+ MACSTR " TargetAP " MACSTR " status %u",
+ MAC2STR(sta_addr), MAC2STR(target_ap_addr), status);
+
+ if (os_memcmp(sta_addr, wpa_s->own_addr, ETH_ALEN) != 0) {
+ wpa_dbg(wpa_s, MSG_DEBUG, "FT: Foreign STA Address " MACSTR
+ " in FT Action Response", MAC2STR(sta_addr));
+ return;
+ }
+
+ if (status) {
+ wpa_dbg(wpa_s, MSG_DEBUG, "FT: FT Action Response indicates "
+ "failure (status code %d)", status);
+ /* TODO: report error to FT code(?) */
+ return;
+ }
+
+ if (wpa_ft_process_response(wpa_s->wpa, data + 1 + 2 * ETH_ALEN + 2,
+ len - (1 + 2 * ETH_ALEN + 2), 1,
+ target_ap_addr, NULL, 0) < 0)
+ return;
+
+#ifdef CONFIG_SME
+ {
+ struct wpa_bss *bss;
+ bss = wpa_bss_get_bssid(wpa_s, target_ap_addr);
+ if (bss)
+ wpa_s->sme.freq = bss->freq;
+ wpa_s->sme.auth_alg = WPA_AUTH_ALG_FT;
+ sme_associate(wpa_s, WPAS_MODE_INFRA, target_ap_addr,
+ WLAN_AUTH_FT);
+ }
+#endif /* CONFIG_SME */
+}
+#endif /* CONFIG_IEEE80211R */
+
+
+static void wpa_supplicant_event_unprot_deauth(struct wpa_supplicant *wpa_s,
+ struct unprot_deauth *e)
+{
+#ifdef CONFIG_IEEE80211W
+ wpa_printf(MSG_DEBUG, "Unprotected Deauthentication frame "
+ "dropped: " MACSTR " -> " MACSTR
+ " (reason code %u)",
+ MAC2STR(e->sa), MAC2STR(e->da), e->reason_code);
+ sme_event_unprot_disconnect(wpa_s, e->sa, e->da, e->reason_code);
+#endif /* CONFIG_IEEE80211W */
+}
+
+
+static void wpa_supplicant_event_unprot_disassoc(struct wpa_supplicant *wpa_s,
+ struct unprot_disassoc *e)
+{
+#ifdef CONFIG_IEEE80211W
+ wpa_printf(MSG_DEBUG, "Unprotected Disassociation frame "
+ "dropped: " MACSTR " -> " MACSTR
+ " (reason code %u)",
+ MAC2STR(e->sa), MAC2STR(e->da), e->reason_code);
+ sme_event_unprot_disconnect(wpa_s, e->sa, e->da, e->reason_code);
+#endif /* CONFIG_IEEE80211W */
+}
+
+
+static void wpas_event_disconnect(struct wpa_supplicant *wpa_s, const u8 *addr,
+ u16 reason_code, int locally_generated,
+ const u8 *ie, size_t ie_len, int deauth)
+{
+#ifdef CONFIG_AP
+ if (wpa_s->ap_iface && addr) {
+ hostapd_notif_disassoc(wpa_s->ap_iface->bss[0], addr);
+ return;
+ }
+
+ if (wpa_s->ap_iface) {
+ wpa_dbg(wpa_s, MSG_DEBUG, "Ignore deauth event in AP mode");
+ return;
+ }
+#endif /* CONFIG_AP */
+
+ if (!locally_generated)
+ wpa_s->own_disconnect_req = 0;
+
+ wpa_supplicant_event_disassoc(wpa_s, reason_code, locally_generated);
+
+ if (((reason_code == WLAN_REASON_IEEE_802_1X_AUTH_FAILED ||
+ ((wpa_key_mgmt_wpa_ieee8021x(wpa_s->key_mgmt) ||
+ (wpa_s->key_mgmt & WPA_KEY_MGMT_IEEE8021X_NO_WPA)) &&
+ eapol_sm_failed(wpa_s->eapol))) &&
+ !wpa_s->eap_expected_failure))
+ wpas_auth_failed(wpa_s, "AUTH_FAILED");
+
+#ifdef CONFIG_P2P
+ if (deauth && reason_code > 0) {
+ if (wpas_p2p_deauth_notif(wpa_s, addr, reason_code, ie, ie_len,
+ locally_generated) > 0) {
+ /*
+ * The interface was removed, so cannot continue
+ * processing any additional operations after this.
+ */
+ return;
+ }
+ }
+#endif /* CONFIG_P2P */
+
+ wpa_supplicant_event_disassoc_finish(wpa_s, reason_code,
+ locally_generated);
+}
+
+
+static void wpas_event_disassoc(struct wpa_supplicant *wpa_s,
+ struct disassoc_info *info)
+{
+ u16 reason_code = 0;
+ int locally_generated = 0;
+ const u8 *addr = NULL;
+ const u8 *ie = NULL;
+ size_t ie_len = 0;
+
+ wpa_dbg(wpa_s, MSG_DEBUG, "Disassociation notification");
+
+ if (info) {
+ addr = info->addr;
+ ie = info->ie;
+ ie_len = info->ie_len;
+ reason_code = info->reason_code;
+ locally_generated = info->locally_generated;
+ wpa_dbg(wpa_s, MSG_DEBUG, " * reason %u%s", reason_code,
+ locally_generated ? " (locally generated)" : "");
+ if (addr)
+ wpa_dbg(wpa_s, MSG_DEBUG, " * address " MACSTR,
+ MAC2STR(addr));
+ wpa_hexdump(MSG_DEBUG, "Disassociation frame IE(s)",
+ ie, ie_len);
+ }
+
+#ifdef CONFIG_AP
+ if (wpa_s->ap_iface && info && info->addr) {
+ hostapd_notif_disassoc(wpa_s->ap_iface->bss[0], info->addr);
+ return;
+ }
+
+ if (wpa_s->ap_iface) {
+ wpa_dbg(wpa_s, MSG_DEBUG, "Ignore disassoc event in AP mode");
+ return;
+ }
+#endif /* CONFIG_AP */
+
+#ifdef CONFIG_P2P
+ if (info) {
+ wpas_p2p_disassoc_notif(
+ wpa_s, info->addr, reason_code, info->ie, info->ie_len,
+ locally_generated);
+ }
+#endif /* CONFIG_P2P */
+
+ if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME)
+ sme_event_disassoc(wpa_s, info);
+
+ wpas_event_disconnect(wpa_s, addr, reason_code, locally_generated,
+ ie, ie_len, 0);
+}
+
+
+static void wpas_event_deauth(struct wpa_supplicant *wpa_s,
+ struct deauth_info *info)
+{
+ u16 reason_code = 0;
+ int locally_generated = 0;
+ const u8 *addr = NULL;
+ const u8 *ie = NULL;
+ size_t ie_len = 0;
+
+ wpa_dbg(wpa_s, MSG_DEBUG, "Deauthentication notification");
+
+ if (info) {
+ addr = info->addr;
+ ie = info->ie;
+ ie_len = info->ie_len;
+ reason_code = info->reason_code;
+ locally_generated = info->locally_generated;
+ wpa_dbg(wpa_s, MSG_DEBUG, " * reason %u%s",
+ reason_code,
+ locally_generated ? " (locally generated)" : "");
+ if (addr) {
+ wpa_dbg(wpa_s, MSG_DEBUG, " * address " MACSTR,
+ MAC2STR(addr));
+ }
+ wpa_hexdump(MSG_DEBUG, "Deauthentication frame IE(s)",
+ ie, ie_len);
+ }
+
+ wpa_reset_ft_completed(wpa_s->wpa);
+
+ wpas_event_disconnect(wpa_s, addr, reason_code,
+ locally_generated, ie, ie_len, 1);
+}
+
+
+static const char * reg_init_str(enum reg_change_initiator init)
+{
+ switch (init) {
+ case REGDOM_SET_BY_CORE:
+ return "CORE";
+ case REGDOM_SET_BY_USER:
+ return "USER";
+ case REGDOM_SET_BY_DRIVER:
+ return "DRIVER";
+ case REGDOM_SET_BY_COUNTRY_IE:
+ return "COUNTRY_IE";
+ case REGDOM_BEACON_HINT:
+ return "BEACON_HINT";
+ }
+ return "?";
+}
+
+
+static const char * reg_type_str(enum reg_type type)
+{
+ switch (type) {
+ case REGDOM_TYPE_UNKNOWN:
+ return "UNKNOWN";
+ case REGDOM_TYPE_COUNTRY:
+ return "COUNTRY";
+ case REGDOM_TYPE_WORLD:
+ return "WORLD";
+ case REGDOM_TYPE_CUSTOM_WORLD:
+ return "CUSTOM_WORLD";
+ case REGDOM_TYPE_INTERSECTION:
+ return "INTERSECTION";
+ }
+ return "?";
+}
+
+
+static void wpa_supplicant_update_channel_list(
+ struct wpa_supplicant *wpa_s, struct channel_list_changed *info)
+{
+ struct wpa_supplicant *ifs;
+
+ wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_REGDOM_CHANGE "init=%s type=%s%s%s",
+ reg_init_str(info->initiator), reg_type_str(info->type),
+ info->alpha2[0] ? " alpha2=" : "",
+ info->alpha2[0] ? info->alpha2 : "");
+
+ if (wpa_s->drv_priv == NULL)
+ return; /* Ignore event during drv initialization */
+
+ dl_list_for_each(ifs, &wpa_s->radio->ifaces, struct wpa_supplicant,
+ radio_list) {
+ wpa_printf(MSG_DEBUG, "%s: Updating hw mode",
+ ifs->ifname);
+ free_hw_features(ifs);
+ ifs->hw.modes = wpa_drv_get_hw_feature_data(
+ ifs, &ifs->hw.num_modes, &ifs->hw.flags);
+ }
+
+ /* Restart sched_scan with updated channel list */
+ if (wpa_s->sched_scanning) {
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ "Channel list changed restart sched scan.");
+ wpa_supplicant_cancel_sched_scan(wpa_s);
+ wpa_supplicant_req_scan(wpa_s, 0, 0);
+ }
+
+ wpas_p2p_update_channel_list(wpa_s, WPAS_P2P_CHANNEL_UPDATE_DRIVER);
+}
+
+
+static void wpas_event_rx_mgmt_action(struct wpa_supplicant *wpa_s,
+ const u8 *frame, size_t len, int freq,
+ int rssi)
+{
+ const struct ieee80211_mgmt *mgmt;
+ const u8 *payload;
+ size_t plen;
+ u8 category;
+
+ if (len < IEEE80211_HDRLEN + 2)
+ return;
+
+ mgmt = (const struct ieee80211_mgmt *) frame;
+ payload = frame + IEEE80211_HDRLEN;
+ category = *payload++;
+ plen = len - IEEE80211_HDRLEN - 1;
+
+ wpa_dbg(wpa_s, MSG_DEBUG, "Received Action frame: SA=" MACSTR
+ " Category=%u DataLen=%d freq=%d MHz",
+ MAC2STR(mgmt->sa), category, (int) plen, freq);
+
+ if (category == WLAN_ACTION_WMM) {
+ wmm_ac_rx_action(wpa_s, mgmt->da, mgmt->sa, payload, plen);
+ return;
+ }
+
+#ifdef CONFIG_IEEE80211R
+ if (category == WLAN_ACTION_FT) {
+ ft_rx_action(wpa_s, payload, plen);
+ return;
+ }
+#endif /* CONFIG_IEEE80211R */
+
+#ifdef CONFIG_IEEE80211W
+#ifdef CONFIG_SME
+ if (category == WLAN_ACTION_SA_QUERY) {
+ sme_sa_query_rx(wpa_s, mgmt->sa, payload, plen);
+ return;
+ }
+#endif /* CONFIG_SME */
+#endif /* CONFIG_IEEE80211W */
+
+#ifdef CONFIG_WNM
+ if (mgmt->u.action.category == WLAN_ACTION_WNM) {
+ ieee802_11_rx_wnm_action(wpa_s, mgmt, len);
+ return;
+ }
+#endif /* CONFIG_WNM */
+
+#ifdef CONFIG_GAS
+ if ((mgmt->u.action.category == WLAN_ACTION_PUBLIC ||
+ mgmt->u.action.category == WLAN_ACTION_PROTECTED_DUAL) &&
+ gas_query_rx(wpa_s->gas, mgmt->da, mgmt->sa, mgmt->bssid,
+ mgmt->u.action.category,
+ payload, plen, freq) == 0)
+ return;
+#endif /* CONFIG_GAS */
+
+#ifdef CONFIG_TDLS
+ if (category == WLAN_ACTION_PUBLIC && plen >= 4 &&
+ payload[0] == WLAN_TDLS_DISCOVERY_RESPONSE) {
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ "TDLS: Received Discovery Response from " MACSTR,
+ MAC2STR(mgmt->sa));
+ return;
+ }
+#endif /* CONFIG_TDLS */
+
+#ifdef CONFIG_INTERWORKING
+ if (category == WLAN_ACTION_QOS && plen >= 1 &&
+ payload[0] == QOS_QOS_MAP_CONFIG) {
+ const u8 *pos = payload + 1;
+ size_t qlen = plen - 1;
+ wpa_dbg(wpa_s, MSG_DEBUG, "Interworking: Received QoS Map Configure frame from "
+ MACSTR, MAC2STR(mgmt->sa));
+ if (os_memcmp(mgmt->sa, wpa_s->bssid, ETH_ALEN) == 0 &&
+ qlen > 2 && pos[0] == WLAN_EID_QOS_MAP_SET &&
+ pos[1] <= qlen - 2 && pos[1] >= 16)
+ wpas_qos_map_set(wpa_s, pos + 2, pos[1]);
+ return;
+ }
+#endif /* CONFIG_INTERWORKING */
+
+ if (category == WLAN_ACTION_RADIO_MEASUREMENT &&
+ payload[0] == WLAN_RRM_NEIGHBOR_REPORT_RESPONSE) {
+ wpas_rrm_process_neighbor_rep(wpa_s, payload + 1, plen - 1);
+ return;
+ }
+
+ if (category == WLAN_ACTION_RADIO_MEASUREMENT &&
+ payload[0] == WLAN_RRM_LINK_MEASUREMENT_REQUEST) {
+ wpas_rrm_handle_link_measurement_request(wpa_s, mgmt->sa,
+ payload + 1, plen - 1,
+ rssi);
+ return;
+ }
+
+#ifdef CONFIG_FST
+ if (mgmt->u.action.category == WLAN_ACTION_FST && wpa_s->fst) {
+ fst_rx_action(wpa_s->fst, mgmt, len);
+ return;
+ }
+#endif /* CONFIG_FST */
+
+ wpas_p2p_rx_action(wpa_s, mgmt->da, mgmt->sa, mgmt->bssid,
+ category, payload, plen, freq);
+ if (wpa_s->ifmsh)
+ mesh_mpm_action_rx(wpa_s, mgmt, len);
+}
+
+
+static void wpa_supplicant_notify_avoid_freq(struct wpa_supplicant *wpa_s,
+ union wpa_event_data *event)
+{
+ struct wpa_freq_range_list *list;
+ char *str = NULL;
+
+ list = &event->freq_range;
+
+ if (list->num)
+ str = freq_range_list_str(list);
+ wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_AVOID_FREQ "ranges=%s",
+ str ? str : "");
+
+#ifdef CONFIG_P2P
+ if (freq_range_list_parse(&wpa_s->global->p2p_go_avoid_freq, str)) {
+ wpa_dbg(wpa_s, MSG_ERROR, "%s: Failed to parse freq range",
+ __func__);
+ } else {
+ wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Update channel list based on frequency avoid event");
+
+ /*
+ * The update channel flow will also take care of moving a GO
+ * from the unsafe frequency if needed.
+ */
+ wpas_p2p_update_channel_list(wpa_s,
+ WPAS_P2P_CHANNEL_UPDATE_AVOID);
+ }
+#endif /* CONFIG_P2P */
+
+ os_free(str);
+}
+
+
+static void wpa_supplicant_event_assoc_auth(struct wpa_supplicant *wpa_s,
+ union wpa_event_data *data)
+{
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ "Connection authorized by device, previous state %d",
+ wpa_s->wpa_state);
+ if (wpa_s->wpa_state == WPA_ASSOCIATED) {
+ wpa_supplicant_cancel_auth_timeout(wpa_s);
+ wpa_supplicant_set_state(wpa_s, WPA_COMPLETED);
+ eapol_sm_notify_portValid(wpa_s->eapol, TRUE);
+ eapol_sm_notify_eap_success(wpa_s->eapol, TRUE);
+ }
+ wpa_sm_set_rx_replay_ctr(wpa_s->wpa, data->assoc_info.key_replay_ctr);
+ wpa_sm_set_ptk_kck_kek(wpa_s->wpa, data->assoc_info.ptk_kck,
+ data->assoc_info.ptk_kck_len,
+ data->assoc_info.ptk_kek,
+ data->assoc_info.ptk_kek_len);
+}
+
+
+void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
+ union wpa_event_data *data)
+{
+ struct wpa_supplicant *wpa_s = ctx;
+ int resched;
+
+ if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED &&
+ event != EVENT_INTERFACE_ENABLED &&
+ event != EVENT_INTERFACE_STATUS &&
+ event != EVENT_SCHED_SCAN_STOPPED) {
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ "Ignore event %s (%d) while interface is disabled",
+ event_to_string(event), event);
+ return;
+ }
+
+#ifndef CONFIG_NO_STDOUT_DEBUG
+{
+ int level = MSG_DEBUG;
+
+ if (event == EVENT_RX_MGMT && data->rx_mgmt.frame_len >= 24) {
+ const struct ieee80211_hdr *hdr;
+ u16 fc;
+ hdr = (const struct ieee80211_hdr *) data->rx_mgmt.frame;
+ fc = le_to_host16(hdr->frame_control);
+ if (WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_MGMT &&
+ WLAN_FC_GET_STYPE(fc) == WLAN_FC_STYPE_BEACON)
+ level = MSG_EXCESSIVE;
+ }
+
+ wpa_dbg(wpa_s, level, "Event %s (%d) received",
+ event_to_string(event), event);
+}
+#endif /* CONFIG_NO_STDOUT_DEBUG */
+
+ switch (event) {
+ case EVENT_AUTH:
+ sme_event_auth(wpa_s, data);
+ break;
+ case EVENT_ASSOC:
+ wpa_supplicant_event_assoc(wpa_s, data);
+ if (data && data->assoc_info.authorized)
+ wpa_supplicant_event_assoc_auth(wpa_s, data);
+ break;
+ case EVENT_DISASSOC:
+ wpas_event_disassoc(wpa_s,
+ data ? &data->disassoc_info : NULL);
+ break;
+ case EVENT_DEAUTH:
+ wpas_event_deauth(wpa_s,
+ data ? &data->deauth_info : NULL);
+ break;
+ case EVENT_MICHAEL_MIC_FAILURE:
+ wpa_supplicant_event_michael_mic_failure(wpa_s, data);
+ break;
+#ifndef CONFIG_NO_SCAN_PROCESSING
+ case EVENT_SCAN_STARTED:
+ os_get_reltime(&wpa_s->scan_start_time);
+ if (wpa_s->own_scan_requested) {
+ struct os_reltime diff;
+
+ os_reltime_sub(&wpa_s->scan_start_time,
+ &wpa_s->scan_trigger_time, &diff);
+ wpa_dbg(wpa_s, MSG_DEBUG, "Own scan request started a scan in %ld.%06ld seconds",
+ diff.sec, diff.usec);
+ wpa_s->own_scan_requested = 0;
+ wpa_s->own_scan_running = 1;
+ if (wpa_s->last_scan_req == MANUAL_SCAN_REQ &&
+ wpa_s->manual_scan_use_id) {
+ wpa_msg_ctrl(wpa_s, MSG_INFO,
+ WPA_EVENT_SCAN_STARTED "id=%u",
+ wpa_s->manual_scan_id);
+ } else {
+ wpa_msg_ctrl(wpa_s, MSG_INFO,
+ WPA_EVENT_SCAN_STARTED);
+ }
+ } else {
+ wpa_dbg(wpa_s, MSG_DEBUG, "External program started a scan");
+ wpa_s->radio->external_scan_running = 1;
+ wpa_msg_ctrl(wpa_s, MSG_INFO, WPA_EVENT_SCAN_STARTED);
+ }
+ break;
+ case EVENT_SCAN_RESULTS:
+ if (os_reltime_initialized(&wpa_s->scan_start_time)) {
+ struct os_reltime now, diff;
+ os_get_reltime(&now);
+ os_reltime_sub(&now, &wpa_s->scan_start_time, &diff);
+ wpa_s->scan_start_time.sec = 0;
+ wpa_s->scan_start_time.usec = 0;
+ wpa_dbg(wpa_s, MSG_DEBUG, "Scan completed in %ld.%06ld seconds",
+ diff.sec, diff.usec);
+ }
+ if (wpa_supplicant_event_scan_results(wpa_s, data))
+ break; /* interface may have been removed */
+ wpa_s->own_scan_running = 0;
+ wpa_s->radio->external_scan_running = 0;
+ radio_work_check_next(wpa_s);
+ break;
+#endif /* CONFIG_NO_SCAN_PROCESSING */
+ case EVENT_ASSOCINFO:
+ wpa_supplicant_event_associnfo(wpa_s, data);
+ break;
+ case EVENT_INTERFACE_STATUS:
+ wpa_supplicant_event_interface_status(wpa_s, data);
+ break;
+ case EVENT_PMKID_CANDIDATE:
+ wpa_supplicant_event_pmkid_candidate(wpa_s, data);
+ break;
+#ifdef CONFIG_PEERKEY
+ case EVENT_STKSTART:
+ wpa_supplicant_event_stkstart(wpa_s, data);
+ break;
+#endif /* CONFIG_PEERKEY */
+#ifdef CONFIG_TDLS
+ case EVENT_TDLS:
+ wpa_supplicant_event_tdls(wpa_s, data);
+ break;
+#endif /* CONFIG_TDLS */
+#ifdef CONFIG_WNM
+ case EVENT_WNM:
+ wpa_supplicant_event_wnm(wpa_s, data);
+ break;
+#endif /* CONFIG_WNM */
+#ifdef CONFIG_IEEE80211R
+ case EVENT_FT_RESPONSE:
+ wpa_supplicant_event_ft_response(wpa_s, data);
+ break;
+#endif /* CONFIG_IEEE80211R */
+#ifdef CONFIG_IBSS_RSN
+ case EVENT_IBSS_RSN_START:
+ wpa_supplicant_event_ibss_rsn_start(wpa_s, data);
+ break;
+#endif /* CONFIG_IBSS_RSN */
+ case EVENT_ASSOC_REJECT:
+ if (data->assoc_reject.bssid)
+ wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_ASSOC_REJECT
+ "bssid=" MACSTR " status_code=%u",
+ MAC2STR(data->assoc_reject.bssid),
+ data->assoc_reject.status_code);
+ else
+ wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_ASSOC_REJECT
+ "status_code=%u",
+ data->assoc_reject.status_code);
+ if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME)
+ sme_event_assoc_reject(wpa_s, data);
+ else {
+ const u8 *bssid = data->assoc_reject.bssid;
+ if (bssid == NULL || is_zero_ether_addr(bssid))
+ bssid = wpa_s->pending_bssid;
+ wpas_connection_failed(wpa_s, bssid);
+ wpa_supplicant_mark_disassoc(wpa_s);
+ }
+ break;
+ case EVENT_AUTH_TIMED_OUT:
+ /* It is possible to get this event from earlier connection */
+ if (wpa_s->current_ssid &&
+ wpa_s->current_ssid->mode == WPAS_MODE_MESH) {
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ "Ignore AUTH_TIMED_OUT in mesh configuration");
+ break;
+ }
+ if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME)
+ sme_event_auth_timed_out(wpa_s, data);
+ break;
+ case EVENT_ASSOC_TIMED_OUT:
+ /* It is possible to get this event from earlier connection */
+ if (wpa_s->current_ssid &&
+ wpa_s->current_ssid->mode == WPAS_MODE_MESH) {
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ "Ignore ASSOC_TIMED_OUT in mesh configuration");
+ break;
+ }
+ if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME)
+ sme_event_assoc_timed_out(wpa_s, data);
+ break;
+ case EVENT_TX_STATUS:
+ wpa_dbg(wpa_s, MSG_DEBUG, "EVENT_TX_STATUS dst=" MACSTR
+ " type=%d stype=%d",
+ MAC2STR(data->tx_status.dst),
+ data->tx_status.type, data->tx_status.stype);
+#ifdef CONFIG_AP
+ if (wpa_s->ap_iface == NULL) {
+#ifdef CONFIG_OFFCHANNEL
+ if (data->tx_status.type == WLAN_FC_TYPE_MGMT &&
+ data->tx_status.stype == WLAN_FC_STYPE_ACTION)
+ offchannel_send_action_tx_status(
+ wpa_s, data->tx_status.dst,
+ data->tx_status.data,
+ data->tx_status.data_len,
+ data->tx_status.ack ?
+ OFFCHANNEL_SEND_ACTION_SUCCESS :
+ OFFCHANNEL_SEND_ACTION_NO_ACK);
+#endif /* CONFIG_OFFCHANNEL */
+ break;
+ }
+#endif /* CONFIG_AP */
+#ifdef CONFIG_OFFCHANNEL
+ wpa_dbg(wpa_s, MSG_DEBUG, "EVENT_TX_STATUS pending_dst="
+ MACSTR, MAC2STR(wpa_s->parent->pending_action_dst));
+ /*
+ * Catch TX status events for Action frames we sent via group
+ * interface in GO mode.
+ */
+ if (data->tx_status.type == WLAN_FC_TYPE_MGMT &&
+ data->tx_status.stype == WLAN_FC_STYPE_ACTION &&
+ os_memcmp(wpa_s->parent->pending_action_dst,
+ data->tx_status.dst, ETH_ALEN) == 0) {
+ offchannel_send_action_tx_status(
+ wpa_s->parent, data->tx_status.dst,
+ data->tx_status.data,
+ data->tx_status.data_len,
+ data->tx_status.ack ?
+ OFFCHANNEL_SEND_ACTION_SUCCESS :
+ OFFCHANNEL_SEND_ACTION_NO_ACK);
+ break;
+ }
+#endif /* CONFIG_OFFCHANNEL */
+#ifdef CONFIG_AP
+ switch (data->tx_status.type) {
+ case WLAN_FC_TYPE_MGMT:
+ ap_mgmt_tx_cb(wpa_s, data->tx_status.data,
+ data->tx_status.data_len,
+ data->tx_status.stype,
+ data->tx_status.ack);
+ break;
+ case WLAN_FC_TYPE_DATA:
+ ap_tx_status(wpa_s, data->tx_status.dst,
+ data->tx_status.data,
+ data->tx_status.data_len,
+ data->tx_status.ack);
+ break;
+ }
+#endif /* CONFIG_AP */
+ break;
+#ifdef CONFIG_AP
+ case EVENT_EAPOL_TX_STATUS:
+ ap_eapol_tx_status(wpa_s, data->eapol_tx_status.dst,
+ data->eapol_tx_status.data,
+ data->eapol_tx_status.data_len,
+ data->eapol_tx_status.ack);
+ break;
+ case EVENT_DRIVER_CLIENT_POLL_OK:
+ ap_client_poll_ok(wpa_s, data->client_poll.addr);
+ break;
+ case EVENT_RX_FROM_UNKNOWN:
+ if (wpa_s->ap_iface == NULL)
+ break;
+ ap_rx_from_unknown_sta(wpa_s, data->rx_from_unknown.addr,
+ data->rx_from_unknown.wds);
+ break;
+ case EVENT_CH_SWITCH:
+ if (!data)
+ break;
+ if (!wpa_s->ap_iface) {
+ wpa_dbg(wpa_s, MSG_DEBUG, "AP: Ignore channel switch "
+ "event in non-AP mode");
+ break;
+ }
+
+ wpas_ap_ch_switch(wpa_s, data->ch_switch.freq,
+ data->ch_switch.ht_enabled,
+ data->ch_switch.ch_offset,
+ data->ch_switch.ch_width,
+ data->ch_switch.cf1,
+ data->ch_switch.cf2);
+ break;
+#ifdef NEED_AP_MLME
+ case EVENT_DFS_RADAR_DETECTED:
+ if (data)
+ wpas_event_dfs_radar_detected(wpa_s, &data->dfs_event);
+ break;
+ case EVENT_DFS_CAC_STARTED:
+ if (data)
+ wpas_event_dfs_cac_started(wpa_s, &data->dfs_event);
+ break;
+ case EVENT_DFS_CAC_FINISHED:
+ if (data)
+ wpas_event_dfs_cac_finished(wpa_s, &data->dfs_event);
+ break;
+ case EVENT_DFS_CAC_ABORTED:
+ if (data)
+ wpas_event_dfs_cac_aborted(wpa_s, &data->dfs_event);
+ break;
+ case EVENT_DFS_NOP_FINISHED:
+ if (data)
+ wpas_event_dfs_cac_nop_finished(wpa_s,
+ &data->dfs_event);
+ break;
+#endif /* NEED_AP_MLME */
+#endif /* CONFIG_AP */
+ case EVENT_RX_MGMT: {
+ u16 fc, stype;
+ const struct ieee80211_mgmt *mgmt;
+
+#ifdef CONFIG_TESTING_OPTIONS
+ if (wpa_s->ext_mgmt_frame_handling) {
+ struct rx_mgmt *rx = &data->rx_mgmt;
+ size_t hex_len = 2 * rx->frame_len + 1;
+ char *hex = os_malloc(hex_len);
+ if (hex) {
+ wpa_snprintf_hex(hex, hex_len,
+ rx->frame, rx->frame_len);
+ wpa_msg(wpa_s, MSG_INFO, "MGMT-RX freq=%d datarate=%u ssi_signal=%d %s",
+ rx->freq, rx->datarate, rx->ssi_signal,
+ hex);
+ os_free(hex);
+ }
+ break;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ mgmt = (const struct ieee80211_mgmt *)
+ data->rx_mgmt.frame;
+ fc = le_to_host16(mgmt->frame_control);
+ stype = WLAN_FC_GET_STYPE(fc);
+
+#ifdef CONFIG_AP
+ if (wpa_s->ap_iface == NULL) {
+#endif /* CONFIG_AP */
+#ifdef CONFIG_P2P
+ if (stype == WLAN_FC_STYPE_PROBE_REQ &&
+ data->rx_mgmt.frame_len > 24) {
+ const u8 *src = mgmt->sa;
+ const u8 *ie = mgmt->u.probe_req.variable;
+ size_t ie_len = data->rx_mgmt.frame_len -
+ (mgmt->u.probe_req.variable -
+ data->rx_mgmt.frame);
+ wpas_p2p_probe_req_rx(
+ wpa_s, src, mgmt->da,
+ mgmt->bssid, ie, ie_len,
+ data->rx_mgmt.freq,
+ data->rx_mgmt.ssi_signal);
+ break;
+ }
+#endif /* CONFIG_P2P */
+#ifdef CONFIG_IBSS_RSN
+ if (wpa_s->current_ssid &&
+ wpa_s->current_ssid->mode == WPAS_MODE_IBSS &&
+ stype == WLAN_FC_STYPE_AUTH &&
+ data->rx_mgmt.frame_len >= 30) {
+ wpa_supplicant_event_ibss_auth(wpa_s, data);
+ break;
+ }
+#endif /* CONFIG_IBSS_RSN */
+
+ if (stype == WLAN_FC_STYPE_ACTION) {
+ wpas_event_rx_mgmt_action(
+ wpa_s, data->rx_mgmt.frame,
+ data->rx_mgmt.frame_len,
+ data->rx_mgmt.freq,
+ data->rx_mgmt.ssi_signal);
+ break;
+ }
+
+ if (wpa_s->ifmsh) {
+ mesh_mpm_mgmt_rx(wpa_s, &data->rx_mgmt);
+ break;
+ }
+
+ wpa_dbg(wpa_s, MSG_DEBUG, "AP: ignore received "
+ "management frame in non-AP mode");
+ break;
+#ifdef CONFIG_AP
+ }
+
+ if (stype == WLAN_FC_STYPE_PROBE_REQ &&
+ data->rx_mgmt.frame_len > 24) {
+ const u8 *ie = mgmt->u.probe_req.variable;
+ size_t ie_len = data->rx_mgmt.frame_len -
+ (mgmt->u.probe_req.variable -
+ data->rx_mgmt.frame);
+
+ wpas_notify_preq(wpa_s, mgmt->sa, mgmt->da,
+ mgmt->bssid, ie, ie_len,
+ data->rx_mgmt.ssi_signal);
+ }
+
+ ap_mgmt_rx(wpa_s, &data->rx_mgmt);
+#endif /* CONFIG_AP */
+ break;
+ }
+ case EVENT_RX_PROBE_REQ:
+ if (data->rx_probe_req.sa == NULL ||
+ data->rx_probe_req.ie == NULL)
+ break;
+#ifdef CONFIG_AP
+ if (wpa_s->ap_iface) {
+ hostapd_probe_req_rx(wpa_s->ap_iface->bss[0],
+ data->rx_probe_req.sa,
+ data->rx_probe_req.da,
+ data->rx_probe_req.bssid,
+ data->rx_probe_req.ie,
+ data->rx_probe_req.ie_len,
+ data->rx_probe_req.ssi_signal);
+ break;
+ }
+#endif /* CONFIG_AP */
+ wpas_p2p_probe_req_rx(wpa_s, data->rx_probe_req.sa,
+ data->rx_probe_req.da,
+ data->rx_probe_req.bssid,
+ data->rx_probe_req.ie,
+ data->rx_probe_req.ie_len,
+ 0,
+ data->rx_probe_req.ssi_signal);
+ break;
+ case EVENT_REMAIN_ON_CHANNEL:
+#ifdef CONFIG_OFFCHANNEL
+ offchannel_remain_on_channel_cb(
+ wpa_s, data->remain_on_channel.freq,
+ data->remain_on_channel.duration);
+#endif /* CONFIG_OFFCHANNEL */
+ wpas_p2p_remain_on_channel_cb(
+ wpa_s, data->remain_on_channel.freq,
+ data->remain_on_channel.duration);
+ break;
+ case EVENT_CANCEL_REMAIN_ON_CHANNEL:
+#ifdef CONFIG_OFFCHANNEL
+ offchannel_cancel_remain_on_channel_cb(
+ wpa_s, data->remain_on_channel.freq);
+#endif /* CONFIG_OFFCHANNEL */
+ wpas_p2p_cancel_remain_on_channel_cb(
+ wpa_s, data->remain_on_channel.freq);
+ break;
+ case EVENT_EAPOL_RX:
+ wpa_supplicant_rx_eapol(wpa_s, data->eapol_rx.src,
+ data->eapol_rx.data,
+ data->eapol_rx.data_len);
+ break;
+ case EVENT_SIGNAL_CHANGE:
+ wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_SIGNAL_CHANGE
+ "above=%d signal=%d noise=%d txrate=%d",
+ data->signal_change.above_threshold,
+ data->signal_change.current_signal,
+ data->signal_change.current_noise,
+ data->signal_change.current_txrate);
+ wpa_bss_update_level(wpa_s->current_bss,
+ data->signal_change.current_signal);
+ bgscan_notify_signal_change(
+ wpa_s, data->signal_change.above_threshold,
+ data->signal_change.current_signal,
+ data->signal_change.current_noise,
+ data->signal_change.current_txrate);
+ break;
+ case EVENT_INTERFACE_ENABLED:
+ wpa_dbg(wpa_s, MSG_DEBUG, "Interface was enabled");
+ if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED) {
+ wpa_supplicant_update_mac_addr(wpa_s);
+ if (wpa_s->p2p_mgmt) {
+ wpa_supplicant_set_state(wpa_s,
+ WPA_DISCONNECTED);
+ break;
+ }
+
+#ifdef CONFIG_AP
+ if (!wpa_s->ap_iface) {
+ wpa_supplicant_set_state(wpa_s,
+ WPA_DISCONNECTED);
+ wpa_s->scan_req = NORMAL_SCAN_REQ;
+ wpa_supplicant_req_scan(wpa_s, 0, 0);
+ } else
+ wpa_supplicant_set_state(wpa_s,
+ WPA_COMPLETED);
+#else /* CONFIG_AP */
+ wpa_supplicant_set_state(wpa_s, WPA_DISCONNECTED);
+ wpa_supplicant_req_scan(wpa_s, 0, 0);
+#endif /* CONFIG_AP */
+ }
+ break;
+ case EVENT_INTERFACE_DISABLED:
+ wpa_dbg(wpa_s, MSG_DEBUG, "Interface was disabled");
+#ifdef CONFIG_P2P
+ if (wpa_s->p2p_group_interface == P2P_GROUP_INTERFACE_GO ||
+ (wpa_s->current_ssid && wpa_s->current_ssid->p2p_group &&
+ wpa_s->current_ssid->mode == WPAS_MODE_P2P_GO)) {
+ /*
+ * Mark interface disabled if this happens to end up not
+ * being removed as a separate P2P group interface.
+ */
+ wpa_supplicant_set_state(wpa_s, WPA_INTERFACE_DISABLED);
+ /*
+ * The interface was externally disabled. Remove
+ * it assuming an external entity will start a
+ * new session if needed.
+ */
+ if (wpa_s->current_ssid &&
+ wpa_s->current_ssid->p2p_group)
+ wpas_p2p_interface_unavailable(wpa_s);
+ else
+ wpas_p2p_disconnect(wpa_s);
+ /*
+ * wpa_s instance may have been freed, so must not use
+ * it here anymore.
+ */
+ break;
+ }
+ if (wpa_s->p2p_scan_work && wpa_s->global->p2p &&
+ p2p_in_progress(wpa_s->global->p2p) > 1) {
+ /* This radio work will be cancelled, so clear P2P
+ * state as well.
+ */
+ p2p_stop_find(wpa_s->global->p2p);
+ }
+#endif /* CONFIG_P2P */
+
+ if (wpa_s->wpa_state >= WPA_AUTHENTICATING) {
+ /*
+ * Indicate disconnection to keep ctrl_iface events
+ * consistent.
+ */
+ wpa_supplicant_event_disassoc(
+ wpa_s, WLAN_REASON_DEAUTH_LEAVING, 1);
+ }
+ wpa_supplicant_mark_disassoc(wpa_s);
+ radio_remove_works(wpa_s, NULL, 0);
+
+ wpa_supplicant_set_state(wpa_s, WPA_INTERFACE_DISABLED);
+ break;
+ case EVENT_CHANNEL_LIST_CHANGED:
+ wpa_supplicant_update_channel_list(
+ wpa_s, &data->channel_list_changed);
+ break;
+ case EVENT_INTERFACE_UNAVAILABLE:
+ wpas_p2p_interface_unavailable(wpa_s);
+ break;
+ case EVENT_BEST_CHANNEL:
+ wpa_dbg(wpa_s, MSG_DEBUG, "Best channel event received "
+ "(%d %d %d)",
+ data->best_chan.freq_24, data->best_chan.freq_5,
+ data->best_chan.freq_overall);
+ wpa_s->best_24_freq = data->best_chan.freq_24;
+ wpa_s->best_5_freq = data->best_chan.freq_5;
+ wpa_s->best_overall_freq = data->best_chan.freq_overall;
+ wpas_p2p_update_best_channels(wpa_s, data->best_chan.freq_24,
+ data->best_chan.freq_5,
+ data->best_chan.freq_overall);
+ break;
+ case EVENT_UNPROT_DEAUTH:
+ wpa_supplicant_event_unprot_deauth(wpa_s,
+ &data->unprot_deauth);
+ break;
+ case EVENT_UNPROT_DISASSOC:
+ wpa_supplicant_event_unprot_disassoc(wpa_s,
+ &data->unprot_disassoc);
+ break;
+ case EVENT_STATION_LOW_ACK:
+#ifdef CONFIG_AP
+ if (wpa_s->ap_iface && data)
+ hostapd_event_sta_low_ack(wpa_s->ap_iface->bss[0],
+ data->low_ack.addr);
+#endif /* CONFIG_AP */
+#ifdef CONFIG_TDLS
+ if (data)
+ wpa_tdls_disable_unreachable_link(wpa_s->wpa,
+ data->low_ack.addr);
+#endif /* CONFIG_TDLS */
+ break;
+ case EVENT_IBSS_PEER_LOST:
+#ifdef CONFIG_IBSS_RSN
+ ibss_rsn_stop(wpa_s->ibss_rsn, data->ibss_peer_lost.peer);
+#endif /* CONFIG_IBSS_RSN */
+ break;
+ case EVENT_DRIVER_GTK_REKEY:
+ if (os_memcmp(data->driver_gtk_rekey.bssid,
+ wpa_s->bssid, ETH_ALEN))
+ break;
+ if (!wpa_s->wpa)
+ break;
+ wpa_sm_update_replay_ctr(wpa_s->wpa,
+ data->driver_gtk_rekey.replay_ctr);
+ break;
+ case EVENT_SCHED_SCAN_STOPPED:
+ wpa_s->pno = 0;
+ wpa_s->sched_scanning = 0;
+ resched = wpa_s->scanning;
+ wpa_supplicant_notify_scanning(wpa_s, 0);
+
+ if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED)
+ break;
+
+ /*
+ * Start a new sched scan to continue searching for more SSIDs
+ * either if timed out or PNO schedule scan is pending.
+ */
+ if (wpa_s->sched_scan_timed_out) {
+ wpa_supplicant_req_sched_scan(wpa_s);
+ } else if (wpa_s->pno_sched_pending) {
+ wpa_s->pno_sched_pending = 0;
+ wpas_start_pno(wpa_s);
+ } else if (resched) {
+ wpa_supplicant_req_scan(wpa_s, 0, 0);
+ }
+
+ break;
+ case EVENT_WPS_BUTTON_PUSHED:
+#ifdef CONFIG_WPS
+ wpas_wps_start_pbc(wpa_s, NULL, 0);
+#endif /* CONFIG_WPS */
+ break;
+ case EVENT_AVOID_FREQUENCIES:
+ wpa_supplicant_notify_avoid_freq(wpa_s, data);
+ break;
+ case EVENT_CONNECT_FAILED_REASON:
+#ifdef CONFIG_AP
+ if (!wpa_s->ap_iface || !data)
+ break;
+ hostapd_event_connect_failed_reason(
+ wpa_s->ap_iface->bss[0],
+ data->connect_failed_reason.addr,
+ data->connect_failed_reason.code);
+#endif /* CONFIG_AP */
+ break;
+ case EVENT_NEW_PEER_CANDIDATE:
+#ifdef CONFIG_MESH
+ if (!wpa_s->ifmsh || !data)
+ break;
+ wpa_mesh_notify_peer(wpa_s, data->mesh_peer.peer,
+ data->mesh_peer.ies,
+ data->mesh_peer.ie_len);
+#endif /* CONFIG_MESH */
+ break;
+ default:
+ wpa_msg(wpa_s, MSG_INFO, "Unknown event %d", event);
+ break;
+ }
+}
diff --git a/freebsd/contrib/wpa/wpa_supplicant/gas_query.c b/freebsd/contrib/wpa/wpa_supplicant/gas_query.c
new file mode 100644
index 00000000..a93f8064
--- /dev/null
+++ b/freebsd/contrib/wpa/wpa_supplicant/gas_query.c
@@ -0,0 +1,721 @@
+#include <machine/rtems-bsd-user-space.h>
+
+/*
+ * Generic advertisement service (GAS) query
+ * Copyright (c) 2009, Atheros Communications
+ * Copyright (c) 2011-2014, Qualcomm Atheros, Inc.
+ * Copyright (c) 2011-2014, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "utils/eloop.h"
+#include "common/ieee802_11_defs.h"
+#include "common/gas.h"
+#include "common/wpa_ctrl.h"
+#include "rsn_supp/wpa.h"
+#include "wpa_supplicant_i.h"
+#include "driver_i.h"
+#include "offchannel.h"
+#include "gas_query.h"
+
+
+/** GAS query timeout in seconds */
+#define GAS_QUERY_TIMEOUT_PERIOD 2
+
+
+/**
+ * struct gas_query_pending - Pending GAS query
+ */
+struct gas_query_pending {
+ struct dl_list list;
+ struct gas_query *gas;
+ u8 addr[ETH_ALEN];
+ u8 dialog_token;
+ u8 next_frag_id;
+ unsigned int wait_comeback:1;
+ unsigned int offchannel_tx_started:1;
+ int freq;
+ u16 status_code;
+ struct wpabuf *req;
+ struct wpabuf *adv_proto;
+ struct wpabuf *resp;
+ struct os_reltime last_oper;
+ void (*cb)(void *ctx, const u8 *dst, u8 dialog_token,
+ enum gas_query_result result,
+ const struct wpabuf *adv_proto,
+ const struct wpabuf *resp, u16 status_code);
+ void *ctx;
+};
+
+/**
+ * struct gas_query - Internal GAS query data
+ */
+struct gas_query {
+ struct wpa_supplicant *wpa_s;
+ struct dl_list pending; /* struct gas_query_pending */
+ struct gas_query_pending *current;
+ struct wpa_radio_work *work;
+};
+
+
+static void gas_query_tx_comeback_timeout(void *eloop_data, void *user_ctx);
+static void gas_query_timeout(void *eloop_data, void *user_ctx);
+
+
+static int ms_from_time(struct os_reltime *last)
+{
+ struct os_reltime now, res;
+
+ os_get_reltime(&now);
+ os_reltime_sub(&now, last, &res);
+ return res.sec * 1000 + res.usec / 1000;
+}
+
+
+/**
+ * gas_query_init - Initialize GAS query component
+ * @wpa_s: Pointer to wpa_supplicant data
+ * Returns: Pointer to GAS query data or %NULL on failure
+ */
+struct gas_query * gas_query_init(struct wpa_supplicant *wpa_s)
+{
+ struct gas_query *gas;
+
+ gas = os_zalloc(sizeof(*gas));
+ if (gas == NULL)
+ return NULL;
+
+ gas->wpa_s = wpa_s;
+ dl_list_init(&gas->pending);
+
+ return gas;
+}
+
+
+static const char * gas_result_txt(enum gas_query_result result)
+{
+ switch (result) {
+ case GAS_QUERY_SUCCESS:
+ return "SUCCESS";
+ case GAS_QUERY_FAILURE:
+ return "FAILURE";
+ case GAS_QUERY_TIMEOUT:
+ return "TIMEOUT";
+ case GAS_QUERY_PEER_ERROR:
+ return "PEER_ERROR";
+ case GAS_QUERY_INTERNAL_ERROR:
+ return "INTERNAL_ERROR";
+ case GAS_QUERY_CANCELLED:
+ return "CANCELLED";
+ case GAS_QUERY_DELETED_AT_DEINIT:
+ return "DELETED_AT_DEINIT";
+ }
+
+ return "N/A";
+}
+
+
+static void gas_query_free(struct gas_query_pending *query, int del_list)
+{
+ struct gas_query *gas = query->gas;
+
+ if (del_list)
+ dl_list_del(&query->list);
+
+ if (gas->work && gas->work->ctx == query) {
+ radio_work_done(gas->work);
+ gas->work = NULL;
+ }
+
+ wpabuf_free(query->req);
+ wpabuf_free(query->adv_proto);
+ wpabuf_free(query->resp);
+ os_free(query);
+}
+
+
+static void gas_query_done(struct gas_query *gas,
+ struct gas_query_pending *query,
+ enum gas_query_result result)
+{
+ wpa_msg(gas->wpa_s, MSG_INFO, GAS_QUERY_DONE "addr=" MACSTR
+ " dialog_token=%u freq=%d status_code=%u result=%s",
+ MAC2STR(query->addr), query->dialog_token, query->freq,
+ query->status_code, gas_result_txt(result));
+ if (gas->current == query)
+ gas->current = NULL;
+ if (query->offchannel_tx_started)
+ offchannel_send_action_done(gas->wpa_s);
+ eloop_cancel_timeout(gas_query_tx_comeback_timeout, gas, query);
+ eloop_cancel_timeout(gas_query_timeout, gas, query);
+ dl_list_del(&query->list);
+ query->cb(query->ctx, query->addr, query->dialog_token, result,
+ query->adv_proto, query->resp, query->status_code);
+ gas_query_free(query, 0);
+}
+
+
+/**
+ * gas_query_deinit - Deinitialize GAS query component
+ * @gas: GAS query data from gas_query_init()
+ */
+void gas_query_deinit(struct gas_query *gas)
+{
+ struct gas_query_pending *query, *next;
+
+ if (gas == NULL)
+ return;
+
+ dl_list_for_each_safe(query, next, &gas->pending,
+ struct gas_query_pending, list)
+ gas_query_done(gas, query, GAS_QUERY_DELETED_AT_DEINIT);
+
+ os_free(gas);
+}
+
+
+static struct gas_query_pending *
+gas_query_get_pending(struct gas_query *gas, const u8 *addr, u8 dialog_token)
+{
+ struct gas_query_pending *q;
+ dl_list_for_each(q, &gas->pending, struct gas_query_pending, list) {
+ if (os_memcmp(q->addr, addr, ETH_ALEN) == 0 &&
+ q->dialog_token == dialog_token)
+ return q;
+ }
+ return NULL;
+}
+
+
+static int gas_query_append(struct gas_query_pending *query, const u8 *data,
+ size_t len)
+{
+ if (wpabuf_resize(&query->resp, len) < 0) {
+ wpa_printf(MSG_DEBUG, "GAS: No memory to store the response");
+ return -1;
+ }
+ wpabuf_put_data(query->resp, data, len);
+ return 0;
+}
+
+
+static void gas_query_tx_status(struct wpa_supplicant *wpa_s,
+ unsigned int freq, const u8 *dst,
+ const u8 *src, const u8 *bssid,
+ const u8 *data, size_t data_len,
+ enum offchannel_send_action_result result)
+{
+ struct gas_query_pending *query;
+ struct gas_query *gas = wpa_s->gas;
+ int dur;
+
+ if (gas->current == NULL) {
+ wpa_printf(MSG_DEBUG, "GAS: Unexpected TX status: freq=%u dst="
+ MACSTR " result=%d - no query in progress",
+ freq, MAC2STR(dst), result);
+ return;
+ }
+
+ query = gas->current;
+
+ dur = ms_from_time(&query->last_oper);
+ wpa_printf(MSG_DEBUG, "GAS: TX status: freq=%u dst=" MACSTR
+ " result=%d query=%p dialog_token=%u dur=%d ms",
+ freq, MAC2STR(dst), result, query, query->dialog_token, dur);
+ if (os_memcmp(dst, query->addr, ETH_ALEN) != 0) {
+ wpa_printf(MSG_DEBUG, "GAS: TX status for unexpected destination");
+ return;
+ }
+ os_get_reltime(&query->last_oper);
+
+ if (result == OFFCHANNEL_SEND_ACTION_SUCCESS) {
+ eloop_cancel_timeout(gas_query_timeout, gas, query);
+ eloop_register_timeout(GAS_QUERY_TIMEOUT_PERIOD, 0,
+ gas_query_timeout, gas, query);
+ }
+ if (result == OFFCHANNEL_SEND_ACTION_FAILED) {
+ eloop_cancel_timeout(gas_query_timeout, gas, query);
+ eloop_register_timeout(0, 0, gas_query_timeout, gas, query);
+ }
+}
+
+
+static int pmf_in_use(struct wpa_supplicant *wpa_s, const u8 *addr)
+{
+ if (wpa_s->current_ssid == NULL ||
+ wpa_s->wpa_state < WPA_4WAY_HANDSHAKE ||
+ os_memcmp(addr, wpa_s->bssid, ETH_ALEN) != 0)
+ return 0;
+ return wpa_sm_pmf_enabled(wpa_s->wpa);
+}
+
+
+static int gas_query_tx(struct gas_query *gas, struct gas_query_pending *query,
+ struct wpabuf *req)
+{
+ unsigned int wait_time;
+ int res, prot = pmf_in_use(gas->wpa_s, query->addr);
+
+ wpa_printf(MSG_DEBUG, "GAS: Send action frame to " MACSTR " len=%u "
+ "freq=%d prot=%d", MAC2STR(query->addr),
+ (unsigned int) wpabuf_len(req), query->freq, prot);
+ if (prot) {
+ u8 *categ = wpabuf_mhead_u8(req);
+ *categ = WLAN_ACTION_PROTECTED_DUAL;
+ }
+ os_get_reltime(&query->last_oper);
+ wait_time = 1000;
+ if (gas->wpa_s->max_remain_on_chan &&
+ wait_time > gas->wpa_s->max_remain_on_chan)
+ wait_time = gas->wpa_s->max_remain_on_chan;
+ res = offchannel_send_action(gas->wpa_s, query->freq, query->addr,
+ gas->wpa_s->own_addr, query->addr,
+ wpabuf_head(req), wpabuf_len(req),
+ wait_time, gas_query_tx_status, 0);
+ if (res == 0)
+ query->offchannel_tx_started = 1;
+ return res;
+}
+
+
+static void gas_query_tx_comeback_req(struct gas_query *gas,
+ struct gas_query_pending *query)
+{
+ struct wpabuf *req;
+
+ req = gas_build_comeback_req(query->dialog_token);
+ if (req == NULL) {
+ gas_query_done(gas, query, GAS_QUERY_INTERNAL_ERROR);
+ return;
+ }
+
+ if (gas_query_tx(gas, query, req) < 0) {
+ wpa_printf(MSG_DEBUG, "GAS: Failed to send Action frame to "
+ MACSTR, MAC2STR(query->addr));
+ gas_query_done(gas, query, GAS_QUERY_INTERNAL_ERROR);
+ }
+
+ wpabuf_free(req);
+}
+
+
+static void gas_query_tx_comeback_timeout(void *eloop_data, void *user_ctx)
+{
+ struct gas_query *gas = eloop_data;
+ struct gas_query_pending *query = user_ctx;
+
+ wpa_printf(MSG_DEBUG, "GAS: Comeback timeout for request to " MACSTR,
+ MAC2STR(query->addr));
+ gas_query_tx_comeback_req(gas, query);
+}
+
+
+static void gas_query_tx_comeback_req_delay(struct gas_query *gas,
+ struct gas_query_pending *query,
+ u16 comeback_delay)
+{
+ unsigned int secs, usecs;
+
+ secs = (comeback_delay * 1024) / 1000000;
+ usecs = comeback_delay * 1024 - secs * 1000000;
+ wpa_printf(MSG_DEBUG, "GAS: Send comeback request to " MACSTR
+ " in %u secs %u usecs", MAC2STR(query->addr), secs, usecs);
+ eloop_cancel_timeout(gas_query_tx_comeback_timeout, gas, query);
+ eloop_register_timeout(secs, usecs, gas_query_tx_comeback_timeout,
+ gas, query);
+}
+
+
+static void gas_query_rx_initial(struct gas_query *gas,
+ struct gas_query_pending *query,
+ const u8 *adv_proto, const u8 *resp,
+ size_t len, u16 comeback_delay)
+{
+ wpa_printf(MSG_DEBUG, "GAS: Received initial response from "
+ MACSTR " (dialog_token=%u comeback_delay=%u)",
+ MAC2STR(query->addr), query->dialog_token, comeback_delay);
+
+ query->adv_proto = wpabuf_alloc_copy(adv_proto, 2 + adv_proto[1]);
+ if (query->adv_proto == NULL) {
+ gas_query_done(gas, query, GAS_QUERY_INTERNAL_ERROR);
+ return;
+ }
+
+ if (comeback_delay) {
+ query->wait_comeback = 1;
+ gas_query_tx_comeback_req_delay(gas, query, comeback_delay);
+ return;
+ }
+
+ /* Query was completed without comeback mechanism */
+ if (gas_query_append(query, resp, len) < 0) {
+ gas_query_done(gas, query, GAS_QUERY_INTERNAL_ERROR);
+ return;
+ }
+
+ gas_query_done(gas, query, GAS_QUERY_SUCCESS);
+}
+
+
+static void gas_query_rx_comeback(struct gas_query *gas,
+ struct gas_query_pending *query,
+ const u8 *adv_proto, const u8 *resp,
+ size_t len, u8 frag_id, u8 more_frags,
+ u16 comeback_delay)
+{
+ wpa_printf(MSG_DEBUG, "GAS: Received comeback response from "
+ MACSTR " (dialog_token=%u frag_id=%u more_frags=%u "
+ "comeback_delay=%u)",
+ MAC2STR(query->addr), query->dialog_token, frag_id,
+ more_frags, comeback_delay);
+
+ if ((size_t) 2 + adv_proto[1] != wpabuf_len(query->adv_proto) ||
+ os_memcmp(adv_proto, wpabuf_head(query->adv_proto),
+ wpabuf_len(query->adv_proto)) != 0) {
+ wpa_printf(MSG_DEBUG, "GAS: Advertisement Protocol changed "
+ "between initial and comeback response from "
+ MACSTR, MAC2STR(query->addr));
+ gas_query_done(gas, query, GAS_QUERY_PEER_ERROR);
+ return;
+ }
+
+ if (comeback_delay) {
+ if (frag_id) {
+ wpa_printf(MSG_DEBUG, "GAS: Invalid comeback response "
+ "with non-zero frag_id and comeback_delay "
+ "from " MACSTR, MAC2STR(query->addr));
+ gas_query_done(gas, query, GAS_QUERY_PEER_ERROR);
+ return;
+ }
+ gas_query_tx_comeback_req_delay(gas, query, comeback_delay);
+ return;
+ }
+
+ if (frag_id != query->next_frag_id) {
+ wpa_printf(MSG_DEBUG, "GAS: Unexpected frag_id in response "
+ "from " MACSTR, MAC2STR(query->addr));
+ if (frag_id + 1 == query->next_frag_id) {
+ wpa_printf(MSG_DEBUG, "GAS: Drop frame as possible "
+ "retry of previous fragment");
+ return;
+ }
+ gas_query_done(gas, query, GAS_QUERY_PEER_ERROR);
+ return;
+ }
+ query->next_frag_id++;
+
+ if (gas_query_append(query, resp, len) < 0) {
+ gas_query_done(gas, query, GAS_QUERY_INTERNAL_ERROR);
+ return;
+ }
+
+ if (more_frags) {
+ gas_query_tx_comeback_req(gas, query);
+ return;
+ }
+
+ gas_query_done(gas, query, GAS_QUERY_SUCCESS);
+}
+
+
+/**
+ * gas_query_rx - Indicate reception of a Public Action or Protected Dual frame
+ * @gas: GAS query data from gas_query_init()
+ * @da: Destination MAC address of the Action frame
+ * @sa: Source MAC address of the Action frame
+ * @bssid: BSSID of the Action frame
+ * @categ: Category of the Action frame
+ * @data: Payload of the Action frame
+ * @len: Length of @data
+ * @freq: Frequency (in MHz) on which the frame was received
+ * Returns: 0 if the Public Action frame was a GAS frame or -1 if not
+ */
+int gas_query_rx(struct gas_query *gas, const u8 *da, const u8 *sa,
+ const u8 *bssid, u8 categ, const u8 *data, size_t len,
+ int freq)
+{
+ struct gas_query_pending *query;
+ u8 action, dialog_token, frag_id = 0, more_frags = 0;
+ u16 comeback_delay, resp_len;
+ const u8 *pos, *adv_proto;
+ int prot, pmf;
+ unsigned int left;
+
+ if (gas == NULL || len < 4)
+ return -1;
+
+ prot = categ == WLAN_ACTION_PROTECTED_DUAL;
+ pmf = pmf_in_use(gas->wpa_s, bssid);
+ if (prot && !pmf) {
+ wpa_printf(MSG_DEBUG, "GAS: Drop unexpected protected GAS frame when PMF is disabled");
+ return 0;
+ }
+ if (!prot && pmf) {
+ wpa_printf(MSG_DEBUG, "GAS: Drop unexpected unprotected GAS frame when PMF is enabled");
+ return 0;
+ }
+
+ pos = data;
+ action = *pos++;
+ dialog_token = *pos++;
+
+ if (action != WLAN_PA_GAS_INITIAL_RESP &&
+ action != WLAN_PA_GAS_COMEBACK_RESP)
+ return -1; /* Not a GAS response */
+
+ query = gas_query_get_pending(gas, sa, dialog_token);
+ if (query == NULL) {
+ wpa_printf(MSG_DEBUG, "GAS: No pending query found for " MACSTR
+ " dialog token %u", MAC2STR(sa), dialog_token);
+ return -1;
+ }
+
+ wpa_printf(MSG_DEBUG, "GAS: Response in %d ms from " MACSTR,
+ ms_from_time(&query->last_oper), MAC2STR(sa));
+
+ if (query->wait_comeback && action == WLAN_PA_GAS_INITIAL_RESP) {
+ wpa_printf(MSG_DEBUG, "GAS: Unexpected initial response from "
+ MACSTR " dialog token %u when waiting for comeback "
+ "response", MAC2STR(sa), dialog_token);
+ return 0;
+ }
+
+ if (!query->wait_comeback && action == WLAN_PA_GAS_COMEBACK_RESP) {
+ wpa_printf(MSG_DEBUG, "GAS: Unexpected comeback response from "
+ MACSTR " dialog token %u when waiting for initial "
+ "response", MAC2STR(sa), dialog_token);
+ return 0;
+ }
+
+ query->status_code = WPA_GET_LE16(pos);
+ pos += 2;
+
+ if (query->status_code == WLAN_STATUS_QUERY_RESP_OUTSTANDING &&
+ action == WLAN_PA_GAS_COMEBACK_RESP) {
+ wpa_printf(MSG_DEBUG, "GAS: Allow non-zero status for outstanding comeback response");
+ } else if (query->status_code != WLAN_STATUS_SUCCESS) {
+ wpa_printf(MSG_DEBUG, "GAS: Query to " MACSTR " dialog token "
+ "%u failed - status code %u",
+ MAC2STR(sa), dialog_token, query->status_code);
+ gas_query_done(gas, query, GAS_QUERY_FAILURE);
+ return 0;
+ }
+
+ if (action == WLAN_PA_GAS_COMEBACK_RESP) {
+ if (pos + 1 > data + len)
+ return 0;
+ frag_id = *pos & 0x7f;
+ more_frags = (*pos & 0x80) >> 7;
+ pos++;
+ }
+
+ /* Comeback Delay */
+ if (pos + 2 > data + len)
+ return 0;
+ comeback_delay = WPA_GET_LE16(pos);
+ pos += 2;
+
+ /* Advertisement Protocol element */
+ if (pos + 2 > data + len || pos + 2 + pos[1] > data + len) {
+ wpa_printf(MSG_DEBUG, "GAS: No room for Advertisement "
+ "Protocol element in the response from " MACSTR,
+ MAC2STR(sa));
+ return 0;
+ }
+
+ if (*pos != WLAN_EID_ADV_PROTO) {
+ wpa_printf(MSG_DEBUG, "GAS: Unexpected Advertisement "
+ "Protocol element ID %u in response from " MACSTR,
+ *pos, MAC2STR(sa));
+ return 0;
+ }
+
+ adv_proto = pos;
+ pos += 2 + pos[1];
+
+ /* Query Response Length */
+ if (pos + 2 > data + len) {
+ wpa_printf(MSG_DEBUG, "GAS: No room for GAS Response Length");
+ return 0;
+ }
+ resp_len = WPA_GET_LE16(pos);
+ pos += 2;
+
+ left = data + len - pos;
+ if (resp_len > left) {
+ wpa_printf(MSG_DEBUG, "GAS: Truncated Query Response in "
+ "response from " MACSTR, MAC2STR(sa));
+ return 0;
+ }
+
+ if (resp_len < left) {
+ wpa_printf(MSG_DEBUG, "GAS: Ignore %u octets of extra data "
+ "after Query Response from " MACSTR,
+ left - resp_len, MAC2STR(sa));
+ }
+
+ if (action == WLAN_PA_GAS_COMEBACK_RESP)
+ gas_query_rx_comeback(gas, query, adv_proto, pos, resp_len,
+ frag_id, more_frags, comeback_delay);
+ else
+ gas_query_rx_initial(gas, query, adv_proto, pos, resp_len,
+ comeback_delay);
+
+ return 0;
+}
+
+
+static void gas_query_timeout(void *eloop_data, void *user_ctx)
+{
+ struct gas_query *gas = eloop_data;
+ struct gas_query_pending *query = user_ctx;
+
+ wpa_printf(MSG_DEBUG, "GAS: No response received for query to " MACSTR
+ " dialog token %u",
+ MAC2STR(query->addr), query->dialog_token);
+ gas_query_done(gas, query, GAS_QUERY_TIMEOUT);
+}
+
+
+static int gas_query_dialog_token_available(struct gas_query *gas,
+ const u8 *dst, u8 dialog_token)
+{
+ struct gas_query_pending *q;
+ dl_list_for_each(q, &gas->pending, struct gas_query_pending, list) {
+ if (os_memcmp(dst, q->addr, ETH_ALEN) == 0 &&
+ dialog_token == q->dialog_token)
+ return 0;
+ }
+
+ return 1;
+}
+
+
+static void gas_query_start_cb(struct wpa_radio_work *work, int deinit)
+{
+ struct gas_query_pending *query = work->ctx;
+ struct gas_query *gas = query->gas;
+ struct wpa_supplicant *wpa_s = gas->wpa_s;
+
+ if (deinit) {
+ if (work->started) {
+ gas->work = NULL;
+ gas_query_done(gas, query, GAS_QUERY_DELETED_AT_DEINIT);
+ return;
+ }
+
+ gas_query_free(query, 1);
+ return;
+ }
+
+ if (wpas_update_random_addr_disassoc(wpa_s) < 0) {
+ wpa_msg(wpa_s, MSG_INFO,
+ "Failed to assign random MAC address for GAS");
+ gas_query_free(query, 1);
+ radio_work_done(work);
+ return;
+ }
+
+ gas->work = work;
+
+ if (gas_query_tx(gas, query, query->req) < 0) {
+ wpa_printf(MSG_DEBUG, "GAS: Failed to send Action frame to "
+ MACSTR, MAC2STR(query->addr));
+ gas_query_free(query, 1);
+ return;
+ }
+ gas->current = query;
+
+ wpa_printf(MSG_DEBUG, "GAS: Starting query timeout for dialog token %u",
+ query->dialog_token);
+ eloop_register_timeout(GAS_QUERY_TIMEOUT_PERIOD, 0,
+ gas_query_timeout, gas, query);
+
+}
+
+
+/**
+ * gas_query_req - Request a GAS query
+ * @gas: GAS query data from gas_query_init()
+ * @dst: Destination MAC address for the query
+ * @freq: Frequency (in MHz) for the channel on which to send the query
+ * @req: GAS query payload (to be freed by gas_query module in case of success
+ * return)
+ * @cb: Callback function for reporting GAS query result and response
+ * @ctx: Context pointer to use with the @cb call
+ * Returns: dialog token (>= 0) on success or -1 on failure
+ */
+int gas_query_req(struct gas_query *gas, const u8 *dst, int freq,
+ struct wpabuf *req,
+ void (*cb)(void *ctx, const u8 *dst, u8 dialog_token,
+ enum gas_query_result result,
+ const struct wpabuf *adv_proto,
+ const struct wpabuf *resp, u16 status_code),
+ void *ctx)
+{
+ struct gas_query_pending *query;
+ int dialog_token;
+ static int next_start = 0;
+
+ if (wpabuf_len(req) < 3)
+ return -1;
+
+ for (dialog_token = 0; dialog_token < 256; dialog_token++) {
+ if (gas_query_dialog_token_available(
+ gas, dst, (next_start + dialog_token) % 256))
+ break;
+ }
+ if (dialog_token == 256)
+ return -1; /* Too many pending queries */
+ dialog_token = (next_start + dialog_token) % 256;
+ next_start = (dialog_token + 1) % 256;
+
+ query = os_zalloc(sizeof(*query));
+ if (query == NULL)
+ return -1;
+
+ query->gas = gas;
+ os_memcpy(query->addr, dst, ETH_ALEN);
+ query->dialog_token = dialog_token;
+ query->freq = freq;
+ query->cb = cb;
+ query->ctx = ctx;
+ query->req = req;
+ dl_list_add(&gas->pending, &query->list);
+
+ *(wpabuf_mhead_u8(req) + 2) = dialog_token;
+
+ wpa_msg(gas->wpa_s, MSG_INFO, GAS_QUERY_START "addr=" MACSTR
+ " dialog_token=%u freq=%d",
+ MAC2STR(query->addr), query->dialog_token, query->freq);
+
+ if (radio_add_work(gas->wpa_s, freq, "gas-query", 0, gas_query_start_cb,
+ query) < 0) {
+ gas_query_free(query, 1);
+ return -1;
+ }
+
+ return dialog_token;
+}
+
+
+/**
+ * gas_query_cancel - Cancel a pending GAS query
+ * @gas: GAS query data from gas_query_init()
+ * @dst: Destination MAC address for the query
+ * @dialog_token: Dialog token from gas_query_req()
+ */
+void gas_query_cancel(struct gas_query *gas, const u8 *dst, u8 dialog_token)
+{
+ struct gas_query_pending *query;
+
+ query = gas_query_get_pending(gas, dst, dialog_token);
+ if (query)
+ gas_query_done(gas, query, GAS_QUERY_CANCELLED);
+
+}
diff --git a/freebsd/contrib/wpa/wpa_supplicant/gas_query.h b/freebsd/contrib/wpa/wpa_supplicant/gas_query.h
new file mode 100644
index 00000000..ad134908
--- /dev/null
+++ b/freebsd/contrib/wpa/wpa_supplicant/gas_query.h
@@ -0,0 +1,59 @@
+/*
+ * Generic advertisement service (GAS) query
+ * Copyright (c) 2009, Atheros Communications
+ * Copyright (c) 2011, Qualcomm Atheros
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef GAS_QUERY_H
+#define GAS_QUERY_H
+
+struct gas_query;
+
+#ifdef CONFIG_GAS
+
+struct gas_query * gas_query_init(struct wpa_supplicant *wpa_s);
+void gas_query_deinit(struct gas_query *gas);
+int gas_query_rx(struct gas_query *gas, const u8 *da, const u8 *sa,
+ const u8 *bssid, u8 categ, const u8 *data, size_t len,
+ int freq);
+
+/**
+ * enum gas_query_result - GAS query result
+ */
+enum gas_query_result {
+ GAS_QUERY_SUCCESS,
+ GAS_QUERY_FAILURE,
+ GAS_QUERY_TIMEOUT,
+ GAS_QUERY_PEER_ERROR,
+ GAS_QUERY_INTERNAL_ERROR,
+ GAS_QUERY_CANCELLED,
+ GAS_QUERY_DELETED_AT_DEINIT
+};
+
+int gas_query_req(struct gas_query *gas, const u8 *dst, int freq,
+ struct wpabuf *req,
+ void (*cb)(void *ctx, const u8 *dst, u8 dialog_token,
+ enum gas_query_result result,
+ const struct wpabuf *adv_proto,
+ const struct wpabuf *resp, u16 status_code),
+ void *ctx);
+void gas_query_cancel(struct gas_query *gas, const u8 *dst, u8 dialog_token);
+
+#else /* CONFIG_GAS */
+
+static inline struct gas_query * gas_query_init(struct wpa_supplicant *wpa_s)
+{
+ return (void *) 1;
+}
+
+static inline void gas_query_deinit(struct gas_query *gas)
+{
+}
+
+#endif /* CONFIG_GAS */
+
+
+#endif /* GAS_QUERY_H */
diff --git a/freebsd/contrib/wpa/wpa_supplicant/hs20_supplicant.c b/freebsd/contrib/wpa/wpa_supplicant/hs20_supplicant.c
new file mode 100644
index 00000000..ab52320d
--- /dev/null
+++ b/freebsd/contrib/wpa/wpa_supplicant/hs20_supplicant.c
@@ -0,0 +1,1011 @@
+#include <machine/rtems-bsd-user-space.h>
+
+/*
+ * Copyright (c) 2009, Atheros Communications, Inc.
+ * Copyright (c) 2011-2013, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+#include <sys/stat.h>
+
+#include "common.h"
+#include "eloop.h"
+#include "common/ieee802_11_common.h"
+#include "common/ieee802_11_defs.h"
+#include "common/gas.h"
+#include "common/wpa_ctrl.h"
+#include "rsn_supp/wpa.h"
+#include "wpa_supplicant_i.h"
+#include "driver_i.h"
+#include "config.h"
+#include "scan.h"
+#include "bss.h"
+#include "blacklist.h"
+#include "gas_query.h"
+#include "interworking.h"
+#include "hs20_supplicant.h"
+
+
+#define OSU_MAX_ITEMS 10
+
+struct osu_lang_string {
+ char lang[4];
+ char text[253];
+};
+
+struct osu_icon {
+ u16 width;
+ u16 height;
+ char lang[4];
+ char icon_type[256];
+ char filename[256];
+ unsigned int id;
+ unsigned int failed:1;
+};
+
+struct osu_provider {
+ u8 bssid[ETH_ALEN];
+ u8 osu_ssid[SSID_MAX_LEN];
+ u8 osu_ssid_len;
+ char server_uri[256];
+ u32 osu_methods; /* bit 0 = OMA-DM, bit 1 = SOAP-XML SPP */
+ char osu_nai[256];
+ struct osu_lang_string friendly_name[OSU_MAX_ITEMS];
+ size_t friendly_name_count;
+ struct osu_lang_string serv_desc[OSU_MAX_ITEMS];
+ size_t serv_desc_count;
+ struct osu_icon icon[OSU_MAX_ITEMS];
+ size_t icon_count;
+};
+
+
+void wpas_hs20_add_indication(struct wpabuf *buf, int pps_mo_id)
+{
+ u8 conf;
+
+ wpabuf_put_u8(buf, WLAN_EID_VENDOR_SPECIFIC);
+ wpabuf_put_u8(buf, pps_mo_id >= 0 ? 7 : 5);
+ wpabuf_put_be24(buf, OUI_WFA);
+ wpabuf_put_u8(buf, HS20_INDICATION_OUI_TYPE);
+ conf = HS20_VERSION;
+ if (pps_mo_id >= 0)
+ conf |= HS20_PPS_MO_ID_PRESENT;
+ wpabuf_put_u8(buf, conf);
+ if (pps_mo_id >= 0)
+ wpabuf_put_le16(buf, pps_mo_id);
+}
+
+
+int is_hs20_network(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid,
+ struct wpa_bss *bss)
+{
+ if (!wpa_s->conf->hs20 || !ssid)
+ return 0;
+
+ if (ssid->parent_cred)
+ return 1;
+
+ if (bss && !wpa_bss_get_vendor_ie(bss, HS20_IE_VENDOR_TYPE))
+ return 0;
+
+ /*
+ * This may catch some non-Hotspot 2.0 cases, but it is safer to do that
+ * than cause Hotspot 2.0 connections without indication element getting
+ * added. Non-Hotspot 2.0 APs should ignore the unknown vendor element.
+ */
+
+ if (!(ssid->key_mgmt & WPA_KEY_MGMT_IEEE8021X))
+ return 0;
+ if (!(ssid->pairwise_cipher & WPA_CIPHER_CCMP))
+ return 0;
+ if (ssid->proto != WPA_PROTO_RSN)
+ return 0;
+
+ return 1;
+}
+
+
+int hs20_get_pps_mo_id(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid)
+{
+ struct wpa_cred *cred;
+
+ if (ssid == NULL)
+ return 0;
+
+ if (ssid->update_identifier)
+ return ssid->update_identifier;
+
+ if (ssid->parent_cred == NULL)
+ return 0;
+
+ for (cred = wpa_s->conf->cred; cred; cred = cred->next) {
+ if (ssid->parent_cred == cred)
+ return cred->update_identifier;
+ }
+
+ return 0;
+}
+
+
+void hs20_put_anqp_req(u32 stypes, const u8 *payload, size_t payload_len,
+ struct wpabuf *buf)
+{
+ u8 *len_pos;
+
+ if (buf == NULL)
+ return;
+
+ len_pos = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC);
+ wpabuf_put_be24(buf, OUI_WFA);
+ wpabuf_put_u8(buf, HS20_ANQP_OUI_TYPE);
+ if (stypes == BIT(HS20_STYPE_NAI_HOME_REALM_QUERY)) {
+ wpabuf_put_u8(buf, HS20_STYPE_NAI_HOME_REALM_QUERY);
+ wpabuf_put_u8(buf, 0); /* Reserved */
+ if (payload)
+ wpabuf_put_data(buf, payload, payload_len);
+ } else if (stypes == BIT(HS20_STYPE_ICON_REQUEST)) {
+ wpabuf_put_u8(buf, HS20_STYPE_ICON_REQUEST);
+ wpabuf_put_u8(buf, 0); /* Reserved */
+ if (payload)
+ wpabuf_put_data(buf, payload, payload_len);
+ } else {
+ u8 i;
+ wpabuf_put_u8(buf, HS20_STYPE_QUERY_LIST);
+ wpabuf_put_u8(buf, 0); /* Reserved */
+ for (i = 0; i < 32; i++) {
+ if (stypes & BIT(i))
+ wpabuf_put_u8(buf, i);
+ }
+ }
+ gas_anqp_set_element_len(buf, len_pos);
+
+ gas_anqp_set_len(buf);
+}
+
+
+struct wpabuf * hs20_build_anqp_req(u32 stypes, const u8 *payload,
+ size_t payload_len)
+{
+ struct wpabuf *buf;
+
+ buf = gas_anqp_build_initial_req(0, 100 + payload_len);
+ if (buf == NULL)
+ return NULL;
+
+ hs20_put_anqp_req(stypes, payload, payload_len, buf);
+
+ return buf;
+}
+
+
+int hs20_anqp_send_req(struct wpa_supplicant *wpa_s, const u8 *dst, u32 stypes,
+ const u8 *payload, size_t payload_len)
+{
+ struct wpabuf *buf;
+ int ret = 0;
+ int freq;
+ struct wpa_bss *bss;
+ int res;
+
+ bss = wpa_bss_get_bssid(wpa_s, dst);
+ if (!bss) {
+ wpa_printf(MSG_WARNING,
+ "ANQP: Cannot send query to unknown BSS "
+ MACSTR, MAC2STR(dst));
+ return -1;
+ }
+
+ wpa_bss_anqp_unshare_alloc(bss);
+ freq = bss->freq;
+
+ wpa_printf(MSG_DEBUG, "HS20: ANQP Query Request to " MACSTR " for "
+ "subtypes 0x%x", MAC2STR(dst), stypes);
+
+ buf = hs20_build_anqp_req(stypes, payload, payload_len);
+ if (buf == NULL)
+ return -1;
+
+ res = gas_query_req(wpa_s->gas, dst, freq, buf, anqp_resp_cb, wpa_s);
+ if (res < 0) {
+ wpa_printf(MSG_DEBUG, "ANQP: Failed to send Query Request");
+ wpabuf_free(buf);
+ ret = -1;
+ } else
+ wpa_printf(MSG_DEBUG, "ANQP: Query started with dialog token "
+ "%u", res);
+
+ return ret;
+}
+
+
+static void hs20_set_osu_access_permission(const char *osu_dir,
+ const char *fname)
+{
+ struct stat statbuf;
+
+ /* Get OSU directory information */
+ if (stat(osu_dir, &statbuf) < 0) {
+ wpa_printf(MSG_WARNING, "Cannot stat the OSU directory %s",
+ osu_dir);
+ return;
+ }
+
+ if (chmod(fname, statbuf.st_mode) < 0) {
+ wpa_printf(MSG_WARNING,
+ "Cannot change the permissions for %s", fname);
+ return;
+ }
+
+ if (chown(fname, statbuf.st_uid, statbuf.st_gid) < 0) {
+ wpa_printf(MSG_WARNING, "Cannot change the ownership for %s",
+ fname);
+ }
+}
+
+static int hs20_process_icon_binary_file(struct wpa_supplicant *wpa_s,
+ const u8 *sa, const u8 *pos,
+ size_t slen)
+{
+ char fname[256];
+ int png;
+ FILE *f;
+ u16 data_len;
+
+ wpa_msg(wpa_s, MSG_INFO, "RX-HS20-ANQP " MACSTR " Icon Binary File",
+ MAC2STR(sa));
+
+ if (slen < 4) {
+ wpa_dbg(wpa_s, MSG_DEBUG, "HS 2.0: Too short Icon Binary File "
+ "value from " MACSTR, MAC2STR(sa));
+ return -1;
+ }
+
+ wpa_printf(MSG_DEBUG, "HS 2.0: Download Status Code %u", *pos);
+ if (*pos != 0)
+ return -1;
+ pos++;
+ slen--;
+
+ if ((size_t) 1 + pos[0] > slen) {
+ wpa_dbg(wpa_s, MSG_DEBUG, "HS 2.0: Too short Icon Binary File "
+ "value from " MACSTR, MAC2STR(sa));
+ return -1;
+ }
+ wpa_hexdump_ascii(MSG_DEBUG, "Icon Type", pos + 1, pos[0]);
+ png = os_strncasecmp((char *) pos + 1, "image/png", 9) == 0;
+ slen -= 1 + pos[0];
+ pos += 1 + pos[0];
+
+ if (slen < 2) {
+ wpa_dbg(wpa_s, MSG_DEBUG, "HS 2.0: Too short Icon Binary File "
+ "value from " MACSTR, MAC2STR(sa));
+ return -1;
+ }
+ data_len = WPA_GET_LE16(pos);
+ pos += 2;
+ slen -= 2;
+
+ if (data_len > slen) {
+ wpa_dbg(wpa_s, MSG_DEBUG, "HS 2.0: Too short Icon Binary File "
+ "value from " MACSTR, MAC2STR(sa));
+ return -1;
+ }
+
+ wpa_printf(MSG_DEBUG, "Icon Binary Data: %u bytes", data_len);
+ if (wpa_s->conf->osu_dir == NULL)
+ return -1;
+
+ wpa_s->osu_icon_id++;
+ if (wpa_s->osu_icon_id == 0)
+ wpa_s->osu_icon_id++;
+ snprintf(fname, sizeof(fname), "%s/osu-icon-%u.%s",
+ wpa_s->conf->osu_dir, wpa_s->osu_icon_id,
+ png ? "png" : "icon");
+ f = fopen(fname, "wb");
+ if (f == NULL)
+ return -1;
+
+ hs20_set_osu_access_permission(wpa_s->conf->osu_dir, fname);
+
+ if (fwrite(pos, slen, 1, f) != 1) {
+ fclose(f);
+ unlink(fname);
+ return -1;
+ }
+ fclose(f);
+
+ wpa_msg(wpa_s, MSG_INFO, "RX-HS20-ANQP-ICON %s", fname);
+ return 0;
+}
+
+
+static void hs20_continue_icon_fetch(void *eloop_ctx, void *sock_ctx)
+{
+ struct wpa_supplicant *wpa_s = eloop_ctx;
+ if (wpa_s->fetch_osu_icon_in_progress)
+ hs20_next_osu_icon(wpa_s);
+}
+
+
+static void hs20_osu_icon_fetch_result(struct wpa_supplicant *wpa_s, int res)
+{
+ size_t i, j;
+ struct os_reltime now, tmp;
+ int dur;
+
+ os_get_reltime(&now);
+ os_reltime_sub(&now, &wpa_s->osu_icon_fetch_start, &tmp);
+ dur = tmp.sec * 1000 + tmp.usec / 1000;
+ wpa_printf(MSG_DEBUG, "HS 2.0: Icon fetch dur=%d ms res=%d",
+ dur, res);
+
+ for (i = 0; i < wpa_s->osu_prov_count; i++) {
+ struct osu_provider *osu = &wpa_s->osu_prov[i];
+ for (j = 0; j < osu->icon_count; j++) {
+ struct osu_icon *icon = &osu->icon[j];
+ if (icon->id || icon->failed)
+ continue;
+ if (res < 0)
+ icon->failed = 1;
+ else
+ icon->id = wpa_s->osu_icon_id;
+ return;
+ }
+ }
+}
+
+
+void hs20_parse_rx_hs20_anqp_resp(struct wpa_supplicant *wpa_s,
+ struct wpa_bss *bss, const u8 *sa,
+ const u8 *data, size_t slen)
+{
+ const u8 *pos = data;
+ u8 subtype;
+ struct wpa_bss_anqp *anqp = NULL;
+ int ret;
+
+ if (slen < 2)
+ return;
+
+ if (bss)
+ anqp = bss->anqp;
+
+ subtype = *pos++;
+ slen--;
+
+ pos++; /* Reserved */
+ slen--;
+
+ switch (subtype) {
+ case HS20_STYPE_CAPABILITY_LIST:
+ wpa_msg(wpa_s, MSG_INFO, "RX-HS20-ANQP " MACSTR
+ " HS Capability List", MAC2STR(sa));
+ wpa_hexdump_ascii(MSG_DEBUG, "HS Capability List", pos, slen);
+ if (anqp) {
+ wpabuf_free(anqp->hs20_capability_list);
+ anqp->hs20_capability_list =
+ wpabuf_alloc_copy(pos, slen);
+ }
+ break;
+ case HS20_STYPE_OPERATOR_FRIENDLY_NAME:
+ wpa_msg(wpa_s, MSG_INFO, "RX-HS20-ANQP " MACSTR
+ " Operator Friendly Name", MAC2STR(sa));
+ wpa_hexdump_ascii(MSG_DEBUG, "oper friendly name", pos, slen);
+ if (anqp) {
+ wpabuf_free(anqp->hs20_operator_friendly_name);
+ anqp->hs20_operator_friendly_name =
+ wpabuf_alloc_copy(pos, slen);
+ }
+ break;
+ case HS20_STYPE_WAN_METRICS:
+ wpa_hexdump(MSG_DEBUG, "WAN Metrics", pos, slen);
+ if (slen < 13) {
+ wpa_dbg(wpa_s, MSG_DEBUG, "HS 2.0: Too short WAN "
+ "Metrics value from " MACSTR, MAC2STR(sa));
+ break;
+ }
+ wpa_msg(wpa_s, MSG_INFO, "RX-HS20-ANQP " MACSTR
+ " WAN Metrics %02x:%u:%u:%u:%u:%u", MAC2STR(sa),
+ pos[0], WPA_GET_LE32(pos + 1), WPA_GET_LE32(pos + 5),
+ pos[9], pos[10], WPA_GET_LE16(pos + 11));
+ if (anqp) {
+ wpabuf_free(anqp->hs20_wan_metrics);
+ anqp->hs20_wan_metrics = wpabuf_alloc_copy(pos, slen);
+ }
+ break;
+ case HS20_STYPE_CONNECTION_CAPABILITY:
+ wpa_msg(wpa_s, MSG_INFO, "RX-HS20-ANQP " MACSTR
+ " Connection Capability", MAC2STR(sa));
+ wpa_hexdump_ascii(MSG_DEBUG, "conn capability", pos, slen);
+ if (anqp) {
+ wpabuf_free(anqp->hs20_connection_capability);
+ anqp->hs20_connection_capability =
+ wpabuf_alloc_copy(pos, slen);
+ }
+ break;
+ case HS20_STYPE_OPERATING_CLASS:
+ wpa_msg(wpa_s, MSG_INFO, "RX-HS20-ANQP " MACSTR
+ " Operating Class", MAC2STR(sa));
+ wpa_hexdump_ascii(MSG_DEBUG, "Operating Class", pos, slen);
+ if (anqp) {
+ wpabuf_free(anqp->hs20_operating_class);
+ anqp->hs20_operating_class =
+ wpabuf_alloc_copy(pos, slen);
+ }
+ break;
+ case HS20_STYPE_OSU_PROVIDERS_LIST:
+ wpa_msg(wpa_s, MSG_INFO, "RX-HS20-ANQP " MACSTR
+ " OSU Providers list", MAC2STR(sa));
+ wpa_s->num_prov_found++;
+ if (anqp) {
+ wpabuf_free(anqp->hs20_osu_providers_list);
+ anqp->hs20_osu_providers_list =
+ wpabuf_alloc_copy(pos, slen);
+ }
+ break;
+ case HS20_STYPE_ICON_BINARY_FILE:
+ ret = hs20_process_icon_binary_file(wpa_s, sa, pos, slen);
+ if (wpa_s->fetch_osu_icon_in_progress) {
+ hs20_osu_icon_fetch_result(wpa_s, ret);
+ eloop_cancel_timeout(hs20_continue_icon_fetch,
+ wpa_s, NULL);
+ eloop_register_timeout(0, 0, hs20_continue_icon_fetch,
+ wpa_s, NULL);
+ }
+ break;
+ default:
+ wpa_printf(MSG_DEBUG, "HS20: Unsupported subtype %u", subtype);
+ break;
+ }
+}
+
+
+void hs20_notify_parse_done(struct wpa_supplicant *wpa_s)
+{
+ if (!wpa_s->fetch_osu_icon_in_progress)
+ return;
+ if (eloop_is_timeout_registered(hs20_continue_icon_fetch, wpa_s, NULL))
+ return;
+ /*
+ * We are going through icon fetch, but no icon response was received.
+ * Assume this means the current AP could not provide an answer to avoid
+ * getting stuck in fetch iteration.
+ */
+ hs20_icon_fetch_failed(wpa_s);
+}
+
+
+static void hs20_free_osu_prov_entry(struct osu_provider *prov)
+{
+}
+
+
+void hs20_free_osu_prov(struct wpa_supplicant *wpa_s)
+{
+ size_t i;
+ for (i = 0; i < wpa_s->osu_prov_count; i++)
+ hs20_free_osu_prov_entry(&wpa_s->osu_prov[i]);
+ os_free(wpa_s->osu_prov);
+ wpa_s->osu_prov = NULL;
+ wpa_s->osu_prov_count = 0;
+}
+
+
+static void hs20_osu_fetch_done(struct wpa_supplicant *wpa_s)
+{
+ char fname[256];
+ FILE *f;
+ size_t i, j;
+
+ wpa_s->fetch_osu_info = 0;
+ wpa_s->fetch_osu_icon_in_progress = 0;
+
+ if (wpa_s->conf->osu_dir == NULL) {
+ hs20_free_osu_prov(wpa_s);
+ wpa_s->fetch_anqp_in_progress = 0;
+ return;
+ }
+
+ snprintf(fname, sizeof(fname), "%s/osu-providers.txt",
+ wpa_s->conf->osu_dir);
+ f = fopen(fname, "w");
+ if (f == NULL) {
+ hs20_free_osu_prov(wpa_s);
+ return;
+ }
+
+ hs20_set_osu_access_permission(wpa_s->conf->osu_dir, fname);
+
+ for (i = 0; i < wpa_s->osu_prov_count; i++) {
+ struct osu_provider *osu = &wpa_s->osu_prov[i];
+ if (i > 0)
+ fprintf(f, "\n");
+ fprintf(f, "OSU-PROVIDER " MACSTR "\n"
+ "uri=%s\n"
+ "methods=%08x\n",
+ MAC2STR(osu->bssid), osu->server_uri, osu->osu_methods);
+ if (osu->osu_ssid_len) {
+ fprintf(f, "osu_ssid=%s\n",
+ wpa_ssid_txt(osu->osu_ssid,
+ osu->osu_ssid_len));
+ }
+ if (osu->osu_nai[0])
+ fprintf(f, "osu_nai=%s\n", osu->osu_nai);
+ for (j = 0; j < osu->friendly_name_count; j++) {
+ fprintf(f, "friendly_name=%s:%s\n",
+ osu->friendly_name[j].lang,
+ osu->friendly_name[j].text);
+ }
+ for (j = 0; j < osu->serv_desc_count; j++) {
+ fprintf(f, "desc=%s:%s\n",
+ osu->serv_desc[j].lang,
+ osu->serv_desc[j].text);
+ }
+ for (j = 0; j < osu->icon_count; j++) {
+ struct osu_icon *icon = &osu->icon[j];
+ if (icon->failed)
+ continue; /* could not fetch icon */
+ fprintf(f, "icon=%u:%u:%u:%s:%s:%s\n",
+ icon->id, icon->width, icon->height, icon->lang,
+ icon->icon_type, icon->filename);
+ }
+ }
+ fclose(f);
+ hs20_free_osu_prov(wpa_s);
+
+ wpa_msg(wpa_s, MSG_INFO, "OSU provider fetch completed");
+ wpa_s->fetch_anqp_in_progress = 0;
+}
+
+
+void hs20_next_osu_icon(struct wpa_supplicant *wpa_s)
+{
+ size_t i, j;
+
+ wpa_printf(MSG_DEBUG, "HS 2.0: Ready to fetch next icon");
+
+ for (i = 0; i < wpa_s->osu_prov_count; i++) {
+ struct osu_provider *osu = &wpa_s->osu_prov[i];
+ for (j = 0; j < osu->icon_count; j++) {
+ struct osu_icon *icon = &osu->icon[j];
+ if (icon->id || icon->failed)
+ continue;
+
+ wpa_printf(MSG_DEBUG, "HS 2.0: Try to fetch icon '%s' "
+ "from " MACSTR, icon->filename,
+ MAC2STR(osu->bssid));
+ os_get_reltime(&wpa_s->osu_icon_fetch_start);
+ if (hs20_anqp_send_req(wpa_s, osu->bssid,
+ BIT(HS20_STYPE_ICON_REQUEST),
+ (u8 *) icon->filename,
+ os_strlen(icon->filename)) < 0) {
+ icon->failed = 1;
+ continue;
+ }
+ return;
+ }
+ }
+
+ wpa_printf(MSG_DEBUG, "HS 2.0: No more icons to fetch");
+ hs20_osu_fetch_done(wpa_s);
+}
+
+
+static void hs20_osu_add_prov(struct wpa_supplicant *wpa_s, struct wpa_bss *bss,
+ const u8 *osu_ssid, u8 osu_ssid_len,
+ const u8 *pos, size_t len)
+{
+ struct osu_provider *prov;
+ const u8 *end = pos + len;
+ u16 len2;
+ const u8 *pos2;
+ u8 uri_len, osu_method_len, osu_nai_len;
+
+ wpa_hexdump(MSG_DEBUG, "HS 2.0: Parsing OSU Provider", pos, len);
+ prov = os_realloc_array(wpa_s->osu_prov,
+ wpa_s->osu_prov_count + 1,
+ sizeof(*prov));
+ if (prov == NULL)
+ return;
+ wpa_s->osu_prov = prov;
+ prov = &prov[wpa_s->osu_prov_count];
+ os_memset(prov, 0, sizeof(*prov));
+
+ os_memcpy(prov->bssid, bss->bssid, ETH_ALEN);
+ os_memcpy(prov->osu_ssid, osu_ssid, osu_ssid_len);
+ prov->osu_ssid_len = osu_ssid_len;
+
+ /* OSU Friendly Name Length */
+ if (pos + 2 > end) {
+ wpa_printf(MSG_DEBUG, "HS 2.0: Not enough room for OSU "
+ "Friendly Name Length");
+ return;
+ }
+ len2 = WPA_GET_LE16(pos);
+ pos += 2;
+ if (len2 > end - pos) {
+ wpa_printf(MSG_DEBUG, "HS 2.0: Not enough room for OSU "
+ "Friendly Name Duples");
+ return;
+ }
+ pos2 = pos;
+ pos += len2;
+
+ /* OSU Friendly Name Duples */
+ while (pos2 + 4 <= pos && prov->friendly_name_count < OSU_MAX_ITEMS) {
+ struct osu_lang_string *f;
+ if (pos2 + 1 + pos2[0] > pos || pos2[0] < 3) {
+ wpa_printf(MSG_DEBUG, "Invalid OSU Friendly Name");
+ break;
+ }
+ f = &prov->friendly_name[prov->friendly_name_count++];
+ os_memcpy(f->lang, pos2 + 1, 3);
+ os_memcpy(f->text, pos2 + 1 + 3, pos2[0] - 3);
+ pos2 += 1 + pos2[0];
+ }
+
+ /* OSU Server URI */
+ if (pos + 1 > end) {
+ wpa_printf(MSG_DEBUG,
+ "HS 2.0: Not enough room for OSU Server URI length");
+ return;
+ }
+ uri_len = *pos++;
+ if (uri_len > end - pos) {
+ wpa_printf(MSG_DEBUG, "HS 2.0: Not enough room for OSU Server "
+ "URI");
+ return;
+ }
+ os_memcpy(prov->server_uri, pos, uri_len);
+ pos += uri_len;
+
+ /* OSU Method list */
+ if (pos + 1 > end) {
+ wpa_printf(MSG_DEBUG, "HS 2.0: Not enough room for OSU Method "
+ "list length");
+ return;
+ }
+ osu_method_len = pos[0];
+ if (osu_method_len > end - pos - 1) {
+ wpa_printf(MSG_DEBUG, "HS 2.0: Not enough room for OSU Method "
+ "list");
+ return;
+ }
+ pos2 = pos + 1;
+ pos += 1 + osu_method_len;
+ while (pos2 < pos) {
+ if (*pos2 < 32)
+ prov->osu_methods |= BIT(*pos2);
+ pos2++;
+ }
+
+ /* Icons Available Length */
+ if (pos + 2 > end) {
+ wpa_printf(MSG_DEBUG, "HS 2.0: Not enough room for Icons "
+ "Available Length");
+ return;
+ }
+ len2 = WPA_GET_LE16(pos);
+ pos += 2;
+ if (len2 > end - pos) {
+ wpa_printf(MSG_DEBUG, "HS 2.0: Not enough room for Icons "
+ "Available");
+ return;
+ }
+ pos2 = pos;
+ pos += len2;
+
+ /* Icons Available */
+ while (pos2 < pos) {
+ struct osu_icon *icon = &prov->icon[prov->icon_count];
+ u8 flen;
+
+ if (pos2 + 2 + 2 + 3 + 1 + 1 > pos) {
+ wpa_printf(MSG_DEBUG, "HS 2.0: Invalid Icon Metadata");
+ break;
+ }
+
+ icon->width = WPA_GET_LE16(pos2);
+ pos2 += 2;
+ icon->height = WPA_GET_LE16(pos2);
+ pos2 += 2;
+ os_memcpy(icon->lang, pos2, 3);
+ pos2 += 3;
+
+ flen = pos2[0];
+ if (flen > pos - pos2 - 1) {
+ wpa_printf(MSG_DEBUG, "HS 2.0: Not room for Icon Type");
+ break;
+ }
+ os_memcpy(icon->icon_type, pos2 + 1, flen);
+ pos2 += 1 + flen;
+
+ if (pos2 + 1 > pos) {
+ wpa_printf(MSG_DEBUG, "HS 2.0: Not room for Icon "
+ "Filename length");
+ break;
+ }
+ flen = pos2[0];
+ if (flen > pos - pos2 - 1) {
+ wpa_printf(MSG_DEBUG, "HS 2.0: Not room for Icon "
+ "Filename");
+ break;
+ }
+ os_memcpy(icon->filename, pos2 + 1, flen);
+ pos2 += 1 + flen;
+
+ prov->icon_count++;
+ }
+
+ /* OSU_NAI */
+ if (pos + 1 > end) {
+ wpa_printf(MSG_DEBUG, "HS 2.0: Not enough room for OSU_NAI");
+ return;
+ }
+ osu_nai_len = pos[0];
+ if (osu_nai_len > end - pos - 1) {
+ wpa_printf(MSG_DEBUG, "HS 2.0: Not enough room for OSU_NAI");
+ return;
+ }
+ os_memcpy(prov->osu_nai, pos + 1, osu_nai_len);
+ pos += 1 + osu_nai_len;
+
+ /* OSU Service Description Length */
+ if (pos + 2 > end) {
+ wpa_printf(MSG_DEBUG, "HS 2.0: Not enough room for OSU "
+ "Service Description Length");
+ return;
+ }
+ len2 = WPA_GET_LE16(pos);
+ pos += 2;
+ if (len2 > end - pos) {
+ wpa_printf(MSG_DEBUG, "HS 2.0: Not enough room for OSU "
+ "Service Description Duples");
+ return;
+ }
+ pos2 = pos;
+ pos += len2;
+
+ /* OSU Service Description Duples */
+ while (pos2 + 4 <= pos && prov->serv_desc_count < OSU_MAX_ITEMS) {
+ struct osu_lang_string *f;
+ u8 descr_len;
+
+ descr_len = pos2[0];
+ if (descr_len > pos - pos2 - 1 || descr_len < 3) {
+ wpa_printf(MSG_DEBUG, "Invalid OSU Service "
+ "Description");
+ break;
+ }
+ f = &prov->serv_desc[prov->serv_desc_count++];
+ os_memcpy(f->lang, pos2 + 1, 3);
+ os_memcpy(f->text, pos2 + 1 + 3, descr_len - 3);
+ pos2 += 1 + descr_len;
+ }
+
+ wpa_printf(MSG_DEBUG, "HS 2.0: Added OSU Provider through " MACSTR,
+ MAC2STR(bss->bssid));
+ wpa_s->osu_prov_count++;
+}
+
+
+void hs20_osu_icon_fetch(struct wpa_supplicant *wpa_s)
+{
+ struct wpa_bss *bss;
+ struct wpabuf *prov_anqp;
+ const u8 *pos, *end;
+ u16 len;
+ const u8 *osu_ssid;
+ u8 osu_ssid_len;
+ u8 num_providers;
+
+ hs20_free_osu_prov(wpa_s);
+
+ dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) {
+ if (bss->anqp == NULL)
+ continue;
+ prov_anqp = bss->anqp->hs20_osu_providers_list;
+ if (prov_anqp == NULL)
+ continue;
+ wpa_printf(MSG_DEBUG, "HS 2.0: Parsing OSU Providers list from "
+ MACSTR, MAC2STR(bss->bssid));
+ wpa_hexdump_buf(MSG_DEBUG, "HS 2.0: OSU Providers list",
+ prov_anqp);
+ pos = wpabuf_head(prov_anqp);
+ end = pos + wpabuf_len(prov_anqp);
+
+ /* OSU SSID */
+ if (pos + 1 > end)
+ continue;
+ if (pos + 1 + pos[0] > end) {
+ wpa_printf(MSG_DEBUG, "HS 2.0: Not enough room for "
+ "OSU SSID");
+ continue;
+ }
+ osu_ssid_len = *pos++;
+ if (osu_ssid_len > SSID_MAX_LEN) {
+ wpa_printf(MSG_DEBUG, "HS 2.0: Invalid OSU SSID "
+ "Length %u", osu_ssid_len);
+ continue;
+ }
+ osu_ssid = pos;
+ pos += osu_ssid_len;
+
+ if (pos + 1 > end) {
+ wpa_printf(MSG_DEBUG, "HS 2.0: Not enough room for "
+ "Number of OSU Providers");
+ continue;
+ }
+ num_providers = *pos++;
+ wpa_printf(MSG_DEBUG, "HS 2.0: Number of OSU Providers: %u",
+ num_providers);
+
+ /* OSU Providers */
+ while (pos + 2 < end && num_providers > 0) {
+ num_providers--;
+ len = WPA_GET_LE16(pos);
+ pos += 2;
+ if (len > (unsigned int) (end - pos))
+ break;
+ hs20_osu_add_prov(wpa_s, bss, osu_ssid,
+ osu_ssid_len, pos, len);
+ pos += len;
+ }
+
+ if (pos != end) {
+ wpa_printf(MSG_DEBUG, "HS 2.0: Ignored %d bytes of "
+ "extra data after OSU Providers",
+ (int) (end - pos));
+ }
+ }
+
+ wpa_s->fetch_osu_icon_in_progress = 1;
+ hs20_next_osu_icon(wpa_s);
+}
+
+
+static void hs20_osu_scan_res_handler(struct wpa_supplicant *wpa_s,
+ struct wpa_scan_results *scan_res)
+{
+ wpa_printf(MSG_DEBUG, "OSU provisioning fetch scan completed");
+ if (!wpa_s->fetch_osu_waiting_scan) {
+ wpa_printf(MSG_DEBUG, "OSU fetch have been canceled");
+ return;
+ }
+ wpa_s->network_select = 0;
+ wpa_s->fetch_all_anqp = 1;
+ wpa_s->fetch_osu_info = 1;
+ wpa_s->fetch_osu_icon_in_progress = 0;
+
+ interworking_start_fetch_anqp(wpa_s);
+}
+
+
+int hs20_fetch_osu(struct wpa_supplicant *wpa_s)
+{
+ if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED) {
+ wpa_printf(MSG_DEBUG, "HS 2.0: Cannot start fetch_osu - "
+ "interface disabled");
+ return -1;
+ }
+
+ if (wpa_s->scanning) {
+ wpa_printf(MSG_DEBUG, "HS 2.0: Cannot start fetch_osu - "
+ "scanning");
+ return -1;
+ }
+
+ if (wpa_s->conf->osu_dir == NULL) {
+ wpa_printf(MSG_DEBUG, "HS 2.0: Cannot start fetch_osu - "
+ "osu_dir not configured");
+ return -1;
+ }
+
+ if (wpa_s->fetch_anqp_in_progress || wpa_s->network_select) {
+ wpa_printf(MSG_DEBUG, "HS 2.0: Cannot start fetch_osu - "
+ "fetch in progress (%d, %d)",
+ wpa_s->fetch_anqp_in_progress,
+ wpa_s->network_select);
+ return -1;
+ }
+
+ wpa_msg(wpa_s, MSG_INFO, "Starting OSU provisioning information fetch");
+ wpa_s->num_osu_scans = 0;
+ wpa_s->num_prov_found = 0;
+ hs20_start_osu_scan(wpa_s);
+
+ return 0;
+}
+
+
+void hs20_start_osu_scan(struct wpa_supplicant *wpa_s)
+{
+ wpa_s->fetch_osu_waiting_scan = 1;
+ wpa_s->num_osu_scans++;
+ wpa_s->scan_req = MANUAL_SCAN_REQ;
+ wpa_s->scan_res_handler = hs20_osu_scan_res_handler;
+ wpa_supplicant_req_scan(wpa_s, 0, 0);
+}
+
+
+void hs20_cancel_fetch_osu(struct wpa_supplicant *wpa_s)
+{
+ wpa_printf(MSG_DEBUG, "Cancel OSU fetch");
+ interworking_stop_fetch_anqp(wpa_s);
+ wpa_s->fetch_osu_waiting_scan = 0;
+ wpa_s->network_select = 0;
+ wpa_s->fetch_osu_info = 0;
+ wpa_s->fetch_osu_icon_in_progress = 0;
+}
+
+
+void hs20_icon_fetch_failed(struct wpa_supplicant *wpa_s)
+{
+ hs20_osu_icon_fetch_result(wpa_s, -1);
+ eloop_cancel_timeout(hs20_continue_icon_fetch, wpa_s, NULL);
+ eloop_register_timeout(0, 0, hs20_continue_icon_fetch, wpa_s, NULL);
+}
+
+
+void hs20_rx_subscription_remediation(struct wpa_supplicant *wpa_s,
+ const char *url, u8 osu_method)
+{
+ if (url)
+ wpa_msg(wpa_s, MSG_INFO, HS20_SUBSCRIPTION_REMEDIATION "%u %s",
+ osu_method, url);
+ else
+ wpa_msg(wpa_s, MSG_INFO, HS20_SUBSCRIPTION_REMEDIATION);
+}
+
+
+void hs20_rx_deauth_imminent_notice(struct wpa_supplicant *wpa_s, u8 code,
+ u16 reauth_delay, const char *url)
+{
+ if (!wpa_sm_pmf_enabled(wpa_s->wpa)) {
+ wpa_printf(MSG_DEBUG, "HS 2.0: Ignore deauthentication imminent notice since PMF was not enabled");
+ return;
+ }
+
+ wpa_msg(wpa_s, MSG_INFO, HS20_DEAUTH_IMMINENT_NOTICE "%u %u %s",
+ code, reauth_delay, url);
+
+ if (code == HS20_DEAUTH_REASON_CODE_BSS) {
+ wpa_printf(MSG_DEBUG, "HS 2.0: Add BSS to blacklist");
+ wpa_blacklist_add(wpa_s, wpa_s->bssid);
+ /* TODO: For now, disable full ESS since some drivers may not
+ * support disabling per BSS. */
+ if (wpa_s->current_ssid) {
+ struct os_reltime now;
+ os_get_reltime(&now);
+ if (now.sec + reauth_delay <=
+ wpa_s->current_ssid->disabled_until.sec)
+ return;
+ wpa_printf(MSG_DEBUG, "HS 2.0: Disable network for %u seconds (BSS)",
+ reauth_delay);
+ wpa_s->current_ssid->disabled_until.sec =
+ now.sec + reauth_delay;
+ }
+ }
+
+ if (code == HS20_DEAUTH_REASON_CODE_ESS && wpa_s->current_ssid) {
+ struct os_reltime now;
+ os_get_reltime(&now);
+ if (now.sec + reauth_delay <=
+ wpa_s->current_ssid->disabled_until.sec)
+ return;
+ wpa_printf(MSG_DEBUG, "HS 2.0: Disable network for %u seconds",
+ reauth_delay);
+ wpa_s->current_ssid->disabled_until.sec =
+ now.sec + reauth_delay;
+ }
+}
+
+
+void hs20_deinit(struct wpa_supplicant *wpa_s)
+{
+ eloop_cancel_timeout(hs20_continue_icon_fetch, wpa_s, NULL);
+ hs20_free_osu_prov(wpa_s);
+}
diff --git a/freebsd/contrib/wpa/wpa_supplicant/hs20_supplicant.h b/freebsd/contrib/wpa/wpa_supplicant/hs20_supplicant.h
new file mode 100644
index 00000000..85b51201
--- /dev/null
+++ b/freebsd/contrib/wpa/wpa_supplicant/hs20_supplicant.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2011-2013, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef HS20_SUPPLICANT_H
+#define HS20_SUPPLICANT_H
+
+void wpas_hs20_add_indication(struct wpabuf *buf, int pps_mo_id);
+
+int hs20_anqp_send_req(struct wpa_supplicant *wpa_s, const u8 *dst, u32 stypes,
+ const u8 *payload, size_t payload_len);
+struct wpabuf * hs20_build_anqp_req(u32 stypes, const u8 *payload,
+ size_t payload_len);
+void hs20_put_anqp_req(u32 stypes, const u8 *payload, size_t payload_len,
+ struct wpabuf *buf);
+void hs20_parse_rx_hs20_anqp_resp(struct wpa_supplicant *wpa_s,
+ struct wpa_bss *bss, const u8 *sa,
+ const u8 *data, size_t slen);
+int is_hs20_network(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid,
+ struct wpa_bss *bss);
+int hs20_get_pps_mo_id(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid);
+void hs20_notify_parse_done(struct wpa_supplicant *wpa_s);
+
+void hs20_rx_subscription_remediation(struct wpa_supplicant *wpa_s,
+ const char *url, u8 osu_method);
+void hs20_rx_deauth_imminent_notice(struct wpa_supplicant *wpa_s, u8 code,
+ u16 reauth_delay, const char *url);
+
+void hs20_free_osu_prov(struct wpa_supplicant *wpa_s);
+void hs20_next_osu_icon(struct wpa_supplicant *wpa_s);
+void hs20_osu_icon_fetch(struct wpa_supplicant *wpa_s);
+int hs20_fetch_osu(struct wpa_supplicant *wpa_s);
+void hs20_cancel_fetch_osu(struct wpa_supplicant *wpa_s);
+void hs20_icon_fetch_failed(struct wpa_supplicant *wpa_s);
+void hs20_start_osu_scan(struct wpa_supplicant *wpa_s);
+void hs20_deinit(struct wpa_supplicant *wpa_s);
+
+#endif /* HS20_SUPPLICANT_H */
diff --git a/freebsd/contrib/wpa/wpa_supplicant/ibss_rsn.h b/freebsd/contrib/wpa/wpa_supplicant/ibss_rsn.h
new file mode 100644
index 00000000..67fae2d1
--- /dev/null
+++ b/freebsd/contrib/wpa/wpa_supplicant/ibss_rsn.h
@@ -0,0 +1,64 @@
+/*
+ * wpa_supplicant - IBSS RSN
+ * 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.
+ */
+
+#ifndef IBSS_RSN_H
+#define IBSS_RSN_H
+
+struct ibss_rsn;
+
+/* not authenticated */
+#define IBSS_RSN_AUTH_NOT_AUTHENTICATED 0x00
+/* remote peer sent an EAPOL message */
+#define IBSS_RSN_AUTH_EAPOL_BY_PEER 0x01
+/* we sent an AUTH message with seq 1 */
+#define IBSS_RSN_AUTH_BY_US 0x02
+/* we sent an EAPOL message */
+#define IBSS_RSN_AUTH_EAPOL_BY_US 0x04
+/* PTK derived as supplicant */
+#define IBSS_RSN_SET_PTK_SUPP 0x08
+/* PTK derived as authenticator */
+#define IBSS_RSN_SET_PTK_AUTH 0x10
+/* PTK completion reported */
+#define IBSS_RSN_REPORTED_PTK 0x20
+
+struct ibss_rsn_peer {
+ struct ibss_rsn_peer *next;
+ struct ibss_rsn *ibss_rsn;
+
+ u8 addr[ETH_ALEN];
+
+ struct wpa_sm *supp;
+ enum wpa_states supp_state;
+ u8 supp_ie[80];
+ size_t supp_ie_len;
+
+ struct wpa_state_machine *auth;
+ int authentication_status;
+
+ struct os_reltime own_auth_tx;
+};
+
+struct ibss_rsn {
+ struct wpa_supplicant *wpa_s;
+ struct wpa_authenticator *auth_group;
+ struct ibss_rsn_peer *peers;
+ u8 psk[PMK_LEN];
+};
+
+
+struct ibss_rsn * ibss_rsn_init(struct wpa_supplicant *wpa_s);
+void ibss_rsn_deinit(struct ibss_rsn *ibss_rsn);
+int ibss_rsn_start(struct ibss_rsn *ibss_rsn, const u8 *addr);
+void ibss_rsn_stop(struct ibss_rsn *ibss_rsn, const u8 *peermac);
+int ibss_rsn_rx_eapol(struct ibss_rsn *ibss_rsn, const u8 *src_addr,
+ const u8 *buf, size_t len);
+void ibss_rsn_set_psk(struct ibss_rsn *ibss_rsn, const u8 *psk);
+void ibss_rsn_handle_auth(struct ibss_rsn *ibss_rsn, const u8 *auth_frame,
+ size_t len);
+
+#endif /* IBSS_RSN_H */
diff --git a/freebsd/contrib/wpa/wpa_supplicant/interworking.c b/freebsd/contrib/wpa/wpa_supplicant/interworking.c
new file mode 100644
index 00000000..21875e15
--- /dev/null
+++ b/freebsd/contrib/wpa/wpa_supplicant/interworking.c
@@ -0,0 +1,3059 @@
+#include <machine/rtems-bsd-user-space.h>
+
+/*
+ * Interworking (IEEE 802.11u)
+ * Copyright (c) 2011-2013, Qualcomm Atheros, Inc.
+ * Copyright (c) 2011-2014, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "common/ieee802_11_defs.h"
+#include "common/gas.h"
+#include "common/wpa_ctrl.h"
+#include "utils/pcsc_funcs.h"
+#include "utils/eloop.h"
+#include "drivers/driver.h"
+#include "eap_common/eap_defs.h"
+#include "eap_peer/eap.h"
+#include "eap_peer/eap_methods.h"
+#include "eapol_supp/eapol_supp_sm.h"
+#include "rsn_supp/wpa.h"
+#include "wpa_supplicant_i.h"
+#include "config.h"
+#include "config_ssid.h"
+#include "bss.h"
+#include "scan.h"
+#include "notify.h"
+#include "driver_i.h"
+#include "gas_query.h"
+#include "hs20_supplicant.h"
+#include "interworking.h"
+
+
+#if defined(EAP_SIM) | defined(EAP_SIM_DYNAMIC)
+#define INTERWORKING_3GPP
+#else
+#if defined(EAP_AKA) | defined(EAP_AKA_DYNAMIC)
+#define INTERWORKING_3GPP
+#else
+#if defined(EAP_AKA_PRIME) | defined(EAP_AKA_PRIME_DYNAMIC)
+#define INTERWORKING_3GPP
+#endif
+#endif
+#endif
+
+static void interworking_next_anqp_fetch(struct wpa_supplicant *wpa_s);
+static struct wpa_cred * interworking_credentials_available_realm(
+ struct wpa_supplicant *wpa_s, struct wpa_bss *bss, int ignore_bw,
+ int *excluded);
+static struct wpa_cred * interworking_credentials_available_3gpp(
+ struct wpa_supplicant *wpa_s, struct wpa_bss *bss, int ignore_bw,
+ int *excluded);
+
+
+static int cred_prio_cmp(const struct wpa_cred *a, const struct wpa_cred *b)
+{
+ if (a->priority > b->priority)
+ return 1;
+ if (a->priority < b->priority)
+ return -1;
+ if (a->provisioning_sp == NULL || b->provisioning_sp == NULL ||
+ os_strcmp(a->provisioning_sp, b->provisioning_sp) != 0)
+ return 0;
+ if (a->sp_priority < b->sp_priority)
+ return 1;
+ if (a->sp_priority > b->sp_priority)
+ return -1;
+ return 0;
+}
+
+
+static void interworking_reconnect(struct wpa_supplicant *wpa_s)
+{
+ unsigned int tried;
+
+ if (wpa_s->wpa_state >= WPA_AUTHENTICATING) {
+ wpa_supplicant_cancel_sched_scan(wpa_s);
+ wpa_s->own_disconnect_req = 1;
+ wpa_supplicant_deauthenticate(wpa_s,
+ WLAN_REASON_DEAUTH_LEAVING);
+ }
+ wpa_s->disconnected = 0;
+ wpa_s->reassociate = 1;
+ tried = wpa_s->interworking_fast_assoc_tried;
+ wpa_s->interworking_fast_assoc_tried = 1;
+
+ if (!tried && wpa_supplicant_fast_associate(wpa_s) >= 0)
+ return;
+
+ wpa_s->interworking_fast_assoc_tried = 0;
+ wpa_supplicant_req_scan(wpa_s, 0, 0);
+}
+
+
+static struct wpabuf * anqp_build_req(u16 info_ids[], size_t num_ids,
+ struct wpabuf *extra)
+{
+ struct wpabuf *buf;
+ size_t i;
+ u8 *len_pos;
+
+ buf = gas_anqp_build_initial_req(0, 4 + num_ids * 2 +
+ (extra ? wpabuf_len(extra) : 0));
+ if (buf == NULL)
+ return NULL;
+
+ len_pos = gas_anqp_add_element(buf, ANQP_QUERY_LIST);
+ for (i = 0; i < num_ids; i++)
+ wpabuf_put_le16(buf, info_ids[i]);
+ gas_anqp_set_element_len(buf, len_pos);
+ if (extra)
+ wpabuf_put_buf(buf, extra);
+
+ gas_anqp_set_len(buf);
+
+ return buf;
+}
+
+
+static void interworking_anqp_resp_cb(void *ctx, const u8 *dst,
+ u8 dialog_token,
+ enum gas_query_result result,
+ const struct wpabuf *adv_proto,
+ const struct wpabuf *resp,
+ u16 status_code)
+{
+ struct wpa_supplicant *wpa_s = ctx;
+
+ wpa_printf(MSG_DEBUG, "ANQP: Response callback dst=" MACSTR
+ " dialog_token=%u result=%d status_code=%u",
+ MAC2STR(dst), dialog_token, result, status_code);
+ anqp_resp_cb(wpa_s, dst, dialog_token, result, adv_proto, resp,
+ status_code);
+ interworking_next_anqp_fetch(wpa_s);
+}
+
+
+static int cred_with_roaming_consortium(struct wpa_supplicant *wpa_s)
+{
+ struct wpa_cred *cred;
+
+ for (cred = wpa_s->conf->cred; cred; cred = cred->next) {
+ if (cred->roaming_consortium_len)
+ return 1;
+ if (cred->required_roaming_consortium_len)
+ return 1;
+ }
+ return 0;
+}
+
+
+static int cred_with_3gpp(struct wpa_supplicant *wpa_s)
+{
+ struct wpa_cred *cred;
+
+ for (cred = wpa_s->conf->cred; cred; cred = cred->next) {
+ if (cred->pcsc || cred->imsi)
+ return 1;
+ }
+ return 0;
+}
+
+
+static int cred_with_nai_realm(struct wpa_supplicant *wpa_s)
+{
+ struct wpa_cred *cred;
+
+ for (cred = wpa_s->conf->cred; cred; cred = cred->next) {
+ if (cred->pcsc || cred->imsi)
+ continue;
+ if (!cred->eap_method)
+ return 1;
+ if (cred->realm && cred->roaming_consortium_len == 0)
+ return 1;
+ }
+ return 0;
+}
+
+
+static int cred_with_domain(struct wpa_supplicant *wpa_s)
+{
+ struct wpa_cred *cred;
+
+ for (cred = wpa_s->conf->cred; cred; cred = cred->next) {
+ if (cred->domain || cred->pcsc || cred->imsi ||
+ cred->roaming_partner)
+ return 1;
+ }
+ return 0;
+}
+
+
+#ifdef CONFIG_HS20
+
+static int cred_with_min_backhaul(struct wpa_supplicant *wpa_s)
+{
+ struct wpa_cred *cred;
+
+ for (cred = wpa_s->conf->cred; cred; cred = cred->next) {
+ if (cred->min_dl_bandwidth_home ||
+ cred->min_ul_bandwidth_home ||
+ cred->min_dl_bandwidth_roaming ||
+ cred->min_ul_bandwidth_roaming)
+ return 1;
+ }
+ return 0;
+}
+
+
+static int cred_with_conn_capab(struct wpa_supplicant *wpa_s)
+{
+ struct wpa_cred *cred;
+
+ for (cred = wpa_s->conf->cred; cred; cred = cred->next) {
+ if (cred->num_req_conn_capab)
+ return 1;
+ }
+ return 0;
+}
+
+#endif /* CONFIG_HS20 */
+
+
+static int additional_roaming_consortiums(struct wpa_bss *bss)
+{
+ const u8 *ie;
+ ie = wpa_bss_get_ie(bss, WLAN_EID_ROAMING_CONSORTIUM);
+ if (ie == NULL || ie[1] == 0)
+ return 0;
+ return ie[2]; /* Number of ANQP OIs */
+}
+
+
+static void interworking_continue_anqp(void *eloop_ctx, void *sock_ctx)
+{
+ struct wpa_supplicant *wpa_s = eloop_ctx;
+ interworking_next_anqp_fetch(wpa_s);
+}
+
+
+static int interworking_anqp_send_req(struct wpa_supplicant *wpa_s,
+ struct wpa_bss *bss)
+{
+ struct wpabuf *buf;
+ int ret = 0;
+ int res;
+ u16 info_ids[8];
+ size_t num_info_ids = 0;
+ struct wpabuf *extra = NULL;
+ int all = wpa_s->fetch_all_anqp;
+
+ wpa_msg(wpa_s, MSG_DEBUG, "Interworking: ANQP Query Request to " MACSTR,
+ MAC2STR(bss->bssid));
+ wpa_s->interworking_gas_bss = bss;
+
+ info_ids[num_info_ids++] = ANQP_CAPABILITY_LIST;
+ if (all) {
+ info_ids[num_info_ids++] = ANQP_VENUE_NAME;
+ info_ids[num_info_ids++] = ANQP_NETWORK_AUTH_TYPE;
+ }
+ if (all || (cred_with_roaming_consortium(wpa_s) &&
+ additional_roaming_consortiums(bss)))
+ info_ids[num_info_ids++] = ANQP_ROAMING_CONSORTIUM;
+ if (all)
+ info_ids[num_info_ids++] = ANQP_IP_ADDR_TYPE_AVAILABILITY;
+ if (all || cred_with_nai_realm(wpa_s))
+ info_ids[num_info_ids++] = ANQP_NAI_REALM;
+ if (all || cred_with_3gpp(wpa_s)) {
+ info_ids[num_info_ids++] = ANQP_3GPP_CELLULAR_NETWORK;
+ wpa_supplicant_scard_init(wpa_s, NULL);
+ }
+ if (all || cred_with_domain(wpa_s))
+ info_ids[num_info_ids++] = ANQP_DOMAIN_NAME;
+ wpa_hexdump(MSG_DEBUG, "Interworking: ANQP Query info",
+ (u8 *) info_ids, num_info_ids * 2);
+
+#ifdef CONFIG_HS20
+ if (wpa_bss_get_vendor_ie(bss, HS20_IE_VENDOR_TYPE)) {
+ u8 *len_pos;
+
+ extra = wpabuf_alloc(100);
+ if (!extra)
+ return -1;
+
+ len_pos = gas_anqp_add_element(extra, ANQP_VENDOR_SPECIFIC);
+ wpabuf_put_be24(extra, OUI_WFA);
+ wpabuf_put_u8(extra, HS20_ANQP_OUI_TYPE);
+ wpabuf_put_u8(extra, HS20_STYPE_QUERY_LIST);
+ wpabuf_put_u8(extra, 0); /* Reserved */
+ wpabuf_put_u8(extra, HS20_STYPE_CAPABILITY_LIST);
+ if (all)
+ wpabuf_put_u8(extra,
+ HS20_STYPE_OPERATOR_FRIENDLY_NAME);
+ if (all || cred_with_min_backhaul(wpa_s))
+ wpabuf_put_u8(extra, HS20_STYPE_WAN_METRICS);
+ if (all || cred_with_conn_capab(wpa_s))
+ wpabuf_put_u8(extra, HS20_STYPE_CONNECTION_CAPABILITY);
+ if (all)
+ wpabuf_put_u8(extra, HS20_STYPE_OPERATING_CLASS);
+ if (all)
+ wpabuf_put_u8(extra, HS20_STYPE_OSU_PROVIDERS_LIST);
+ gas_anqp_set_element_len(extra, len_pos);
+ }
+#endif /* CONFIG_HS20 */
+
+ buf = anqp_build_req(info_ids, num_info_ids, extra);
+ wpabuf_free(extra);
+ if (buf == NULL)
+ return -1;
+
+ res = gas_query_req(wpa_s->gas, bss->bssid, bss->freq, buf,
+ interworking_anqp_resp_cb, wpa_s);
+ if (res < 0) {
+ wpa_msg(wpa_s, MSG_DEBUG, "ANQP: Failed to send Query Request");
+ wpabuf_free(buf);
+ ret = -1;
+ eloop_register_timeout(0, 0, interworking_continue_anqp, wpa_s,
+ NULL);
+ } else
+ wpa_msg(wpa_s, MSG_DEBUG,
+ "ANQP: Query started with dialog token %u", res);
+
+ return ret;
+}
+
+
+struct nai_realm_eap {
+ u8 method;
+ u8 inner_method;
+ enum nai_realm_eap_auth_inner_non_eap inner_non_eap;
+ u8 cred_type;
+ u8 tunneled_cred_type;
+};
+
+struct nai_realm {
+ u8 encoding;
+ char *realm;
+ u8 eap_count;
+ struct nai_realm_eap *eap;
+};
+
+
+static void nai_realm_free(struct nai_realm *realms, u16 count)
+{
+ u16 i;
+
+ if (realms == NULL)
+ return;
+ for (i = 0; i < count; i++) {
+ os_free(realms[i].eap);
+ os_free(realms[i].realm);
+ }
+ os_free(realms);
+}
+
+
+static const u8 * nai_realm_parse_eap(struct nai_realm_eap *e, const u8 *pos,
+ const u8 *end)
+{
+ u8 elen, auth_count, a;
+ const u8 *e_end;
+
+ if (pos + 3 > end) {
+ wpa_printf(MSG_DEBUG, "No room for EAP Method fixed fields");
+ return NULL;
+ }
+
+ elen = *pos++;
+ if (pos + elen > end || elen < 2) {
+ wpa_printf(MSG_DEBUG, "No room for EAP Method subfield");
+ return NULL;
+ }
+ e_end = pos + elen;
+ e->method = *pos++;
+ auth_count = *pos++;
+ wpa_printf(MSG_DEBUG, "EAP Method: len=%u method=%u auth_count=%u",
+ elen, e->method, auth_count);
+
+ for (a = 0; a < auth_count; a++) {
+ u8 id, len;
+
+ if (pos + 2 > end || pos + 2 + pos[1] > end) {
+ wpa_printf(MSG_DEBUG, "No room for Authentication "
+ "Parameter subfield");
+ return NULL;
+ }
+
+ id = *pos++;
+ len = *pos++;
+
+ switch (id) {
+ case NAI_REALM_EAP_AUTH_NON_EAP_INNER_AUTH:
+ if (len < 1)
+ break;
+ e->inner_non_eap = *pos;
+ if (e->method != EAP_TYPE_TTLS)
+ break;
+ switch (*pos) {
+ case NAI_REALM_INNER_NON_EAP_PAP:
+ wpa_printf(MSG_DEBUG, "EAP-TTLS/PAP");
+ break;
+ case NAI_REALM_INNER_NON_EAP_CHAP:
+ wpa_printf(MSG_DEBUG, "EAP-TTLS/CHAP");
+ break;
+ case NAI_REALM_INNER_NON_EAP_MSCHAP:
+ wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAP");
+ break;
+ case NAI_REALM_INNER_NON_EAP_MSCHAPV2:
+ wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAPV2");
+ break;
+ }
+ break;
+ case NAI_REALM_EAP_AUTH_INNER_AUTH_EAP_METHOD:
+ if (len < 1)
+ break;
+ e->inner_method = *pos;
+ wpa_printf(MSG_DEBUG, "Inner EAP method: %u",
+ e->inner_method);
+ break;
+ case NAI_REALM_EAP_AUTH_CRED_TYPE:
+ if (len < 1)
+ break;
+ e->cred_type = *pos;
+ wpa_printf(MSG_DEBUG, "Credential Type: %u",
+ e->cred_type);
+ break;
+ case NAI_REALM_EAP_AUTH_TUNNELED_CRED_TYPE:
+ if (len < 1)
+ break;
+ e->tunneled_cred_type = *pos;
+ wpa_printf(MSG_DEBUG, "Tunneled EAP Method Credential "
+ "Type: %u", e->tunneled_cred_type);
+ break;
+ default:
+ wpa_printf(MSG_DEBUG, "Unsupported Authentication "
+ "Parameter: id=%u len=%u", id, len);
+ wpa_hexdump(MSG_DEBUG, "Authentication Parameter "
+ "Value", pos, len);
+ break;
+ }
+
+ pos += len;
+ }
+
+ return e_end;
+}
+
+
+static const u8 * nai_realm_parse_realm(struct nai_realm *r, const u8 *pos,
+ const u8 *end)
+{
+ u16 len;
+ const u8 *f_end;
+ u8 realm_len, e;
+
+ if (end - pos < 4) {
+ wpa_printf(MSG_DEBUG, "No room for NAI Realm Data "
+ "fixed fields");
+ return NULL;
+ }
+
+ len = WPA_GET_LE16(pos); /* NAI Realm Data field Length */
+ pos += 2;
+ if (pos + len > end || len < 3) {
+ wpa_printf(MSG_DEBUG, "No room for NAI Realm Data "
+ "(len=%u; left=%u)",
+ len, (unsigned int) (end - pos));
+ return NULL;
+ }
+ f_end = pos + len;
+
+ r->encoding = *pos++;
+ realm_len = *pos++;
+ if (pos + realm_len > f_end) {
+ wpa_printf(MSG_DEBUG, "No room for NAI Realm "
+ "(len=%u; left=%u)",
+ realm_len, (unsigned int) (f_end - pos));
+ return NULL;
+ }
+ wpa_hexdump_ascii(MSG_DEBUG, "NAI Realm", pos, realm_len);
+ r->realm = dup_binstr(pos, realm_len);
+ if (r->realm == NULL)
+ return NULL;
+ pos += realm_len;
+
+ if (pos + 1 > f_end) {
+ wpa_printf(MSG_DEBUG, "No room for EAP Method Count");
+ return NULL;
+ }
+ r->eap_count = *pos++;
+ wpa_printf(MSG_DEBUG, "EAP Count: %u", r->eap_count);
+ if (pos + r->eap_count * 3 > f_end) {
+ wpa_printf(MSG_DEBUG, "No room for EAP Methods");
+ return NULL;
+ }
+ r->eap = os_calloc(r->eap_count, sizeof(struct nai_realm_eap));
+ if (r->eap == NULL)
+ return NULL;
+
+ for (e = 0; e < r->eap_count; e++) {
+ pos = nai_realm_parse_eap(&r->eap[e], pos, f_end);
+ if (pos == NULL)
+ return NULL;
+ }
+
+ return f_end;
+}
+
+
+static struct nai_realm * nai_realm_parse(struct wpabuf *anqp, u16 *count)
+{
+ struct nai_realm *realm;
+ const u8 *pos, *end;
+ u16 i, num;
+ size_t left;
+
+ if (anqp == NULL)
+ return NULL;
+ left = wpabuf_len(anqp);
+ if (left < 2)
+ return NULL;
+
+ pos = wpabuf_head_u8(anqp);
+ end = pos + left;
+ num = WPA_GET_LE16(pos);
+ wpa_printf(MSG_DEBUG, "NAI Realm Count: %u", num);
+ pos += 2;
+ left -= 2;
+
+ if (num > left / 5) {
+ wpa_printf(MSG_DEBUG, "Invalid NAI Realm Count %u - not "
+ "enough data (%u octets) for that many realms",
+ num, (unsigned int) left);
+ return NULL;
+ }
+
+ realm = os_calloc(num, sizeof(struct nai_realm));
+ if (realm == NULL)
+ return NULL;
+
+ for (i = 0; i < num; i++) {
+ pos = nai_realm_parse_realm(&realm[i], pos, end);
+ if (pos == NULL) {
+ nai_realm_free(realm, num);
+ return NULL;
+ }
+ }
+
+ *count = num;
+ return realm;
+}
+
+
+static int nai_realm_match(struct nai_realm *realm, const char *home_realm)
+{
+ char *tmp, *pos, *end;
+ int match = 0;
+
+ if (realm->realm == NULL || home_realm == NULL)
+ return 0;
+
+ if (os_strchr(realm->realm, ';') == NULL)
+ return os_strcasecmp(realm->realm, home_realm) == 0;
+
+ tmp = os_strdup(realm->realm);
+ if (tmp == NULL)
+ return 0;
+
+ pos = tmp;
+ while (*pos) {
+ end = os_strchr(pos, ';');
+ if (end)
+ *end = '\0';
+ if (os_strcasecmp(pos, home_realm) == 0) {
+ match = 1;
+ break;
+ }
+ if (end == NULL)
+ break;
+ pos = end + 1;
+ }
+
+ os_free(tmp);
+
+ return match;
+}
+
+
+static int nai_realm_cred_username(struct wpa_supplicant *wpa_s,
+ struct nai_realm_eap *eap)
+{
+ if (eap_get_name(EAP_VENDOR_IETF, eap->method) == NULL) {
+ wpa_msg(wpa_s, MSG_DEBUG,
+ "nai-realm-cred-username: EAP method not supported: %d",
+ eap->method);
+ return 0; /* method not supported */
+ }
+
+ if (eap->method != EAP_TYPE_TTLS && eap->method != EAP_TYPE_PEAP &&
+ eap->method != EAP_TYPE_FAST) {
+ /* Only tunneled methods with username/password supported */
+ wpa_msg(wpa_s, MSG_DEBUG,
+ "nai-realm-cred-username: Method: %d is not TTLS, PEAP, or FAST",
+ eap->method);
+ return 0;
+ }
+
+ if (eap->method == EAP_TYPE_PEAP || eap->method == EAP_TYPE_FAST) {
+ if (eap->inner_method &&
+ eap_get_name(EAP_VENDOR_IETF, eap->inner_method) == NULL) {
+ wpa_msg(wpa_s, MSG_DEBUG,
+ "nai-realm-cred-username: PEAP/FAST: Inner method not supported: %d",
+ eap->inner_method);
+ return 0;
+ }
+ if (!eap->inner_method &&
+ eap_get_name(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2) == NULL) {
+ wpa_msg(wpa_s, MSG_DEBUG,
+ "nai-realm-cred-username: MSCHAPv2 not supported");
+ return 0;
+ }
+ }
+
+ if (eap->method == EAP_TYPE_TTLS) {
+ if (eap->inner_method == 0 && eap->inner_non_eap == 0)
+ return 1; /* Assume TTLS/MSCHAPv2 is used */
+ if (eap->inner_method &&
+ eap_get_name(EAP_VENDOR_IETF, eap->inner_method) == NULL) {
+ wpa_msg(wpa_s, MSG_DEBUG,
+ "nai-realm-cred-username: TTLS, but inner not supported: %d",
+ eap->inner_method);
+ return 0;
+ }
+ if (eap->inner_non_eap &&
+ eap->inner_non_eap != NAI_REALM_INNER_NON_EAP_PAP &&
+ eap->inner_non_eap != NAI_REALM_INNER_NON_EAP_CHAP &&
+ eap->inner_non_eap != NAI_REALM_INNER_NON_EAP_MSCHAP &&
+ eap->inner_non_eap != NAI_REALM_INNER_NON_EAP_MSCHAPV2) {
+ wpa_msg(wpa_s, MSG_DEBUG,
+ "nai-realm-cred-username: TTLS, inner-non-eap not supported: %d",
+ eap->inner_non_eap);
+ return 0;
+ }
+ }
+
+ if (eap->inner_method &&
+ eap->inner_method != EAP_TYPE_GTC &&
+ eap->inner_method != EAP_TYPE_MSCHAPV2) {
+ wpa_msg(wpa_s, MSG_DEBUG,
+ "nai-realm-cred-username: inner-method not GTC or MSCHAPv2: %d",
+ eap->inner_method);
+ return 0;
+ }
+
+ return 1;
+}
+
+
+static int nai_realm_cred_cert(struct wpa_supplicant *wpa_s,
+ struct nai_realm_eap *eap)
+{
+ if (eap_get_name(EAP_VENDOR_IETF, eap->method) == NULL) {
+ wpa_msg(wpa_s, MSG_DEBUG,
+ "nai-realm-cred-cert: Method not supported: %d",
+ eap->method);
+ return 0; /* method not supported */
+ }
+
+ if (eap->method != EAP_TYPE_TLS) {
+ /* Only EAP-TLS supported for credential authentication */
+ wpa_msg(wpa_s, MSG_DEBUG,
+ "nai-realm-cred-cert: Method not TLS: %d",
+ eap->method);
+ return 0;
+ }
+
+ return 1;
+}
+
+
+static struct nai_realm_eap * nai_realm_find_eap(struct wpa_supplicant *wpa_s,
+ struct wpa_cred *cred,
+ struct nai_realm *realm)
+{
+ u8 e;
+
+ if (cred->username == NULL ||
+ cred->username[0] == '\0' ||
+ ((cred->password == NULL ||
+ cred->password[0] == '\0') &&
+ (cred->private_key == NULL ||
+ cred->private_key[0] == '\0'))) {
+ wpa_msg(wpa_s, MSG_DEBUG,
+ "nai-realm-find-eap: incomplete cred info: username: %s password: %s private_key: %s",
+ cred->username ? cred->username : "NULL",
+ cred->password ? cred->password : "NULL",
+ cred->private_key ? cred->private_key : "NULL");
+ return NULL;
+ }
+
+ for (e = 0; e < realm->eap_count; e++) {
+ struct nai_realm_eap *eap = &realm->eap[e];
+ if (cred->password && cred->password[0] &&
+ nai_realm_cred_username(wpa_s, eap))
+ return eap;
+ if (cred->private_key && cred->private_key[0] &&
+ nai_realm_cred_cert(wpa_s, eap))
+ return eap;
+ }
+
+ return NULL;
+}
+
+
+#ifdef INTERWORKING_3GPP
+
+static int plmn_id_match(struct wpabuf *anqp, const char *imsi, int mnc_len)
+{
+ u8 plmn[3], plmn2[3];
+ const u8 *pos, *end;
+ u8 udhl;
+
+ /*
+ * See Annex A of 3GPP TS 24.234 v8.1.0 for description. The network
+ * operator is allowed to include only two digits of the MNC, so allow
+ * matches based on both two and three digit MNC assumptions. Since some
+ * SIM/USIM cards may not expose MNC length conveniently, we may be
+ * provided the default MNC length 3 here and as such, checking with MNC
+ * length 2 is justifiable even though 3GPP TS 24.234 does not mention
+ * that case. Anyway, MCC/MNC pair where both 2 and 3 digit MNC is used
+ * with otherwise matching values would not be good idea in general, so
+ * this should not result in selecting incorrect networks.
+ */
+ /* Match with 3 digit MNC */
+ plmn[0] = (imsi[0] - '0') | ((imsi[1] - '0') << 4);
+ plmn[1] = (imsi[2] - '0') | ((imsi[5] - '0') << 4);
+ plmn[2] = (imsi[3] - '0') | ((imsi[4] - '0') << 4);
+ /* Match with 2 digit MNC */
+ plmn2[0] = (imsi[0] - '0') | ((imsi[1] - '0') << 4);
+ plmn2[1] = (imsi[2] - '0') | 0xf0;
+ plmn2[2] = (imsi[3] - '0') | ((imsi[4] - '0') << 4);
+
+ if (anqp == NULL)
+ return 0;
+ pos = wpabuf_head_u8(anqp);
+ end = pos + wpabuf_len(anqp);
+ if (pos + 2 > end)
+ return 0;
+ if (*pos != 0) {
+ wpa_printf(MSG_DEBUG, "Unsupported GUD version 0x%x", *pos);
+ return 0;
+ }
+ pos++;
+ udhl = *pos++;
+ if (pos + udhl > end) {
+ wpa_printf(MSG_DEBUG, "Invalid UDHL");
+ return 0;
+ }
+ end = pos + udhl;
+
+ wpa_printf(MSG_DEBUG, "Interworking: Matching against MCC/MNC alternatives: %02x:%02x:%02x or %02x:%02x:%02x (IMSI %s, MNC length %d)",
+ plmn[0], plmn[1], plmn[2], plmn2[0], plmn2[1], plmn2[2],
+ imsi, mnc_len);
+
+ while (pos + 2 <= end) {
+ u8 iei, len;
+ const u8 *l_end;
+ iei = *pos++;
+ len = *pos++ & 0x7f;
+ if (pos + len > end)
+ break;
+ l_end = pos + len;
+
+ if (iei == 0 && len > 0) {
+ /* PLMN List */
+ u8 num, i;
+ wpa_hexdump(MSG_DEBUG, "Interworking: PLMN List information element",
+ pos, len);
+ num = *pos++;
+ for (i = 0; i < num; i++) {
+ if (pos + 3 > l_end)
+ break;
+ if (os_memcmp(pos, plmn, 3) == 0 ||
+ os_memcmp(pos, plmn2, 3) == 0)
+ return 1; /* Found matching PLMN */
+ pos += 3;
+ }
+ } else {
+ wpa_hexdump(MSG_DEBUG, "Interworking: Unrecognized 3GPP information element",
+ pos, len);
+ }
+
+ pos = l_end;
+ }
+
+ return 0;
+}
+
+
+static int build_root_nai(char *nai, size_t nai_len, const char *imsi,
+ size_t mnc_len, char prefix)
+{
+ const char *sep, *msin;
+ char *end, *pos;
+ size_t msin_len, plmn_len;
+
+ /*
+ * TS 23.003, Clause 14 (3GPP to WLAN Interworking)
+ * Root NAI:
+ * <aka:0|sim:1><IMSI>@wlan.mnc<MNC>.mcc<MCC>.3gppnetwork.org
+ * <MNC> is zero-padded to three digits in case two-digit MNC is used
+ */
+
+ if (imsi == NULL || os_strlen(imsi) > 16) {
+ wpa_printf(MSG_DEBUG, "No valid IMSI available");
+ return -1;
+ }
+ sep = os_strchr(imsi, '-');
+ if (sep) {
+ plmn_len = sep - imsi;
+ msin = sep + 1;
+ } else if (mnc_len && os_strlen(imsi) >= 3 + mnc_len) {
+ plmn_len = 3 + mnc_len;
+ msin = imsi + plmn_len;
+ } else
+ return -1;
+ if (plmn_len != 5 && plmn_len != 6)
+ return -1;
+ msin_len = os_strlen(msin);
+
+ pos = nai;
+ end = nai + nai_len;
+ if (prefix)
+ *pos++ = prefix;
+ os_memcpy(pos, imsi, plmn_len);
+ pos += plmn_len;
+ os_memcpy(pos, msin, msin_len);
+ pos += msin_len;
+ pos += os_snprintf(pos, end - pos, "@wlan.mnc");
+ if (plmn_len == 5) {
+ *pos++ = '0';
+ *pos++ = imsi[3];
+ *pos++ = imsi[4];
+ } else {
+ *pos++ = imsi[3];
+ *pos++ = imsi[4];
+ *pos++ = imsi[5];
+ }
+ os_snprintf(pos, end - pos, ".mcc%c%c%c.3gppnetwork.org",
+ imsi[0], imsi[1], imsi[2]);
+
+ return 0;
+}
+
+
+static int set_root_nai(struct wpa_ssid *ssid, const char *imsi, char prefix)
+{
+ char nai[100];
+ if (build_root_nai(nai, sizeof(nai), imsi, 0, prefix) < 0)
+ return -1;
+ return wpa_config_set_quoted(ssid, "identity", nai);
+}
+
+#endif /* INTERWORKING_3GPP */
+
+
+static int already_connected(struct wpa_supplicant *wpa_s,
+ struct wpa_cred *cred, struct wpa_bss *bss)
+{
+ struct wpa_ssid *ssid, *sel_ssid;
+ struct wpa_bss *selected;
+
+ if (wpa_s->wpa_state < WPA_ASSOCIATED || wpa_s->current_ssid == NULL)
+ return 0;
+
+ ssid = wpa_s->current_ssid;
+ if (ssid->parent_cred != cred)
+ return 0;
+
+ if (ssid->ssid_len != bss->ssid_len ||
+ os_memcmp(ssid->ssid, bss->ssid, bss->ssid_len) != 0)
+ return 0;
+
+ sel_ssid = NULL;
+ selected = wpa_supplicant_pick_network(wpa_s, &sel_ssid);
+ if (selected && sel_ssid && sel_ssid->priority > ssid->priority)
+ return 0; /* higher priority network in scan results */
+
+ return 1;
+}
+
+
+static void remove_duplicate_network(struct wpa_supplicant *wpa_s,
+ struct wpa_cred *cred,
+ struct wpa_bss *bss)
+{
+ struct wpa_ssid *ssid;
+
+ for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next) {
+ if (ssid->parent_cred != cred)
+ continue;
+ if (ssid->ssid_len != bss->ssid_len ||
+ os_memcmp(ssid->ssid, bss->ssid, bss->ssid_len) != 0)
+ continue;
+
+ break;
+ }
+
+ if (ssid == NULL)
+ return;
+
+ wpa_printf(MSG_DEBUG, "Interworking: Remove duplicate network entry for the same credential");
+
+ if (ssid == wpa_s->current_ssid) {
+ wpa_sm_set_config(wpa_s->wpa, NULL);
+ eapol_sm_notify_config(wpa_s->eapol, NULL, NULL);
+ wpa_s->own_disconnect_req = 1;
+ wpa_supplicant_deauthenticate(wpa_s,
+ WLAN_REASON_DEAUTH_LEAVING);
+ }
+
+ wpas_notify_network_removed(wpa_s, ssid);
+ wpa_config_remove_network(wpa_s->conf, ssid->id);
+}
+
+
+static int interworking_set_hs20_params(struct wpa_supplicant *wpa_s,
+ struct wpa_ssid *ssid)
+{
+ const char *key_mgmt = NULL;
+#ifdef CONFIG_IEEE80211R
+ int res;
+ struct wpa_driver_capa capa;
+
+ res = wpa_drv_get_capa(wpa_s, &capa);
+ if (res == 0 && capa.key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_FT) {
+ key_mgmt = wpa_s->conf->pmf != NO_MGMT_FRAME_PROTECTION ?
+ "WPA-EAP WPA-EAP-SHA256 FT-EAP" :
+ "WPA-EAP FT-EAP";
+ }
+#endif /* CONFIG_IEEE80211R */
+
+ if (!key_mgmt)
+ key_mgmt = wpa_s->conf->pmf != NO_MGMT_FRAME_PROTECTION ?
+ "WPA-EAP WPA-EAP-SHA256" : "WPA-EAP";
+ if (wpa_config_set(ssid, "key_mgmt", key_mgmt, 0) < 0)
+ return -1;
+ if (wpa_config_set(ssid, "proto", "RSN", 0) < 0)
+ return -1;
+ if (wpa_config_set(ssid, "pairwise", "CCMP", 0) < 0)
+ return -1;
+ return 0;
+}
+
+
+static int interworking_connect_3gpp(struct wpa_supplicant *wpa_s,
+ struct wpa_cred *cred,
+ struct wpa_bss *bss, int only_add)
+{
+#ifdef INTERWORKING_3GPP
+ struct wpa_ssid *ssid;
+ int eap_type;
+ int res;
+ char prefix;
+
+ if (bss->anqp == NULL || bss->anqp->anqp_3gpp == NULL)
+ return -1;
+
+ wpa_msg(wpa_s, MSG_DEBUG, "Interworking: Connect with " MACSTR
+ " (3GPP)", MAC2STR(bss->bssid));
+
+ if (already_connected(wpa_s, cred, bss)) {
+ wpa_msg(wpa_s, MSG_INFO, INTERWORKING_ALREADY_CONNECTED MACSTR,
+ MAC2STR(bss->bssid));
+ return wpa_s->current_ssid->id;
+ }
+
+ remove_duplicate_network(wpa_s, cred, bss);
+
+ ssid = wpa_config_add_network(wpa_s->conf);
+ if (ssid == NULL)
+ return -1;
+ ssid->parent_cred = cred;
+
+ wpas_notify_network_added(wpa_s, ssid);
+ wpa_config_set_network_defaults(ssid);
+ ssid->priority = cred->priority;
+ ssid->temporary = 1;
+ ssid->ssid = os_zalloc(bss->ssid_len + 1);
+ if (ssid->ssid == NULL)
+ goto fail;
+ os_memcpy(ssid->ssid, bss->ssid, bss->ssid_len);
+ ssid->ssid_len = bss->ssid_len;
+ ssid->eap.sim_num = cred->sim_num;
+
+ if (interworking_set_hs20_params(wpa_s, ssid) < 0)
+ goto fail;
+
+ eap_type = EAP_TYPE_SIM;
+ if (cred->pcsc && wpa_s->scard && scard_supports_umts(wpa_s->scard))
+ eap_type = EAP_TYPE_AKA;
+ if (cred->eap_method && cred->eap_method[0].vendor == EAP_VENDOR_IETF) {
+ if (cred->eap_method[0].method == EAP_TYPE_SIM ||
+ cred->eap_method[0].method == EAP_TYPE_AKA ||
+ cred->eap_method[0].method == EAP_TYPE_AKA_PRIME)
+ eap_type = cred->eap_method[0].method;
+ }
+
+ switch (eap_type) {
+ case EAP_TYPE_SIM:
+ prefix = '1';
+ res = wpa_config_set(ssid, "eap", "SIM", 0);
+ break;
+ case EAP_TYPE_AKA:
+ prefix = '0';
+ res = wpa_config_set(ssid, "eap", "AKA", 0);
+ break;
+ case EAP_TYPE_AKA_PRIME:
+ prefix = '6';
+ res = wpa_config_set(ssid, "eap", "AKA'", 0);
+ break;
+ default:
+ res = -1;
+ break;
+ }
+ if (res < 0) {
+ wpa_msg(wpa_s, MSG_DEBUG,
+ "Selected EAP method (%d) not supported", eap_type);
+ goto fail;
+ }
+
+ if (!cred->pcsc && set_root_nai(ssid, cred->imsi, prefix) < 0) {
+ wpa_msg(wpa_s, MSG_DEBUG, "Failed to set Root NAI");
+ goto fail;
+ }
+
+ if (cred->milenage && cred->milenage[0]) {
+ if (wpa_config_set_quoted(ssid, "password",
+ cred->milenage) < 0)
+ goto fail;
+ } else if (cred->pcsc) {
+ if (wpa_config_set_quoted(ssid, "pcsc", "") < 0)
+ goto fail;
+ if (wpa_s->conf->pcsc_pin &&
+ wpa_config_set_quoted(ssid, "pin", wpa_s->conf->pcsc_pin)
+ < 0)
+ goto fail;
+ }
+
+ wpa_s->next_ssid = ssid;
+ wpa_config_update_prio_list(wpa_s->conf);
+ if (!only_add)
+ interworking_reconnect(wpa_s);
+
+ return ssid->id;
+
+fail:
+ wpas_notify_network_removed(wpa_s, ssid);
+ wpa_config_remove_network(wpa_s->conf, ssid->id);
+#endif /* INTERWORKING_3GPP */
+ return -1;
+}
+
+
+static int roaming_consortium_element_match(const u8 *ie, const u8 *rc_id,
+ size_t rc_len)
+{
+ const u8 *pos, *end;
+ u8 lens;
+
+ if (ie == NULL)
+ return 0;
+
+ pos = ie + 2;
+ end = ie + 2 + ie[1];
+
+ /* Roaming Consortium element:
+ * Number of ANQP OIs
+ * OI #1 and #2 lengths
+ * OI #1, [OI #2], [OI #3]
+ */
+
+ if (pos + 2 > end)
+ return 0;
+
+ pos++; /* skip Number of ANQP OIs */
+ lens = *pos++;
+ if (pos + (lens & 0x0f) + (lens >> 4) > end)
+ return 0;
+
+ if ((lens & 0x0f) == rc_len && os_memcmp(pos, rc_id, rc_len) == 0)
+ return 1;
+ pos += lens & 0x0f;
+
+ if ((lens >> 4) == rc_len && os_memcmp(pos, rc_id, rc_len) == 0)
+ return 1;
+ pos += lens >> 4;
+
+ if (pos < end && (size_t) (end - pos) == rc_len &&
+ os_memcmp(pos, rc_id, rc_len) == 0)
+ return 1;
+
+ return 0;
+}
+
+
+static int roaming_consortium_anqp_match(const struct wpabuf *anqp,
+ const u8 *rc_id, size_t rc_len)
+{
+ const u8 *pos, *end;
+ u8 len;
+
+ if (anqp == NULL)
+ return 0;
+
+ pos = wpabuf_head(anqp);
+ end = pos + wpabuf_len(anqp);
+
+ /* Set of <OI Length, OI> duples */
+ while (pos < end) {
+ len = *pos++;
+ if (pos + len > end)
+ break;
+ if (len == rc_len && os_memcmp(pos, rc_id, rc_len) == 0)
+ return 1;
+ pos += len;
+ }
+
+ return 0;
+}
+
+
+static int roaming_consortium_match(const u8 *ie, const struct wpabuf *anqp,
+ const u8 *rc_id, size_t rc_len)
+{
+ return roaming_consortium_element_match(ie, rc_id, rc_len) ||
+ roaming_consortium_anqp_match(anqp, rc_id, rc_len);
+}
+
+
+static int cred_no_required_oi_match(struct wpa_cred *cred, struct wpa_bss *bss)
+{
+ const u8 *ie;
+
+ if (cred->required_roaming_consortium_len == 0)
+ return 0;
+
+ ie = wpa_bss_get_ie(bss, WLAN_EID_ROAMING_CONSORTIUM);
+
+ if (ie == NULL &&
+ (bss->anqp == NULL || bss->anqp->roaming_consortium == NULL))
+ return 1;
+
+ return !roaming_consortium_match(ie,
+ bss->anqp ?
+ bss->anqp->roaming_consortium : NULL,
+ cred->required_roaming_consortium,
+ cred->required_roaming_consortium_len);
+}
+
+
+static int cred_excluded_ssid(struct wpa_cred *cred, struct wpa_bss *bss)
+{
+ size_t i;
+
+ if (!cred->excluded_ssid)
+ return 0;
+
+ for (i = 0; i < cred->num_excluded_ssid; i++) {
+ struct excluded_ssid *e = &cred->excluded_ssid[i];
+ if (bss->ssid_len == e->ssid_len &&
+ os_memcmp(bss->ssid, e->ssid, e->ssid_len) == 0)
+ return 1;
+ }
+
+ return 0;
+}
+
+
+static int cred_below_min_backhaul(struct wpa_supplicant *wpa_s,
+ struct wpa_cred *cred, struct wpa_bss *bss)
+{
+ int res;
+ unsigned int dl_bandwidth, ul_bandwidth;
+ const u8 *wan;
+ u8 wan_info, dl_load, ul_load;
+ u16 lmd;
+ u32 ul_speed, dl_speed;
+
+ if (!cred->min_dl_bandwidth_home &&
+ !cred->min_ul_bandwidth_home &&
+ !cred->min_dl_bandwidth_roaming &&
+ !cred->min_ul_bandwidth_roaming)
+ return 0; /* No bandwidth constraint specified */
+
+ if (bss->anqp == NULL || bss->anqp->hs20_wan_metrics == NULL)
+ return 0; /* No WAN Metrics known - ignore constraint */
+
+ wan = wpabuf_head(bss->anqp->hs20_wan_metrics);
+ wan_info = wan[0];
+ if (wan_info & BIT(3))
+ return 1; /* WAN link at capacity */
+ lmd = WPA_GET_LE16(wan + 11);
+ if (lmd == 0)
+ return 0; /* Downlink/Uplink Load was not measured */
+ dl_speed = WPA_GET_LE32(wan + 1);
+ ul_speed = WPA_GET_LE32(wan + 5);
+ dl_load = wan[9];
+ ul_load = wan[10];
+
+ if (dl_speed >= 0xffffff)
+ dl_bandwidth = dl_speed / 255 * (255 - dl_load);
+ else
+ dl_bandwidth = dl_speed * (255 - dl_load) / 255;
+
+ if (ul_speed >= 0xffffff)
+ ul_bandwidth = ul_speed / 255 * (255 - ul_load);
+ else
+ ul_bandwidth = ul_speed * (255 - ul_load) / 255;
+
+ res = interworking_home_sp_cred(wpa_s, cred, bss->anqp ?
+ bss->anqp->domain_name : NULL);
+ if (res > 0) {
+ if (cred->min_dl_bandwidth_home > dl_bandwidth)
+ return 1;
+ if (cred->min_ul_bandwidth_home > ul_bandwidth)
+ return 1;
+ } else {
+ if (cred->min_dl_bandwidth_roaming > dl_bandwidth)
+ return 1;
+ if (cred->min_ul_bandwidth_roaming > ul_bandwidth)
+ return 1;
+ }
+
+ return 0;
+}
+
+
+static int cred_over_max_bss_load(struct wpa_supplicant *wpa_s,
+ struct wpa_cred *cred, struct wpa_bss *bss)
+{
+ const u8 *ie;
+ int res;
+
+ if (!cred->max_bss_load)
+ return 0; /* No BSS Load constraint specified */
+
+ ie = wpa_bss_get_ie(bss, WLAN_EID_BSS_LOAD);
+ if (ie == NULL || ie[1] < 3)
+ return 0; /* No BSS Load advertised */
+
+ res = interworking_home_sp_cred(wpa_s, cred, bss->anqp ?
+ bss->anqp->domain_name : NULL);
+ if (res <= 0)
+ return 0; /* Not a home network */
+
+ return ie[4] > cred->max_bss_load;
+}
+
+
+static int has_proto_match(const u8 *pos, const u8 *end, u8 proto)
+{
+ while (pos + 4 <= end) {
+ if (pos[0] == proto && pos[3] == 1 /* Open */)
+ return 1;
+ pos += 4;
+ }
+
+ return 0;
+}
+
+
+static int has_proto_port_match(const u8 *pos, const u8 *end, u8 proto,
+ u16 port)
+{
+ while (pos + 4 <= end) {
+ if (pos[0] == proto && WPA_GET_LE16(&pos[1]) == port &&
+ pos[3] == 1 /* Open */)
+ return 1;
+ pos += 4;
+ }
+
+ return 0;
+}
+
+
+static int cred_conn_capab_missing(struct wpa_supplicant *wpa_s,
+ struct wpa_cred *cred, struct wpa_bss *bss)
+{
+ int res;
+ const u8 *capab, *end;
+ unsigned int i, j;
+ int *ports;
+
+ if (!cred->num_req_conn_capab)
+ return 0; /* No connection capability constraint specified */
+
+ if (bss->anqp == NULL || bss->anqp->hs20_connection_capability == NULL)
+ return 0; /* No Connection Capability known - ignore constraint
+ */
+
+ res = interworking_home_sp_cred(wpa_s, cred, bss->anqp ?
+ bss->anqp->domain_name : NULL);
+ if (res > 0)
+ return 0; /* No constraint in home network */
+
+ capab = wpabuf_head(bss->anqp->hs20_connection_capability);
+ end = capab + wpabuf_len(bss->anqp->hs20_connection_capability);
+
+ for (i = 0; i < cred->num_req_conn_capab; i++) {
+ ports = cred->req_conn_capab_port[i];
+ if (!ports) {
+ if (!has_proto_match(capab, end,
+ cred->req_conn_capab_proto[i]))
+ return 1;
+ } else {
+ for (j = 0; ports[j] > -1; j++) {
+ if (!has_proto_port_match(
+ capab, end,
+ cred->req_conn_capab_proto[i],
+ ports[j]))
+ return 1;
+ }
+ }
+ }
+
+ return 0;
+}
+
+
+static struct wpa_cred * interworking_credentials_available_roaming_consortium(
+ struct wpa_supplicant *wpa_s, struct wpa_bss *bss, int ignore_bw,
+ int *excluded)
+{
+ struct wpa_cred *cred, *selected = NULL;
+ const u8 *ie;
+ int is_excluded = 0;
+
+ ie = wpa_bss_get_ie(bss, WLAN_EID_ROAMING_CONSORTIUM);
+
+ if (ie == NULL &&
+ (bss->anqp == NULL || bss->anqp->roaming_consortium == NULL))
+ return NULL;
+
+ if (wpa_s->conf->cred == NULL)
+ return NULL;
+
+ for (cred = wpa_s->conf->cred; cred; cred = cred->next) {
+ if (cred->roaming_consortium_len == 0)
+ continue;
+
+ if (!roaming_consortium_match(ie,
+ bss->anqp ?
+ bss->anqp->roaming_consortium :
+ NULL,
+ cred->roaming_consortium,
+ cred->roaming_consortium_len))
+ continue;
+
+ if (cred_no_required_oi_match(cred, bss))
+ continue;
+ if (!ignore_bw && cred_below_min_backhaul(wpa_s, cred, bss))
+ continue;
+ if (!ignore_bw && cred_over_max_bss_load(wpa_s, cred, bss))
+ continue;
+ if (!ignore_bw && cred_conn_capab_missing(wpa_s, cred, bss))
+ continue;
+ if (cred_excluded_ssid(cred, bss)) {
+ if (excluded == NULL)
+ continue;
+ if (selected == NULL) {
+ selected = cred;
+ is_excluded = 1;
+ }
+ } else {
+ if (selected == NULL || is_excluded ||
+ cred_prio_cmp(selected, cred) < 0) {
+ selected = cred;
+ is_excluded = 0;
+ }
+ }
+ }
+
+ if (excluded)
+ *excluded = is_excluded;
+
+ return selected;
+}
+
+
+static int interworking_set_eap_params(struct wpa_ssid *ssid,
+ struct wpa_cred *cred, int ttls)
+{
+ if (cred->eap_method) {
+ ttls = cred->eap_method->vendor == EAP_VENDOR_IETF &&
+ cred->eap_method->method == EAP_TYPE_TTLS;
+
+ os_free(ssid->eap.eap_methods);
+ ssid->eap.eap_methods =
+ os_malloc(sizeof(struct eap_method_type) * 2);
+ if (ssid->eap.eap_methods == NULL)
+ return -1;
+ os_memcpy(ssid->eap.eap_methods, cred->eap_method,
+ sizeof(*cred->eap_method));
+ ssid->eap.eap_methods[1].vendor = EAP_VENDOR_IETF;
+ ssid->eap.eap_methods[1].method = EAP_TYPE_NONE;
+ }
+
+ if (ttls && cred->username && cred->username[0]) {
+ const char *pos;
+ char *anon;
+ /* Use anonymous NAI in Phase 1 */
+ pos = os_strchr(cred->username, '@');
+ if (pos) {
+ size_t buflen = 9 + os_strlen(pos) + 1;
+ anon = os_malloc(buflen);
+ if (anon == NULL)
+ return -1;
+ os_snprintf(anon, buflen, "anonymous%s", pos);
+ } else if (cred->realm) {
+ size_t buflen = 10 + os_strlen(cred->realm) + 1;
+ anon = os_malloc(buflen);
+ if (anon == NULL)
+ return -1;
+ os_snprintf(anon, buflen, "anonymous@%s", cred->realm);
+ } else {
+ anon = os_strdup("anonymous");
+ if (anon == NULL)
+ return -1;
+ }
+ if (wpa_config_set_quoted(ssid, "anonymous_identity", anon) <
+ 0) {
+ os_free(anon);
+ return -1;
+ }
+ os_free(anon);
+ }
+
+ if (cred->username && cred->username[0] &&
+ wpa_config_set_quoted(ssid, "identity", cred->username) < 0)
+ return -1;
+
+ if (cred->password && cred->password[0]) {
+ if (cred->ext_password &&
+ wpa_config_set(ssid, "password", cred->password, 0) < 0)
+ return -1;
+ if (!cred->ext_password &&
+ wpa_config_set_quoted(ssid, "password", cred->password) <
+ 0)
+ return -1;
+ }
+
+ if (cred->client_cert && cred->client_cert[0] &&
+ wpa_config_set_quoted(ssid, "client_cert", cred->client_cert) < 0)
+ return -1;
+
+#ifdef ANDROID
+ if (cred->private_key &&
+ os_strncmp(cred->private_key, "keystore://", 11) == 0) {
+ /* Use OpenSSL engine configuration for Android keystore */
+ if (wpa_config_set_quoted(ssid, "engine_id", "keystore") < 0 ||
+ wpa_config_set_quoted(ssid, "key_id",
+ cred->private_key + 11) < 0 ||
+ wpa_config_set(ssid, "engine", "1", 0) < 0)
+ return -1;
+ } else
+#endif /* ANDROID */
+ if (cred->private_key && cred->private_key[0] &&
+ wpa_config_set_quoted(ssid, "private_key", cred->private_key) < 0)
+ return -1;
+
+ if (cred->private_key_passwd && cred->private_key_passwd[0] &&
+ wpa_config_set_quoted(ssid, "private_key_passwd",
+ cred->private_key_passwd) < 0)
+ return -1;
+
+ if (cred->phase1) {
+ os_free(ssid->eap.phase1);
+ ssid->eap.phase1 = os_strdup(cred->phase1);
+ }
+ if (cred->phase2) {
+ os_free(ssid->eap.phase2);
+ ssid->eap.phase2 = os_strdup(cred->phase2);
+ }
+
+ if (cred->ca_cert && cred->ca_cert[0] &&
+ wpa_config_set_quoted(ssid, "ca_cert", cred->ca_cert) < 0)
+ return -1;
+
+ if (cred->domain_suffix_match && cred->domain_suffix_match[0] &&
+ wpa_config_set_quoted(ssid, "domain_suffix_match",
+ cred->domain_suffix_match) < 0)
+ return -1;
+
+ ssid->eap.ocsp = cred->ocsp;
+
+ return 0;
+}
+
+
+static int interworking_connect_roaming_consortium(
+ struct wpa_supplicant *wpa_s, struct wpa_cred *cred,
+ struct wpa_bss *bss, int only_add)
+{
+ struct wpa_ssid *ssid;
+
+ wpa_msg(wpa_s, MSG_DEBUG, "Interworking: Connect with " MACSTR
+ " based on roaming consortium match", MAC2STR(bss->bssid));
+
+ if (already_connected(wpa_s, cred, bss)) {
+ wpa_msg(wpa_s, MSG_INFO, INTERWORKING_ALREADY_CONNECTED MACSTR,
+ MAC2STR(bss->bssid));
+ return wpa_s->current_ssid->id;
+ }
+
+ remove_duplicate_network(wpa_s, cred, bss);
+
+ ssid = wpa_config_add_network(wpa_s->conf);
+ if (ssid == NULL)
+ return -1;
+ ssid->parent_cred = cred;
+ wpas_notify_network_added(wpa_s, ssid);
+ wpa_config_set_network_defaults(ssid);
+ ssid->priority = cred->priority;
+ ssid->temporary = 1;
+ ssid->ssid = os_zalloc(bss->ssid_len + 1);
+ if (ssid->ssid == NULL)
+ goto fail;
+ os_memcpy(ssid->ssid, bss->ssid, bss->ssid_len);
+ ssid->ssid_len = bss->ssid_len;
+
+ if (interworking_set_hs20_params(wpa_s, ssid) < 0)
+ goto fail;
+
+ if (cred->eap_method == NULL) {
+ wpa_msg(wpa_s, MSG_DEBUG,
+ "Interworking: No EAP method set for credential using roaming consortium");
+ goto fail;
+ }
+
+ if (interworking_set_eap_params(
+ ssid, cred,
+ cred->eap_method->vendor == EAP_VENDOR_IETF &&
+ cred->eap_method->method == EAP_TYPE_TTLS) < 0)
+ goto fail;
+
+ wpa_s->next_ssid = ssid;
+ wpa_config_update_prio_list(wpa_s->conf);
+ if (!only_add)
+ interworking_reconnect(wpa_s);
+
+ return ssid->id;
+
+fail:
+ wpas_notify_network_removed(wpa_s, ssid);
+ wpa_config_remove_network(wpa_s->conf, ssid->id);
+ return -1;
+}
+
+
+static int interworking_connect_helper(struct wpa_supplicant *wpa_s,
+ struct wpa_bss *bss, int allow_excluded,
+ int only_add)
+{
+ struct wpa_cred *cred, *cred_rc, *cred_3gpp;
+ struct wpa_ssid *ssid;
+ struct nai_realm *realm;
+ struct nai_realm_eap *eap = NULL;
+ u16 count, i;
+ char buf[100];
+ int excluded = 0, *excl = allow_excluded ? &excluded : NULL;
+ const char *name;
+
+ if (wpa_s->conf->cred == NULL || bss == NULL)
+ return -1;
+ if (disallowed_bssid(wpa_s, bss->bssid) ||
+ disallowed_ssid(wpa_s, bss->ssid, bss->ssid_len)) {
+ wpa_msg(wpa_s, MSG_DEBUG,
+ "Interworking: Reject connection to disallowed BSS "
+ MACSTR, MAC2STR(bss->bssid));
+ return -1;
+ }
+
+ wpa_printf(MSG_DEBUG, "Interworking: Considering BSS " MACSTR
+ " for connection (allow_excluded=%d)",
+ MAC2STR(bss->bssid), allow_excluded);
+
+ if (!wpa_bss_get_ie(bss, WLAN_EID_RSN)) {
+ /*
+ * We currently support only HS 2.0 networks and those are
+ * required to use WPA2-Enterprise.
+ */
+ wpa_msg(wpa_s, MSG_DEBUG,
+ "Interworking: Network does not use RSN");
+ return -1;
+ }
+
+ cred_rc = interworking_credentials_available_roaming_consortium(
+ wpa_s, bss, 0, excl);
+ if (cred_rc) {
+ wpa_msg(wpa_s, MSG_DEBUG,
+ "Interworking: Highest roaming consortium matching credential priority %d sp_priority %d",
+ cred_rc->priority, cred_rc->sp_priority);
+ if (allow_excluded && excl && !(*excl))
+ excl = NULL;
+ }
+
+ cred = interworking_credentials_available_realm(wpa_s, bss, 0, excl);
+ if (cred) {
+ wpa_msg(wpa_s, MSG_DEBUG,
+ "Interworking: Highest NAI Realm list matching credential priority %d sp_priority %d",
+ cred->priority, cred->sp_priority);
+ if (allow_excluded && excl && !(*excl))
+ excl = NULL;
+ }
+
+ cred_3gpp = interworking_credentials_available_3gpp(wpa_s, bss, 0,
+ excl);
+ if (cred_3gpp) {
+ wpa_msg(wpa_s, MSG_DEBUG,
+ "Interworking: Highest 3GPP matching credential priority %d sp_priority %d",
+ cred_3gpp->priority, cred_3gpp->sp_priority);
+ if (allow_excluded && excl && !(*excl))
+ excl = NULL;
+ }
+
+ if (!cred_rc && !cred && !cred_3gpp) {
+ wpa_msg(wpa_s, MSG_DEBUG,
+ "Interworking: No full credential matches - consider options without BW(etc.) limits");
+ cred_rc = interworking_credentials_available_roaming_consortium(
+ wpa_s, bss, 1, excl);
+ if (cred_rc) {
+ wpa_msg(wpa_s, MSG_DEBUG,
+ "Interworking: Highest roaming consortium matching credential priority %d sp_priority %d (ignore BW)",
+ cred_rc->priority, cred_rc->sp_priority);
+ if (allow_excluded && excl && !(*excl))
+ excl = NULL;
+ }
+
+ cred = interworking_credentials_available_realm(wpa_s, bss, 1,
+ excl);
+ if (cred) {
+ wpa_msg(wpa_s, MSG_DEBUG,
+ "Interworking: Highest NAI Realm list matching credential priority %d sp_priority %d (ignore BW)",
+ cred->priority, cred->sp_priority);
+ if (allow_excluded && excl && !(*excl))
+ excl = NULL;
+ }
+
+ cred_3gpp = interworking_credentials_available_3gpp(wpa_s, bss,
+ 1, excl);
+ if (cred_3gpp) {
+ wpa_msg(wpa_s, MSG_DEBUG,
+ "Interworking: Highest 3GPP matching credential priority %d sp_priority %d (ignore BW)",
+ cred_3gpp->priority, cred_3gpp->sp_priority);
+ if (allow_excluded && excl && !(*excl))
+ excl = NULL;
+ }
+ }
+
+ if (cred_rc &&
+ (cred == NULL || cred_prio_cmp(cred_rc, cred) >= 0) &&
+ (cred_3gpp == NULL || cred_prio_cmp(cred_rc, cred_3gpp) >= 0))
+ return interworking_connect_roaming_consortium(wpa_s, cred_rc,
+ bss, only_add);
+
+ if (cred_3gpp &&
+ (cred == NULL || cred_prio_cmp(cred_3gpp, cred) >= 0)) {
+ return interworking_connect_3gpp(wpa_s, cred_3gpp, bss,
+ only_add);
+ }
+
+ if (cred == NULL) {
+ wpa_msg(wpa_s, MSG_DEBUG,
+ "Interworking: No matching credentials found for "
+ MACSTR, MAC2STR(bss->bssid));
+ return -1;
+ }
+
+ realm = nai_realm_parse(bss->anqp ? bss->anqp->nai_realm : NULL,
+ &count);
+ if (realm == NULL) {
+ wpa_msg(wpa_s, MSG_DEBUG,
+ "Interworking: Could not parse NAI Realm list from "
+ MACSTR, MAC2STR(bss->bssid));
+ return -1;
+ }
+
+ for (i = 0; i < count; i++) {
+ if (!nai_realm_match(&realm[i], cred->realm))
+ continue;
+ eap = nai_realm_find_eap(wpa_s, cred, &realm[i]);
+ if (eap)
+ break;
+ }
+
+ if (!eap) {
+ wpa_msg(wpa_s, MSG_DEBUG,
+ "Interworking: No matching credentials and EAP method found for "
+ MACSTR, MAC2STR(bss->bssid));
+ nai_realm_free(realm, count);
+ return -1;
+ }
+
+ wpa_msg(wpa_s, MSG_DEBUG, "Interworking: Connect with " MACSTR,
+ MAC2STR(bss->bssid));
+
+ if (already_connected(wpa_s, cred, bss)) {
+ wpa_msg(wpa_s, MSG_INFO, INTERWORKING_ALREADY_CONNECTED MACSTR,
+ MAC2STR(bss->bssid));
+ nai_realm_free(realm, count);
+ return 0;
+ }
+
+ remove_duplicate_network(wpa_s, cred, bss);
+
+ ssid = wpa_config_add_network(wpa_s->conf);
+ if (ssid == NULL) {
+ nai_realm_free(realm, count);
+ return -1;
+ }
+ ssid->parent_cred = cred;
+ wpas_notify_network_added(wpa_s, ssid);
+ wpa_config_set_network_defaults(ssid);
+ ssid->priority = cred->priority;
+ ssid->temporary = 1;
+ ssid->ssid = os_zalloc(bss->ssid_len + 1);
+ if (ssid->ssid == NULL)
+ goto fail;
+ os_memcpy(ssid->ssid, bss->ssid, bss->ssid_len);
+ ssid->ssid_len = bss->ssid_len;
+
+ if (interworking_set_hs20_params(wpa_s, ssid) < 0)
+ goto fail;
+
+ if (wpa_config_set(ssid, "eap", eap_get_name(EAP_VENDOR_IETF,
+ eap->method), 0) < 0)
+ goto fail;
+
+ switch (eap->method) {
+ case EAP_TYPE_TTLS:
+ if (eap->inner_method) {
+ os_snprintf(buf, sizeof(buf), "\"autheap=%s\"",
+ eap_get_name(EAP_VENDOR_IETF,
+ eap->inner_method));
+ if (wpa_config_set(ssid, "phase2", buf, 0) < 0)
+ goto fail;
+ break;
+ }
+ switch (eap->inner_non_eap) {
+ case NAI_REALM_INNER_NON_EAP_PAP:
+ if (wpa_config_set(ssid, "phase2", "\"auth=PAP\"", 0) <
+ 0)
+ goto fail;
+ break;
+ case NAI_REALM_INNER_NON_EAP_CHAP:
+ if (wpa_config_set(ssid, "phase2", "\"auth=CHAP\"", 0)
+ < 0)
+ goto fail;
+ break;
+ case NAI_REALM_INNER_NON_EAP_MSCHAP:
+ if (wpa_config_set(ssid, "phase2", "\"auth=MSCHAP\"",
+ 0) < 0)
+ goto fail;
+ break;
+ case NAI_REALM_INNER_NON_EAP_MSCHAPV2:
+ if (wpa_config_set(ssid, "phase2", "\"auth=MSCHAPV2\"",
+ 0) < 0)
+ goto fail;
+ break;
+ default:
+ /* EAP params were not set - assume TTLS/MSCHAPv2 */
+ if (wpa_config_set(ssid, "phase2", "\"auth=MSCHAPV2\"",
+ 0) < 0)
+ goto fail;
+ break;
+ }
+ break;
+ case EAP_TYPE_PEAP:
+ case EAP_TYPE_FAST:
+ if (wpa_config_set(ssid, "phase1", "\"fast_provisioning=2\"",
+ 0) < 0)
+ goto fail;
+ if (wpa_config_set(ssid, "pac_file",
+ "\"blob://pac_interworking\"", 0) < 0)
+ goto fail;
+ name = eap_get_name(EAP_VENDOR_IETF,
+ eap->inner_method ? eap->inner_method :
+ EAP_TYPE_MSCHAPV2);
+ if (name == NULL)
+ goto fail;
+ os_snprintf(buf, sizeof(buf), "\"auth=%s\"", name);
+ if (wpa_config_set(ssid, "phase2", buf, 0) < 0)
+ goto fail;
+ break;
+ case EAP_TYPE_TLS:
+ break;
+ }
+
+ if (interworking_set_eap_params(ssid, cred,
+ eap->method == EAP_TYPE_TTLS) < 0)
+ goto fail;
+
+ nai_realm_free(realm, count);
+
+ wpa_s->next_ssid = ssid;
+ wpa_config_update_prio_list(wpa_s->conf);
+ if (!only_add)
+ interworking_reconnect(wpa_s);
+
+ return ssid->id;
+
+fail:
+ wpas_notify_network_removed(wpa_s, ssid);
+ wpa_config_remove_network(wpa_s->conf, ssid->id);
+ nai_realm_free(realm, count);
+ return -1;
+}
+
+
+int interworking_connect(struct wpa_supplicant *wpa_s, struct wpa_bss *bss,
+ int only_add)
+{
+ return interworking_connect_helper(wpa_s, bss, 1, only_add);
+}
+
+
+#ifdef PCSC_FUNCS
+static int interworking_pcsc_read_imsi(struct wpa_supplicant *wpa_s)
+{
+ size_t len;
+
+ if (wpa_s->imsi[0] && wpa_s->mnc_len)
+ return 0;
+
+ len = sizeof(wpa_s->imsi) - 1;
+ if (scard_get_imsi(wpa_s->scard, wpa_s->imsi, &len)) {
+ scard_deinit(wpa_s->scard);
+ wpa_s->scard = NULL;
+ wpa_msg(wpa_s, MSG_ERROR, "Could not read IMSI");
+ return -1;
+ }
+ wpa_s->imsi[len] = '\0';
+ wpa_s->mnc_len = scard_get_mnc_len(wpa_s->scard);
+ wpa_printf(MSG_DEBUG, "SCARD: IMSI %s (MNC length %d)",
+ wpa_s->imsi, wpa_s->mnc_len);
+
+ return 0;
+}
+#endif /* PCSC_FUNCS */
+
+
+static struct wpa_cred * interworking_credentials_available_3gpp(
+ struct wpa_supplicant *wpa_s, struct wpa_bss *bss, int ignore_bw,
+ int *excluded)
+{
+ struct wpa_cred *selected = NULL;
+#ifdef INTERWORKING_3GPP
+ struct wpa_cred *cred;
+ int ret;
+ int is_excluded = 0;
+
+ if (bss->anqp == NULL || bss->anqp->anqp_3gpp == NULL) {
+ wpa_msg(wpa_s, MSG_DEBUG,
+ "interworking-avail-3gpp: not avail, anqp: %p anqp_3gpp: %p",
+ bss->anqp, bss->anqp ? bss->anqp->anqp_3gpp : NULL);
+ return NULL;
+ }
+
+#ifdef CONFIG_EAP_PROXY
+ if (!wpa_s->imsi[0]) {
+ size_t len;
+ wpa_msg(wpa_s, MSG_DEBUG,
+ "Interworking: IMSI not available - try to read again through eap_proxy");
+ wpa_s->mnc_len = eapol_sm_get_eap_proxy_imsi(wpa_s->eapol,
+ wpa_s->imsi,
+ &len);
+ if (wpa_s->mnc_len > 0) {
+ wpa_s->imsi[len] = '\0';
+ wpa_msg(wpa_s, MSG_DEBUG,
+ "eap_proxy: IMSI %s (MNC length %d)",
+ wpa_s->imsi, wpa_s->mnc_len);
+ } else {
+ wpa_msg(wpa_s, MSG_DEBUG,
+ "eap_proxy: IMSI not available");
+ }
+ }
+#endif /* CONFIG_EAP_PROXY */
+
+ for (cred = wpa_s->conf->cred; cred; cred = cred->next) {
+ char *sep;
+ const char *imsi;
+ int mnc_len;
+ char imsi_buf[16];
+ size_t msin_len;
+
+#ifdef PCSC_FUNCS
+ if (cred->pcsc && wpa_s->scard) {
+ if (interworking_pcsc_read_imsi(wpa_s) < 0)
+ continue;
+ imsi = wpa_s->imsi;
+ mnc_len = wpa_s->mnc_len;
+ goto compare;
+ }
+#endif /* PCSC_FUNCS */
+#ifdef CONFIG_EAP_PROXY
+ if (cred->pcsc && wpa_s->mnc_len > 0 && wpa_s->imsi[0]) {
+ imsi = wpa_s->imsi;
+ mnc_len = wpa_s->mnc_len;
+ goto compare;
+ }
+#endif /* CONFIG_EAP_PROXY */
+
+ if (cred->imsi == NULL || !cred->imsi[0] ||
+ (!wpa_s->conf->external_sim &&
+ (cred->milenage == NULL || !cred->milenage[0])))
+ continue;
+
+ sep = os_strchr(cred->imsi, '-');
+ if (sep == NULL ||
+ (sep - cred->imsi != 5 && sep - cred->imsi != 6))
+ continue;
+ mnc_len = sep - cred->imsi - 3;
+ os_memcpy(imsi_buf, cred->imsi, 3 + mnc_len);
+ sep++;
+ msin_len = os_strlen(cred->imsi);
+ if (3 + mnc_len + msin_len >= sizeof(imsi_buf) - 1)
+ msin_len = sizeof(imsi_buf) - 3 - mnc_len - 1;
+ os_memcpy(&imsi_buf[3 + mnc_len], sep, msin_len);
+ imsi_buf[3 + mnc_len + msin_len] = '\0';
+ imsi = imsi_buf;
+
+#if defined(PCSC_FUNCS) || defined(CONFIG_EAP_PROXY)
+ compare:
+#endif /* PCSC_FUNCS || CONFIG_EAP_PROXY */
+ wpa_msg(wpa_s, MSG_DEBUG,
+ "Interworking: Parsing 3GPP info from " MACSTR,
+ MAC2STR(bss->bssid));
+ ret = plmn_id_match(bss->anqp->anqp_3gpp, imsi, mnc_len);
+ wpa_msg(wpa_s, MSG_DEBUG, "PLMN match %sfound",
+ ret ? "" : "not ");
+ if (ret) {
+ if (cred_no_required_oi_match(cred, bss))
+ continue;
+ if (!ignore_bw &&
+ cred_below_min_backhaul(wpa_s, cred, bss))
+ continue;
+ if (!ignore_bw &&
+ cred_over_max_bss_load(wpa_s, cred, bss))
+ continue;
+ if (!ignore_bw &&
+ cred_conn_capab_missing(wpa_s, cred, bss))
+ continue;
+ if (cred_excluded_ssid(cred, bss)) {
+ if (excluded == NULL)
+ continue;
+ if (selected == NULL) {
+ selected = cred;
+ is_excluded = 1;
+ }
+ } else {
+ if (selected == NULL || is_excluded ||
+ cred_prio_cmp(selected, cred) < 0) {
+ selected = cred;
+ is_excluded = 0;
+ }
+ }
+ }
+ }
+
+ if (excluded)
+ *excluded = is_excluded;
+#endif /* INTERWORKING_3GPP */
+ return selected;
+}
+
+
+static struct wpa_cred * interworking_credentials_available_realm(
+ struct wpa_supplicant *wpa_s, struct wpa_bss *bss, int ignore_bw,
+ int *excluded)
+{
+ struct wpa_cred *cred, *selected = NULL;
+ struct nai_realm *realm;
+ u16 count, i;
+ int is_excluded = 0;
+
+ if (bss->anqp == NULL || bss->anqp->nai_realm == NULL)
+ return NULL;
+
+ if (wpa_s->conf->cred == NULL)
+ return NULL;
+
+ wpa_msg(wpa_s, MSG_DEBUG, "Interworking: Parsing NAI Realm list from "
+ MACSTR, MAC2STR(bss->bssid));
+ realm = nai_realm_parse(bss->anqp->nai_realm, &count);
+ if (realm == NULL) {
+ wpa_msg(wpa_s, MSG_DEBUG,
+ "Interworking: Could not parse NAI Realm list from "
+ MACSTR, MAC2STR(bss->bssid));
+ return NULL;
+ }
+
+ for (cred = wpa_s->conf->cred; cred; cred = cred->next) {
+ if (cred->realm == NULL)
+ continue;
+
+ for (i = 0; i < count; i++) {
+ if (!nai_realm_match(&realm[i], cred->realm))
+ continue;
+ if (nai_realm_find_eap(wpa_s, cred, &realm[i])) {
+ if (cred_no_required_oi_match(cred, bss))
+ continue;
+ if (!ignore_bw &&
+ cred_below_min_backhaul(wpa_s, cred, bss))
+ continue;
+ if (!ignore_bw &&
+ cred_over_max_bss_load(wpa_s, cred, bss))
+ continue;
+ if (!ignore_bw &&
+ cred_conn_capab_missing(wpa_s, cred, bss))
+ continue;
+ if (cred_excluded_ssid(cred, bss)) {
+ if (excluded == NULL)
+ continue;
+ if (selected == NULL) {
+ selected = cred;
+ is_excluded = 1;
+ }
+ } else {
+ if (selected == NULL || is_excluded ||
+ cred_prio_cmp(selected, cred) < 0)
+ {
+ selected = cred;
+ is_excluded = 0;
+ }
+ }
+ break;
+ } else {
+ wpa_msg(wpa_s, MSG_DEBUG,
+ "Interworking: realm-find-eap returned false");
+ }
+ }
+ }
+
+ nai_realm_free(realm, count);
+
+ if (excluded)
+ *excluded = is_excluded;
+
+ return selected;
+}
+
+
+static struct wpa_cred * interworking_credentials_available_helper(
+ struct wpa_supplicant *wpa_s, struct wpa_bss *bss, int ignore_bw,
+ int *excluded)
+{
+ struct wpa_cred *cred, *cred2;
+ int excluded1, excluded2 = 0;
+
+ if (disallowed_bssid(wpa_s, bss->bssid) ||
+ disallowed_ssid(wpa_s, bss->ssid, bss->ssid_len)) {
+ wpa_printf(MSG_DEBUG, "Interworking: Ignore disallowed BSS "
+ MACSTR, MAC2STR(bss->bssid));
+ return NULL;
+ }
+
+ cred = interworking_credentials_available_realm(wpa_s, bss, ignore_bw,
+ &excluded1);
+ cred2 = interworking_credentials_available_3gpp(wpa_s, bss, ignore_bw,
+ &excluded2);
+ if (cred && cred2 &&
+ (cred_prio_cmp(cred2, cred) >= 0 || (!excluded2 && excluded1))) {
+ cred = cred2;
+ excluded1 = excluded2;
+ }
+ if (!cred) {
+ cred = cred2;
+ excluded1 = excluded2;
+ }
+
+ cred2 = interworking_credentials_available_roaming_consortium(
+ wpa_s, bss, ignore_bw, &excluded2);
+ if (cred && cred2 &&
+ (cred_prio_cmp(cred2, cred) >= 0 || (!excluded2 && excluded1))) {
+ cred = cred2;
+ excluded1 = excluded2;
+ }
+ if (!cred) {
+ cred = cred2;
+ excluded1 = excluded2;
+ }
+
+ if (excluded)
+ *excluded = excluded1;
+ return cred;
+}
+
+
+static struct wpa_cred * interworking_credentials_available(
+ struct wpa_supplicant *wpa_s, struct wpa_bss *bss, int *excluded)
+{
+ struct wpa_cred *cred;
+
+ if (excluded)
+ *excluded = 0;
+ cred = interworking_credentials_available_helper(wpa_s, bss, 0,
+ excluded);
+ if (cred)
+ return cred;
+ return interworking_credentials_available_helper(wpa_s, bss, 1,
+ excluded);
+}
+
+
+int domain_name_list_contains(struct wpabuf *domain_names,
+ const char *domain, int exact_match)
+{
+ const u8 *pos, *end;
+ size_t len;
+
+ len = os_strlen(domain);
+ pos = wpabuf_head(domain_names);
+ end = pos + wpabuf_len(domain_names);
+
+ while (pos + 1 < end) {
+ if (pos + 1 + pos[0] > end)
+ break;
+
+ wpa_hexdump_ascii(MSG_DEBUG, "Interworking: AP domain name",
+ pos + 1, pos[0]);
+ if (pos[0] == len &&
+ os_strncasecmp(domain, (const char *) (pos + 1), len) == 0)
+ return 1;
+ if (!exact_match && pos[0] > len && pos[pos[0] - len] == '.') {
+ const char *ap = (const char *) (pos + 1);
+ int offset = pos[0] - len;
+ if (os_strncasecmp(domain, ap + offset, len) == 0)
+ return 1;
+ }
+
+ pos += 1 + pos[0];
+ }
+
+ return 0;
+}
+
+
+int interworking_home_sp_cred(struct wpa_supplicant *wpa_s,
+ struct wpa_cred *cred,
+ struct wpabuf *domain_names)
+{
+ size_t i;
+ int ret = -1;
+#ifdef INTERWORKING_3GPP
+ char nai[100], *realm;
+
+ char *imsi = NULL;
+ int mnc_len = 0;
+ if (cred->imsi)
+ imsi = cred->imsi;
+#ifdef PCSC_FUNCS
+ else if (cred->pcsc && wpa_s->scard) {
+ if (interworking_pcsc_read_imsi(wpa_s) < 0)
+ return -1;
+ imsi = wpa_s->imsi;
+ mnc_len = wpa_s->mnc_len;
+ }
+#endif /* PCSC_FUNCS */
+#ifdef CONFIG_EAP_PROXY
+ else if (cred->pcsc && wpa_s->mnc_len > 0 && wpa_s->imsi[0]) {
+ imsi = wpa_s->imsi;
+ mnc_len = wpa_s->mnc_len;
+ }
+#endif /* CONFIG_EAP_PROXY */
+ if (domain_names &&
+ imsi && build_root_nai(nai, sizeof(nai), imsi, mnc_len, 0) == 0) {
+ realm = os_strchr(nai, '@');
+ if (realm)
+ realm++;
+ wpa_msg(wpa_s, MSG_DEBUG,
+ "Interworking: Search for match with SIM/USIM domain %s",
+ realm);
+ if (realm &&
+ domain_name_list_contains(domain_names, realm, 1))
+ return 1;
+ if (realm)
+ ret = 0;
+ }
+#endif /* INTERWORKING_3GPP */
+
+ if (domain_names == NULL || cred->domain == NULL)
+ return ret;
+
+ for (i = 0; i < cred->num_domain; i++) {
+ wpa_msg(wpa_s, MSG_DEBUG,
+ "Interworking: Search for match with home SP FQDN %s",
+ cred->domain[i]);
+ if (domain_name_list_contains(domain_names, cred->domain[i], 1))
+ return 1;
+ }
+
+ return 0;
+}
+
+
+static int interworking_home_sp(struct wpa_supplicant *wpa_s,
+ struct wpabuf *domain_names)
+{
+ struct wpa_cred *cred;
+
+ if (domain_names == NULL || wpa_s->conf->cred == NULL)
+ return -1;
+
+ for (cred = wpa_s->conf->cred; cred; cred = cred->next) {
+ int res = interworking_home_sp_cred(wpa_s, cred, domain_names);
+ if (res)
+ return res;
+ }
+
+ return 0;
+}
+
+
+static int interworking_find_network_match(struct wpa_supplicant *wpa_s)
+{
+ struct wpa_bss *bss;
+ struct wpa_ssid *ssid;
+
+ dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) {
+ for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next) {
+ if (wpas_network_disabled(wpa_s, ssid) ||
+ ssid->mode != WPAS_MODE_INFRA)
+ continue;
+ if (ssid->ssid_len != bss->ssid_len ||
+ os_memcmp(ssid->ssid, bss->ssid, ssid->ssid_len) !=
+ 0)
+ continue;
+ /*
+ * TODO: Consider more accurate matching of security
+ * configuration similarly to what is done in events.c
+ */
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+
+static int roaming_partner_match(struct wpa_supplicant *wpa_s,
+ struct roaming_partner *partner,
+ struct wpabuf *domain_names)
+{
+ wpa_printf(MSG_DEBUG, "Interworking: Comparing roaming_partner info fqdn='%s' exact_match=%d priority=%u country='%s'",
+ partner->fqdn, partner->exact_match, partner->priority,
+ partner->country);
+ wpa_hexdump_ascii(MSG_DEBUG, "Interworking: Domain names",
+ wpabuf_head(domain_names),
+ wpabuf_len(domain_names));
+ if (!domain_name_list_contains(domain_names, partner->fqdn,
+ partner->exact_match))
+ return 0;
+ /* TODO: match Country */
+ return 1;
+}
+
+
+static u8 roaming_prio(struct wpa_supplicant *wpa_s, struct wpa_cred *cred,
+ struct wpa_bss *bss)
+{
+ size_t i;
+
+ if (bss->anqp == NULL || bss->anqp->domain_name == NULL) {
+ wpa_printf(MSG_DEBUG, "Interworking: No ANQP domain name info -> use default roaming partner priority 128");
+ return 128; /* cannot check preference with domain name */
+ }
+
+ if (interworking_home_sp_cred(wpa_s, cred, bss->anqp->domain_name) > 0)
+ {
+ wpa_printf(MSG_DEBUG, "Interworking: Determined to be home SP -> use maximum preference 0 as roaming partner priority");
+ return 0; /* max preference for home SP network */
+ }
+
+ for (i = 0; i < cred->num_roaming_partner; i++) {
+ if (roaming_partner_match(wpa_s, &cred->roaming_partner[i],
+ bss->anqp->domain_name)) {
+ wpa_printf(MSG_DEBUG, "Interworking: Roaming partner preference match - priority %u",
+ cred->roaming_partner[i].priority);
+ return cred->roaming_partner[i].priority;
+ }
+ }
+
+ wpa_printf(MSG_DEBUG, "Interworking: No roaming partner preference match - use default roaming partner priority 128");
+ return 128;
+}
+
+
+static struct wpa_bss * pick_best_roaming_partner(struct wpa_supplicant *wpa_s,
+ struct wpa_bss *selected,
+ struct wpa_cred *cred)
+{
+ struct wpa_bss *bss;
+ u8 best_prio, prio;
+ struct wpa_cred *cred2;
+
+ /*
+ * Check if any other BSS is operated by a more preferred roaming
+ * partner.
+ */
+
+ best_prio = roaming_prio(wpa_s, cred, selected);
+ wpa_printf(MSG_DEBUG, "Interworking: roaming_prio=%u for selected BSS "
+ MACSTR " (cred=%d)", best_prio, MAC2STR(selected->bssid),
+ cred->id);
+
+ dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) {
+ if (bss == selected)
+ continue;
+ cred2 = interworking_credentials_available(wpa_s, bss, NULL);
+ if (!cred2)
+ continue;
+ if (!wpa_bss_get_ie(bss, WLAN_EID_RSN))
+ continue;
+ prio = roaming_prio(wpa_s, cred2, bss);
+ wpa_printf(MSG_DEBUG, "Interworking: roaming_prio=%u for BSS "
+ MACSTR " (cred=%d)", prio, MAC2STR(bss->bssid),
+ cred2->id);
+ if (prio < best_prio) {
+ int bh1, bh2, load1, load2, conn1, conn2;
+ bh1 = cred_below_min_backhaul(wpa_s, cred, selected);
+ load1 = cred_over_max_bss_load(wpa_s, cred, selected);
+ conn1 = cred_conn_capab_missing(wpa_s, cred, selected);
+ bh2 = cred_below_min_backhaul(wpa_s, cred2, bss);
+ load2 = cred_over_max_bss_load(wpa_s, cred2, bss);
+ conn2 = cred_conn_capab_missing(wpa_s, cred2, bss);
+ wpa_printf(MSG_DEBUG, "Interworking: old: %d %d %d new: %d %d %d",
+ bh1, load1, conn1, bh2, load2, conn2);
+ if (bh1 || load1 || conn1 || !(bh2 || load2 || conn2)) {
+ wpa_printf(MSG_DEBUG, "Interworking: Better roaming partner " MACSTR " selected", MAC2STR(bss->bssid));
+ best_prio = prio;
+ selected = bss;
+ }
+ }
+ }
+
+ return selected;
+}
+
+
+static void interworking_select_network(struct wpa_supplicant *wpa_s)
+{
+ struct wpa_bss *bss, *selected = NULL, *selected_home = NULL;
+ struct wpa_bss *selected2 = NULL, *selected2_home = NULL;
+ unsigned int count = 0;
+ const char *type;
+ int res;
+ struct wpa_cred *cred, *selected_cred = NULL;
+ struct wpa_cred *selected_home_cred = NULL;
+ struct wpa_cred *selected2_cred = NULL;
+ struct wpa_cred *selected2_home_cred = NULL;
+
+ wpa_s->network_select = 0;
+
+ wpa_printf(MSG_DEBUG, "Interworking: Select network (auto_select=%d)",
+ wpa_s->auto_select);
+ dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) {
+ int excluded = 0;
+ int bh, bss_load, conn_capab;
+ cred = interworking_credentials_available(wpa_s, bss,
+ &excluded);
+ if (!cred)
+ continue;
+
+ if (!wpa_bss_get_ie(bss, WLAN_EID_RSN)) {
+ /*
+ * We currently support only HS 2.0 networks and those
+ * are required to use WPA2-Enterprise.
+ */
+ wpa_msg(wpa_s, MSG_DEBUG,
+ "Interworking: Credential match with " MACSTR
+ " but network does not use RSN",
+ MAC2STR(bss->bssid));
+ continue;
+ }
+ if (!excluded)
+ count++;
+ res = interworking_home_sp(wpa_s, bss->anqp ?
+ bss->anqp->domain_name : NULL);
+ if (res > 0)
+ type = "home";
+ else if (res == 0)
+ type = "roaming";
+ else
+ type = "unknown";
+ bh = cred_below_min_backhaul(wpa_s, cred, bss);
+ bss_load = cred_over_max_bss_load(wpa_s, cred, bss);
+ conn_capab = cred_conn_capab_missing(wpa_s, cred, bss);
+ wpa_msg(wpa_s, MSG_INFO, "%s" MACSTR " type=%s%s%s%s id=%d priority=%d sp_priority=%d",
+ excluded ? INTERWORKING_BLACKLISTED : INTERWORKING_AP,
+ MAC2STR(bss->bssid), type,
+ bh ? " below_min_backhaul=1" : "",
+ bss_load ? " over_max_bss_load=1" : "",
+ conn_capab ? " conn_capab_missing=1" : "",
+ cred->id, cred->priority, cred->sp_priority);
+ if (excluded)
+ continue;
+ if (wpa_s->auto_select ||
+ (wpa_s->conf->auto_interworking &&
+ wpa_s->auto_network_select)) {
+ if (bh || bss_load || conn_capab) {
+ if (selected2_cred == NULL ||
+ cred_prio_cmp(cred, selected2_cred) > 0) {
+ wpa_printf(MSG_DEBUG, "Interworking: Mark as selected2");
+ selected2 = bss;
+ selected2_cred = cred;
+ }
+ if (res > 0 &&
+ (selected2_home_cred == NULL ||
+ cred_prio_cmp(cred, selected2_home_cred) >
+ 0)) {
+ wpa_printf(MSG_DEBUG, "Interworking: Mark as selected2_home");
+ selected2_home = bss;
+ selected2_home_cred = cred;
+ }
+ } else {
+ if (selected_cred == NULL ||
+ cred_prio_cmp(cred, selected_cred) > 0) {
+ wpa_printf(MSG_DEBUG, "Interworking: Mark as selected");
+ selected = bss;
+ selected_cred = cred;
+ }
+ if (res > 0 &&
+ (selected_home_cred == NULL ||
+ cred_prio_cmp(cred, selected_home_cred) >
+ 0)) {
+ wpa_printf(MSG_DEBUG, "Interworking: Mark as selected_home");
+ selected_home = bss;
+ selected_home_cred = cred;
+ }
+ }
+ }
+ }
+
+ if (selected_home && selected_home != selected &&
+ selected_home_cred &&
+ (selected_cred == NULL ||
+ cred_prio_cmp(selected_home_cred, selected_cred) >= 0)) {
+ /* Prefer network operated by the Home SP */
+ wpa_printf(MSG_DEBUG, "Interworking: Overrided selected with selected_home");
+ selected = selected_home;
+ selected_cred = selected_home_cred;
+ }
+
+ if (!selected) {
+ if (selected2_home) {
+ wpa_printf(MSG_DEBUG, "Interworking: Use home BSS with BW limit mismatch since no other network could be selected");
+ selected = selected2_home;
+ selected_cred = selected2_home_cred;
+ } else if (selected2) {
+ wpa_printf(MSG_DEBUG, "Interworking: Use visited BSS with BW limit mismatch since no other network could be selected");
+ selected = selected2;
+ selected_cred = selected2_cred;
+ }
+ }
+
+ if (count == 0) {
+ /*
+ * No matching network was found based on configured
+ * credentials. Check whether any of the enabled network blocks
+ * have matching APs.
+ */
+ if (interworking_find_network_match(wpa_s)) {
+ wpa_msg(wpa_s, MSG_DEBUG,
+ "Interworking: Possible BSS match for enabled network configurations");
+ if (wpa_s->auto_select) {
+ interworking_reconnect(wpa_s);
+ return;
+ }
+ }
+
+ if (wpa_s->auto_network_select) {
+ wpa_msg(wpa_s, MSG_DEBUG,
+ "Interworking: Continue scanning after ANQP fetch");
+ wpa_supplicant_req_scan(wpa_s, wpa_s->scan_interval,
+ 0);
+ return;
+ }
+
+ wpa_msg(wpa_s, MSG_INFO, INTERWORKING_NO_MATCH "No network "
+ "with matching credentials found");
+ if (wpa_s->wpa_state == WPA_SCANNING)
+ wpa_supplicant_set_state(wpa_s, WPA_DISCONNECTED);
+ }
+
+ if (selected) {
+ wpa_printf(MSG_DEBUG, "Interworking: Selected " MACSTR,
+ MAC2STR(selected->bssid));
+ selected = pick_best_roaming_partner(wpa_s, selected,
+ selected_cred);
+ wpa_printf(MSG_DEBUG, "Interworking: Selected " MACSTR
+ " (after best roaming partner selection)",
+ MAC2STR(selected->bssid));
+ wpa_msg(wpa_s, MSG_INFO, INTERWORKING_SELECTED MACSTR,
+ MAC2STR(selected->bssid));
+ interworking_connect(wpa_s, selected, 0);
+ }
+}
+
+
+static struct wpa_bss_anqp *
+interworking_match_anqp_info(struct wpa_supplicant *wpa_s, struct wpa_bss *bss)
+{
+ struct wpa_bss *other;
+
+ if (is_zero_ether_addr(bss->hessid))
+ return NULL; /* Cannot be in the same homegenous ESS */
+
+ dl_list_for_each(other, &wpa_s->bss, struct wpa_bss, list) {
+ if (other == bss)
+ continue;
+ if (other->anqp == NULL)
+ continue;
+ if (other->anqp->roaming_consortium == NULL &&
+ other->anqp->nai_realm == NULL &&
+ other->anqp->anqp_3gpp == NULL &&
+ other->anqp->domain_name == NULL)
+ continue;
+ if (!(other->flags & WPA_BSS_ANQP_FETCH_TRIED))
+ continue;
+ if (os_memcmp(bss->hessid, other->hessid, ETH_ALEN) != 0)
+ continue;
+ if (bss->ssid_len != other->ssid_len ||
+ os_memcmp(bss->ssid, other->ssid, bss->ssid_len) != 0)
+ continue;
+
+ wpa_msg(wpa_s, MSG_DEBUG,
+ "Interworking: Share ANQP data with already fetched BSSID "
+ MACSTR " and " MACSTR,
+ MAC2STR(other->bssid), MAC2STR(bss->bssid));
+ other->anqp->users++;
+ return other->anqp;
+ }
+
+ return NULL;
+}
+
+
+static void interworking_next_anqp_fetch(struct wpa_supplicant *wpa_s)
+{
+ struct wpa_bss *bss;
+ int found = 0;
+ const u8 *ie;
+
+ wpa_printf(MSG_DEBUG, "Interworking: next_anqp_fetch - "
+ "fetch_anqp_in_progress=%d fetch_osu_icon_in_progress=%d",
+ wpa_s->fetch_anqp_in_progress,
+ wpa_s->fetch_osu_icon_in_progress);
+
+ if (eloop_terminated() || !wpa_s->fetch_anqp_in_progress) {
+ wpa_printf(MSG_DEBUG, "Interworking: Stop next-ANQP-fetch");
+ return;
+ }
+
+ if (wpa_s->fetch_osu_icon_in_progress) {
+ wpa_printf(MSG_DEBUG, "Interworking: Next icon (in progress)");
+ hs20_next_osu_icon(wpa_s);
+ return;
+ }
+
+ dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) {
+ if (!(bss->caps & IEEE80211_CAP_ESS))
+ continue;
+ ie = wpa_bss_get_ie(bss, WLAN_EID_EXT_CAPAB);
+ if (ie == NULL || ie[1] < 4 || !(ie[5] & 0x80))
+ continue; /* AP does not support Interworking */
+ if (disallowed_bssid(wpa_s, bss->bssid) ||
+ disallowed_ssid(wpa_s, bss->ssid, bss->ssid_len))
+ continue; /* Disallowed BSS */
+
+ if (!(bss->flags & WPA_BSS_ANQP_FETCH_TRIED)) {
+ if (bss->anqp == NULL) {
+ bss->anqp = interworking_match_anqp_info(wpa_s,
+ bss);
+ if (bss->anqp) {
+ /* Shared data already fetched */
+ continue;
+ }
+ bss->anqp = wpa_bss_anqp_alloc();
+ if (bss->anqp == NULL)
+ break;
+ }
+ found++;
+ bss->flags |= WPA_BSS_ANQP_FETCH_TRIED;
+ wpa_msg(wpa_s, MSG_INFO, "Starting ANQP fetch for "
+ MACSTR, MAC2STR(bss->bssid));
+ interworking_anqp_send_req(wpa_s, bss);
+ break;
+ }
+ }
+
+ if (found == 0) {
+ if (wpa_s->fetch_osu_info) {
+ if (wpa_s->num_prov_found == 0 &&
+ wpa_s->fetch_osu_waiting_scan &&
+ wpa_s->num_osu_scans < 3) {
+ wpa_printf(MSG_DEBUG, "HS 2.0: No OSU providers seen - try to scan again");
+ hs20_start_osu_scan(wpa_s);
+ return;
+ }
+ wpa_printf(MSG_DEBUG, "Interworking: Next icon");
+ hs20_osu_icon_fetch(wpa_s);
+ return;
+ }
+ wpa_msg(wpa_s, MSG_INFO, "ANQP fetch completed");
+ wpa_s->fetch_anqp_in_progress = 0;
+ if (wpa_s->network_select)
+ interworking_select_network(wpa_s);
+ }
+}
+
+
+void interworking_start_fetch_anqp(struct wpa_supplicant *wpa_s)
+{
+ struct wpa_bss *bss;
+
+ dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list)
+ bss->flags &= ~WPA_BSS_ANQP_FETCH_TRIED;
+
+ wpa_s->fetch_anqp_in_progress = 1;
+
+ /*
+ * Start actual ANQP operation from eloop call to make sure the loop
+ * does not end up using excessive recursion.
+ */
+ eloop_register_timeout(0, 0, interworking_continue_anqp, wpa_s, NULL);
+}
+
+
+int interworking_fetch_anqp(struct wpa_supplicant *wpa_s)
+{
+ if (wpa_s->fetch_anqp_in_progress || wpa_s->network_select)
+ return 0;
+
+ wpa_s->network_select = 0;
+ wpa_s->fetch_all_anqp = 1;
+ wpa_s->fetch_osu_info = 0;
+
+ interworking_start_fetch_anqp(wpa_s);
+
+ return 0;
+}
+
+
+void interworking_stop_fetch_anqp(struct wpa_supplicant *wpa_s)
+{
+ if (!wpa_s->fetch_anqp_in_progress)
+ return;
+
+ wpa_s->fetch_anqp_in_progress = 0;
+}
+
+
+int anqp_send_req(struct wpa_supplicant *wpa_s, const u8 *dst,
+ u16 info_ids[], size_t num_ids, u32 subtypes)
+{
+ struct wpabuf *buf;
+ struct wpabuf *hs20_buf = NULL;
+ int ret = 0;
+ int freq;
+ struct wpa_bss *bss;
+ int res;
+
+ bss = wpa_bss_get_bssid(wpa_s, dst);
+ if (!bss) {
+ wpa_printf(MSG_WARNING,
+ "ANQP: Cannot send query to unknown BSS "
+ MACSTR, MAC2STR(dst));
+ return -1;
+ }
+
+ wpa_bss_anqp_unshare_alloc(bss);
+ freq = bss->freq;
+
+ wpa_msg(wpa_s, MSG_DEBUG,
+ "ANQP: Query Request to " MACSTR " for %u id(s)",
+ MAC2STR(dst), (unsigned int) num_ids);
+
+#ifdef CONFIG_HS20
+ if (subtypes != 0) {
+ hs20_buf = wpabuf_alloc(100);
+ if (hs20_buf == NULL)
+ return -1;
+ hs20_put_anqp_req(subtypes, NULL, 0, hs20_buf);
+ }
+#endif /* CONFIG_HS20 */
+
+ buf = anqp_build_req(info_ids, num_ids, hs20_buf);
+ wpabuf_free(hs20_buf);
+ if (buf == NULL)
+ return -1;
+
+ res = gas_query_req(wpa_s->gas, dst, freq, buf, anqp_resp_cb, wpa_s);
+ if (res < 0) {
+ wpa_msg(wpa_s, MSG_DEBUG, "ANQP: Failed to send Query Request");
+ wpabuf_free(buf);
+ ret = -1;
+ } else {
+ wpa_msg(wpa_s, MSG_DEBUG,
+ "ANQP: Query started with dialog token %u", res);
+ }
+
+ return ret;
+}
+
+
+static void interworking_parse_rx_anqp_resp(struct wpa_supplicant *wpa_s,
+ struct wpa_bss *bss, const u8 *sa,
+ u16 info_id,
+ const u8 *data, size_t slen)
+{
+ const u8 *pos = data;
+ struct wpa_bss_anqp *anqp = NULL;
+#ifdef CONFIG_HS20
+ u8 type;
+#endif /* CONFIG_HS20 */
+
+ if (bss)
+ anqp = bss->anqp;
+
+ switch (info_id) {
+ case ANQP_CAPABILITY_LIST:
+ wpa_msg(wpa_s, MSG_INFO, "RX-ANQP " MACSTR
+ " ANQP Capability list", MAC2STR(sa));
+ wpa_hexdump_ascii(MSG_DEBUG, "ANQP: Capability list",
+ pos, slen);
+ if (anqp) {
+ wpabuf_free(anqp->capability_list);
+ anqp->capability_list = wpabuf_alloc_copy(pos, slen);
+ }
+ break;
+ case ANQP_VENUE_NAME:
+ wpa_msg(wpa_s, MSG_INFO, "RX-ANQP " MACSTR
+ " Venue Name", MAC2STR(sa));
+ wpa_hexdump_ascii(MSG_DEBUG, "ANQP: Venue Name", pos, slen);
+ if (anqp) {
+ wpabuf_free(anqp->venue_name);
+ anqp->venue_name = wpabuf_alloc_copy(pos, slen);
+ }
+ break;
+ case ANQP_NETWORK_AUTH_TYPE:
+ wpa_msg(wpa_s, MSG_INFO, "RX-ANQP " MACSTR
+ " Network Authentication Type information",
+ MAC2STR(sa));
+ wpa_hexdump_ascii(MSG_DEBUG, "ANQP: Network Authentication "
+ "Type", pos, slen);
+ if (anqp) {
+ wpabuf_free(anqp->network_auth_type);
+ anqp->network_auth_type = wpabuf_alloc_copy(pos, slen);
+ }
+ break;
+ case ANQP_ROAMING_CONSORTIUM:
+ wpa_msg(wpa_s, MSG_INFO, "RX-ANQP " MACSTR
+ " Roaming Consortium list", MAC2STR(sa));
+ wpa_hexdump_ascii(MSG_DEBUG, "ANQP: Roaming Consortium",
+ pos, slen);
+ if (anqp) {
+ wpabuf_free(anqp->roaming_consortium);
+ anqp->roaming_consortium = wpabuf_alloc_copy(pos, slen);
+ }
+ break;
+ case ANQP_IP_ADDR_TYPE_AVAILABILITY:
+ wpa_msg(wpa_s, MSG_INFO, "RX-ANQP " MACSTR
+ " IP Address Type Availability information",
+ MAC2STR(sa));
+ wpa_hexdump(MSG_MSGDUMP, "ANQP: IP Address Availability",
+ pos, slen);
+ if (anqp) {
+ wpabuf_free(anqp->ip_addr_type_availability);
+ anqp->ip_addr_type_availability =
+ wpabuf_alloc_copy(pos, slen);
+ }
+ break;
+ case ANQP_NAI_REALM:
+ wpa_msg(wpa_s, MSG_INFO, "RX-ANQP " MACSTR
+ " NAI Realm list", MAC2STR(sa));
+ wpa_hexdump_ascii(MSG_DEBUG, "ANQP: NAI Realm", pos, slen);
+ if (anqp) {
+ wpabuf_free(anqp->nai_realm);
+ anqp->nai_realm = wpabuf_alloc_copy(pos, slen);
+ }
+ break;
+ case ANQP_3GPP_CELLULAR_NETWORK:
+ wpa_msg(wpa_s, MSG_INFO, "RX-ANQP " MACSTR
+ " 3GPP Cellular Network information", MAC2STR(sa));
+ wpa_hexdump_ascii(MSG_DEBUG, "ANQP: 3GPP Cellular Network",
+ pos, slen);
+ if (anqp) {
+ wpabuf_free(anqp->anqp_3gpp);
+ anqp->anqp_3gpp = wpabuf_alloc_copy(pos, slen);
+ }
+ break;
+ case ANQP_DOMAIN_NAME:
+ wpa_msg(wpa_s, MSG_INFO, "RX-ANQP " MACSTR
+ " Domain Name list", MAC2STR(sa));
+ wpa_hexdump_ascii(MSG_MSGDUMP, "ANQP: Domain Name", pos, slen);
+ if (anqp) {
+ wpabuf_free(anqp->domain_name);
+ anqp->domain_name = wpabuf_alloc_copy(pos, slen);
+ }
+ break;
+ case ANQP_VENDOR_SPECIFIC:
+ if (slen < 3)
+ return;
+
+ switch (WPA_GET_BE24(pos)) {
+#ifdef CONFIG_HS20
+ case OUI_WFA:
+ pos += 3;
+ slen -= 3;
+
+ if (slen < 1)
+ return;
+ type = *pos++;
+ slen--;
+
+ switch (type) {
+ case HS20_ANQP_OUI_TYPE:
+ hs20_parse_rx_hs20_anqp_resp(wpa_s, bss, sa,
+ pos, slen);
+ break;
+ default:
+ wpa_msg(wpa_s, MSG_DEBUG,
+ "HS20: Unsupported ANQP vendor type %u",
+ type);
+ break;
+ }
+ break;
+#endif /* CONFIG_HS20 */
+ default:
+ wpa_msg(wpa_s, MSG_DEBUG,
+ "Interworking: Unsupported vendor-specific ANQP OUI %06x",
+ WPA_GET_BE24(pos));
+ return;
+ }
+ break;
+ default:
+ wpa_msg(wpa_s, MSG_DEBUG,
+ "Interworking: Unsupported ANQP Info ID %u", info_id);
+ break;
+ }
+}
+
+
+void anqp_resp_cb(void *ctx, const u8 *dst, u8 dialog_token,
+ enum gas_query_result result,
+ const struct wpabuf *adv_proto,
+ const struct wpabuf *resp, u16 status_code)
+{
+ struct wpa_supplicant *wpa_s = ctx;
+ const u8 *pos;
+ const u8 *end;
+ u16 info_id;
+ u16 slen;
+ struct wpa_bss *bss = NULL, *tmp;
+ const char *anqp_result = "SUCCESS";
+
+ wpa_printf(MSG_DEBUG, "Interworking: anqp_resp_cb dst=" MACSTR
+ " dialog_token=%u result=%d status_code=%u",
+ MAC2STR(dst), dialog_token, result, status_code);
+ if (result != GAS_QUERY_SUCCESS) {
+ if (wpa_s->fetch_osu_icon_in_progress)
+ hs20_icon_fetch_failed(wpa_s);
+ anqp_result = "FAILURE";
+ goto out;
+ }
+
+ pos = wpabuf_head(adv_proto);
+ if (wpabuf_len(adv_proto) < 4 || pos[0] != WLAN_EID_ADV_PROTO ||
+ pos[1] < 2 || pos[3] != ACCESS_NETWORK_QUERY_PROTOCOL) {
+ wpa_msg(wpa_s, MSG_DEBUG,
+ "ANQP: Unexpected Advertisement Protocol in response");
+ if (wpa_s->fetch_osu_icon_in_progress)
+ hs20_icon_fetch_failed(wpa_s);
+ anqp_result = "INVALID_FRAME";
+ goto out;
+ }
+
+ /*
+ * If possible, select the BSS entry based on which BSS entry was used
+ * for the request. This can help in cases where multiple BSS entries
+ * may exist for the same AP.
+ */
+ dl_list_for_each_reverse(tmp, &wpa_s->bss, struct wpa_bss, list) {
+ if (tmp == wpa_s->interworking_gas_bss &&
+ os_memcmp(tmp->bssid, dst, ETH_ALEN) == 0) {
+ bss = tmp;
+ break;
+ }
+ }
+ if (bss == NULL)
+ bss = wpa_bss_get_bssid(wpa_s, dst);
+
+ pos = wpabuf_head(resp);
+ end = pos + wpabuf_len(resp);
+
+ while (pos < end) {
+ unsigned int left = end - pos;
+
+ if (left < 4) {
+ wpa_msg(wpa_s, MSG_DEBUG, "ANQP: Invalid element");
+ anqp_result = "INVALID_FRAME";
+ goto out_parse_done;
+ }
+ info_id = WPA_GET_LE16(pos);
+ pos += 2;
+ slen = WPA_GET_LE16(pos);
+ pos += 2;
+ left -= 4;
+ if (left < slen) {
+ wpa_msg(wpa_s, MSG_DEBUG,
+ "ANQP: Invalid element length for Info ID %u",
+ info_id);
+ anqp_result = "INVALID_FRAME";
+ goto out_parse_done;
+ }
+ interworking_parse_rx_anqp_resp(wpa_s, bss, dst, info_id, pos,
+ slen);
+ pos += slen;
+ }
+
+out_parse_done:
+ hs20_notify_parse_done(wpa_s);
+out:
+ wpa_msg(wpa_s, MSG_INFO, ANQP_QUERY_DONE "addr=" MACSTR " result=%s",
+ MAC2STR(dst), anqp_result);
+}
+
+
+static void interworking_scan_res_handler(struct wpa_supplicant *wpa_s,
+ struct wpa_scan_results *scan_res)
+{
+ wpa_msg(wpa_s, MSG_DEBUG,
+ "Interworking: Scan results available - start ANQP fetch");
+ interworking_start_fetch_anqp(wpa_s);
+}
+
+
+int interworking_select(struct wpa_supplicant *wpa_s, int auto_select,
+ int *freqs)
+{
+ interworking_stop_fetch_anqp(wpa_s);
+ wpa_s->network_select = 1;
+ wpa_s->auto_network_select = 0;
+ wpa_s->auto_select = !!auto_select;
+ wpa_s->fetch_all_anqp = 0;
+ wpa_s->fetch_osu_info = 0;
+ wpa_msg(wpa_s, MSG_DEBUG,
+ "Interworking: Start scan for network selection");
+ wpa_s->scan_res_handler = interworking_scan_res_handler;
+ wpa_s->normal_scans = 0;
+ wpa_s->scan_req = MANUAL_SCAN_REQ;
+ os_free(wpa_s->manual_scan_freqs);
+ wpa_s->manual_scan_freqs = freqs;
+ wpa_s->after_wps = 0;
+ wpa_s->known_wps_freq = 0;
+ wpa_supplicant_req_scan(wpa_s, 0, 0);
+
+ return 0;
+}
+
+
+static void gas_resp_cb(void *ctx, const u8 *addr, u8 dialog_token,
+ enum gas_query_result result,
+ const struct wpabuf *adv_proto,
+ const struct wpabuf *resp, u16 status_code)
+{
+ struct wpa_supplicant *wpa_s = ctx;
+ struct wpabuf *n;
+
+ wpa_msg(wpa_s, MSG_INFO, GAS_RESPONSE_INFO "addr=" MACSTR
+ " dialog_token=%d status_code=%d resp_len=%d",
+ MAC2STR(addr), dialog_token, status_code,
+ resp ? (int) wpabuf_len(resp) : -1);
+ if (!resp)
+ return;
+
+ n = wpabuf_dup(resp);
+ if (n == NULL)
+ return;
+ wpabuf_free(wpa_s->prev_gas_resp);
+ wpa_s->prev_gas_resp = wpa_s->last_gas_resp;
+ os_memcpy(wpa_s->prev_gas_addr, wpa_s->last_gas_addr, ETH_ALEN);
+ wpa_s->prev_gas_dialog_token = wpa_s->last_gas_dialog_token;
+ wpa_s->last_gas_resp = n;
+ os_memcpy(wpa_s->last_gas_addr, addr, ETH_ALEN);
+ wpa_s->last_gas_dialog_token = dialog_token;
+}
+
+
+int gas_send_request(struct wpa_supplicant *wpa_s, const u8 *dst,
+ const struct wpabuf *adv_proto,
+ const struct wpabuf *query)
+{
+ struct wpabuf *buf;
+ int ret = 0;
+ int freq;
+ struct wpa_bss *bss;
+ int res;
+ size_t len;
+ u8 query_resp_len_limit = 0;
+
+ freq = wpa_s->assoc_freq;
+ bss = wpa_bss_get_bssid(wpa_s, dst);
+ if (bss)
+ freq = bss->freq;
+ if (freq <= 0)
+ return -1;
+
+ wpa_msg(wpa_s, MSG_DEBUG, "GAS request to " MACSTR " (freq %d MHz)",
+ MAC2STR(dst), freq);
+ wpa_hexdump_buf(MSG_DEBUG, "Advertisement Protocol ID", adv_proto);
+ wpa_hexdump_buf(MSG_DEBUG, "GAS Query", query);
+
+ len = 3 + wpabuf_len(adv_proto) + 2;
+ if (query)
+ len += wpabuf_len(query);
+ buf = gas_build_initial_req(0, len);
+ if (buf == NULL)
+ return -1;
+
+ /* Advertisement Protocol IE */
+ wpabuf_put_u8(buf, WLAN_EID_ADV_PROTO);
+ wpabuf_put_u8(buf, 1 + wpabuf_len(adv_proto)); /* Length */
+ wpabuf_put_u8(buf, query_resp_len_limit & 0x7f);
+ wpabuf_put_buf(buf, adv_proto);
+
+ /* GAS Query */
+ if (query) {
+ wpabuf_put_le16(buf, wpabuf_len(query));
+ wpabuf_put_buf(buf, query);
+ } else
+ wpabuf_put_le16(buf, 0);
+
+ res = gas_query_req(wpa_s->gas, dst, freq, buf, gas_resp_cb, wpa_s);
+ if (res < 0) {
+ wpa_msg(wpa_s, MSG_DEBUG, "GAS: Failed to send Query Request");
+ wpabuf_free(buf);
+ ret = -1;
+ } else
+ wpa_msg(wpa_s, MSG_DEBUG,
+ "GAS: Query started with dialog token %u", res);
+
+ return ret;
+}
diff --git a/freebsd/contrib/wpa/wpa_supplicant/interworking.h b/freebsd/contrib/wpa/wpa_supplicant/interworking.h
new file mode 100644
index 00000000..3743dc00
--- /dev/null
+++ b/freebsd/contrib/wpa/wpa_supplicant/interworking.h
@@ -0,0 +1,36 @@
+/*
+ * Interworking (IEEE 802.11u)
+ * Copyright (c) 2011-2012, Qualcomm Atheros
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef INTERWORKING_H
+#define INTERWORKING_H
+
+enum gas_query_result;
+
+int anqp_send_req(struct wpa_supplicant *wpa_s, const u8 *dst,
+ u16 info_ids[], size_t num_ids, u32 subtypes);
+void anqp_resp_cb(void *ctx, const u8 *dst, u8 dialog_token,
+ enum gas_query_result result,
+ const struct wpabuf *adv_proto,
+ const struct wpabuf *resp, u16 status_code);
+int gas_send_request(struct wpa_supplicant *wpa_s, const u8 *dst,
+ const struct wpabuf *adv_proto,
+ const struct wpabuf *query);
+int interworking_fetch_anqp(struct wpa_supplicant *wpa_s);
+void interworking_stop_fetch_anqp(struct wpa_supplicant *wpa_s);
+int interworking_select(struct wpa_supplicant *wpa_s, int auto_select,
+ int *freqs);
+int interworking_connect(struct wpa_supplicant *wpa_s, struct wpa_bss *bss,
+ int only_add);
+void interworking_start_fetch_anqp(struct wpa_supplicant *wpa_s);
+int interworking_home_sp_cred(struct wpa_supplicant *wpa_s,
+ struct wpa_cred *cred,
+ struct wpabuf *domain_names);
+int domain_name_list_contains(struct wpabuf *domain_names,
+ const char *domain, int exact_match);
+
+#endif /* INTERWORKING_H */
diff --git a/freebsd/contrib/wpa/wpa_supplicant/main.c b/freebsd/contrib/wpa/wpa_supplicant/main.c
new file mode 100644
index 00000000..0e86b939
--- /dev/null
+++ b/freebsd/contrib/wpa/wpa_supplicant/main.c
@@ -0,0 +1,361 @@
+#include <machine/rtems-bsd-user-space.h>
+
+/*
+ * WPA Supplicant / main() function for UNIX like OSes and MinGW
+ * Copyright (c) 2003-2013, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+#ifdef __linux__
+#include <fcntl.h>
+#endif /* __linux__ */
+
+#include "common.h"
+#include "fst/fst.h"
+#include "wpa_supplicant_i.h"
+#include "driver_i.h"
+#include "p2p_supplicant.h"
+
+
+static void usage(void)
+{
+ int i;
+ printf("%s\n\n%s\n"
+ "usage:\n"
+ " wpa_supplicant [-BddhKLqq"
+#ifdef CONFIG_DEBUG_SYSLOG
+ "s"
+#endif /* CONFIG_DEBUG_SYSLOG */
+ "t"
+#ifdef CONFIG_DBUS
+ "u"
+#endif /* CONFIG_DBUS */
+ "vW] [-P<pid file>] "
+ "[-g<global ctrl>] \\\n"
+ " [-G<group>] \\\n"
+ " -i<ifname> -c<config file> [-C<ctrl>] [-D<driver>] "
+ "[-p<driver_param>] \\\n"
+ " [-b<br_ifname>] [-e<entropy file>]"
+#ifdef CONFIG_DEBUG_FILE
+ " [-f<debug file>]"
+#endif /* CONFIG_DEBUG_FILE */
+ " \\\n"
+ " [-o<override driver>] [-O<override ctrl>] \\\n"
+ " [-N -i<ifname> -c<conf> [-C<ctrl>] "
+ "[-D<driver>] \\\n"
+#ifdef CONFIG_P2P
+ " [-m<P2P Device config file>] \\\n"
+#endif /* CONFIG_P2P */
+ " [-p<driver_param>] [-b<br_ifname>] [-I<config file>] "
+ "...]\n"
+ "\n"
+ "drivers:\n",
+ wpa_supplicant_version, wpa_supplicant_license);
+
+ for (i = 0; wpa_drivers[i]; i++) {
+ printf(" %s = %s\n",
+ wpa_drivers[i]->name,
+ wpa_drivers[i]->desc);
+ }
+
+#ifndef CONFIG_NO_STDOUT_DEBUG
+ printf("options:\n"
+ " -b = optional bridge interface name\n"
+ " -B = run daemon in the background\n"
+ " -c = Configuration file\n"
+ " -C = ctrl_interface parameter (only used if -c is not)\n"
+ " -i = interface name\n"
+ " -I = additional configuration file\n"
+ " -d = increase debugging verbosity (-dd even more)\n"
+ " -D = driver name (can be multiple drivers: nl80211,wext)\n"
+ " -e = entropy file\n");
+#ifdef CONFIG_DEBUG_FILE
+ printf(" -f = log output to debug file instead of stdout\n");
+#endif /* CONFIG_DEBUG_FILE */
+ printf(" -g = global ctrl_interface\n"
+ " -G = global ctrl_interface group\n"
+ " -K = include keys (passwords, etc.) in debug output\n");
+#ifdef CONFIG_DEBUG_SYSLOG
+ printf(" -s = log output to syslog instead of stdout\n");
+#endif /* CONFIG_DEBUG_SYSLOG */
+#ifdef CONFIG_DEBUG_LINUX_TRACING
+ printf(" -T = record to Linux tracing in addition to logging\n");
+ printf(" (records all messages regardless of debug verbosity)\n");
+#endif /* CONFIG_DEBUG_LINUX_TRACING */
+ printf(" -t = include timestamp in debug messages\n"
+ " -h = show this help text\n"
+ " -L = show license (BSD)\n"
+ " -o = override driver parameter for new interfaces\n"
+ " -O = override ctrl_interface parameter for new interfaces\n"
+ " -p = driver parameters\n"
+ " -P = PID file\n"
+ " -q = decrease debugging verbosity (-qq even less)\n");
+#ifdef CONFIG_DBUS
+ printf(" -u = enable DBus control interface\n");
+#endif /* CONFIG_DBUS */
+ printf(" -v = show version\n"
+ " -W = wait for a control interface monitor before starting\n"
+#ifdef CONFIG_P2P
+ " -m = Configuration file for the P2P Device interface\n"
+#endif /* CONFIG_P2P */
+ " -N = start describing new interface\n");
+
+ printf("example:\n"
+ " wpa_supplicant -D%s -iwlan0 -c/etc/wpa_supplicant.conf\n",
+ wpa_drivers[0] ? wpa_drivers[0]->name : "nl80211");
+#endif /* CONFIG_NO_STDOUT_DEBUG */
+}
+
+
+static void license(void)
+{
+#ifndef CONFIG_NO_STDOUT_DEBUG
+ printf("%s\n\n%s%s%s%s%s\n",
+ wpa_supplicant_version,
+ wpa_supplicant_full_license1,
+ wpa_supplicant_full_license2,
+ wpa_supplicant_full_license3,
+ wpa_supplicant_full_license4,
+ wpa_supplicant_full_license5);
+#endif /* CONFIG_NO_STDOUT_DEBUG */
+}
+
+
+static void wpa_supplicant_fd_workaround(int start)
+{
+#ifdef __linux__
+ static int fd[3] = { -1, -1, -1 };
+ int i;
+ /* When started from pcmcia-cs scripts, wpa_supplicant might start with
+ * fd 0, 1, and 2 closed. This will cause some issues because many
+ * places in wpa_supplicant are still printing out to stdout. As a
+ * workaround, make sure that fd's 0, 1, and 2 are not used for other
+ * sockets. */
+ if (start) {
+ for (i = 0; i < 3; i++) {
+ fd[i] = open("/dev/null", O_RDWR);
+ if (fd[i] > 2) {
+ close(fd[i]);
+ fd[i] = -1;
+ break;
+ }
+ }
+ } else {
+ for (i = 0; i < 3; i++) {
+ if (fd[i] >= 0) {
+ close(fd[i]);
+ fd[i] = -1;
+ }
+ }
+ }
+#endif /* __linux__ */
+}
+
+
+int main(int argc, char *argv[])
+{
+ int c, i;
+ struct wpa_interface *ifaces, *iface;
+ int iface_count, exitcode = -1;
+ struct wpa_params params;
+ struct wpa_global *global;
+
+ if (os_program_init())
+ return -1;
+
+ os_memset(&params, 0, sizeof(params));
+ params.wpa_debug_level = MSG_INFO;
+
+ iface = ifaces = os_zalloc(sizeof(struct wpa_interface));
+ if (ifaces == NULL)
+ return -1;
+ iface_count = 1;
+
+ wpa_supplicant_fd_workaround(1);
+
+ for (;;) {
+ c = getopt(argc, argv,
+ "b:Bc:C:D:de:f:g:G:hi:I:KLm:No:O:p:P:qsTtuvW");
+ if (c < 0)
+ break;
+ switch (c) {
+ case 'b':
+ iface->bridge_ifname = optarg;
+ break;
+ case 'B':
+ params.daemonize++;
+ break;
+ case 'c':
+ iface->confname = optarg;
+ break;
+ case 'C':
+ iface->ctrl_interface = optarg;
+ break;
+ case 'D':
+ iface->driver = optarg;
+ break;
+ case 'd':
+#ifdef CONFIG_NO_STDOUT_DEBUG
+ printf("Debugging disabled with "
+ "CONFIG_NO_STDOUT_DEBUG=y build time "
+ "option.\n");
+ goto out;
+#else /* CONFIG_NO_STDOUT_DEBUG */
+ params.wpa_debug_level--;
+ break;
+#endif /* CONFIG_NO_STDOUT_DEBUG */
+ case 'e':
+ params.entropy_file = optarg;
+ break;
+#ifdef CONFIG_DEBUG_FILE
+ case 'f':
+ params.wpa_debug_file_path = optarg;
+ break;
+#endif /* CONFIG_DEBUG_FILE */
+ case 'g':
+ params.ctrl_interface = optarg;
+ break;
+ case 'G':
+ params.ctrl_interface_group = optarg;
+ break;
+ case 'h':
+ usage();
+ exitcode = 0;
+ goto out;
+ case 'i':
+ iface->ifname = optarg;
+ break;
+ case 'I':
+ iface->confanother = optarg;
+ break;
+ case 'K':
+ params.wpa_debug_show_keys++;
+ break;
+ case 'L':
+ license();
+ exitcode = 0;
+ goto out;
+#ifdef CONFIG_P2P
+ case 'm':
+ params.conf_p2p_dev = optarg;
+ break;
+#endif /* CONFIG_P2P */
+ case 'o':
+ params.override_driver = optarg;
+ break;
+ case 'O':
+ params.override_ctrl_interface = optarg;
+ break;
+ case 'p':
+ iface->driver_param = optarg;
+ break;
+ case 'P':
+ os_free(params.pid_file);
+ params.pid_file = os_rel2abs_path(optarg);
+ break;
+ case 'q':
+ params.wpa_debug_level++;
+ break;
+#ifdef CONFIG_DEBUG_SYSLOG
+ case 's':
+ params.wpa_debug_syslog++;
+ break;
+#endif /* CONFIG_DEBUG_SYSLOG */
+#ifdef CONFIG_DEBUG_LINUX_TRACING
+ case 'T':
+ params.wpa_debug_tracing++;
+ break;
+#endif /* CONFIG_DEBUG_LINUX_TRACING */
+ case 't':
+ params.wpa_debug_timestamp++;
+ break;
+#ifdef CONFIG_DBUS
+ case 'u':
+ params.dbus_ctrl_interface = 1;
+ break;
+#endif /* CONFIG_DBUS */
+ case 'v':
+ printf("%s\n", wpa_supplicant_version);
+ exitcode = 0;
+ goto out;
+ case 'W':
+ params.wait_for_monitor++;
+ break;
+ case 'N':
+ iface_count++;
+ iface = os_realloc_array(ifaces, iface_count,
+ sizeof(struct wpa_interface));
+ if (iface == NULL)
+ goto out;
+ ifaces = iface;
+ iface = &ifaces[iface_count - 1];
+ os_memset(iface, 0, sizeof(*iface));
+ break;
+ default:
+ usage();
+ exitcode = 0;
+ goto out;
+ }
+ }
+
+ exitcode = 0;
+ global = wpa_supplicant_init(&params);
+ if (global == NULL) {
+ wpa_printf(MSG_ERROR, "Failed to initialize wpa_supplicant");
+ exitcode = -1;
+ goto out;
+ } else {
+ wpa_printf(MSG_INFO, "Successfully initialized "
+ "wpa_supplicant");
+ }
+
+ if (fst_global_init()) {
+ wpa_printf(MSG_ERROR, "Failed to initialize FST");
+ exitcode = -1;
+ goto out;
+ }
+
+#if defined(CONFIG_FST) && defined(CONFIG_CTRL_IFACE)
+ if (!fst_global_add_ctrl(fst_ctrl_cli))
+ wpa_printf(MSG_WARNING, "Failed to add CLI FST ctrl");
+#endif
+
+ for (i = 0; exitcode == 0 && i < iface_count; i++) {
+ struct wpa_supplicant *wpa_s;
+
+ if ((ifaces[i].confname == NULL &&
+ ifaces[i].ctrl_interface == NULL) ||
+ ifaces[i].ifname == NULL) {
+ if (iface_count == 1 && (params.ctrl_interface ||
+ params.dbus_ctrl_interface))
+ break;
+ usage();
+ exitcode = -1;
+ break;
+ }
+ wpa_s = wpa_supplicant_add_iface(global, &ifaces[i], NULL);
+ if (wpa_s == NULL) {
+ exitcode = -1;
+ break;
+ }
+ }
+
+ if (exitcode == 0)
+ exitcode = wpa_supplicant_run(global);
+
+ wpa_supplicant_deinit(global);
+
+ fst_global_deinit();
+
+out:
+ wpa_supplicant_fd_workaround(0);
+ os_free(ifaces);
+ os_free(params.pid_file);
+
+ os_program_deinit();
+
+ return exitcode;
+}
diff --git a/freebsd/contrib/wpa/wpa_supplicant/mesh.h b/freebsd/contrib/wpa/wpa_supplicant/mesh.h
new file mode 100644
index 00000000..3cb7f1b1
--- /dev/null
+++ b/freebsd/contrib/wpa/wpa_supplicant/mesh.h
@@ -0,0 +1,44 @@
+/*
+ * WPA Supplicant - Basic mesh mode routines
+ * Copyright (c) 2013-2014, cozybit, Inc. All rights reserved.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef MESH_H
+#define MESH_H
+
+int wpa_supplicant_join_mesh(struct wpa_supplicant *wpa_s,
+ struct wpa_ssid *ssid);
+int wpa_supplicant_leave_mesh(struct wpa_supplicant *wpa_s);
+void wpa_supplicant_mesh_iface_deinit(struct wpa_supplicant *wpa_s,
+ struct hostapd_iface *ifmsh);
+int wpas_mesh_scan_result_text(const u8 *ies, size_t ies_len, char *buf,
+ char *end);
+int wpas_mesh_add_interface(struct wpa_supplicant *wpa_s, char *ifname,
+ size_t len);
+
+#ifdef CONFIG_MESH
+
+void wpa_mesh_notify_peer(struct wpa_supplicant *wpa_s, const u8 *addr,
+ const u8 *ies, size_t ie_len);
+void wpa_supplicant_mesh_add_scan_ie(struct wpa_supplicant *wpa_s,
+ struct wpabuf **extra_ie);
+
+#else /* CONFIG_MESH */
+
+static inline void wpa_mesh_notify_peer(struct wpa_supplicant *wpa_s,
+ const u8 *addr,
+ const u8 *ies, size_t ie_len)
+{
+}
+
+static inline void wpa_supplicant_mesh_add_scan_ie(struct wpa_supplicant *wpa_s,
+ struct wpabuf **extra_ie)
+{
+}
+
+#endif /* CONFIG_MESH */
+
+#endif /* MESH_H */
diff --git a/freebsd/contrib/wpa/wpa_supplicant/mesh_mpm.h b/freebsd/contrib/wpa/wpa_supplicant/mesh_mpm.h
new file mode 100644
index 00000000..7ebaef0c
--- /dev/null
+++ b/freebsd/contrib/wpa/wpa_supplicant/mesh_mpm.h
@@ -0,0 +1,43 @@
+/*
+ * WPA Supplicant - Basic mesh peer management
+ * Copyright (c) 2013-2014, cozybit, Inc. All rights reserved.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef MESH_MPM_H
+#define MESH_MPM_H
+
+/* notify MPM of new mesh peer to be inserted in MPM and driver */
+void wpa_mesh_new_mesh_peer(struct wpa_supplicant *wpa_s, const u8 *addr,
+ struct ieee802_11_elems *elems);
+void mesh_mpm_deinit(struct wpa_supplicant *wpa_s, struct hostapd_iface *ifmsh);
+void mesh_mpm_auth_peer(struct wpa_supplicant *wpa_s, const u8 *addr);
+void mesh_mpm_free_sta(struct sta_info *sta);
+void wpa_mesh_set_plink_state(struct wpa_supplicant *wpa_s,
+ struct sta_info *sta,
+ enum mesh_plink_state state);
+
+#ifdef CONFIG_MESH
+
+void mesh_mpm_action_rx(struct wpa_supplicant *wpa_s,
+ const struct ieee80211_mgmt *mgmt, size_t len);
+void mesh_mpm_mgmt_rx(struct wpa_supplicant *wpa_s, struct rx_mgmt *rx_mgmt);
+
+#else /* CONFIG_MESH */
+
+static inline void mesh_mpm_action_rx(struct wpa_supplicant *wpa_s,
+ const struct ieee80211_mgmt *mgmt,
+ size_t len)
+{
+}
+
+static inline void mesh_mpm_mgmt_rx(struct wpa_supplicant *wpa_s,
+ struct rx_mgmt *rx_mgmt)
+{
+}
+
+#endif /* CONFIG_MESH */
+
+#endif /* MESH_MPM_H */
diff --git a/freebsd/contrib/wpa/wpa_supplicant/mesh_rsn.h b/freebsd/contrib/wpa/wpa_supplicant/mesh_rsn.h
new file mode 100644
index 00000000..b1471b2d
--- /dev/null
+++ b/freebsd/contrib/wpa/wpa_supplicant/mesh_rsn.h
@@ -0,0 +1,36 @@
+/*
+ * WPA Supplicant - Mesh RSN routines
+ * Copyright (c) 2013-2014, cozybit, Inc. All rights reserved.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef MESH_RSN_H
+#define MESH_RSN_H
+
+struct mesh_rsn {
+ struct wpa_supplicant *wpa_s;
+ struct wpa_authenticator *auth;
+ u8 mgtk[16];
+#ifdef CONFIG_SAE
+ struct wpabuf *sae_token;
+ int sae_group_index;
+#endif /* CONFIG_SAE */
+};
+
+struct mesh_rsn * mesh_rsn_auth_init(struct wpa_supplicant *wpa_s,
+ struct mesh_conf *conf);
+int mesh_rsn_auth_sae_sta(struct wpa_supplicant *wpa_s, struct sta_info *sta);
+int mesh_rsn_derive_mtk(struct wpa_supplicant *wpa_s, struct sta_info *sta);
+void mesh_rsn_get_pmkid(struct mesh_rsn *rsn, struct sta_info *sta, u8 *pmkid);
+void mesh_rsn_init_ampe_sta(struct wpa_supplicant *wpa_s,
+ struct sta_info *sta);
+int mesh_rsn_protect_frame(struct mesh_rsn *rsn, struct sta_info *sta,
+ const u8 *cat, struct wpabuf *buf);
+int mesh_rsn_process_ampe(struct wpa_supplicant *wpa_s, struct sta_info *sta,
+ struct ieee802_11_elems *elems, const u8 *cat,
+ const u8 *start, size_t elems_len);
+void mesh_auth_timer(void *eloop_ctx, void *user_data);
+
+#endif /* MESH_RSN_H */
diff --git a/freebsd/contrib/wpa/wpa_supplicant/notify.c b/freebsd/contrib/wpa/wpa_supplicant/notify.c
new file mode 100644
index 00000000..1b41f147
--- /dev/null
+++ b/freebsd/contrib/wpa/wpa_supplicant/notify.c
@@ -0,0 +1,833 @@
+#include <machine/rtems-bsd-user-space.h>
+
+/*
+ * wpa_supplicant - Event notifications
+ * Copyright (c) 2009-2010, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+
+#include "utils/common.h"
+#include "common/wpa_ctrl.h"
+#include "config.h"
+#include "wpa_supplicant_i.h"
+#include "wps_supplicant.h"
+#include "dbus/dbus_common.h"
+#include "dbus/dbus_old.h"
+#include "dbus/dbus_new.h"
+#include "rsn_supp/wpa.h"
+#include "fst/fst.h"
+#include "driver_i.h"
+#include "scan.h"
+#include "p2p_supplicant.h"
+#include "sme.h"
+#include "notify.h"
+
+int wpas_notify_supplicant_initialized(struct wpa_global *global)
+{
+#ifdef CONFIG_DBUS
+ if (global->params.dbus_ctrl_interface) {
+ global->dbus = wpas_dbus_init(global);
+ if (global->dbus == NULL)
+ return -1;
+ }
+#endif /* CONFIG_DBUS */
+
+ return 0;
+}
+
+
+void wpas_notify_supplicant_deinitialized(struct wpa_global *global)
+{
+#ifdef CONFIG_DBUS
+ if (global->dbus)
+ wpas_dbus_deinit(global->dbus);
+#endif /* CONFIG_DBUS */
+}
+
+
+int wpas_notify_iface_added(struct wpa_supplicant *wpa_s)
+{
+ if (wpa_s->p2p_mgmt)
+ return 0;
+
+ if (wpas_dbus_register_iface(wpa_s))
+ return -1;
+
+ if (wpas_dbus_register_interface(wpa_s))
+ return -1;
+
+ return 0;
+}
+
+
+void wpas_notify_iface_removed(struct wpa_supplicant *wpa_s)
+{
+ if (wpa_s->p2p_mgmt)
+ return;
+
+ /* unregister interface in old DBus ctrl iface */
+ wpas_dbus_unregister_iface(wpa_s);
+
+ /* unregister interface in new DBus ctrl iface */
+ wpas_dbus_unregister_interface(wpa_s);
+}
+
+
+void wpas_notify_state_changed(struct wpa_supplicant *wpa_s,
+ enum wpa_states new_state,
+ enum wpa_states old_state)
+{
+ if (wpa_s->p2p_mgmt)
+ return;
+
+ /* notify the old DBus API */
+ wpa_supplicant_dbus_notify_state_change(wpa_s, new_state,
+ old_state);
+
+ /* notify the new DBus API */
+ wpas_dbus_signal_prop_changed(wpa_s, WPAS_DBUS_PROP_STATE);
+
+#ifdef CONFIG_FST
+ if (wpa_s->fst && !is_zero_ether_addr(wpa_s->bssid)) {
+ if (new_state == WPA_COMPLETED)
+ fst_notify_peer_connected(wpa_s->fst, wpa_s->bssid);
+ else if (old_state >= WPA_ASSOCIATED &&
+ new_state < WPA_ASSOCIATED)
+ fst_notify_peer_disconnected(wpa_s->fst, wpa_s->bssid);
+ }
+#endif /* CONFIG_FST */
+
+ if (new_state == WPA_COMPLETED)
+ wpas_p2p_notif_connected(wpa_s);
+ else if (old_state >= WPA_ASSOCIATED && new_state < WPA_ASSOCIATED)
+ wpas_p2p_notif_disconnected(wpa_s);
+
+ sme_state_changed(wpa_s);
+
+#ifdef ANDROID
+ wpa_msg_ctrl(wpa_s, MSG_INFO, WPA_EVENT_STATE_CHANGE
+ "id=%d state=%d BSSID=" MACSTR " SSID=%s",
+ wpa_s->current_ssid ? wpa_s->current_ssid->id : -1,
+ new_state,
+ MAC2STR(wpa_s->bssid),
+ wpa_s->current_ssid && wpa_s->current_ssid->ssid ?
+ wpa_ssid_txt(wpa_s->current_ssid->ssid,
+ wpa_s->current_ssid->ssid_len) : "");
+#endif /* ANDROID */
+}
+
+
+void wpas_notify_disconnect_reason(struct wpa_supplicant *wpa_s)
+{
+ if (wpa_s->p2p_mgmt)
+ return;
+
+ wpas_dbus_signal_prop_changed(wpa_s, WPAS_DBUS_PROP_DISCONNECT_REASON);
+}
+
+
+void wpas_notify_network_changed(struct wpa_supplicant *wpa_s)
+{
+ if (wpa_s->p2p_mgmt)
+ return;
+
+ wpas_dbus_signal_prop_changed(wpa_s, WPAS_DBUS_PROP_CURRENT_NETWORK);
+}
+
+
+void wpas_notify_ap_scan_changed(struct wpa_supplicant *wpa_s)
+{
+ if (wpa_s->p2p_mgmt)
+ return;
+
+ wpas_dbus_signal_prop_changed(wpa_s, WPAS_DBUS_PROP_AP_SCAN);
+}
+
+
+void wpas_notify_bssid_changed(struct wpa_supplicant *wpa_s)
+{
+ if (wpa_s->p2p_mgmt)
+ return;
+
+ wpas_dbus_signal_prop_changed(wpa_s, WPAS_DBUS_PROP_CURRENT_BSS);
+}
+
+
+void wpas_notify_auth_changed(struct wpa_supplicant *wpa_s)
+{
+ if (wpa_s->p2p_mgmt)
+ return;
+
+ wpas_dbus_signal_prop_changed(wpa_s, WPAS_DBUS_PROP_CURRENT_AUTH_MODE);
+}
+
+
+void wpas_notify_network_enabled_changed(struct wpa_supplicant *wpa_s,
+ struct wpa_ssid *ssid)
+{
+ if (wpa_s->p2p_mgmt)
+ return;
+
+ wpas_dbus_signal_network_enabled_changed(wpa_s, ssid);
+}
+
+
+void wpas_notify_network_selected(struct wpa_supplicant *wpa_s,
+ struct wpa_ssid *ssid)
+{
+ if (wpa_s->p2p_mgmt)
+ return;
+
+ wpas_dbus_signal_network_selected(wpa_s, ssid->id);
+}
+
+
+void wpas_notify_network_request(struct wpa_supplicant *wpa_s,
+ struct wpa_ssid *ssid,
+ enum wpa_ctrl_req_type rtype,
+ const char *default_txt)
+{
+ if (wpa_s->p2p_mgmt)
+ return;
+
+ wpas_dbus_signal_network_request(wpa_s, ssid, rtype, default_txt);
+}
+
+
+void wpas_notify_scanning(struct wpa_supplicant *wpa_s)
+{
+ if (wpa_s->p2p_mgmt)
+ return;
+
+ /* notify the old DBus API */
+ wpa_supplicant_dbus_notify_scanning(wpa_s);
+
+ /* notify the new DBus API */
+ wpas_dbus_signal_prop_changed(wpa_s, WPAS_DBUS_PROP_SCANNING);
+}
+
+
+void wpas_notify_scan_done(struct wpa_supplicant *wpa_s, int success)
+{
+ if (wpa_s->p2p_mgmt)
+ return;
+
+ wpas_dbus_signal_scan_done(wpa_s, success);
+}
+
+
+void wpas_notify_scan_results(struct wpa_supplicant *wpa_s)
+{
+ if (wpa_s->p2p_mgmt)
+ return;
+
+ /* notify the old DBus API */
+ wpa_supplicant_dbus_notify_scan_results(wpa_s);
+
+ wpas_wps_notify_scan_results(wpa_s);
+}
+
+
+void wpas_notify_wps_credential(struct wpa_supplicant *wpa_s,
+ const struct wps_credential *cred)
+{
+ if (wpa_s->p2p_mgmt)
+ return;
+
+#ifdef CONFIG_WPS
+ /* notify the old DBus API */
+ wpa_supplicant_dbus_notify_wps_cred(wpa_s, cred);
+ /* notify the new DBus API */
+ wpas_dbus_signal_wps_cred(wpa_s, cred);
+#endif /* CONFIG_WPS */
+}
+
+
+void wpas_notify_wps_event_m2d(struct wpa_supplicant *wpa_s,
+ struct wps_event_m2d *m2d)
+{
+ if (wpa_s->p2p_mgmt)
+ return;
+
+#ifdef CONFIG_WPS
+ wpas_dbus_signal_wps_event_m2d(wpa_s, m2d);
+#endif /* CONFIG_WPS */
+}
+
+
+void wpas_notify_wps_event_fail(struct wpa_supplicant *wpa_s,
+ struct wps_event_fail *fail)
+{
+ if (wpa_s->p2p_mgmt)
+ return;
+
+#ifdef CONFIG_WPS
+ wpas_dbus_signal_wps_event_fail(wpa_s, fail);
+#endif /* CONFIG_WPS */
+}
+
+
+void wpas_notify_wps_event_success(struct wpa_supplicant *wpa_s)
+{
+ if (wpa_s->p2p_mgmt)
+ return;
+
+#ifdef CONFIG_WPS
+ wpas_dbus_signal_wps_event_success(wpa_s);
+#endif /* CONFIG_WPS */
+}
+
+void wpas_notify_wps_event_pbc_overlap(struct wpa_supplicant *wpa_s)
+{
+ if (wpa_s->p2p_mgmt)
+ return;
+
+#ifdef CONFIG_WPS
+ wpas_dbus_signal_wps_event_pbc_overlap(wpa_s);
+#endif /* CONFIG_WPS */
+}
+
+
+void wpas_notify_network_added(struct wpa_supplicant *wpa_s,
+ struct wpa_ssid *ssid)
+{
+ if (wpa_s->p2p_mgmt)
+ return;
+
+ /*
+ * Networks objects created during any P2P activities should not be
+ * exposed out. They might/will confuse certain non-P2P aware
+ * applications since these network objects won't behave like
+ * regular ones.
+ */
+ if (!ssid->p2p_group && wpa_s->global->p2p_group_formation != wpa_s)
+ wpas_dbus_register_network(wpa_s, ssid);
+}
+
+
+void wpas_notify_persistent_group_added(struct wpa_supplicant *wpa_s,
+ struct wpa_ssid *ssid)
+{
+#ifdef CONFIG_P2P
+ wpas_dbus_register_persistent_group(wpa_s, ssid);
+#endif /* CONFIG_P2P */
+}
+
+
+void wpas_notify_persistent_group_removed(struct wpa_supplicant *wpa_s,
+ struct wpa_ssid *ssid)
+{
+#ifdef CONFIG_P2P
+ wpas_dbus_unregister_persistent_group(wpa_s, ssid->id);
+#endif /* CONFIG_P2P */
+}
+
+
+void wpas_notify_network_removed(struct wpa_supplicant *wpa_s,
+ struct wpa_ssid *ssid)
+{
+ if (wpa_s->next_ssid == ssid)
+ wpa_s->next_ssid = NULL;
+ if (wpa_s->wpa)
+ wpa_sm_pmksa_cache_flush(wpa_s->wpa, ssid);
+ if (!ssid->p2p_group && wpa_s->global->p2p_group_formation != wpa_s &&
+ !wpa_s->p2p_mgmt)
+ wpas_dbus_unregister_network(wpa_s, ssid->id);
+ if (network_is_persistent_group(ssid))
+ wpas_notify_persistent_group_removed(wpa_s, ssid);
+
+ wpas_p2p_network_removed(wpa_s, ssid);
+}
+
+
+void wpas_notify_bss_added(struct wpa_supplicant *wpa_s,
+ u8 bssid[], unsigned int id)
+{
+ if (wpa_s->p2p_mgmt)
+ return;
+
+ wpas_dbus_register_bss(wpa_s, bssid, id);
+ wpa_msg_ctrl(wpa_s, MSG_INFO, WPA_EVENT_BSS_ADDED "%u " MACSTR,
+ id, MAC2STR(bssid));
+}
+
+
+void wpas_notify_bss_removed(struct wpa_supplicant *wpa_s,
+ u8 bssid[], unsigned int id)
+{
+ if (wpa_s->p2p_mgmt)
+ return;
+
+ wpas_dbus_unregister_bss(wpa_s, bssid, id);
+ wpa_msg_ctrl(wpa_s, MSG_INFO, WPA_EVENT_BSS_REMOVED "%u " MACSTR,
+ id, MAC2STR(bssid));
+}
+
+
+void wpas_notify_bss_freq_changed(struct wpa_supplicant *wpa_s,
+ unsigned int id)
+{
+ if (wpa_s->p2p_mgmt)
+ return;
+
+ wpas_dbus_bss_signal_prop_changed(wpa_s, WPAS_DBUS_BSS_PROP_FREQ, id);
+}
+
+
+void wpas_notify_bss_signal_changed(struct wpa_supplicant *wpa_s,
+ unsigned int id)
+{
+ if (wpa_s->p2p_mgmt)
+ return;
+
+ wpas_dbus_bss_signal_prop_changed(wpa_s, WPAS_DBUS_BSS_PROP_SIGNAL,
+ id);
+}
+
+
+void wpas_notify_bss_privacy_changed(struct wpa_supplicant *wpa_s,
+ unsigned int id)
+{
+ if (wpa_s->p2p_mgmt)
+ return;
+
+ wpas_dbus_bss_signal_prop_changed(wpa_s, WPAS_DBUS_BSS_PROP_PRIVACY,
+ id);
+}
+
+
+void wpas_notify_bss_mode_changed(struct wpa_supplicant *wpa_s,
+ unsigned int id)
+{
+ if (wpa_s->p2p_mgmt)
+ return;
+
+ wpas_dbus_bss_signal_prop_changed(wpa_s, WPAS_DBUS_BSS_PROP_MODE, id);
+}
+
+
+void wpas_notify_bss_wpaie_changed(struct wpa_supplicant *wpa_s,
+ unsigned int id)
+{
+ if (wpa_s->p2p_mgmt)
+ return;
+
+ wpas_dbus_bss_signal_prop_changed(wpa_s, WPAS_DBUS_BSS_PROP_WPA, id);
+}
+
+
+void wpas_notify_bss_rsnie_changed(struct wpa_supplicant *wpa_s,
+ unsigned int id)
+{
+ if (wpa_s->p2p_mgmt)
+ return;
+
+ wpas_dbus_bss_signal_prop_changed(wpa_s, WPAS_DBUS_BSS_PROP_RSN, id);
+}
+
+
+void wpas_notify_bss_wps_changed(struct wpa_supplicant *wpa_s,
+ unsigned int id)
+{
+ if (wpa_s->p2p_mgmt)
+ return;
+
+#ifdef CONFIG_WPS
+ wpas_dbus_bss_signal_prop_changed(wpa_s, WPAS_DBUS_BSS_PROP_WPS, id);
+#endif /* CONFIG_WPS */
+}
+
+
+void wpas_notify_bss_ies_changed(struct wpa_supplicant *wpa_s,
+ unsigned int id)
+{
+ if (wpa_s->p2p_mgmt)
+ return;
+
+ wpas_dbus_bss_signal_prop_changed(wpa_s, WPAS_DBUS_BSS_PROP_IES, id);
+}
+
+
+void wpas_notify_bss_rates_changed(struct wpa_supplicant *wpa_s,
+ unsigned int id)
+{
+ if (wpa_s->p2p_mgmt)
+ return;
+
+ wpas_dbus_bss_signal_prop_changed(wpa_s, WPAS_DBUS_BSS_PROP_RATES, id);
+}
+
+
+void wpas_notify_bss_seen(struct wpa_supplicant *wpa_s, unsigned int id)
+{
+ if (wpa_s->p2p_mgmt)
+ return;
+
+ wpas_dbus_bss_signal_prop_changed(wpa_s, WPAS_DBUS_BSS_PROP_AGE, id);
+}
+
+
+void wpas_notify_blob_added(struct wpa_supplicant *wpa_s, const char *name)
+{
+ if (wpa_s->p2p_mgmt)
+ return;
+
+ wpas_dbus_signal_blob_added(wpa_s, name);
+}
+
+
+void wpas_notify_blob_removed(struct wpa_supplicant *wpa_s, const char *name)
+{
+ if (wpa_s->p2p_mgmt)
+ return;
+
+ wpas_dbus_signal_blob_removed(wpa_s, name);
+}
+
+
+void wpas_notify_debug_level_changed(struct wpa_global *global)
+{
+ wpas_dbus_signal_debug_level_changed(global);
+}
+
+
+void wpas_notify_debug_timestamp_changed(struct wpa_global *global)
+{
+ wpas_dbus_signal_debug_timestamp_changed(global);
+}
+
+
+void wpas_notify_debug_show_keys_changed(struct wpa_global *global)
+{
+ wpas_dbus_signal_debug_show_keys_changed(global);
+}
+
+
+void wpas_notify_suspend(struct wpa_global *global)
+{
+ struct wpa_supplicant *wpa_s;
+
+ os_get_time(&global->suspend_time);
+ wpa_printf(MSG_DEBUG, "System suspend notification");
+ for (wpa_s = global->ifaces; wpa_s; wpa_s = wpa_s->next)
+ wpa_drv_suspend(wpa_s);
+}
+
+
+void wpas_notify_resume(struct wpa_global *global)
+{
+ struct os_time now;
+ int slept;
+ struct wpa_supplicant *wpa_s;
+
+ if (global->suspend_time.sec == 0)
+ slept = -1;
+ else {
+ os_get_time(&now);
+ slept = now.sec - global->suspend_time.sec;
+ }
+ wpa_printf(MSG_DEBUG, "System resume notification (slept %d seconds)",
+ slept);
+
+ for (wpa_s = global->ifaces; wpa_s; wpa_s = wpa_s->next) {
+ wpa_drv_resume(wpa_s);
+ if (wpa_s->wpa_state == WPA_DISCONNECTED)
+ wpa_supplicant_req_scan(wpa_s, 0, 100000);
+ }
+}
+
+
+#ifdef CONFIG_P2P
+
+void wpas_notify_p2p_find_stopped(struct wpa_supplicant *wpa_s)
+{
+ /* Notify P2P find has stopped */
+ wpas_dbus_signal_p2p_find_stopped(wpa_s);
+}
+
+
+void wpas_notify_p2p_device_found(struct wpa_supplicant *wpa_s,
+ const u8 *dev_addr, int new_device)
+{
+ if (new_device) {
+ /* Create the new peer object */
+ wpas_dbus_register_peer(wpa_s, dev_addr);
+ }
+
+ /* Notify a new peer has been detected*/
+ wpas_dbus_signal_peer_device_found(wpa_s, dev_addr);
+}
+
+
+void wpas_notify_p2p_device_lost(struct wpa_supplicant *wpa_s,
+ const u8 *dev_addr)
+{
+ wpas_dbus_unregister_peer(wpa_s, dev_addr);
+
+ /* Create signal on interface object*/
+ wpas_dbus_signal_peer_device_lost(wpa_s, dev_addr);
+}
+
+
+void wpas_notify_p2p_group_removed(struct wpa_supplicant *wpa_s,
+ const struct wpa_ssid *ssid,
+ const char *role)
+{
+ wpas_dbus_signal_p2p_group_removed(wpa_s, role);
+
+ wpas_dbus_unregister_p2p_group(wpa_s, ssid);
+}
+
+
+void wpas_notify_p2p_go_neg_req(struct wpa_supplicant *wpa_s,
+ const u8 *src, u16 dev_passwd_id, u8 go_intent)
+{
+ wpas_dbus_signal_p2p_go_neg_req(wpa_s, src, dev_passwd_id, go_intent);
+}
+
+
+void wpas_notify_p2p_go_neg_completed(struct wpa_supplicant *wpa_s,
+ struct p2p_go_neg_results *res)
+{
+ wpas_dbus_signal_p2p_go_neg_resp(wpa_s, res);
+}
+
+
+void wpas_notify_p2p_invitation_result(struct wpa_supplicant *wpa_s,
+ int status, const u8 *bssid)
+{
+ wpas_dbus_signal_p2p_invitation_result(wpa_s, status, bssid);
+}
+
+
+void wpas_notify_p2p_sd_request(struct wpa_supplicant *wpa_s,
+ int freq, const u8 *sa, u8 dialog_token,
+ u16 update_indic, const u8 *tlvs,
+ size_t tlvs_len)
+{
+ wpas_dbus_signal_p2p_sd_request(wpa_s, freq, sa, dialog_token,
+ update_indic, tlvs, tlvs_len);
+}
+
+
+void wpas_notify_p2p_sd_response(struct wpa_supplicant *wpa_s,
+ const u8 *sa, u16 update_indic,
+ const u8 *tlvs, size_t tlvs_len)
+{
+ wpas_dbus_signal_p2p_sd_response(wpa_s, sa, update_indic,
+ tlvs, tlvs_len);
+}
+
+
+/**
+ * wpas_notify_p2p_provision_discovery - Notification of provision discovery
+ * @dev_addr: Who sent the request or responded to our request.
+ * @request: Will be 1 if request, 0 for response.
+ * @status: Valid only in case of response (0 in case of success)
+ * @config_methods: WPS config methods
+ * @generated_pin: PIN to be displayed in case of WPS_CONFIG_DISPLAY method
+ *
+ * This can be used to notify:
+ * - Requests or responses
+ * - Various config methods
+ * - Failure condition in case of response
+ */
+void wpas_notify_p2p_provision_discovery(struct wpa_supplicant *wpa_s,
+ const u8 *dev_addr, int request,
+ enum p2p_prov_disc_status status,
+ u16 config_methods,
+ unsigned int generated_pin)
+{
+ wpas_dbus_signal_p2p_provision_discovery(wpa_s, dev_addr, request,
+ status, config_methods,
+ generated_pin);
+}
+
+
+void wpas_notify_p2p_group_started(struct wpa_supplicant *wpa_s,
+ struct wpa_ssid *ssid, int network_id,
+ int client)
+{
+ /* Notify a group has been started */
+ wpas_dbus_register_p2p_group(wpa_s, ssid);
+
+ wpas_dbus_signal_p2p_group_started(wpa_s, ssid, client, network_id);
+}
+
+
+void wpas_notify_p2p_group_formation_failure(struct wpa_supplicant *wpa_s,
+ const char *reason)
+{
+ /* Notify a group formation failed */
+ wpas_dbus_signal_p2p_group_formation_failure(wpa_s, reason);
+}
+
+
+void wpas_notify_p2p_wps_failed(struct wpa_supplicant *wpa_s,
+ struct wps_event_fail *fail)
+{
+ wpas_dbus_signal_p2p_wps_failed(wpa_s, fail);
+}
+
+
+void wpas_notify_p2p_invitation_received(struct wpa_supplicant *wpa_s,
+ const u8 *sa, const u8 *go_dev_addr,
+ const u8 *bssid, int id, int op_freq)
+{
+ /* Notify a P2P Invitation Request */
+ wpas_dbus_signal_p2p_invitation_received(wpa_s, sa, go_dev_addr, bssid,
+ id, op_freq);
+}
+
+#endif /* CONFIG_P2P */
+
+
+static void wpas_notify_ap_sta_authorized(struct wpa_supplicant *wpa_s,
+ const u8 *sta,
+ const u8 *p2p_dev_addr)
+{
+#ifdef CONFIG_P2P
+ wpas_p2p_notify_ap_sta_authorized(wpa_s, p2p_dev_addr);
+
+ /*
+ * Create 'peer-joined' signal on group object -- will also
+ * check P2P itself.
+ */
+ if (p2p_dev_addr)
+ wpas_dbus_signal_p2p_peer_joined(wpa_s, p2p_dev_addr);
+#endif /* CONFIG_P2P */
+
+ /* Notify listeners a new station has been authorized */
+ wpas_dbus_signal_sta_authorized(wpa_s, sta);
+}
+
+
+static void wpas_notify_ap_sta_deauthorized(struct wpa_supplicant *wpa_s,
+ const u8 *sta,
+ const u8 *p2p_dev_addr)
+{
+#ifdef CONFIG_P2P
+ /*
+ * Create 'peer-disconnected' signal on group object if this
+ * is a P2P group.
+ */
+ if (p2p_dev_addr)
+ wpas_dbus_signal_p2p_peer_disconnected(wpa_s, p2p_dev_addr);
+#endif /* CONFIG_P2P */
+
+ /* Notify listeners a station has been deauthorized */
+ wpas_dbus_signal_sta_deauthorized(wpa_s, sta);
+}
+
+
+void wpas_notify_sta_authorized(struct wpa_supplicant *wpa_s,
+ const u8 *mac_addr, int authorized,
+ const u8 *p2p_dev_addr)
+{
+ if (authorized)
+ wpas_notify_ap_sta_authorized(wpa_s, mac_addr, p2p_dev_addr);
+ else
+ wpas_notify_ap_sta_deauthorized(wpa_s, mac_addr, p2p_dev_addr);
+}
+
+
+void wpas_notify_certification(struct wpa_supplicant *wpa_s, int depth,
+ const char *subject, const char *altsubject[],
+ int num_altsubject, const char *cert_hash,
+ const struct wpabuf *cert)
+{
+ wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_EAP_PEER_CERT
+ "depth=%d subject='%s'%s%s",
+ depth, subject, cert_hash ? " hash=" : "",
+ cert_hash ? cert_hash : "");
+
+ if (cert) {
+ char *cert_hex;
+ size_t len = wpabuf_len(cert) * 2 + 1;
+ cert_hex = os_malloc(len);
+ if (cert_hex) {
+ wpa_snprintf_hex(cert_hex, len, wpabuf_head(cert),
+ wpabuf_len(cert));
+ wpa_msg_ctrl(wpa_s, MSG_INFO,
+ WPA_EVENT_EAP_PEER_CERT
+ "depth=%d subject='%s' cert=%s",
+ depth, subject, cert_hex);
+ os_free(cert_hex);
+ }
+ }
+
+ if (altsubject) {
+ int i;
+
+ for (i = 0; i < num_altsubject; i++)
+ wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_EAP_PEER_ALT
+ "depth=%d %s", depth, altsubject[i]);
+ }
+
+ /* notify the old DBus API */
+ wpa_supplicant_dbus_notify_certification(wpa_s, depth, subject,
+ cert_hash, cert);
+ /* notify the new DBus API */
+ wpas_dbus_signal_certification(wpa_s, depth, subject, altsubject,
+ num_altsubject, cert_hash, cert);
+}
+
+
+void wpas_notify_preq(struct wpa_supplicant *wpa_s,
+ const u8 *addr, const u8 *dst, const u8 *bssid,
+ const u8 *ie, size_t ie_len, u32 ssi_signal)
+{
+#ifdef CONFIG_AP
+ wpas_dbus_signal_preq(wpa_s, addr, dst, bssid, ie, ie_len, ssi_signal);
+#endif /* CONFIG_AP */
+}
+
+
+void wpas_notify_eap_status(struct wpa_supplicant *wpa_s, const char *status,
+ const char *parameter)
+{
+ wpas_dbus_signal_eap_status(wpa_s, status, parameter);
+ wpa_msg_ctrl(wpa_s, MSG_INFO, WPA_EVENT_EAP_STATUS
+ "status='%s' parameter='%s'",
+ status, parameter);
+}
+
+
+void wpas_notify_network_bssid_set_changed(struct wpa_supplicant *wpa_s,
+ struct wpa_ssid *ssid)
+{
+ if (wpa_s->current_ssid != ssid)
+ return;
+
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ "Network bssid config changed for the current network - within-ESS roaming %s",
+ ssid->bssid_set ? "disabled" : "enabled");
+
+ wpa_drv_roaming(wpa_s, !ssid->bssid_set,
+ ssid->bssid_set ? ssid->bssid : NULL);
+}
+
+
+void wpas_notify_network_type_changed(struct wpa_supplicant *wpa_s,
+ struct wpa_ssid *ssid)
+{
+#ifdef CONFIG_P2P
+ if (ssid->disabled == 2) {
+ /* Changed from normal network profile to persistent group */
+ ssid->disabled = 0;
+ wpas_dbus_unregister_network(wpa_s, ssid->id);
+ ssid->disabled = 2;
+ ssid->p2p_persistent_group = 1;
+ wpas_dbus_register_persistent_group(wpa_s, ssid);
+ } else {
+ /* Changed from persistent group to normal network profile */
+ wpas_dbus_unregister_persistent_group(wpa_s, ssid->id);
+ ssid->p2p_persistent_group = 0;
+ wpas_dbus_register_network(wpa_s, ssid);
+ }
+#endif /* CONFIG_P2P */
+}
diff --git a/freebsd/contrib/wpa/wpa_supplicant/notify.h b/freebsd/contrib/wpa/wpa_supplicant/notify.h
new file mode 100644
index 00000000..d9f0f5a9
--- /dev/null
+++ b/freebsd/contrib/wpa/wpa_supplicant/notify.h
@@ -0,0 +1,144 @@
+/*
+ * wpa_supplicant - Event notifications
+ * Copyright (c) 2009-2010, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef NOTIFY_H
+#define NOTIFY_H
+
+#include "p2p/p2p.h"
+
+struct wps_credential;
+struct wps_event_m2d;
+struct wps_event_fail;
+
+int wpas_notify_supplicant_initialized(struct wpa_global *global);
+void wpas_notify_supplicant_deinitialized(struct wpa_global *global);
+int wpas_notify_iface_added(struct wpa_supplicant *wpa_s);
+void wpas_notify_iface_removed(struct wpa_supplicant *wpa_s);
+void wpas_notify_state_changed(struct wpa_supplicant *wpa_s,
+ enum wpa_states new_state,
+ enum wpa_states old_state);
+void wpas_notify_disconnect_reason(struct wpa_supplicant *wpa_s);
+void wpas_notify_network_changed(struct wpa_supplicant *wpa_s);
+void wpas_notify_ap_scan_changed(struct wpa_supplicant *wpa_s);
+void wpas_notify_bssid_changed(struct wpa_supplicant *wpa_s);
+void wpas_notify_auth_changed(struct wpa_supplicant *wpa_s);
+void wpas_notify_network_enabled_changed(struct wpa_supplicant *wpa_s,
+ struct wpa_ssid *ssid);
+void wpas_notify_network_selected(struct wpa_supplicant *wpa_s,
+ struct wpa_ssid *ssid);
+void wpas_notify_network_request(struct wpa_supplicant *wpa_s,
+ struct wpa_ssid *ssid,
+ enum wpa_ctrl_req_type rtype,
+ const char *default_txt);
+void wpas_notify_scanning(struct wpa_supplicant *wpa_s);
+void wpas_notify_scan_done(struct wpa_supplicant *wpa_s, int success);
+void wpas_notify_scan_results(struct wpa_supplicant *wpa_s);
+void wpas_notify_wps_credential(struct wpa_supplicant *wpa_s,
+ const struct wps_credential *cred);
+void wpas_notify_wps_event_m2d(struct wpa_supplicant *wpa_s,
+ struct wps_event_m2d *m2d);
+void wpas_notify_wps_event_fail(struct wpa_supplicant *wpa_s,
+ struct wps_event_fail *fail);
+void wpas_notify_wps_event_success(struct wpa_supplicant *wpa_s);
+void wpas_notify_wps_event_pbc_overlap(struct wpa_supplicant *wpa_s);
+void wpas_notify_network_added(struct wpa_supplicant *wpa_s,
+ struct wpa_ssid *ssid);
+void wpas_notify_network_removed(struct wpa_supplicant *wpa_s,
+ struct wpa_ssid *ssid);
+void wpas_notify_bss_added(struct wpa_supplicant *wpa_s, u8 bssid[],
+ unsigned int id);
+void wpas_notify_bss_removed(struct wpa_supplicant *wpa_s, u8 bssid[],
+ unsigned int id);
+void wpas_notify_bss_freq_changed(struct wpa_supplicant *wpa_s,
+ unsigned int id);
+void wpas_notify_bss_signal_changed(struct wpa_supplicant *wpa_s,
+ unsigned int id);
+void wpas_notify_bss_privacy_changed(struct wpa_supplicant *wpa_s,
+ unsigned int id);
+void wpas_notify_bss_mode_changed(struct wpa_supplicant *wpa_s,
+ unsigned int id);
+void wpas_notify_bss_wpaie_changed(struct wpa_supplicant *wpa_s,
+ unsigned int id);
+void wpas_notify_bss_rsnie_changed(struct wpa_supplicant *wpa_s,
+ unsigned int id);
+void wpas_notify_bss_wps_changed(struct wpa_supplicant *wpa_s,
+ unsigned int id);
+void wpas_notify_bss_ies_changed(struct wpa_supplicant *wpa_s,
+ unsigned int id);
+void wpas_notify_bss_rates_changed(struct wpa_supplicant *wpa_s,
+ unsigned int id);
+void wpas_notify_bss_seen(struct wpa_supplicant *wpa_s, unsigned int id);
+void wpas_notify_blob_added(struct wpa_supplicant *wpa_s, const char *name);
+void wpas_notify_blob_removed(struct wpa_supplicant *wpa_s, const char *name);
+
+void wpas_notify_debug_level_changed(struct wpa_global *global);
+void wpas_notify_debug_timestamp_changed(struct wpa_global *global);
+void wpas_notify_debug_show_keys_changed(struct wpa_global *global);
+void wpas_notify_suspend(struct wpa_global *global);
+void wpas_notify_resume(struct wpa_global *global);
+
+void wpas_notify_sta_authorized(struct wpa_supplicant *wpa_s,
+ const u8 *mac_addr, int authorized,
+ const u8 *p2p_dev_addr);
+void wpas_notify_p2p_find_stopped(struct wpa_supplicant *wpa_s);
+void wpas_notify_p2p_device_found(struct wpa_supplicant *wpa_s,
+ const u8 *dev_addr, int new_device);
+void wpas_notify_p2p_device_lost(struct wpa_supplicant *wpa_s,
+ const u8 *dev_addr);
+void wpas_notify_p2p_group_removed(struct wpa_supplicant *wpa_s,
+ const struct wpa_ssid *ssid,
+ const char *role);
+void wpas_notify_p2p_go_neg_req(struct wpa_supplicant *wpa_s,
+ const u8 *src, u16 dev_passwd_id, u8 go_intent);
+void wpas_notify_p2p_go_neg_completed(struct wpa_supplicant *wpa_s,
+ struct p2p_go_neg_results *res);
+void wpas_notify_p2p_invitation_result(struct wpa_supplicant *wpa_s,
+ int status, const u8 *bssid);
+void wpas_notify_p2p_sd_request(struct wpa_supplicant *wpa_s,
+ int freq, const u8 *sa, u8 dialog_token,
+ u16 update_indic, const u8 *tlvs,
+ size_t tlvs_len);
+void wpas_notify_p2p_sd_response(struct wpa_supplicant *wpa_s,
+ const u8 *sa, u16 update_indic,
+ const u8 *tlvs, size_t tlvs_len);
+void wpas_notify_p2p_provision_discovery(struct wpa_supplicant *wpa_s,
+ const u8 *dev_addr, int request,
+ enum p2p_prov_disc_status status,
+ u16 config_methods,
+ unsigned int generated_pin);
+void wpas_notify_p2p_group_started(struct wpa_supplicant *wpa_s,
+ struct wpa_ssid *ssid, int network_id,
+ int client);
+void wpas_notify_p2p_group_formation_failure(struct wpa_supplicant *wpa_s,
+ const char *reason);
+void wpas_notify_persistent_group_added(struct wpa_supplicant *wpa_s,
+ struct wpa_ssid *ssid);
+void wpas_notify_persistent_group_removed(struct wpa_supplicant *wpa_s,
+ struct wpa_ssid *ssid);
+
+void wpas_notify_p2p_wps_failed(struct wpa_supplicant *wpa_s,
+ struct wps_event_fail *fail);
+
+void wpas_notify_certification(struct wpa_supplicant *wpa_s, int depth,
+ const char *subject, const char *altsubject[],
+ int num_altsubject, const char *cert_hash,
+ const struct wpabuf *cert);
+void wpas_notify_preq(struct wpa_supplicant *wpa_s,
+ const u8 *addr, const u8 *dst, const u8 *bssid,
+ const u8 *ie, size_t ie_len, u32 ssi_signal);
+void wpas_notify_eap_status(struct wpa_supplicant *wpa_s, const char *status,
+ const char *parameter);
+void wpas_notify_network_bssid_set_changed(struct wpa_supplicant *wpa_s,
+ struct wpa_ssid *ssid);
+void wpas_notify_network_type_changed(struct wpa_supplicant *wpa_s,
+ struct wpa_ssid *ssid);
+void wpas_notify_p2p_invitation_received(struct wpa_supplicant *wpa_s,
+ const u8 *sa, const u8 *go_dev_addr,
+ const u8 *bssid, int id, int op_freq);
+
+#endif /* NOTIFY_H */
diff --git a/freebsd/contrib/wpa/wpa_supplicant/offchannel.c b/freebsd/contrib/wpa/wpa_supplicant/offchannel.c
new file mode 100644
index 00000000..54b8772b
--- /dev/null
+++ b/freebsd/contrib/wpa/wpa_supplicant/offchannel.c
@@ -0,0 +1,449 @@
+#include <machine/rtems-bsd-user-space.h>
+
+/*
+ * wpa_supplicant - Off-channel Action frame TX/RX
+ * Copyright (c) 2009-2010, Atheros Communications
+ * Copyright (c) 2011, Qualcomm Atheros
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "utils/eloop.h"
+#include "wpa_supplicant_i.h"
+#include "p2p_supplicant.h"
+#include "driver_i.h"
+#include "offchannel.h"
+
+
+
+static struct wpa_supplicant *
+wpas_get_tx_interface(struct wpa_supplicant *wpa_s, const u8 *src)
+{
+ struct wpa_supplicant *iface;
+
+ if (os_memcmp(src, wpa_s->own_addr, ETH_ALEN) == 0)
+ return wpa_s;
+
+ /*
+ * Try to find a group interface that matches with the source address.
+ */
+ iface = wpa_s->global->ifaces;
+ while (iface) {
+ if (os_memcmp(src, iface->own_addr, ETH_ALEN) == 0)
+ break;
+ iface = iface->next;
+ }
+ if (iface) {
+ wpa_printf(MSG_DEBUG, "P2P: Use group interface %s "
+ "instead of interface %s for Action TX",
+ iface->ifname, wpa_s->ifname);
+ return iface;
+ }
+
+ return wpa_s;
+}
+
+
+static void wpas_send_action_cb(void *eloop_ctx, void *timeout_ctx)
+{
+ struct wpa_supplicant *wpa_s = eloop_ctx;
+ struct wpa_supplicant *iface;
+ int res;
+ int without_roc;
+
+ without_roc = wpa_s->pending_action_without_roc;
+ wpa_s->pending_action_without_roc = 0;
+ wpa_printf(MSG_DEBUG,
+ "Off-channel: Send Action callback (without_roc=%d pending_action_tx=%p pending_action_tx_done=%d)",
+ without_roc, wpa_s->pending_action_tx,
+ !!wpa_s->pending_action_tx_done);
+
+ if (wpa_s->pending_action_tx == NULL || wpa_s->pending_action_tx_done)
+ return;
+
+ /*
+ * This call is likely going to be on the P2P device instance if the
+ * driver uses a separate interface for that purpose. However, some
+ * Action frames are actually sent within a P2P Group and when that is
+ * the case, we need to follow power saving (e.g., GO buffering the
+ * frame for a client in PS mode or a client following the advertised
+ * NoA from its GO). To make that easier for the driver, select the
+ * correct group interface here.
+ */
+ iface = wpas_get_tx_interface(wpa_s, wpa_s->pending_action_src);
+
+ if (wpa_s->off_channel_freq != wpa_s->pending_action_freq &&
+ wpa_s->pending_action_freq != 0 &&
+ wpa_s->pending_action_freq != iface->assoc_freq) {
+ wpa_printf(MSG_DEBUG, "Off-channel: Pending Action frame TX "
+ "waiting for another freq=%u (off_channel_freq=%u "
+ "assoc_freq=%u)",
+ wpa_s->pending_action_freq,
+ wpa_s->off_channel_freq,
+ iface->assoc_freq);
+ if (without_roc && wpa_s->off_channel_freq == 0) {
+ unsigned int duration = 200;
+ /*
+ * We may get here if wpas_send_action() found us to be
+ * on the correct channel, but remain-on-channel cancel
+ * event was received before getting here.
+ */
+ wpa_printf(MSG_DEBUG, "Off-channel: Schedule "
+ "remain-on-channel to send Action frame");
+#ifdef CONFIG_TESTING_OPTIONS
+ if (wpa_s->extra_roc_dur) {
+ wpa_printf(MSG_DEBUG,
+ "TESTING: Increase ROC duration %u -> %u",
+ duration,
+ duration + wpa_s->extra_roc_dur);
+ duration += wpa_s->extra_roc_dur;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+ if (wpa_drv_remain_on_channel(
+ wpa_s, wpa_s->pending_action_freq,
+ duration) < 0) {
+ wpa_printf(MSG_DEBUG, "Off-channel: Failed to "
+ "request driver to remain on "
+ "channel (%u MHz) for Action Frame "
+ "TX", wpa_s->pending_action_freq);
+ } else {
+ wpa_s->off_channel_freq = 0;
+ wpa_s->roc_waiting_drv_freq =
+ wpa_s->pending_action_freq;
+ }
+ }
+ return;
+ }
+
+ wpa_printf(MSG_DEBUG, "Off-channel: Sending pending Action frame to "
+ MACSTR " using interface %s",
+ MAC2STR(wpa_s->pending_action_dst), iface->ifname);
+ res = wpa_drv_send_action(iface, wpa_s->pending_action_freq, 0,
+ wpa_s->pending_action_dst,
+ wpa_s->pending_action_src,
+ wpa_s->pending_action_bssid,
+ wpabuf_head(wpa_s->pending_action_tx),
+ wpabuf_len(wpa_s->pending_action_tx),
+ wpa_s->pending_action_no_cck);
+ if (res) {
+ wpa_printf(MSG_DEBUG, "Off-channel: Failed to send the "
+ "pending Action frame");
+ /*
+ * Use fake TX status event to allow state machines to
+ * continue.
+ */
+ offchannel_send_action_tx_status(
+ wpa_s, wpa_s->pending_action_dst,
+ wpabuf_head(wpa_s->pending_action_tx),
+ wpabuf_len(wpa_s->pending_action_tx),
+ OFFCHANNEL_SEND_ACTION_FAILED);
+ }
+}
+
+
+/**
+ * offchannel_send_action_tx_status - TX status callback
+ * @wpa_s: Pointer to wpa_supplicant data
+ * @dst: Destination MAC address of the transmitted Action frame
+ * @data: Transmitted frame payload
+ * @data_len: Length of @data in bytes
+ * @result: TX status
+ *
+ * This function is called whenever the driver indicates a TX status event for
+ * a frame sent by offchannel_send_action() using wpa_drv_send_action().
+ */
+void offchannel_send_action_tx_status(
+ struct wpa_supplicant *wpa_s, const u8 *dst, const u8 *data,
+ size_t data_len, enum offchannel_send_action_result result)
+{
+ if (wpa_s->pending_action_tx == NULL) {
+ wpa_printf(MSG_DEBUG, "Off-channel: Ignore Action TX status - "
+ "no pending operation");
+ return;
+ }
+
+ if (os_memcmp(dst, wpa_s->pending_action_dst, ETH_ALEN) != 0) {
+ wpa_printf(MSG_DEBUG, "Off-channel: Ignore Action TX status - "
+ "unknown destination address");
+ return;
+ }
+
+ /* Accept report only if the contents of the frame matches */
+ if (data_len - wpabuf_len(wpa_s->pending_action_tx) != 24 ||
+ os_memcmp(data + 24, wpabuf_head(wpa_s->pending_action_tx),
+ wpabuf_len(wpa_s->pending_action_tx)) != 0) {
+ wpa_printf(MSG_DEBUG, "Off-channel: Ignore Action TX status - "
+ "mismatching contents with pending frame");
+ wpa_hexdump(MSG_MSGDUMP, "TX status frame data",
+ data, data_len);
+ wpa_hexdump_buf(MSG_MSGDUMP, "Pending TX frame",
+ wpa_s->pending_action_tx);
+ return;
+ }
+
+ wpa_printf(MSG_DEBUG, "Off-channel: Delete matching pending action frame");
+
+ wpabuf_free(wpa_s->pending_action_tx);
+ wpa_s->pending_action_tx = NULL;
+
+ wpa_printf(MSG_DEBUG, "Off-channel: TX status result=%d cb=%p",
+ result, wpa_s->pending_action_tx_status_cb);
+
+ if (wpa_s->pending_action_tx_status_cb) {
+ wpa_s->pending_action_tx_status_cb(
+ wpa_s, wpa_s->pending_action_freq,
+ wpa_s->pending_action_dst, wpa_s->pending_action_src,
+ wpa_s->pending_action_bssid,
+ data, data_len, result);
+ }
+
+#ifdef CONFIG_P2P
+ if (wpa_s->p2p_long_listen > 0) {
+ /* Continue the listen */
+ wpa_printf(MSG_DEBUG, "P2P: Continuing long Listen state");
+ wpas_p2p_listen_start(wpa_s, wpa_s->p2p_long_listen);
+ }
+#endif /* CONFIG_P2P */
+}
+
+
+/**
+ * offchannel_send_action - Request off-channel Action frame TX
+ * @wpa_s: Pointer to wpa_supplicant data
+ * @freq: The frequency in MHz indicating the channel on which the frame is to
+ * transmitted or 0 for the current channel (only if associated)
+ * @dst: Action frame destination MAC address
+ * @src: Action frame source MAC address
+ * @bssid: Action frame BSSID
+ * @buf: Frame to transmit starting from the Category field
+ * @len: Length of @buf in bytes
+ * @wait_time: Wait time for response in milliseconds
+ * @tx_cb: Callback function for indicating TX status or %NULL for now callback
+ * @no_cck: Whether CCK rates are to be disallowed for TX rate selection
+ * Returns: 0 on success or -1 on failure
+ *
+ * This function is used to request an Action frame to be transmitted on the
+ * current operating channel or on another channel (off-channel). The actual
+ * frame transmission will be delayed until the driver is ready on the specified
+ * channel. The @wait_time parameter can be used to request the driver to remain
+ * awake on the channel to wait for a response.
+ */
+int offchannel_send_action(struct wpa_supplicant *wpa_s, unsigned int freq,
+ const u8 *dst, const u8 *src, const u8 *bssid,
+ const u8 *buf, size_t len, unsigned int wait_time,
+ void (*tx_cb)(struct wpa_supplicant *wpa_s,
+ unsigned int freq, const u8 *dst,
+ const u8 *src, const u8 *bssid,
+ const u8 *data, size_t data_len,
+ enum offchannel_send_action_result
+ result),
+ int no_cck)
+{
+ wpa_printf(MSG_DEBUG, "Off-channel: Send action frame: freq=%d dst="
+ MACSTR " src=" MACSTR " bssid=" MACSTR " len=%d",
+ freq, MAC2STR(dst), MAC2STR(src), MAC2STR(bssid),
+ (int) len);
+
+ wpa_s->pending_action_tx_status_cb = tx_cb;
+
+ if (wpa_s->pending_action_tx) {
+ wpa_printf(MSG_DEBUG, "Off-channel: Dropped pending Action "
+ "frame TX to " MACSTR,
+ MAC2STR(wpa_s->pending_action_dst));
+ wpabuf_free(wpa_s->pending_action_tx);
+ }
+ wpa_s->pending_action_tx_done = 0;
+ wpa_s->pending_action_tx = wpabuf_alloc(len);
+ if (wpa_s->pending_action_tx == NULL) {
+ wpa_printf(MSG_DEBUG, "Off-channel: Failed to allocate Action "
+ "frame TX buffer (len=%llu)",
+ (unsigned long long) len);
+ return -1;
+ }
+ wpabuf_put_data(wpa_s->pending_action_tx, buf, len);
+ os_memcpy(wpa_s->pending_action_src, src, ETH_ALEN);
+ os_memcpy(wpa_s->pending_action_dst, dst, ETH_ALEN);
+ os_memcpy(wpa_s->pending_action_bssid, bssid, ETH_ALEN);
+ wpa_s->pending_action_freq = freq;
+ wpa_s->pending_action_no_cck = no_cck;
+
+ if (freq != 0 && wpa_s->drv_flags & WPA_DRIVER_FLAGS_OFFCHANNEL_TX) {
+ struct wpa_supplicant *iface;
+ int ret;
+
+ iface = wpas_get_tx_interface(wpa_s, src);
+ wpa_s->action_tx_wait_time = wait_time;
+
+ ret = wpa_drv_send_action(
+ iface, wpa_s->pending_action_freq,
+ wait_time, wpa_s->pending_action_dst,
+ wpa_s->pending_action_src, wpa_s->pending_action_bssid,
+ wpabuf_head(wpa_s->pending_action_tx),
+ wpabuf_len(wpa_s->pending_action_tx),
+ wpa_s->pending_action_no_cck);
+ if (ret == 0)
+ wpa_s->pending_action_tx_done = 1;
+ return ret;
+ }
+
+ if (freq) {
+ struct wpa_supplicant *tx_iface;
+ tx_iface = wpas_get_tx_interface(wpa_s, src);
+ if (tx_iface->assoc_freq == freq) {
+ wpa_printf(MSG_DEBUG, "Off-channel: Already on "
+ "requested channel (TX interface operating "
+ "channel)");
+ freq = 0;
+ }
+ }
+
+ if (wpa_s->off_channel_freq == freq || freq == 0) {
+ wpa_printf(MSG_DEBUG, "Off-channel: Already on requested "
+ "channel; send Action frame immediately");
+ /* TODO: Would there ever be need to extend the current
+ * duration on the channel? */
+ wpa_s->pending_action_without_roc = 1;
+ eloop_cancel_timeout(wpas_send_action_cb, wpa_s, NULL);
+ eloop_register_timeout(0, 0, wpas_send_action_cb, wpa_s, NULL);
+ return 0;
+ }
+ wpa_s->pending_action_without_roc = 0;
+
+ if (wpa_s->roc_waiting_drv_freq == freq) {
+ wpa_printf(MSG_DEBUG, "Off-channel: Already waiting for "
+ "driver to get to frequency %u MHz; continue "
+ "waiting to send the Action frame", freq);
+ return 0;
+ }
+
+ wpa_printf(MSG_DEBUG, "Off-channel: Schedule Action frame to be "
+ "transmitted once the driver gets to the requested "
+ "channel");
+ if (wait_time > wpa_s->max_remain_on_chan)
+ wait_time = wpa_s->max_remain_on_chan;
+ else if (wait_time == 0)
+ wait_time = 20;
+#ifdef CONFIG_TESTING_OPTIONS
+ if (wpa_s->extra_roc_dur) {
+ wpa_printf(MSG_DEBUG, "TESTING: Increase ROC duration %u -> %u",
+ wait_time, wait_time + wpa_s->extra_roc_dur);
+ wait_time += wpa_s->extra_roc_dur;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+ if (wpa_drv_remain_on_channel(wpa_s, freq, wait_time) < 0) {
+ wpa_printf(MSG_DEBUG, "Off-channel: Failed to request driver "
+ "to remain on channel (%u MHz) for Action "
+ "Frame TX", freq);
+ return -1;
+ }
+ wpa_s->off_channel_freq = 0;
+ wpa_s->roc_waiting_drv_freq = freq;
+
+ return 0;
+}
+
+
+/**
+ * offchannel_send_send_action_done - Notify completion of Action frame sequence
+ * @wpa_s: Pointer to wpa_supplicant data
+ *
+ * This function can be used to cancel a wait for additional response frames on
+ * the channel that was used with offchannel_send_action().
+ */
+void offchannel_send_action_done(struct wpa_supplicant *wpa_s)
+{
+ wpa_printf(MSG_DEBUG,
+ "Off-channel: Action frame sequence done notification: pending_action_tx=%p drv_offchan_tx=%d action_tx_wait_time=%d off_channel_freq=%d roc_waiting_drv_freq=%d",
+ wpa_s->pending_action_tx,
+ !!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_OFFCHANNEL_TX),
+ wpa_s->action_tx_wait_time, wpa_s->off_channel_freq,
+ wpa_s->roc_waiting_drv_freq);
+ wpabuf_free(wpa_s->pending_action_tx);
+ wpa_s->pending_action_tx = NULL;
+ if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_OFFCHANNEL_TX &&
+ wpa_s->action_tx_wait_time)
+ wpa_drv_send_action_cancel_wait(wpa_s);
+ else if (wpa_s->off_channel_freq || wpa_s->roc_waiting_drv_freq) {
+ wpa_drv_cancel_remain_on_channel(wpa_s);
+ wpa_s->off_channel_freq = 0;
+ wpa_s->roc_waiting_drv_freq = 0;
+ }
+}
+
+
+/**
+ * offchannel_remain_on_channel_cb - Remain-on-channel callback function
+ * @wpa_s: Pointer to wpa_supplicant data
+ * @freq: Frequency (in MHz) of the selected channel
+ * @duration: Duration of the remain-on-channel operation in milliseconds
+ *
+ * This function is called whenever the driver notifies beginning of a
+ * remain-on-channel operation.
+ */
+void offchannel_remain_on_channel_cb(struct wpa_supplicant *wpa_s,
+ unsigned int freq, unsigned int duration)
+{
+ wpa_s->roc_waiting_drv_freq = 0;
+ wpa_s->off_channel_freq = freq;
+ wpas_send_action_cb(wpa_s, NULL);
+}
+
+
+/**
+ * offchannel_cancel_remain_on_channel_cb - Remain-on-channel stopped callback
+ * @wpa_s: Pointer to wpa_supplicant data
+ * @freq: Frequency (in MHz) of the selected channel
+ *
+ * This function is called whenever the driver notifies termination of a
+ * remain-on-channel operation.
+ */
+void offchannel_cancel_remain_on_channel_cb(struct wpa_supplicant *wpa_s,
+ unsigned int freq)
+{
+ wpa_s->off_channel_freq = 0;
+}
+
+
+/**
+ * offchannel_pending_action_tx - Check whether there is a pending Action TX
+ * @wpa_s: Pointer to wpa_supplicant data
+ * Returns: Pointer to pending frame or %NULL if no pending operation
+ *
+ * This function can be used to check whether there is a pending Action frame TX
+ * operation. The returned pointer should be used only for checking whether it
+ * is %NULL (no pending frame) or to print the pointer value in debug
+ * information (i.e., the pointer should not be dereferenced).
+ */
+const void * offchannel_pending_action_tx(struct wpa_supplicant *wpa_s)
+{
+ return wpa_s->pending_action_tx;
+}
+
+
+/**
+ * offchannel_clear_pending_action_tx - Clear pending Action frame TX
+ * @wpa_s: Pointer to wpa_supplicant data
+ */
+void offchannel_clear_pending_action_tx(struct wpa_supplicant *wpa_s)
+{
+ wpabuf_free(wpa_s->pending_action_tx);
+ wpa_s->pending_action_tx = NULL;
+}
+
+
+/**
+ * offchannel_deinit - Deinit off-channel operations
+ * @wpa_s: Pointer to wpa_supplicant data
+ *
+ * This function is used to free up any allocated resources for off-channel
+ * operations.
+ */
+void offchannel_deinit(struct wpa_supplicant *wpa_s)
+{
+ offchannel_clear_pending_action_tx(wpa_s);
+ eloop_cancel_timeout(wpas_send_action_cb, wpa_s, NULL);
+}
diff --git a/freebsd/contrib/wpa/wpa_supplicant/offchannel.h b/freebsd/contrib/wpa/wpa_supplicant/offchannel.h
new file mode 100644
index 00000000..0ad7e18f
--- /dev/null
+++ b/freebsd/contrib/wpa/wpa_supplicant/offchannel.h
@@ -0,0 +1,35 @@
+/*
+ * wpa_supplicant - Off-channel Action frame TX/RX
+ * Copyright (c) 2009-2010, Atheros Communications
+ * Copyright (c) 2011, Qualcomm Atheros
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef OFFCHANNEL_H
+#define OFFCHANNEL_H
+
+int offchannel_send_action(struct wpa_supplicant *wpa_s, unsigned int freq,
+ const u8 *dst, const u8 *src, const u8 *bssid,
+ const u8 *buf, size_t len, unsigned int wait_time,
+ void (*tx_cb)(struct wpa_supplicant *wpa_s,
+ unsigned int freq, const u8 *dst,
+ const u8 *src, const u8 *bssid,
+ const u8 *data, size_t data_len,
+ enum offchannel_send_action_result
+ result),
+ int no_cck);
+void offchannel_send_action_done(struct wpa_supplicant *wpa_s);
+void offchannel_remain_on_channel_cb(struct wpa_supplicant *wpa_s,
+ unsigned int freq, unsigned int duration);
+void offchannel_cancel_remain_on_channel_cb(struct wpa_supplicant *wpa_s,
+ unsigned int freq);
+void offchannel_deinit(struct wpa_supplicant *wpa_s);
+void offchannel_send_action_tx_status(
+ struct wpa_supplicant *wpa_s, const u8 *dst, const u8 *data,
+ size_t data_len, enum offchannel_send_action_result result);
+const void * offchannel_pending_action_tx(struct wpa_supplicant *wpa_s);
+void offchannel_clear_pending_action_tx(struct wpa_supplicant *wpa_s);
+
+#endif /* OFFCHANNEL_H */
diff --git a/freebsd/contrib/wpa/wpa_supplicant/p2p_supplicant.h b/freebsd/contrib/wpa/wpa_supplicant/p2p_supplicant.h
new file mode 100644
index 00000000..56e68349
--- /dev/null
+++ b/freebsd/contrib/wpa/wpa_supplicant/p2p_supplicant.h
@@ -0,0 +1,335 @@
+/*
+ * wpa_supplicant - P2P
+ * Copyright (c) 2009-2010, Atheros Communications
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef P2P_SUPPLICANT_H
+#define P2P_SUPPLICANT_H
+
+enum p2p_wps_method;
+struct p2p_go_neg_results;
+enum p2p_send_action_result;
+struct p2p_peer_info;
+struct p2p_channels;
+struct wps_event_fail;
+struct p2ps_provision;
+
+enum wpas_p2p_channel_update_trig {
+ WPAS_P2P_CHANNEL_UPDATE_ANY,
+ WPAS_P2P_CHANNEL_UPDATE_DRIVER,
+ WPAS_P2P_CHANNEL_UPDATE_STATE_CHANGE,
+ WPAS_P2P_CHANNEL_UPDATE_AVOID,
+ WPAS_P2P_CHANNEL_UPDATE_DISALLOW,
+ WPAS_P2P_CHANNEL_UPDATE_CS,
+};
+
+int wpas_p2p_add_p2pdev_interface(struct wpa_supplicant *wpa_s,
+ const char *conf_p2p_dev);
+struct wpa_supplicant * wpas_get_p2p_go_iface(struct wpa_supplicant *wpa_s,
+ const u8 *ssid, size_t ssid_len);
+struct wpa_supplicant * wpas_get_p2p_client_iface(struct wpa_supplicant *wpa_s,
+ const u8 *peer_dev_addr);
+int wpas_p2p_connect(struct wpa_supplicant *wpa_s, const u8 *peer_addr,
+ const char *pin, enum p2p_wps_method wps_method,
+ int persistent_group, int auto_join, int join,
+ int auth, int go_intent, int freq, int persistent_id,
+ int pd, int ht40, int vht);
+int wpas_p2p_handle_frequency_conflicts(struct wpa_supplicant *wpa_s,
+ int freq, struct wpa_ssid *ssid);
+int wpas_p2p_group_add(struct wpa_supplicant *wpa_s, int persistent_group,
+ int freq, int ht40, int vht);
+int wpas_p2p_group_add_persistent(struct wpa_supplicant *wpa_s,
+ struct wpa_ssid *ssid, int addr_allocated,
+ int force_freq, int neg_freq, int ht40,
+ int vht, const struct p2p_channels *channels,
+ int connection_timeout, int force_scan);
+struct p2p_group * wpas_p2p_group_init(struct wpa_supplicant *wpa_s,
+ struct wpa_ssid *ssid);
+enum wpas_p2p_prov_disc_use {
+ WPAS_P2P_PD_FOR_GO_NEG,
+ WPAS_P2P_PD_FOR_JOIN,
+ WPAS_P2P_PD_AUTO,
+ WPAS_P2P_PD_FOR_ASP
+};
+int wpas_p2p_prov_disc(struct wpa_supplicant *wpa_s, const u8 *peer_addr,
+ const char *config_method,
+ enum wpas_p2p_prov_disc_use use,
+ struct p2ps_provision *p2ps_prov);
+void wpas_send_action_tx_status(struct wpa_supplicant *wpa_s, const u8 *dst,
+ const u8 *data, size_t data_len,
+ enum p2p_send_action_result result);
+int wpas_p2p_scan_result_text(const u8 *ies, size_t ies_len, char *buf,
+ char *end);
+enum p2p_discovery_type;
+int wpas_p2p_find(struct wpa_supplicant *wpa_s, unsigned int timeout,
+ enum p2p_discovery_type type,
+ unsigned int num_req_dev_types, const u8 *req_dev_types,
+ const u8 *dev_id, unsigned int search_delay,
+ u8 seek_cnt, const char **seek_string, int freq);
+void wpas_p2p_stop_find(struct wpa_supplicant *wpa_s);
+int wpas_p2p_listen(struct wpa_supplicant *wpa_s, unsigned int timeout);
+int wpas_p2p_listen_start(struct wpa_supplicant *wpa_s, unsigned int timeout);
+int wpas_p2p_assoc_req_ie(struct wpa_supplicant *wpa_s, struct wpa_bss *bss,
+ u8 *buf, size_t len, int p2p_group);
+void wpas_p2p_scan_ie(struct wpa_supplicant *wpa_s, struct wpabuf *ies);
+u64 wpas_p2p_sd_request(struct wpa_supplicant *wpa_s, const u8 *dst,
+ const struct wpabuf *tlvs);
+u64 wpas_p2p_sd_request_asp(struct wpa_supplicant *wpa_s, const u8 *dst, u8 id,
+ const char *svc_str, const char *info_substr);
+u64 wpas_p2p_sd_request_upnp(struct wpa_supplicant *wpa_s, const u8 *dst,
+ u8 version, const char *query);
+u64 wpas_p2p_sd_request_wifi_display(struct wpa_supplicant *wpa_s,
+ const u8 *dst, const char *role);
+int wpas_p2p_sd_cancel_request(struct wpa_supplicant *wpa_s, u64 req);
+void wpas_p2p_sd_response(struct wpa_supplicant *wpa_s, int freq,
+ const u8 *dst, u8 dialog_token,
+ const struct wpabuf *resp_tlvs);
+void wpas_p2p_sd_service_update(struct wpa_supplicant *wpa_s);
+void wpas_p2p_service_flush(struct wpa_supplicant *wpa_s);
+int wpas_p2p_service_add_bonjour(struct wpa_supplicant *wpa_s,
+ struct wpabuf *query, struct wpabuf *resp);
+int wpas_p2p_service_del_bonjour(struct wpa_supplicant *wpa_s,
+ const struct wpabuf *query);
+int wpas_p2p_service_add_upnp(struct wpa_supplicant *wpa_s, u8 version,
+ const char *service);
+int wpas_p2p_service_del_upnp(struct wpa_supplicant *wpa_s, u8 version,
+ const char *service);
+int wpas_p2p_service_add_asp(struct wpa_supplicant *wpa_s, int auto_accept,
+ u32 adv_id, const char *adv_str, u8 svc_state,
+ u16 config_methods, const char *svc_info,
+ const u8 *cpt_priority);
+int wpas_p2p_service_del_asp(struct wpa_supplicant *wpa_s, u32 adv_id);
+void wpas_p2p_service_flush_asp(struct wpa_supplicant *wpa_s);
+int wpas_p2p_service_p2ps_id_exists(struct wpa_supplicant *wpa_s, u32 adv_id);
+void wpas_sd_request(void *ctx, int freq, const u8 *sa, u8 dialog_token,
+ u16 update_indic, const u8 *tlvs, size_t tlvs_len);
+void wpas_sd_response(void *ctx, const u8 *sa, u16 update_indic,
+ const u8 *tlvs, size_t tlvs_len);
+int wpas_p2p_reject(struct wpa_supplicant *wpa_s, const u8 *addr);
+int wpas_p2p_invite(struct wpa_supplicant *wpa_s, const u8 *peer_addr,
+ struct wpa_ssid *ssid, const u8 *go_dev_addr, int freq,
+ int ht40, int vht, int pref_freq);
+int wpas_p2p_invite_group(struct wpa_supplicant *wpa_s, const char *ifname,
+ const u8 *peer_addr, const u8 *go_dev_addr);
+int wpas_p2p_presence_req(struct wpa_supplicant *wpa_s, u32 duration1,
+ u32 interval1, u32 duration2, u32 interval2);
+int wpas_p2p_ext_listen(struct wpa_supplicant *wpa_s, unsigned int period,
+ unsigned int interval);
+int wpas_p2p_deauth_notif(struct wpa_supplicant *wpa_s, const u8 *bssid,
+ u16 reason_code, const u8 *ie, size_t ie_len,
+ int locally_generated);
+void wpas_p2p_disassoc_notif(struct wpa_supplicant *wpa_s, const u8 *bssid,
+ u16 reason_code, const u8 *ie, size_t ie_len,
+ int locally_generated);
+int wpas_p2p_set_noa(struct wpa_supplicant *wpa_s, u8 count, int start,
+ int duration);
+int wpas_p2p_set_cross_connect(struct wpa_supplicant *wpa_s, int enabled);
+int wpas_p2p_cancel(struct wpa_supplicant *wpa_s);
+int wpas_p2p_unauthorize(struct wpa_supplicant *wpa_s, const char *addr);
+int wpas_p2p_disconnect(struct wpa_supplicant *wpa_s);
+struct wpa_ssid * wpas_p2p_get_persistent(struct wpa_supplicant *wpa_s,
+ const u8 *addr, const u8 *ssid,
+ size_t ssid_len);
+void wpas_p2p_notify_ap_sta_authorized(struct wpa_supplicant *wpa_s,
+ const u8 *addr);
+int wpas_p2p_scan_no_go_seen(struct wpa_supplicant *wpa_s);
+int wpas_p2p_get_ht40_mode(struct wpa_supplicant *wpa_s,
+ struct hostapd_hw_modes *mode, u8 channel);
+int wpas_p2p_get_vht80_center(struct wpa_supplicant *wpa_s,
+ struct hostapd_hw_modes *mode, u8 channel);
+unsigned int wpas_p2p_search_delay(struct wpa_supplicant *wpa_s);
+void wpas_p2p_new_psk_cb(struct wpa_supplicant *wpa_s, const u8 *mac_addr,
+ const u8 *p2p_dev_addr,
+ const u8 *psk, size_t psk_len);
+void wpas_p2p_remove_client(struct wpa_supplicant *wpa_s, const u8 *peer,
+ int iface_addr);
+struct wpabuf * wpas_p2p_nfc_handover_req(struct wpa_supplicant *wpa_s,
+ int ndef);
+struct wpabuf * wpas_p2p_nfc_handover_sel(struct wpa_supplicant *wpa_s,
+ int ndef, int tag);
+int wpas_p2p_nfc_tag_process(struct wpa_supplicant *wpa_s,
+ const struct wpabuf *data, int forced_freq);
+int wpas_p2p_nfc_report_handover(struct wpa_supplicant *wpa_s, int init,
+ const struct wpabuf *req,
+ const struct wpabuf *sel, int forced_freq);
+int wpas_p2p_nfc_tag_enabled(struct wpa_supplicant *wpa_s, int enabled);
+void wpas_p2p_pbc_overlap_cb(void *eloop_ctx, void *timeout_ctx);
+
+#ifdef CONFIG_P2P
+
+int wpas_p2p_init(struct wpa_global *global, struct wpa_supplicant *wpa_s);
+void wpas_p2p_deinit(struct wpa_supplicant *wpa_s);
+void wpas_p2p_completed(struct wpa_supplicant *wpa_s);
+void wpas_p2p_update_config(struct wpa_supplicant *wpa_s);
+int wpas_p2p_probe_req_rx(struct wpa_supplicant *wpa_s, const u8 *addr,
+ const u8 *dst, const u8 *bssid,
+ const u8 *ie, size_t ie_len,
+ unsigned int rx_freq, int ssi_signal);
+void wpas_p2p_wps_success(struct wpa_supplicant *wpa_s, const u8 *peer_addr,
+ int registrar);
+
+void wpas_p2p_update_channel_list(struct wpa_supplicant *wpa_s,
+ enum wpas_p2p_channel_update_trig trig);
+
+void wpas_p2p_update_best_channels(struct wpa_supplicant *wpa_s,
+ int freq_24, int freq_5, int freq_overall);
+void wpas_p2p_rx_action(struct wpa_supplicant *wpa_s, const u8 *da,
+ const u8 *sa, const u8 *bssid,
+ u8 category, const u8 *data, size_t len, int freq);
+void wpas_p2p_remain_on_channel_cb(struct wpa_supplicant *wpa_s,
+ unsigned int freq, unsigned int duration);
+void wpas_p2p_cancel_remain_on_channel_cb(struct wpa_supplicant *wpa_s,
+ unsigned int freq);
+void wpas_p2p_interface_unavailable(struct wpa_supplicant *wpa_s);
+void wpas_p2p_notif_connected(struct wpa_supplicant *wpa_s);
+void wpas_p2p_notif_disconnected(struct wpa_supplicant *wpa_s);
+int wpas_p2p_notif_pbc_overlap(struct wpa_supplicant *wpa_s);
+int wpas_p2p_4way_hs_failed(struct wpa_supplicant *wpa_s);
+void wpas_p2p_ap_setup_failed(struct wpa_supplicant *wpa_s);
+void wpas_p2p_indicate_state_change(struct wpa_supplicant *wpa_s);
+void wpas_p2p_deinit_iface(struct wpa_supplicant *wpa_s);
+void wpas_p2p_ap_deinit(struct wpa_supplicant *wpa_s);
+void wpas_p2p_network_removed(struct wpa_supplicant *wpa_s,
+ struct wpa_ssid *ssid);
+int wpas_p2p_in_progress(struct wpa_supplicant *wpa_s);
+int wpas_p2p_wps_eapol_cb(struct wpa_supplicant *wpa_s);
+void wpas_p2p_wps_failed(struct wpa_supplicant *wpa_s,
+ struct wps_event_fail *fail);
+int wpas_p2p_group_remove(struct wpa_supplicant *wpa_s, const char *ifname);
+
+#else /* CONFIG_P2P */
+
+static inline int
+wpas_p2p_init(struct wpa_global *global, struct wpa_supplicant *wpa_s)
+{
+ return 0;
+}
+
+static inline void wpas_p2p_deinit(struct wpa_supplicant *wpa_s)
+{
+}
+
+static inline void wpas_p2p_completed(struct wpa_supplicant *wpa_s)
+{
+}
+
+static inline void wpas_p2p_update_config(struct wpa_supplicant *wpa_s)
+{
+}
+
+static inline int wpas_p2p_probe_req_rx(struct wpa_supplicant *wpa_s,
+ const u8 *addr,
+ const u8 *dst, const u8 *bssid,
+ const u8 *ie, size_t ie_len,
+ unsigned int rx_freq, int ssi_signal)
+{
+ return 0;
+}
+
+static inline void wpas_p2p_wps_success(struct wpa_supplicant *wpa_s,
+ const u8 *peer_addr, int registrar)
+{
+}
+
+static inline void
+wpas_p2p_update_channel_list(struct wpa_supplicant *wpa_s,
+ enum wpas_p2p_channel_update_trig trig)
+{
+}
+
+static inline void wpas_p2p_update_best_channels(struct wpa_supplicant *wpa_s,
+ int freq_24, int freq_5,
+ int freq_overall)
+{
+}
+
+static inline void wpas_p2p_rx_action(struct wpa_supplicant *wpa_s,
+ const u8 *da,
+ const u8 *sa, const u8 *bssid,
+ u8 category, const u8 *data, size_t len,
+ int freq)
+{
+}
+
+static inline void wpas_p2p_remain_on_channel_cb(struct wpa_supplicant *wpa_s,
+ unsigned int freq,
+ unsigned int duration)
+{
+}
+
+static inline void
+wpas_p2p_cancel_remain_on_channel_cb(struct wpa_supplicant *wpa_s,
+ unsigned int freq)
+{
+}
+
+static inline void wpas_p2p_interface_unavailable(struct wpa_supplicant *wpa_s)
+{
+}
+
+static inline void wpas_p2p_notif_connected(struct wpa_supplicant *wpa_s)
+{
+}
+
+static inline void wpas_p2p_notif_disconnected(struct wpa_supplicant *wpa_s)
+{
+}
+
+static inline int wpas_p2p_notif_pbc_overlap(struct wpa_supplicant *wpa_s)
+{
+ return 0;
+}
+
+static inline int wpas_p2p_4way_hs_failed(struct wpa_supplicant *wpa_s)
+{
+ return 0;
+}
+
+static inline void wpas_p2p_ap_setup_failed(struct wpa_supplicant *wpa_s)
+{
+}
+
+static inline void wpas_p2p_indicate_state_change(struct wpa_supplicant *wpa_s)
+{
+}
+
+static inline void wpas_p2p_deinit_iface(struct wpa_supplicant *wpa_s)
+{
+}
+
+static inline void wpas_p2p_ap_deinit(struct wpa_supplicant *wpa_s)
+{
+}
+
+static inline void wpas_p2p_network_removed(struct wpa_supplicant *wpa_s,
+ struct wpa_ssid *ssid)
+{
+}
+
+static inline int wpas_p2p_in_progress(struct wpa_supplicant *wpa_s)
+{
+ return 0;
+}
+
+static inline int wpas_p2p_wps_eapol_cb(struct wpa_supplicant *wpa_s)
+{
+ return 0;
+}
+
+static inline void wpas_p2p_wps_failed(struct wpa_supplicant *wpa_s,
+ struct wps_event_fail *fail)
+{
+}
+
+static inline int wpas_p2p_group_remove(struct wpa_supplicant *wpa_s,
+ const char *ifname)
+{
+ return 0;
+}
+
+#endif /* CONFIG_P2P */
+
+#endif /* P2P_SUPPLICANT_H */
diff --git a/freebsd/contrib/wpa/wpa_supplicant/scan.c b/freebsd/contrib/wpa/wpa_supplicant/scan.c
new file mode 100644
index 00000000..9f9f4148
--- /dev/null
+++ b/freebsd/contrib/wpa/wpa_supplicant/scan.c
@@ -0,0 +1,2466 @@
+#include <machine/rtems-bsd-user-space.h>
+
+/*
+ * WPA Supplicant - Scanning
+ * Copyright (c) 2003-2014, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+
+#include "utils/common.h"
+#include "utils/eloop.h"
+#include "common/ieee802_11_defs.h"
+#include "common/wpa_ctrl.h"
+#include "config.h"
+#include "wpa_supplicant_i.h"
+#include "driver_i.h"
+#include "wps_supplicant.h"
+#include "p2p_supplicant.h"
+#include "p2p/p2p.h"
+#include "hs20_supplicant.h"
+#include "notify.h"
+#include "bss.h"
+#include "scan.h"
+#include "mesh.h"
+
+
+static void wpa_supplicant_gen_assoc_event(struct wpa_supplicant *wpa_s)
+{
+ struct wpa_ssid *ssid;
+ union wpa_event_data data;
+
+ ssid = wpa_supplicant_get_ssid(wpa_s);
+ if (ssid == NULL)
+ return;
+
+ if (wpa_s->current_ssid == NULL) {
+ wpa_s->current_ssid = ssid;
+ if (wpa_s->current_ssid != NULL)
+ wpas_notify_network_changed(wpa_s);
+ }
+ wpa_supplicant_initiate_eapol(wpa_s);
+ wpa_dbg(wpa_s, MSG_DEBUG, "Already associated with a configured "
+ "network - generating associated event");
+ os_memset(&data, 0, sizeof(data));
+ wpa_supplicant_event(wpa_s, EVENT_ASSOC, &data);
+}
+
+
+#ifdef CONFIG_WPS
+static int wpas_wps_in_use(struct wpa_supplicant *wpa_s,
+ enum wps_request_type *req_type)
+{
+ struct wpa_ssid *ssid;
+ int wps = 0;
+
+ for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next) {
+ if (!(ssid->key_mgmt & WPA_KEY_MGMT_WPS))
+ continue;
+
+ wps = 1;
+ *req_type = wpas_wps_get_req_type(ssid);
+ if (!ssid->eap.phase1)
+ continue;
+
+ if (os_strstr(ssid->eap.phase1, "pbc=1"))
+ return 2;
+ }
+
+#ifdef CONFIG_P2P
+ if (!wpa_s->global->p2p_disabled && wpa_s->global->p2p &&
+ !wpa_s->conf->p2p_disabled) {
+ wpa_s->wps->dev.p2p = 1;
+ if (!wps) {
+ wps = 1;
+ *req_type = WPS_REQ_ENROLLEE_INFO;
+ }
+ }
+#endif /* CONFIG_P2P */
+
+ return wps;
+}
+#endif /* CONFIG_WPS */
+
+
+/**
+ * wpa_supplicant_enabled_networks - Check whether there are enabled networks
+ * @wpa_s: Pointer to wpa_supplicant data
+ * Returns: 0 if no networks are enabled, >0 if networks are enabled
+ *
+ * This function is used to figure out whether any networks (or Interworking
+ * with enabled credentials and auto_interworking) are present in the current
+ * configuration.
+ */
+int wpa_supplicant_enabled_networks(struct wpa_supplicant *wpa_s)
+{
+ struct wpa_ssid *ssid = wpa_s->conf->ssid;
+ int count = 0, disabled = 0;
+
+ if (wpa_s->p2p_mgmt)
+ return 0; /* no normal network profiles on p2p_mgmt interface */
+
+ while (ssid) {
+ if (!wpas_network_disabled(wpa_s, ssid))
+ count++;
+ else
+ disabled++;
+ ssid = ssid->next;
+ }
+ if (wpa_s->conf->cred && wpa_s->conf->interworking &&
+ wpa_s->conf->auto_interworking)
+ count++;
+ if (count == 0 && disabled > 0) {
+ wpa_dbg(wpa_s, MSG_DEBUG, "No enabled networks (%d disabled "
+ "networks)", disabled);
+ }
+ return count;
+}
+
+
+static void wpa_supplicant_assoc_try(struct wpa_supplicant *wpa_s,
+ struct wpa_ssid *ssid)
+{
+ while (ssid) {
+ if (!wpas_network_disabled(wpa_s, ssid))
+ break;
+ ssid = ssid->next;
+ }
+
+ /* ap_scan=2 mode - try to associate with each SSID. */
+ if (ssid == NULL) {
+ wpa_dbg(wpa_s, MSG_DEBUG, "wpa_supplicant_assoc_try: Reached "
+ "end of scan list - go back to beginning");
+ wpa_s->prev_scan_ssid = WILDCARD_SSID_SCAN;
+ wpa_supplicant_req_scan(wpa_s, 0, 0);
+ return;
+ }
+ if (ssid->next) {
+ /* Continue from the next SSID on the next attempt. */
+ wpa_s->prev_scan_ssid = ssid;
+ } else {
+ /* Start from the beginning of the SSID list. */
+ wpa_s->prev_scan_ssid = WILDCARD_SSID_SCAN;
+ }
+ wpa_supplicant_associate(wpa_s, NULL, ssid);
+}
+
+
+static void wpas_trigger_scan_cb(struct wpa_radio_work *work, int deinit)
+{
+ struct wpa_supplicant *wpa_s = work->wpa_s;
+ struct wpa_driver_scan_params *params = work->ctx;
+ int ret;
+
+ if (deinit) {
+ if (!work->started) {
+ wpa_scan_free_params(params);
+ return;
+ }
+ wpa_supplicant_notify_scanning(wpa_s, 0);
+ wpas_notify_scan_done(wpa_s, 0);
+ wpa_s->scan_work = NULL;
+ return;
+ }
+
+ if (wpas_update_random_addr_disassoc(wpa_s) < 0) {
+ wpa_msg(wpa_s, MSG_INFO,
+ "Failed to assign random MAC address for a scan");
+ radio_work_done(work);
+ return;
+ }
+
+ wpa_supplicant_notify_scanning(wpa_s, 1);
+
+ if (wpa_s->clear_driver_scan_cache) {
+ wpa_printf(MSG_DEBUG,
+ "Request driver to clear scan cache due to local BSS flush");
+ params->only_new_results = 1;
+ }
+ ret = wpa_drv_scan(wpa_s, params);
+ wpa_scan_free_params(params);
+ work->ctx = NULL;
+ if (ret) {
+ int retry = wpa_s->last_scan_req != MANUAL_SCAN_REQ;
+
+ if (wpa_s->disconnected)
+ retry = 0;
+
+ wpa_supplicant_notify_scanning(wpa_s, 0);
+ wpas_notify_scan_done(wpa_s, 0);
+ if (wpa_s->wpa_state == WPA_SCANNING)
+ wpa_supplicant_set_state(wpa_s,
+ wpa_s->scan_prev_wpa_state);
+ wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_SCAN_FAILED "ret=%d%s",
+ ret, retry ? " retry=1" : "");
+ radio_work_done(work);
+
+ if (retry) {
+ /* Restore scan_req since we will try to scan again */
+ wpa_s->scan_req = wpa_s->last_scan_req;
+ wpa_supplicant_req_scan(wpa_s, 1, 0);
+ }
+ return;
+ }
+
+ os_get_reltime(&wpa_s->scan_trigger_time);
+ wpa_s->scan_runs++;
+ wpa_s->normal_scans++;
+ wpa_s->own_scan_requested = 1;
+ wpa_s->clear_driver_scan_cache = 0;
+ wpa_s->scan_work = work;
+}
+
+
+/**
+ * wpa_supplicant_trigger_scan - Request driver to start a scan
+ * @wpa_s: Pointer to wpa_supplicant data
+ * @params: Scan parameters
+ * Returns: 0 on success, -1 on failure
+ */
+int wpa_supplicant_trigger_scan(struct wpa_supplicant *wpa_s,
+ struct wpa_driver_scan_params *params)
+{
+ struct wpa_driver_scan_params *ctx;
+
+ if (wpa_s->scan_work) {
+ wpa_dbg(wpa_s, MSG_INFO, "Reject scan trigger since one is already pending");
+ return -1;
+ }
+
+ ctx = wpa_scan_clone_params(params);
+ if (ctx == NULL)
+ return -1;
+
+ if (radio_add_work(wpa_s, 0, "scan", 0, wpas_trigger_scan_cb, ctx) < 0)
+ {
+ wpa_scan_free_params(ctx);
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static void
+wpa_supplicant_delayed_sched_scan_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+ struct wpa_supplicant *wpa_s = eloop_ctx;
+
+ wpa_dbg(wpa_s, MSG_DEBUG, "Starting delayed sched scan");
+
+ if (wpa_supplicant_req_sched_scan(wpa_s))
+ wpa_supplicant_req_scan(wpa_s, 0, 0);
+}
+
+
+static void
+wpa_supplicant_sched_scan_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+ struct wpa_supplicant *wpa_s = eloop_ctx;
+
+ wpa_dbg(wpa_s, MSG_DEBUG, "Sched scan timeout - stopping it");
+
+ wpa_s->sched_scan_timed_out = 1;
+ wpa_supplicant_cancel_sched_scan(wpa_s);
+}
+
+
+int wpa_supplicant_start_sched_scan(struct wpa_supplicant *wpa_s,
+ struct wpa_driver_scan_params *params,
+ int interval)
+{
+ int ret;
+
+ wpa_supplicant_notify_scanning(wpa_s, 1);
+ ret = wpa_drv_sched_scan(wpa_s, params, interval * 1000);
+ if (ret)
+ wpa_supplicant_notify_scanning(wpa_s, 0);
+ else
+ wpa_s->sched_scanning = 1;
+
+ return ret;
+}
+
+
+int wpa_supplicant_stop_sched_scan(struct wpa_supplicant *wpa_s)
+{
+ int ret;
+
+ ret = wpa_drv_stop_sched_scan(wpa_s);
+ if (ret) {
+ wpa_dbg(wpa_s, MSG_DEBUG, "stopping sched_scan failed!");
+ /* TODO: what to do if stopping fails? */
+ return -1;
+ }
+
+ return ret;
+}
+
+
+static struct wpa_driver_scan_filter *
+wpa_supplicant_build_filter_ssids(struct wpa_config *conf, size_t *num_ssids)
+{
+ struct wpa_driver_scan_filter *ssids;
+ struct wpa_ssid *ssid;
+ size_t count;
+
+ *num_ssids = 0;
+ if (!conf->filter_ssids)
+ return NULL;
+
+ for (count = 0, ssid = conf->ssid; ssid; ssid = ssid->next) {
+ if (ssid->ssid && ssid->ssid_len)
+ count++;
+ }
+ if (count == 0)
+ return NULL;
+ ssids = os_calloc(count, sizeof(struct wpa_driver_scan_filter));
+ if (ssids == NULL)
+ return NULL;
+
+ for (ssid = conf->ssid; ssid; ssid = ssid->next) {
+ if (!ssid->ssid || !ssid->ssid_len)
+ continue;
+ os_memcpy(ssids[*num_ssids].ssid, ssid->ssid, ssid->ssid_len);
+ ssids[*num_ssids].ssid_len = ssid->ssid_len;
+ (*num_ssids)++;
+ }
+
+ return ssids;
+}
+
+
+static void wpa_supplicant_optimize_freqs(
+ struct wpa_supplicant *wpa_s, struct wpa_driver_scan_params *params)
+{
+#ifdef CONFIG_P2P
+ if (params->freqs == NULL && wpa_s->p2p_in_provisioning &&
+ wpa_s->go_params) {
+ /* Optimize provisioning state scan based on GO information */
+ if (wpa_s->p2p_in_provisioning < 5 &&
+ wpa_s->go_params->freq > 0) {
+ wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Scan only GO "
+ "preferred frequency %d MHz",
+ wpa_s->go_params->freq);
+ params->freqs = os_calloc(2, sizeof(int));
+ if (params->freqs)
+ params->freqs[0] = wpa_s->go_params->freq;
+ } else if (wpa_s->p2p_in_provisioning < 8 &&
+ wpa_s->go_params->freq_list[0]) {
+ wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Scan only common "
+ "channels");
+ int_array_concat(&params->freqs,
+ wpa_s->go_params->freq_list);
+ if (params->freqs)
+ int_array_sort_unique(params->freqs);
+ }
+ wpa_s->p2p_in_provisioning++;
+ }
+
+ if (params->freqs == NULL && wpa_s->p2p_in_invitation) {
+ /*
+ * Optimize scan based on GO information during persistent
+ * group reinvocation
+ */
+ if (wpa_s->p2p_in_invitation < 5 &&
+ wpa_s->p2p_invite_go_freq > 0) {
+ wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Scan only GO preferred frequency %d MHz during invitation",
+ wpa_s->p2p_invite_go_freq);
+ params->freqs = os_calloc(2, sizeof(int));
+ if (params->freqs)
+ params->freqs[0] = wpa_s->p2p_invite_go_freq;
+ }
+ wpa_s->p2p_in_invitation++;
+ if (wpa_s->p2p_in_invitation > 20) {
+ /*
+ * This should not really happen since the variable is
+ * cleared on group removal, but if it does happen, make
+ * sure we do not get stuck in special invitation scan
+ * mode.
+ */
+ wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Clear p2p_in_invitation");
+ wpa_s->p2p_in_invitation = 0;
+ }
+ }
+#endif /* CONFIG_P2P */
+
+#ifdef CONFIG_WPS
+ if (params->freqs == NULL && wpa_s->after_wps && wpa_s->wps_freq) {
+ /*
+ * Optimize post-provisioning scan based on channel used
+ * during provisioning.
+ */
+ wpa_dbg(wpa_s, MSG_DEBUG, "WPS: Scan only frequency %u MHz "
+ "that was used during provisioning", wpa_s->wps_freq);
+ params->freqs = os_calloc(2, sizeof(int));
+ if (params->freqs)
+ params->freqs[0] = wpa_s->wps_freq;
+ wpa_s->after_wps--;
+ } else if (wpa_s->after_wps)
+ wpa_s->after_wps--;
+
+ if (params->freqs == NULL && wpa_s->known_wps_freq && wpa_s->wps_freq)
+ {
+ /* Optimize provisioning scan based on already known channel */
+ wpa_dbg(wpa_s, MSG_DEBUG, "WPS: Scan only frequency %u MHz",
+ wpa_s->wps_freq);
+ params->freqs = os_calloc(2, sizeof(int));
+ if (params->freqs)
+ params->freqs[0] = wpa_s->wps_freq;
+ wpa_s->known_wps_freq = 0; /* only do this once */
+ }
+#endif /* CONFIG_WPS */
+}
+
+
+#ifdef CONFIG_INTERWORKING
+static void wpas_add_interworking_elements(struct wpa_supplicant *wpa_s,
+ struct wpabuf *buf)
+{
+ wpabuf_put_u8(buf, WLAN_EID_INTERWORKING);
+ wpabuf_put_u8(buf, is_zero_ether_addr(wpa_s->conf->hessid) ? 1 :
+ 1 + ETH_ALEN);
+ wpabuf_put_u8(buf, wpa_s->conf->access_network_type);
+ /* No Venue Info */
+ if (!is_zero_ether_addr(wpa_s->conf->hessid))
+ wpabuf_put_data(buf, wpa_s->conf->hessid, ETH_ALEN);
+}
+#endif /* CONFIG_INTERWORKING */
+
+
+static struct wpabuf * wpa_supplicant_extra_ies(struct wpa_supplicant *wpa_s)
+{
+ struct wpabuf *extra_ie = NULL;
+ u8 ext_capab[18];
+ int ext_capab_len;
+#ifdef CONFIG_WPS
+ int wps = 0;
+ enum wps_request_type req_type = WPS_REQ_ENROLLEE_INFO;
+#endif /* CONFIG_WPS */
+
+ ext_capab_len = wpas_build_ext_capab(wpa_s, ext_capab,
+ sizeof(ext_capab));
+ if (ext_capab_len > 0 &&
+ wpabuf_resize(&extra_ie, ext_capab_len) == 0)
+ wpabuf_put_data(extra_ie, ext_capab, ext_capab_len);
+
+#ifdef CONFIG_INTERWORKING
+ if (wpa_s->conf->interworking &&
+ wpabuf_resize(&extra_ie, 100) == 0)
+ wpas_add_interworking_elements(wpa_s, extra_ie);
+#endif /* CONFIG_INTERWORKING */
+
+#ifdef CONFIG_WPS
+ wps = wpas_wps_in_use(wpa_s, &req_type);
+
+ if (wps) {
+ struct wpabuf *wps_ie;
+ wps_ie = wps_build_probe_req_ie(wps == 2 ? DEV_PW_PUSHBUTTON :
+ DEV_PW_DEFAULT,
+ &wpa_s->wps->dev,
+ wpa_s->wps->uuid, req_type,
+ 0, NULL);
+ if (wps_ie) {
+ if (wpabuf_resize(&extra_ie, wpabuf_len(wps_ie)) == 0)
+ wpabuf_put_buf(extra_ie, wps_ie);
+ wpabuf_free(wps_ie);
+ }
+ }
+
+#ifdef CONFIG_P2P
+ if (wps) {
+ size_t ielen = p2p_scan_ie_buf_len(wpa_s->global->p2p);
+ if (wpabuf_resize(&extra_ie, ielen) == 0)
+ wpas_p2p_scan_ie(wpa_s, extra_ie);
+ }
+#endif /* CONFIG_P2P */
+
+ wpa_supplicant_mesh_add_scan_ie(wpa_s, &extra_ie);
+
+#endif /* CONFIG_WPS */
+
+#ifdef CONFIG_HS20
+ if (wpa_s->conf->hs20 && wpabuf_resize(&extra_ie, 7) == 0)
+ wpas_hs20_add_indication(extra_ie, -1);
+#endif /* CONFIG_HS20 */
+
+#ifdef CONFIG_FST
+ if (wpa_s->fst_ies &&
+ wpabuf_resize(&extra_ie, wpabuf_len(wpa_s->fst_ies)) == 0)
+ wpabuf_put_buf(extra_ie, wpa_s->fst_ies);
+#endif /* CONFIG_FST */
+
+ return extra_ie;
+}
+
+
+#ifdef CONFIG_P2P
+
+/*
+ * Check whether there are any enabled networks or credentials that could be
+ * used for a non-P2P connection.
+ */
+static int non_p2p_network_enabled(struct wpa_supplicant *wpa_s)
+{
+ struct wpa_ssid *ssid;
+
+ for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next) {
+ if (wpas_network_disabled(wpa_s, ssid))
+ continue;
+ if (!ssid->p2p_group)
+ return 1;
+ }
+
+ if (wpa_s->conf->cred && wpa_s->conf->interworking &&
+ wpa_s->conf->auto_interworking)
+ return 1;
+
+ return 0;
+}
+
+#endif /* CONFIG_P2P */
+
+
+static struct hostapd_hw_modes * get_mode(struct hostapd_hw_modes *modes,
+ u16 num_modes,
+ enum hostapd_hw_mode mode)
+{
+ u16 i;
+
+ for (i = 0; i < num_modes; i++) {
+ if (modes[i].mode == mode)
+ return &modes[i];
+ }
+
+ return NULL;
+}
+
+
+static void wpa_setband_scan_freqs_list(struct wpa_supplicant *wpa_s,
+ enum hostapd_hw_mode band,
+ struct wpa_driver_scan_params *params)
+{
+ /* Include only supported channels for the specified band */
+ struct hostapd_hw_modes *mode;
+ int count, i;
+
+ mode = get_mode(wpa_s->hw.modes, wpa_s->hw.num_modes, band);
+ if (mode == NULL) {
+ /* No channels supported in this band - use empty list */
+ params->freqs = os_zalloc(sizeof(int));
+ return;
+ }
+
+ params->freqs = os_calloc(mode->num_channels + 1, sizeof(int));
+ if (params->freqs == NULL)
+ return;
+ for (count = 0, i = 0; i < mode->num_channels; i++) {
+ if (mode->channels[i].flag & HOSTAPD_CHAN_DISABLED)
+ continue;
+ params->freqs[count++] = mode->channels[i].freq;
+ }
+}
+
+
+static void wpa_setband_scan_freqs(struct wpa_supplicant *wpa_s,
+ struct wpa_driver_scan_params *params)
+{
+ if (wpa_s->hw.modes == NULL)
+ return; /* unknown what channels the driver supports */
+ if (params->freqs)
+ return; /* already using a limited channel set */
+ if (wpa_s->setband == WPA_SETBAND_5G)
+ wpa_setband_scan_freqs_list(wpa_s, HOSTAPD_MODE_IEEE80211A,
+ params);
+ else if (wpa_s->setband == WPA_SETBAND_2G)
+ wpa_setband_scan_freqs_list(wpa_s, HOSTAPD_MODE_IEEE80211G,
+ params);
+}
+
+
+static void wpa_set_scan_ssids(struct wpa_supplicant *wpa_s,
+ struct wpa_driver_scan_params *params,
+ size_t max_ssids)
+{
+ unsigned int i;
+ struct wpa_ssid *ssid;
+
+ for (i = 0; i < wpa_s->scan_id_count; i++) {
+ unsigned int j;
+
+ ssid = wpa_config_get_network(wpa_s->conf, wpa_s->scan_id[i]);
+ if (!ssid || !ssid->scan_ssid)
+ continue;
+
+ for (j = 0; j < params->num_ssids; j++) {
+ if (params->ssids[j].ssid_len == ssid->ssid_len &&
+ params->ssids[j].ssid &&
+ os_memcmp(params->ssids[j].ssid, ssid->ssid,
+ ssid->ssid_len) == 0)
+ break;
+ }
+ if (j < params->num_ssids)
+ continue; /* already in the list */
+
+ if (params->num_ssids + 1 > max_ssids) {
+ wpa_printf(MSG_DEBUG,
+ "Over max scan SSIDs for manual request");
+ break;
+ }
+
+ wpa_printf(MSG_DEBUG, "Scan SSID (manual request): %s",
+ wpa_ssid_txt(ssid->ssid, ssid->ssid_len));
+ params->ssids[params->num_ssids].ssid = ssid->ssid;
+ params->ssids[params->num_ssids].ssid_len = ssid->ssid_len;
+ params->num_ssids++;
+ }
+
+ wpa_s->scan_id_count = 0;
+}
+
+
+static int wpa_set_ssids_from_scan_req(struct wpa_supplicant *wpa_s,
+ struct wpa_driver_scan_params *params,
+ size_t max_ssids)
+{
+ unsigned int i;
+
+ if (wpa_s->ssids_from_scan_req == NULL ||
+ wpa_s->num_ssids_from_scan_req == 0)
+ return 0;
+
+ if (wpa_s->num_ssids_from_scan_req > max_ssids) {
+ wpa_s->num_ssids_from_scan_req = max_ssids;
+ wpa_printf(MSG_DEBUG, "Over max scan SSIDs from scan req: %u",
+ (unsigned int) max_ssids);
+ }
+
+ for (i = 0; i < wpa_s->num_ssids_from_scan_req; i++) {
+ params->ssids[i].ssid = wpa_s->ssids_from_scan_req[i].ssid;
+ params->ssids[i].ssid_len =
+ wpa_s->ssids_from_scan_req[i].ssid_len;
+ wpa_hexdump_ascii(MSG_DEBUG, "specific SSID",
+ params->ssids[i].ssid,
+ params->ssids[i].ssid_len);
+ }
+
+ params->num_ssids = wpa_s->num_ssids_from_scan_req;
+ wpa_s->num_ssids_from_scan_req = 0;
+ return 1;
+}
+
+
+static void wpa_supplicant_scan(void *eloop_ctx, void *timeout_ctx)
+{
+ struct wpa_supplicant *wpa_s = eloop_ctx;
+ struct wpa_ssid *ssid;
+ int ret, p2p_in_prog;
+ struct wpabuf *extra_ie = NULL;
+ struct wpa_driver_scan_params params;
+ struct wpa_driver_scan_params *scan_params;
+ size_t max_ssids;
+ int connect_without_scan = 0;
+
+ if (wpa_s->pno || wpa_s->pno_sched_pending) {
+ wpa_dbg(wpa_s, MSG_DEBUG, "Skip scan - PNO is in progress");
+ return;
+ }
+
+ if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED) {
+ wpa_dbg(wpa_s, MSG_DEBUG, "Skip scan - interface disabled");
+ return;
+ }
+
+ if (wpa_s->disconnected && wpa_s->scan_req == NORMAL_SCAN_REQ) {
+ wpa_dbg(wpa_s, MSG_DEBUG, "Disconnected - do not scan");
+ wpa_supplicant_set_state(wpa_s, WPA_DISCONNECTED);
+ return;
+ }
+
+ if (wpa_s->scanning) {
+ /*
+ * If we are already in scanning state, we shall reschedule the
+ * the incoming scan request.
+ */
+ wpa_dbg(wpa_s, MSG_DEBUG, "Already scanning - Reschedule the incoming scan req");
+ wpa_supplicant_req_scan(wpa_s, 1, 0);
+ return;
+ }
+
+ if (!wpa_supplicant_enabled_networks(wpa_s) &&
+ wpa_s->scan_req == NORMAL_SCAN_REQ) {
+ wpa_dbg(wpa_s, MSG_DEBUG, "No enabled networks - do not scan");
+ wpa_supplicant_set_state(wpa_s, WPA_INACTIVE);
+ return;
+ }
+
+ if (wpa_s->conf->ap_scan != 0 &&
+ (wpa_s->drv_flags & WPA_DRIVER_FLAGS_WIRED)) {
+ wpa_dbg(wpa_s, MSG_DEBUG, "Using wired authentication - "
+ "overriding ap_scan configuration");
+ wpa_s->conf->ap_scan = 0;
+ wpas_notify_ap_scan_changed(wpa_s);
+ }
+
+ if (wpa_s->conf->ap_scan == 0) {
+ wpa_supplicant_gen_assoc_event(wpa_s);
+ return;
+ }
+
+ ssid = NULL;
+ if (wpa_s->scan_req != MANUAL_SCAN_REQ &&
+ wpa_s->connect_without_scan) {
+ connect_without_scan = 1;
+ for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next) {
+ if (ssid == wpa_s->connect_without_scan)
+ break;
+ }
+ }
+
+ p2p_in_prog = wpas_p2p_in_progress(wpa_s);
+ if (p2p_in_prog && p2p_in_prog != 2 &&
+ (!ssid ||
+ (ssid->mode != WPAS_MODE_AP && ssid->mode != WPAS_MODE_P2P_GO))) {
+ wpa_dbg(wpa_s, MSG_DEBUG, "Delay station mode scan while P2P operation is in progress");
+ wpa_supplicant_req_scan(wpa_s, 5, 0);
+ return;
+ }
+
+ if (wpa_s->conf->ap_scan == 2)
+ max_ssids = 1;
+ else {
+ max_ssids = wpa_s->max_scan_ssids;
+ if (max_ssids > WPAS_MAX_SCAN_SSIDS)
+ max_ssids = WPAS_MAX_SCAN_SSIDS;
+ }
+
+ wpa_s->last_scan_req = wpa_s->scan_req;
+ wpa_s->scan_req = NORMAL_SCAN_REQ;
+
+ if (connect_without_scan) {
+ wpa_s->connect_without_scan = NULL;
+ if (ssid) {
+ wpa_printf(MSG_DEBUG, "Start a pre-selected network "
+ "without scan step");
+ wpa_supplicant_associate(wpa_s, NULL, ssid);
+ return;
+ }
+ }
+
+ os_memset(&params, 0, sizeof(params));
+
+ wpa_s->scan_prev_wpa_state = wpa_s->wpa_state;
+ if (wpa_s->wpa_state == WPA_DISCONNECTED ||
+ wpa_s->wpa_state == WPA_INACTIVE)
+ wpa_supplicant_set_state(wpa_s, WPA_SCANNING);
+
+ /*
+ * If autoscan has set its own scanning parameters
+ */
+ if (wpa_s->autoscan_params != NULL) {
+ scan_params = wpa_s->autoscan_params;
+ goto scan;
+ }
+
+ if (wpa_s->last_scan_req == MANUAL_SCAN_REQ &&
+ wpa_set_ssids_from_scan_req(wpa_s, &params, max_ssids)) {
+ wpa_printf(MSG_DEBUG, "Use specific SSIDs from SCAN command");
+ goto ssid_list_set;
+ }
+
+#ifdef CONFIG_P2P
+ if ((wpa_s->p2p_in_provisioning || wpa_s->show_group_started) &&
+ wpa_s->go_params && !wpa_s->conf->passive_scan) {
+ wpa_printf(MSG_DEBUG, "P2P: Use specific SSID for scan during P2P group formation (p2p_in_provisioning=%d show_group_started=%d)",
+ wpa_s->p2p_in_provisioning,
+ wpa_s->show_group_started);
+ params.ssids[0].ssid = wpa_s->go_params->ssid;
+ params.ssids[0].ssid_len = wpa_s->go_params->ssid_len;
+ params.num_ssids = 1;
+ goto ssid_list_set;
+ }
+
+ if (wpa_s->p2p_in_invitation) {
+ if (wpa_s->current_ssid) {
+ wpa_printf(MSG_DEBUG, "P2P: Use specific SSID for scan during invitation");
+ params.ssids[0].ssid = wpa_s->current_ssid->ssid;
+ params.ssids[0].ssid_len =
+ wpa_s->current_ssid->ssid_len;
+ params.num_ssids = 1;
+ } else {
+ wpa_printf(MSG_DEBUG, "P2P: No specific SSID known for scan during invitation");
+ }
+ goto ssid_list_set;
+ }
+#endif /* CONFIG_P2P */
+
+ /* Find the starting point from which to continue scanning */
+ ssid = wpa_s->conf->ssid;
+ if (wpa_s->prev_scan_ssid != WILDCARD_SSID_SCAN) {
+ while (ssid) {
+ if (ssid == wpa_s->prev_scan_ssid) {
+ ssid = ssid->next;
+ break;
+ }
+ ssid = ssid->next;
+ }
+ }
+
+ if (wpa_s->last_scan_req != MANUAL_SCAN_REQ &&
+#ifdef CONFIG_AP
+ !wpa_s->ap_iface &&
+#endif /* CONFIG_AP */
+ wpa_s->conf->ap_scan == 2) {
+ wpa_s->connect_without_scan = NULL;
+ wpa_s->prev_scan_wildcard = 0;
+ wpa_supplicant_assoc_try(wpa_s, ssid);
+ return;
+ } else if (wpa_s->conf->ap_scan == 2) {
+ /*
+ * User-initiated scan request in ap_scan == 2; scan with
+ * wildcard SSID.
+ */
+ ssid = NULL;
+ } else if (wpa_s->reattach && wpa_s->current_ssid != NULL) {
+ /*
+ * Perform single-channel single-SSID scan for
+ * reassociate-to-same-BSS operation.
+ */
+ /* Setup SSID */
+ ssid = wpa_s->current_ssid;
+ wpa_hexdump_ascii(MSG_DEBUG, "Scan SSID",
+ ssid->ssid, ssid->ssid_len);
+ params.ssids[0].ssid = ssid->ssid;
+ params.ssids[0].ssid_len = ssid->ssid_len;
+ params.num_ssids = 1;
+
+ /*
+ * Allocate memory for frequency array, allocate one extra
+ * slot for the zero-terminator.
+ */
+ params.freqs = os_malloc(sizeof(int) * 2);
+ if (params.freqs == NULL) {
+ wpa_dbg(wpa_s, MSG_ERROR, "Memory allocation failed");
+ return;
+ }
+ params.freqs[0] = wpa_s->assoc_freq;
+ params.freqs[1] = 0;
+
+ /*
+ * Reset the reattach flag so that we fall back to full scan if
+ * this scan fails.
+ */
+ wpa_s->reattach = 0;
+ } else {
+ struct wpa_ssid *start = ssid, *tssid;
+ int freqs_set = 0;
+ if (ssid == NULL && max_ssids > 1)
+ ssid = wpa_s->conf->ssid;
+ while (ssid) {
+ if (!wpas_network_disabled(wpa_s, ssid) &&
+ ssid->scan_ssid) {
+ wpa_hexdump_ascii(MSG_DEBUG, "Scan SSID",
+ ssid->ssid, ssid->ssid_len);
+ params.ssids[params.num_ssids].ssid =
+ ssid->ssid;
+ params.ssids[params.num_ssids].ssid_len =
+ ssid->ssid_len;
+ params.num_ssids++;
+ if (params.num_ssids + 1 >= max_ssids)
+ break;
+ }
+ ssid = ssid->next;
+ if (ssid == start)
+ break;
+ if (ssid == NULL && max_ssids > 1 &&
+ start != wpa_s->conf->ssid)
+ ssid = wpa_s->conf->ssid;
+ }
+
+ if (wpa_s->scan_id_count &&
+ wpa_s->last_scan_req == MANUAL_SCAN_REQ)
+ wpa_set_scan_ssids(wpa_s, &params, max_ssids);
+
+ for (tssid = wpa_s->conf->ssid;
+ wpa_s->last_scan_req != MANUAL_SCAN_REQ && tssid;
+ tssid = tssid->next) {
+ if (wpas_network_disabled(wpa_s, tssid))
+ continue;
+ if ((params.freqs || !freqs_set) && tssid->scan_freq) {
+ int_array_concat(&params.freqs,
+ tssid->scan_freq);
+ } else {
+ os_free(params.freqs);
+ params.freqs = NULL;
+ }
+ freqs_set = 1;
+ }
+ int_array_sort_unique(params.freqs);
+ }
+
+ if (ssid && max_ssids == 1) {
+ /*
+ * If the driver is limited to 1 SSID at a time interleave
+ * wildcard SSID scans with specific SSID scans to avoid
+ * waiting a long time for a wildcard scan.
+ */
+ if (!wpa_s->prev_scan_wildcard) {
+ params.ssids[0].ssid = NULL;
+ params.ssids[0].ssid_len = 0;
+ wpa_s->prev_scan_wildcard = 1;
+ wpa_dbg(wpa_s, MSG_DEBUG, "Starting AP scan for "
+ "wildcard SSID (Interleave with specific)");
+ } else {
+ wpa_s->prev_scan_ssid = ssid;
+ wpa_s->prev_scan_wildcard = 0;
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ "Starting AP scan for specific SSID: %s",
+ wpa_ssid_txt(ssid->ssid, ssid->ssid_len));
+ }
+ } else if (ssid) {
+ /* max_ssids > 1 */
+
+ wpa_s->prev_scan_ssid = ssid;
+ wpa_dbg(wpa_s, MSG_DEBUG, "Include wildcard SSID in "
+ "the scan request");
+ params.num_ssids++;
+ } else if (wpa_s->last_scan_req == MANUAL_SCAN_REQ &&
+ wpa_s->manual_scan_passive && params.num_ssids == 0) {
+ wpa_dbg(wpa_s, MSG_DEBUG, "Use passive scan based on manual request");
+ } else if (wpa_s->conf->passive_scan) {
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ "Use passive scan based on configuration");
+ } else {
+ wpa_s->prev_scan_ssid = WILDCARD_SSID_SCAN;
+ params.num_ssids++;
+ wpa_dbg(wpa_s, MSG_DEBUG, "Starting AP scan for wildcard "
+ "SSID");
+ }
+
+ssid_list_set:
+ wpa_supplicant_optimize_freqs(wpa_s, &params);
+ extra_ie = wpa_supplicant_extra_ies(wpa_s);
+
+ if (wpa_s->last_scan_req == MANUAL_SCAN_REQ &&
+ wpa_s->manual_scan_only_new) {
+ wpa_printf(MSG_DEBUG,
+ "Request driver to clear scan cache due to manual only_new=1 scan");
+ params.only_new_results = 1;
+ }
+
+ if (wpa_s->last_scan_req == MANUAL_SCAN_REQ && params.freqs == NULL &&
+ wpa_s->manual_scan_freqs) {
+ wpa_dbg(wpa_s, MSG_DEBUG, "Limit manual scan to specified channels");
+ params.freqs = wpa_s->manual_scan_freqs;
+ wpa_s->manual_scan_freqs = NULL;
+ }
+
+ if (params.freqs == NULL && wpa_s->next_scan_freqs) {
+ wpa_dbg(wpa_s, MSG_DEBUG, "Optimize scan based on previously "
+ "generated frequency list");
+ params.freqs = wpa_s->next_scan_freqs;
+ } else
+ os_free(wpa_s->next_scan_freqs);
+ wpa_s->next_scan_freqs = NULL;
+ wpa_setband_scan_freqs(wpa_s, &params);
+
+ /* See if user specified frequencies. If so, scan only those. */
+ if (wpa_s->conf->freq_list && !params.freqs) {
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ "Optimize scan based on conf->freq_list");
+ int_array_concat(&params.freqs, wpa_s->conf->freq_list);
+ }
+
+ /* Use current associated channel? */
+ if (wpa_s->conf->scan_cur_freq && !params.freqs) {
+ unsigned int num = wpa_s->num_multichan_concurrent;
+
+ params.freqs = os_calloc(num + 1, sizeof(int));
+ if (params.freqs) {
+ num = get_shared_radio_freqs(wpa_s, params.freqs, num);
+ if (num > 0) {
+ wpa_dbg(wpa_s, MSG_DEBUG, "Scan only the "
+ "current operating channels since "
+ "scan_cur_freq is enabled");
+ } else {
+ os_free(params.freqs);
+ params.freqs = NULL;
+ }
+ }
+ }
+
+ params.filter_ssids = wpa_supplicant_build_filter_ssids(
+ wpa_s->conf, &params.num_filter_ssids);
+ if (extra_ie) {
+ params.extra_ies = wpabuf_head(extra_ie);
+ params.extra_ies_len = wpabuf_len(extra_ie);
+ }
+
+#ifdef CONFIG_P2P
+ if (wpa_s->p2p_in_provisioning || wpa_s->p2p_in_invitation ||
+ (wpa_s->show_group_started && wpa_s->go_params)) {
+ /*
+ * The interface may not yet be in P2P mode, so we have to
+ * explicitly request P2P probe to disable CCK rates.
+ */
+ params.p2p_probe = 1;
+ }
+#endif /* CONFIG_P2P */
+
+ if (wpa_s->mac_addr_rand_enable & MAC_ADDR_RAND_SCAN) {
+ params.mac_addr_rand = 1;
+ if (wpa_s->mac_addr_scan) {
+ params.mac_addr = wpa_s->mac_addr_scan;
+ params.mac_addr_mask = wpa_s->mac_addr_scan + ETH_ALEN;
+ }
+ }
+
+ scan_params = &params;
+
+scan:
+#ifdef CONFIG_P2P
+ /*
+ * If the driver does not support multi-channel concurrency and a
+ * virtual interface that shares the same radio with the wpa_s interface
+ * is operating there may not be need to scan other channels apart from
+ * the current operating channel on the other virtual interface. Filter
+ * out other channels in case we are trying to find a connection for a
+ * station interface when we are not configured to prefer station
+ * connection and a concurrent operation is already in process.
+ */
+ if (wpa_s->scan_for_connection &&
+ wpa_s->last_scan_req == NORMAL_SCAN_REQ &&
+ !scan_params->freqs && !params.freqs &&
+ wpas_is_p2p_prioritized(wpa_s) &&
+ wpa_s->p2p_group_interface == NOT_P2P_GROUP_INTERFACE &&
+ non_p2p_network_enabled(wpa_s)) {
+ unsigned int num = wpa_s->num_multichan_concurrent;
+
+ params.freqs = os_calloc(num + 1, sizeof(int));
+ if (params.freqs) {
+ num = get_shared_radio_freqs(wpa_s, params.freqs, num);
+ if (num > 0 && num == wpa_s->num_multichan_concurrent) {
+ wpa_dbg(wpa_s, MSG_DEBUG, "Scan only the current operating channels since all channels are already used");
+ } else {
+ os_free(params.freqs);
+ params.freqs = NULL;
+ }
+ }
+ }
+#endif /* CONFIG_P2P */
+
+ ret = wpa_supplicant_trigger_scan(wpa_s, scan_params);
+
+ if (ret && wpa_s->last_scan_req == MANUAL_SCAN_REQ && params.freqs &&
+ !wpa_s->manual_scan_freqs) {
+ /* Restore manual_scan_freqs for the next attempt */
+ wpa_s->manual_scan_freqs = params.freqs;
+ params.freqs = NULL;
+ }
+
+ wpabuf_free(extra_ie);
+ os_free(params.freqs);
+ os_free(params.filter_ssids);
+
+ if (ret) {
+ wpa_msg(wpa_s, MSG_WARNING, "Failed to initiate AP scan");
+ if (wpa_s->scan_prev_wpa_state != wpa_s->wpa_state)
+ wpa_supplicant_set_state(wpa_s,
+ wpa_s->scan_prev_wpa_state);
+ /* Restore scan_req since we will try to scan again */
+ wpa_s->scan_req = wpa_s->last_scan_req;
+ wpa_supplicant_req_scan(wpa_s, 1, 0);
+ } else {
+ wpa_s->scan_for_connection = 0;
+#ifdef CONFIG_INTERWORKING
+ wpa_s->interworking_fast_assoc_tried = 0;
+#endif /* CONFIG_INTERWORKING */
+ }
+}
+
+
+void wpa_supplicant_update_scan_int(struct wpa_supplicant *wpa_s, int sec)
+{
+ struct os_reltime remaining, new_int;
+ int cancelled;
+
+ cancelled = eloop_cancel_timeout_one(wpa_supplicant_scan, wpa_s, NULL,
+ &remaining);
+
+ new_int.sec = sec;
+ new_int.usec = 0;
+ if (cancelled && os_reltime_before(&remaining, &new_int)) {
+ new_int.sec = remaining.sec;
+ new_int.usec = remaining.usec;
+ }
+
+ if (cancelled) {
+ eloop_register_timeout(new_int.sec, new_int.usec,
+ wpa_supplicant_scan, wpa_s, NULL);
+ }
+ wpa_s->scan_interval = sec;
+}
+
+
+/**
+ * wpa_supplicant_req_scan - Schedule a scan for neighboring access points
+ * @wpa_s: Pointer to wpa_supplicant data
+ * @sec: Number of seconds after which to scan
+ * @usec: Number of microseconds after which to scan
+ *
+ * This function is used to schedule a scan for neighboring access points after
+ * the specified time.
+ */
+void wpa_supplicant_req_scan(struct wpa_supplicant *wpa_s, int sec, int usec)
+{
+ int res;
+
+ if (wpa_s->p2p_mgmt) {
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ "Ignore scan request (%d.%06d sec) on p2p_mgmt interface",
+ sec, usec);
+ return;
+ }
+
+ res = eloop_deplete_timeout(sec, usec, wpa_supplicant_scan, wpa_s,
+ NULL);
+ if (res == 1) {
+ wpa_dbg(wpa_s, MSG_DEBUG, "Rescheduling scan request: %d.%06d sec",
+ sec, usec);
+ } else if (res == 0) {
+ wpa_dbg(wpa_s, MSG_DEBUG, "Ignore new scan request for %d.%06d sec since an earlier request is scheduled to trigger sooner",
+ sec, usec);
+ } else {
+ wpa_dbg(wpa_s, MSG_DEBUG, "Setting scan request: %d.%06d sec",
+ sec, usec);
+ eloop_register_timeout(sec, usec, wpa_supplicant_scan, wpa_s, NULL);
+ }
+}
+
+
+/**
+ * wpa_supplicant_delayed_sched_scan - Request a delayed scheduled scan
+ * @wpa_s: Pointer to wpa_supplicant data
+ * @sec: Number of seconds after which to scan
+ * @usec: Number of microseconds after which to scan
+ * Returns: 0 on success or -1 otherwise
+ *
+ * This function is used to schedule periodic scans for neighboring
+ * access points after the specified time.
+ */
+int wpa_supplicant_delayed_sched_scan(struct wpa_supplicant *wpa_s,
+ int sec, int usec)
+{
+ if (!wpa_s->sched_scan_supported)
+ return -1;
+
+ eloop_register_timeout(sec, usec,
+ wpa_supplicant_delayed_sched_scan_timeout,
+ wpa_s, NULL);
+
+ return 0;
+}
+
+
+/**
+ * wpa_supplicant_req_sched_scan - Start a periodic scheduled scan
+ * @wpa_s: Pointer to wpa_supplicant data
+ * Returns: 0 is sched_scan was started or -1 otherwise
+ *
+ * This function is used to schedule periodic scans for neighboring
+ * access points repeating the scan continuously.
+ */
+int wpa_supplicant_req_sched_scan(struct wpa_supplicant *wpa_s)
+{
+ struct wpa_driver_scan_params params;
+ struct wpa_driver_scan_params *scan_params;
+ enum wpa_states prev_state;
+ struct wpa_ssid *ssid = NULL;
+ struct wpabuf *extra_ie = NULL;
+ int ret;
+ unsigned int max_sched_scan_ssids;
+ int wildcard = 0;
+ int need_ssids;
+
+ if (!wpa_s->sched_scan_supported)
+ return -1;
+
+ if (wpa_s->max_sched_scan_ssids > WPAS_MAX_SCAN_SSIDS)
+ max_sched_scan_ssids = WPAS_MAX_SCAN_SSIDS;
+ else
+ max_sched_scan_ssids = wpa_s->max_sched_scan_ssids;
+ if (max_sched_scan_ssids < 1 || wpa_s->conf->disable_scan_offload)
+ return -1;
+
+ if (wpa_s->sched_scanning) {
+ wpa_dbg(wpa_s, MSG_DEBUG, "Already sched scanning");
+ return 0;
+ }
+
+ need_ssids = 0;
+ for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next) {
+ if (!wpas_network_disabled(wpa_s, ssid) && !ssid->scan_ssid) {
+ /* Use wildcard SSID to find this network */
+ wildcard = 1;
+ } else if (!wpas_network_disabled(wpa_s, ssid) &&
+ ssid->ssid_len)
+ need_ssids++;
+
+#ifdef CONFIG_WPS
+ if (!wpas_network_disabled(wpa_s, ssid) &&
+ ssid->key_mgmt == WPA_KEY_MGMT_WPS) {
+ /*
+ * Normal scan is more reliable and faster for WPS
+ * operations and since these are for short periods of
+ * time, the benefit of trying to use sched_scan would
+ * be limited.
+ */
+ wpa_dbg(wpa_s, MSG_DEBUG, "Use normal scan instead of "
+ "sched_scan for WPS");
+ return -1;
+ }
+#endif /* CONFIG_WPS */
+ }
+ if (wildcard)
+ need_ssids++;
+
+ if (wpa_s->normal_scans < 3 &&
+ (need_ssids <= wpa_s->max_scan_ssids ||
+ wpa_s->max_scan_ssids >= (int) max_sched_scan_ssids)) {
+ /*
+ * When normal scan can speed up operations, use that for the
+ * first operations before starting the sched_scan to allow
+ * user space sleep more. We do this only if the normal scan
+ * has functionality that is suitable for this or if the
+ * sched_scan does not have better support for multiple SSIDs.
+ */
+ wpa_dbg(wpa_s, MSG_DEBUG, "Use normal scan instead of "
+ "sched_scan for initial scans (normal_scans=%d)",
+ wpa_s->normal_scans);
+ return -1;
+ }
+
+ os_memset(&params, 0, sizeof(params));
+
+ /* If we can't allocate space for the filters, we just don't filter */
+ params.filter_ssids = os_calloc(wpa_s->max_match_sets,
+ sizeof(struct wpa_driver_scan_filter));
+
+ prev_state = wpa_s->wpa_state;
+ if (wpa_s->wpa_state == WPA_DISCONNECTED ||
+ wpa_s->wpa_state == WPA_INACTIVE)
+ wpa_supplicant_set_state(wpa_s, WPA_SCANNING);
+
+ if (wpa_s->autoscan_params != NULL) {
+ scan_params = wpa_s->autoscan_params;
+ goto scan;
+ }
+
+ /* Find the starting point from which to continue scanning */
+ ssid = wpa_s->conf->ssid;
+ if (wpa_s->prev_sched_ssid) {
+ while (ssid) {
+ if (ssid == wpa_s->prev_sched_ssid) {
+ ssid = ssid->next;
+ break;
+ }
+ ssid = ssid->next;
+ }
+ }
+
+ if (!ssid || !wpa_s->prev_sched_ssid) {
+ wpa_dbg(wpa_s, MSG_DEBUG, "Beginning of SSID list");
+ if (wpa_s->conf->sched_scan_interval)
+ wpa_s->sched_scan_interval =
+ wpa_s->conf->sched_scan_interval;
+ if (wpa_s->sched_scan_interval == 0)
+ wpa_s->sched_scan_interval = 10;
+ wpa_s->sched_scan_timeout = max_sched_scan_ssids * 2;
+ wpa_s->first_sched_scan = 1;
+ ssid = wpa_s->conf->ssid;
+ wpa_s->prev_sched_ssid = ssid;
+ }
+
+ if (wildcard) {
+ wpa_dbg(wpa_s, MSG_DEBUG, "Add wildcard SSID to sched_scan");
+ params.num_ssids++;
+ }
+
+ while (ssid) {
+ if (wpas_network_disabled(wpa_s, ssid))
+ goto next;
+
+ if (params.num_filter_ssids < wpa_s->max_match_sets &&
+ params.filter_ssids && ssid->ssid && ssid->ssid_len) {
+ wpa_dbg(wpa_s, MSG_DEBUG, "add to filter ssid: %s",
+ wpa_ssid_txt(ssid->ssid, ssid->ssid_len));
+ os_memcpy(params.filter_ssids[params.num_filter_ssids].ssid,
+ ssid->ssid, ssid->ssid_len);
+ params.filter_ssids[params.num_filter_ssids].ssid_len =
+ ssid->ssid_len;
+ params.num_filter_ssids++;
+ } else if (params.filter_ssids && ssid->ssid && ssid->ssid_len)
+ {
+ wpa_dbg(wpa_s, MSG_DEBUG, "Not enough room for SSID "
+ "filter for sched_scan - drop filter");
+ os_free(params.filter_ssids);
+ params.filter_ssids = NULL;
+ params.num_filter_ssids = 0;
+ }
+
+ if (ssid->scan_ssid && ssid->ssid && ssid->ssid_len) {
+ if (params.num_ssids == max_sched_scan_ssids)
+ break; /* only room for broadcast SSID */
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ "add to active scan ssid: %s",
+ wpa_ssid_txt(ssid->ssid, ssid->ssid_len));
+ params.ssids[params.num_ssids].ssid =
+ ssid->ssid;
+ params.ssids[params.num_ssids].ssid_len =
+ ssid->ssid_len;
+ params.num_ssids++;
+ if (params.num_ssids >= max_sched_scan_ssids) {
+ wpa_s->prev_sched_ssid = ssid;
+ do {
+ ssid = ssid->next;
+ } while (ssid &&
+ (wpas_network_disabled(wpa_s, ssid) ||
+ !ssid->scan_ssid));
+ break;
+ }
+ }
+
+ next:
+ wpa_s->prev_sched_ssid = ssid;
+ ssid = ssid->next;
+ }
+
+ if (params.num_filter_ssids == 0) {
+ os_free(params.filter_ssids);
+ params.filter_ssids = NULL;
+ }
+
+ extra_ie = wpa_supplicant_extra_ies(wpa_s);
+ if (extra_ie) {
+ params.extra_ies = wpabuf_head(extra_ie);
+ params.extra_ies_len = wpabuf_len(extra_ie);
+ }
+
+ if (wpa_s->conf->filter_rssi)
+ params.filter_rssi = wpa_s->conf->filter_rssi;
+
+ /* See if user specified frequencies. If so, scan only those. */
+ if (wpa_s->conf->freq_list && !params.freqs) {
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ "Optimize scan based on conf->freq_list");
+ int_array_concat(&params.freqs, wpa_s->conf->freq_list);
+ }
+
+ scan_params = &params;
+
+scan:
+ if (ssid || !wpa_s->first_sched_scan) {
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ "Starting sched scan: interval %d timeout %d",
+ wpa_s->sched_scan_interval, wpa_s->sched_scan_timeout);
+ } else {
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ "Starting sched scan: interval %d (no timeout)",
+ wpa_s->sched_scan_interval);
+ }
+
+ wpa_setband_scan_freqs(wpa_s, scan_params);
+
+ if (wpa_s->mac_addr_rand_enable & MAC_ADDR_RAND_SCHED_SCAN) {
+ params.mac_addr_rand = 1;
+ if (wpa_s->mac_addr_sched_scan) {
+ params.mac_addr = wpa_s->mac_addr_sched_scan;
+ params.mac_addr_mask = wpa_s->mac_addr_sched_scan +
+ ETH_ALEN;
+ }
+ }
+
+ ret = wpa_supplicant_start_sched_scan(wpa_s, scan_params,
+ wpa_s->sched_scan_interval);
+ wpabuf_free(extra_ie);
+ os_free(params.filter_ssids);
+ if (ret) {
+ wpa_msg(wpa_s, MSG_WARNING, "Failed to initiate sched scan");
+ if (prev_state != wpa_s->wpa_state)
+ wpa_supplicant_set_state(wpa_s, prev_state);
+ return ret;
+ }
+
+ /* If we have more SSIDs to scan, add a timeout so we scan them too */
+ if (ssid || !wpa_s->first_sched_scan) {
+ wpa_s->sched_scan_timed_out = 0;
+ eloop_register_timeout(wpa_s->sched_scan_timeout, 0,
+ wpa_supplicant_sched_scan_timeout,
+ wpa_s, NULL);
+ wpa_s->first_sched_scan = 0;
+ wpa_s->sched_scan_timeout /= 2;
+ wpa_s->sched_scan_interval *= 2;
+ if (wpa_s->sched_scan_timeout < wpa_s->sched_scan_interval) {
+ wpa_s->sched_scan_interval = 10;
+ wpa_s->sched_scan_timeout = max_sched_scan_ssids * 2;
+ }
+ }
+
+ /* If there is no more ssids, start next time from the beginning */
+ if (!ssid)
+ wpa_s->prev_sched_ssid = NULL;
+
+ return 0;
+}
+
+
+/**
+ * wpa_supplicant_cancel_scan - Cancel a scheduled scan request
+ * @wpa_s: Pointer to wpa_supplicant data
+ *
+ * This function is used to cancel a scan request scheduled with
+ * wpa_supplicant_req_scan().
+ */
+void wpa_supplicant_cancel_scan(struct wpa_supplicant *wpa_s)
+{
+ wpa_dbg(wpa_s, MSG_DEBUG, "Cancelling scan request");
+ eloop_cancel_timeout(wpa_supplicant_scan, wpa_s, NULL);
+}
+
+
+/**
+ * wpa_supplicant_cancel_delayed_sched_scan - Stop a delayed scheduled scan
+ * @wpa_s: Pointer to wpa_supplicant data
+ *
+ * This function is used to stop a delayed scheduled scan.
+ */
+void wpa_supplicant_cancel_delayed_sched_scan(struct wpa_supplicant *wpa_s)
+{
+ if (!wpa_s->sched_scan_supported)
+ return;
+
+ wpa_dbg(wpa_s, MSG_DEBUG, "Cancelling delayed sched scan");
+ eloop_cancel_timeout(wpa_supplicant_delayed_sched_scan_timeout,
+ wpa_s, NULL);
+}
+
+
+/**
+ * wpa_supplicant_cancel_sched_scan - Stop running scheduled scans
+ * @wpa_s: Pointer to wpa_supplicant data
+ *
+ * This function is used to stop a periodic scheduled scan.
+ */
+void wpa_supplicant_cancel_sched_scan(struct wpa_supplicant *wpa_s)
+{
+ if (!wpa_s->sched_scanning)
+ return;
+
+ wpa_dbg(wpa_s, MSG_DEBUG, "Cancelling sched scan");
+ eloop_cancel_timeout(wpa_supplicant_sched_scan_timeout, wpa_s, NULL);
+ wpa_supplicant_stop_sched_scan(wpa_s);
+}
+
+
+/**
+ * wpa_supplicant_notify_scanning - Indicate possible scan state change
+ * @wpa_s: Pointer to wpa_supplicant data
+ * @scanning: Whether scanning is currently in progress
+ *
+ * This function is to generate scanning notifycations. It is called whenever
+ * there may have been a change in scanning (scan started, completed, stopped).
+ * wpas_notify_scanning() is called whenever the scanning state changed from the
+ * previously notified state.
+ */
+void wpa_supplicant_notify_scanning(struct wpa_supplicant *wpa_s,
+ int scanning)
+{
+ if (wpa_s->scanning != scanning) {
+ wpa_s->scanning = scanning;
+ wpas_notify_scanning(wpa_s);
+ }
+}
+
+
+static int wpa_scan_get_max_rate(const struct wpa_scan_res *res)
+{
+ int rate = 0;
+ const u8 *ie;
+ int i;
+
+ ie = wpa_scan_get_ie(res, WLAN_EID_SUPP_RATES);
+ for (i = 0; ie && i < ie[1]; i++) {
+ if ((ie[i + 2] & 0x7f) > rate)
+ rate = ie[i + 2] & 0x7f;
+ }
+
+ ie = wpa_scan_get_ie(res, WLAN_EID_EXT_SUPP_RATES);
+ for (i = 0; ie && i < ie[1]; i++) {
+ if ((ie[i + 2] & 0x7f) > rate)
+ rate = ie[i + 2] & 0x7f;
+ }
+
+ return rate;
+}
+
+
+/**
+ * wpa_scan_get_ie - Fetch a specified information element from a scan result
+ * @res: Scan result entry
+ * @ie: Information element identitifier (WLAN_EID_*)
+ * Returns: Pointer to the information element (id field) or %NULL if not found
+ *
+ * This function returns the first matching information element in the scan
+ * result.
+ */
+const u8 * wpa_scan_get_ie(const struct wpa_scan_res *res, u8 ie)
+{
+ const u8 *end, *pos;
+
+ pos = (const u8 *) (res + 1);
+ end = pos + res->ie_len;
+
+ while (pos + 1 < end) {
+ if (pos + 2 + pos[1] > end)
+ break;
+ if (pos[0] == ie)
+ return pos;
+ pos += 2 + pos[1];
+ }
+
+ return NULL;
+}
+
+
+/**
+ * wpa_scan_get_vendor_ie - Fetch vendor information element from a scan result
+ * @res: Scan result entry
+ * @vendor_type: Vendor type (four octets starting the IE payload)
+ * Returns: Pointer to the information element (id field) or %NULL if not found
+ *
+ * This function returns the first matching information element in the scan
+ * result.
+ */
+const u8 * wpa_scan_get_vendor_ie(const struct wpa_scan_res *res,
+ u32 vendor_type)
+{
+ const u8 *end, *pos;
+
+ pos = (const u8 *) (res + 1);
+ end = pos + res->ie_len;
+
+ while (pos + 1 < end) {
+ if (pos + 2 + pos[1] > end)
+ break;
+ if (pos[0] == WLAN_EID_VENDOR_SPECIFIC && pos[1] >= 4 &&
+ vendor_type == WPA_GET_BE32(&pos[2]))
+ return pos;
+ pos += 2 + pos[1];
+ }
+
+ return NULL;
+}
+
+
+/**
+ * wpa_scan_get_vendor_ie_beacon - Fetch vendor information from a scan result
+ * @res: Scan result entry
+ * @vendor_type: Vendor type (four octets starting the IE payload)
+ * Returns: Pointer to the information element (id field) or %NULL if not found
+ *
+ * This function returns the first matching information element in the scan
+ * result.
+ *
+ * This function is like wpa_scan_get_vendor_ie(), but uses IE buffer only
+ * from Beacon frames instead of either Beacon or Probe Response frames.
+ */
+const u8 * wpa_scan_get_vendor_ie_beacon(const struct wpa_scan_res *res,
+ u32 vendor_type)
+{
+ const u8 *end, *pos;
+
+ if (res->beacon_ie_len == 0)
+ return NULL;
+
+ pos = (const u8 *) (res + 1);
+ pos += res->ie_len;
+ end = pos + res->beacon_ie_len;
+
+ while (pos + 1 < end) {
+ if (pos + 2 + pos[1] > end)
+ break;
+ if (pos[0] == WLAN_EID_VENDOR_SPECIFIC && pos[1] >= 4 &&
+ vendor_type == WPA_GET_BE32(&pos[2]))
+ return pos;
+ pos += 2 + pos[1];
+ }
+
+ return NULL;
+}
+
+
+/**
+ * wpa_scan_get_vendor_ie_multi - Fetch vendor IE data from a scan result
+ * @res: Scan result entry
+ * @vendor_type: Vendor type (four octets starting the IE payload)
+ * Returns: Pointer to the information element payload or %NULL if not found
+ *
+ * This function returns concatenated payload of possibly fragmented vendor
+ * specific information elements in the scan result. The caller is responsible
+ * for freeing the returned buffer.
+ */
+struct wpabuf * wpa_scan_get_vendor_ie_multi(const struct wpa_scan_res *res,
+ u32 vendor_type)
+{
+ struct wpabuf *buf;
+ const u8 *end, *pos;
+
+ buf = wpabuf_alloc(res->ie_len);
+ if (buf == NULL)
+ return NULL;
+
+ pos = (const u8 *) (res + 1);
+ end = pos + res->ie_len;
+
+ while (pos + 1 < end) {
+ if (pos + 2 + pos[1] > end)
+ break;
+ if (pos[0] == WLAN_EID_VENDOR_SPECIFIC && pos[1] >= 4 &&
+ vendor_type == WPA_GET_BE32(&pos[2]))
+ wpabuf_put_data(buf, pos + 2 + 4, pos[1] - 4);
+ pos += 2 + pos[1];
+ }
+
+ if (wpabuf_len(buf) == 0) {
+ wpabuf_free(buf);
+ buf = NULL;
+ }
+
+ return buf;
+}
+
+
+/*
+ * Channels with a great SNR can operate at full rate. What is a great SNR?
+ * This doc https://supportforums.cisco.com/docs/DOC-12954 says, "the general
+ * rule of thumb is that any SNR above 20 is good." This one
+ * http://www.cisco.com/en/US/tech/tk722/tk809/technologies_q_and_a_item09186a00805e9a96.shtml#qa23
+ * recommends 25 as a minimum SNR for 54 Mbps data rate. 30 is chosen here as a
+ * conservative value.
+ */
+#define GREAT_SNR 30
+
+#define IS_5GHZ(n) (n > 4000)
+
+/* Compare function for sorting scan results. Return >0 if @b is considered
+ * better. */
+static int wpa_scan_result_compar(const void *a, const void *b)
+{
+#define MIN(a,b) a < b ? a : b
+ struct wpa_scan_res **_wa = (void *) a;
+ struct wpa_scan_res **_wb = (void *) b;
+ struct wpa_scan_res *wa = *_wa;
+ struct wpa_scan_res *wb = *_wb;
+ int wpa_a, wpa_b;
+ int snr_a, snr_b, snr_a_full, snr_b_full;
+
+ /* WPA/WPA2 support preferred */
+ wpa_a = wpa_scan_get_vendor_ie(wa, WPA_IE_VENDOR_TYPE) != NULL ||
+ wpa_scan_get_ie(wa, WLAN_EID_RSN) != NULL;
+ wpa_b = wpa_scan_get_vendor_ie(wb, WPA_IE_VENDOR_TYPE) != NULL ||
+ wpa_scan_get_ie(wb, WLAN_EID_RSN) != NULL;
+
+ if (wpa_b && !wpa_a)
+ return 1;
+ if (!wpa_b && wpa_a)
+ return -1;
+
+ /* privacy support preferred */
+ if ((wa->caps & IEEE80211_CAP_PRIVACY) == 0 &&
+ (wb->caps & IEEE80211_CAP_PRIVACY))
+ return 1;
+ if ((wa->caps & IEEE80211_CAP_PRIVACY) &&
+ (wb->caps & IEEE80211_CAP_PRIVACY) == 0)
+ return -1;
+
+ if (wa->flags & wb->flags & WPA_SCAN_LEVEL_DBM) {
+ snr_a_full = wa->snr;
+ snr_a = MIN(wa->snr, GREAT_SNR);
+ snr_b_full = wb->snr;
+ snr_b = MIN(wb->snr, GREAT_SNR);
+ } else {
+ /* Level is not in dBm, so we can't calculate
+ * SNR. Just use raw level (units unknown). */
+ snr_a = snr_a_full = wa->level;
+ snr_b = snr_b_full = wb->level;
+ }
+
+ /* if SNR is close, decide by max rate or frequency band */
+ if ((snr_a && snr_b && abs(snr_b - snr_a) < 5) ||
+ (wa->qual && wb->qual && abs(wb->qual - wa->qual) < 10)) {
+ if (wa->est_throughput != wb->est_throughput)
+ return wb->est_throughput - wa->est_throughput;
+ if (IS_5GHZ(wa->freq) ^ IS_5GHZ(wb->freq))
+ return IS_5GHZ(wa->freq) ? -1 : 1;
+ }
+
+ /* all things being equal, use SNR; if SNRs are
+ * identical, use quality values since some drivers may only report
+ * that value and leave the signal level zero */
+ if (snr_b_full == snr_a_full)
+ return wb->qual - wa->qual;
+ return snr_b_full - snr_a_full;
+#undef MIN
+}
+
+
+#ifdef CONFIG_WPS
+/* Compare function for sorting scan results when searching a WPS AP for
+ * provisioning. Return >0 if @b is considered better. */
+static int wpa_scan_result_wps_compar(const void *a, const void *b)
+{
+ struct wpa_scan_res **_wa = (void *) a;
+ struct wpa_scan_res **_wb = (void *) b;
+ struct wpa_scan_res *wa = *_wa;
+ struct wpa_scan_res *wb = *_wb;
+ int uses_wps_a, uses_wps_b;
+ struct wpabuf *wps_a, *wps_b;
+ int res;
+
+ /* Optimization - check WPS IE existence before allocated memory and
+ * doing full reassembly. */
+ uses_wps_a = wpa_scan_get_vendor_ie(wa, WPS_IE_VENDOR_TYPE) != NULL;
+ uses_wps_b = wpa_scan_get_vendor_ie(wb, WPS_IE_VENDOR_TYPE) != NULL;
+ if (uses_wps_a && !uses_wps_b)
+ return -1;
+ if (!uses_wps_a && uses_wps_b)
+ return 1;
+
+ if (uses_wps_a && uses_wps_b) {
+ wps_a = wpa_scan_get_vendor_ie_multi(wa, WPS_IE_VENDOR_TYPE);
+ wps_b = wpa_scan_get_vendor_ie_multi(wb, WPS_IE_VENDOR_TYPE);
+ res = wps_ap_priority_compar(wps_a, wps_b);
+ wpabuf_free(wps_a);
+ wpabuf_free(wps_b);
+ if (res)
+ return res;
+ }
+
+ /*
+ * Do not use current AP security policy as a sorting criteria during
+ * WPS provisioning step since the AP may get reconfigured at the
+ * completion of provisioning.
+ */
+
+ /* all things being equal, use signal level; if signal levels are
+ * identical, use quality values since some drivers may only report
+ * that value and leave the signal level zero */
+ if (wb->level == wa->level)
+ return wb->qual - wa->qual;
+ return wb->level - wa->level;
+}
+#endif /* CONFIG_WPS */
+
+
+static void dump_scan_res(struct wpa_scan_results *scan_res)
+{
+#ifndef CONFIG_NO_STDOUT_DEBUG
+ size_t i;
+
+ if (scan_res->res == NULL || scan_res->num == 0)
+ return;
+
+ wpa_printf(MSG_EXCESSIVE, "Sorted scan results");
+
+ for (i = 0; i < scan_res->num; i++) {
+ struct wpa_scan_res *r = scan_res->res[i];
+ u8 *pos;
+ if (r->flags & WPA_SCAN_LEVEL_DBM) {
+ int noise_valid = !(r->flags & WPA_SCAN_NOISE_INVALID);
+
+ wpa_printf(MSG_EXCESSIVE, MACSTR " freq=%d qual=%d "
+ "noise=%d%s level=%d snr=%d%s flags=0x%x age=%u est=%u",
+ MAC2STR(r->bssid), r->freq, r->qual,
+ r->noise, noise_valid ? "" : "~", r->level,
+ r->snr, r->snr >= GREAT_SNR ? "*" : "",
+ r->flags,
+ r->age, r->est_throughput);
+ } else {
+ wpa_printf(MSG_EXCESSIVE, MACSTR " freq=%d qual=%d "
+ "noise=%d level=%d flags=0x%x age=%u est=%u",
+ MAC2STR(r->bssid), r->freq, r->qual,
+ r->noise, r->level, r->flags, r->age,
+ r->est_throughput);
+ }
+ pos = (u8 *) (r + 1);
+ if (r->ie_len)
+ wpa_hexdump(MSG_EXCESSIVE, "IEs", pos, r->ie_len);
+ pos += r->ie_len;
+ if (r->beacon_ie_len)
+ wpa_hexdump(MSG_EXCESSIVE, "Beacon IEs",
+ pos, r->beacon_ie_len);
+ }
+#endif /* CONFIG_NO_STDOUT_DEBUG */
+}
+
+
+/**
+ * wpa_supplicant_filter_bssid_match - Is the specified BSSID allowed
+ * @wpa_s: Pointer to wpa_supplicant data
+ * @bssid: BSSID to check
+ * Returns: 0 if the BSSID is filtered or 1 if not
+ *
+ * This function is used to filter out specific BSSIDs from scan reslts mainly
+ * for testing purposes (SET bssid_filter ctrl_iface command).
+ */
+int wpa_supplicant_filter_bssid_match(struct wpa_supplicant *wpa_s,
+ const u8 *bssid)
+{
+ size_t i;
+
+ if (wpa_s->bssid_filter == NULL)
+ return 1;
+
+ for (i = 0; i < wpa_s->bssid_filter_count; i++) {
+ if (os_memcmp(wpa_s->bssid_filter + i * ETH_ALEN, bssid,
+ ETH_ALEN) == 0)
+ return 1;
+ }
+
+ return 0;
+}
+
+
+static void filter_scan_res(struct wpa_supplicant *wpa_s,
+ struct wpa_scan_results *res)
+{
+ size_t i, j;
+
+ if (wpa_s->bssid_filter == NULL)
+ return;
+
+ for (i = 0, j = 0; i < res->num; i++) {
+ if (wpa_supplicant_filter_bssid_match(wpa_s,
+ res->res[i]->bssid)) {
+ res->res[j++] = res->res[i];
+ } else {
+ os_free(res->res[i]);
+ res->res[i] = NULL;
+ }
+ }
+
+ if (res->num != j) {
+ wpa_printf(MSG_DEBUG, "Filtered out %d scan results",
+ (int) (res->num - j));
+ res->num = j;
+ }
+}
+
+
+/*
+ * Noise floor values to use when we have signal strength
+ * measurements, but no noise floor measurments. These values were
+ * measured in an office environment with many APs.
+ */
+#define DEFAULT_NOISE_FLOOR_2GHZ (-89)
+#define DEFAULT_NOISE_FLOOR_5GHZ (-92)
+
+static void scan_snr(struct wpa_scan_res *res)
+{
+ if (res->flags & WPA_SCAN_NOISE_INVALID) {
+ res->noise = IS_5GHZ(res->freq) ?
+ DEFAULT_NOISE_FLOOR_5GHZ :
+ DEFAULT_NOISE_FLOOR_2GHZ;
+ }
+
+ if (res->flags & WPA_SCAN_LEVEL_DBM) {
+ res->snr = res->level - res->noise;
+ } else {
+ /* Level is not in dBm, so we can't calculate
+ * SNR. Just use raw level (units unknown). */
+ res->snr = res->level;
+ }
+}
+
+
+static unsigned int max_ht20_rate(int snr)
+{
+ if (snr < 6)
+ return 6500; /* HT20 MCS0 */
+ if (snr < 8)
+ return 13000; /* HT20 MCS1 */
+ if (snr < 13)
+ return 19500; /* HT20 MCS2 */
+ if (snr < 17)
+ return 26000; /* HT20 MCS3 */
+ if (snr < 20)
+ return 39000; /* HT20 MCS4 */
+ if (snr < 23)
+ return 52000; /* HT20 MCS5 */
+ if (snr < 24)
+ return 58500; /* HT20 MCS6 */
+ return 65000; /* HT20 MCS7 */
+}
+
+
+static unsigned int max_ht40_rate(int snr)
+{
+ if (snr < 3)
+ return 13500; /* HT40 MCS0 */
+ if (snr < 6)
+ return 27000; /* HT40 MCS1 */
+ if (snr < 10)
+ return 40500; /* HT40 MCS2 */
+ if (snr < 15)
+ return 54000; /* HT40 MCS3 */
+ if (snr < 17)
+ return 81000; /* HT40 MCS4 */
+ if (snr < 22)
+ return 108000; /* HT40 MCS5 */
+ if (snr < 24)
+ return 121500; /* HT40 MCS6 */
+ return 135000; /* HT40 MCS7 */
+}
+
+
+static unsigned int max_vht80_rate(int snr)
+{
+ if (snr < 1)
+ return 0;
+ if (snr < 2)
+ return 29300; /* VHT80 MCS0 */
+ if (snr < 5)
+ return 58500; /* VHT80 MCS1 */
+ if (snr < 9)
+ return 87800; /* VHT80 MCS2 */
+ if (snr < 11)
+ return 117000; /* VHT80 MCS3 */
+ if (snr < 15)
+ return 175500; /* VHT80 MCS4 */
+ if (snr < 16)
+ return 234000; /* VHT80 MCS5 */
+ if (snr < 18)
+ return 263300; /* VHT80 MCS6 */
+ if (snr < 20)
+ return 292500; /* VHT80 MCS7 */
+ if (snr < 22)
+ return 351000; /* VHT80 MCS8 */
+ return 390000; /* VHT80 MCS9 */
+}
+
+
+static void scan_est_throughput(struct wpa_supplicant *wpa_s,
+ struct wpa_scan_res *res)
+{
+ enum local_hw_capab capab = wpa_s->hw_capab;
+ int rate; /* max legacy rate in 500 kb/s units */
+ const u8 *ie;
+ unsigned int est, tmp;
+ int snr = res->snr;
+
+ if (res->est_throughput)
+ return;
+
+ /* Get maximum legacy rate */
+ rate = wpa_scan_get_max_rate(res);
+
+ /* Limit based on estimated SNR */
+ if (rate > 1 * 2 && snr < 1)
+ rate = 1 * 2;
+ else if (rate > 2 * 2 && snr < 4)
+ rate = 2 * 2;
+ else if (rate > 6 * 2 && snr < 5)
+ rate = 6 * 2;
+ else if (rate > 9 * 2 && snr < 6)
+ rate = 9 * 2;
+ else if (rate > 12 * 2 && snr < 7)
+ rate = 12 * 2;
+ else if (rate > 18 * 2 && snr < 10)
+ rate = 18 * 2;
+ else if (rate > 24 * 2 && snr < 11)
+ rate = 24 * 2;
+ else if (rate > 36 * 2 && snr < 15)
+ rate = 36 * 2;
+ else if (rate > 48 * 2 && snr < 19)
+ rate = 48 * 2;
+ else if (rate > 54 * 2 && snr < 21)
+ rate = 54 * 2;
+ est = rate * 500;
+
+ if (capab == CAPAB_HT || capab == CAPAB_HT40 || capab == CAPAB_VHT) {
+ ie = wpa_scan_get_ie(res, WLAN_EID_HT_CAP);
+ if (ie) {
+ tmp = max_ht20_rate(snr);
+ if (tmp > est)
+ est = tmp;
+ }
+ }
+
+ if (capab == CAPAB_HT40 || capab == CAPAB_VHT) {
+ ie = wpa_scan_get_ie(res, WLAN_EID_HT_OPERATION);
+ if (ie && ie[1] >= 2 &&
+ (ie[3] & HT_INFO_HT_PARAM_SECONDARY_CHNL_OFF_MASK)) {
+ tmp = max_ht40_rate(snr);
+ if (tmp > est)
+ est = tmp;
+ }
+ }
+
+ if (capab == CAPAB_VHT) {
+ /* Use +1 to assume VHT is always faster than HT */
+ ie = wpa_scan_get_ie(res, WLAN_EID_VHT_CAP);
+ if (ie) {
+ tmp = max_ht20_rate(snr) + 1;
+ if (tmp > est)
+ est = tmp;
+
+ ie = wpa_scan_get_ie(res, WLAN_EID_HT_OPERATION);
+ if (ie && ie[1] >= 2 &&
+ (ie[3] &
+ HT_INFO_HT_PARAM_SECONDARY_CHNL_OFF_MASK)) {
+ tmp = max_ht40_rate(snr) + 1;
+ if (tmp > est)
+ est = tmp;
+ }
+
+ ie = wpa_scan_get_ie(res, WLAN_EID_VHT_OPERATION);
+ if (ie && ie[1] >= 1 &&
+ (ie[2] & VHT_OPMODE_CHANNEL_WIDTH_MASK)) {
+ tmp = max_vht80_rate(snr) + 1;
+ if (tmp > est)
+ est = tmp;
+ }
+ }
+ }
+
+ /* TODO: channel utilization and AP load (e.g., from AP Beacon) */
+
+ res->est_throughput = est;
+}
+
+
+/**
+ * wpa_supplicant_get_scan_results - Get scan results
+ * @wpa_s: Pointer to wpa_supplicant data
+ * @info: Information about what was scanned or %NULL if not available
+ * @new_scan: Whether a new scan was performed
+ * Returns: Scan results, %NULL on failure
+ *
+ * This function request the current scan results from the driver and updates
+ * the local BSS list wpa_s->bss. The caller is responsible for freeing the
+ * results with wpa_scan_results_free().
+ */
+struct wpa_scan_results *
+wpa_supplicant_get_scan_results(struct wpa_supplicant *wpa_s,
+ struct scan_info *info, int new_scan)
+{
+ struct wpa_scan_results *scan_res;
+ size_t i;
+ int (*compar)(const void *, const void *) = wpa_scan_result_compar;
+
+ scan_res = wpa_drv_get_scan_results2(wpa_s);
+ if (scan_res == NULL) {
+ wpa_dbg(wpa_s, MSG_DEBUG, "Failed to get scan results");
+ return NULL;
+ }
+ if (scan_res->fetch_time.sec == 0) {
+ /*
+ * Make sure we have a valid timestamp if the driver wrapper
+ * does not set this.
+ */
+ os_get_reltime(&scan_res->fetch_time);
+ }
+ filter_scan_res(wpa_s, scan_res);
+
+ for (i = 0; i < scan_res->num; i++) {
+ struct wpa_scan_res *scan_res_item = scan_res->res[i];
+
+ scan_snr(scan_res_item);
+ scan_est_throughput(wpa_s, scan_res_item);
+ }
+
+#ifdef CONFIG_WPS
+ if (wpas_wps_searching(wpa_s)) {
+ wpa_dbg(wpa_s, MSG_DEBUG, "WPS: Order scan results with WPS "
+ "provisioning rules");
+ compar = wpa_scan_result_wps_compar;
+ }
+#endif /* CONFIG_WPS */
+
+ qsort(scan_res->res, scan_res->num, sizeof(struct wpa_scan_res *),
+ compar);
+ dump_scan_res(scan_res);
+
+ wpa_bss_update_start(wpa_s);
+ for (i = 0; i < scan_res->num; i++)
+ wpa_bss_update_scan_res(wpa_s, scan_res->res[i],
+ &scan_res->fetch_time);
+ wpa_bss_update_end(wpa_s, info, new_scan);
+
+ return scan_res;
+}
+
+
+/**
+ * wpa_supplicant_update_scan_results - Update scan results from the driver
+ * @wpa_s: Pointer to wpa_supplicant data
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function updates the BSS table within wpa_supplicant based on the
+ * currently available scan results from the driver without requesting a new
+ * scan. This is used in cases where the driver indicates an association
+ * (including roaming within ESS) and wpa_supplicant does not yet have the
+ * needed information to complete the connection (e.g., to perform validation
+ * steps in 4-way handshake).
+ */
+int wpa_supplicant_update_scan_results(struct wpa_supplicant *wpa_s)
+{
+ struct wpa_scan_results *scan_res;
+ scan_res = wpa_supplicant_get_scan_results(wpa_s, NULL, 0);
+ if (scan_res == NULL)
+ return -1;
+ wpa_scan_results_free(scan_res);
+
+ return 0;
+}
+
+
+/**
+ * scan_only_handler - Reports scan results
+ */
+void scan_only_handler(struct wpa_supplicant *wpa_s,
+ struct wpa_scan_results *scan_res)
+{
+ wpa_dbg(wpa_s, MSG_DEBUG, "Scan-only results received");
+ if (wpa_s->last_scan_req == MANUAL_SCAN_REQ &&
+ wpa_s->manual_scan_use_id && wpa_s->own_scan_running) {
+ wpa_msg_ctrl(wpa_s, MSG_INFO, WPA_EVENT_SCAN_RESULTS "id=%u",
+ wpa_s->manual_scan_id);
+ wpa_s->manual_scan_use_id = 0;
+ } else {
+ wpa_msg_ctrl(wpa_s, MSG_INFO, WPA_EVENT_SCAN_RESULTS);
+ }
+ wpas_notify_scan_results(wpa_s);
+ wpas_notify_scan_done(wpa_s, 1);
+ if (wpa_s->scan_work) {
+ struct wpa_radio_work *work = wpa_s->scan_work;
+ wpa_s->scan_work = NULL;
+ radio_work_done(work);
+ }
+}
+
+
+int wpas_scan_scheduled(struct wpa_supplicant *wpa_s)
+{
+ return eloop_is_timeout_registered(wpa_supplicant_scan, wpa_s, NULL);
+}
+
+
+struct wpa_driver_scan_params *
+wpa_scan_clone_params(const struct wpa_driver_scan_params *src)
+{
+ struct wpa_driver_scan_params *params;
+ size_t i;
+ u8 *n;
+
+ params = os_zalloc(sizeof(*params));
+ if (params == NULL)
+ return NULL;
+
+ for (i = 0; i < src->num_ssids; i++) {
+ if (src->ssids[i].ssid) {
+ n = os_malloc(src->ssids[i].ssid_len);
+ if (n == NULL)
+ goto failed;
+ os_memcpy(n, src->ssids[i].ssid,
+ src->ssids[i].ssid_len);
+ params->ssids[i].ssid = n;
+ params->ssids[i].ssid_len = src->ssids[i].ssid_len;
+ }
+ }
+ params->num_ssids = src->num_ssids;
+
+ if (src->extra_ies) {
+ n = os_malloc(src->extra_ies_len);
+ if (n == NULL)
+ goto failed;
+ os_memcpy(n, src->extra_ies, src->extra_ies_len);
+ params->extra_ies = n;
+ params->extra_ies_len = src->extra_ies_len;
+ }
+
+ if (src->freqs) {
+ int len = int_array_len(src->freqs);
+ params->freqs = os_malloc((len + 1) * sizeof(int));
+ if (params->freqs == NULL)
+ goto failed;
+ os_memcpy(params->freqs, src->freqs, (len + 1) * sizeof(int));
+ }
+
+ if (src->filter_ssids) {
+ params->filter_ssids = os_malloc(sizeof(*params->filter_ssids) *
+ src->num_filter_ssids);
+ if (params->filter_ssids == NULL)
+ goto failed;
+ os_memcpy(params->filter_ssids, src->filter_ssids,
+ sizeof(*params->filter_ssids) *
+ src->num_filter_ssids);
+ params->num_filter_ssids = src->num_filter_ssids;
+ }
+
+ params->filter_rssi = src->filter_rssi;
+ params->p2p_probe = src->p2p_probe;
+ params->only_new_results = src->only_new_results;
+ params->low_priority = src->low_priority;
+
+ if (src->mac_addr_rand) {
+ params->mac_addr_rand = src->mac_addr_rand;
+
+ if (src->mac_addr && src->mac_addr_mask) {
+ u8 *mac_addr;
+
+ mac_addr = os_malloc(2 * ETH_ALEN);
+ if (!mac_addr)
+ goto failed;
+
+ os_memcpy(mac_addr, src->mac_addr, ETH_ALEN);
+ os_memcpy(mac_addr + ETH_ALEN, src->mac_addr_mask,
+ ETH_ALEN);
+ params->mac_addr = mac_addr;
+ params->mac_addr_mask = mac_addr + ETH_ALEN;
+ }
+ }
+ return params;
+
+failed:
+ wpa_scan_free_params(params);
+ return NULL;
+}
+
+
+void wpa_scan_free_params(struct wpa_driver_scan_params *params)
+{
+ size_t i;
+
+ if (params == NULL)
+ return;
+
+ for (i = 0; i < params->num_ssids; i++)
+ os_free((u8 *) params->ssids[i].ssid);
+ os_free((u8 *) params->extra_ies);
+ os_free(params->freqs);
+ os_free(params->filter_ssids);
+
+ /*
+ * Note: params->mac_addr_mask points to same memory allocation and
+ * must not be freed separately.
+ */
+ os_free((u8 *) params->mac_addr);
+
+ os_free(params);
+}
+
+
+int wpas_start_pno(struct wpa_supplicant *wpa_s)
+{
+ int ret, interval, prio;
+ size_t i, num_ssid, num_match_ssid;
+ struct wpa_ssid *ssid;
+ struct wpa_driver_scan_params params;
+
+ if (!wpa_s->sched_scan_supported)
+ return -1;
+
+ if (wpa_s->pno || wpa_s->pno_sched_pending)
+ return 0;
+
+ if ((wpa_s->wpa_state > WPA_SCANNING) &&
+ (wpa_s->wpa_state <= WPA_COMPLETED)) {
+ wpa_printf(MSG_ERROR, "PNO: In assoc process");
+ return -EAGAIN;
+ }
+
+ if (wpa_s->wpa_state == WPA_SCANNING) {
+ wpa_supplicant_cancel_scan(wpa_s);
+ if (wpa_s->sched_scanning) {
+ wpa_printf(MSG_DEBUG, "Schedule PNO on completion of "
+ "ongoing sched scan");
+ wpa_supplicant_cancel_sched_scan(wpa_s);
+ wpa_s->pno_sched_pending = 1;
+ return 0;
+ }
+ }
+
+ os_memset(&params, 0, sizeof(params));
+
+ num_ssid = num_match_ssid = 0;
+ ssid = wpa_s->conf->ssid;
+ while (ssid) {
+ if (!wpas_network_disabled(wpa_s, ssid)) {
+ num_match_ssid++;
+ if (ssid->scan_ssid)
+ num_ssid++;
+ }
+ ssid = ssid->next;
+ }
+
+ if (num_match_ssid == 0) {
+ wpa_printf(MSG_DEBUG, "PNO: No configured SSIDs");
+ return -1;
+ }
+
+ if (num_match_ssid > num_ssid) {
+ params.num_ssids++; /* wildcard */
+ num_ssid++;
+ }
+
+ if (num_ssid > WPAS_MAX_SCAN_SSIDS) {
+ wpa_printf(MSG_DEBUG, "PNO: Use only the first %u SSIDs from "
+ "%u", WPAS_MAX_SCAN_SSIDS, (unsigned int) num_ssid);
+ num_ssid = WPAS_MAX_SCAN_SSIDS;
+ }
+
+ if (num_match_ssid > wpa_s->max_match_sets) {
+ num_match_ssid = wpa_s->max_match_sets;
+ wpa_dbg(wpa_s, MSG_DEBUG, "PNO: Too many SSIDs to match");
+ }
+ params.filter_ssids = os_calloc(num_match_ssid,
+ sizeof(struct wpa_driver_scan_filter));
+ if (params.filter_ssids == NULL)
+ return -1;
+
+ i = 0;
+ prio = 0;
+ ssid = wpa_s->conf->pssid[prio];
+ while (ssid) {
+ if (!wpas_network_disabled(wpa_s, ssid)) {
+ if (ssid->scan_ssid && params.num_ssids < num_ssid) {
+ params.ssids[params.num_ssids].ssid =
+ ssid->ssid;
+ params.ssids[params.num_ssids].ssid_len =
+ ssid->ssid_len;
+ params.num_ssids++;
+ }
+ os_memcpy(params.filter_ssids[i].ssid, ssid->ssid,
+ ssid->ssid_len);
+ params.filter_ssids[i].ssid_len = ssid->ssid_len;
+ params.num_filter_ssids++;
+ i++;
+ if (i == num_match_ssid)
+ break;
+ }
+ if (ssid->pnext)
+ ssid = ssid->pnext;
+ else if (prio + 1 == wpa_s->conf->num_prio)
+ break;
+ else
+ ssid = wpa_s->conf->pssid[++prio];
+ }
+
+ if (wpa_s->conf->filter_rssi)
+ params.filter_rssi = wpa_s->conf->filter_rssi;
+
+ interval = wpa_s->conf->sched_scan_interval ?
+ wpa_s->conf->sched_scan_interval : 10;
+
+ if (params.freqs == NULL && wpa_s->manual_sched_scan_freqs) {
+ wpa_dbg(wpa_s, MSG_DEBUG, "Limit sched scan to specified channels");
+ params.freqs = wpa_s->manual_sched_scan_freqs;
+ }
+
+ if (wpa_s->mac_addr_rand_enable & MAC_ADDR_RAND_PNO) {
+ params.mac_addr_rand = 1;
+ if (wpa_s->mac_addr_pno) {
+ params.mac_addr = wpa_s->mac_addr_pno;
+ params.mac_addr_mask = wpa_s->mac_addr_pno + ETH_ALEN;
+ }
+ }
+
+ ret = wpa_supplicant_start_sched_scan(wpa_s, &params, interval);
+ os_free(params.filter_ssids);
+ if (ret == 0)
+ wpa_s->pno = 1;
+ else
+ wpa_msg(wpa_s, MSG_ERROR, "Failed to schedule PNO");
+ return ret;
+}
+
+
+int wpas_stop_pno(struct wpa_supplicant *wpa_s)
+{
+ int ret = 0;
+
+ if (!wpa_s->pno)
+ return 0;
+
+ ret = wpa_supplicant_stop_sched_scan(wpa_s);
+
+ wpa_s->pno = 0;
+ wpa_s->pno_sched_pending = 0;
+
+ if (wpa_s->wpa_state == WPA_SCANNING)
+ wpa_supplicant_req_scan(wpa_s, 0, 0);
+
+ return ret;
+}
+
+
+void wpas_mac_addr_rand_scan_clear(struct wpa_supplicant *wpa_s,
+ unsigned int type)
+{
+ type &= MAC_ADDR_RAND_ALL;
+ wpa_s->mac_addr_rand_enable &= ~type;
+
+ if (type & MAC_ADDR_RAND_SCAN) {
+ os_free(wpa_s->mac_addr_scan);
+ wpa_s->mac_addr_scan = NULL;
+ }
+
+ if (type & MAC_ADDR_RAND_SCHED_SCAN) {
+ os_free(wpa_s->mac_addr_sched_scan);
+ wpa_s->mac_addr_sched_scan = NULL;
+ }
+
+ if (type & MAC_ADDR_RAND_PNO) {
+ os_free(wpa_s->mac_addr_pno);
+ wpa_s->mac_addr_pno = NULL;
+ }
+}
+
+
+int wpas_mac_addr_rand_scan_set(struct wpa_supplicant *wpa_s,
+ unsigned int type, const u8 *addr,
+ const u8 *mask)
+{
+ u8 *tmp = NULL;
+
+ wpas_mac_addr_rand_scan_clear(wpa_s, type);
+
+ if (addr) {
+ tmp = os_malloc(2 * ETH_ALEN);
+ if (!tmp)
+ return -1;
+ os_memcpy(tmp, addr, ETH_ALEN);
+ os_memcpy(tmp + ETH_ALEN, mask, ETH_ALEN);
+ }
+
+ if (type == MAC_ADDR_RAND_SCAN) {
+ wpa_s->mac_addr_scan = tmp;
+ } else if (type == MAC_ADDR_RAND_SCHED_SCAN) {
+ wpa_s->mac_addr_sched_scan = tmp;
+ } else if (type == MAC_ADDR_RAND_PNO) {
+ wpa_s->mac_addr_pno = tmp;
+ } else {
+ wpa_printf(MSG_INFO,
+ "scan: Invalid MAC randomization type=0x%x",
+ type);
+ os_free(tmp);
+ return -1;
+ }
+
+ wpa_s->mac_addr_rand_enable |= type;
+ return 0;
+}
diff --git a/freebsd/contrib/wpa/wpa_supplicant/scan.h b/freebsd/contrib/wpa/wpa_supplicant/scan.h
new file mode 100644
index 00000000..7650f5a2
--- /dev/null
+++ b/freebsd/contrib/wpa/wpa_supplicant/scan.h
@@ -0,0 +1,58 @@
+/*
+ * WPA Supplicant - Scanning
+ * Copyright (c) 2003-2014, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef SCAN_H
+#define SCAN_H
+
+int wpa_supplicant_enabled_networks(struct wpa_supplicant *wpa_s);
+void wpa_supplicant_req_scan(struct wpa_supplicant *wpa_s, int sec, int usec);
+int wpa_supplicant_delayed_sched_scan(struct wpa_supplicant *wpa_s,
+ int sec, int usec);
+int wpa_supplicant_req_sched_scan(struct wpa_supplicant *wpa_s);
+void wpa_supplicant_cancel_scan(struct wpa_supplicant *wpa_s);
+void wpa_supplicant_cancel_delayed_sched_scan(struct wpa_supplicant *wpa_s);
+void wpa_supplicant_cancel_sched_scan(struct wpa_supplicant *wpa_s);
+void wpa_supplicant_notify_scanning(struct wpa_supplicant *wpa_s,
+ int scanning);
+struct wpa_driver_scan_params;
+int wpa_supplicant_trigger_scan(struct wpa_supplicant *wpa_s,
+ struct wpa_driver_scan_params *params);
+struct wpa_scan_results *
+wpa_supplicant_get_scan_results(struct wpa_supplicant *wpa_s,
+ struct scan_info *info, int new_scan);
+int wpa_supplicant_update_scan_results(struct wpa_supplicant *wpa_s);
+const u8 * wpa_scan_get_ie(const struct wpa_scan_res *res, u8 ie);
+const u8 * wpa_scan_get_vendor_ie(const struct wpa_scan_res *res,
+ u32 vendor_type);
+const u8 * wpa_scan_get_vendor_ie_beacon(const struct wpa_scan_res *res,
+ u32 vendor_type);
+struct wpabuf * wpa_scan_get_vendor_ie_multi(const struct wpa_scan_res *res,
+ u32 vendor_type);
+int wpa_supplicant_filter_bssid_match(struct wpa_supplicant *wpa_s,
+ const u8 *bssid);
+void wpa_supplicant_update_scan_int(struct wpa_supplicant *wpa_s, int sec);
+void scan_only_handler(struct wpa_supplicant *wpa_s,
+ struct wpa_scan_results *scan_res);
+int wpas_scan_scheduled(struct wpa_supplicant *wpa_s);
+int wpa_supplicant_start_sched_scan(struct wpa_supplicant *wpa_s,
+ struct wpa_driver_scan_params *params,
+ int interval);
+int wpa_supplicant_stop_sched_scan(struct wpa_supplicant *wpa_s);
+struct wpa_driver_scan_params *
+wpa_scan_clone_params(const struct wpa_driver_scan_params *src);
+void wpa_scan_free_params(struct wpa_driver_scan_params *params);
+int wpas_start_pno(struct wpa_supplicant *wpa_s);
+int wpas_stop_pno(struct wpa_supplicant *wpa_s);
+
+void wpas_mac_addr_rand_scan_clear(struct wpa_supplicant *wpa_s,
+ unsigned int type);
+int wpas_mac_addr_rand_scan_set(struct wpa_supplicant *wpa_s,
+ unsigned int type, const u8 *addr,
+ const u8 *mask);
+
+#endif /* SCAN_H */
diff --git a/freebsd/contrib/wpa/wpa_supplicant/sme.h b/freebsd/contrib/wpa/wpa_supplicant/sme.h
new file mode 100644
index 00000000..fd5c3b4e
--- /dev/null
+++ b/freebsd/contrib/wpa/wpa_supplicant/sme.h
@@ -0,0 +1,118 @@
+/*
+ * wpa_supplicant - SME
+ * Copyright (c) 2009-2010, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef SME_H
+#define SME_H
+
+#ifdef CONFIG_SME
+
+void sme_authenticate(struct wpa_supplicant *wpa_s,
+ struct wpa_bss *bss, struct wpa_ssid *ssid);
+void sme_associate(struct wpa_supplicant *wpa_s, enum wpas_mode mode,
+ const u8 *bssid, u16 auth_type);
+void sme_event_auth(struct wpa_supplicant *wpa_s, union wpa_event_data *data);
+int sme_update_ft_ies(struct wpa_supplicant *wpa_s, const u8 *md,
+ const u8 *ies, size_t ies_len);
+void sme_event_assoc_reject(struct wpa_supplicant *wpa_s,
+ union wpa_event_data *data);
+void sme_event_auth_timed_out(struct wpa_supplicant *wpa_s,
+ union wpa_event_data *data);
+void sme_event_assoc_timed_out(struct wpa_supplicant *wpa_s,
+ union wpa_event_data *data);
+void sme_event_disassoc(struct wpa_supplicant *wpa_s,
+ struct disassoc_info *info);
+void sme_event_unprot_disconnect(struct wpa_supplicant *wpa_s, const u8 *sa,
+ const u8 *da, u16 reason_code);
+void sme_sa_query_rx(struct wpa_supplicant *wpa_s, const u8 *sa,
+ const u8 *data, size_t len);
+void sme_state_changed(struct wpa_supplicant *wpa_s);
+void sme_disassoc_while_authenticating(struct wpa_supplicant *wpa_s,
+ const u8 *prev_pending_bssid);
+void sme_clear_on_disassoc(struct wpa_supplicant *wpa_s);
+void sme_deinit(struct wpa_supplicant *wpa_s);
+
+int sme_proc_obss_scan(struct wpa_supplicant *wpa_s);
+void sme_sched_obss_scan(struct wpa_supplicant *wpa_s, int enable);
+
+#else /* CONFIG_SME */
+
+static inline void sme_authenticate(struct wpa_supplicant *wpa_s,
+ struct wpa_bss *bss,
+ struct wpa_ssid *ssid)
+{
+}
+
+static inline void sme_event_auth(struct wpa_supplicant *wpa_s,
+ union wpa_event_data *data)
+{
+}
+
+static inline int sme_update_ft_ies(struct wpa_supplicant *wpa_s, const u8 *md,
+ const u8 *ies, size_t ies_len)
+{
+ return -1;
+}
+
+
+static inline void sme_event_assoc_reject(struct wpa_supplicant *wpa_s,
+ union wpa_event_data *data)
+{
+}
+
+static inline void sme_event_auth_timed_out(struct wpa_supplicant *wpa_s,
+ union wpa_event_data *data)
+{
+}
+
+static inline void sme_event_assoc_timed_out(struct wpa_supplicant *wpa_s,
+ union wpa_event_data *data)
+{
+}
+
+static inline void sme_event_disassoc(struct wpa_supplicant *wpa_s,
+ struct disassoc_info *info)
+{
+}
+
+static inline void sme_event_unprot_disconnect(struct wpa_supplicant *wpa_s,
+ const u8 *sa, const u8 *da,
+ u16 reason_code)
+{
+}
+
+static inline void sme_state_changed(struct wpa_supplicant *wpa_s)
+{
+}
+
+static inline void
+sme_disassoc_while_authenticating(struct wpa_supplicant *wpa_s,
+ const u8 *prev_pending_bssid)
+{
+}
+
+static inline void sme_clear_on_disassoc(struct wpa_supplicant *wpa_s)
+{
+}
+
+static inline void sme_deinit(struct wpa_supplicant *wpa_s)
+{
+}
+
+static inline int sme_proc_obss_scan(struct wpa_supplicant *wpa_s)
+{
+ return 0;
+}
+
+static inline void sme_sched_obss_scan(struct wpa_supplicant *wpa_s,
+ int enable)
+{
+}
+
+#endif /* CONFIG_SME */
+
+#endif /* SME_H */
diff --git a/freebsd/contrib/wpa/wpa_supplicant/wifi_display.h b/freebsd/contrib/wpa/wpa_supplicant/wifi_display.h
new file mode 100644
index 00000000..0966bdb9
--- /dev/null
+++ b/freebsd/contrib/wpa/wpa_supplicant/wifi_display.h
@@ -0,0 +1,24 @@
+/*
+ * wpa_supplicant - Wi-Fi Display
+ * Copyright (c) 2011, Atheros Communications, Inc.
+ * Copyright (c) 2011-2012, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef WIFI_DISPLAY_H
+#define WIFI_DISPLAY_H
+
+int wifi_display_init(struct wpa_global *global);
+void wifi_display_deinit(struct wpa_global *global);
+void wifi_display_enable(struct wpa_global *global, int enabled);
+struct wpabuf *wifi_display_get_wfd_ie(struct wpa_global *global);
+int wifi_display_subelem_set(struct wpa_global *global, char *cmd);
+int wifi_display_subelem_set_from_ies(struct wpa_global *global,
+ struct wpabuf *ie);
+int wifi_display_subelem_get(struct wpa_global *global, char *cmd,
+ char *buf, size_t buflen);
+char * wifi_display_subelem_hex(const struct wpabuf *wfd_subelems, u8 id);
+
+#endif /* WIFI_DISPLAY_H */
diff --git a/freebsd/contrib/wpa/wpa_supplicant/wmm_ac.c b/freebsd/contrib/wpa/wpa_supplicant/wmm_ac.c
new file mode 100644
index 00000000..4d7b064b
--- /dev/null
+++ b/freebsd/contrib/wpa/wpa_supplicant/wmm_ac.c
@@ -0,0 +1,997 @@
+#include <machine/rtems-bsd-user-space.h>
+
+/*
+ * Wi-Fi Multimedia Admission Control (WMM-AC)
+ * Copyright(c) 2014, Intel Mobile Communication GmbH.
+ * Copyright(c) 2014, Intel Corporation. All rights reserved.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "utils/common.h"
+#include "utils/list.h"
+#include "utils/eloop.h"
+#include "common/ieee802_11_common.h"
+#include "wpa_supplicant_i.h"
+#include "bss.h"
+#include "driver_i.h"
+#include "wmm_ac.h"
+
+static void wmm_ac_addts_req_timeout(void *eloop_ctx, void *timeout_ctx);
+
+static const enum wmm_ac up_to_ac[8] = {
+ WMM_AC_BK,
+ WMM_AC_BE,
+ WMM_AC_BE,
+ WMM_AC_BK,
+ WMM_AC_VI,
+ WMM_AC_VI,
+ WMM_AC_VO,
+ WMM_AC_VO
+};
+
+
+static inline u8 wmm_ac_get_tsid(const struct wmm_tspec_element *tspec)
+{
+ return (tspec->ts_info[0] >> 1) & 0x0f;
+}
+
+
+static u8 wmm_ac_get_direction(const struct wmm_tspec_element *tspec)
+{
+ return (tspec->ts_info[0] >> 5) & 0x03;
+}
+
+
+static u8 wmm_ac_get_user_priority(const struct wmm_tspec_element *tspec)
+{
+ return (tspec->ts_info[1] >> 3) & 0x07;
+}
+
+
+static u8 wmm_ac_direction_to_idx(u8 direction)
+{
+ switch (direction) {
+ case WMM_AC_DIR_UPLINK:
+ return TS_DIR_IDX_UPLINK;
+ case WMM_AC_DIR_DOWNLINK:
+ return TS_DIR_IDX_DOWNLINK;
+ case WMM_AC_DIR_BIDIRECTIONAL:
+ return TS_DIR_IDX_BIDI;
+ default:
+ wpa_printf(MSG_ERROR, "Invalid direction: %d", direction);
+ return WMM_AC_DIR_UPLINK;
+ }
+}
+
+
+static int wmm_ac_add_ts(struct wpa_supplicant *wpa_s, const u8 *addr,
+ const struct wmm_tspec_element *tspec)
+{
+ struct wmm_tspec_element *_tspec;
+ int ret;
+ u16 admitted_time = le_to_host16(tspec->medium_time);
+ u8 up = wmm_ac_get_user_priority(tspec);
+ u8 ac = up_to_ac[up];
+ u8 dir = wmm_ac_get_direction(tspec);
+ u8 tsid = wmm_ac_get_tsid(tspec);
+ enum ts_dir_idx idx = wmm_ac_direction_to_idx(dir);
+
+ /* should have been verified before, but double-check here */
+ if (wpa_s->tspecs[ac][idx]) {
+ wpa_printf(MSG_ERROR,
+ "WMM AC: tspec (ac=%d, dir=%d) already exists!",
+ ac, dir);
+ return -1;
+ }
+
+ /* copy tspec */
+ _tspec = os_malloc(sizeof(*_tspec));
+ if (!_tspec)
+ return -1;
+
+ /* store the admitted TSPEC */
+ os_memcpy(_tspec, tspec, sizeof(*_tspec));
+
+ if (dir != WMM_AC_DIR_DOWNLINK) {
+ ret = wpa_drv_add_ts(wpa_s, tsid, addr, up, admitted_time);
+ wpa_printf(MSG_DEBUG,
+ "WMM AC: Add TS: addr=" MACSTR
+ " TSID=%u admitted time=%u, ret=%d",
+ MAC2STR(addr), tsid, admitted_time, ret);
+ if (ret < 0) {
+ os_free(_tspec);
+ return -1;
+ }
+ }
+
+ wpa_s->tspecs[ac][idx] = _tspec;
+
+ wpa_printf(MSG_DEBUG, "Traffic stream was created successfully");
+
+ wpa_msg(wpa_s, MSG_INFO, WMM_AC_EVENT_TSPEC_ADDED
+ "tsid=%d addr=" MACSTR " admitted_time=%d",
+ tsid, MAC2STR(addr), admitted_time);
+
+ return 0;
+}
+
+
+static void wmm_ac_del_ts_idx(struct wpa_supplicant *wpa_s, u8 ac,
+ enum ts_dir_idx dir)
+{
+ struct wmm_tspec_element *tspec = wpa_s->tspecs[ac][dir];
+ u8 tsid;
+
+ if (!tspec)
+ return;
+
+ tsid = wmm_ac_get_tsid(tspec);
+ wpa_printf(MSG_DEBUG, "WMM AC: Del TS ac=%d tsid=%d", ac, tsid);
+
+ /* update the driver in case of uplink/bidi */
+ if (wmm_ac_get_direction(tspec) != WMM_AC_DIR_DOWNLINK)
+ wpa_drv_del_ts(wpa_s, tsid, wpa_s->bssid);
+
+ wpa_msg(wpa_s, MSG_INFO, WMM_AC_EVENT_TSPEC_REMOVED
+ "tsid=%d addr=" MACSTR, tsid, MAC2STR(wpa_s->bssid));
+
+ os_free(wpa_s->tspecs[ac][dir]);
+ wpa_s->tspecs[ac][dir] = NULL;
+}
+
+
+static void wmm_ac_del_req(struct wpa_supplicant *wpa_s, int failed)
+{
+ struct wmm_ac_addts_request *req = wpa_s->addts_request;
+
+ if (!req)
+ return;
+
+ if (failed)
+ wpa_msg(wpa_s, MSG_INFO, WMM_AC_EVENT_TSPEC_REQ_FAILED
+ "tsid=%u", wmm_ac_get_tsid(&req->tspec));
+
+ eloop_cancel_timeout(wmm_ac_addts_req_timeout, wpa_s, req);
+ wpa_s->addts_request = NULL;
+ os_free(req);
+}
+
+
+static void wmm_ac_addts_req_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+ struct wpa_supplicant *wpa_s = eloop_ctx;
+ struct wmm_ac_addts_request *addts_req = timeout_ctx;
+
+ wpa_printf(MSG_DEBUG,
+ "Timeout getting ADDTS response (tsid=%d up=%d)",
+ wmm_ac_get_tsid(&addts_req->tspec),
+ wmm_ac_get_user_priority(&addts_req->tspec));
+
+ wmm_ac_del_req(wpa_s, 1);
+}
+
+
+static int wmm_ac_send_addts_request(struct wpa_supplicant *wpa_s,
+ const struct wmm_ac_addts_request *req)
+{
+ struct wpabuf *buf;
+ int ret;
+
+ wpa_printf(MSG_DEBUG, "Sending ADDTS Request to " MACSTR,
+ MAC2STR(req->address));
+
+ /* category + action code + dialog token + status + sizeof(tspec) */
+ buf = wpabuf_alloc(4 + sizeof(req->tspec));
+ if (!buf) {
+ wpa_printf(MSG_ERROR, "WMM AC: Allocation error");
+ return -1;
+ }
+
+ wpabuf_put_u8(buf, WLAN_ACTION_WMM);
+ wpabuf_put_u8(buf, WMM_ACTION_CODE_ADDTS_REQ);
+ wpabuf_put_u8(buf, req->dialog_token);
+ wpabuf_put_u8(buf, 0); /* status code */
+ wpabuf_put_data(buf, &req->tspec, sizeof(req->tspec));
+
+ ret = wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, req->address,
+ wpa_s->own_addr, wpa_s->bssid,
+ wpabuf_head(buf), wpabuf_len(buf), 0);
+ if (ret) {
+ wpa_printf(MSG_WARNING,
+ "WMM AC: Failed to send ADDTS Request");
+ }
+
+ wpabuf_free(buf);
+ return ret;
+}
+
+
+static int wmm_ac_send_delts(struct wpa_supplicant *wpa_s,
+ const struct wmm_tspec_element *tspec,
+ const u8 *address)
+{
+ struct wpabuf *buf;
+ int ret;
+
+ /* category + action code + dialog token + status + sizeof(tspec) */
+ buf = wpabuf_alloc(4 + sizeof(*tspec));
+ if (!buf)
+ return -1;
+
+ wpa_printf(MSG_DEBUG, "Sending DELTS to " MACSTR, MAC2STR(address));
+
+ /* category + action code + dialog token + status + sizeof(tspec) */
+ wpabuf_put_u8(buf, WLAN_ACTION_WMM);
+ wpabuf_put_u8(buf, WMM_ACTION_CODE_DELTS);
+ wpabuf_put_u8(buf, 0); /* Dialog Token (not used) */
+ wpabuf_put_u8(buf, 0); /* Status Code (not used) */
+ wpabuf_put_data(buf, tspec, sizeof(*tspec));
+
+ ret = wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, address,
+ wpa_s->own_addr, wpa_s->bssid,
+ wpabuf_head(buf), wpabuf_len(buf), 0);
+ if (ret)
+ wpa_printf(MSG_WARNING, "Failed to send DELTS frame");
+
+ wpabuf_free(buf);
+ return ret;
+}
+
+
+/* return the AC using the given TSPEC tid */
+static int wmm_ac_find_tsid(struct wpa_supplicant *wpa_s, u8 tsid,
+ enum ts_dir_idx *dir)
+{
+ int ac;
+ enum ts_dir_idx idx;
+
+ for (ac = 0; ac < WMM_AC_NUM; ac++) {
+ for (idx = 0; idx < TS_DIR_IDX_COUNT; idx++) {
+ if (wpa_s->tspecs[ac][idx] &&
+ wmm_ac_get_tsid(wpa_s->tspecs[ac][idx]) == tsid) {
+ if (dir)
+ *dir = idx;
+ return ac;
+ }
+ }
+ }
+
+ return -1;
+}
+
+
+static struct wmm_ac_addts_request *
+wmm_ac_build_addts_req(struct wpa_supplicant *wpa_s,
+ const struct wmm_ac_ts_setup_params *params,
+ const u8 *address)
+{
+ struct wmm_ac_addts_request *addts_req;
+ struct wmm_tspec_element *tspec;
+ u8 ac = up_to_ac[params->user_priority];
+ u8 uapsd = wpa_s->wmm_ac_assoc_info->ac_params[ac].uapsd;
+
+ addts_req = os_zalloc(sizeof(*addts_req));
+ if (!addts_req)
+ return NULL;
+
+ tspec = &addts_req->tspec;
+ os_memcpy(addts_req->address, address, ETH_ALEN);
+
+ /* The dialog token cannot be zero */
+ if (++wpa_s->wmm_ac_last_dialog_token == 0)
+ wpa_s->wmm_ac_last_dialog_token++;
+
+ addts_req->dialog_token = wpa_s->wmm_ac_last_dialog_token;
+ tspec->eid = WLAN_EID_VENDOR_SPECIFIC;
+ tspec->length = sizeof(*tspec) - 2; /* reduce eid and length */
+ tspec->oui[0] = 0x00;
+ tspec->oui[1] = 0x50;
+ tspec->oui[2] = 0xf2;
+ tspec->oui_type = WMM_OUI_TYPE;
+ tspec->oui_subtype = WMM_OUI_SUBTYPE_TSPEC_ELEMENT;
+ tspec->version = WMM_VERSION;
+
+ tspec->ts_info[0] = params->tsid << 1;
+ tspec->ts_info[0] |= params->direction << 5;
+ tspec->ts_info[0] |= WMM_AC_ACCESS_POLICY_EDCA << 7;
+ tspec->ts_info[1] = uapsd << 2;
+ tspec->ts_info[1] |= params->user_priority << 3;
+ tspec->ts_info[2] = 0;
+
+ tspec->nominal_msdu_size = host_to_le16(params->nominal_msdu_size);
+ if (params->fixed_nominal_msdu)
+ tspec->nominal_msdu_size |=
+ host_to_le16(WMM_AC_FIXED_MSDU_SIZE);
+
+ tspec->mean_data_rate = host_to_le32(params->mean_data_rate);
+ tspec->minimum_phy_rate = host_to_le32(params->minimum_phy_rate);
+ tspec->surplus_bandwidth_allowance =
+ host_to_le16(params->surplus_bandwidth_allowance);
+
+ return addts_req;
+}
+
+
+static int param_in_range(const char *name, long value,
+ long min_val, long max_val)
+{
+ if (value < min_val || (max_val >= 0 && value > max_val)) {
+ wpa_printf(MSG_DEBUG,
+ "WMM AC: param %s (%ld) is out of range (%ld-%ld)",
+ name, value, min_val, max_val);
+ return 0;
+ }
+
+ return 1;
+}
+
+
+static int wmm_ac_should_replace_ts(struct wpa_supplicant *wpa_s,
+ u8 tsid, u8 ac, u8 dir)
+{
+ enum ts_dir_idx idx;
+ int cur_ac, existing_ts = 0, replace_ts = 0;
+
+ cur_ac = wmm_ac_find_tsid(wpa_s, tsid, &idx);
+ if (cur_ac >= 0) {
+ if (cur_ac != ac) {
+ wpa_printf(MSG_DEBUG,
+ "WMM AC: TSID %i already exists on different ac (%d)",
+ tsid, cur_ac);
+ return -1;
+ }
+
+ /* same tsid - this tspec will replace the current one */
+ replace_ts |= BIT(idx);
+ }
+
+ for (idx = 0; idx < TS_DIR_IDX_COUNT; idx++) {
+ if (wpa_s->tspecs[ac][idx])
+ existing_ts |= BIT(idx);
+ }
+
+ switch (dir) {
+ case WMM_AC_DIR_UPLINK:
+ /* replace existing uplink/bidi tspecs */
+ replace_ts |= existing_ts & (BIT(TS_DIR_IDX_UPLINK) |
+ BIT(TS_DIR_IDX_BIDI));
+ break;
+ case WMM_AC_DIR_DOWNLINK:
+ /* replace existing downlink/bidi tspecs */
+ replace_ts |= existing_ts & (BIT(TS_DIR_IDX_DOWNLINK) |
+ BIT(TS_DIR_IDX_BIDI));
+ break;
+ case WMM_AC_DIR_BIDIRECTIONAL:
+ /* replace all existing tspecs */
+ replace_ts |= existing_ts;
+ break;
+ default:
+ return -1;
+ }
+
+ return replace_ts;
+}
+
+
+static int wmm_ac_ts_req_is_valid(struct wpa_supplicant *wpa_s,
+ const struct wmm_ac_ts_setup_params *params)
+{
+ enum wmm_ac req_ac;
+
+#define PARAM_IN_RANGE(field, min_value, max_value) \
+ param_in_range(#field, params->field, min_value, max_value)
+
+ if (!PARAM_IN_RANGE(tsid, 0, WMM_AC_MAX_TID) ||
+ !PARAM_IN_RANGE(user_priority, 0, WMM_AC_MAX_USER_PRIORITY) ||
+ !PARAM_IN_RANGE(nominal_msdu_size, 1, WMM_AC_MAX_NOMINAL_MSDU) ||
+ !PARAM_IN_RANGE(mean_data_rate, 1, -1) ||
+ !PARAM_IN_RANGE(minimum_phy_rate, 1, -1) ||
+ !PARAM_IN_RANGE(surplus_bandwidth_allowance, WMM_AC_MIN_SBA_UNITY,
+ -1))
+ return 0;
+#undef PARAM_IN_RANGE
+
+ if (!(params->direction == WMM_TSPEC_DIRECTION_UPLINK ||
+ params->direction == WMM_TSPEC_DIRECTION_DOWNLINK ||
+ params->direction == WMM_TSPEC_DIRECTION_BI_DIRECTIONAL)) {
+ wpa_printf(MSG_DEBUG, "WMM AC: invalid TS direction: %d",
+ params->direction);
+ return 0;
+ }
+
+ req_ac = up_to_ac[params->user_priority];
+
+ /* Requested accesss category must have acm */
+ if (!wpa_s->wmm_ac_assoc_info->ac_params[req_ac].acm) {
+ wpa_printf(MSG_DEBUG, "WMM AC: AC %d is not ACM", req_ac);
+ return 0;
+ }
+
+ if (wmm_ac_should_replace_ts(wpa_s, params->tsid, req_ac,
+ params->direction) < 0)
+ return 0;
+
+ return 1;
+}
+
+
+static struct wmm_ac_assoc_data *
+wmm_ac_process_param_elem(struct wpa_supplicant *wpa_s, const u8 *ies,
+ size_t ies_len)
+{
+ struct ieee802_11_elems elems;
+ struct wmm_parameter_element *wmm_params;
+ struct wmm_ac_assoc_data *assoc_data;
+ int i;
+
+ /* Parsing WMM Parameter Element */
+ if (ieee802_11_parse_elems(ies, ies_len, &elems, 1) == ParseFailed) {
+ wpa_printf(MSG_DEBUG, "WMM AC: could not parse assoc ies");
+ return NULL;
+ }
+
+ if (!elems.wmm) {
+ wpa_printf(MSG_DEBUG, "WMM AC: No WMM IE");
+ return NULL;
+ }
+
+ if (elems.wmm_len != sizeof(*wmm_params)) {
+ wpa_printf(MSG_DEBUG, "WMM AC: Invalid WMM ie length");
+ return NULL;
+ }
+
+ wmm_params = (struct wmm_parameter_element *)(elems.wmm);
+
+ assoc_data = os_zalloc(sizeof(*assoc_data));
+ if (!assoc_data)
+ return NULL;
+
+ for (i = 0; i < WMM_AC_NUM; i++)
+ assoc_data->ac_params[i].acm =
+ !!(wmm_params->ac[i].aci_aifsn & WMM_AC_ACM);
+
+ wpa_printf(MSG_DEBUG,
+ "WMM AC: AC mandatory: AC_BE=%u AC_BK=%u AC_VI=%u AC_VO=%u",
+ assoc_data->ac_params[WMM_AC_BE].acm,
+ assoc_data->ac_params[WMM_AC_BK].acm,
+ assoc_data->ac_params[WMM_AC_VI].acm,
+ assoc_data->ac_params[WMM_AC_VO].acm);
+
+ return assoc_data;
+}
+
+
+static int wmm_ac_init(struct wpa_supplicant *wpa_s, const u8 *ies,
+ size_t ies_len, const struct wmm_params *wmm_params)
+{
+ struct wmm_ac_assoc_data *assoc_data;
+ u8 ac;
+
+ if (wpa_s->wmm_ac_assoc_info) {
+ wpa_printf(MSG_ERROR, "WMM AC: Already initialized");
+ return -1;
+ }
+
+ if (!ies) {
+ wpa_printf(MSG_ERROR, "WMM AC: Missing IEs");
+ return -1;
+ }
+
+ if (!(wmm_params->info_bitmap & WMM_PARAMS_UAPSD_QUEUES_INFO)) {
+ wpa_printf(MSG_DEBUG, "WMM AC: Missing U-APSD configuration");
+ return -1;
+ }
+
+ os_memset(wpa_s->tspecs, 0, sizeof(wpa_s->tspecs));
+ wpa_s->wmm_ac_last_dialog_token = 0;
+ wpa_s->addts_request = NULL;
+
+ assoc_data = wmm_ac_process_param_elem(wpa_s, ies, ies_len);
+ if (!assoc_data)
+ return -1;
+
+ wpa_printf(MSG_DEBUG, "WMM AC: U-APSD queues=0x%x",
+ wmm_params->uapsd_queues);
+
+ for (ac = 0; ac < WMM_AC_NUM; ac++) {
+ assoc_data->ac_params[ac].uapsd =
+ !!(wmm_params->uapsd_queues & BIT(ac));
+ }
+
+ wpa_s->wmm_ac_assoc_info = assoc_data;
+ return 0;
+}
+
+
+static void wmm_ac_del_ts(struct wpa_supplicant *wpa_s, u8 ac, int dir_bitmap)
+{
+ enum ts_dir_idx idx;
+
+ for (idx = 0; idx < TS_DIR_IDX_COUNT; idx++) {
+ if (!(dir_bitmap & BIT(idx)))
+ continue;
+
+ wmm_ac_del_ts_idx(wpa_s, ac, idx);
+ }
+}
+
+
+static void wmm_ac_deinit(struct wpa_supplicant *wpa_s)
+{
+ int i;
+
+ for (i = 0; i < WMM_AC_NUM; i++)
+ wmm_ac_del_ts(wpa_s, i, TS_DIR_IDX_ALL);
+
+ /* delete pending add_ts requset */
+ wmm_ac_del_req(wpa_s, 1);
+
+ os_free(wpa_s->wmm_ac_assoc_info);
+ wpa_s->wmm_ac_assoc_info = NULL;
+}
+
+
+void wmm_ac_notify_assoc(struct wpa_supplicant *wpa_s, const u8 *ies,
+ size_t ies_len, const struct wmm_params *wmm_params)
+{
+ if (wmm_ac_init(wpa_s, ies, ies_len, wmm_params))
+ return;
+
+ wpa_printf(MSG_DEBUG,
+ "WMM AC: Valid WMM association, WMM AC is enabled");
+}
+
+
+void wmm_ac_notify_disassoc(struct wpa_supplicant *wpa_s)
+{
+ if (!wpa_s->wmm_ac_assoc_info)
+ return;
+
+ wmm_ac_deinit(wpa_s);
+ wpa_printf(MSG_DEBUG, "WMM AC: WMM AC is disabled");
+}
+
+
+int wpas_wmm_ac_delts(struct wpa_supplicant *wpa_s, u8 tsid)
+{
+ struct wmm_tspec_element tspec;
+ int ac;
+ enum ts_dir_idx dir;
+
+ if (!wpa_s->wmm_ac_assoc_info) {
+ wpa_printf(MSG_DEBUG,
+ "WMM AC: Failed to delete TS, WMM AC is disabled");
+ return -1;
+ }
+
+ ac = wmm_ac_find_tsid(wpa_s, tsid, &dir);
+ if (ac < 0) {
+ wpa_printf(MSG_DEBUG, "WMM AC: TS does not exist");
+ return -1;
+ }
+
+ tspec = *wpa_s->tspecs[ac][dir];
+
+ wmm_ac_del_ts_idx(wpa_s, ac, dir);
+
+ wmm_ac_send_delts(wpa_s, &tspec, wpa_s->bssid);
+
+ return 0;
+}
+
+
+int wpas_wmm_ac_addts(struct wpa_supplicant *wpa_s,
+ struct wmm_ac_ts_setup_params *params)
+{
+ struct wmm_ac_addts_request *addts_req;
+
+ if (!wpa_s->wmm_ac_assoc_info) {
+ wpa_printf(MSG_DEBUG,
+ "WMM AC: Cannot add TS - missing assoc data");
+ return -1;
+ }
+
+ if (wpa_s->addts_request) {
+ wpa_printf(MSG_DEBUG,
+ "WMM AC: can't add TS - ADDTS request is already pending");
+ return -1;
+ }
+
+ /*
+ * we can setup downlink TS even without driver support.
+ * however, we need driver support for the other directions.
+ */
+ if (params->direction != WMM_AC_DIR_DOWNLINK &&
+ !wpa_s->wmm_ac_supported) {
+ wpa_printf(MSG_DEBUG,
+ "Cannot set uplink/bidi TS without driver support");
+ return -1;
+ }
+
+ if (!wmm_ac_ts_req_is_valid(wpa_s, params))
+ return -1;
+
+ wpa_printf(MSG_DEBUG, "WMM AC: TS setup request (addr=" MACSTR
+ " tsid=%u user priority=%u direction=%d)",
+ MAC2STR(wpa_s->bssid), params->tsid,
+ params->user_priority, params->direction);
+
+ addts_req = wmm_ac_build_addts_req(wpa_s, params, wpa_s->bssid);
+ if (!addts_req)
+ return -1;
+
+ if (wmm_ac_send_addts_request(wpa_s, addts_req))
+ goto err;
+
+ /* save as pending and set ADDTS resp timeout to 1 second */
+ wpa_s->addts_request = addts_req;
+ eloop_register_timeout(1, 0, wmm_ac_addts_req_timeout,
+ wpa_s, addts_req);
+ return 0;
+err:
+ os_free(addts_req);
+ return -1;
+}
+
+
+static void wmm_ac_handle_delts(struct wpa_supplicant *wpa_s, const u8 *sa,
+ const struct wmm_tspec_element *tspec)
+{
+ int ac;
+ u8 tsid;
+ enum ts_dir_idx idx;
+
+ tsid = wmm_ac_get_tsid(tspec);
+
+ wpa_printf(MSG_DEBUG,
+ "WMM AC: DELTS frame has been received TSID=%u addr="
+ MACSTR, tsid, MAC2STR(sa));
+
+ ac = wmm_ac_find_tsid(wpa_s, tsid, &idx);
+ if (ac < 0) {
+ wpa_printf(MSG_DEBUG,
+ "WMM AC: Ignoring DELTS frame - TSID does not exist");
+ return;
+ }
+
+ wmm_ac_del_ts_idx(wpa_s, ac, idx);
+
+ wpa_printf(MSG_DEBUG,
+ "TS was deleted successfully (tsid=%u address=" MACSTR ")",
+ tsid, MAC2STR(sa));
+}
+
+
+static void wmm_ac_handle_addts_resp(struct wpa_supplicant *wpa_s, const u8 *sa,
+ const u8 resp_dialog_token, const u8 status_code,
+ const struct wmm_tspec_element *tspec)
+{
+ struct wmm_ac_addts_request *req = wpa_s->addts_request;
+ u8 ac, tsid, up, dir;
+ int replace_tspecs;
+
+ tsid = wmm_ac_get_tsid(tspec);
+ dir = wmm_ac_get_direction(tspec);
+ up = wmm_ac_get_user_priority(tspec);
+ ac = up_to_ac[up];
+
+ /* make sure we have a matching addts request */
+ if (!req || req->dialog_token != resp_dialog_token) {
+ wpa_printf(MSG_DEBUG,
+ "WMM AC: no req with dialog=%u, ignoring frame",
+ resp_dialog_token);
+ return;
+ }
+
+ /* make sure the params are the same */
+ if (os_memcmp(req->address, sa, ETH_ALEN) != 0 ||
+ tsid != wmm_ac_get_tsid(&req->tspec) ||
+ up != wmm_ac_get_user_priority(&req->tspec) ||
+ dir != wmm_ac_get_direction(&req->tspec)) {
+ wpa_printf(MSG_DEBUG,
+ "WMM AC: ADDTS params do not match, ignoring frame");
+ return;
+ }
+
+ /* delete pending request */
+ wmm_ac_del_req(wpa_s, 0);
+
+ wpa_printf(MSG_DEBUG,
+ "ADDTS response status=%d tsid=%u up=%u direction=%u",
+ status_code, tsid, up, dir);
+
+ if (status_code != WMM_ADDTS_STATUS_ADMISSION_ACCEPTED) {
+ wpa_printf(MSG_INFO, "WMM AC: ADDTS request was rejected");
+ goto err_msg;
+ }
+
+ replace_tspecs = wmm_ac_should_replace_ts(wpa_s, tsid, ac, dir);
+ if (replace_tspecs < 0)
+ goto err_delts;
+
+ wpa_printf(MSG_DEBUG, "ts idx replace bitmap: 0x%x", replace_tspecs);
+
+ /* when replacing tspecs - delete first */
+ wmm_ac_del_ts(wpa_s, ac, replace_tspecs);
+
+ /* Creating a new traffic stream */
+ wpa_printf(MSG_DEBUG,
+ "WMM AC: adding a new TS with TSID=%u address="MACSTR
+ " medium time=%u access category=%d dir=%d ",
+ tsid, MAC2STR(sa),
+ le_to_host16(tspec->medium_time), ac, dir);
+
+ if (wmm_ac_add_ts(wpa_s, sa, tspec))
+ goto err_delts;
+
+ return;
+
+err_delts:
+ /* ask the ap to delete the tspec */
+ wmm_ac_send_delts(wpa_s, tspec, sa);
+err_msg:
+ wpa_msg(wpa_s, MSG_INFO, WMM_AC_EVENT_TSPEC_REQ_FAILED "tsid=%u",
+ tsid);
+}
+
+
+void wmm_ac_rx_action(struct wpa_supplicant *wpa_s, const u8 *da,
+ const u8 *sa, const u8 *data, size_t len)
+{
+ u8 action;
+ u8 dialog_token;
+ u8 status_code;
+ struct ieee802_11_elems elems;
+ struct wmm_tspec_element *tspec;
+
+ if (wpa_s->wmm_ac_assoc_info == NULL) {
+ wpa_printf(MSG_DEBUG,
+ "WMM AC: WMM AC is disabled, ignoring action frame");
+ return;
+ }
+
+ action = data[0];
+
+ if (action != WMM_ACTION_CODE_ADDTS_RESP &&
+ action != WMM_ACTION_CODE_DELTS) {
+ wpa_printf(MSG_DEBUG,
+ "WMM AC: Unknown action (%d), ignoring action frame",
+ action);
+ return;
+ }
+
+ /* WMM AC action frame */
+ if (os_memcmp(da, wpa_s->own_addr, ETH_ALEN) != 0) {
+ wpa_printf(MSG_DEBUG, "WMM AC: frame destination addr="MACSTR
+ " is other than ours, ignoring frame", MAC2STR(da));
+ return;
+ }
+
+ if (os_memcmp(sa, wpa_s->bssid, ETH_ALEN) != 0) {
+ wpa_printf(MSG_DEBUG, "WMM AC: ignore frame with sa " MACSTR
+ " different other than our bssid", MAC2STR(da));
+ return;
+ }
+
+ if (len < 2 + sizeof(struct wmm_tspec_element)) {
+ wpa_printf(MSG_DEBUG,
+ "WMM AC: Short ADDTS response ignored (len=%lu)",
+ (unsigned long) len);
+ return;
+ }
+
+ data++;
+ len--;
+ dialog_token = data[0];
+ status_code = data[1];
+
+ if (ieee802_11_parse_elems(data + 2, len - 2, &elems, 1) != ParseOK) {
+ wpa_printf(MSG_DEBUG,
+ "WMM AC: Could not parse WMM AC action from " MACSTR,
+ MAC2STR(sa));
+ return;
+ }
+
+ /* the struct also contains the type and value, so decrease it */
+ if (elems.wmm_tspec_len != sizeof(struct wmm_tspec_element) - 2) {
+ wpa_printf(MSG_DEBUG, "WMM AC: missing or wrong length TSPEC");
+ return;
+ }
+
+ tspec = (struct wmm_tspec_element *)(elems.wmm_tspec - 2);
+
+ wpa_printf(MSG_DEBUG, "WMM AC: RX WMM AC Action from " MACSTR,
+ MAC2STR(sa));
+ wpa_hexdump(MSG_MSGDUMP, "WMM AC: WMM AC Action content", data, len);
+
+ switch (action) {
+ case WMM_ACTION_CODE_ADDTS_RESP:
+ wmm_ac_handle_addts_resp(wpa_s, sa, dialog_token, status_code,
+ tspec);
+ break;
+ case WMM_ACTION_CODE_DELTS:
+ wmm_ac_handle_delts(wpa_s, sa, tspec);
+ break;
+ default:
+ break;
+ }
+}
+
+
+static const char * get_ac_str(u8 ac)
+{
+ switch (ac) {
+ case WMM_AC_BE:
+ return "BE";
+ case WMM_AC_BK:
+ return "BK";
+ case WMM_AC_VI:
+ return "VI";
+ case WMM_AC_VO:
+ return "VO";
+ default:
+ return "N/A";
+ }
+}
+
+
+static const char * get_direction_str(u8 direction)
+{
+ switch (direction) {
+ case WMM_AC_DIR_DOWNLINK:
+ return "Downlink";
+ case WMM_AC_DIR_UPLINK:
+ return "Uplink";
+ case WMM_AC_DIR_BIDIRECTIONAL:
+ return "Bi-directional";
+ default:
+ return "N/A";
+ }
+}
+
+
+int wpas_wmm_ac_status(struct wpa_supplicant *wpa_s, char *buf, size_t buflen)
+{
+ struct wmm_ac_assoc_data *assoc_info = wpa_s->wmm_ac_assoc_info;
+ enum ts_dir_idx idx;
+ int pos = 0;
+ u8 ac, up;
+
+ if (!assoc_info) {
+ return wpa_scnprintf(buf, buflen - pos,
+ "Not associated to a WMM AP, WMM AC is Disabled\n");
+ }
+
+ pos += wpa_scnprintf(buf + pos, buflen - pos, "WMM AC is Enabled\n");
+
+ for (ac = 0; ac < WMM_AC_NUM; ac++) {
+ int ts_count = 0;
+
+ pos += wpa_scnprintf(buf + pos, buflen - pos,
+ "%s: acm=%d uapsd=%d\n",
+ get_ac_str(ac),
+ assoc_info->ac_params[ac].acm,
+ assoc_info->ac_params[ac].uapsd);
+
+ for (idx = 0; idx < TS_DIR_IDX_COUNT; idx++) {
+ struct wmm_tspec_element *tspec;
+ u8 dir, tsid;
+ const char *dir_str;
+
+ tspec = wpa_s->tspecs[ac][idx];
+ if (!tspec)
+ continue;
+
+ ts_count++;
+
+ dir = wmm_ac_get_direction(tspec);
+ dir_str = get_direction_str(dir);
+ tsid = wmm_ac_get_tsid(tspec);
+ up = wmm_ac_get_user_priority(tspec);
+
+ pos += wpa_scnprintf(buf + pos, buflen - pos,
+ "\tTSID=%u UP=%u\n"
+ "\tAddress = "MACSTR"\n"
+ "\tWMM AC dir = %s\n"
+ "\tTotal admitted time = %u\n\n",
+ tsid, up,
+ MAC2STR(wpa_s->bssid),
+ dir_str,
+ le_to_host16(tspec->medium_time));
+ }
+
+ if (!ts_count) {
+ pos += wpa_scnprintf(buf + pos, buflen - pos,
+ "\t(No Traffic Stream)\n\n");
+ }
+ }
+
+ return pos;
+}
+
+
+static u8 wmm_ac_get_tspecs_count(struct wpa_supplicant *wpa_s)
+{
+ int ac, dir, tspecs_count = 0;
+
+ for (ac = 0; ac < WMM_AC_NUM; ac++) {
+ for (dir = 0; dir < TS_DIR_IDX_COUNT; dir++) {
+ if (wpa_s->tspecs[ac][dir])
+ tspecs_count++;
+ }
+ }
+
+ return tspecs_count;
+}
+
+
+void wmm_ac_save_tspecs(struct wpa_supplicant *wpa_s)
+{
+ int ac, dir, tspecs_count;
+
+ wpa_printf(MSG_DEBUG, "WMM AC: Save last configured tspecs");
+
+ if (!wpa_s->wmm_ac_assoc_info)
+ return;
+
+ tspecs_count = wmm_ac_get_tspecs_count(wpa_s);
+ if (!tspecs_count) {
+ wpa_printf(MSG_DEBUG, "WMM AC: No configured TSPECs");
+ return;
+ }
+
+ wpa_printf(MSG_DEBUG, "WMM AC: Saving tspecs");
+
+ wmm_ac_clear_saved_tspecs(wpa_s);
+ wpa_s->last_tspecs = os_calloc(tspecs_count,
+ sizeof(*wpa_s->last_tspecs));
+ if (!wpa_s->last_tspecs) {
+ wpa_printf(MSG_ERROR, "WMM AC: Failed to save tspecs!");
+ return;
+ }
+
+ for (ac = 0; ac < WMM_AC_NUM; ac++) {
+ for (dir = 0; dir < TS_DIR_IDX_COUNT; dir++) {
+ if (!wpa_s->tspecs[ac][dir])
+ continue;
+
+ wpa_s->last_tspecs[wpa_s->last_tspecs_count++] =
+ *wpa_s->tspecs[ac][dir];
+ }
+ }
+
+ wpa_printf(MSG_DEBUG, "WMM AC: Successfully saved %d TSPECs",
+ wpa_s->last_tspecs_count);
+}
+
+
+void wmm_ac_clear_saved_tspecs(struct wpa_supplicant *wpa_s)
+{
+ if (wpa_s->last_tspecs) {
+ wpa_printf(MSG_DEBUG, "WMM AC: Clear saved tspecs");
+ os_free(wpa_s->last_tspecs);
+ wpa_s->last_tspecs = NULL;
+ wpa_s->last_tspecs_count = 0;
+ }
+}
+
+
+int wmm_ac_restore_tspecs(struct wpa_supplicant *wpa_s)
+{
+ unsigned int i;
+
+ if (!wpa_s->wmm_ac_assoc_info || !wpa_s->last_tspecs_count)
+ return 0;
+
+ wpa_printf(MSG_DEBUG, "WMM AC: Restore %u saved tspecs",
+ wpa_s->last_tspecs_count);
+
+ for (i = 0; i < wpa_s->last_tspecs_count; i++)
+ wmm_ac_add_ts(wpa_s, wpa_s->bssid, &wpa_s->last_tspecs[i]);
+
+ return 0;
+}
diff --git a/freebsd/contrib/wpa/wpa_supplicant/wmm_ac.h b/freebsd/contrib/wpa/wpa_supplicant/wmm_ac.h
new file mode 100644
index 00000000..5171b168
--- /dev/null
+++ b/freebsd/contrib/wpa/wpa_supplicant/wmm_ac.h
@@ -0,0 +1,176 @@
+/*
+ * Wi-Fi Multimedia Admission Control (WMM-AC)
+ * Copyright(c) 2014, Intel Mobile Communication GmbH.
+ * Copyright(c) 2014, Intel Corporation. All rights reserved.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef WMM_AC_H
+#define WMM_AC_H
+
+#include "common/ieee802_11_defs.h"
+#include "drivers/driver.h"
+
+struct wpa_supplicant;
+
+#define WMM_AC_ACCESS_POLICY_EDCA 1
+#define WMM_AC_FIXED_MSDU_SIZE BIT(15)
+
+#define WMM_AC_MAX_TID 7
+#define WMM_AC_MAX_USER_PRIORITY 7
+#define WMM_AC_MIN_SBA_UNITY 0x2000
+#define WMM_AC_MAX_NOMINAL_MSDU 32767
+
+/**
+ * struct wmm_ac_assoc_data - WMM Admission Control Association Data
+ *
+ * This struct will store any relevant WMM association data needed by WMM AC.
+ * In case there is a valid WMM association, an instance of this struct will be
+ * created. In case there is no instance of this struct, the station is not
+ * associated to a valid WMM BSS and hence, WMM AC will not be used.
+ */
+struct wmm_ac_assoc_data {
+ struct {
+ /*
+ * acm - Admission Control Mandatory
+ * In case an access category is ACM, the traffic will have
+ * to be admitted by WMM-AC's admission mechanism before use.
+ */
+ unsigned int acm:1;
+
+ /*
+ * uapsd_queues - Unscheduled Automatic Power Save Delivery
+ * queues.
+ * Indicates whether ACs are configured for U-APSD (or legacy
+ * PS). Storing this value is necessary in order to set the
+ * Power Save Bit (PSB) in ADDTS request Action frames (if not
+ * given).
+ */
+ unsigned int uapsd:1;
+ } ac_params[WMM_AC_NUM];
+};
+
+/**
+ * wmm_ac_dir - WMM Admission Control Direction
+ */
+enum wmm_ac_dir {
+ WMM_AC_DIR_UPLINK = 0,
+ WMM_AC_DIR_DOWNLINK = 1,
+ WMM_AC_DIR_BIDIRECTIONAL = 3
+};
+
+/**
+ * ts_dir_idx - indices of internally saved tspecs
+ *
+ * we can have multiple tspecs (downlink + uplink) per ac.
+ * save them in array, and use the enum to directly access
+ * the respective tspec slot (according to the direction).
+ */
+enum ts_dir_idx {
+ TS_DIR_IDX_UPLINK,
+ TS_DIR_IDX_DOWNLINK,
+ TS_DIR_IDX_BIDI,
+
+ TS_DIR_IDX_COUNT
+};
+#define TS_DIR_IDX_ALL (BIT(TS_DIR_IDX_COUNT) - 1)
+
+/**
+ * struct wmm_ac_addts_request - ADDTS Request Information
+ *
+ * The last sent ADDTS request(s) will be saved as element(s) of this struct in
+ * order to be compared with the received ADDTS response in ADDTS response
+ * action frame handling and should be stored until that point.
+ * In case a new traffic stream will be created/replaced/updated, only its
+ * relevant traffic stream information will be stored as a wmm_ac_ts struct.
+ */
+struct wmm_ac_addts_request {
+ /*
+ * dialog token - Used to link the recived ADDTS response with this
+ * saved ADDTS request when ADDTS response is being handled
+ */
+ u8 dialog_token;
+
+ /*
+ * address - The alleged traffic stream's receiver/transmitter address
+ * Address and TID are used to identify the TS (TID is contained in
+ * TSPEC)
+ */
+ u8 address[ETH_ALEN];
+
+ /*
+ * tspec - Traffic Stream Specification, will be used to compare the
+ * sent TSPEC in ADDTS request to the received TSPEC in ADDTS response
+ * and act accordingly in ADDTS response handling
+ */
+ struct wmm_tspec_element tspec;
+};
+
+
+/**
+ * struct wmm_ac_ts_setup_params - TS setup parameters
+ *
+ * This struct holds parameters which should be provided
+ * to wmm_ac_ts_setup in order to setup a traffic stream
+ */
+struct wmm_ac_ts_setup_params {
+ /*
+ * tsid - Traffic ID
+ * TID and address are used to identify the TS
+ */
+ int tsid;
+
+ /*
+ * direction - Traffic Stream's direction
+ */
+ enum wmm_ac_dir direction;
+
+ /*
+ * user_priority - Traffic Stream's user priority
+ */
+ int user_priority;
+
+ /*
+ * nominal_msdu_size - Nominal MAC service data unit size
+ */
+ int nominal_msdu_size;
+
+ /*
+ * fixed_nominal_msdu - Whether the size is fixed
+ * 0 = Nominal MSDU size is not fixed
+ * 1 = Nominal MSDU size is fixed
+ */
+ int fixed_nominal_msdu;
+
+ /*
+ * surplus_bandwidth_allowance - Specifies excess time allocation
+ */
+ int mean_data_rate;
+
+ /*
+ * minimum_phy_rate - Specifies the minimum supported PHY rate in bps
+ */
+ int minimum_phy_rate;
+
+ /*
+ * surplus_bandwidth_allowance - Specifies excess time allocation
+ */
+ int surplus_bandwidth_allowance;
+};
+
+void wmm_ac_notify_assoc(struct wpa_supplicant *wpa_s, const u8 *ies,
+ size_t ies_len, const struct wmm_params *wmm_params);
+void wmm_ac_notify_disassoc(struct wpa_supplicant *wpa_s);
+int wpas_wmm_ac_addts(struct wpa_supplicant *wpa_s,
+ struct wmm_ac_ts_setup_params *params);
+int wpas_wmm_ac_delts(struct wpa_supplicant *wpa_s, u8 tsid);
+void wmm_ac_rx_action(struct wpa_supplicant *wpa_s, const u8 *da,
+ const u8 *sa, const u8 *data, size_t len);
+int wpas_wmm_ac_status(struct wpa_supplicant *wpa_s, char *buf, size_t buflen);
+void wmm_ac_save_tspecs(struct wpa_supplicant *wpa_s);
+void wmm_ac_clear_saved_tspecs(struct wpa_supplicant *wpa_s);
+int wmm_ac_restore_tspecs(struct wpa_supplicant *wpa_s);
+
+#endif /* WMM_AC_H */
diff --git a/freebsd/contrib/wpa/wpa_supplicant/wnm_sta.h b/freebsd/contrib/wpa/wpa_supplicant/wnm_sta.h
new file mode 100644
index 00000000..8de43480
--- /dev/null
+++ b/freebsd/contrib/wpa/wpa_supplicant/wnm_sta.h
@@ -0,0 +1,77 @@
+/*
+ * IEEE 802.11v WNM related functions and structures
+ * Copyright (c) 2011-2012, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef WNM_STA_H
+#define WNM_STA_H
+
+struct measurement_pilot {
+ u8 measurement_pilot;
+ u8 subelem_len;
+ u8 subelems[255];
+};
+
+struct multiple_bssid {
+ u8 max_bssid_indicator;
+ u8 subelem_len;
+ u8 subelems[255];
+};
+
+struct neighbor_report {
+ u8 bssid[ETH_ALEN];
+ u32 bssid_info;
+ u8 regulatory_class;
+ u8 channel_number;
+ u8 phy_type;
+ u8 preference; /* valid if preference_present=1 */
+ u16 tsf_offset; /* valid if tsf_present=1 */
+ u16 beacon_int; /* valid if tsf_present=1 */
+ char country[2]; /* valid if country_present=1 */
+ u8 rm_capab[5]; /* valid if rm_capab_present=1 */
+ u16 bearing; /* valid if bearing_present=1 */
+ u16 rel_height; /* valid if bearing_present=1 */
+ u32 distance; /* valid if bearing_present=1 */
+ u64 bss_term_tsf; /* valid if bss_term_present=1 */
+ u16 bss_term_dur; /* valid if bss_term_present=1 */
+ unsigned int preference_present:1;
+ unsigned int tsf_present:1;
+ unsigned int country_present:1;
+ unsigned int rm_capab_present:1;
+ unsigned int bearing_present:1;
+ unsigned int bss_term_present:1;
+ struct measurement_pilot *meas_pilot;
+ struct multiple_bssid *mul_bssid;
+ int freq;
+};
+
+
+int ieee802_11_send_wnmsleep_req(struct wpa_supplicant *wpa_s,
+ u8 action, u16 intval, struct wpabuf *tfs_req);
+
+void ieee802_11_rx_wnm_action(struct wpa_supplicant *wpa_s,
+ const struct ieee80211_mgmt *mgmt, size_t len);
+
+int wnm_send_bss_transition_mgmt_query(struct wpa_supplicant *wpa_s,
+ u8 query_reason);
+void wnm_deallocate_memory(struct wpa_supplicant *wpa_s);
+
+
+#ifdef CONFIG_WNM
+
+int wnm_scan_process(struct wpa_supplicant *wpa_s, int reply_on_fail);
+
+#else /* CONFIG_WNM */
+
+static inline int wnm_scan_process(struct wpa_supplicant *wpa_s,
+ int reply_on_fail)
+{
+ return 0;
+}
+
+#endif /* CONFIG_WNM */
+
+#endif /* WNM_STA_H */
diff --git a/freebsd/contrib/wpa/wpa_supplicant/wpa_supplicant.c b/freebsd/contrib/wpa/wpa_supplicant/wpa_supplicant.c
new file mode 100644
index 00000000..d65398e2
--- /dev/null
+++ b/freebsd/contrib/wpa/wpa_supplicant/wpa_supplicant.c
@@ -0,0 +1,5872 @@
+#include <machine/rtems-bsd-user-space.h>
+
+/*
+ * WPA Supplicant
+ * Copyright (c) 2003-2015, 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 functions for registering and unregistering
+ * %wpa_supplicant interfaces. In addition, this file contains number of
+ * functions for managing network connections.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "crypto/random.h"
+#include "crypto/sha1.h"
+#include "eapol_supp/eapol_supp_sm.h"
+#include "eap_peer/eap.h"
+#include "eap_peer/eap_proxy.h"
+#include "eap_server/eap_methods.h"
+#include "rsn_supp/wpa.h"
+#include "eloop.h"
+#include "config.h"
+#include "utils/ext_password.h"
+#include "l2_packet/l2_packet.h"
+#include "wpa_supplicant_i.h"
+#include "driver_i.h"
+#include "ctrl_iface.h"
+#include "pcsc_funcs.h"
+#include "common/version.h"
+#include "rsn_supp/preauth.h"
+#include "rsn_supp/pmksa_cache.h"
+#include "common/wpa_ctrl.h"
+#include "common/ieee802_11_defs.h"
+#include "common/hw_features_common.h"
+#include "p2p/p2p.h"
+#include "fst/fst.h"
+#include "blacklist.h"
+#include "wpas_glue.h"
+#include "wps_supplicant.h"
+#include "ibss_rsn.h"
+#include "sme.h"
+#include "gas_query.h"
+#include "ap.h"
+#include "p2p_supplicant.h"
+#include "wifi_display.h"
+#include "notify.h"
+#include "bgscan.h"
+#include "autoscan.h"
+#include "bss.h"
+#include "scan.h"
+#include "offchannel.h"
+#include "hs20_supplicant.h"
+#include "wnm_sta.h"
+#include "wpas_kay.h"
+#include "mesh.h"
+
+const char *const wpa_supplicant_version =
+"wpa_supplicant v" VERSION_STR "\n"
+"Copyright (c) 2003-2015, Jouni Malinen <j@w1.fi> and contributors";
+
+const char *const wpa_supplicant_license =
+"This software may be distributed under the terms of the BSD license.\n"
+"See README for more details.\n"
+#ifdef EAP_TLS_OPENSSL
+"\nThis product includes software developed by the OpenSSL Project\n"
+"for use in the OpenSSL Toolkit (http://www.openssl.org/)\n"
+#endif /* EAP_TLS_OPENSSL */
+;
+
+#ifndef CONFIG_NO_STDOUT_DEBUG
+/* Long text divided into parts in order to fit in C89 strings size limits. */
+const char *const wpa_supplicant_full_license1 =
+"";
+const char *const wpa_supplicant_full_license2 =
+"This software may be distributed under the terms of the BSD license.\n"
+"\n"
+"Redistribution and use in source and binary forms, with or without\n"
+"modification, are permitted provided that the following conditions are\n"
+"met:\n"
+"\n";
+const char *const wpa_supplicant_full_license3 =
+"1. Redistributions of source code must retain the above copyright\n"
+" notice, this list of conditions and the following disclaimer.\n"
+"\n"
+"2. Redistributions in binary form must reproduce the above copyright\n"
+" notice, this list of conditions and the following disclaimer in the\n"
+" documentation and/or other materials provided with the distribution.\n"
+"\n";
+const char *const wpa_supplicant_full_license4 =
+"3. Neither the name(s) of the above-listed copyright holder(s) nor the\n"
+" names of its contributors may be used to endorse or promote products\n"
+" derived from this software without specific prior written permission.\n"
+"\n"
+"THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n"
+"\"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n"
+"LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\n"
+"A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\n";
+const char *const wpa_supplicant_full_license5 =
+"OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n"
+"SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\n"
+"LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\n"
+"DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\n"
+"THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n"
+"(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n"
+"OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n"
+"\n";
+#endif /* CONFIG_NO_STDOUT_DEBUG */
+
+/* Configure default/group WEP keys for static WEP */
+int wpa_set_wep_keys(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid)
+{
+ int i, set = 0;
+
+ for (i = 0; i < NUM_WEP_KEYS; i++) {
+ if (ssid->wep_key_len[i] == 0)
+ continue;
+
+ set = 1;
+ wpa_drv_set_key(wpa_s, WPA_ALG_WEP, NULL,
+ i, i == ssid->wep_tx_keyidx, NULL, 0,
+ ssid->wep_key[i], ssid->wep_key_len[i]);
+ }
+
+ return set;
+}
+
+
+int wpa_supplicant_set_wpa_none_key(struct wpa_supplicant *wpa_s,
+ struct wpa_ssid *ssid)
+{
+ u8 key[32];
+ size_t keylen;
+ enum wpa_alg alg;
+ u8 seq[6] = { 0 };
+ int ret;
+
+ /* IBSS/WPA-None uses only one key (Group) for both receiving and
+ * sending unicast and multicast packets. */
+
+ if (ssid->mode != WPAS_MODE_IBSS) {
+ wpa_msg(wpa_s, MSG_INFO, "WPA: Invalid mode %d (not "
+ "IBSS/ad-hoc) for WPA-None", ssid->mode);
+ return -1;
+ }
+
+ if (!ssid->psk_set) {
+ wpa_msg(wpa_s, MSG_INFO, "WPA: No PSK configured for "
+ "WPA-None");
+ return -1;
+ }
+
+ switch (wpa_s->group_cipher) {
+ case WPA_CIPHER_CCMP:
+ os_memcpy(key, ssid->psk, 16);
+ keylen = 16;
+ alg = WPA_ALG_CCMP;
+ break;
+ case WPA_CIPHER_GCMP:
+ os_memcpy(key, ssid->psk, 16);
+ keylen = 16;
+ alg = WPA_ALG_GCMP;
+ break;
+ case WPA_CIPHER_TKIP:
+ /* WPA-None uses the same Michael MIC key for both TX and RX */
+ os_memcpy(key, ssid->psk, 16 + 8);
+ os_memcpy(key + 16 + 8, ssid->psk + 16, 8);
+ keylen = 32;
+ alg = WPA_ALG_TKIP;
+ break;
+ default:
+ wpa_msg(wpa_s, MSG_INFO, "WPA: Invalid group cipher %d for "
+ "WPA-None", wpa_s->group_cipher);
+ return -1;
+ }
+
+ /* TODO: should actually remember the previously used seq#, both for TX
+ * and RX from each STA.. */
+
+ ret = wpa_drv_set_key(wpa_s, alg, NULL, 0, 1, seq, 6, key, keylen);
+ os_memset(key, 0, sizeof(key));
+ return ret;
+}
+
+
+static void wpa_supplicant_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+ struct wpa_supplicant *wpa_s = eloop_ctx;
+ const u8 *bssid = wpa_s->bssid;
+ if (is_zero_ether_addr(bssid))
+ bssid = wpa_s->pending_bssid;
+ wpa_msg(wpa_s, MSG_INFO, "Authentication with " MACSTR " timed out.",
+ MAC2STR(bssid));
+ wpa_blacklist_add(wpa_s, bssid);
+ wpa_sm_notify_disassoc(wpa_s->wpa);
+ wpa_supplicant_deauthenticate(wpa_s, WLAN_REASON_DEAUTH_LEAVING);
+ wpa_s->reassociate = 1;
+
+ /*
+ * If we timed out, the AP or the local radio may be busy.
+ * So, wait a second until scanning again.
+ */
+ wpa_supplicant_req_scan(wpa_s, 1, 0);
+}
+
+
+/**
+ * wpa_supplicant_req_auth_timeout - Schedule a timeout for authentication
+ * @wpa_s: Pointer to wpa_supplicant data
+ * @sec: Number of seconds after which to time out authentication
+ * @usec: Number of microseconds after which to time out authentication
+ *
+ * This function is used to schedule a timeout for the current authentication
+ * attempt.
+ */
+void wpa_supplicant_req_auth_timeout(struct wpa_supplicant *wpa_s,
+ int sec, int usec)
+{
+ if (wpa_s->conf->ap_scan == 0 &&
+ (wpa_s->drv_flags & WPA_DRIVER_FLAGS_WIRED))
+ return;
+
+ wpa_dbg(wpa_s, MSG_DEBUG, "Setting authentication timeout: %d sec "
+ "%d usec", sec, usec);
+ eloop_cancel_timeout(wpa_supplicant_timeout, wpa_s, NULL);
+ eloop_register_timeout(sec, usec, wpa_supplicant_timeout, wpa_s, NULL);
+}
+
+
+/**
+ * wpa_supplicant_cancel_auth_timeout - Cancel authentication timeout
+ * @wpa_s: Pointer to wpa_supplicant data
+ *
+ * This function is used to cancel authentication timeout scheduled with
+ * wpa_supplicant_req_auth_timeout() and it is called when authentication has
+ * been completed.
+ */
+void wpa_supplicant_cancel_auth_timeout(struct wpa_supplicant *wpa_s)
+{
+ wpa_dbg(wpa_s, MSG_DEBUG, "Cancelling authentication timeout");
+ eloop_cancel_timeout(wpa_supplicant_timeout, wpa_s, NULL);
+ wpa_blacklist_del(wpa_s, wpa_s->bssid);
+}
+
+
+/**
+ * wpa_supplicant_initiate_eapol - Configure EAPOL state machine
+ * @wpa_s: Pointer to wpa_supplicant data
+ *
+ * This function is used to configure EAPOL state machine based on the selected
+ * authentication mode.
+ */
+void wpa_supplicant_initiate_eapol(struct wpa_supplicant *wpa_s)
+{
+#ifdef IEEE8021X_EAPOL
+ struct eapol_config eapol_conf;
+ struct wpa_ssid *ssid = wpa_s->current_ssid;
+
+#ifdef CONFIG_IBSS_RSN
+ if (ssid->mode == WPAS_MODE_IBSS &&
+ wpa_s->key_mgmt != WPA_KEY_MGMT_NONE &&
+ wpa_s->key_mgmt != WPA_KEY_MGMT_WPA_NONE) {
+ /*
+ * RSN IBSS authentication is per-STA and we can disable the
+ * per-BSSID EAPOL authentication.
+ */
+ eapol_sm_notify_portControl(wpa_s->eapol, ForceAuthorized);
+ eapol_sm_notify_eap_success(wpa_s->eapol, TRUE);
+ eapol_sm_notify_eap_fail(wpa_s->eapol, FALSE);
+ return;
+ }
+#endif /* CONFIG_IBSS_RSN */
+
+ eapol_sm_notify_eap_success(wpa_s->eapol, FALSE);
+ eapol_sm_notify_eap_fail(wpa_s->eapol, FALSE);
+
+ if (wpa_s->key_mgmt == WPA_KEY_MGMT_NONE ||
+ wpa_s->key_mgmt == WPA_KEY_MGMT_WPA_NONE)
+ eapol_sm_notify_portControl(wpa_s->eapol, ForceAuthorized);
+ else
+ eapol_sm_notify_portControl(wpa_s->eapol, Auto);
+
+ os_memset(&eapol_conf, 0, sizeof(eapol_conf));
+ if (wpa_s->key_mgmt == WPA_KEY_MGMT_IEEE8021X_NO_WPA) {
+ eapol_conf.accept_802_1x_keys = 1;
+ eapol_conf.required_keys = 0;
+ if (ssid->eapol_flags & EAPOL_FLAG_REQUIRE_KEY_UNICAST) {
+ eapol_conf.required_keys |= EAPOL_REQUIRE_KEY_UNICAST;
+ }
+ if (ssid->eapol_flags & EAPOL_FLAG_REQUIRE_KEY_BROADCAST) {
+ eapol_conf.required_keys |=
+ EAPOL_REQUIRE_KEY_BROADCAST;
+ }
+
+ if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_WIRED)
+ eapol_conf.required_keys = 0;
+ }
+ eapol_conf.fast_reauth = wpa_s->conf->fast_reauth;
+ eapol_conf.workaround = ssid->eap_workaround;
+ eapol_conf.eap_disabled =
+ !wpa_key_mgmt_wpa_ieee8021x(wpa_s->key_mgmt) &&
+ wpa_s->key_mgmt != WPA_KEY_MGMT_IEEE8021X_NO_WPA &&
+ wpa_s->key_mgmt != WPA_KEY_MGMT_WPS;
+ eapol_conf.external_sim = wpa_s->conf->external_sim;
+
+#ifdef CONFIG_WPS
+ if (wpa_s->key_mgmt == WPA_KEY_MGMT_WPS) {
+ eapol_conf.wps |= EAPOL_LOCAL_WPS_IN_USE;
+ if (wpa_s->current_bss) {
+ struct wpabuf *ie;
+ ie = wpa_bss_get_vendor_ie_multi(wpa_s->current_bss,
+ WPS_IE_VENDOR_TYPE);
+ if (ie) {
+ if (wps_is_20(ie))
+ eapol_conf.wps |=
+ EAPOL_PEER_IS_WPS20_AP;
+ wpabuf_free(ie);
+ }
+ }
+ }
+#endif /* CONFIG_WPS */
+
+ eapol_sm_notify_config(wpa_s->eapol, &ssid->eap, &eapol_conf);
+
+ ieee802_1x_alloc_kay_sm(wpa_s, ssid);
+#endif /* IEEE8021X_EAPOL */
+}
+
+
+/**
+ * wpa_supplicant_set_non_wpa_policy - Set WPA parameters to non-WPA mode
+ * @wpa_s: Pointer to wpa_supplicant data
+ * @ssid: Configuration data for the network
+ *
+ * This function is used to configure WPA state machine and related parameters
+ * to a mode where WPA is not enabled. This is called as part of the
+ * authentication configuration when the selected network does not use WPA.
+ */
+void wpa_supplicant_set_non_wpa_policy(struct wpa_supplicant *wpa_s,
+ struct wpa_ssid *ssid)
+{
+ int i;
+
+ if (ssid->key_mgmt & WPA_KEY_MGMT_WPS)
+ wpa_s->key_mgmt = WPA_KEY_MGMT_WPS;
+ else if (ssid->key_mgmt & WPA_KEY_MGMT_IEEE8021X_NO_WPA)
+ wpa_s->key_mgmt = WPA_KEY_MGMT_IEEE8021X_NO_WPA;
+ else
+ wpa_s->key_mgmt = WPA_KEY_MGMT_NONE;
+ wpa_sm_set_ap_wpa_ie(wpa_s->wpa, NULL, 0);
+ wpa_sm_set_ap_rsn_ie(wpa_s->wpa, NULL, 0);
+ wpa_sm_set_assoc_wpa_ie(wpa_s->wpa, NULL, 0);
+ wpa_s->pairwise_cipher = WPA_CIPHER_NONE;
+ wpa_s->group_cipher = WPA_CIPHER_NONE;
+ wpa_s->mgmt_group_cipher = 0;
+
+ for (i = 0; i < NUM_WEP_KEYS; i++) {
+ if (ssid->wep_key_len[i] > 5) {
+ wpa_s->pairwise_cipher = WPA_CIPHER_WEP104;
+ wpa_s->group_cipher = WPA_CIPHER_WEP104;
+ break;
+ } else if (ssid->wep_key_len[i] > 0) {
+ wpa_s->pairwise_cipher = WPA_CIPHER_WEP40;
+ wpa_s->group_cipher = WPA_CIPHER_WEP40;
+ break;
+ }
+ }
+
+ wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_RSN_ENABLED, 0);
+ wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_KEY_MGMT, wpa_s->key_mgmt);
+ wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_PAIRWISE,
+ wpa_s->pairwise_cipher);
+ wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_GROUP, wpa_s->group_cipher);
+#ifdef CONFIG_IEEE80211W
+ wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_MGMT_GROUP,
+ wpa_s->mgmt_group_cipher);
+#endif /* CONFIG_IEEE80211W */
+
+ pmksa_cache_clear_current(wpa_s->wpa);
+}
+
+
+void free_hw_features(struct wpa_supplicant *wpa_s)
+{
+ int i;
+ if (wpa_s->hw.modes == NULL)
+ return;
+
+ for (i = 0; i < wpa_s->hw.num_modes; i++) {
+ os_free(wpa_s->hw.modes[i].channels);
+ os_free(wpa_s->hw.modes[i].rates);
+ }
+
+ os_free(wpa_s->hw.modes);
+ wpa_s->hw.modes = NULL;
+}
+
+
+static void wpa_supplicant_cleanup(struct wpa_supplicant *wpa_s)
+{
+ int i;
+
+ bgscan_deinit(wpa_s);
+ autoscan_deinit(wpa_s);
+ scard_deinit(wpa_s->scard);
+ wpa_s->scard = NULL;
+ wpa_sm_set_scard_ctx(wpa_s->wpa, NULL);
+ eapol_sm_register_scard_ctx(wpa_s->eapol, NULL);
+ l2_packet_deinit(wpa_s->l2);
+ wpa_s->l2 = NULL;
+ if (wpa_s->l2_br) {
+ l2_packet_deinit(wpa_s->l2_br);
+ wpa_s->l2_br = NULL;
+ }
+#ifdef CONFIG_TESTING_OPTIONS
+ l2_packet_deinit(wpa_s->l2_test);
+ wpa_s->l2_test = NULL;
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ if (wpa_s->conf != NULL) {
+ struct wpa_ssid *ssid;
+ for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next)
+ wpas_notify_network_removed(wpa_s, ssid);
+ }
+
+ os_free(wpa_s->confname);
+ wpa_s->confname = NULL;
+
+ os_free(wpa_s->confanother);
+ wpa_s->confanother = NULL;
+
+ wpa_sm_set_eapol(wpa_s->wpa, NULL);
+ eapol_sm_deinit(wpa_s->eapol);
+ wpa_s->eapol = NULL;
+
+ rsn_preauth_deinit(wpa_s->wpa);
+
+#ifdef CONFIG_TDLS
+ wpa_tdls_deinit(wpa_s->wpa);
+#endif /* CONFIG_TDLS */
+
+ wmm_ac_clear_saved_tspecs(wpa_s);
+ pmksa_candidate_free(wpa_s->wpa);
+ wpa_sm_deinit(wpa_s->wpa);
+ wpa_s->wpa = NULL;
+ wpa_blacklist_clear(wpa_s);
+
+ wpa_bss_deinit(wpa_s);
+
+ wpa_supplicant_cancel_delayed_sched_scan(wpa_s);
+ wpa_supplicant_cancel_scan(wpa_s);
+ wpa_supplicant_cancel_auth_timeout(wpa_s);
+ eloop_cancel_timeout(wpa_supplicant_stop_countermeasures, wpa_s, NULL);
+#ifdef CONFIG_DELAYED_MIC_ERROR_REPORT
+ eloop_cancel_timeout(wpa_supplicant_delayed_mic_error_report,
+ wpa_s, NULL);
+#endif /* CONFIG_DELAYED_MIC_ERROR_REPORT */
+
+ eloop_cancel_timeout(wpas_network_reenabled, wpa_s, NULL);
+
+ wpas_wps_deinit(wpa_s);
+
+ wpabuf_free(wpa_s->pending_eapol_rx);
+ wpa_s->pending_eapol_rx = NULL;
+
+#ifdef CONFIG_IBSS_RSN
+ ibss_rsn_deinit(wpa_s->ibss_rsn);
+ wpa_s->ibss_rsn = NULL;
+#endif /* CONFIG_IBSS_RSN */
+
+ sme_deinit(wpa_s);
+
+#ifdef CONFIG_AP
+ wpa_supplicant_ap_deinit(wpa_s);
+#endif /* CONFIG_AP */
+
+ wpas_p2p_deinit(wpa_s);
+
+#ifdef CONFIG_OFFCHANNEL
+ offchannel_deinit(wpa_s);
+#endif /* CONFIG_OFFCHANNEL */
+
+ wpa_supplicant_cancel_sched_scan(wpa_s);
+
+ os_free(wpa_s->next_scan_freqs);
+ wpa_s->next_scan_freqs = NULL;
+
+ os_free(wpa_s->manual_scan_freqs);
+ wpa_s->manual_scan_freqs = NULL;
+
+ os_free(wpa_s->manual_sched_scan_freqs);
+ wpa_s->manual_sched_scan_freqs = NULL;
+
+ wpas_mac_addr_rand_scan_clear(wpa_s, MAC_ADDR_RAND_ALL);
+
+ /*
+ * Need to remove any pending gas-query radio work before the
+ * gas_query_deinit() call because gas_query::work has not yet been set
+ * for works that have not been started. gas_query_free() will be unable
+ * to cancel such pending radio works and once the pending gas-query
+ * radio work eventually gets removed, the deinit notification call to
+ * gas_query_start_cb() would result in dereferencing freed memory.
+ */
+ if (wpa_s->radio)
+ radio_remove_works(wpa_s, "gas-query", 0);
+ gas_query_deinit(wpa_s->gas);
+ wpa_s->gas = NULL;
+
+ free_hw_features(wpa_s);
+
+ ieee802_1x_dealloc_kay_sm(wpa_s);
+
+ os_free(wpa_s->bssid_filter);
+ wpa_s->bssid_filter = NULL;
+
+ os_free(wpa_s->disallow_aps_bssid);
+ wpa_s->disallow_aps_bssid = NULL;
+ os_free(wpa_s->disallow_aps_ssid);
+ wpa_s->disallow_aps_ssid = NULL;
+
+ wnm_bss_keep_alive_deinit(wpa_s);
+#ifdef CONFIG_WNM
+ wnm_deallocate_memory(wpa_s);
+#endif /* CONFIG_WNM */
+
+ ext_password_deinit(wpa_s->ext_pw);
+ wpa_s->ext_pw = NULL;
+
+ wpabuf_free(wpa_s->last_gas_resp);
+ wpa_s->last_gas_resp = NULL;
+ wpabuf_free(wpa_s->prev_gas_resp);
+ wpa_s->prev_gas_resp = NULL;
+
+ os_free(wpa_s->last_scan_res);
+ wpa_s->last_scan_res = NULL;
+
+#ifdef CONFIG_HS20
+ hs20_deinit(wpa_s);
+#endif /* CONFIG_HS20 */
+
+ for (i = 0; i < NUM_VENDOR_ELEM_FRAMES; i++) {
+ wpabuf_free(wpa_s->vendor_elem[i]);
+ wpa_s->vendor_elem[i] = NULL;
+ }
+
+ wmm_ac_notify_disassoc(wpa_s);
+}
+
+
+/**
+ * wpa_clear_keys - Clear keys configured for the driver
+ * @wpa_s: Pointer to wpa_supplicant data
+ * @addr: Previously used BSSID or %NULL if not available
+ *
+ * This function clears the encryption keys that has been previously configured
+ * for the driver.
+ */
+void wpa_clear_keys(struct wpa_supplicant *wpa_s, const u8 *addr)
+{
+ int i, max;
+
+#ifdef CONFIG_IEEE80211W
+ max = 6;
+#else /* CONFIG_IEEE80211W */
+ max = 4;
+#endif /* CONFIG_IEEE80211W */
+
+ /* MLME-DELETEKEYS.request */
+ for (i = 0; i < max; i++) {
+ if (wpa_s->keys_cleared & BIT(i))
+ continue;
+ wpa_drv_set_key(wpa_s, WPA_ALG_NONE, NULL, i, 0, NULL, 0,
+ NULL, 0);
+ }
+ if (!(wpa_s->keys_cleared & BIT(0)) && addr &&
+ !is_zero_ether_addr(addr)) {
+ wpa_drv_set_key(wpa_s, WPA_ALG_NONE, addr, 0, 0, NULL, 0, NULL,
+ 0);
+ /* MLME-SETPROTECTION.request(None) */
+ wpa_drv_mlme_setprotection(
+ wpa_s, addr,
+ MLME_SETPROTECTION_PROTECT_TYPE_NONE,
+ MLME_SETPROTECTION_KEY_TYPE_PAIRWISE);
+ }
+ wpa_s->keys_cleared = (u32) -1;
+}
+
+
+/**
+ * wpa_supplicant_state_txt - Get the connection state name as a text string
+ * @state: State (wpa_state; WPA_*)
+ * Returns: The state name as a printable text string
+ */
+const char * wpa_supplicant_state_txt(enum wpa_states state)
+{
+ switch (state) {
+ case WPA_DISCONNECTED:
+ return "DISCONNECTED";
+ case WPA_INACTIVE:
+ return "INACTIVE";
+ case WPA_INTERFACE_DISABLED:
+ return "INTERFACE_DISABLED";
+ case WPA_SCANNING:
+ return "SCANNING";
+ case WPA_AUTHENTICATING:
+ return "AUTHENTICATING";
+ case WPA_ASSOCIATING:
+ return "ASSOCIATING";
+ case WPA_ASSOCIATED:
+ return "ASSOCIATED";
+ case WPA_4WAY_HANDSHAKE:
+ return "4WAY_HANDSHAKE";
+ case WPA_GROUP_HANDSHAKE:
+ return "GROUP_HANDSHAKE";
+ case WPA_COMPLETED:
+ return "COMPLETED";
+ default:
+ return "UNKNOWN";
+ }
+}
+
+
+#ifdef CONFIG_BGSCAN
+
+static void wpa_supplicant_start_bgscan(struct wpa_supplicant *wpa_s)
+{
+ const char *name;
+
+ if (wpa_s->current_ssid && wpa_s->current_ssid->bgscan)
+ name = wpa_s->current_ssid->bgscan;
+ else
+ name = wpa_s->conf->bgscan;
+ if (name == NULL || name[0] == '\0')
+ return;
+ if (wpas_driver_bss_selection(wpa_s))
+ return;
+ if (wpa_s->current_ssid == wpa_s->bgscan_ssid)
+ return;
+#ifdef CONFIG_P2P
+ if (wpa_s->p2p_group_interface != NOT_P2P_GROUP_INTERFACE)
+ return;
+#endif /* CONFIG_P2P */
+
+ bgscan_deinit(wpa_s);
+ if (wpa_s->current_ssid) {
+ if (bgscan_init(wpa_s, wpa_s->current_ssid, name)) {
+ wpa_dbg(wpa_s, MSG_DEBUG, "Failed to initialize "
+ "bgscan");
+ /*
+ * Live without bgscan; it is only used as a roaming
+ * optimization, so the initial connection is not
+ * affected.
+ */
+ } else {
+ struct wpa_scan_results *scan_res;
+ wpa_s->bgscan_ssid = wpa_s->current_ssid;
+ scan_res = wpa_supplicant_get_scan_results(wpa_s, NULL,
+ 0);
+ if (scan_res) {
+ bgscan_notify_scan(wpa_s, scan_res);
+ wpa_scan_results_free(scan_res);
+ }
+ }
+ } else
+ wpa_s->bgscan_ssid = NULL;
+}
+
+
+static void wpa_supplicant_stop_bgscan(struct wpa_supplicant *wpa_s)
+{
+ if (wpa_s->bgscan_ssid != NULL) {
+ bgscan_deinit(wpa_s);
+ wpa_s->bgscan_ssid = NULL;
+ }
+}
+
+#endif /* CONFIG_BGSCAN */
+
+
+static void wpa_supplicant_start_autoscan(struct wpa_supplicant *wpa_s)
+{
+ if (autoscan_init(wpa_s, 0))
+ wpa_dbg(wpa_s, MSG_DEBUG, "Failed to initialize autoscan");
+}
+
+
+static void wpa_supplicant_stop_autoscan(struct wpa_supplicant *wpa_s)
+{
+ autoscan_deinit(wpa_s);
+}
+
+
+void wpa_supplicant_reinit_autoscan(struct wpa_supplicant *wpa_s)
+{
+ if (wpa_s->wpa_state == WPA_DISCONNECTED ||
+ wpa_s->wpa_state == WPA_SCANNING) {
+ autoscan_deinit(wpa_s);
+ wpa_supplicant_start_autoscan(wpa_s);
+ }
+}
+
+
+/**
+ * wpa_supplicant_set_state - Set current connection state
+ * @wpa_s: Pointer to wpa_supplicant data
+ * @state: The new connection state
+ *
+ * This function is called whenever the connection state changes, e.g.,
+ * association is completed for WPA/WPA2 4-Way Handshake is started.
+ */
+void wpa_supplicant_set_state(struct wpa_supplicant *wpa_s,
+ enum wpa_states state)
+{
+ enum wpa_states old_state = wpa_s->wpa_state;
+
+ wpa_dbg(wpa_s, MSG_DEBUG, "State: %s -> %s",
+ wpa_supplicant_state_txt(wpa_s->wpa_state),
+ wpa_supplicant_state_txt(state));
+
+ if (state == WPA_INTERFACE_DISABLED) {
+ /* Assure normal scan when interface is restored */
+ wpa_s->normal_scans = 0;
+ }
+
+ if (state == WPA_COMPLETED) {
+ wpas_connect_work_done(wpa_s);
+ /* Reinitialize normal_scan counter */
+ wpa_s->normal_scans = 0;
+ }
+
+#ifdef CONFIG_P2P
+ /*
+ * P2PS client has to reply to Probe Request frames received on the
+ * group operating channel. Enable Probe Request frame reporting for
+ * P2P connected client in case p2p_cli_probe configuration property is
+ * set to 1.
+ */
+ if (wpa_s->conf->p2p_cli_probe && wpa_s->current_ssid &&
+ wpa_s->current_ssid->mode == WPAS_MODE_INFRA &&
+ wpa_s->current_ssid->p2p_group) {
+ if (state == WPA_COMPLETED && !wpa_s->p2p_cli_probe) {
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ "P2P: Enable CLI Probe Request RX reporting");
+ wpa_s->p2p_cli_probe =
+ wpa_drv_probe_req_report(wpa_s, 1) >= 0;
+ } else if (state != WPA_COMPLETED && wpa_s->p2p_cli_probe) {
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ "P2P: Disable CLI Probe Request RX reporting");
+ wpa_s->p2p_cli_probe = 0;
+ wpa_drv_probe_req_report(wpa_s, 0);
+ }
+ }
+#endif /* CONFIG_P2P */
+
+ if (state != WPA_SCANNING)
+ wpa_supplicant_notify_scanning(wpa_s, 0);
+
+ if (state == WPA_COMPLETED && wpa_s->new_connection) {
+ struct wpa_ssid *ssid = wpa_s->current_ssid;
+#if defined(CONFIG_CTRL_IFACE) || !defined(CONFIG_NO_STDOUT_DEBUG)
+ wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_CONNECTED "- Connection to "
+ MACSTR " completed [id=%d id_str=%s]",
+ MAC2STR(wpa_s->bssid),
+ ssid ? ssid->id : -1,
+ ssid && ssid->id_str ? ssid->id_str : "");
+#endif /* CONFIG_CTRL_IFACE || !CONFIG_NO_STDOUT_DEBUG */
+ wpas_clear_temp_disabled(wpa_s, ssid, 1);
+ wpa_blacklist_clear(wpa_s);
+ wpa_s->extra_blacklist_count = 0;
+ wpa_s->new_connection = 0;
+ wpa_drv_set_operstate(wpa_s, 1);
+#ifndef IEEE8021X_EAPOL
+ wpa_drv_set_supp_port(wpa_s, 1);
+#endif /* IEEE8021X_EAPOL */
+ wpa_s->after_wps = 0;
+ wpa_s->known_wps_freq = 0;
+ wpas_p2p_completed(wpa_s);
+
+ sme_sched_obss_scan(wpa_s, 1);
+ } else if (state == WPA_DISCONNECTED || state == WPA_ASSOCIATING ||
+ state == WPA_ASSOCIATED) {
+ wpa_s->new_connection = 1;
+ wpa_drv_set_operstate(wpa_s, 0);
+#ifndef IEEE8021X_EAPOL
+ wpa_drv_set_supp_port(wpa_s, 0);
+#endif /* IEEE8021X_EAPOL */
+ sme_sched_obss_scan(wpa_s, 0);
+ }
+ wpa_s->wpa_state = state;
+
+#ifdef CONFIG_BGSCAN
+ if (state == WPA_COMPLETED)
+ wpa_supplicant_start_bgscan(wpa_s);
+ else if (state < WPA_ASSOCIATED)
+ wpa_supplicant_stop_bgscan(wpa_s);
+#endif /* CONFIG_BGSCAN */
+
+ if (state == WPA_AUTHENTICATING)
+ wpa_supplicant_stop_autoscan(wpa_s);
+
+ if (state == WPA_DISCONNECTED || state == WPA_INACTIVE)
+ wpa_supplicant_start_autoscan(wpa_s);
+
+ if (old_state >= WPA_ASSOCIATED && wpa_s->wpa_state < WPA_ASSOCIATED)
+ wmm_ac_notify_disassoc(wpa_s);
+
+ if (wpa_s->wpa_state != old_state) {
+ wpas_notify_state_changed(wpa_s, wpa_s->wpa_state, old_state);
+
+ /*
+ * Notify the P2P Device interface about a state change in one
+ * of the interfaces.
+ */
+ wpas_p2p_indicate_state_change(wpa_s);
+
+ if (wpa_s->wpa_state == WPA_COMPLETED ||
+ old_state == WPA_COMPLETED)
+ wpas_notify_auth_changed(wpa_s);
+ }
+}
+
+
+void wpa_supplicant_terminate_proc(struct wpa_global *global)
+{
+ int pending = 0;
+#ifdef CONFIG_WPS
+ struct wpa_supplicant *wpa_s = global->ifaces;
+ while (wpa_s) {
+ struct wpa_supplicant *next = wpa_s->next;
+ if (wpas_wps_terminate_pending(wpa_s) == 1)
+ pending = 1;
+#ifdef CONFIG_P2P
+ if (wpa_s->p2p_group_interface != NOT_P2P_GROUP_INTERFACE ||
+ (wpa_s->current_ssid && wpa_s->current_ssid->p2p_group))
+ wpas_p2p_disconnect(wpa_s);
+#endif /* CONFIG_P2P */
+ wpa_s = next;
+ }
+#endif /* CONFIG_WPS */
+ if (pending)
+ return;
+ eloop_terminate();
+}
+
+
+static void wpa_supplicant_terminate(int sig, void *signal_ctx)
+{
+ struct wpa_global *global = signal_ctx;
+ wpa_supplicant_terminate_proc(global);
+}
+
+
+void wpa_supplicant_clear_status(struct wpa_supplicant *wpa_s)
+{
+ enum wpa_states old_state = wpa_s->wpa_state;
+
+ wpa_s->pairwise_cipher = 0;
+ wpa_s->group_cipher = 0;
+ wpa_s->mgmt_group_cipher = 0;
+ wpa_s->key_mgmt = 0;
+ if (wpa_s->wpa_state != WPA_INTERFACE_DISABLED)
+ wpa_supplicant_set_state(wpa_s, WPA_DISCONNECTED);
+
+ if (wpa_s->wpa_state != old_state)
+ wpas_notify_state_changed(wpa_s, wpa_s->wpa_state, old_state);
+}
+
+
+/**
+ * wpa_supplicant_reload_configuration - Reload configuration data
+ * @wpa_s: Pointer to wpa_supplicant data
+ * Returns: 0 on success or -1 if configuration parsing failed
+ *
+ * This function can be used to request that the configuration data is reloaded
+ * (e.g., after configuration file change). This function is reloading
+ * configuration only for one interface, so this may need to be called multiple
+ * times if %wpa_supplicant is controlling multiple interfaces and all
+ * interfaces need reconfiguration.
+ */
+int wpa_supplicant_reload_configuration(struct wpa_supplicant *wpa_s)
+{
+ struct wpa_config *conf;
+ int reconf_ctrl;
+ int old_ap_scan;
+
+ if (wpa_s->confname == NULL)
+ return -1;
+ conf = wpa_config_read(wpa_s->confname, NULL);
+ if (conf == NULL) {
+ wpa_msg(wpa_s, MSG_ERROR, "Failed to parse the configuration "
+ "file '%s' - exiting", wpa_s->confname);
+ return -1;
+ }
+ wpa_config_read(wpa_s->confanother, conf);
+
+ conf->changed_parameters = (unsigned int) -1;
+
+ reconf_ctrl = !!conf->ctrl_interface != !!wpa_s->conf->ctrl_interface
+ || (conf->ctrl_interface && wpa_s->conf->ctrl_interface &&
+ os_strcmp(conf->ctrl_interface,
+ wpa_s->conf->ctrl_interface) != 0);
+
+ if (reconf_ctrl && wpa_s->ctrl_iface) {
+ wpa_supplicant_ctrl_iface_deinit(wpa_s->ctrl_iface);
+ wpa_s->ctrl_iface = NULL;
+ }
+
+ eapol_sm_invalidate_cached_session(wpa_s->eapol);
+ if (wpa_s->current_ssid) {
+ if (wpa_s->wpa_state >= WPA_AUTHENTICATING)
+ wpa_s->own_disconnect_req = 1;
+ wpa_supplicant_deauthenticate(wpa_s,
+ WLAN_REASON_DEAUTH_LEAVING);
+ }
+
+ /*
+ * TODO: should notify EAPOL SM about changes in opensc_engine_path,
+ * pkcs11_engine_path, pkcs11_module_path, openssl_ciphers.
+ */
+ if (wpa_key_mgmt_wpa_psk(wpa_s->key_mgmt)) {
+ /*
+ * Clear forced success to clear EAP state for next
+ * authentication.
+ */
+ eapol_sm_notify_eap_success(wpa_s->eapol, FALSE);
+ }
+ eapol_sm_notify_config(wpa_s->eapol, NULL, NULL);
+ wpa_sm_set_config(wpa_s->wpa, NULL);
+ wpa_sm_pmksa_cache_flush(wpa_s->wpa, NULL);
+ wpa_sm_set_fast_reauth(wpa_s->wpa, wpa_s->conf->fast_reauth);
+ rsn_preauth_deinit(wpa_s->wpa);
+
+ old_ap_scan = wpa_s->conf->ap_scan;
+ wpa_config_free(wpa_s->conf);
+ wpa_s->conf = conf;
+ if (old_ap_scan != wpa_s->conf->ap_scan)
+ wpas_notify_ap_scan_changed(wpa_s);
+
+ if (reconf_ctrl)
+ wpa_s->ctrl_iface = wpa_supplicant_ctrl_iface_init(wpa_s);
+
+ wpa_supplicant_update_config(wpa_s);
+
+ wpa_supplicant_clear_status(wpa_s);
+ if (wpa_supplicant_enabled_networks(wpa_s)) {
+ wpa_s->reassociate = 1;
+ wpa_supplicant_req_scan(wpa_s, 0, 0);
+ }
+ wpa_dbg(wpa_s, MSG_DEBUG, "Reconfiguration completed");
+ return 0;
+}
+
+
+static void wpa_supplicant_reconfig(int sig, void *signal_ctx)
+{
+ struct wpa_global *global = signal_ctx;
+ struct wpa_supplicant *wpa_s;
+ for (wpa_s = global->ifaces; wpa_s; wpa_s = wpa_s->next) {
+ wpa_dbg(wpa_s, MSG_DEBUG, "Signal %d received - reconfiguring",
+ sig);
+ if (wpa_supplicant_reload_configuration(wpa_s) < 0) {
+ wpa_supplicant_terminate_proc(global);
+ }
+ }
+}
+
+
+static int wpa_supplicant_suites_from_ai(struct wpa_supplicant *wpa_s,
+ struct wpa_ssid *ssid,
+ struct wpa_ie_data *ie)
+{
+ int ret = wpa_sm_parse_own_wpa_ie(wpa_s->wpa, ie);
+ if (ret) {
+ if (ret == -2) {
+ wpa_msg(wpa_s, MSG_INFO, "WPA: Failed to parse WPA IE "
+ "from association info");
+ }
+ return -1;
+ }
+
+ wpa_dbg(wpa_s, MSG_DEBUG, "WPA: Using WPA IE from AssocReq to set "
+ "cipher suites");
+ if (!(ie->group_cipher & ssid->group_cipher)) {
+ wpa_msg(wpa_s, MSG_INFO, "WPA: Driver used disabled group "
+ "cipher 0x%x (mask 0x%x) - reject",
+ ie->group_cipher, ssid->group_cipher);
+ return -1;
+ }
+ if (!(ie->pairwise_cipher & ssid->pairwise_cipher)) {
+ wpa_msg(wpa_s, MSG_INFO, "WPA: Driver used disabled pairwise "
+ "cipher 0x%x (mask 0x%x) - reject",
+ ie->pairwise_cipher, ssid->pairwise_cipher);
+ return -1;
+ }
+ if (!(ie->key_mgmt & ssid->key_mgmt)) {
+ wpa_msg(wpa_s, MSG_INFO, "WPA: Driver used disabled key "
+ "management 0x%x (mask 0x%x) - reject",
+ ie->key_mgmt, ssid->key_mgmt);
+ return -1;
+ }
+
+#ifdef CONFIG_IEEE80211W
+ if (!(ie->capabilities & WPA_CAPABILITY_MFPC) &&
+ wpas_get_ssid_pmf(wpa_s, ssid) == MGMT_FRAME_PROTECTION_REQUIRED) {
+ wpa_msg(wpa_s, MSG_INFO, "WPA: Driver associated with an AP "
+ "that does not support management frame protection - "
+ "reject");
+ return -1;
+ }
+#endif /* CONFIG_IEEE80211W */
+
+ return 0;
+}
+
+
+/**
+ * wpa_supplicant_set_suites - Set authentication and encryption parameters
+ * @wpa_s: Pointer to wpa_supplicant data
+ * @bss: Scan results for the selected BSS, or %NULL if not available
+ * @ssid: Configuration data for the selected network
+ * @wpa_ie: Buffer for the WPA/RSN IE
+ * @wpa_ie_len: Maximum wpa_ie buffer size on input. This is changed to be the
+ * used buffer length in case the functions returns success.
+ * Returns: 0 on success or -1 on failure
+ *
+ * This function is used to configure authentication and encryption parameters
+ * based on the network configuration and scan result for the selected BSS (if
+ * available).
+ */
+int wpa_supplicant_set_suites(struct wpa_supplicant *wpa_s,
+ struct wpa_bss *bss, struct wpa_ssid *ssid,
+ u8 *wpa_ie, size_t *wpa_ie_len)
+{
+ struct wpa_ie_data ie;
+ int sel, proto;
+ const u8 *bss_wpa, *bss_rsn, *bss_osen;
+
+ if (bss) {
+ bss_wpa = wpa_bss_get_vendor_ie(bss, WPA_IE_VENDOR_TYPE);
+ bss_rsn = wpa_bss_get_ie(bss, WLAN_EID_RSN);
+ bss_osen = wpa_bss_get_vendor_ie(bss, OSEN_IE_VENDOR_TYPE);
+ } else
+ bss_wpa = bss_rsn = bss_osen = NULL;
+
+ if (bss_rsn && (ssid->proto & WPA_PROTO_RSN) &&
+ wpa_parse_wpa_ie(bss_rsn, 2 + bss_rsn[1], &ie) == 0 &&
+ (ie.group_cipher & ssid->group_cipher) &&
+ (ie.pairwise_cipher & ssid->pairwise_cipher) &&
+ (ie.key_mgmt & ssid->key_mgmt)) {
+ wpa_dbg(wpa_s, MSG_DEBUG, "RSN: using IEEE 802.11i/D9.0");
+ proto = WPA_PROTO_RSN;
+ } else if (bss_wpa && (ssid->proto & WPA_PROTO_WPA) &&
+ wpa_parse_wpa_ie(bss_wpa, 2 + bss_wpa[1], &ie) == 0 &&
+ (ie.group_cipher & ssid->group_cipher) &&
+ (ie.pairwise_cipher & ssid->pairwise_cipher) &&
+ (ie.key_mgmt & ssid->key_mgmt)) {
+ wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using IEEE 802.11i/D3.0");
+ proto = WPA_PROTO_WPA;
+#ifdef CONFIG_HS20
+ } else if (bss_osen && (ssid->proto & WPA_PROTO_OSEN)) {
+ wpa_dbg(wpa_s, MSG_DEBUG, "HS 2.0: using OSEN");
+ /* TODO: parse OSEN element */
+ os_memset(&ie, 0, sizeof(ie));
+ ie.group_cipher = WPA_CIPHER_CCMP;
+ ie.pairwise_cipher = WPA_CIPHER_CCMP;
+ ie.key_mgmt = WPA_KEY_MGMT_OSEN;
+ proto = WPA_PROTO_OSEN;
+#endif /* CONFIG_HS20 */
+ } else if (bss) {
+ wpa_msg(wpa_s, MSG_WARNING, "WPA: Failed to select WPA/RSN");
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ "WPA: ssid proto=0x%x pairwise_cipher=0x%x group_cipher=0x%x key_mgmt=0x%x",
+ ssid->proto, ssid->pairwise_cipher, ssid->group_cipher,
+ ssid->key_mgmt);
+ wpa_dbg(wpa_s, MSG_DEBUG, "WPA: BSS " MACSTR " ssid='%s'%s%s%s",
+ MAC2STR(bss->bssid),
+ wpa_ssid_txt(bss->ssid, bss->ssid_len),
+ bss_wpa ? " WPA" : "",
+ bss_rsn ? " RSN" : "",
+ bss_osen ? " OSEN" : "");
+ if (bss_rsn) {
+ wpa_hexdump(MSG_DEBUG, "RSN", bss_rsn, 2 + bss_rsn[1]);
+ if (wpa_parse_wpa_ie(bss_rsn, 2 + bss_rsn[1], &ie)) {
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ "Could not parse RSN element");
+ } else {
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ "RSN: pairwise_cipher=0x%x group_cipher=0x%x key_mgmt=0x%x",
+ ie.pairwise_cipher, ie.group_cipher,
+ ie.key_mgmt);
+ }
+ }
+ if (bss_wpa) {
+ wpa_hexdump(MSG_DEBUG, "WPA", bss_wpa, 2 + bss_wpa[1]);
+ if (wpa_parse_wpa_ie(bss_wpa, 2 + bss_wpa[1], &ie)) {
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ "Could not parse WPA element");
+ } else {
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ "WPA: pairwise_cipher=0x%x group_cipher=0x%x key_mgmt=0x%x",
+ ie.pairwise_cipher, ie.group_cipher,
+ ie.key_mgmt);
+ }
+ }
+ return -1;
+ } else {
+ if (ssid->proto & WPA_PROTO_OSEN)
+ proto = WPA_PROTO_OSEN;
+ else if (ssid->proto & WPA_PROTO_RSN)
+ proto = WPA_PROTO_RSN;
+ else
+ proto = WPA_PROTO_WPA;
+ if (wpa_supplicant_suites_from_ai(wpa_s, ssid, &ie) < 0) {
+ os_memset(&ie, 0, sizeof(ie));
+ ie.group_cipher = ssid->group_cipher;
+ ie.pairwise_cipher = ssid->pairwise_cipher;
+ ie.key_mgmt = ssid->key_mgmt;
+#ifdef CONFIG_IEEE80211W
+ ie.mgmt_group_cipher =
+ ssid->ieee80211w != NO_MGMT_FRAME_PROTECTION ?
+ WPA_CIPHER_AES_128_CMAC : 0;
+#endif /* CONFIG_IEEE80211W */
+ wpa_dbg(wpa_s, MSG_DEBUG, "WPA: Set cipher suites "
+ "based on configuration");
+ } else
+ proto = ie.proto;
+ }
+
+ wpa_dbg(wpa_s, MSG_DEBUG, "WPA: Selected cipher suites: group %d "
+ "pairwise %d key_mgmt %d proto %d",
+ ie.group_cipher, ie.pairwise_cipher, ie.key_mgmt, proto);
+#ifdef CONFIG_IEEE80211W
+ if (ssid->ieee80211w) {
+ wpa_dbg(wpa_s, MSG_DEBUG, "WPA: Selected mgmt group cipher %d",
+ ie.mgmt_group_cipher);
+ }
+#endif /* CONFIG_IEEE80211W */
+
+ wpa_s->wpa_proto = proto;
+ wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_PROTO, proto);
+ wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_RSN_ENABLED,
+ !!(ssid->proto & (WPA_PROTO_RSN | WPA_PROTO_OSEN)));
+
+ if (bss || !wpa_s->ap_ies_from_associnfo) {
+ if (wpa_sm_set_ap_wpa_ie(wpa_s->wpa, bss_wpa,
+ bss_wpa ? 2 + bss_wpa[1] : 0) ||
+ wpa_sm_set_ap_rsn_ie(wpa_s->wpa, bss_rsn,
+ bss_rsn ? 2 + bss_rsn[1] : 0))
+ return -1;
+ }
+
+ sel = ie.group_cipher & ssid->group_cipher;
+ wpa_s->group_cipher = wpa_pick_group_cipher(sel);
+ if (wpa_s->group_cipher < 0) {
+ wpa_msg(wpa_s, MSG_WARNING, "WPA: Failed to select group "
+ "cipher");
+ return -1;
+ }
+ wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using GTK %s",
+ wpa_cipher_txt(wpa_s->group_cipher));
+
+ sel = ie.pairwise_cipher & ssid->pairwise_cipher;
+ wpa_s->pairwise_cipher = wpa_pick_pairwise_cipher(sel, 1);
+ if (wpa_s->pairwise_cipher < 0) {
+ wpa_msg(wpa_s, MSG_WARNING, "WPA: Failed to select pairwise "
+ "cipher");
+ return -1;
+ }
+ wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using PTK %s",
+ wpa_cipher_txt(wpa_s->pairwise_cipher));
+
+ sel = ie.key_mgmt & ssid->key_mgmt;
+#ifdef CONFIG_SAE
+ if (!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_SAE))
+ sel &= ~(WPA_KEY_MGMT_SAE | WPA_KEY_MGMT_FT_SAE);
+#endif /* CONFIG_SAE */
+ if (0) {
+#ifdef CONFIG_SUITEB192
+ } else if (sel & WPA_KEY_MGMT_IEEE8021X_SUITE_B_192) {
+ wpa_s->key_mgmt = WPA_KEY_MGMT_IEEE8021X_SUITE_B_192;
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ "WPA: using KEY_MGMT 802.1X with Suite B (192-bit)");
+#endif /* CONFIG_SUITEB192 */
+#ifdef CONFIG_SUITEB
+ } else if (sel & WPA_KEY_MGMT_IEEE8021X_SUITE_B) {
+ wpa_s->key_mgmt = WPA_KEY_MGMT_IEEE8021X_SUITE_B;
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ "WPA: using KEY_MGMT 802.1X with Suite B");
+#endif /* CONFIG_SUITEB */
+#ifdef CONFIG_IEEE80211R
+ } else if (sel & WPA_KEY_MGMT_FT_IEEE8021X) {
+ wpa_s->key_mgmt = WPA_KEY_MGMT_FT_IEEE8021X;
+ wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using KEY_MGMT FT/802.1X");
+ } else if (sel & WPA_KEY_MGMT_FT_PSK) {
+ wpa_s->key_mgmt = WPA_KEY_MGMT_FT_PSK;
+ wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using KEY_MGMT FT/PSK");
+#endif /* CONFIG_IEEE80211R */
+#ifdef CONFIG_SAE
+ } else if (sel & WPA_KEY_MGMT_SAE) {
+ wpa_s->key_mgmt = WPA_KEY_MGMT_SAE;
+ wpa_dbg(wpa_s, MSG_DEBUG, "RSN: using KEY_MGMT SAE");
+ } else if (sel & WPA_KEY_MGMT_FT_SAE) {
+ wpa_s->key_mgmt = WPA_KEY_MGMT_FT_SAE;
+ wpa_dbg(wpa_s, MSG_DEBUG, "RSN: using KEY_MGMT FT/SAE");
+#endif /* CONFIG_SAE */
+#ifdef CONFIG_IEEE80211W
+ } else if (sel & WPA_KEY_MGMT_IEEE8021X_SHA256) {
+ wpa_s->key_mgmt = WPA_KEY_MGMT_IEEE8021X_SHA256;
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ "WPA: using KEY_MGMT 802.1X with SHA256");
+ } else if (sel & WPA_KEY_MGMT_PSK_SHA256) {
+ wpa_s->key_mgmt = WPA_KEY_MGMT_PSK_SHA256;
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ "WPA: using KEY_MGMT PSK with SHA256");
+#endif /* CONFIG_IEEE80211W */
+ } else if (sel & WPA_KEY_MGMT_IEEE8021X) {
+ wpa_s->key_mgmt = WPA_KEY_MGMT_IEEE8021X;
+ wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using KEY_MGMT 802.1X");
+ } else if (sel & WPA_KEY_MGMT_PSK) {
+ wpa_s->key_mgmt = WPA_KEY_MGMT_PSK;
+ wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using KEY_MGMT WPA-PSK");
+ } else if (sel & WPA_KEY_MGMT_WPA_NONE) {
+ wpa_s->key_mgmt = WPA_KEY_MGMT_WPA_NONE;
+ wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using KEY_MGMT WPA-NONE");
+#ifdef CONFIG_HS20
+ } else if (sel & WPA_KEY_MGMT_OSEN) {
+ wpa_s->key_mgmt = WPA_KEY_MGMT_OSEN;
+ wpa_dbg(wpa_s, MSG_DEBUG, "HS 2.0: using KEY_MGMT OSEN");
+#endif /* CONFIG_HS20 */
+ } else {
+ wpa_msg(wpa_s, MSG_WARNING, "WPA: Failed to select "
+ "authenticated key management type");
+ return -1;
+ }
+
+ wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_KEY_MGMT, wpa_s->key_mgmt);
+ wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_PAIRWISE,
+ wpa_s->pairwise_cipher);
+ wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_GROUP, wpa_s->group_cipher);
+
+#ifdef CONFIG_IEEE80211W
+ sel = ie.mgmt_group_cipher;
+ if (wpas_get_ssid_pmf(wpa_s, ssid) == NO_MGMT_FRAME_PROTECTION ||
+ !(ie.capabilities & WPA_CAPABILITY_MFPC))
+ sel = 0;
+ if (sel & WPA_CIPHER_AES_128_CMAC) {
+ wpa_s->mgmt_group_cipher = WPA_CIPHER_AES_128_CMAC;
+ wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using MGMT group cipher "
+ "AES-128-CMAC");
+ } else if (sel & WPA_CIPHER_BIP_GMAC_128) {
+ wpa_s->mgmt_group_cipher = WPA_CIPHER_BIP_GMAC_128;
+ wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using MGMT group cipher "
+ "BIP-GMAC-128");
+ } else if (sel & WPA_CIPHER_BIP_GMAC_256) {
+ wpa_s->mgmt_group_cipher = WPA_CIPHER_BIP_GMAC_256;
+ wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using MGMT group cipher "
+ "BIP-GMAC-256");
+ } else if (sel & WPA_CIPHER_BIP_CMAC_256) {
+ wpa_s->mgmt_group_cipher = WPA_CIPHER_BIP_CMAC_256;
+ wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using MGMT group cipher "
+ "BIP-CMAC-256");
+ } else {
+ wpa_s->mgmt_group_cipher = 0;
+ wpa_dbg(wpa_s, MSG_DEBUG, "WPA: not using MGMT group cipher");
+ }
+ wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_MGMT_GROUP,
+ wpa_s->mgmt_group_cipher);
+ wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_MFP,
+ wpas_get_ssid_pmf(wpa_s, ssid));
+#endif /* CONFIG_IEEE80211W */
+
+ if (wpa_sm_set_assoc_wpa_ie_default(wpa_s->wpa, wpa_ie, wpa_ie_len)) {
+ wpa_msg(wpa_s, MSG_WARNING, "WPA: Failed to generate WPA IE");
+ return -1;
+ }
+
+ if (wpa_key_mgmt_wpa_psk(ssid->key_mgmt)) {
+ int psk_set = 0;
+
+ if (ssid->psk_set) {
+ wpa_sm_set_pmk(wpa_s->wpa, ssid->psk, PMK_LEN, NULL);
+ psk_set = 1;
+ }
+#ifndef CONFIG_NO_PBKDF2
+ if (bss && ssid->bssid_set && ssid->ssid_len == 0 &&
+ ssid->passphrase) {
+ u8 psk[PMK_LEN];
+ pbkdf2_sha1(ssid->passphrase, bss->ssid, bss->ssid_len,
+ 4096, psk, PMK_LEN);
+ wpa_hexdump_key(MSG_MSGDUMP, "PSK (from passphrase)",
+ psk, PMK_LEN);
+ wpa_sm_set_pmk(wpa_s->wpa, psk, PMK_LEN, NULL);
+ psk_set = 1;
+ os_memset(psk, 0, sizeof(psk));
+ }
+#endif /* CONFIG_NO_PBKDF2 */
+#ifdef CONFIG_EXT_PASSWORD
+ if (ssid->ext_psk) {
+ struct wpabuf *pw = ext_password_get(wpa_s->ext_pw,
+ ssid->ext_psk);
+ char pw_str[64 + 1];
+ u8 psk[PMK_LEN];
+
+ if (pw == NULL) {
+ wpa_msg(wpa_s, MSG_INFO, "EXT PW: No PSK "
+ "found from external storage");
+ return -1;
+ }
+
+ if (wpabuf_len(pw) < 8 || wpabuf_len(pw) > 64) {
+ wpa_msg(wpa_s, MSG_INFO, "EXT PW: Unexpected "
+ "PSK length %d in external storage",
+ (int) wpabuf_len(pw));
+ ext_password_free(pw);
+ return -1;
+ }
+
+ os_memcpy(pw_str, wpabuf_head(pw), wpabuf_len(pw));
+ pw_str[wpabuf_len(pw)] = '\0';
+
+#ifndef CONFIG_NO_PBKDF2
+ if (wpabuf_len(pw) >= 8 && wpabuf_len(pw) < 64 && bss)
+ {
+ pbkdf2_sha1(pw_str, bss->ssid, bss->ssid_len,
+ 4096, psk, PMK_LEN);
+ os_memset(pw_str, 0, sizeof(pw_str));
+ wpa_hexdump_key(MSG_MSGDUMP, "PSK (from "
+ "external passphrase)",
+ psk, PMK_LEN);
+ wpa_sm_set_pmk(wpa_s->wpa, psk, PMK_LEN, NULL);
+ psk_set = 1;
+ os_memset(psk, 0, sizeof(psk));
+ } else
+#endif /* CONFIG_NO_PBKDF2 */
+ if (wpabuf_len(pw) == 2 * PMK_LEN) {
+ if (hexstr2bin(pw_str, psk, PMK_LEN) < 0) {
+ wpa_msg(wpa_s, MSG_INFO, "EXT PW: "
+ "Invalid PSK hex string");
+ os_memset(pw_str, 0, sizeof(pw_str));
+ ext_password_free(pw);
+ return -1;
+ }
+ wpa_sm_set_pmk(wpa_s->wpa, psk, PMK_LEN, NULL);
+ psk_set = 1;
+ os_memset(psk, 0, sizeof(psk));
+ } else {
+ wpa_msg(wpa_s, MSG_INFO, "EXT PW: No suitable "
+ "PSK available");
+ os_memset(pw_str, 0, sizeof(pw_str));
+ ext_password_free(pw);
+ return -1;
+ }
+
+ os_memset(pw_str, 0, sizeof(pw_str));
+ ext_password_free(pw);
+ }
+#endif /* CONFIG_EXT_PASSWORD */
+
+ if (!psk_set) {
+ wpa_msg(wpa_s, MSG_INFO,
+ "No PSK available for association");
+ return -1;
+ }
+ } else
+ wpa_sm_set_pmk_from_pmksa(wpa_s->wpa);
+
+ return 0;
+}
+
+
+static void wpas_ext_capab_byte(struct wpa_supplicant *wpa_s, u8 *pos, int idx)
+{
+ *pos = 0x00;
+
+ switch (idx) {
+ case 0: /* Bits 0-7 */
+ break;
+ case 1: /* Bits 8-15 */
+ break;
+ case 2: /* Bits 16-23 */
+#ifdef CONFIG_WNM
+ *pos |= 0x02; /* Bit 17 - WNM-Sleep Mode */
+ *pos |= 0x08; /* Bit 19 - BSS Transition */
+#endif /* CONFIG_WNM */
+ break;
+ case 3: /* Bits 24-31 */
+#ifdef CONFIG_WNM
+ *pos |= 0x02; /* Bit 25 - SSID List */
+#endif /* CONFIG_WNM */
+#ifdef CONFIG_INTERWORKING
+ if (wpa_s->conf->interworking)
+ *pos |= 0x80; /* Bit 31 - Interworking */
+#endif /* CONFIG_INTERWORKING */
+ break;
+ case 4: /* Bits 32-39 */
+#ifdef CONFIG_INTERWORKING
+ if (wpa_s->drv_flags / WPA_DRIVER_FLAGS_QOS_MAPPING)
+ *pos |= 0x01; /* Bit 32 - QoS Map */
+#endif /* CONFIG_INTERWORKING */
+ break;
+ case 5: /* Bits 40-47 */
+#ifdef CONFIG_HS20
+ if (wpa_s->conf->hs20)
+ *pos |= 0x40; /* Bit 46 - WNM-Notification */
+#endif /* CONFIG_HS20 */
+ break;
+ case 6: /* Bits 48-55 */
+ break;
+ }
+}
+
+
+int wpas_build_ext_capab(struct wpa_supplicant *wpa_s, u8 *buf, size_t buflen)
+{
+ u8 *pos = buf;
+ u8 len = 6, i;
+
+ if (len < wpa_s->extended_capa_len)
+ len = wpa_s->extended_capa_len;
+ if (buflen < (size_t) len + 2) {
+ wpa_printf(MSG_INFO,
+ "Not enough room for building extended capabilities element");
+ return -1;
+ }
+
+ *pos++ = WLAN_EID_EXT_CAPAB;
+ *pos++ = len;
+ for (i = 0; i < len; i++, pos++) {
+ wpas_ext_capab_byte(wpa_s, pos, i);
+
+ if (i < wpa_s->extended_capa_len) {
+ *pos &= ~wpa_s->extended_capa_mask[i];
+ *pos |= wpa_s->extended_capa[i];
+ }
+ }
+
+ while (len > 0 && buf[1 + len] == 0) {
+ len--;
+ buf[1] = len;
+ }
+ if (len == 0)
+ return 0;
+
+ return 2 + len;
+}
+
+
+static int wpas_valid_bss(struct wpa_supplicant *wpa_s,
+ struct wpa_bss *test_bss)
+{
+ struct wpa_bss *bss;
+
+ dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) {
+ if (bss == test_bss)
+ return 1;
+ }
+
+ return 0;
+}
+
+
+static int wpas_valid_ssid(struct wpa_supplicant *wpa_s,
+ struct wpa_ssid *test_ssid)
+{
+ struct wpa_ssid *ssid;
+
+ for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next) {
+ if (ssid == test_ssid)
+ return 1;
+ }
+
+ return 0;
+}
+
+
+int wpas_valid_bss_ssid(struct wpa_supplicant *wpa_s, struct wpa_bss *test_bss,
+ struct wpa_ssid *test_ssid)
+{
+ if (test_bss && !wpas_valid_bss(wpa_s, test_bss))
+ return 0;
+
+ return test_ssid == NULL || wpas_valid_ssid(wpa_s, test_ssid);
+}
+
+
+void wpas_connect_work_free(struct wpa_connect_work *cwork)
+{
+ if (cwork == NULL)
+ return;
+ os_free(cwork);
+}
+
+
+void wpas_connect_work_done(struct wpa_supplicant *wpa_s)
+{
+ struct wpa_connect_work *cwork;
+ struct wpa_radio_work *work = wpa_s->connect_work;
+
+ if (!work)
+ return;
+
+ wpa_s->connect_work = NULL;
+ cwork = work->ctx;
+ work->ctx = NULL;
+ wpas_connect_work_free(cwork);
+ radio_work_done(work);
+}
+
+
+int wpas_update_random_addr(struct wpa_supplicant *wpa_s, int style)
+{
+ struct os_reltime now;
+ u8 addr[ETH_ALEN];
+
+ os_get_reltime(&now);
+ if (wpa_s->last_mac_addr_style == style &&
+ wpa_s->last_mac_addr_change.sec != 0 &&
+ !os_reltime_expired(&now, &wpa_s->last_mac_addr_change,
+ wpa_s->conf->rand_addr_lifetime)) {
+ wpa_msg(wpa_s, MSG_DEBUG,
+ "Previously selected random MAC address has not yet expired");
+ return 0;
+ }
+
+ switch (style) {
+ case 1:
+ if (random_mac_addr(addr) < 0)
+ return -1;
+ break;
+ case 2:
+ os_memcpy(addr, wpa_s->perm_addr, ETH_ALEN);
+ if (random_mac_addr_keep_oui(addr) < 0)
+ return -1;
+ break;
+ default:
+ return -1;
+ }
+
+ if (wpa_drv_set_mac_addr(wpa_s, addr) < 0) {
+ wpa_msg(wpa_s, MSG_INFO,
+ "Failed to set random MAC address");
+ return -1;
+ }
+
+ os_get_reltime(&wpa_s->last_mac_addr_change);
+ wpa_s->mac_addr_changed = 1;
+ wpa_s->last_mac_addr_style = style;
+
+ if (wpa_supplicant_update_mac_addr(wpa_s) < 0) {
+ wpa_msg(wpa_s, MSG_INFO,
+ "Could not update MAC address information");
+ return -1;
+ }
+
+ wpa_msg(wpa_s, MSG_DEBUG, "Using random MAC address " MACSTR,
+ MAC2STR(addr));
+
+ return 0;
+}
+
+
+int wpas_update_random_addr_disassoc(struct wpa_supplicant *wpa_s)
+{
+ if (wpa_s->wpa_state >= WPA_AUTHENTICATING ||
+ !wpa_s->conf->preassoc_mac_addr)
+ return 0;
+
+ return wpas_update_random_addr(wpa_s, wpa_s->conf->preassoc_mac_addr);
+}
+
+
+static void wpas_start_assoc_cb(struct wpa_radio_work *work, int deinit);
+
+/**
+ * wpa_supplicant_associate - Request association
+ * @wpa_s: Pointer to wpa_supplicant data
+ * @bss: Scan results for the selected BSS, or %NULL if not available
+ * @ssid: Configuration data for the selected network
+ *
+ * This function is used to request %wpa_supplicant to associate with a BSS.
+ */
+void wpa_supplicant_associate(struct wpa_supplicant *wpa_s,
+ struct wpa_bss *bss, struct wpa_ssid *ssid)
+{
+ struct wpa_connect_work *cwork;
+ int rand_style;
+
+ if (ssid->mac_addr == -1)
+ rand_style = wpa_s->conf->mac_addr;
+ else
+ rand_style = ssid->mac_addr;
+
+ wmm_ac_clear_saved_tspecs(wpa_s);
+ wpa_s->reassoc_same_bss = 0;
+
+ if (wpa_s->last_ssid == ssid) {
+ wpa_dbg(wpa_s, MSG_DEBUG, "Re-association to the same ESS");
+ if (wpa_s->current_bss && wpa_s->current_bss == bss) {
+ wmm_ac_save_tspecs(wpa_s);
+ wpa_s->reassoc_same_bss = 1;
+ }
+ } else if (rand_style > 0) {
+ if (wpas_update_random_addr(wpa_s, rand_style) < 0)
+ return;
+ wpa_sm_pmksa_cache_flush(wpa_s->wpa, ssid);
+ } else if (wpa_s->mac_addr_changed) {
+ if (wpa_drv_set_mac_addr(wpa_s, NULL) < 0) {
+ wpa_msg(wpa_s, MSG_INFO,
+ "Could not restore permanent MAC address");
+ return;
+ }
+ wpa_s->mac_addr_changed = 0;
+ if (wpa_supplicant_update_mac_addr(wpa_s) < 0) {
+ wpa_msg(wpa_s, MSG_INFO,
+ "Could not update MAC address information");
+ return;
+ }
+ wpa_msg(wpa_s, MSG_DEBUG, "Using permanent MAC address");
+ }
+ wpa_s->last_ssid = ssid;
+
+#ifdef CONFIG_IBSS_RSN
+ ibss_rsn_deinit(wpa_s->ibss_rsn);
+ wpa_s->ibss_rsn = NULL;
+#endif /* CONFIG_IBSS_RSN */
+
+ if (ssid->mode == WPAS_MODE_AP || ssid->mode == WPAS_MODE_P2P_GO ||
+ ssid->mode == WPAS_MODE_P2P_GROUP_FORMATION) {
+#ifdef CONFIG_AP
+ if (!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_AP)) {
+ wpa_msg(wpa_s, MSG_INFO, "Driver does not support AP "
+ "mode");
+ return;
+ }
+ if (wpa_supplicant_create_ap(wpa_s, ssid) < 0) {
+ wpa_supplicant_set_state(wpa_s, WPA_DISCONNECTED);
+ if (ssid->mode == WPAS_MODE_P2P_GROUP_FORMATION)
+ wpas_p2p_ap_setup_failed(wpa_s);
+ return;
+ }
+ wpa_s->current_bss = bss;
+#else /* CONFIG_AP */
+ wpa_msg(wpa_s, MSG_ERROR, "AP mode support not included in "
+ "the build");
+#endif /* CONFIG_AP */
+ return;
+ }
+
+ if (ssid->mode == WPAS_MODE_MESH) {
+#ifdef CONFIG_MESH
+ if (!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_MESH)) {
+ wpa_msg(wpa_s, MSG_INFO,
+ "Driver does not support mesh mode");
+ return;
+ }
+ if (bss)
+ ssid->frequency = bss->freq;
+ if (wpa_supplicant_join_mesh(wpa_s, ssid) < 0) {
+ wpa_msg(wpa_s, MSG_ERROR, "Could not join mesh");
+ return;
+ }
+ wpa_s->current_bss = bss;
+ wpa_msg_ctrl(wpa_s, MSG_INFO, MESH_GROUP_STARTED
+ "ssid=\"%s\" id=%d",
+ wpa_ssid_txt(ssid->ssid, ssid->ssid_len),
+ ssid->id);
+#else /* CONFIG_MESH */
+ wpa_msg(wpa_s, MSG_ERROR,
+ "mesh mode support not included in the build");
+#endif /* CONFIG_MESH */
+ return;
+ }
+
+#ifdef CONFIG_TDLS
+ if (bss)
+ wpa_tdls_ap_ies(wpa_s->wpa, (const u8 *) (bss + 1),
+ bss->ie_len);
+#endif /* CONFIG_TDLS */
+
+ if ((wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME) &&
+ ssid->mode == IEEE80211_MODE_INFRA) {
+ sme_authenticate(wpa_s, bss, ssid);
+ return;
+ }
+
+ if (wpa_s->connect_work) {
+ wpa_dbg(wpa_s, MSG_DEBUG, "Reject wpa_supplicant_associate() call since connect_work exist");
+ return;
+ }
+
+ if (radio_work_pending(wpa_s, "connect")) {
+ wpa_dbg(wpa_s, MSG_DEBUG, "Reject wpa_supplicant_associate() call since pending work exist");
+ return;
+ }
+
+ cwork = os_zalloc(sizeof(*cwork));
+ if (cwork == NULL)
+ return;
+
+ cwork->bss = bss;
+ cwork->ssid = ssid;
+
+ if (radio_add_work(wpa_s, bss ? bss->freq : 0, "connect", 1,
+ wpas_start_assoc_cb, cwork) < 0) {
+ os_free(cwork);
+ }
+}
+
+
+static int bss_is_ibss(struct wpa_bss *bss)
+{
+ return (bss->caps & (IEEE80211_CAP_ESS | IEEE80211_CAP_IBSS)) ==
+ IEEE80211_CAP_IBSS;
+}
+
+
+void ibss_mesh_setup_freq(struct wpa_supplicant *wpa_s,
+ const struct wpa_ssid *ssid,
+ struct hostapd_freq_params *freq)
+{
+ enum hostapd_hw_mode hw_mode;
+ struct hostapd_hw_modes *mode = NULL;
+ int ht40plus[] = { 36, 44, 52, 60, 100, 108, 116, 124, 132, 149, 157,
+ 184, 192 };
+ int vht80[] = { 36, 52, 100, 116, 132, 149 };
+ struct hostapd_channel_data *pri_chan = NULL, *sec_chan = NULL;
+ u8 channel;
+ int i, chan_idx, ht40 = -1, res, obss_scan = 1;
+ unsigned int j;
+ struct hostapd_freq_params vht_freq;
+
+ freq->freq = ssid->frequency;
+
+ for (j = 0; j < wpa_s->last_scan_res_used; j++) {
+ struct wpa_bss *bss = wpa_s->last_scan_res[j];
+
+ if (ssid->mode != WPAS_MODE_IBSS)
+ break;
+
+ /* Don't adjust control freq in case of fixed_freq */
+ if (ssid->fixed_freq)
+ break;
+
+ if (!bss_is_ibss(bss))
+ continue;
+
+ if (ssid->ssid_len == bss->ssid_len &&
+ os_memcmp(ssid->ssid, bss->ssid, bss->ssid_len) == 0) {
+ wpa_printf(MSG_DEBUG,
+ "IBSS already found in scan results, adjust control freq: %d",
+ bss->freq);
+ freq->freq = bss->freq;
+ obss_scan = 0;
+ break;
+ }
+ }
+
+ /* For IBSS check HT_IBSS flag */
+ if (ssid->mode == WPAS_MODE_IBSS &&
+ !(wpa_s->drv_flags & WPA_DRIVER_FLAGS_HT_IBSS))
+ return;
+
+ if (wpa_s->group_cipher == WPA_CIPHER_WEP40 ||
+ wpa_s->group_cipher == WPA_CIPHER_WEP104 ||
+ wpa_s->pairwise_cipher == WPA_CIPHER_TKIP) {
+ wpa_printf(MSG_DEBUG,
+ "IBSS: WEP/TKIP detected, do not try to enable HT");
+ return;
+ }
+
+ hw_mode = ieee80211_freq_to_chan(freq->freq, &channel);
+ for (i = 0; wpa_s->hw.modes && i < wpa_s->hw.num_modes; i++) {
+ if (wpa_s->hw.modes[i].mode == hw_mode) {
+ mode = &wpa_s->hw.modes[i];
+ break;
+ }
+ }
+
+ if (!mode)
+ return;
+
+ freq->ht_enabled = ht_supported(mode);
+ if (!freq->ht_enabled)
+ return;
+
+ /* Setup higher BW only for 5 GHz */
+ if (mode->mode != HOSTAPD_MODE_IEEE80211A)
+ return;
+
+ for (chan_idx = 0; chan_idx < mode->num_channels; chan_idx++) {
+ pri_chan = &mode->channels[chan_idx];
+ if (pri_chan->chan == channel)
+ break;
+ pri_chan = NULL;
+ }
+ if (!pri_chan)
+ return;
+
+ /* Check primary channel flags */
+ if (pri_chan->flag & (HOSTAPD_CHAN_DISABLED | HOSTAPD_CHAN_NO_IR))
+ return;
+
+ /* Check/setup HT40+/HT40- */
+ for (j = 0; j < ARRAY_SIZE(ht40plus); j++) {
+ if (ht40plus[j] == channel) {
+ ht40 = 1;
+ break;
+ }
+ }
+
+ /* Find secondary channel */
+ for (i = 0; i < mode->num_channels; i++) {
+ sec_chan = &mode->channels[i];
+ if (sec_chan->chan == channel + ht40 * 4)
+ break;
+ sec_chan = NULL;
+ }
+ if (!sec_chan)
+ return;
+
+ /* Check secondary channel flags */
+ if (sec_chan->flag & (HOSTAPD_CHAN_DISABLED | HOSTAPD_CHAN_NO_IR))
+ return;
+
+ freq->channel = pri_chan->chan;
+
+ switch (ht40) {
+ case -1:
+ if (!(pri_chan->flag & HOSTAPD_CHAN_HT40MINUS))
+ return;
+ freq->sec_channel_offset = -1;
+ break;
+ case 1:
+ if (!(pri_chan->flag & HOSTAPD_CHAN_HT40PLUS))
+ return;
+ freq->sec_channel_offset = 1;
+ break;
+ default:
+ break;
+ }
+
+ if (freq->sec_channel_offset && obss_scan) {
+ struct wpa_scan_results *scan_res;
+
+ scan_res = wpa_supplicant_get_scan_results(wpa_s, NULL, 0);
+ if (scan_res == NULL) {
+ /* Back to HT20 */
+ freq->sec_channel_offset = 0;
+ return;
+ }
+
+ res = check_40mhz_5g(mode, scan_res, pri_chan->chan,
+ sec_chan->chan);
+ switch (res) {
+ case 0:
+ /* Back to HT20 */
+ freq->sec_channel_offset = 0;
+ break;
+ case 1:
+ /* Configuration allowed */
+ break;
+ case 2:
+ /* Switch pri/sec channels */
+ freq->freq = hw_get_freq(mode, sec_chan->chan);
+ freq->sec_channel_offset = -freq->sec_channel_offset;
+ freq->channel = sec_chan->chan;
+ break;
+ default:
+ freq->sec_channel_offset = 0;
+ break;
+ }
+
+ wpa_scan_results_free(scan_res);
+ }
+
+ wpa_printf(MSG_DEBUG,
+ "IBSS/mesh: setup freq channel %d, sec_channel_offset %d",
+ freq->channel, freq->sec_channel_offset);
+
+ /* Not sure if mesh is ready for VHT */
+ if (ssid->mode != WPAS_MODE_IBSS)
+ return;
+
+ /* For IBSS check VHT_IBSS flag */
+ if (!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_VHT_IBSS))
+ return;
+
+ vht_freq = *freq;
+
+ vht_freq.vht_enabled = vht_supported(mode);
+ if (!vht_freq.vht_enabled)
+ return;
+
+ /* setup center_freq1, bandwidth */
+ for (j = 0; j < ARRAY_SIZE(vht80); j++) {
+ if (freq->channel >= vht80[j] &&
+ freq->channel < vht80[j] + 16)
+ break;
+ }
+
+ if (j == ARRAY_SIZE(vht80))
+ return;
+
+ for (i = vht80[j]; i < vht80[j] + 16; i += 4) {
+ struct hostapd_channel_data *chan;
+
+ chan = hw_get_channel_chan(mode, i, NULL);
+ if (!chan)
+ return;
+
+ /* Back to HT configuration if channel not usable */
+ if (chan->flag & (HOSTAPD_CHAN_DISABLED | HOSTAPD_CHAN_NO_IR))
+ return;
+ }
+
+ if (hostapd_set_freq_params(&vht_freq, mode->mode, freq->freq,
+ freq->channel, freq->ht_enabled,
+ vht_freq.vht_enabled,
+ freq->sec_channel_offset,
+ VHT_CHANWIDTH_80MHZ,
+ vht80[j] + 6, 0, 0) != 0)
+ return;
+
+ *freq = vht_freq;
+
+ wpa_printf(MSG_DEBUG, "IBSS: VHT setup freq cf1 %d, cf2 %d, bw %d",
+ freq->center_freq1, freq->center_freq2, freq->bandwidth);
+}
+
+
+static void wpas_start_assoc_cb(struct wpa_radio_work *work, int deinit)
+{
+ struct wpa_connect_work *cwork = work->ctx;
+ struct wpa_bss *bss = cwork->bss;
+ struct wpa_ssid *ssid = cwork->ssid;
+ struct wpa_supplicant *wpa_s = work->wpa_s;
+ u8 wpa_ie[200];
+ size_t wpa_ie_len;
+ int use_crypt, ret, i, bssid_changed;
+ int algs = WPA_AUTH_ALG_OPEN;
+ unsigned int cipher_pairwise, cipher_group;
+ struct wpa_driver_associate_params params;
+ int wep_keys_set = 0;
+ int assoc_failed = 0;
+ struct wpa_ssid *old_ssid;
+#ifdef CONFIG_HT_OVERRIDES
+ struct ieee80211_ht_capabilities htcaps;
+ struct ieee80211_ht_capabilities htcaps_mask;
+#endif /* CONFIG_HT_OVERRIDES */
+#ifdef CONFIG_VHT_OVERRIDES
+ struct ieee80211_vht_capabilities vhtcaps;
+ struct ieee80211_vht_capabilities vhtcaps_mask;
+#endif /* CONFIG_VHT_OVERRIDES */
+
+ if (deinit) {
+ if (work->started) {
+ wpa_s->connect_work = NULL;
+
+ /* cancel possible auth. timeout */
+ eloop_cancel_timeout(wpa_supplicant_timeout, wpa_s,
+ NULL);
+ }
+ wpas_connect_work_free(cwork);
+ return;
+ }
+
+ wpa_s->connect_work = work;
+
+ if (cwork->bss_removed || !wpas_valid_bss_ssid(wpa_s, bss, ssid) ||
+ wpas_network_disabled(wpa_s, ssid)) {
+ wpa_dbg(wpa_s, MSG_DEBUG, "BSS/SSID entry for association not valid anymore - drop connection attempt");
+ wpas_connect_work_done(wpa_s);
+ return;
+ }
+
+ os_memset(&params, 0, sizeof(params));
+ wpa_s->reassociate = 0;
+ wpa_s->eap_expected_failure = 0;
+ if (bss &&
+ (!wpas_driver_bss_selection(wpa_s) || wpas_wps_searching(wpa_s))) {
+#ifdef CONFIG_IEEE80211R
+ const u8 *ie, *md = NULL;
+#endif /* CONFIG_IEEE80211R */
+ wpa_msg(wpa_s, MSG_INFO, "Trying to associate with " MACSTR
+ " (SSID='%s' freq=%d MHz)", MAC2STR(bss->bssid),
+ wpa_ssid_txt(bss->ssid, bss->ssid_len), bss->freq);
+ bssid_changed = !is_zero_ether_addr(wpa_s->bssid);
+ os_memset(wpa_s->bssid, 0, ETH_ALEN);
+ os_memcpy(wpa_s->pending_bssid, bss->bssid, ETH_ALEN);
+ if (bssid_changed)
+ wpas_notify_bssid_changed(wpa_s);
+#ifdef CONFIG_IEEE80211R
+ ie = wpa_bss_get_ie(bss, WLAN_EID_MOBILITY_DOMAIN);
+ if (ie && ie[1] >= MOBILITY_DOMAIN_ID_LEN)
+ md = ie + 2;
+ wpa_sm_set_ft_params(wpa_s->wpa, ie, ie ? 2 + ie[1] : 0);
+ if (md) {
+ /* Prepare for the next transition */
+ wpa_ft_prepare_auth_request(wpa_s->wpa, ie);
+ }
+#endif /* CONFIG_IEEE80211R */
+#ifdef CONFIG_WPS
+ } else if ((ssid->ssid == NULL || ssid->ssid_len == 0) &&
+ wpa_s->conf->ap_scan == 2 &&
+ (ssid->key_mgmt & WPA_KEY_MGMT_WPS)) {
+ /* Use ap_scan==1 style network selection to find the network
+ */
+ wpas_connect_work_done(wpa_s);
+ wpa_s->scan_req = MANUAL_SCAN_REQ;
+ wpa_s->reassociate = 1;
+ wpa_supplicant_req_scan(wpa_s, 0, 0);
+ return;
+#endif /* CONFIG_WPS */
+ } else {
+ wpa_msg(wpa_s, MSG_INFO, "Trying to associate with SSID '%s'",
+ wpa_ssid_txt(ssid->ssid, ssid->ssid_len));
+ os_memset(wpa_s->pending_bssid, 0, ETH_ALEN);
+ }
+ if (!wpa_s->pno)
+ wpa_supplicant_cancel_sched_scan(wpa_s);
+
+ wpa_supplicant_cancel_scan(wpa_s);
+
+ /* Starting new association, so clear the possibly used WPA IE from the
+ * previous association. */
+ wpa_sm_set_assoc_wpa_ie(wpa_s->wpa, NULL, 0);
+
+#ifdef IEEE8021X_EAPOL
+ if (ssid->key_mgmt & WPA_KEY_MGMT_IEEE8021X_NO_WPA) {
+ if (ssid->leap) {
+ if (ssid->non_leap == 0)
+ algs = WPA_AUTH_ALG_LEAP;
+ else
+ algs |= WPA_AUTH_ALG_LEAP;
+ }
+ }
+#endif /* IEEE8021X_EAPOL */
+ wpa_dbg(wpa_s, MSG_DEBUG, "Automatic auth_alg selection: 0x%x", algs);
+ if (ssid->auth_alg) {
+ algs = ssid->auth_alg;
+ wpa_dbg(wpa_s, MSG_DEBUG, "Overriding auth_alg selection: "
+ "0x%x", algs);
+ }
+
+ if (bss && (wpa_bss_get_vendor_ie(bss, WPA_IE_VENDOR_TYPE) ||
+ wpa_bss_get_ie(bss, WLAN_EID_RSN)) &&
+ wpa_key_mgmt_wpa(ssid->key_mgmt)) {
+ int try_opportunistic;
+ try_opportunistic = (ssid->proactive_key_caching < 0 ?
+ wpa_s->conf->okc :
+ ssid->proactive_key_caching) &&
+ (ssid->proto & WPA_PROTO_RSN);
+ if (pmksa_cache_set_current(wpa_s->wpa, NULL, bss->bssid,
+ ssid, try_opportunistic) == 0)
+ eapol_sm_notify_pmkid_attempt(wpa_s->eapol);
+ wpa_ie_len = sizeof(wpa_ie);
+ if (wpa_supplicant_set_suites(wpa_s, bss, ssid,
+ wpa_ie, &wpa_ie_len)) {
+ wpa_msg(wpa_s, MSG_WARNING, "WPA: Failed to set WPA "
+ "key management and encryption suites");
+ wpas_connect_work_done(wpa_s);
+ return;
+ }
+ } else if ((ssid->key_mgmt & WPA_KEY_MGMT_IEEE8021X_NO_WPA) && bss &&
+ wpa_key_mgmt_wpa_ieee8021x(ssid->key_mgmt)) {
+ /*
+ * Both WPA and non-WPA IEEE 802.1X enabled in configuration -
+ * use non-WPA since the scan results did not indicate that the
+ * AP is using WPA or WPA2.
+ */
+ wpa_supplicant_set_non_wpa_policy(wpa_s, ssid);
+ wpa_ie_len = 0;
+ wpa_s->wpa_proto = 0;
+ } else if (wpa_key_mgmt_wpa_any(ssid->key_mgmt)) {
+ wpa_ie_len = sizeof(wpa_ie);
+ if (wpa_supplicant_set_suites(wpa_s, NULL, ssid,
+ wpa_ie, &wpa_ie_len)) {
+ wpa_msg(wpa_s, MSG_WARNING, "WPA: Failed to set WPA "
+ "key management and encryption suites (no "
+ "scan results)");
+ wpas_connect_work_done(wpa_s);
+ return;
+ }
+#ifdef CONFIG_WPS
+ } else if (ssid->key_mgmt & WPA_KEY_MGMT_WPS) {
+ struct wpabuf *wps_ie;
+ wps_ie = wps_build_assoc_req_ie(wpas_wps_get_req_type(ssid));
+ if (wps_ie && wpabuf_len(wps_ie) <= sizeof(wpa_ie)) {
+ wpa_ie_len = wpabuf_len(wps_ie);
+ os_memcpy(wpa_ie, wpabuf_head(wps_ie), wpa_ie_len);
+ } else
+ wpa_ie_len = 0;
+ wpabuf_free(wps_ie);
+ wpa_supplicant_set_non_wpa_policy(wpa_s, ssid);
+ if (!bss || (bss->caps & IEEE80211_CAP_PRIVACY))
+ params.wps = WPS_MODE_PRIVACY;
+ else
+ params.wps = WPS_MODE_OPEN;
+ wpa_s->wpa_proto = 0;
+#endif /* CONFIG_WPS */
+ } else {
+ wpa_supplicant_set_non_wpa_policy(wpa_s, ssid);
+ wpa_ie_len = 0;
+ wpa_s->wpa_proto = 0;
+ }
+
+#ifdef CONFIG_P2P
+ if (wpa_s->global->p2p) {
+ u8 *pos;
+ size_t len;
+ int res;
+ pos = wpa_ie + wpa_ie_len;
+ len = sizeof(wpa_ie) - wpa_ie_len;
+ res = wpas_p2p_assoc_req_ie(wpa_s, bss, pos, len,
+ ssid->p2p_group);
+ if (res >= 0)
+ wpa_ie_len += res;
+ }
+
+ wpa_s->cross_connect_disallowed = 0;
+ if (bss) {
+ struct wpabuf *p2p;
+ p2p = wpa_bss_get_vendor_ie_multi(bss, P2P_IE_VENDOR_TYPE);
+ if (p2p) {
+ wpa_s->cross_connect_disallowed =
+ p2p_get_cross_connect_disallowed(p2p);
+ wpabuf_free(p2p);
+ wpa_dbg(wpa_s, MSG_DEBUG, "P2P: WLAN AP %s cross "
+ "connection",
+ wpa_s->cross_connect_disallowed ?
+ "disallows" : "allows");
+ }
+ }
+
+ os_memset(wpa_s->p2p_ip_addr_info, 0, sizeof(wpa_s->p2p_ip_addr_info));
+#endif /* CONFIG_P2P */
+
+#ifdef CONFIG_HS20
+ if (is_hs20_network(wpa_s, ssid, bss)) {
+ struct wpabuf *hs20;
+ hs20 = wpabuf_alloc(20);
+ if (hs20) {
+ int pps_mo_id = hs20_get_pps_mo_id(wpa_s, ssid);
+ size_t len;
+
+ wpas_hs20_add_indication(hs20, pps_mo_id);
+ len = sizeof(wpa_ie) - wpa_ie_len;
+ if (wpabuf_len(hs20) <= len) {
+ os_memcpy(wpa_ie + wpa_ie_len,
+ wpabuf_head(hs20), wpabuf_len(hs20));
+ wpa_ie_len += wpabuf_len(hs20);
+ }
+ wpabuf_free(hs20);
+ }
+ }
+#endif /* CONFIG_HS20 */
+
+ /*
+ * Workaround: Add Extended Capabilities element only if the AP
+ * included this element in Beacon/Probe Response frames. Some older
+ * APs seem to have interoperability issues if this element is
+ * included, so while the standard may require us to include the
+ * element in all cases, it is justifiable to skip it to avoid
+ * interoperability issues.
+ */
+ if (!bss || wpa_bss_get_ie(bss, WLAN_EID_EXT_CAPAB)) {
+ u8 ext_capab[18];
+ int ext_capab_len;
+ ext_capab_len = wpas_build_ext_capab(wpa_s, ext_capab,
+ sizeof(ext_capab));
+ if (ext_capab_len > 0) {
+ u8 *pos = wpa_ie;
+ if (wpa_ie_len > 0 && pos[0] == WLAN_EID_RSN)
+ pos += 2 + pos[1];
+ os_memmove(pos + ext_capab_len, pos,
+ wpa_ie_len - (pos - wpa_ie));
+ wpa_ie_len += ext_capab_len;
+ os_memcpy(pos, ext_capab, ext_capab_len);
+ }
+ }
+
+ if (wpa_s->vendor_elem[VENDOR_ELEM_ASSOC_REQ]) {
+ struct wpabuf *buf = wpa_s->vendor_elem[VENDOR_ELEM_ASSOC_REQ];
+ size_t len;
+
+ len = sizeof(wpa_ie) - wpa_ie_len;
+ if (wpabuf_len(buf) <= len) {
+ os_memcpy(wpa_ie + wpa_ie_len,
+ wpabuf_head(buf), wpabuf_len(buf));
+ wpa_ie_len += wpabuf_len(buf);
+ }
+ }
+
+#ifdef CONFIG_FST
+ if (wpa_s->fst_ies) {
+ int fst_ies_len = wpabuf_len(wpa_s->fst_ies);
+
+ if (wpa_ie_len + fst_ies_len <= sizeof(wpa_ie)) {
+ os_memcpy(wpa_ie + wpa_ie_len,
+ wpabuf_head(wpa_s->fst_ies), fst_ies_len);
+ wpa_ie_len += fst_ies_len;
+ }
+ }
+#endif /* CONFIG_FST */
+
+ wpa_clear_keys(wpa_s, bss ? bss->bssid : NULL);
+ use_crypt = 1;
+ cipher_pairwise = wpa_s->pairwise_cipher;
+ cipher_group = wpa_s->group_cipher;
+ if (wpa_s->key_mgmt == WPA_KEY_MGMT_NONE ||
+ wpa_s->key_mgmt == WPA_KEY_MGMT_IEEE8021X_NO_WPA) {
+ if (wpa_s->key_mgmt == WPA_KEY_MGMT_NONE)
+ use_crypt = 0;
+ if (wpa_set_wep_keys(wpa_s, ssid)) {
+ use_crypt = 1;
+ wep_keys_set = 1;
+ }
+ }
+ if (wpa_s->key_mgmt == WPA_KEY_MGMT_WPS)
+ use_crypt = 0;
+
+#ifdef IEEE8021X_EAPOL
+ if (wpa_s->key_mgmt == WPA_KEY_MGMT_IEEE8021X_NO_WPA) {
+ if ((ssid->eapol_flags &
+ (EAPOL_FLAG_REQUIRE_KEY_UNICAST |
+ EAPOL_FLAG_REQUIRE_KEY_BROADCAST)) == 0 &&
+ !wep_keys_set) {
+ use_crypt = 0;
+ } else {
+ /* Assume that dynamic WEP-104 keys will be used and
+ * set cipher suites in order for drivers to expect
+ * encryption. */
+ cipher_pairwise = cipher_group = WPA_CIPHER_WEP104;
+ }
+ }
+#endif /* IEEE8021X_EAPOL */
+
+ if (wpa_s->key_mgmt == WPA_KEY_MGMT_WPA_NONE) {
+ /* Set the key before (and later after) association */
+ wpa_supplicant_set_wpa_none_key(wpa_s, ssid);
+ }
+
+ wpa_supplicant_set_state(wpa_s, WPA_ASSOCIATING);
+ if (bss) {
+ params.ssid = bss->ssid;
+ params.ssid_len = bss->ssid_len;
+ if (!wpas_driver_bss_selection(wpa_s) || ssid->bssid_set) {
+ wpa_printf(MSG_DEBUG, "Limit connection to BSSID "
+ MACSTR " freq=%u MHz based on scan results "
+ "(bssid_set=%d)",
+ MAC2STR(bss->bssid), bss->freq,
+ ssid->bssid_set);
+ params.bssid = bss->bssid;
+ params.freq.freq = bss->freq;
+ }
+ params.bssid_hint = bss->bssid;
+ params.freq_hint = bss->freq;
+ } else {
+ params.ssid = ssid->ssid;
+ params.ssid_len = ssid->ssid_len;
+ }
+
+ if (ssid->mode == WPAS_MODE_IBSS && ssid->bssid_set &&
+ wpa_s->conf->ap_scan == 2) {
+ params.bssid = ssid->bssid;
+ params.fixed_bssid = 1;
+ }
+
+ /* Initial frequency for IBSS/mesh */
+ if ((ssid->mode == WPAS_MODE_IBSS || ssid->mode == WPAS_MODE_MESH) &&
+ ssid->frequency > 0 && params.freq.freq == 0)
+ ibss_mesh_setup_freq(wpa_s, ssid, &params.freq);
+
+ if (ssid->mode == WPAS_MODE_IBSS) {
+ params.fixed_freq = ssid->fixed_freq;
+ if (ssid->beacon_int)
+ params.beacon_int = ssid->beacon_int;
+ else
+ params.beacon_int = wpa_s->conf->beacon_int;
+ }
+
+ params.wpa_ie = wpa_ie;
+ params.wpa_ie_len = wpa_ie_len;
+ params.pairwise_suite = cipher_pairwise;
+ params.group_suite = cipher_group;
+ params.key_mgmt_suite = wpa_s->key_mgmt;
+ params.wpa_proto = wpa_s->wpa_proto;
+ params.auth_alg = algs;
+ params.mode = ssid->mode;
+ params.bg_scan_period = ssid->bg_scan_period;
+ for (i = 0; i < NUM_WEP_KEYS; i++) {
+ if (ssid->wep_key_len[i])
+ params.wep_key[i] = ssid->wep_key[i];
+ params.wep_key_len[i] = ssid->wep_key_len[i];
+ }
+ params.wep_tx_keyidx = ssid->wep_tx_keyidx;
+
+ if ((wpa_s->drv_flags & WPA_DRIVER_FLAGS_4WAY_HANDSHAKE) &&
+ (params.key_mgmt_suite == WPA_KEY_MGMT_PSK ||
+ params.key_mgmt_suite == WPA_KEY_MGMT_FT_PSK)) {
+ params.passphrase = ssid->passphrase;
+ if (ssid->psk_set)
+ params.psk = ssid->psk;
+ }
+
+ if (wpa_s->conf->key_mgmt_offload) {
+ if (params.key_mgmt_suite == WPA_KEY_MGMT_IEEE8021X ||
+ params.key_mgmt_suite == WPA_KEY_MGMT_IEEE8021X_SHA256 ||
+ params.key_mgmt_suite == WPA_KEY_MGMT_IEEE8021X_SUITE_B ||
+ params.key_mgmt_suite == WPA_KEY_MGMT_IEEE8021X_SUITE_B_192)
+ params.req_key_mgmt_offload =
+ ssid->proactive_key_caching < 0 ?
+ wpa_s->conf->okc : ssid->proactive_key_caching;
+ else
+ params.req_key_mgmt_offload = 1;
+
+ if ((params.key_mgmt_suite == WPA_KEY_MGMT_PSK ||
+ params.key_mgmt_suite == WPA_KEY_MGMT_PSK_SHA256 ||
+ params.key_mgmt_suite == WPA_KEY_MGMT_FT_PSK) &&
+ ssid->psk_set)
+ params.psk = ssid->psk;
+ }
+
+ params.drop_unencrypted = use_crypt;
+
+#ifdef CONFIG_IEEE80211W
+ params.mgmt_frame_protection = wpas_get_ssid_pmf(wpa_s, ssid);
+ if (params.mgmt_frame_protection != NO_MGMT_FRAME_PROTECTION && bss) {
+ const u8 *rsn = wpa_bss_get_ie(bss, WLAN_EID_RSN);
+ struct wpa_ie_data ie;
+ if (rsn && wpa_parse_wpa_ie(rsn, 2 + rsn[1], &ie) == 0 &&
+ ie.capabilities &
+ (WPA_CAPABILITY_MFPC | WPA_CAPABILITY_MFPR)) {
+ wpa_dbg(wpa_s, MSG_DEBUG, "WPA: Selected AP supports "
+ "MFP: require MFP");
+ params.mgmt_frame_protection =
+ MGMT_FRAME_PROTECTION_REQUIRED;
+ }
+ }
+#endif /* CONFIG_IEEE80211W */
+
+ params.p2p = ssid->p2p_group;
+
+ if (wpa_s->parent->set_sta_uapsd)
+ params.uapsd = wpa_s->parent->sta_uapsd;
+ else
+ params.uapsd = -1;
+
+#ifdef CONFIG_HT_OVERRIDES
+ os_memset(&htcaps, 0, sizeof(htcaps));
+ os_memset(&htcaps_mask, 0, sizeof(htcaps_mask));
+ params.htcaps = (u8 *) &htcaps;
+ params.htcaps_mask = (u8 *) &htcaps_mask;
+ wpa_supplicant_apply_ht_overrides(wpa_s, ssid, &params);
+#endif /* CONFIG_HT_OVERRIDES */
+#ifdef CONFIG_VHT_OVERRIDES
+ os_memset(&vhtcaps, 0, sizeof(vhtcaps));
+ os_memset(&vhtcaps_mask, 0, sizeof(vhtcaps_mask));
+ params.vhtcaps = &vhtcaps;
+ params.vhtcaps_mask = &vhtcaps_mask;
+ wpa_supplicant_apply_vht_overrides(wpa_s, ssid, &params);
+#endif /* CONFIG_VHT_OVERRIDES */
+
+#ifdef CONFIG_P2P
+ /*
+ * If multi-channel concurrency is not supported, check for any
+ * frequency conflict. In case of any frequency conflict, remove the
+ * least prioritized connection.
+ */
+ if (wpa_s->num_multichan_concurrent < 2) {
+ int freq, num;
+ num = get_shared_radio_freqs(wpa_s, &freq, 1);
+ if (num > 0 && freq > 0 && freq != params.freq.freq) {
+ wpa_printf(MSG_DEBUG,
+ "Assoc conflicting freq found (%d != %d)",
+ freq, params.freq.freq);
+ if (wpas_p2p_handle_frequency_conflicts(
+ wpa_s, params.freq.freq, ssid) < 0) {
+ wpas_connect_work_done(wpa_s);
+ return;
+ }
+ }
+ }
+#endif /* CONFIG_P2P */
+
+ ret = wpa_drv_associate(wpa_s, &params);
+ if (ret < 0) {
+ wpa_msg(wpa_s, MSG_INFO, "Association request to the driver "
+ "failed");
+ if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_SANE_ERROR_CODES) {
+ /*
+ * The driver is known to mean what is saying, so we
+ * can stop right here; the association will not
+ * succeed.
+ */
+ wpas_connection_failed(wpa_s, wpa_s->pending_bssid);
+ wpa_supplicant_set_state(wpa_s, WPA_DISCONNECTED);
+ os_memset(wpa_s->pending_bssid, 0, ETH_ALEN);
+ return;
+ }
+ /* try to continue anyway; new association will be tried again
+ * after timeout */
+ assoc_failed = 1;
+ }
+
+ if (wpa_s->key_mgmt == WPA_KEY_MGMT_WPA_NONE) {
+ /* Set the key after the association just in case association
+ * cleared the previously configured key. */
+ wpa_supplicant_set_wpa_none_key(wpa_s, ssid);
+ /* No need to timeout authentication since there is no key
+ * management. */
+ wpa_supplicant_cancel_auth_timeout(wpa_s);
+ wpa_supplicant_set_state(wpa_s, WPA_COMPLETED);
+#ifdef CONFIG_IBSS_RSN
+ } else if (ssid->mode == WPAS_MODE_IBSS &&
+ wpa_s->key_mgmt != WPA_KEY_MGMT_NONE &&
+ wpa_s->key_mgmt != WPA_KEY_MGMT_WPA_NONE) {
+ /*
+ * RSN IBSS authentication is per-STA and we can disable the
+ * per-BSSID authentication.
+ */
+ wpa_supplicant_cancel_auth_timeout(wpa_s);
+#endif /* CONFIG_IBSS_RSN */
+ } else {
+ /* Timeout for IEEE 802.11 authentication and association */
+ int timeout = 60;
+
+ if (assoc_failed) {
+ /* give IBSS a bit more time */
+ timeout = ssid->mode == WPAS_MODE_IBSS ? 10 : 5;
+ } else if (wpa_s->conf->ap_scan == 1) {
+ /* give IBSS a bit more time */
+ timeout = ssid->mode == WPAS_MODE_IBSS ? 20 : 10;
+ }
+ wpa_supplicant_req_auth_timeout(wpa_s, timeout, 0);
+ }
+
+ if (wep_keys_set &&
+ (wpa_s->drv_flags & WPA_DRIVER_FLAGS_SET_KEYS_AFTER_ASSOC)) {
+ /* Set static WEP keys again */
+ wpa_set_wep_keys(wpa_s, ssid);
+ }
+
+ if (wpa_s->current_ssid && wpa_s->current_ssid != ssid) {
+ /*
+ * Do not allow EAP session resumption between different
+ * network configurations.
+ */
+ eapol_sm_invalidate_cached_session(wpa_s->eapol);
+ }
+ old_ssid = wpa_s->current_ssid;
+ wpa_s->current_ssid = ssid;
+ if (!wpas_driver_bss_selection(wpa_s) || ssid->bssid_set)
+ wpa_s->current_bss = bss;
+ wpa_supplicant_rsn_supp_set_config(wpa_s, wpa_s->current_ssid);
+ wpa_supplicant_initiate_eapol(wpa_s);
+ if (old_ssid != wpa_s->current_ssid)
+ wpas_notify_network_changed(wpa_s);
+}
+
+
+static void wpa_supplicant_clear_connection(struct wpa_supplicant *wpa_s,
+ const u8 *addr)
+{
+ struct wpa_ssid *old_ssid;
+
+ wpas_connect_work_done(wpa_s);
+ wpa_clear_keys(wpa_s, addr);
+ old_ssid = wpa_s->current_ssid;
+ wpa_supplicant_mark_disassoc(wpa_s);
+ wpa_sm_set_config(wpa_s->wpa, NULL);
+ eapol_sm_notify_config(wpa_s->eapol, NULL, NULL);
+ if (old_ssid != wpa_s->current_ssid)
+ wpas_notify_network_changed(wpa_s);
+ eloop_cancel_timeout(wpa_supplicant_timeout, wpa_s, NULL);
+}
+
+
+/**
+ * wpa_supplicant_deauthenticate - Deauthenticate the current connection
+ * @wpa_s: Pointer to wpa_supplicant data
+ * @reason_code: IEEE 802.11 reason code for the deauthenticate frame
+ *
+ * This function is used to request %wpa_supplicant to deauthenticate from the
+ * current AP.
+ */
+void wpa_supplicant_deauthenticate(struct wpa_supplicant *wpa_s,
+ int reason_code)
+{
+ u8 *addr = NULL;
+ union wpa_event_data event;
+ int zero_addr = 0;
+
+ wpa_dbg(wpa_s, MSG_DEBUG, "Request to deauthenticate - bssid=" MACSTR
+ " pending_bssid=" MACSTR " reason=%d state=%s",
+ MAC2STR(wpa_s->bssid), MAC2STR(wpa_s->pending_bssid),
+ reason_code, wpa_supplicant_state_txt(wpa_s->wpa_state));
+
+ if (!is_zero_ether_addr(wpa_s->bssid))
+ addr = wpa_s->bssid;
+ else if (!is_zero_ether_addr(wpa_s->pending_bssid) &&
+ (wpa_s->wpa_state == WPA_AUTHENTICATING ||
+ wpa_s->wpa_state == WPA_ASSOCIATING))
+ addr = wpa_s->pending_bssid;
+ else if (wpa_s->wpa_state == WPA_ASSOCIATING) {
+ /*
+ * When using driver-based BSS selection, we may not know the
+ * BSSID with which we are currently trying to associate. We
+ * need to notify the driver of this disconnection even in such
+ * a case, so use the all zeros address here.
+ */
+ addr = wpa_s->bssid;
+ zero_addr = 1;
+ }
+
+#ifdef CONFIG_TDLS
+ wpa_tdls_teardown_peers(wpa_s->wpa);
+#endif /* CONFIG_TDLS */
+
+#ifdef CONFIG_MESH
+ if (wpa_s->ifmsh) {
+ wpa_msg_ctrl(wpa_s, MSG_INFO, MESH_GROUP_REMOVED "%s",
+ wpa_s->ifname);
+ wpa_supplicant_leave_mesh(wpa_s);
+ }
+#endif /* CONFIG_MESH */
+
+ if (addr) {
+ wpa_drv_deauthenticate(wpa_s, addr, reason_code);
+ os_memset(&event, 0, sizeof(event));
+ event.deauth_info.reason_code = (u16) reason_code;
+ event.deauth_info.locally_generated = 1;
+ wpa_supplicant_event(wpa_s, EVENT_DEAUTH, &event);
+ if (zero_addr)
+ addr = NULL;
+ }
+
+ wpa_supplicant_clear_connection(wpa_s, addr);
+}
+
+static void wpa_supplicant_enable_one_network(struct wpa_supplicant *wpa_s,
+ struct wpa_ssid *ssid)
+{
+ if (!ssid || !ssid->disabled || ssid->disabled == 2)
+ return;
+
+ ssid->disabled = 0;
+ wpas_clear_temp_disabled(wpa_s, ssid, 1);
+ wpas_notify_network_enabled_changed(wpa_s, ssid);
+
+ /*
+ * Try to reassociate since there is no current configuration and a new
+ * network was made available.
+ */
+ if (!wpa_s->current_ssid && !wpa_s->disconnected)
+ wpa_s->reassociate = 1;
+}
+
+
+/**
+ * wpa_supplicant_enable_network - Mark a configured network as enabled
+ * @wpa_s: wpa_supplicant structure for a network interface
+ * @ssid: wpa_ssid structure for a configured network or %NULL
+ *
+ * Enables the specified network or all networks if no network specified.
+ */
+void wpa_supplicant_enable_network(struct wpa_supplicant *wpa_s,
+ struct wpa_ssid *ssid)
+{
+ if (ssid == NULL) {
+ for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next)
+ wpa_supplicant_enable_one_network(wpa_s, ssid);
+ } else
+ wpa_supplicant_enable_one_network(wpa_s, ssid);
+
+ if (wpa_s->reassociate && !wpa_s->disconnected &&
+ (!wpa_s->current_ssid ||
+ wpa_s->wpa_state == WPA_DISCONNECTED ||
+ wpa_s->wpa_state == WPA_SCANNING)) {
+ if (wpa_s->sched_scanning) {
+ wpa_printf(MSG_DEBUG, "Stop ongoing sched_scan to add "
+ "new network to scan filters");
+ wpa_supplicant_cancel_sched_scan(wpa_s);
+ }
+
+ if (wpa_supplicant_fast_associate(wpa_s) != 1) {
+ wpa_s->scan_req = NORMAL_SCAN_REQ;
+ wpa_supplicant_req_scan(wpa_s, 0, 0);
+ }
+ }
+}
+
+
+/**
+ * wpa_supplicant_disable_network - Mark a configured network as disabled
+ * @wpa_s: wpa_supplicant structure for a network interface
+ * @ssid: wpa_ssid structure for a configured network or %NULL
+ *
+ * Disables the specified network or all networks if no network specified.
+ */
+void wpa_supplicant_disable_network(struct wpa_supplicant *wpa_s,
+ struct wpa_ssid *ssid)
+{
+ struct wpa_ssid *other_ssid;
+ int was_disabled;
+
+ if (ssid == NULL) {
+ if (wpa_s->sched_scanning)
+ wpa_supplicant_cancel_sched_scan(wpa_s);
+
+ for (other_ssid = wpa_s->conf->ssid; other_ssid;
+ other_ssid = other_ssid->next) {
+ was_disabled = other_ssid->disabled;
+ if (was_disabled == 2)
+ continue; /* do not change persistent P2P group
+ * data */
+
+ other_ssid->disabled = 1;
+
+ if (was_disabled != other_ssid->disabled)
+ wpas_notify_network_enabled_changed(
+ wpa_s, other_ssid);
+ }
+ if (wpa_s->current_ssid)
+ wpa_supplicant_deauthenticate(
+ wpa_s, WLAN_REASON_DEAUTH_LEAVING);
+ } else if (ssid->disabled != 2) {
+ if (ssid == wpa_s->current_ssid)
+ wpa_supplicant_deauthenticate(
+ wpa_s, WLAN_REASON_DEAUTH_LEAVING);
+
+ was_disabled = ssid->disabled;
+
+ ssid->disabled = 1;
+
+ if (was_disabled != ssid->disabled) {
+ wpas_notify_network_enabled_changed(wpa_s, ssid);
+ if (wpa_s->sched_scanning) {
+ wpa_printf(MSG_DEBUG, "Stop ongoing sched_scan "
+ "to remove network from filters");
+ wpa_supplicant_cancel_sched_scan(wpa_s);
+ wpa_supplicant_req_scan(wpa_s, 0, 0);
+ }
+ }
+ }
+}
+
+
+/**
+ * wpa_supplicant_select_network - Attempt association with a network
+ * @wpa_s: wpa_supplicant structure for a network interface
+ * @ssid: wpa_ssid structure for a configured network or %NULL for any network
+ */
+void wpa_supplicant_select_network(struct wpa_supplicant *wpa_s,
+ struct wpa_ssid *ssid)
+{
+
+ struct wpa_ssid *other_ssid;
+ int disconnected = 0;
+
+ if (ssid && ssid != wpa_s->current_ssid && wpa_s->current_ssid) {
+ if (wpa_s->wpa_state >= WPA_AUTHENTICATING)
+ wpa_s->own_disconnect_req = 1;
+ wpa_supplicant_deauthenticate(
+ wpa_s, WLAN_REASON_DEAUTH_LEAVING);
+ disconnected = 1;
+ }
+
+ if (ssid)
+ wpas_clear_temp_disabled(wpa_s, ssid, 1);
+
+ /*
+ * Mark all other networks disabled or mark all networks enabled if no
+ * network specified.
+ */
+ for (other_ssid = wpa_s->conf->ssid; other_ssid;
+ other_ssid = other_ssid->next) {
+ int was_disabled = other_ssid->disabled;
+ if (was_disabled == 2)
+ continue; /* do not change persistent P2P group data */
+
+ other_ssid->disabled = ssid ? (ssid->id != other_ssid->id) : 0;
+ if (was_disabled && !other_ssid->disabled)
+ wpas_clear_temp_disabled(wpa_s, other_ssid, 0);
+
+ if (was_disabled != other_ssid->disabled)
+ wpas_notify_network_enabled_changed(wpa_s, other_ssid);
+ }
+
+ if (ssid && ssid == wpa_s->current_ssid && wpa_s->current_ssid) {
+ /* We are already associated with the selected network */
+ wpa_printf(MSG_DEBUG, "Already associated with the "
+ "selected network - do nothing");
+ return;
+ }
+
+ if (ssid) {
+ wpa_s->current_ssid = ssid;
+ eapol_sm_notify_config(wpa_s->eapol, NULL, NULL);
+ wpa_s->connect_without_scan =
+ (ssid->mode == WPAS_MODE_MESH) ? ssid : NULL;
+
+ /*
+ * Don't optimize next scan freqs since a new ESS has been
+ * selected.
+ */
+ os_free(wpa_s->next_scan_freqs);
+ wpa_s->next_scan_freqs = NULL;
+ } else {
+ wpa_s->connect_without_scan = NULL;
+ }
+
+ wpa_s->disconnected = 0;
+ wpa_s->reassociate = 1;
+
+ if (wpa_s->connect_without_scan ||
+ wpa_supplicant_fast_associate(wpa_s) != 1) {
+ wpa_s->scan_req = NORMAL_SCAN_REQ;
+ wpa_supplicant_req_scan(wpa_s, 0, disconnected ? 100000 : 0);
+ }
+
+ if (ssid)
+ wpas_notify_network_selected(wpa_s, ssid);
+}
+
+
+/**
+ * wpas_set_pkcs11_engine_and_module_path - Set PKCS #11 engine and module path
+ * @wpa_s: wpa_supplicant structure for a network interface
+ * @pkcs11_engine_path: PKCS #11 engine path or NULL
+ * @pkcs11_module_path: PKCS #11 module path or NULL
+ * Returns: 0 on success; -1 on failure
+ *
+ * Sets the PKCS #11 engine and module path. Both have to be NULL or a valid
+ * path. If resetting the EAPOL state machine with the new PKCS #11 engine and
+ * module path fails the paths will be reset to the default value (NULL).
+ */
+int wpas_set_pkcs11_engine_and_module_path(struct wpa_supplicant *wpa_s,
+ const char *pkcs11_engine_path,
+ const char *pkcs11_module_path)
+{
+ char *pkcs11_engine_path_copy = NULL;
+ char *pkcs11_module_path_copy = NULL;
+
+ if (pkcs11_engine_path != NULL) {
+ pkcs11_engine_path_copy = os_strdup(pkcs11_engine_path);
+ if (pkcs11_engine_path_copy == NULL)
+ return -1;
+ }
+ if (pkcs11_module_path != NULL) {
+ pkcs11_module_path_copy = os_strdup(pkcs11_module_path);
+ if (pkcs11_module_path_copy == NULL) {
+ os_free(pkcs11_engine_path_copy);
+ return -1;
+ }
+ }
+
+ os_free(wpa_s->conf->pkcs11_engine_path);
+ os_free(wpa_s->conf->pkcs11_module_path);
+ wpa_s->conf->pkcs11_engine_path = pkcs11_engine_path_copy;
+ wpa_s->conf->pkcs11_module_path = pkcs11_module_path_copy;
+
+ wpa_sm_set_eapol(wpa_s->wpa, NULL);
+ eapol_sm_deinit(wpa_s->eapol);
+ wpa_s->eapol = NULL;
+ if (wpa_supplicant_init_eapol(wpa_s)) {
+ /* Error -> Reset paths to the default value (NULL) once. */
+ if (pkcs11_engine_path != NULL && pkcs11_module_path != NULL)
+ wpas_set_pkcs11_engine_and_module_path(wpa_s, NULL,
+ NULL);
+
+ return -1;
+ }
+ wpa_sm_set_eapol(wpa_s->wpa, wpa_s->eapol);
+
+ return 0;
+}
+
+
+/**
+ * wpa_supplicant_set_ap_scan - Set AP scan mode for interface
+ * @wpa_s: wpa_supplicant structure for a network interface
+ * @ap_scan: AP scan mode
+ * Returns: 0 if succeed or -1 if ap_scan has an invalid value
+ *
+ */
+int wpa_supplicant_set_ap_scan(struct wpa_supplicant *wpa_s, int ap_scan)
+{
+
+ int old_ap_scan;
+
+ if (ap_scan < 0 || ap_scan > 2)
+ return -1;
+
+ if (ap_scan == 2 && os_strcmp(wpa_s->driver->name, "nl80211") == 0) {
+ wpa_printf(MSG_INFO,
+ "Note: nl80211 driver interface is not designed to be used with ap_scan=2; this can result in connection failures");
+ }
+
+#ifdef ANDROID
+ if (ap_scan == 2 && ap_scan != wpa_s->conf->ap_scan &&
+ wpa_s->wpa_state >= WPA_ASSOCIATING &&
+ wpa_s->wpa_state < WPA_COMPLETED) {
+ wpa_printf(MSG_ERROR, "ap_scan = %d (%d) rejected while "
+ "associating", wpa_s->conf->ap_scan, ap_scan);
+ return 0;
+ }
+#endif /* ANDROID */
+
+ old_ap_scan = wpa_s->conf->ap_scan;
+ wpa_s->conf->ap_scan = ap_scan;
+
+ if (old_ap_scan != wpa_s->conf->ap_scan)
+ wpas_notify_ap_scan_changed(wpa_s);
+
+ return 0;
+}
+
+
+/**
+ * wpa_supplicant_set_bss_expiration_age - Set BSS entry expiration age
+ * @wpa_s: wpa_supplicant structure for a network interface
+ * @expire_age: Expiration age in seconds
+ * Returns: 0 if succeed or -1 if expire_age has an invalid value
+ *
+ */
+int wpa_supplicant_set_bss_expiration_age(struct wpa_supplicant *wpa_s,
+ unsigned int bss_expire_age)
+{
+ if (bss_expire_age < 10) {
+ wpa_msg(wpa_s, MSG_ERROR, "Invalid bss expiration age %u",
+ bss_expire_age);
+ return -1;
+ }
+ wpa_msg(wpa_s, MSG_DEBUG, "Setting bss expiration age: %d sec",
+ bss_expire_age);
+ wpa_s->conf->bss_expiration_age = bss_expire_age;
+
+ return 0;
+}
+
+
+/**
+ * wpa_supplicant_set_bss_expiration_count - Set BSS entry expiration scan count
+ * @wpa_s: wpa_supplicant structure for a network interface
+ * @expire_count: number of scans after which an unseen BSS is reclaimed
+ * Returns: 0 if succeed or -1 if expire_count has an invalid value
+ *
+ */
+int wpa_supplicant_set_bss_expiration_count(struct wpa_supplicant *wpa_s,
+ unsigned int bss_expire_count)
+{
+ if (bss_expire_count < 1) {
+ wpa_msg(wpa_s, MSG_ERROR, "Invalid bss expiration count %u",
+ bss_expire_count);
+ return -1;
+ }
+ wpa_msg(wpa_s, MSG_DEBUG, "Setting bss expiration scan count: %u",
+ bss_expire_count);
+ wpa_s->conf->bss_expiration_scan_count = bss_expire_count;
+
+ return 0;
+}
+
+
+/**
+ * wpa_supplicant_set_scan_interval - Set scan interval
+ * @wpa_s: wpa_supplicant structure for a network interface
+ * @scan_interval: scan interval in seconds
+ * Returns: 0 if succeed or -1 if scan_interval has an invalid value
+ *
+ */
+int wpa_supplicant_set_scan_interval(struct wpa_supplicant *wpa_s,
+ int scan_interval)
+{
+ if (scan_interval < 0) {
+ wpa_msg(wpa_s, MSG_ERROR, "Invalid scan interval %d",
+ scan_interval);
+ return -1;
+ }
+ wpa_msg(wpa_s, MSG_DEBUG, "Setting scan interval: %d sec",
+ scan_interval);
+ wpa_supplicant_update_scan_int(wpa_s, scan_interval);
+
+ return 0;
+}
+
+
+/**
+ * wpa_supplicant_set_debug_params - Set global debug params
+ * @global: wpa_global structure
+ * @debug_level: debug level
+ * @debug_timestamp: determines if show timestamp in debug data
+ * @debug_show_keys: determines if show keys in debug data
+ * Returns: 0 if succeed or -1 if debug_level has wrong value
+ */
+int wpa_supplicant_set_debug_params(struct wpa_global *global, int debug_level,
+ int debug_timestamp, int debug_show_keys)
+{
+
+ int old_level, old_timestamp, old_show_keys;
+
+ /* check for allowed debuglevels */
+ if (debug_level != MSG_EXCESSIVE &&
+ debug_level != MSG_MSGDUMP &&
+ debug_level != MSG_DEBUG &&
+ debug_level != MSG_INFO &&
+ debug_level != MSG_WARNING &&
+ debug_level != MSG_ERROR)
+ return -1;
+
+ old_level = wpa_debug_level;
+ old_timestamp = wpa_debug_timestamp;
+ old_show_keys = wpa_debug_show_keys;
+
+ wpa_debug_level = debug_level;
+ wpa_debug_timestamp = debug_timestamp ? 1 : 0;
+ wpa_debug_show_keys = debug_show_keys ? 1 : 0;
+
+ if (wpa_debug_level != old_level)
+ wpas_notify_debug_level_changed(global);
+ if (wpa_debug_timestamp != old_timestamp)
+ wpas_notify_debug_timestamp_changed(global);
+ if (wpa_debug_show_keys != old_show_keys)
+ wpas_notify_debug_show_keys_changed(global);
+
+ return 0;
+}
+
+
+/**
+ * wpa_supplicant_get_ssid - Get a pointer to the current network structure
+ * @wpa_s: Pointer to wpa_supplicant data
+ * Returns: A pointer to the current network structure or %NULL on failure
+ */
+struct wpa_ssid * wpa_supplicant_get_ssid(struct wpa_supplicant *wpa_s)
+{
+ struct wpa_ssid *entry;
+ u8 ssid[SSID_MAX_LEN];
+ int res;
+ size_t ssid_len;
+ u8 bssid[ETH_ALEN];
+ int wired;
+
+ res = wpa_drv_get_ssid(wpa_s, ssid);
+ if (res < 0) {
+ wpa_msg(wpa_s, MSG_WARNING, "Could not read SSID from "
+ "driver");
+ return NULL;
+ }
+ ssid_len = res;
+
+ if (wpa_drv_get_bssid(wpa_s, bssid) < 0) {
+ wpa_msg(wpa_s, MSG_WARNING, "Could not read BSSID from "
+ "driver");
+ return NULL;
+ }
+
+ wired = wpa_s->conf->ap_scan == 0 &&
+ (wpa_s->drv_flags & WPA_DRIVER_FLAGS_WIRED);
+
+ entry = wpa_s->conf->ssid;
+ while (entry) {
+ if (!wpas_network_disabled(wpa_s, entry) &&
+ ((ssid_len == entry->ssid_len &&
+ os_memcmp(ssid, entry->ssid, ssid_len) == 0) || wired) &&
+ (!entry->bssid_set ||
+ os_memcmp(bssid, entry->bssid, ETH_ALEN) == 0))
+ return entry;
+#ifdef CONFIG_WPS
+ if (!wpas_network_disabled(wpa_s, entry) &&
+ (entry->key_mgmt & WPA_KEY_MGMT_WPS) &&
+ (entry->ssid == NULL || entry->ssid_len == 0) &&
+ (!entry->bssid_set ||
+ os_memcmp(bssid, entry->bssid, ETH_ALEN) == 0))
+ return entry;
+#endif /* CONFIG_WPS */
+
+ if (!wpas_network_disabled(wpa_s, entry) && entry->bssid_set &&
+ entry->ssid_len == 0 &&
+ os_memcmp(bssid, entry->bssid, ETH_ALEN) == 0)
+ return entry;
+
+ entry = entry->next;
+ }
+
+ return NULL;
+}
+
+
+static int select_driver(struct wpa_supplicant *wpa_s, int i)
+{
+ struct wpa_global *global = wpa_s->global;
+
+ if (wpa_drivers[i]->global_init && global->drv_priv[i] == NULL) {
+ global->drv_priv[i] = wpa_drivers[i]->global_init();
+ if (global->drv_priv[i] == NULL) {
+ wpa_printf(MSG_ERROR, "Failed to initialize driver "
+ "'%s'", wpa_drivers[i]->name);
+ return -1;
+ }
+ }
+
+ wpa_s->driver = wpa_drivers[i];
+ wpa_s->global_drv_priv = global->drv_priv[i];
+
+ return 0;
+}
+
+
+static int wpa_supplicant_set_driver(struct wpa_supplicant *wpa_s,
+ const char *name)
+{
+ int i;
+ size_t len;
+ const char *pos, *driver = name;
+
+ if (wpa_s == NULL)
+ return -1;
+
+ if (wpa_drivers[0] == NULL) {
+ wpa_msg(wpa_s, MSG_ERROR, "No driver interfaces build into "
+ "wpa_supplicant");
+ return -1;
+ }
+
+ if (name == NULL) {
+ /* default to first driver in the list */
+ return select_driver(wpa_s, 0);
+ }
+
+ do {
+ pos = os_strchr(driver, ',');
+ if (pos)
+ len = pos - driver;
+ else
+ len = os_strlen(driver);
+
+ for (i = 0; wpa_drivers[i]; i++) {
+ if (os_strlen(wpa_drivers[i]->name) == len &&
+ os_strncmp(driver, wpa_drivers[i]->name, len) ==
+ 0) {
+ /* First driver that succeeds wins */
+ if (select_driver(wpa_s, i) == 0)
+ return 0;
+ }
+ }
+
+ driver = pos + 1;
+ } while (pos);
+
+ wpa_msg(wpa_s, MSG_ERROR, "Unsupported driver '%s'", name);
+ return -1;
+}
+
+
+/**
+ * wpa_supplicant_rx_eapol - Deliver a received EAPOL frame to wpa_supplicant
+ * @ctx: Context pointer (wpa_s); this is the ctx variable registered
+ * with struct wpa_driver_ops::init()
+ * @src_addr: Source address of the EAPOL frame
+ * @buf: EAPOL data starting from the EAPOL header (i.e., no Ethernet header)
+ * @len: Length of the EAPOL data
+ *
+ * This function is called for each received EAPOL frame. Most driver
+ * interfaces rely on more generic OS mechanism for receiving frames through
+ * l2_packet, but if such a mechanism is not available, the driver wrapper may
+ * take care of received EAPOL frames and deliver them to the core supplicant
+ * code by calling this function.
+ */
+void wpa_supplicant_rx_eapol(void *ctx, const u8 *src_addr,
+ const u8 *buf, size_t len)
+{
+ struct wpa_supplicant *wpa_s = ctx;
+
+ wpa_dbg(wpa_s, MSG_DEBUG, "RX EAPOL from " MACSTR, MAC2STR(src_addr));
+ wpa_hexdump(MSG_MSGDUMP, "RX EAPOL", buf, len);
+
+#ifdef CONFIG_PEERKEY
+ if (wpa_s->wpa_state > WPA_ASSOCIATED && wpa_s->current_ssid &&
+ wpa_s->current_ssid->peerkey &&
+ !(wpa_s->drv_flags & WPA_DRIVER_FLAGS_4WAY_HANDSHAKE) &&
+ wpa_sm_rx_eapol_peerkey(wpa_s->wpa, src_addr, buf, len) == 1) {
+ wpa_dbg(wpa_s, MSG_DEBUG, "RSN: Processed PeerKey EAPOL-Key");
+ return;
+ }
+#endif /* CONFIG_PEERKEY */
+
+ if (wpa_s->wpa_state < WPA_ASSOCIATED ||
+ (wpa_s->last_eapol_matches_bssid &&
+#ifdef CONFIG_AP
+ !wpa_s->ap_iface &&
+#endif /* CONFIG_AP */
+ os_memcmp(src_addr, wpa_s->bssid, ETH_ALEN) != 0)) {
+ /*
+ * There is possible race condition between receiving the
+ * association event and the EAPOL frame since they are coming
+ * through different paths from the driver. In order to avoid
+ * issues in trying to process the EAPOL frame before receiving
+ * association information, lets queue it for processing until
+ * the association event is received. This may also be needed in
+ * driver-based roaming case, so also use src_addr != BSSID as a
+ * trigger if we have previously confirmed that the
+ * Authenticator uses BSSID as the src_addr (which is not the
+ * case with wired IEEE 802.1X).
+ */
+ wpa_dbg(wpa_s, MSG_DEBUG, "Not associated - Delay processing "
+ "of received EAPOL frame (state=%s bssid=" MACSTR ")",
+ wpa_supplicant_state_txt(wpa_s->wpa_state),
+ MAC2STR(wpa_s->bssid));
+ wpabuf_free(wpa_s->pending_eapol_rx);
+ wpa_s->pending_eapol_rx = wpabuf_alloc_copy(buf, len);
+ if (wpa_s->pending_eapol_rx) {
+ os_get_reltime(&wpa_s->pending_eapol_rx_time);
+ os_memcpy(wpa_s->pending_eapol_rx_src, src_addr,
+ ETH_ALEN);
+ }
+ return;
+ }
+
+ wpa_s->last_eapol_matches_bssid =
+ os_memcmp(src_addr, wpa_s->bssid, ETH_ALEN) == 0;
+
+#ifdef CONFIG_AP
+ if (wpa_s->ap_iface) {
+ wpa_supplicant_ap_rx_eapol(wpa_s, src_addr, buf, len);
+ return;
+ }
+#endif /* CONFIG_AP */
+
+ if (wpa_s->key_mgmt == WPA_KEY_MGMT_NONE) {
+ wpa_dbg(wpa_s, MSG_DEBUG, "Ignored received EAPOL frame since "
+ "no key management is configured");
+ return;
+ }
+
+ if (wpa_s->eapol_received == 0 &&
+ (!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_4WAY_HANDSHAKE) ||
+ !wpa_key_mgmt_wpa_psk(wpa_s->key_mgmt) ||
+ wpa_s->wpa_state != WPA_COMPLETED) &&
+ (wpa_s->current_ssid == NULL ||
+ wpa_s->current_ssid->mode != IEEE80211_MODE_IBSS)) {
+ /* Timeout for completing IEEE 802.1X and WPA authentication */
+ int timeout = 10;
+
+ if (wpa_key_mgmt_wpa_ieee8021x(wpa_s->key_mgmt) ||
+ wpa_s->key_mgmt == WPA_KEY_MGMT_IEEE8021X_NO_WPA ||
+ wpa_s->key_mgmt == WPA_KEY_MGMT_WPS) {
+ /* Use longer timeout for IEEE 802.1X/EAP */
+ timeout = 70;
+ }
+
+#ifdef CONFIG_WPS
+ if (wpa_s->current_ssid && wpa_s->current_bss &&
+ (wpa_s->current_ssid->key_mgmt & WPA_KEY_MGMT_WPS) &&
+ eap_is_wps_pin_enrollee(&wpa_s->current_ssid->eap)) {
+ /*
+ * Use shorter timeout if going through WPS AP iteration
+ * for PIN config method with an AP that does not
+ * advertise Selected Registrar.
+ */
+ struct wpabuf *wps_ie;
+
+ wps_ie = wpa_bss_get_vendor_ie_multi(
+ wpa_s->current_bss, WPS_IE_VENDOR_TYPE);
+ if (wps_ie &&
+ !wps_is_addr_authorized(wps_ie, wpa_s->own_addr, 1))
+ timeout = 10;
+ wpabuf_free(wps_ie);
+ }
+#endif /* CONFIG_WPS */
+
+ wpa_supplicant_req_auth_timeout(wpa_s, timeout, 0);
+ }
+ wpa_s->eapol_received++;
+
+ if (wpa_s->countermeasures) {
+ wpa_msg(wpa_s, MSG_INFO, "WPA: Countermeasures - dropped "
+ "EAPOL packet");
+ return;
+ }
+
+#ifdef CONFIG_IBSS_RSN
+ if (wpa_s->current_ssid &&
+ wpa_s->current_ssid->mode == WPAS_MODE_IBSS) {
+ ibss_rsn_rx_eapol(wpa_s->ibss_rsn, src_addr, buf, len);
+ return;
+ }
+#endif /* CONFIG_IBSS_RSN */
+
+ /* Source address of the incoming EAPOL frame could be compared to the
+ * current BSSID. However, it is possible that a centralized
+ * Authenticator could be using another MAC address than the BSSID of
+ * an AP, so just allow any address to be used for now. The replies are
+ * still sent to the current BSSID (if available), though. */
+
+ os_memcpy(wpa_s->last_eapol_src, src_addr, ETH_ALEN);
+ if (!wpa_key_mgmt_wpa_psk(wpa_s->key_mgmt) &&
+ eapol_sm_rx_eapol(wpa_s->eapol, src_addr, buf, len) > 0)
+ return;
+ wpa_drv_poll(wpa_s);
+ if (!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_4WAY_HANDSHAKE))
+ wpa_sm_rx_eapol(wpa_s->wpa, src_addr, buf, len);
+ else if (wpa_key_mgmt_wpa_ieee8021x(wpa_s->key_mgmt)) {
+ /*
+ * Set portValid = TRUE here since we are going to skip 4-way
+ * handshake processing which would normally set portValid. We
+ * need this to allow the EAPOL state machines to be completed
+ * without going through EAPOL-Key handshake.
+ */
+ eapol_sm_notify_portValid(wpa_s->eapol, TRUE);
+ }
+}
+
+
+int wpa_supplicant_update_mac_addr(struct wpa_supplicant *wpa_s)
+{
+ if ((!wpa_s->p2p_mgmt ||
+ !(wpa_s->drv_flags & WPA_DRIVER_FLAGS_DEDICATED_P2P_DEVICE)) &&
+ !(wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_DEDICATED_INTERFACE)) {
+ l2_packet_deinit(wpa_s->l2);
+ wpa_s->l2 = l2_packet_init(wpa_s->ifname,
+ wpa_drv_get_mac_addr(wpa_s),
+ ETH_P_EAPOL,
+ wpa_supplicant_rx_eapol, wpa_s, 0);
+ if (wpa_s->l2 == NULL)
+ return -1;
+ } else {
+ const u8 *addr = wpa_drv_get_mac_addr(wpa_s);
+ if (addr)
+ os_memcpy(wpa_s->own_addr, addr, ETH_ALEN);
+ }
+
+ if (wpa_s->l2 && l2_packet_get_own_addr(wpa_s->l2, wpa_s->own_addr)) {
+ wpa_msg(wpa_s, MSG_ERROR, "Failed to get own L2 address");
+ return -1;
+ }
+
+ wpa_sm_set_own_addr(wpa_s->wpa, wpa_s->own_addr);
+
+ return 0;
+}
+
+
+static void wpa_supplicant_rx_eapol_bridge(void *ctx, const u8 *src_addr,
+ const u8 *buf, size_t len)
+{
+ struct wpa_supplicant *wpa_s = ctx;
+ const struct l2_ethhdr *eth;
+
+ if (len < sizeof(*eth))
+ return;
+ eth = (const struct l2_ethhdr *) buf;
+
+ if (os_memcmp(eth->h_dest, wpa_s->own_addr, ETH_ALEN) != 0 &&
+ !(eth->h_dest[0] & 0x01)) {
+ wpa_dbg(wpa_s, MSG_DEBUG, "RX EAPOL from " MACSTR " to " MACSTR
+ " (bridge - not for this interface - ignore)",
+ MAC2STR(src_addr), MAC2STR(eth->h_dest));
+ return;
+ }
+
+ wpa_dbg(wpa_s, MSG_DEBUG, "RX EAPOL from " MACSTR " to " MACSTR
+ " (bridge)", MAC2STR(src_addr), MAC2STR(eth->h_dest));
+ wpa_supplicant_rx_eapol(wpa_s, src_addr, buf + sizeof(*eth),
+ len - sizeof(*eth));
+}
+
+
+/**
+ * wpa_supplicant_driver_init - Initialize driver interface parameters
+ * @wpa_s: Pointer to wpa_supplicant data
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function is called to initialize driver interface parameters.
+ * wpa_drv_init() must have been called before this function to initialize the
+ * driver interface.
+ */
+int wpa_supplicant_driver_init(struct wpa_supplicant *wpa_s)
+{
+ static int interface_count = 0;
+
+ if (wpa_supplicant_update_mac_addr(wpa_s) < 0)
+ return -1;
+
+ wpa_dbg(wpa_s, MSG_DEBUG, "Own MAC address: " MACSTR,
+ MAC2STR(wpa_s->own_addr));
+ os_memcpy(wpa_s->perm_addr, wpa_s->own_addr, ETH_ALEN);
+ wpa_sm_set_own_addr(wpa_s->wpa, wpa_s->own_addr);
+
+ if (wpa_s->bridge_ifname[0]) {
+ wpa_dbg(wpa_s, MSG_DEBUG, "Receiving packets from bridge "
+ "interface '%s'", wpa_s->bridge_ifname);
+ wpa_s->l2_br = l2_packet_init_bridge(
+ wpa_s->bridge_ifname, wpa_s->ifname, wpa_s->own_addr,
+ ETH_P_EAPOL, wpa_supplicant_rx_eapol_bridge, wpa_s, 1);
+ if (wpa_s->l2_br == NULL) {
+ wpa_msg(wpa_s, MSG_ERROR, "Failed to open l2_packet "
+ "connection for the bridge interface '%s'",
+ wpa_s->bridge_ifname);
+ return -1;
+ }
+ }
+
+ if (wpa_s->conf->ap_scan == 2 &&
+ os_strcmp(wpa_s->driver->name, "nl80211") == 0) {
+ wpa_printf(MSG_INFO,
+ "Note: nl80211 driver interface is not designed to be used with ap_scan=2; this can result in connection failures");
+ }
+
+ wpa_clear_keys(wpa_s, NULL);
+
+ /* Make sure that TKIP countermeasures are not left enabled (could
+ * happen if wpa_supplicant is killed during countermeasures. */
+ wpa_drv_set_countermeasures(wpa_s, 0);
+
+ wpa_dbg(wpa_s, MSG_DEBUG, "RSN: flushing PMKID list in the driver");
+ wpa_drv_flush_pmkid(wpa_s);
+
+ wpa_s->prev_scan_ssid = WILDCARD_SSID_SCAN;
+ wpa_s->prev_scan_wildcard = 0;
+
+ if (wpa_supplicant_enabled_networks(wpa_s)) {
+ if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED) {
+ wpa_supplicant_set_state(wpa_s, WPA_DISCONNECTED);
+ interface_count = 0;
+ }
+#ifndef ANDROID
+ if (!wpa_s->p2p_mgmt &&
+ wpa_supplicant_delayed_sched_scan(wpa_s,
+ interface_count % 3,
+ 100000))
+ wpa_supplicant_req_scan(wpa_s, interface_count % 3,
+ 100000);
+#endif /* ANDROID */
+ interface_count++;
+ } else
+ wpa_supplicant_set_state(wpa_s, WPA_INACTIVE);
+
+ return 0;
+}
+
+
+static int wpa_supplicant_daemon(const char *pid_file)
+{
+ wpa_printf(MSG_DEBUG, "Daemonize..");
+ return os_daemonize(pid_file);
+}
+
+
+static struct wpa_supplicant *
+wpa_supplicant_alloc(struct wpa_supplicant *parent)
+{
+ struct wpa_supplicant *wpa_s;
+
+ wpa_s = os_zalloc(sizeof(*wpa_s));
+ if (wpa_s == NULL)
+ return NULL;
+ wpa_s->scan_req = INITIAL_SCAN_REQ;
+ wpa_s->scan_interval = 5;
+ wpa_s->new_connection = 1;
+ wpa_s->parent = parent ? parent : wpa_s;
+ wpa_s->sched_scanning = 0;
+
+ return wpa_s;
+}
+
+
+#ifdef CONFIG_HT_OVERRIDES
+
+static int wpa_set_htcap_mcs(struct wpa_supplicant *wpa_s,
+ struct ieee80211_ht_capabilities *htcaps,
+ struct ieee80211_ht_capabilities *htcaps_mask,
+ const char *ht_mcs)
+{
+ /* parse ht_mcs into hex array */
+ int i;
+ const char *tmp = ht_mcs;
+ char *end = NULL;
+
+ /* If ht_mcs is null, do not set anything */
+ if (!ht_mcs)
+ return 0;
+
+ /* This is what we are setting in the kernel */
+ os_memset(&htcaps->supported_mcs_set, 0, IEEE80211_HT_MCS_MASK_LEN);
+
+ wpa_msg(wpa_s, MSG_DEBUG, "set_htcap, ht_mcs -:%s:-", ht_mcs);
+
+ for (i = 0; i < IEEE80211_HT_MCS_MASK_LEN; i++) {
+ errno = 0;
+ long v = strtol(tmp, &end, 16);
+ if (errno == 0) {
+ wpa_msg(wpa_s, MSG_DEBUG,
+ "htcap value[%i]: %ld end: %p tmp: %p",
+ i, v, end, tmp);
+ if (end == tmp)
+ break;
+
+ htcaps->supported_mcs_set[i] = v;
+ tmp = end;
+ } else {
+ wpa_msg(wpa_s, MSG_ERROR,
+ "Failed to parse ht-mcs: %s, error: %s\n",
+ ht_mcs, strerror(errno));
+ return -1;
+ }
+ }
+
+ /*
+ * If we were able to parse any values, then set mask for the MCS set.
+ */
+ if (i) {
+ os_memset(&htcaps_mask->supported_mcs_set, 0xff,
+ IEEE80211_HT_MCS_MASK_LEN - 1);
+ /* skip the 3 reserved bits */
+ htcaps_mask->supported_mcs_set[IEEE80211_HT_MCS_MASK_LEN - 1] =
+ 0x1f;
+ }
+
+ return 0;
+}
+
+
+static int wpa_disable_max_amsdu(struct wpa_supplicant *wpa_s,
+ struct ieee80211_ht_capabilities *htcaps,
+ struct ieee80211_ht_capabilities *htcaps_mask,
+ int disabled)
+{
+ le16 msk;
+
+ wpa_msg(wpa_s, MSG_DEBUG, "set_disable_max_amsdu: %d", disabled);
+
+ if (disabled == -1)
+ return 0;
+
+ msk = host_to_le16(HT_CAP_INFO_MAX_AMSDU_SIZE);
+ htcaps_mask->ht_capabilities_info |= msk;
+ if (disabled)
+ htcaps->ht_capabilities_info &= msk;
+ else
+ htcaps->ht_capabilities_info |= msk;
+
+ return 0;
+}
+
+
+static int wpa_set_ampdu_factor(struct wpa_supplicant *wpa_s,
+ struct ieee80211_ht_capabilities *htcaps,
+ struct ieee80211_ht_capabilities *htcaps_mask,
+ int factor)
+{
+ wpa_msg(wpa_s, MSG_DEBUG, "set_ampdu_factor: %d", factor);
+
+ if (factor == -1)
+ return 0;
+
+ if (factor < 0 || factor > 3) {
+ wpa_msg(wpa_s, MSG_ERROR, "ampdu_factor: %d out of range. "
+ "Must be 0-3 or -1", factor);
+ return -EINVAL;
+ }
+
+ htcaps_mask->a_mpdu_params |= 0x3; /* 2 bits for factor */
+ htcaps->a_mpdu_params &= ~0x3;
+ htcaps->a_mpdu_params |= factor & 0x3;
+
+ return 0;
+}
+
+
+static int wpa_set_ampdu_density(struct wpa_supplicant *wpa_s,
+ struct ieee80211_ht_capabilities *htcaps,
+ struct ieee80211_ht_capabilities *htcaps_mask,
+ int density)
+{
+ wpa_msg(wpa_s, MSG_DEBUG, "set_ampdu_density: %d", density);
+
+ if (density == -1)
+ return 0;
+
+ if (density < 0 || density > 7) {
+ wpa_msg(wpa_s, MSG_ERROR,
+ "ampdu_density: %d out of range. Must be 0-7 or -1.",
+ density);
+ return -EINVAL;
+ }
+
+ htcaps_mask->a_mpdu_params |= 0x1C;
+ htcaps->a_mpdu_params &= ~(0x1C);
+ htcaps->a_mpdu_params |= (density << 2) & 0x1C;
+
+ return 0;
+}
+
+
+static int wpa_set_disable_ht40(struct wpa_supplicant *wpa_s,
+ struct ieee80211_ht_capabilities *htcaps,
+ struct ieee80211_ht_capabilities *htcaps_mask,
+ int disabled)
+{
+ /* Masking these out disables HT40 */
+ le16 msk = host_to_le16(HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET |
+ HT_CAP_INFO_SHORT_GI40MHZ);
+
+ wpa_msg(wpa_s, MSG_DEBUG, "set_disable_ht40: %d", disabled);
+
+ if (disabled)
+ htcaps->ht_capabilities_info &= ~msk;
+ else
+ htcaps->ht_capabilities_info |= msk;
+
+ htcaps_mask->ht_capabilities_info |= msk;
+
+ return 0;
+}
+
+
+static int wpa_set_disable_sgi(struct wpa_supplicant *wpa_s,
+ struct ieee80211_ht_capabilities *htcaps,
+ struct ieee80211_ht_capabilities *htcaps_mask,
+ int disabled)
+{
+ /* Masking these out disables SGI */
+ le16 msk = host_to_le16(HT_CAP_INFO_SHORT_GI20MHZ |
+ HT_CAP_INFO_SHORT_GI40MHZ);
+
+ wpa_msg(wpa_s, MSG_DEBUG, "set_disable_sgi: %d", disabled);
+
+ if (disabled)
+ htcaps->ht_capabilities_info &= ~msk;
+ else
+ htcaps->ht_capabilities_info |= msk;
+
+ htcaps_mask->ht_capabilities_info |= msk;
+
+ return 0;
+}
+
+
+static int wpa_set_disable_ldpc(struct wpa_supplicant *wpa_s,
+ struct ieee80211_ht_capabilities *htcaps,
+ struct ieee80211_ht_capabilities *htcaps_mask,
+ int disabled)
+{
+ /* Masking these out disables LDPC */
+ le16 msk = host_to_le16(HT_CAP_INFO_LDPC_CODING_CAP);
+
+ wpa_msg(wpa_s, MSG_DEBUG, "set_disable_ldpc: %d", disabled);
+
+ if (disabled)
+ htcaps->ht_capabilities_info &= ~msk;
+ else
+ htcaps->ht_capabilities_info |= msk;
+
+ htcaps_mask->ht_capabilities_info |= msk;
+
+ return 0;
+}
+
+
+void wpa_supplicant_apply_ht_overrides(
+ struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid,
+ struct wpa_driver_associate_params *params)
+{
+ struct ieee80211_ht_capabilities *htcaps;
+ struct ieee80211_ht_capabilities *htcaps_mask;
+
+ if (!ssid)
+ return;
+
+ params->disable_ht = ssid->disable_ht;
+ if (!params->htcaps || !params->htcaps_mask)
+ return;
+
+ htcaps = (struct ieee80211_ht_capabilities *) params->htcaps;
+ htcaps_mask = (struct ieee80211_ht_capabilities *) params->htcaps_mask;
+ wpa_set_htcap_mcs(wpa_s, htcaps, htcaps_mask, ssid->ht_mcs);
+ wpa_disable_max_amsdu(wpa_s, htcaps, htcaps_mask,
+ ssid->disable_max_amsdu);
+ wpa_set_ampdu_factor(wpa_s, htcaps, htcaps_mask, ssid->ampdu_factor);
+ wpa_set_ampdu_density(wpa_s, htcaps, htcaps_mask, ssid->ampdu_density);
+ wpa_set_disable_ht40(wpa_s, htcaps, htcaps_mask, ssid->disable_ht40);
+ wpa_set_disable_sgi(wpa_s, htcaps, htcaps_mask, ssid->disable_sgi);
+ wpa_set_disable_ldpc(wpa_s, htcaps, htcaps_mask, ssid->disable_ldpc);
+
+ if (ssid->ht40_intolerant) {
+ le16 bit = host_to_le16(HT_CAP_INFO_40MHZ_INTOLERANT);
+ htcaps->ht_capabilities_info |= bit;
+ htcaps_mask->ht_capabilities_info |= bit;
+ }
+}
+
+#endif /* CONFIG_HT_OVERRIDES */
+
+
+#ifdef CONFIG_VHT_OVERRIDES
+void wpa_supplicant_apply_vht_overrides(
+ struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid,
+ struct wpa_driver_associate_params *params)
+{
+ struct ieee80211_vht_capabilities *vhtcaps;
+ struct ieee80211_vht_capabilities *vhtcaps_mask;
+
+ if (!ssid)
+ return;
+
+ params->disable_vht = ssid->disable_vht;
+
+ vhtcaps = (void *) params->vhtcaps;
+ vhtcaps_mask = (void *) params->vhtcaps_mask;
+
+ if (!vhtcaps || !vhtcaps_mask)
+ return;
+
+ vhtcaps->vht_capabilities_info = ssid->vht_capa;
+ vhtcaps_mask->vht_capabilities_info = ssid->vht_capa_mask;
+
+#ifdef CONFIG_HT_OVERRIDES
+ /* if max ampdu is <= 3, we have to make the HT cap the same */
+ if (ssid->vht_capa_mask & VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MAX) {
+ int max_ampdu;
+
+ max_ampdu = (ssid->vht_capa &
+ VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MAX) >>
+ VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MAX_SHIFT;
+
+ max_ampdu = max_ampdu < 3 ? max_ampdu : 3;
+ wpa_set_ampdu_factor(wpa_s,
+ (void *) params->htcaps,
+ (void *) params->htcaps_mask,
+ max_ampdu);
+ }
+#endif /* CONFIG_HT_OVERRIDES */
+
+#define OVERRIDE_MCS(i) \
+ if (ssid->vht_tx_mcs_nss_ ##i >= 0) { \
+ vhtcaps_mask->vht_supported_mcs_set.tx_map |= \
+ 3 << 2 * (i - 1); \
+ vhtcaps->vht_supported_mcs_set.tx_map |= \
+ ssid->vht_tx_mcs_nss_ ##i << 2 * (i - 1); \
+ } \
+ if (ssid->vht_rx_mcs_nss_ ##i >= 0) { \
+ vhtcaps_mask->vht_supported_mcs_set.rx_map |= \
+ 3 << 2 * (i - 1); \
+ vhtcaps->vht_supported_mcs_set.rx_map |= \
+ ssid->vht_rx_mcs_nss_ ##i << 2 * (i - 1); \
+ }
+
+ OVERRIDE_MCS(1);
+ OVERRIDE_MCS(2);
+ OVERRIDE_MCS(3);
+ OVERRIDE_MCS(4);
+ OVERRIDE_MCS(5);
+ OVERRIDE_MCS(6);
+ OVERRIDE_MCS(7);
+ OVERRIDE_MCS(8);
+}
+#endif /* CONFIG_VHT_OVERRIDES */
+
+
+static int pcsc_reader_init(struct wpa_supplicant *wpa_s)
+{
+#ifdef PCSC_FUNCS
+ size_t len;
+
+ if (!wpa_s->conf->pcsc_reader)
+ return 0;
+
+ wpa_s->scard = scard_init(wpa_s->conf->pcsc_reader);
+ if (!wpa_s->scard)
+ return 1;
+
+ if (wpa_s->conf->pcsc_pin &&
+ scard_set_pin(wpa_s->scard, wpa_s->conf->pcsc_pin) < 0) {
+ scard_deinit(wpa_s->scard);
+ wpa_s->scard = NULL;
+ wpa_msg(wpa_s, MSG_ERROR, "PC/SC PIN validation failed");
+ return -1;
+ }
+
+ len = sizeof(wpa_s->imsi) - 1;
+ if (scard_get_imsi(wpa_s->scard, wpa_s->imsi, &len)) {
+ scard_deinit(wpa_s->scard);
+ wpa_s->scard = NULL;
+ wpa_msg(wpa_s, MSG_ERROR, "Could not read IMSI");
+ return -1;
+ }
+ wpa_s->imsi[len] = '\0';
+
+ wpa_s->mnc_len = scard_get_mnc_len(wpa_s->scard);
+
+ wpa_printf(MSG_DEBUG, "SCARD: IMSI %s (MNC length %d)",
+ wpa_s->imsi, wpa_s->mnc_len);
+
+ wpa_sm_set_scard_ctx(wpa_s->wpa, wpa_s->scard);
+ eapol_sm_register_scard_ctx(wpa_s->eapol, wpa_s->scard);
+#endif /* PCSC_FUNCS */
+
+ return 0;
+}
+
+
+int wpas_init_ext_pw(struct wpa_supplicant *wpa_s)
+{
+ char *val, *pos;
+
+ ext_password_deinit(wpa_s->ext_pw);
+ wpa_s->ext_pw = NULL;
+ eapol_sm_set_ext_pw_ctx(wpa_s->eapol, NULL);
+
+ if (!wpa_s->conf->ext_password_backend)
+ return 0;
+
+ val = os_strdup(wpa_s->conf->ext_password_backend);
+ if (val == NULL)
+ return -1;
+ pos = os_strchr(val, ':');
+ if (pos)
+ *pos++ = '\0';
+
+ wpa_printf(MSG_DEBUG, "EXT PW: Initialize backend '%s'", val);
+
+ wpa_s->ext_pw = ext_password_init(val, pos);
+ os_free(val);
+ if (wpa_s->ext_pw == NULL) {
+ wpa_printf(MSG_DEBUG, "EXT PW: Failed to initialize backend");
+ return -1;
+ }
+ eapol_sm_set_ext_pw_ctx(wpa_s->eapol, wpa_s->ext_pw);
+
+ return 0;
+}
+
+
+#ifdef CONFIG_FST
+
+static const u8 * wpas_fst_get_bssid_cb(void *ctx)
+{
+ struct wpa_supplicant *wpa_s = ctx;
+
+ return (is_zero_ether_addr(wpa_s->bssid) ||
+ wpa_s->wpa_state != WPA_COMPLETED) ? NULL : wpa_s->bssid;
+}
+
+
+static void wpas_fst_get_channel_info_cb(void *ctx,
+ enum hostapd_hw_mode *hw_mode,
+ u8 *channel)
+{
+ struct wpa_supplicant *wpa_s = ctx;
+
+ if (wpa_s->current_bss) {
+ *hw_mode = ieee80211_freq_to_chan(wpa_s->current_bss->freq,
+ channel);
+ } else if (wpa_s->hw.num_modes) {
+ *hw_mode = wpa_s->hw.modes[0].mode;
+ } else {
+ WPA_ASSERT(0);
+ *hw_mode = 0;
+ }
+}
+
+
+static int wpas_fst_get_hw_modes(void *ctx, struct hostapd_hw_modes **modes)
+{
+ struct wpa_supplicant *wpa_s = ctx;
+
+ *modes = wpa_s->hw.modes;
+ return wpa_s->hw.num_modes;
+}
+
+
+static void wpas_fst_set_ies_cb(void *ctx, const struct wpabuf *fst_ies)
+{
+ struct wpa_supplicant *wpa_s = ctx;
+
+ wpa_hexdump_buf(MSG_DEBUG, "FST: Set IEs", fst_ies);
+ wpa_s->fst_ies = fst_ies;
+}
+
+
+static int wpas_fst_send_action_cb(void *ctx, const u8 *da, struct wpabuf *data)
+{
+ struct wpa_supplicant *wpa_s = ctx;
+
+ WPA_ASSERT(os_memcmp(wpa_s->bssid, da, ETH_ALEN) == 0);
+ return wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, wpa_s->bssid,
+ wpa_s->own_addr, wpa_s->bssid,
+ wpabuf_head(data), wpabuf_len(data),
+ 0);
+}
+
+
+static const struct wpabuf * wpas_fst_get_mb_ie_cb(void *ctx, const u8 *addr)
+{
+ struct wpa_supplicant *wpa_s = ctx;
+
+ WPA_ASSERT(os_memcmp(wpa_s->bssid, addr, ETH_ALEN) == 0);
+ return wpa_s->received_mb_ies;
+}
+
+
+static void wpas_fst_update_mb_ie_cb(void *ctx, const u8 *addr,
+ const u8 *buf, size_t size)
+{
+ struct wpa_supplicant *wpa_s = ctx;
+ struct mb_ies_info info;
+
+ WPA_ASSERT(os_memcmp(wpa_s->bssid, addr, ETH_ALEN) == 0);
+
+ if (!mb_ies_info_by_ies(&info, buf, size)) {
+ wpabuf_free(wpa_s->received_mb_ies);
+ wpa_s->received_mb_ies = mb_ies_by_info(&info);
+ }
+}
+
+
+const u8 * wpas_fst_get_peer_first(void *ctx, struct fst_get_peer_ctx **get_ctx,
+ Boolean mb_only)
+{
+ struct wpa_supplicant *wpa_s = ctx;
+
+ *get_ctx = NULL;
+ if (!is_zero_ether_addr(wpa_s->bssid))
+ return (wpa_s->received_mb_ies || !mb_only) ?
+ wpa_s->bssid : NULL;
+ return NULL;
+}
+
+
+const u8 * wpas_fst_get_peer_next(void *ctx, struct fst_get_peer_ctx **get_ctx,
+ Boolean mb_only)
+{
+ return NULL;
+}
+
+void fst_wpa_supplicant_fill_iface_obj(struct wpa_supplicant *wpa_s,
+ struct fst_wpa_obj *iface_obj)
+{
+ iface_obj->ctx = wpa_s;
+ iface_obj->get_bssid = wpas_fst_get_bssid_cb;
+ iface_obj->get_channel_info = wpas_fst_get_channel_info_cb;
+ iface_obj->get_hw_modes = wpas_fst_get_hw_modes;
+ iface_obj->set_ies = wpas_fst_set_ies_cb;
+ iface_obj->send_action = wpas_fst_send_action_cb;
+ iface_obj->get_mb_ie = wpas_fst_get_mb_ie_cb;
+ iface_obj->update_mb_ie = wpas_fst_update_mb_ie_cb;
+ iface_obj->get_peer_first = wpas_fst_get_peer_first;
+ iface_obj->get_peer_next = wpas_fst_get_peer_next;
+}
+#endif /* CONFIG_FST */
+
+static int wpas_set_wowlan_triggers(struct wpa_supplicant *wpa_s,
+ const struct wpa_driver_capa *capa)
+{
+ struct wowlan_triggers *triggers;
+ int ret = 0;
+
+ if (!wpa_s->conf->wowlan_triggers)
+ return 0;
+
+ triggers = wpa_get_wowlan_triggers(wpa_s->conf->wowlan_triggers, capa);
+ if (triggers) {
+ ret = wpa_drv_wowlan(wpa_s, triggers);
+ os_free(triggers);
+ }
+ return ret;
+}
+
+
+static struct wpa_radio * radio_add_interface(struct wpa_supplicant *wpa_s,
+ const char *rn)
+{
+ struct wpa_supplicant *iface = wpa_s->global->ifaces;
+ struct wpa_radio *radio;
+
+ while (rn && iface) {
+ radio = iface->radio;
+ if (radio && os_strcmp(rn, radio->name) == 0) {
+ wpa_printf(MSG_DEBUG, "Add interface %s to existing radio %s",
+ wpa_s->ifname, rn);
+ dl_list_add(&radio->ifaces, &wpa_s->radio_list);
+ return radio;
+ }
+
+ iface = iface->next;
+ }
+
+ wpa_printf(MSG_DEBUG, "Add interface %s to a new radio %s",
+ wpa_s->ifname, rn ? rn : "N/A");
+ radio = os_zalloc(sizeof(*radio));
+ if (radio == NULL)
+ return NULL;
+
+ if (rn)
+ os_strlcpy(radio->name, rn, sizeof(radio->name));
+ dl_list_init(&radio->ifaces);
+ dl_list_init(&radio->work);
+ dl_list_add(&radio->ifaces, &wpa_s->radio_list);
+
+ return radio;
+}
+
+
+static void radio_work_free(struct wpa_radio_work *work)
+{
+ if (work->wpa_s->scan_work == work) {
+ /* This should not really happen. */
+ wpa_dbg(work->wpa_s, MSG_INFO, "Freeing radio work '%s'@%p (started=%d) that is marked as scan_work",
+ work->type, work, work->started);
+ work->wpa_s->scan_work = NULL;
+ }
+
+#ifdef CONFIG_P2P
+ if (work->wpa_s->p2p_scan_work == work) {
+ /* This should not really happen. */
+ wpa_dbg(work->wpa_s, MSG_INFO, "Freeing radio work '%s'@%p (started=%d) that is marked as p2p_scan_work",
+ work->type, work, work->started);
+ work->wpa_s->p2p_scan_work = NULL;
+ }
+#endif /* CONFIG_P2P */
+
+ dl_list_del(&work->list);
+ os_free(work);
+}
+
+
+static void radio_start_next_work(void *eloop_ctx, void *timeout_ctx)
+{
+ struct wpa_radio *radio = eloop_ctx;
+ struct wpa_radio_work *work;
+ struct os_reltime now, diff;
+ struct wpa_supplicant *wpa_s;
+
+ work = dl_list_first(&radio->work, struct wpa_radio_work, list);
+ if (work == NULL)
+ return;
+
+ if (work->started)
+ return; /* already started and still in progress */
+
+ wpa_s = dl_list_first(&radio->ifaces, struct wpa_supplicant,
+ radio_list);
+ if (wpa_s && wpa_s->radio->external_scan_running) {
+ wpa_printf(MSG_DEBUG, "Delay radio work start until externally triggered scan completes");
+ return;
+ }
+
+ os_get_reltime(&now);
+ os_reltime_sub(&now, &work->time, &diff);
+ wpa_dbg(work->wpa_s, MSG_DEBUG, "Starting radio work '%s'@%p after %ld.%06ld second wait",
+ work->type, work, diff.sec, diff.usec);
+ work->started = 1;
+ work->time = now;
+ work->cb(work, 0);
+}
+
+
+/*
+ * This function removes both started and pending radio works running on
+ * the provided interface's radio.
+ * Prior to the removal of the radio work, its callback (cb) is called with
+ * deinit set to be 1. Each work's callback is responsible for clearing its
+ * internal data and restoring to a correct state.
+ * @wpa_s: wpa_supplicant data
+ * @type: type of works to be removed
+ * @remove_all: 1 to remove all the works on this radio, 0 to remove only
+ * this interface's works.
+ */
+void radio_remove_works(struct wpa_supplicant *wpa_s,
+ const char *type, int remove_all)
+{
+ struct wpa_radio_work *work, *tmp;
+ struct wpa_radio *radio = wpa_s->radio;
+
+ dl_list_for_each_safe(work, tmp, &radio->work, struct wpa_radio_work,
+ list) {
+ if (type && os_strcmp(type, work->type) != 0)
+ continue;
+
+ /* skip other ifaces' works */
+ if (!remove_all && work->wpa_s != wpa_s)
+ continue;
+
+ wpa_dbg(wpa_s, MSG_DEBUG, "Remove radio work '%s'@%p%s",
+ work->type, work, work->started ? " (started)" : "");
+ work->cb(work, 1);
+ radio_work_free(work);
+ }
+
+ /* in case we removed the started work */
+ radio_work_check_next(wpa_s);
+}
+
+
+static void radio_remove_interface(struct wpa_supplicant *wpa_s)
+{
+ struct wpa_radio *radio = wpa_s->radio;
+
+ if (!radio)
+ return;
+
+ wpa_printf(MSG_DEBUG, "Remove interface %s from radio %s",
+ wpa_s->ifname, radio->name);
+ dl_list_del(&wpa_s->radio_list);
+ radio_remove_works(wpa_s, NULL, 0);
+ wpa_s->radio = NULL;
+ if (!dl_list_empty(&radio->ifaces))
+ return; /* Interfaces remain for this radio */
+
+ wpa_printf(MSG_DEBUG, "Remove radio %s", radio->name);
+ eloop_cancel_timeout(radio_start_next_work, radio, NULL);
+ os_free(radio);
+}
+
+
+void radio_work_check_next(struct wpa_supplicant *wpa_s)
+{
+ struct wpa_radio *radio = wpa_s->radio;
+
+ if (dl_list_empty(&radio->work))
+ return;
+ if (wpa_s->ext_work_in_progress) {
+ wpa_printf(MSG_DEBUG,
+ "External radio work in progress - delay start of pending item");
+ return;
+ }
+ eloop_cancel_timeout(radio_start_next_work, radio, NULL);
+ eloop_register_timeout(0, 0, radio_start_next_work, radio, NULL);
+}
+
+
+/**
+ * radio_add_work - Add a radio work item
+ * @wpa_s: Pointer to wpa_supplicant data
+ * @freq: Frequency of the offchannel operation in MHz or 0
+ * @type: Unique identifier for each type of work
+ * @next: Force as the next work to be executed
+ * @cb: Callback function for indicating when radio is available
+ * @ctx: Context pointer for the work (work->ctx in cb())
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function is used to request time for an operation that requires
+ * exclusive radio control. Once the radio is available, the registered callback
+ * function will be called. radio_work_done() must be called once the exclusive
+ * radio operation has been completed, so that the radio is freed for other
+ * operations. The special case of deinit=1 is used to free the context data
+ * during interface removal. That does not allow the callback function to start
+ * the radio operation, i.e., it must free any resources allocated for the radio
+ * work and return.
+ *
+ * The @freq parameter can be used to indicate a single channel on which the
+ * offchannel operation will occur. This may allow multiple radio work
+ * operations to be performed in parallel if they apply for the same channel.
+ * Setting this to 0 indicates that the work item may use multiple channels or
+ * requires exclusive control of the radio.
+ */
+int radio_add_work(struct wpa_supplicant *wpa_s, unsigned int freq,
+ const char *type, int next,
+ void (*cb)(struct wpa_radio_work *work, int deinit),
+ void *ctx)
+{
+ struct wpa_radio_work *work;
+ int was_empty;
+
+ work = os_zalloc(sizeof(*work));
+ if (work == NULL)
+ return -1;
+ wpa_dbg(wpa_s, MSG_DEBUG, "Add radio work '%s'@%p", type, work);
+ os_get_reltime(&work->time);
+ work->freq = freq;
+ work->type = type;
+ work->wpa_s = wpa_s;
+ work->cb = cb;
+ work->ctx = ctx;
+
+ was_empty = dl_list_empty(&wpa_s->radio->work);
+ if (next)
+ dl_list_add(&wpa_s->radio->work, &work->list);
+ else
+ dl_list_add_tail(&wpa_s->radio->work, &work->list);
+ if (was_empty) {
+ wpa_dbg(wpa_s, MSG_DEBUG, "First radio work item in the queue - schedule start immediately");
+ radio_work_check_next(wpa_s);
+ }
+
+ return 0;
+}
+
+
+/**
+ * radio_work_done - Indicate that a radio work item has been completed
+ * @work: Completed work
+ *
+ * This function is called once the callback function registered with
+ * radio_add_work() has completed its work.
+ */
+void radio_work_done(struct wpa_radio_work *work)
+{
+ struct wpa_supplicant *wpa_s = work->wpa_s;
+ struct os_reltime now, diff;
+ unsigned int started = work->started;
+
+ os_get_reltime(&now);
+ os_reltime_sub(&now, &work->time, &diff);
+ wpa_dbg(wpa_s, MSG_DEBUG, "Radio work '%s'@%p %s in %ld.%06ld seconds",
+ work->type, work, started ? "done" : "canceled",
+ diff.sec, diff.usec);
+ radio_work_free(work);
+ if (started)
+ radio_work_check_next(wpa_s);
+}
+
+
+struct wpa_radio_work *
+radio_work_pending(struct wpa_supplicant *wpa_s, const char *type)
+{
+ struct wpa_radio_work *work;
+ struct wpa_radio *radio = wpa_s->radio;
+
+ dl_list_for_each(work, &radio->work, struct wpa_radio_work, list) {
+ if (work->wpa_s == wpa_s && os_strcmp(work->type, type) == 0)
+ return work;
+ }
+
+ return NULL;
+}
+
+
+static int wpas_init_driver(struct wpa_supplicant *wpa_s,
+ struct wpa_interface *iface)
+{
+ const char *ifname, *driver, *rn;
+
+ driver = iface->driver;
+next_driver:
+ if (wpa_supplicant_set_driver(wpa_s, driver) < 0)
+ return -1;
+
+ wpa_s->drv_priv = wpa_drv_init(wpa_s, wpa_s->ifname);
+ if (wpa_s->drv_priv == NULL) {
+ const char *pos;
+ pos = driver ? os_strchr(driver, ',') : NULL;
+ if (pos) {
+ wpa_dbg(wpa_s, MSG_DEBUG, "Failed to initialize "
+ "driver interface - try next driver wrapper");
+ driver = pos + 1;
+ goto next_driver;
+ }
+ wpa_msg(wpa_s, MSG_ERROR, "Failed to initialize driver "
+ "interface");
+ return -1;
+ }
+ if (wpa_drv_set_param(wpa_s, wpa_s->conf->driver_param) < 0) {
+ wpa_msg(wpa_s, MSG_ERROR, "Driver interface rejected "
+ "driver_param '%s'", wpa_s->conf->driver_param);
+ return -1;
+ }
+
+ ifname = wpa_drv_get_ifname(wpa_s);
+ if (ifname && os_strcmp(ifname, wpa_s->ifname) != 0) {
+ wpa_dbg(wpa_s, MSG_DEBUG, "Driver interface replaced "
+ "interface name with '%s'", ifname);
+ os_strlcpy(wpa_s->ifname, ifname, sizeof(wpa_s->ifname));
+ }
+
+ rn = wpa_driver_get_radio_name(wpa_s);
+ if (rn && rn[0] == '\0')
+ rn = NULL;
+
+ wpa_s->radio = radio_add_interface(wpa_s, rn);
+ if (wpa_s->radio == NULL)
+ return -1;
+
+ return 0;
+}
+
+
+static int wpa_supplicant_init_iface(struct wpa_supplicant *wpa_s,
+ struct wpa_interface *iface)
+{
+ struct wpa_driver_capa capa;
+ int capa_res;
+
+ wpa_printf(MSG_DEBUG, "Initializing interface '%s' conf '%s' driver "
+ "'%s' ctrl_interface '%s' bridge '%s'", iface->ifname,
+ iface->confname ? iface->confname : "N/A",
+ iface->driver ? iface->driver : "default",
+ iface->ctrl_interface ? iface->ctrl_interface : "N/A",
+ iface->bridge_ifname ? iface->bridge_ifname : "N/A");
+
+ if (iface->confname) {
+#ifdef CONFIG_BACKEND_FILE
+ wpa_s->confname = os_rel2abs_path(iface->confname);
+ if (wpa_s->confname == NULL) {
+ wpa_printf(MSG_ERROR, "Failed to get absolute path "
+ "for configuration file '%s'.",
+ iface->confname);
+ return -1;
+ }
+ wpa_printf(MSG_DEBUG, "Configuration file '%s' -> '%s'",
+ iface->confname, wpa_s->confname);
+#else /* CONFIG_BACKEND_FILE */
+ wpa_s->confname = os_strdup(iface->confname);
+#endif /* CONFIG_BACKEND_FILE */
+ wpa_s->conf = wpa_config_read(wpa_s->confname, NULL);
+ if (wpa_s->conf == NULL) {
+ wpa_printf(MSG_ERROR, "Failed to read or parse "
+ "configuration '%s'.", wpa_s->confname);
+ return -1;
+ }
+ wpa_s->confanother = os_rel2abs_path(iface->confanother);
+ wpa_config_read(wpa_s->confanother, wpa_s->conf);
+
+ /*
+ * Override ctrl_interface and driver_param if set on command
+ * line.
+ */
+ if (iface->ctrl_interface) {
+ os_free(wpa_s->conf->ctrl_interface);
+ wpa_s->conf->ctrl_interface =
+ os_strdup(iface->ctrl_interface);
+ }
+
+ if (iface->driver_param) {
+ os_free(wpa_s->conf->driver_param);
+ wpa_s->conf->driver_param =
+ os_strdup(iface->driver_param);
+ }
+
+ if (iface->p2p_mgmt && !iface->ctrl_interface) {
+ os_free(wpa_s->conf->ctrl_interface);
+ wpa_s->conf->ctrl_interface = NULL;
+ }
+ } else
+ wpa_s->conf = wpa_config_alloc_empty(iface->ctrl_interface,
+ iface->driver_param);
+
+ if (wpa_s->conf == NULL) {
+ wpa_printf(MSG_ERROR, "\nNo configuration found.");
+ return -1;
+ }
+
+ if (iface->ifname == NULL) {
+ wpa_printf(MSG_ERROR, "\nInterface name is required.");
+ return -1;
+ }
+ if (os_strlen(iface->ifname) >= sizeof(wpa_s->ifname)) {
+ wpa_printf(MSG_ERROR, "\nToo long interface name '%s'.",
+ iface->ifname);
+ return -1;
+ }
+ os_strlcpy(wpa_s->ifname, iface->ifname, sizeof(wpa_s->ifname));
+
+ if (iface->bridge_ifname) {
+ if (os_strlen(iface->bridge_ifname) >=
+ sizeof(wpa_s->bridge_ifname)) {
+ wpa_printf(MSG_ERROR, "\nToo long bridge interface "
+ "name '%s'.", iface->bridge_ifname);
+ return -1;
+ }
+ os_strlcpy(wpa_s->bridge_ifname, iface->bridge_ifname,
+ sizeof(wpa_s->bridge_ifname));
+ }
+
+ /* RSNA Supplicant Key Management - INITIALIZE */
+ eapol_sm_notify_portEnabled(wpa_s->eapol, FALSE);
+ eapol_sm_notify_portValid(wpa_s->eapol, FALSE);
+
+ /* Initialize driver interface and register driver event handler before
+ * L2 receive handler so that association events are processed before
+ * EAPOL-Key packets if both become available for the same select()
+ * call. */
+ if (wpas_init_driver(wpa_s, iface) < 0)
+ return -1;
+
+ if (wpa_supplicant_init_wpa(wpa_s) < 0)
+ return -1;
+
+ wpa_sm_set_ifname(wpa_s->wpa, wpa_s->ifname,
+ wpa_s->bridge_ifname[0] ? wpa_s->bridge_ifname :
+ NULL);
+ wpa_sm_set_fast_reauth(wpa_s->wpa, wpa_s->conf->fast_reauth);
+
+ if (wpa_s->conf->dot11RSNAConfigPMKLifetime &&
+ wpa_sm_set_param(wpa_s->wpa, RSNA_PMK_LIFETIME,
+ wpa_s->conf->dot11RSNAConfigPMKLifetime)) {
+ wpa_msg(wpa_s, MSG_ERROR, "Invalid WPA parameter value for "
+ "dot11RSNAConfigPMKLifetime");
+ return -1;
+ }
+
+ if (wpa_s->conf->dot11RSNAConfigPMKReauthThreshold &&
+ wpa_sm_set_param(wpa_s->wpa, RSNA_PMK_REAUTH_THRESHOLD,
+ wpa_s->conf->dot11RSNAConfigPMKReauthThreshold)) {
+ wpa_msg(wpa_s, MSG_ERROR, "Invalid WPA parameter value for "
+ "dot11RSNAConfigPMKReauthThreshold");
+ return -1;
+ }
+
+ if (wpa_s->conf->dot11RSNAConfigSATimeout &&
+ wpa_sm_set_param(wpa_s->wpa, RSNA_SA_TIMEOUT,
+ wpa_s->conf->dot11RSNAConfigSATimeout)) {
+ wpa_msg(wpa_s, MSG_ERROR, "Invalid WPA parameter value for "
+ "dot11RSNAConfigSATimeout");
+ return -1;
+ }
+
+ wpa_s->hw.modes = wpa_drv_get_hw_feature_data(wpa_s,
+ &wpa_s->hw.num_modes,
+ &wpa_s->hw.flags);
+ if (wpa_s->hw.modes) {
+ u16 i;
+
+ for (i = 0; i < wpa_s->hw.num_modes; i++) {
+ if (wpa_s->hw.modes[i].vht_capab) {
+ wpa_s->hw_capab = CAPAB_VHT;
+ break;
+ }
+
+ if (wpa_s->hw.modes[i].ht_capab &
+ HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET)
+ wpa_s->hw_capab = CAPAB_HT40;
+ else if (wpa_s->hw.modes[i].ht_capab &&
+ wpa_s->hw_capab == CAPAB_NO_HT_VHT)
+ wpa_s->hw_capab = CAPAB_HT;
+ }
+ }
+
+ capa_res = wpa_drv_get_capa(wpa_s, &capa);
+ if (capa_res == 0) {
+ wpa_s->drv_capa_known = 1;
+ wpa_s->drv_flags = capa.flags;
+ wpa_s->drv_enc = capa.enc;
+ wpa_s->drv_smps_modes = capa.smps_modes;
+ wpa_s->drv_rrm_flags = capa.rrm_flags;
+ wpa_s->probe_resp_offloads = capa.probe_resp_offloads;
+ wpa_s->max_scan_ssids = capa.max_scan_ssids;
+ wpa_s->max_sched_scan_ssids = capa.max_sched_scan_ssids;
+ wpa_s->sched_scan_supported = capa.sched_scan_supported;
+ wpa_s->max_match_sets = capa.max_match_sets;
+ wpa_s->max_remain_on_chan = capa.max_remain_on_chan;
+ wpa_s->max_stations = capa.max_stations;
+ wpa_s->extended_capa = capa.extended_capa;
+ wpa_s->extended_capa_mask = capa.extended_capa_mask;
+ wpa_s->extended_capa_len = capa.extended_capa_len;
+ wpa_s->num_multichan_concurrent =
+ capa.num_multichan_concurrent;
+ wpa_s->wmm_ac_supported = capa.wmm_ac_supported;
+
+ if (capa.mac_addr_rand_scan_supported)
+ wpa_s->mac_addr_rand_supported |= MAC_ADDR_RAND_SCAN;
+ if (wpa_s->sched_scan_supported &&
+ capa.mac_addr_rand_sched_scan_supported)
+ wpa_s->mac_addr_rand_supported |=
+ (MAC_ADDR_RAND_SCHED_SCAN | MAC_ADDR_RAND_PNO);
+ }
+ if (wpa_s->max_remain_on_chan == 0)
+ wpa_s->max_remain_on_chan = 1000;
+
+ /*
+ * Only take p2p_mgmt parameters when P2P Device is supported.
+ * Doing it here as it determines whether l2_packet_init() will be done
+ * during wpa_supplicant_driver_init().
+ */
+ if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_DEDICATED_P2P_DEVICE)
+ wpa_s->p2p_mgmt = iface->p2p_mgmt;
+ else
+ iface->p2p_mgmt = 1;
+
+ if (wpa_s->num_multichan_concurrent == 0)
+ wpa_s->num_multichan_concurrent = 1;
+
+ if (wpa_supplicant_driver_init(wpa_s) < 0)
+ return -1;
+
+#ifdef CONFIG_TDLS
+ if ((!iface->p2p_mgmt ||
+ !(wpa_s->drv_flags &
+ WPA_DRIVER_FLAGS_DEDICATED_P2P_DEVICE)) &&
+ wpa_tdls_init(wpa_s->wpa))
+ return -1;
+#endif /* CONFIG_TDLS */
+
+ if (wpa_s->conf->country[0] && wpa_s->conf->country[1] &&
+ wpa_drv_set_country(wpa_s, wpa_s->conf->country)) {
+ wpa_dbg(wpa_s, MSG_DEBUG, "Failed to set country");
+ return -1;
+ }
+
+#ifdef CONFIG_FST
+ if (wpa_s->conf->fst_group_id) {
+ struct fst_iface_cfg cfg;
+ struct fst_wpa_obj iface_obj;
+
+ fst_wpa_supplicant_fill_iface_obj(wpa_s, &iface_obj);
+ os_strlcpy(cfg.group_id, wpa_s->conf->fst_group_id,
+ sizeof(cfg.group_id));
+ cfg.priority = wpa_s->conf->fst_priority;
+ cfg.llt = wpa_s->conf->fst_llt;
+
+ wpa_s->fst = fst_attach(wpa_s->ifname, wpa_s->own_addr,
+ &iface_obj, &cfg);
+ if (!wpa_s->fst) {
+ wpa_msg(wpa_s, MSG_ERROR,
+ "FST: Cannot attach iface %s to group %s",
+ wpa_s->ifname, cfg.group_id);
+ return -1;
+ }
+ }
+#endif /* CONFIG_FST */
+
+ if (wpas_wps_init(wpa_s))
+ return -1;
+
+ if (wpa_supplicant_init_eapol(wpa_s) < 0)
+ return -1;
+ wpa_sm_set_eapol(wpa_s->wpa, wpa_s->eapol);
+
+ wpa_s->ctrl_iface = wpa_supplicant_ctrl_iface_init(wpa_s);
+ if (wpa_s->ctrl_iface == NULL) {
+ wpa_printf(MSG_ERROR,
+ "Failed to initialize control interface '%s'.\n"
+ "You may have another wpa_supplicant process "
+ "already running or the file was\n"
+ "left by an unclean termination of wpa_supplicant "
+ "in which case you will need\n"
+ "to manually remove this file before starting "
+ "wpa_supplicant again.\n",
+ wpa_s->conf->ctrl_interface);
+ return -1;
+ }
+
+ wpa_s->gas = gas_query_init(wpa_s);
+ if (wpa_s->gas == NULL) {
+ wpa_printf(MSG_ERROR, "Failed to initialize GAS query");
+ return -1;
+ }
+
+ if (iface->p2p_mgmt && wpas_p2p_init(wpa_s->global, wpa_s) < 0) {
+ wpa_msg(wpa_s, MSG_ERROR, "Failed to init P2P");
+ return -1;
+ }
+
+ if (wpa_bss_init(wpa_s) < 0)
+ return -1;
+
+ /*
+ * Set Wake-on-WLAN triggers, if configured.
+ * Note: We don't restore/remove the triggers on shutdown (it doesn't
+ * have effect anyway when the interface is down).
+ */
+ if (capa_res == 0 && wpas_set_wowlan_triggers(wpa_s, &capa) < 0)
+ return -1;
+
+#ifdef CONFIG_EAP_PROXY
+{
+ size_t len;
+ wpa_s->mnc_len = eapol_sm_get_eap_proxy_imsi(wpa_s->eapol, wpa_s->imsi,
+ &len);
+ if (wpa_s->mnc_len > 0) {
+ wpa_s->imsi[len] = '\0';
+ wpa_printf(MSG_DEBUG, "eap_proxy: IMSI %s (MNC length %d)",
+ wpa_s->imsi, wpa_s->mnc_len);
+ } else {
+ wpa_printf(MSG_DEBUG, "eap_proxy: IMSI not available");
+ }
+}
+#endif /* CONFIG_EAP_PROXY */
+
+ if (pcsc_reader_init(wpa_s) < 0)
+ return -1;
+
+ if (wpas_init_ext_pw(wpa_s) < 0)
+ return -1;
+
+ wpas_rrm_reset(wpa_s);
+
+ return 0;
+}
+
+
+static void wpa_supplicant_deinit_iface(struct wpa_supplicant *wpa_s,
+ int notify, int terminate)
+{
+ struct wpa_global *global = wpa_s->global;
+ struct wpa_supplicant *iface, *prev;
+
+ if (wpa_s == wpa_s->parent)
+ wpas_p2p_group_remove(wpa_s, "*");
+
+ iface = global->ifaces;
+ while (iface) {
+ if (iface == wpa_s || iface->parent != wpa_s) {
+ iface = iface->next;
+ continue;
+ }
+ wpa_printf(MSG_DEBUG,
+ "Remove remaining child interface %s from parent %s",
+ iface->ifname, wpa_s->ifname);
+ prev = iface;
+ iface = iface->next;
+ wpa_supplicant_remove_iface(global, prev, terminate);
+ }
+
+ wpa_s->disconnected = 1;
+ if (wpa_s->drv_priv) {
+ wpa_supplicant_deauthenticate(wpa_s,
+ WLAN_REASON_DEAUTH_LEAVING);
+
+ wpa_drv_set_countermeasures(wpa_s, 0);
+ wpa_clear_keys(wpa_s, NULL);
+ }
+
+ wpa_supplicant_cleanup(wpa_s);
+ wpas_p2p_deinit_iface(wpa_s);
+
+ wpas_ctrl_radio_work_flush(wpa_s);
+ radio_remove_interface(wpa_s);
+
+#ifdef CONFIG_FST
+ if (wpa_s->fst) {
+ fst_detach(wpa_s->fst);
+ wpa_s->fst = NULL;
+ }
+ if (wpa_s->received_mb_ies) {
+ wpabuf_free(wpa_s->received_mb_ies);
+ wpa_s->received_mb_ies = NULL;
+ }
+#endif /* CONFIG_FST */
+
+ if (wpa_s->drv_priv)
+ wpa_drv_deinit(wpa_s);
+
+ if (notify)
+ wpas_notify_iface_removed(wpa_s);
+
+ if (terminate)
+ wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_TERMINATING);
+
+ if (wpa_s->ctrl_iface) {
+ wpa_supplicant_ctrl_iface_deinit(wpa_s->ctrl_iface);
+ wpa_s->ctrl_iface = NULL;
+ }
+
+#ifdef CONFIG_MESH
+ if (wpa_s->ifmsh) {
+ wpa_supplicant_mesh_iface_deinit(wpa_s, wpa_s->ifmsh);
+ wpa_s->ifmsh = NULL;
+ }
+#endif /* CONFIG_MESH */
+
+ if (wpa_s->conf != NULL) {
+ wpa_config_free(wpa_s->conf);
+ wpa_s->conf = NULL;
+ }
+
+ os_free(wpa_s->ssids_from_scan_req);
+
+ os_free(wpa_s);
+}
+
+
+/**
+ * wpa_supplicant_add_iface - Add a new network interface
+ * @global: Pointer to global data from wpa_supplicant_init()
+ * @iface: Interface configuration options
+ * @parent: Parent interface or %NULL to assign new interface as parent
+ * Returns: Pointer to the created interface or %NULL on failure
+ *
+ * This function is used to add new network interfaces for %wpa_supplicant.
+ * This can be called before wpa_supplicant_run() to add interfaces before the
+ * main event loop has been started. In addition, new interfaces can be added
+ * dynamically while %wpa_supplicant is already running. This could happen,
+ * e.g., when a hotplug network adapter is inserted.
+ */
+struct wpa_supplicant * wpa_supplicant_add_iface(struct wpa_global *global,
+ struct wpa_interface *iface,
+ struct wpa_supplicant *parent)
+{
+ struct wpa_supplicant *wpa_s;
+ struct wpa_interface t_iface;
+ struct wpa_ssid *ssid;
+
+ if (global == NULL || iface == NULL)
+ return NULL;
+
+ wpa_s = wpa_supplicant_alloc(parent);
+ if (wpa_s == NULL)
+ return NULL;
+
+ wpa_s->global = global;
+
+ t_iface = *iface;
+ if (global->params.override_driver) {
+ wpa_printf(MSG_DEBUG, "Override interface parameter: driver "
+ "('%s' -> '%s')",
+ iface->driver, global->params.override_driver);
+ t_iface.driver = global->params.override_driver;
+ }
+ if (global->params.override_ctrl_interface) {
+ wpa_printf(MSG_DEBUG, "Override interface parameter: "
+ "ctrl_interface ('%s' -> '%s')",
+ iface->ctrl_interface,
+ global->params.override_ctrl_interface);
+ t_iface.ctrl_interface =
+ global->params.override_ctrl_interface;
+ }
+ if (wpa_supplicant_init_iface(wpa_s, &t_iface)) {
+ wpa_printf(MSG_DEBUG, "Failed to add interface %s",
+ iface->ifname);
+ wpa_supplicant_deinit_iface(wpa_s, 0, 0);
+ return NULL;
+ }
+
+ if (iface->p2p_mgmt == 0) {
+ /* Notify the control interfaces about new iface */
+ if (wpas_notify_iface_added(wpa_s)) {
+ wpa_supplicant_deinit_iface(wpa_s, 1, 0);
+ return NULL;
+ }
+
+ for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next)
+ wpas_notify_network_added(wpa_s, ssid);
+ }
+
+ wpa_s->next = global->ifaces;
+ global->ifaces = wpa_s;
+
+ wpa_dbg(wpa_s, MSG_DEBUG, "Added interface %s", wpa_s->ifname);
+ wpa_supplicant_set_state(wpa_s, WPA_DISCONNECTED);
+
+#ifdef CONFIG_P2P
+ if (wpa_s->global->p2p == NULL &&
+ !wpa_s->global->p2p_disabled && !wpa_s->conf->p2p_disabled &&
+ (wpa_s->drv_flags & WPA_DRIVER_FLAGS_DEDICATED_P2P_DEVICE) &&
+ wpas_p2p_add_p2pdev_interface(
+ wpa_s, wpa_s->global->params.conf_p2p_dev) < 0) {
+ wpa_printf(MSG_INFO,
+ "P2P: Failed to enable P2P Device interface");
+ /* Try to continue without. P2P will be disabled. */
+ }
+#endif /* CONFIG_P2P */
+
+ return wpa_s;
+}
+
+
+/**
+ * wpa_supplicant_remove_iface - Remove a network interface
+ * @global: Pointer to global data from wpa_supplicant_init()
+ * @wpa_s: Pointer to the network interface to be removed
+ * Returns: 0 if interface was removed, -1 if interface was not found
+ *
+ * This function can be used to dynamically remove network interfaces from
+ * %wpa_supplicant, e.g., when a hotplug network adapter is ejected. In
+ * addition, this function is used to remove all remaining interfaces when
+ * %wpa_supplicant is terminated.
+ */
+int wpa_supplicant_remove_iface(struct wpa_global *global,
+ struct wpa_supplicant *wpa_s,
+ int terminate)
+{
+ struct wpa_supplicant *prev;
+#ifdef CONFIG_MESH
+ unsigned int mesh_if_created = wpa_s->mesh_if_created;
+ char *ifname = NULL;
+#endif /* CONFIG_MESH */
+
+ /* Remove interface from the global list of interfaces */
+ prev = global->ifaces;
+ if (prev == wpa_s) {
+ global->ifaces = wpa_s->next;
+ } else {
+ while (prev && prev->next != wpa_s)
+ prev = prev->next;
+ if (prev == NULL)
+ return -1;
+ prev->next = wpa_s->next;
+ }
+
+ wpa_dbg(wpa_s, MSG_DEBUG, "Removing interface %s", wpa_s->ifname);
+
+#ifdef CONFIG_MESH
+ if (mesh_if_created) {
+ ifname = os_strdup(wpa_s->ifname);
+ if (ifname == NULL) {
+ wpa_dbg(wpa_s, MSG_ERROR,
+ "mesh: Failed to malloc ifname");
+ return -1;
+ }
+ }
+#endif /* CONFIG_MESH */
+
+ if (global->p2p_group_formation == wpa_s)
+ global->p2p_group_formation = NULL;
+ if (global->p2p_invite_group == wpa_s)
+ global->p2p_invite_group = NULL;
+ wpa_supplicant_deinit_iface(wpa_s, 1, terminate);
+
+#ifdef CONFIG_MESH
+ if (mesh_if_created) {
+ wpa_drv_if_remove(global->ifaces, WPA_IF_MESH, ifname);
+ os_free(ifname);
+ }
+#endif /* CONFIG_MESH */
+
+ return 0;
+}
+
+
+/**
+ * wpa_supplicant_get_eap_mode - Get the current EAP mode
+ * @wpa_s: Pointer to the network interface
+ * Returns: Pointer to the eap mode or the string "UNKNOWN" if not found
+ */
+const char * wpa_supplicant_get_eap_mode(struct wpa_supplicant *wpa_s)
+{
+ const char *eapol_method;
+
+ if (wpa_key_mgmt_wpa_ieee8021x(wpa_s->key_mgmt) == 0 &&
+ wpa_s->key_mgmt != WPA_KEY_MGMT_IEEE8021X_NO_WPA) {
+ return "NO-EAP";
+ }
+
+ eapol_method = eapol_sm_get_method_name(wpa_s->eapol);
+ if (eapol_method == NULL)
+ return "UNKNOWN-EAP";
+
+ return eapol_method;
+}
+
+
+/**
+ * wpa_supplicant_get_iface - Get a new network interface
+ * @global: Pointer to global data from wpa_supplicant_init()
+ * @ifname: Interface name
+ * Returns: Pointer to the interface or %NULL if not found
+ */
+struct wpa_supplicant * wpa_supplicant_get_iface(struct wpa_global *global,
+ const char *ifname)
+{
+ struct wpa_supplicant *wpa_s;
+
+ for (wpa_s = global->ifaces; wpa_s; wpa_s = wpa_s->next) {
+ if (os_strcmp(wpa_s->ifname, ifname) == 0)
+ return wpa_s;
+ }
+ return NULL;
+}
+
+
+#ifndef CONFIG_NO_WPA_MSG
+static const char * wpa_supplicant_msg_ifname_cb(void *ctx)
+{
+ struct wpa_supplicant *wpa_s = ctx;
+ if (wpa_s == NULL)
+ return NULL;
+ return wpa_s->ifname;
+}
+#endif /* CONFIG_NO_WPA_MSG */
+
+
+#ifndef WPA_SUPPLICANT_CLEANUP_INTERVAL
+#define WPA_SUPPLICANT_CLEANUP_INTERVAL 10
+#endif /* WPA_SUPPLICANT_CLEANUP_INTERVAL */
+
+/* Periodic cleanup tasks */
+static void wpas_periodic(void *eloop_ctx, void *timeout_ctx)
+{
+ struct wpa_global *global = eloop_ctx;
+ struct wpa_supplicant *wpa_s;
+
+ eloop_register_timeout(WPA_SUPPLICANT_CLEANUP_INTERVAL, 0,
+ wpas_periodic, global, NULL);
+
+#ifdef CONFIG_P2P
+ if (global->p2p)
+ p2p_expire_peers(global->p2p);
+#endif /* CONFIG_P2P */
+
+ for (wpa_s = global->ifaces; wpa_s; wpa_s = wpa_s->next) {
+ wpa_bss_flush_by_age(wpa_s, wpa_s->conf->bss_expiration_age);
+#ifdef CONFIG_AP
+ ap_periodic(wpa_s);
+#endif /* CONFIG_AP */
+ }
+}
+
+
+/**
+ * wpa_supplicant_init - Initialize %wpa_supplicant
+ * @params: Parameters for %wpa_supplicant
+ * Returns: Pointer to global %wpa_supplicant data, or %NULL on failure
+ *
+ * This function is used to initialize %wpa_supplicant. After successful
+ * initialization, the returned data pointer can be used to add and remove
+ * network interfaces, and eventually, to deinitialize %wpa_supplicant.
+ */
+struct wpa_global * wpa_supplicant_init(struct wpa_params *params)
+{
+ struct wpa_global *global;
+ int ret, i;
+
+ if (params == NULL)
+ return NULL;
+
+#ifdef CONFIG_DRIVER_NDIS
+ {
+ void driver_ndis_init_ops(void);
+ driver_ndis_init_ops();
+ }
+#endif /* CONFIG_DRIVER_NDIS */
+
+#ifndef CONFIG_NO_WPA_MSG
+ wpa_msg_register_ifname_cb(wpa_supplicant_msg_ifname_cb);
+#endif /* CONFIG_NO_WPA_MSG */
+
+ if (params->wpa_debug_file_path)
+ wpa_debug_open_file(params->wpa_debug_file_path);
+ else
+ wpa_debug_setup_stdout();
+ if (params->wpa_debug_syslog)
+ wpa_debug_open_syslog();
+ if (params->wpa_debug_tracing) {
+ ret = wpa_debug_open_linux_tracing();
+ if (ret) {
+ wpa_printf(MSG_ERROR,
+ "Failed to enable trace logging");
+ return NULL;
+ }
+ }
+
+ ret = eap_register_methods();
+ if (ret) {
+ wpa_printf(MSG_ERROR, "Failed to register EAP methods");
+ if (ret == -2)
+ wpa_printf(MSG_ERROR, "Two or more EAP methods used "
+ "the same EAP type.");
+ return NULL;
+ }
+
+ global = os_zalloc(sizeof(*global));
+ if (global == NULL)
+ return NULL;
+ dl_list_init(&global->p2p_srv_bonjour);
+ dl_list_init(&global->p2p_srv_upnp);
+ global->params.daemonize = params->daemonize;
+ global->params.wait_for_monitor = params->wait_for_monitor;
+ global->params.dbus_ctrl_interface = params->dbus_ctrl_interface;
+ if (params->pid_file)
+ global->params.pid_file = os_strdup(params->pid_file);
+ if (params->ctrl_interface)
+ global->params.ctrl_interface =
+ os_strdup(params->ctrl_interface);
+ if (params->ctrl_interface_group)
+ global->params.ctrl_interface_group =
+ os_strdup(params->ctrl_interface_group);
+ if (params->override_driver)
+ global->params.override_driver =
+ os_strdup(params->override_driver);
+ if (params->override_ctrl_interface)
+ global->params.override_ctrl_interface =
+ os_strdup(params->override_ctrl_interface);
+#ifdef CONFIG_P2P
+ if (params->conf_p2p_dev)
+ global->params.conf_p2p_dev =
+ os_strdup(params->conf_p2p_dev);
+#endif /* CONFIG_P2P */
+ wpa_debug_level = global->params.wpa_debug_level =
+ params->wpa_debug_level;
+ wpa_debug_show_keys = global->params.wpa_debug_show_keys =
+ params->wpa_debug_show_keys;
+ wpa_debug_timestamp = global->params.wpa_debug_timestamp =
+ params->wpa_debug_timestamp;
+
+ wpa_printf(MSG_DEBUG, "wpa_supplicant v" VERSION_STR);
+
+ if (eloop_init()) {
+ wpa_printf(MSG_ERROR, "Failed to initialize event loop");
+ wpa_supplicant_deinit(global);
+ return NULL;
+ }
+
+ random_init(params->entropy_file);
+
+ global->ctrl_iface = wpa_supplicant_global_ctrl_iface_init(global);
+ if (global->ctrl_iface == NULL) {
+ wpa_supplicant_deinit(global);
+ return NULL;
+ }
+
+ if (wpas_notify_supplicant_initialized(global)) {
+ wpa_supplicant_deinit(global);
+ return NULL;
+ }
+
+ for (i = 0; wpa_drivers[i]; i++)
+ global->drv_count++;
+ if (global->drv_count == 0) {
+ wpa_printf(MSG_ERROR, "No drivers enabled");
+ wpa_supplicant_deinit(global);
+ return NULL;
+ }
+ global->drv_priv = os_calloc(global->drv_count, sizeof(void *));
+ if (global->drv_priv == NULL) {
+ wpa_supplicant_deinit(global);
+ return NULL;
+ }
+
+#ifdef CONFIG_WIFI_DISPLAY
+ if (wifi_display_init(global) < 0) {
+ wpa_printf(MSG_ERROR, "Failed to initialize Wi-Fi Display");
+ wpa_supplicant_deinit(global);
+ return NULL;
+ }
+#endif /* CONFIG_WIFI_DISPLAY */
+
+ eloop_register_timeout(WPA_SUPPLICANT_CLEANUP_INTERVAL, 0,
+ wpas_periodic, global, NULL);
+
+ return global;
+}
+
+
+/**
+ * wpa_supplicant_run - Run the %wpa_supplicant main event loop
+ * @global: Pointer to global data from wpa_supplicant_init()
+ * Returns: 0 after successful event loop run, -1 on failure
+ *
+ * This function starts the main event loop and continues running as long as
+ * there are any remaining events. In most cases, this function is running as
+ * long as the %wpa_supplicant process in still in use.
+ */
+int wpa_supplicant_run(struct wpa_global *global)
+{
+ struct wpa_supplicant *wpa_s;
+
+ if (global->params.daemonize &&
+ wpa_supplicant_daemon(global->params.pid_file))
+ return -1;
+
+ if (global->params.wait_for_monitor) {
+ for (wpa_s = global->ifaces; wpa_s; wpa_s = wpa_s->next)
+ if (wpa_s->ctrl_iface)
+ wpa_supplicant_ctrl_iface_wait(
+ wpa_s->ctrl_iface);
+ }
+
+ eloop_register_signal_terminate(wpa_supplicant_terminate, global);
+ eloop_register_signal_reconfig(wpa_supplicant_reconfig, global);
+
+ eloop_run();
+
+ return 0;
+}
+
+
+/**
+ * wpa_supplicant_deinit - Deinitialize %wpa_supplicant
+ * @global: Pointer to global data from wpa_supplicant_init()
+ *
+ * This function is called to deinitialize %wpa_supplicant and to free all
+ * allocated resources. Remaining network interfaces will also be removed.
+ */
+void wpa_supplicant_deinit(struct wpa_global *global)
+{
+ int i;
+
+ if (global == NULL)
+ return;
+
+ eloop_cancel_timeout(wpas_periodic, global, NULL);
+
+#ifdef CONFIG_WIFI_DISPLAY
+ wifi_display_deinit(global);
+#endif /* CONFIG_WIFI_DISPLAY */
+
+ while (global->ifaces)
+ wpa_supplicant_remove_iface(global, global->ifaces, 1);
+
+ if (global->ctrl_iface)
+ wpa_supplicant_global_ctrl_iface_deinit(global->ctrl_iface);
+
+ wpas_notify_supplicant_deinitialized(global);
+
+ eap_peer_unregister_methods();
+#ifdef CONFIG_AP
+ eap_server_unregister_methods();
+#endif /* CONFIG_AP */
+
+ for (i = 0; wpa_drivers[i] && global->drv_priv; i++) {
+ if (!global->drv_priv[i])
+ continue;
+ wpa_drivers[i]->global_deinit(global->drv_priv[i]);
+ }
+ os_free(global->drv_priv);
+
+ random_deinit();
+
+ eloop_destroy();
+
+ if (global->params.pid_file) {
+ os_daemonize_terminate(global->params.pid_file);
+ os_free(global->params.pid_file);
+ }
+ os_free(global->params.ctrl_interface);
+ os_free(global->params.ctrl_interface_group);
+ os_free(global->params.override_driver);
+ os_free(global->params.override_ctrl_interface);
+#ifdef CONFIG_P2P
+ os_free(global->params.conf_p2p_dev);
+#endif /* CONFIG_P2P */
+
+ os_free(global->p2p_disallow_freq.range);
+ os_free(global->p2p_go_avoid_freq.range);
+ os_free(global->add_psk);
+
+ os_free(global);
+ wpa_debug_close_syslog();
+ wpa_debug_close_file();
+ wpa_debug_close_linux_tracing();
+}
+
+
+void wpa_supplicant_update_config(struct wpa_supplicant *wpa_s)
+{
+ if ((wpa_s->conf->changed_parameters & CFG_CHANGED_COUNTRY) &&
+ wpa_s->conf->country[0] && wpa_s->conf->country[1]) {
+ char country[3];
+ country[0] = wpa_s->conf->country[0];
+ country[1] = wpa_s->conf->country[1];
+ country[2] = '\0';
+ if (wpa_drv_set_country(wpa_s, country) < 0) {
+ wpa_printf(MSG_ERROR, "Failed to set country code "
+ "'%s'", country);
+ }
+ }
+
+ if (wpa_s->conf->changed_parameters & CFG_CHANGED_EXT_PW_BACKEND)
+ wpas_init_ext_pw(wpa_s);
+
+#ifdef CONFIG_WPS
+ wpas_wps_update_config(wpa_s);
+#endif /* CONFIG_WPS */
+ wpas_p2p_update_config(wpa_s);
+ wpa_s->conf->changed_parameters = 0;
+}
+
+
+void add_freq(int *freqs, int *num_freqs, int freq)
+{
+ int i;
+
+ for (i = 0; i < *num_freqs; i++) {
+ if (freqs[i] == freq)
+ return;
+ }
+
+ freqs[*num_freqs] = freq;
+ (*num_freqs)++;
+}
+
+
+static int * get_bss_freqs_in_ess(struct wpa_supplicant *wpa_s)
+{
+ struct wpa_bss *bss, *cbss;
+ const int max_freqs = 10;
+ int *freqs;
+ int num_freqs = 0;
+
+ freqs = os_calloc(max_freqs + 1, sizeof(int));
+ if (freqs == NULL)
+ return NULL;
+
+ cbss = wpa_s->current_bss;
+
+ dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) {
+ if (bss == cbss)
+ continue;
+ if (bss->ssid_len == cbss->ssid_len &&
+ os_memcmp(bss->ssid, cbss->ssid, bss->ssid_len) == 0 &&
+ wpa_blacklist_get(wpa_s, bss->bssid) == NULL) {
+ add_freq(freqs, &num_freqs, bss->freq);
+ if (num_freqs == max_freqs)
+ break;
+ }
+ }
+
+ if (num_freqs == 0) {
+ os_free(freqs);
+ freqs = NULL;
+ }
+
+ return freqs;
+}
+
+
+void wpas_connection_failed(struct wpa_supplicant *wpa_s, const u8 *bssid)
+{
+ int timeout;
+ int count;
+ int *freqs = NULL;
+
+ wpas_connect_work_done(wpa_s);
+
+ /*
+ * Remove possible authentication timeout since the connection failed.
+ */
+ eloop_cancel_timeout(wpa_supplicant_timeout, wpa_s, NULL);
+
+ /*
+ * There is no point in blacklisting the AP if this event is
+ * generated based on local request to disconnect.
+ */
+ if (wpa_s->own_disconnect_req) {
+ wpa_s->own_disconnect_req = 0;
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ "Ignore connection failure due to local request to disconnect");
+ return;
+ }
+ if (wpa_s->disconnected) {
+ wpa_dbg(wpa_s, MSG_DEBUG, "Ignore connection failure "
+ "indication since interface has been put into "
+ "disconnected state");
+ return;
+ }
+
+ /*
+ * Add the failed BSSID into the blacklist and speed up next scan
+ * attempt if there could be other APs that could accept association.
+ * The current blacklist count indicates how many times we have tried
+ * connecting to this AP and multiple attempts mean that other APs are
+ * either not available or has already been tried, so that we can start
+ * increasing the delay here to avoid constant scanning.
+ */
+ count = wpa_blacklist_add(wpa_s, bssid);
+ if (count == 1 && wpa_s->current_bss) {
+ /*
+ * This BSS was not in the blacklist before. If there is
+ * another BSS available for the same ESS, we should try that
+ * next. Otherwise, we may as well try this one once more
+ * before allowing other, likely worse, ESSes to be considered.
+ */
+ freqs = get_bss_freqs_in_ess(wpa_s);
+ if (freqs) {
+ wpa_dbg(wpa_s, MSG_DEBUG, "Another BSS in this ESS "
+ "has been seen; try it next");
+ wpa_blacklist_add(wpa_s, bssid);
+ /*
+ * On the next scan, go through only the known channels
+ * used in this ESS based on previous scans to speed up
+ * common load balancing use case.
+ */
+ os_free(wpa_s->next_scan_freqs);
+ wpa_s->next_scan_freqs = freqs;
+ }
+ }
+
+ /*
+ * Add previous failure count in case the temporary blacklist was
+ * cleared due to no other BSSes being available.
+ */
+ count += wpa_s->extra_blacklist_count;
+
+ if (count > 3 && wpa_s->current_ssid) {
+ wpa_printf(MSG_DEBUG, "Continuous association failures - "
+ "consider temporary network disabling");
+ wpas_auth_failed(wpa_s, "CONN_FAILED");
+ }
+
+ switch (count) {
+ case 1:
+ timeout = 100;
+ break;
+ case 2:
+ timeout = 500;
+ break;
+ case 3:
+ timeout = 1000;
+ break;
+ case 4:
+ timeout = 5000;
+ break;
+ default:
+ timeout = 10000;
+ break;
+ }
+
+ wpa_dbg(wpa_s, MSG_DEBUG, "Blacklist count %d --> request scan in %d "
+ "ms", count, timeout);
+
+ /*
+ * TODO: if more than one possible AP is available in scan results,
+ * could try the other ones before requesting a new scan.
+ */
+ wpa_supplicant_req_scan(wpa_s, timeout / 1000,
+ 1000 * (timeout % 1000));
+}
+
+
+int wpas_driver_bss_selection(struct wpa_supplicant *wpa_s)
+{
+ return wpa_s->conf->ap_scan == 2 ||
+ (wpa_s->drv_flags & WPA_DRIVER_FLAGS_BSS_SELECTION);
+}
+
+
+#if defined(CONFIG_CTRL_IFACE) || defined(CONFIG_CTRL_IFACE_DBUS_NEW)
+int wpa_supplicant_ctrl_iface_ctrl_rsp_handle(struct wpa_supplicant *wpa_s,
+ struct wpa_ssid *ssid,
+ const char *field,
+ const char *value)
+{
+#ifdef IEEE8021X_EAPOL
+ struct eap_peer_config *eap = &ssid->eap;
+
+ wpa_printf(MSG_DEBUG, "CTRL_IFACE: response handle field=%s", field);
+ wpa_hexdump_ascii_key(MSG_DEBUG, "CTRL_IFACE: response value",
+ (const u8 *) value, os_strlen(value));
+
+ switch (wpa_supplicant_ctrl_req_from_string(field)) {
+ case WPA_CTRL_REQ_EAP_IDENTITY:
+ os_free(eap->identity);
+ eap->identity = (u8 *) os_strdup(value);
+ eap->identity_len = os_strlen(value);
+ eap->pending_req_identity = 0;
+ if (ssid == wpa_s->current_ssid)
+ wpa_s->reassociate = 1;
+ break;
+ case WPA_CTRL_REQ_EAP_PASSWORD:
+ bin_clear_free(eap->password, eap->password_len);
+ eap->password = (u8 *) os_strdup(value);
+ eap->password_len = os_strlen(value);
+ eap->pending_req_password = 0;
+ if (ssid == wpa_s->current_ssid)
+ wpa_s->reassociate = 1;
+ break;
+ case WPA_CTRL_REQ_EAP_NEW_PASSWORD:
+ bin_clear_free(eap->new_password, eap->new_password_len);
+ eap->new_password = (u8 *) os_strdup(value);
+ eap->new_password_len = os_strlen(value);
+ eap->pending_req_new_password = 0;
+ if (ssid == wpa_s->current_ssid)
+ wpa_s->reassociate = 1;
+ break;
+ case WPA_CTRL_REQ_EAP_PIN:
+ str_clear_free(eap->pin);
+ eap->pin = os_strdup(value);
+ eap->pending_req_pin = 0;
+ if (ssid == wpa_s->current_ssid)
+ wpa_s->reassociate = 1;
+ break;
+ case WPA_CTRL_REQ_EAP_OTP:
+ bin_clear_free(eap->otp, eap->otp_len);
+ eap->otp = (u8 *) os_strdup(value);
+ eap->otp_len = os_strlen(value);
+ os_free(eap->pending_req_otp);
+ eap->pending_req_otp = NULL;
+ eap->pending_req_otp_len = 0;
+ break;
+ case WPA_CTRL_REQ_EAP_PASSPHRASE:
+ str_clear_free(eap->private_key_passwd);
+ eap->private_key_passwd = os_strdup(value);
+ eap->pending_req_passphrase = 0;
+ if (ssid == wpa_s->current_ssid)
+ wpa_s->reassociate = 1;
+ break;
+ case WPA_CTRL_REQ_SIM:
+ str_clear_free(eap->external_sim_resp);
+ eap->external_sim_resp = os_strdup(value);
+ break;
+ case WPA_CTRL_REQ_PSK_PASSPHRASE:
+ if (wpa_config_set(ssid, "psk", value, 0) < 0)
+ return -1;
+ ssid->mem_only_psk = 1;
+ if (ssid->passphrase)
+ wpa_config_update_psk(ssid);
+ if (wpa_s->wpa_state == WPA_SCANNING && !wpa_s->scanning)
+ wpa_supplicant_req_scan(wpa_s, 0, 0);
+ break;
+ default:
+ wpa_printf(MSG_DEBUG, "CTRL_IFACE: Unknown field '%s'", field);
+ return -1;
+ }
+
+ return 0;
+#else /* IEEE8021X_EAPOL */
+ wpa_printf(MSG_DEBUG, "CTRL_IFACE: IEEE 802.1X not included");
+ return -1;
+#endif /* IEEE8021X_EAPOL */
+}
+#endif /* CONFIG_CTRL_IFACE || CONFIG_CTRL_IFACE_DBUS_NEW */
+
+
+int wpas_network_disabled(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid)
+{
+ int i;
+ unsigned int drv_enc;
+
+ if (wpa_s->p2p_mgmt)
+ return 1; /* no normal network profiles on p2p_mgmt interface */
+
+ if (ssid == NULL)
+ return 1;
+
+ if (ssid->disabled)
+ return 1;
+
+ if (wpa_s->drv_capa_known)
+ drv_enc = wpa_s->drv_enc;
+ else
+ drv_enc = (unsigned int) -1;
+
+ for (i = 0; i < NUM_WEP_KEYS; i++) {
+ size_t len = ssid->wep_key_len[i];
+ if (len == 0)
+ continue;
+ if (len == 5 && (drv_enc & WPA_DRIVER_CAPA_ENC_WEP40))
+ continue;
+ if (len == 13 && (drv_enc & WPA_DRIVER_CAPA_ENC_WEP104))
+ continue;
+ if (len == 16 && (drv_enc & WPA_DRIVER_CAPA_ENC_WEP128))
+ continue;
+ return 1; /* invalid WEP key */
+ }
+
+ if (wpa_key_mgmt_wpa_psk(ssid->key_mgmt) && !ssid->psk_set &&
+ (!ssid->passphrase || ssid->ssid_len != 0) && !ssid->ext_psk &&
+ !ssid->mem_only_psk)
+ return 1;
+
+ return 0;
+}
+
+
+int wpas_get_ssid_pmf(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid)
+{
+#ifdef CONFIG_IEEE80211W
+ if (ssid == NULL || ssid->ieee80211w == MGMT_FRAME_PROTECTION_DEFAULT) {
+ if (wpa_s->conf->pmf == MGMT_FRAME_PROTECTION_OPTIONAL &&
+ !(wpa_s->drv_enc & WPA_DRIVER_CAPA_ENC_BIP)) {
+ /*
+ * Driver does not support BIP -- ignore pmf=1 default
+ * since the connection with PMF would fail and the
+ * configuration does not require PMF to be enabled.
+ */
+ return NO_MGMT_FRAME_PROTECTION;
+ }
+
+ return wpa_s->conf->pmf;
+ }
+
+ return ssid->ieee80211w;
+#else /* CONFIG_IEEE80211W */
+ return NO_MGMT_FRAME_PROTECTION;
+#endif /* CONFIG_IEEE80211W */
+}
+
+
+int wpas_is_p2p_prioritized(struct wpa_supplicant *wpa_s)
+{
+ if (wpa_s->global->conc_pref == WPA_CONC_PREF_P2P)
+ return 1;
+ if (wpa_s->global->conc_pref == WPA_CONC_PREF_STA)
+ return 0;
+ return -1;
+}
+
+
+void wpas_auth_failed(struct wpa_supplicant *wpa_s, char *reason)
+{
+ struct wpa_ssid *ssid = wpa_s->current_ssid;
+ int dur;
+ struct os_reltime now;
+
+ if (ssid == NULL) {
+ wpa_printf(MSG_DEBUG, "Authentication failure but no known "
+ "SSID block");
+ return;
+ }
+
+ if (ssid->key_mgmt == WPA_KEY_MGMT_WPS)
+ return;
+
+ ssid->auth_failures++;
+
+#ifdef CONFIG_P2P
+ if (ssid->p2p_group &&
+ (wpa_s->p2p_in_provisioning || wpa_s->show_group_started)) {
+ /*
+ * Skip the wait time since there is a short timeout on the
+ * connection to a P2P group.
+ */
+ return;
+ }
+#endif /* CONFIG_P2P */
+
+ if (ssid->auth_failures > 50)
+ dur = 300;
+ else if (ssid->auth_failures > 10)
+ dur = 120;
+ else if (ssid->auth_failures > 5)
+ dur = 90;
+ else if (ssid->auth_failures > 3)
+ dur = 60;
+ else if (ssid->auth_failures > 2)
+ dur = 30;
+ else if (ssid->auth_failures > 1)
+ dur = 20;
+ else
+ dur = 10;
+
+ if (ssid->auth_failures > 1 &&
+ wpa_key_mgmt_wpa_ieee8021x(ssid->key_mgmt))
+ dur += os_random() % (ssid->auth_failures * 10);
+
+ os_get_reltime(&now);
+ if (now.sec + dur <= ssid->disabled_until.sec)
+ return;
+
+ ssid->disabled_until.sec = now.sec + dur;
+
+ wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_TEMP_DISABLED
+ "id=%d ssid=\"%s\" auth_failures=%u duration=%d reason=%s",
+ ssid->id, wpa_ssid_txt(ssid->ssid, ssid->ssid_len),
+ ssid->auth_failures, dur, reason);
+}
+
+
+void wpas_clear_temp_disabled(struct wpa_supplicant *wpa_s,
+ struct wpa_ssid *ssid, int clear_failures)
+{
+ if (ssid == NULL)
+ return;
+
+ if (ssid->disabled_until.sec) {
+ wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_REENABLED
+ "id=%d ssid=\"%s\"",
+ ssid->id, wpa_ssid_txt(ssid->ssid, ssid->ssid_len));
+ }
+ ssid->disabled_until.sec = 0;
+ ssid->disabled_until.usec = 0;
+ if (clear_failures)
+ ssid->auth_failures = 0;
+}
+
+
+int disallowed_bssid(struct wpa_supplicant *wpa_s, const u8 *bssid)
+{
+ size_t i;
+
+ if (wpa_s->disallow_aps_bssid == NULL)
+ return 0;
+
+ for (i = 0; i < wpa_s->disallow_aps_bssid_count; i++) {
+ if (os_memcmp(wpa_s->disallow_aps_bssid + i * ETH_ALEN,
+ bssid, ETH_ALEN) == 0)
+ return 1;
+ }
+
+ return 0;
+}
+
+
+int disallowed_ssid(struct wpa_supplicant *wpa_s, const u8 *ssid,
+ size_t ssid_len)
+{
+ size_t i;
+
+ if (wpa_s->disallow_aps_ssid == NULL || ssid == NULL)
+ return 0;
+
+ for (i = 0; i < wpa_s->disallow_aps_ssid_count; i++) {
+ struct wpa_ssid_value *s = &wpa_s->disallow_aps_ssid[i];
+ if (ssid_len == s->ssid_len &&
+ os_memcmp(ssid, s->ssid, ssid_len) == 0)
+ return 1;
+ }
+
+ return 0;
+}
+
+
+/**
+ * wpas_request_connection - Request a new connection
+ * @wpa_s: Pointer to the network interface
+ *
+ * This function is used to request a new connection to be found. It will mark
+ * the interface to allow reassociation and request a new scan to find a
+ * suitable network to connect to.
+ */
+void wpas_request_connection(struct wpa_supplicant *wpa_s)
+{
+ wpa_s->normal_scans = 0;
+ wpa_s->scan_req = NORMAL_SCAN_REQ;
+ wpa_supplicant_reinit_autoscan(wpa_s);
+ wpa_s->extra_blacklist_count = 0;
+ wpa_s->disconnected = 0;
+ wpa_s->reassociate = 1;
+
+ if (wpa_supplicant_fast_associate(wpa_s) != 1)
+ wpa_supplicant_req_scan(wpa_s, 0, 0);
+ else
+ wpa_s->reattach = 0;
+}
+
+
+void dump_freq_data(struct wpa_supplicant *wpa_s, const char *title,
+ struct wpa_used_freq_data *freqs_data,
+ unsigned int len)
+{
+ unsigned int i;
+
+ wpa_dbg(wpa_s, MSG_DEBUG, "Shared frequencies (len=%u): %s",
+ len, title);
+ for (i = 0; i < len; i++) {
+ struct wpa_used_freq_data *cur = &freqs_data[i];
+ wpa_dbg(wpa_s, MSG_DEBUG, "freq[%u]: %d, flags=0x%X",
+ i, cur->freq, cur->flags);
+ }
+}
+
+
+/*
+ * Find the operating frequencies of any of the virtual interfaces that
+ * are using the same radio as the current interface, and in addition, get
+ * information about the interface types that are using the frequency.
+ */
+int get_shared_radio_freqs_data(struct wpa_supplicant *wpa_s,
+ struct wpa_used_freq_data *freqs_data,
+ unsigned int len)
+{
+ struct wpa_supplicant *ifs;
+ u8 bssid[ETH_ALEN];
+ int freq;
+ unsigned int idx = 0, i;
+
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ "Determining shared radio frequencies (max len %u)", len);
+ os_memset(freqs_data, 0, sizeof(struct wpa_used_freq_data) * len);
+
+ dl_list_for_each(ifs, &wpa_s->radio->ifaces, struct wpa_supplicant,
+ radio_list) {
+ if (idx == len)
+ break;
+
+ if (ifs->current_ssid == NULL || ifs->assoc_freq == 0)
+ continue;
+
+ if (ifs->current_ssid->mode == WPAS_MODE_AP ||
+ ifs->current_ssid->mode == WPAS_MODE_P2P_GO ||
+ ifs->current_ssid->mode == WPAS_MODE_MESH)
+ freq = ifs->current_ssid->frequency;
+ else if (wpa_drv_get_bssid(ifs, bssid) == 0)
+ freq = ifs->assoc_freq;
+ else
+ continue;
+
+ /* Hold only distinct freqs */
+ for (i = 0; i < idx; i++)
+ if (freqs_data[i].freq == freq)
+ break;
+
+ if (i == idx)
+ freqs_data[idx++].freq = freq;
+
+ if (ifs->current_ssid->mode == WPAS_MODE_INFRA) {
+ freqs_data[i].flags |= ifs->current_ssid->p2p_group ?
+ WPA_FREQ_USED_BY_P2P_CLIENT :
+ WPA_FREQ_USED_BY_INFRA_STATION;
+ }
+ }
+
+ dump_freq_data(wpa_s, "completed iteration", freqs_data, idx);
+ return idx;
+}
+
+
+/*
+ * Find the operating frequencies of any of the virtual interfaces that
+ * are using the same radio as the current interface.
+ */
+int get_shared_radio_freqs(struct wpa_supplicant *wpa_s,
+ int *freq_array, unsigned int len)
+{
+ struct wpa_used_freq_data *freqs_data;
+ int num, i;
+
+ os_memset(freq_array, 0, sizeof(int) * len);
+
+ freqs_data = os_calloc(len, sizeof(struct wpa_used_freq_data));
+ if (!freqs_data)
+ return -1;
+
+ num = get_shared_radio_freqs_data(wpa_s, freqs_data, len);
+ for (i = 0; i < num; i++)
+ freq_array[i] = freqs_data[i].freq;
+
+ os_free(freqs_data);
+
+ return num;
+}
+
+
+static void wpas_rrm_neighbor_rep_timeout_handler(void *data, void *user_ctx)
+{
+ struct rrm_data *rrm = data;
+
+ if (!rrm->notify_neighbor_rep) {
+ wpa_printf(MSG_ERROR,
+ "RRM: Unexpected neighbor report timeout");
+ return;
+ }
+
+ wpa_printf(MSG_DEBUG, "RRM: Notifying neighbor report - NONE");
+ rrm->notify_neighbor_rep(rrm->neighbor_rep_cb_ctx, NULL);
+
+ rrm->notify_neighbor_rep = NULL;
+ rrm->neighbor_rep_cb_ctx = NULL;
+}
+
+
+/*
+ * wpas_rrm_reset - Clear and reset all RRM data in wpa_supplicant
+ * @wpa_s: Pointer to wpa_supplicant
+ */
+void wpas_rrm_reset(struct wpa_supplicant *wpa_s)
+{
+ wpa_s->rrm.rrm_used = 0;
+
+ eloop_cancel_timeout(wpas_rrm_neighbor_rep_timeout_handler, &wpa_s->rrm,
+ NULL);
+ if (wpa_s->rrm.notify_neighbor_rep)
+ wpas_rrm_neighbor_rep_timeout_handler(&wpa_s->rrm, NULL);
+ wpa_s->rrm.next_neighbor_rep_token = 1;
+}
+
+
+/*
+ * wpas_rrm_process_neighbor_rep - Handle incoming neighbor report
+ * @wpa_s: Pointer to wpa_supplicant
+ * @report: Neighbor report buffer, prefixed by a 1-byte dialog token
+ * @report_len: Length of neighbor report buffer
+ */
+void wpas_rrm_process_neighbor_rep(struct wpa_supplicant *wpa_s,
+ const u8 *report, size_t report_len)
+{
+ struct wpabuf *neighbor_rep;
+
+ wpa_hexdump(MSG_DEBUG, "RRM: New Neighbor Report", report, report_len);
+ if (report_len < 1)
+ return;
+
+ if (report[0] != wpa_s->rrm.next_neighbor_rep_token - 1) {
+ wpa_printf(MSG_DEBUG,
+ "RRM: Discarding neighbor report with token %d (expected %d)",
+ report[0], wpa_s->rrm.next_neighbor_rep_token - 1);
+ return;
+ }
+
+ eloop_cancel_timeout(wpas_rrm_neighbor_rep_timeout_handler, &wpa_s->rrm,
+ NULL);
+
+ if (!wpa_s->rrm.notify_neighbor_rep) {
+ wpa_printf(MSG_ERROR, "RRM: Unexpected neighbor report");
+ return;
+ }
+
+ /* skipping the first byte, which is only an id (dialog token) */
+ neighbor_rep = wpabuf_alloc(report_len - 1);
+ if (neighbor_rep == NULL)
+ return;
+ wpabuf_put_data(neighbor_rep, report + 1, report_len - 1);
+ wpa_printf(MSG_DEBUG, "RRM: Notifying neighbor report (token = %d)",
+ report[0]);
+ wpa_s->rrm.notify_neighbor_rep(wpa_s->rrm.neighbor_rep_cb_ctx,
+ neighbor_rep);
+ wpa_s->rrm.notify_neighbor_rep = NULL;
+ wpa_s->rrm.neighbor_rep_cb_ctx = NULL;
+}
+
+
+#if defined(__CYGWIN__) || defined(CONFIG_NATIVE_WINDOWS)
+/* Workaround different, undefined for Windows, error codes used here */
+#define ENOTCONN -1
+#define EOPNOTSUPP -1
+#define ECANCELED -1
+#endif
+
+/**
+ * wpas_rrm_send_neighbor_rep_request - Request a neighbor report from our AP
+ * @wpa_s: Pointer to wpa_supplicant
+ * @ssid: if not null, this is sent in the request. Otherwise, no SSID IE
+ * is sent in the request.
+ * @cb: Callback function to be called once the requested report arrives, or
+ * timed out after RRM_NEIGHBOR_REPORT_TIMEOUT seconds.
+ * In the former case, 'neighbor_rep' is a newly allocated wpabuf, and it's
+ * the requester's responsibility to free it.
+ * In the latter case NULL will be sent in 'neighbor_rep'.
+ * @cb_ctx: Context value to send the callback function
+ * Returns: 0 in case of success, negative error code otherwise
+ *
+ * In case there is a previous request which has not been answered yet, the
+ * new request fails. The caller may retry after RRM_NEIGHBOR_REPORT_TIMEOUT.
+ * Request must contain a callback function.
+ */
+int wpas_rrm_send_neighbor_rep_request(struct wpa_supplicant *wpa_s,
+ const struct wpa_ssid *ssid,
+ void (*cb)(void *ctx,
+ struct wpabuf *neighbor_rep),
+ void *cb_ctx)
+{
+ struct wpabuf *buf;
+ const u8 *rrm_ie;
+
+ if (wpa_s->wpa_state != WPA_COMPLETED || wpa_s->current_ssid == NULL) {
+ wpa_printf(MSG_DEBUG, "RRM: No connection, no RRM.");
+ return -ENOTCONN;
+ }
+
+ if (!wpa_s->rrm.rrm_used) {
+ wpa_printf(MSG_DEBUG, "RRM: No RRM in current connection.");
+ return -EOPNOTSUPP;
+ }
+
+ rrm_ie = wpa_bss_get_ie(wpa_s->current_bss,
+ WLAN_EID_RRM_ENABLED_CAPABILITIES);
+ if (!rrm_ie || !(wpa_s->current_bss->caps & IEEE80211_CAP_RRM) ||
+ !(rrm_ie[2] & WLAN_RRM_CAPS_NEIGHBOR_REPORT)) {
+ wpa_printf(MSG_DEBUG,
+ "RRM: No network support for Neighbor Report.");
+ return -EOPNOTSUPP;
+ }
+
+ if (!cb) {
+ wpa_printf(MSG_DEBUG,
+ "RRM: Neighbor Report request must provide a callback.");
+ return -EINVAL;
+ }
+
+ /* Refuse if there's a live request */
+ if (wpa_s->rrm.notify_neighbor_rep) {
+ wpa_printf(MSG_DEBUG,
+ "RRM: Currently handling previous Neighbor Report.");
+ return -EBUSY;
+ }
+
+ /* 3 = action category + action code + dialog token */
+ buf = wpabuf_alloc(3 + (ssid ? 2 + ssid->ssid_len : 0));
+ if (buf == NULL) {
+ wpa_printf(MSG_DEBUG,
+ "RRM: Failed to allocate Neighbor Report Request");
+ return -ENOMEM;
+ }
+
+ wpa_printf(MSG_DEBUG, "RRM: Neighbor report request (for %s), token=%d",
+ (ssid ? wpa_ssid_txt(ssid->ssid, ssid->ssid_len) : ""),
+ wpa_s->rrm.next_neighbor_rep_token);
+
+ wpabuf_put_u8(buf, WLAN_ACTION_RADIO_MEASUREMENT);
+ wpabuf_put_u8(buf, WLAN_RRM_NEIGHBOR_REPORT_REQUEST);
+ wpabuf_put_u8(buf, wpa_s->rrm.next_neighbor_rep_token);
+ if (ssid) {
+ wpabuf_put_u8(buf, WLAN_EID_SSID);
+ wpabuf_put_u8(buf, ssid->ssid_len);
+ wpabuf_put_data(buf, ssid->ssid, ssid->ssid_len);
+ }
+
+ wpa_s->rrm.next_neighbor_rep_token++;
+
+ if (wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, wpa_s->bssid,
+ wpa_s->own_addr, wpa_s->bssid,
+ wpabuf_head(buf), wpabuf_len(buf), 0) < 0) {
+ wpa_printf(MSG_DEBUG,
+ "RRM: Failed to send Neighbor Report Request");
+ wpabuf_free(buf);
+ return -ECANCELED;
+ }
+
+ wpa_s->rrm.neighbor_rep_cb_ctx = cb_ctx;
+ wpa_s->rrm.notify_neighbor_rep = cb;
+ eloop_register_timeout(RRM_NEIGHBOR_REPORT_TIMEOUT, 0,
+ wpas_rrm_neighbor_rep_timeout_handler,
+ &wpa_s->rrm, NULL);
+
+ wpabuf_free(buf);
+ return 0;
+}
+
+
+void wpas_rrm_handle_link_measurement_request(struct wpa_supplicant *wpa_s,
+ const u8 *src,
+ const u8 *frame, size_t len,
+ int rssi)
+{
+ struct wpabuf *buf;
+ const struct rrm_link_measurement_request *req;
+ struct rrm_link_measurement_report report;
+
+ if (wpa_s->wpa_state != WPA_COMPLETED) {
+ wpa_printf(MSG_INFO,
+ "RRM: Ignoring link measurement request. Not associated");
+ return;
+ }
+
+ if (!wpa_s->rrm.rrm_used) {
+ wpa_printf(MSG_INFO,
+ "RRM: Ignoring link measurement request. Not RRM network");
+ return;
+ }
+
+ if (!(wpa_s->drv_rrm_flags & WPA_DRIVER_FLAGS_TX_POWER_INSERTION)) {
+ wpa_printf(MSG_INFO,
+ "RRM: Measurement report failed. TX power insertion not supported");
+ return;
+ }
+
+ req = (const struct rrm_link_measurement_request *) frame;
+ if (len < sizeof(*req)) {
+ wpa_printf(MSG_INFO,
+ "RRM: Link measurement report failed. Request too short");
+ return;
+ }
+
+ os_memset(&report, 0, sizeof(report));
+ report.tpc.eid = WLAN_EID_TPC_REPORT;
+ report.tpc.len = 2;
+ report.rsni = 255; /* 255 indicates that RSNI is not available */
+ report.dialog_token = req->dialog_token;
+
+ /*
+ * It's possible to estimate RCPI based on RSSI in dBm. This
+ * calculation will not reflect the correct value for high rates,
+ * but it's good enough for Action frames which are transmitted
+ * with up to 24 Mbps rates.
+ */
+ if (!rssi)
+ report.rcpi = 255; /* not available */
+ else if (rssi < -110)
+ report.rcpi = 0;
+ else if (rssi > 0)
+ report.rcpi = 220;
+ else
+ report.rcpi = (rssi + 110) * 2;
+
+ /* action_category + action_code */
+ buf = wpabuf_alloc(2 + sizeof(report));
+ if (buf == NULL) {
+ wpa_printf(MSG_ERROR,
+ "RRM: Link measurement report failed. Buffer allocation failed");
+ return;
+ }
+
+ wpabuf_put_u8(buf, WLAN_ACTION_RADIO_MEASUREMENT);
+ wpabuf_put_u8(buf, WLAN_RRM_LINK_MEASUREMENT_REPORT);
+ wpabuf_put_data(buf, &report, sizeof(report));
+ wpa_hexdump(MSG_DEBUG, "RRM: Link measurement report:",
+ wpabuf_head(buf), wpabuf_len(buf));
+
+ if (wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, src,
+ wpa_s->own_addr, wpa_s->bssid,
+ wpabuf_head(buf), wpabuf_len(buf), 0)) {
+ wpa_printf(MSG_ERROR,
+ "RRM: Link measurement report failed. Send action failed");
+ }
+ wpabuf_free(buf);
+}
diff --git a/freebsd/contrib/wpa/wpa_supplicant/wpa_supplicant_i.h b/freebsd/contrib/wpa/wpa_supplicant/wpa_supplicant_i.h
new file mode 100644
index 00000000..58df48c5
--- /dev/null
+++ b/freebsd/contrib/wpa/wpa_supplicant/wpa_supplicant_i.h
@@ -0,0 +1,1170 @@
+/*
+ * wpa_supplicant - Internal definitions
+ * Copyright (c) 2003-2014, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef WPA_SUPPLICANT_I_H
+#define WPA_SUPPLICANT_I_H
+
+#include "utils/list.h"
+#include "common/defs.h"
+#include "common/sae.h"
+#include "common/wpa_ctrl.h"
+#include "wps/wps_defs.h"
+#include "config_ssid.h"
+#include "wmm_ac.h"
+
+extern const char *const wpa_supplicant_version;
+extern const char *const wpa_supplicant_license;
+#ifndef CONFIG_NO_STDOUT_DEBUG
+extern const char *const wpa_supplicant_full_license1;
+extern const char *const wpa_supplicant_full_license2;
+extern const char *const wpa_supplicant_full_license3;
+extern const char *const wpa_supplicant_full_license4;
+extern const char *const wpa_supplicant_full_license5;
+#endif /* CONFIG_NO_STDOUT_DEBUG */
+
+struct wpa_sm;
+struct wpa_supplicant;
+struct ibss_rsn;
+struct scan_info;
+struct wpa_bss;
+struct wpa_scan_results;
+struct hostapd_hw_modes;
+struct wpa_driver_associate_params;
+
+/*
+ * Forward declarations of private structures used within the ctrl_iface
+ * backends. Other parts of wpa_supplicant do not have access to data stored in
+ * these structures.
+ */
+struct ctrl_iface_priv;
+struct ctrl_iface_global_priv;
+struct wpas_dbus_priv;
+
+/**
+ * struct wpa_interface - Parameters for wpa_supplicant_add_iface()
+ */
+struct wpa_interface {
+ /**
+ * confname - Configuration name (file or profile) name
+ *
+ * This can also be %NULL when a configuration file is not used. In
+ * that case, ctrl_interface must be set to allow the interface to be
+ * configured.
+ */
+ const char *confname;
+
+ /**
+ * confanother - Additional configuration name (file or profile) name
+ *
+ * This can also be %NULL when the additional configuration file is not
+ * used.
+ */
+ const char *confanother;
+
+ /**
+ * ctrl_interface - Control interface parameter
+ *
+ * If a configuration file is not used, this variable can be used to
+ * set the ctrl_interface parameter that would have otherwise been read
+ * from the configuration file. If both confname and ctrl_interface are
+ * set, ctrl_interface is used to override the value from configuration
+ * file.
+ */
+ const char *ctrl_interface;
+
+ /**
+ * driver - Driver interface name, or %NULL to use the default driver
+ */
+ const char *driver;
+
+ /**
+ * driver_param - Driver interface parameters
+ *
+ * If a configuration file is not used, this variable can be used to
+ * set the driver_param parameters that would have otherwise been read
+ * from the configuration file. If both confname and driver_param are
+ * set, driver_param is used to override the value from configuration
+ * file.
+ */
+ const char *driver_param;
+
+ /**
+ * ifname - Interface name
+ */
+ const char *ifname;
+
+ /**
+ * bridge_ifname - Optional bridge interface name
+ *
+ * If the driver interface (ifname) is included in a Linux bridge
+ * device, the bridge interface may need to be used for receiving EAPOL
+ * frames. This can be enabled by setting this variable to enable
+ * receiving of EAPOL frames from an additional interface.
+ */
+ const char *bridge_ifname;
+
+ /**
+ * p2p_mgmt - Interface used for P2P management (P2P Device operations)
+ *
+ * Indicates whether wpas_p2p_init() must be called for this interface.
+ * This is used only when the driver supports a dedicated P2P Device
+ * interface that is not a network interface.
+ */
+ int p2p_mgmt;
+};
+
+/**
+ * struct wpa_params - Parameters for wpa_supplicant_init()
+ */
+struct wpa_params {
+ /**
+ * daemonize - Run %wpa_supplicant in the background
+ */
+ int daemonize;
+
+ /**
+ * wait_for_monitor - Wait for a monitor program before starting
+ */
+ int wait_for_monitor;
+
+ /**
+ * pid_file - Path to a PID (process ID) file
+ *
+ * If this and daemonize are set, process ID of the background process
+ * will be written to the specified file.
+ */
+ char *pid_file;
+
+ /**
+ * wpa_debug_level - Debugging verbosity level (e.g., MSG_INFO)
+ */
+ int wpa_debug_level;
+
+ /**
+ * wpa_debug_show_keys - Whether keying material is included in debug
+ *
+ * This parameter can be used to allow keying material to be included
+ * in debug messages. This is a security risk and this option should
+ * not be enabled in normal configuration. If needed during
+ * development or while troubleshooting, this option can provide more
+ * details for figuring out what is happening.
+ */
+ int wpa_debug_show_keys;
+
+ /**
+ * wpa_debug_timestamp - Whether to include timestamp in debug messages
+ */
+ int wpa_debug_timestamp;
+
+ /**
+ * ctrl_interface - Global ctrl_iface path/parameter
+ */
+ char *ctrl_interface;
+
+ /**
+ * ctrl_interface_group - Global ctrl_iface group
+ */
+ char *ctrl_interface_group;
+
+ /**
+ * dbus_ctrl_interface - Enable the DBus control interface
+ */
+ int dbus_ctrl_interface;
+
+ /**
+ * wpa_debug_file_path - Path of debug file or %NULL to use stdout
+ */
+ const char *wpa_debug_file_path;
+
+ /**
+ * wpa_debug_syslog - Enable log output through syslog
+ */
+ int wpa_debug_syslog;
+
+ /**
+ * wpa_debug_tracing - Enable log output through Linux tracing
+ */
+ int wpa_debug_tracing;
+
+ /**
+ * override_driver - Optional driver parameter override
+ *
+ * This parameter can be used to override the driver parameter in
+ * dynamic interface addition to force a specific driver wrapper to be
+ * used instead.
+ */
+ char *override_driver;
+
+ /**
+ * override_ctrl_interface - Optional ctrl_interface override
+ *
+ * This parameter can be used to override the ctrl_interface parameter
+ * in dynamic interface addition to force a control interface to be
+ * created.
+ */
+ char *override_ctrl_interface;
+
+ /**
+ * entropy_file - Optional entropy file
+ *
+ * This parameter can be used to configure wpa_supplicant to maintain
+ * its internal entropy store over restarts.
+ */
+ char *entropy_file;
+
+#ifdef CONFIG_P2P
+ /**
+ * conf_p2p_dev - Configuration file used to hold the
+ * P2P Device configuration parameters.
+ *
+ * This can also be %NULL. In such a case, if a P2P Device dedicated
+ * interfaces is created, the main configuration file will be used.
+ */
+ char *conf_p2p_dev;
+#endif /* CONFIG_P2P */
+
+};
+
+struct p2p_srv_bonjour {
+ struct dl_list list;
+ struct wpabuf *query;
+ struct wpabuf *resp;
+};
+
+struct p2p_srv_upnp {
+ struct dl_list list;
+ u8 version;
+ char *service;
+};
+
+/**
+ * struct wpa_global - Internal, global data for all %wpa_supplicant interfaces
+ *
+ * This structure is initialized by calling wpa_supplicant_init() when starting
+ * %wpa_supplicant.
+ */
+struct wpa_global {
+ struct wpa_supplicant *ifaces;
+ struct wpa_params params;
+ struct ctrl_iface_global_priv *ctrl_iface;
+ struct wpas_dbus_priv *dbus;
+ void **drv_priv;
+ size_t drv_count;
+ struct os_time suspend_time;
+ struct p2p_data *p2p;
+ struct wpa_supplicant *p2p_init_wpa_s;
+ struct wpa_supplicant *p2p_group_formation;
+ struct wpa_supplicant *p2p_invite_group;
+ u8 p2p_dev_addr[ETH_ALEN];
+ struct os_reltime p2p_go_wait_client;
+ struct dl_list p2p_srv_bonjour; /* struct p2p_srv_bonjour */
+ struct dl_list p2p_srv_upnp; /* struct p2p_srv_upnp */
+ int p2p_disabled;
+ int cross_connection;
+ struct wpa_freq_range_list p2p_disallow_freq;
+ struct wpa_freq_range_list p2p_go_avoid_freq;
+ enum wpa_conc_pref {
+ WPA_CONC_PREF_NOT_SET,
+ WPA_CONC_PREF_STA,
+ WPA_CONC_PREF_P2P
+ } conc_pref;
+ unsigned int p2p_per_sta_psk:1;
+ unsigned int p2p_fail_on_wps_complete:1;
+ unsigned int p2p_24ghz_social_channels:1;
+ unsigned int pending_p2ps_group:1;
+ unsigned int pending_group_iface_for_p2ps:1;
+
+#ifdef CONFIG_WIFI_DISPLAY
+ int wifi_display;
+#define MAX_WFD_SUBELEMS 10
+ struct wpabuf *wfd_subelem[MAX_WFD_SUBELEMS];
+#endif /* CONFIG_WIFI_DISPLAY */
+
+ struct psk_list_entry *add_psk; /* From group formation */
+};
+
+
+/**
+ * struct wpa_radio - Internal data for per-radio information
+ *
+ * This structure is used to share data about configured interfaces
+ * (struct wpa_supplicant) that share the same physical radio, e.g., to allow
+ * better coordination of offchannel operations.
+ */
+struct wpa_radio {
+ char name[16]; /* from driver_ops get_radio_name() or empty if not
+ * available */
+ unsigned int external_scan_running:1;
+ struct dl_list ifaces; /* struct wpa_supplicant::radio_list entries */
+ struct dl_list work; /* struct wpa_radio_work::list entries */
+};
+
+/**
+ * struct wpa_radio_work - Radio work item
+ */
+struct wpa_radio_work {
+ struct dl_list list;
+ unsigned int freq; /* known frequency (MHz) or 0 for multiple/unknown */
+ const char *type;
+ struct wpa_supplicant *wpa_s;
+ void (*cb)(struct wpa_radio_work *work, int deinit);
+ void *ctx;
+ unsigned int started:1;
+ struct os_reltime time;
+};
+
+int radio_add_work(struct wpa_supplicant *wpa_s, unsigned int freq,
+ const char *type, int next,
+ void (*cb)(struct wpa_radio_work *work, int deinit),
+ void *ctx);
+void radio_work_done(struct wpa_radio_work *work);
+void radio_remove_works(struct wpa_supplicant *wpa_s,
+ const char *type, int remove_all);
+void radio_work_check_next(struct wpa_supplicant *wpa_s);
+struct wpa_radio_work *
+radio_work_pending(struct wpa_supplicant *wpa_s, const char *type);
+
+struct wpa_connect_work {
+ unsigned int sme:1;
+ unsigned int bss_removed:1;
+ struct wpa_bss *bss;
+ struct wpa_ssid *ssid;
+};
+
+int wpas_valid_bss_ssid(struct wpa_supplicant *wpa_s, struct wpa_bss *test_bss,
+ struct wpa_ssid *test_ssid);
+void wpas_connect_work_free(struct wpa_connect_work *cwork);
+void wpas_connect_work_done(struct wpa_supplicant *wpa_s);
+
+struct wpa_external_work {
+ unsigned int id;
+ char type[100];
+ unsigned int timeout;
+};
+
+/**
+ * offchannel_send_action_result - Result of offchannel send Action frame
+ */
+enum offchannel_send_action_result {
+ OFFCHANNEL_SEND_ACTION_SUCCESS /**< Frame was send and acknowledged */,
+ OFFCHANNEL_SEND_ACTION_NO_ACK /**< Frame was sent, but not acknowledged
+ */,
+ OFFCHANNEL_SEND_ACTION_FAILED /**< Frame was not sent due to a failure
+ */
+};
+
+struct wps_ap_info {
+ u8 bssid[ETH_ALEN];
+ enum wps_ap_info_type {
+ WPS_AP_NOT_SEL_REG,
+ WPS_AP_SEL_REG,
+ WPS_AP_SEL_REG_OUR
+ } type;
+ unsigned int tries;
+ struct os_reltime last_attempt;
+ unsigned int pbc_active;
+ u8 uuid[WPS_UUID_LEN];
+};
+
+struct wpa_ssid_value {
+ u8 ssid[SSID_MAX_LEN];
+ size_t ssid_len;
+};
+
+#define WPA_FREQ_USED_BY_INFRA_STATION BIT(0)
+#define WPA_FREQ_USED_BY_P2P_CLIENT BIT(1)
+
+struct wpa_used_freq_data {
+ int freq;
+ unsigned int flags;
+};
+
+#define RRM_NEIGHBOR_REPORT_TIMEOUT 1 /* 1 second for AP to send a report */
+
+/*
+ * struct rrm_data - Data used for managing RRM features
+ */
+struct rrm_data {
+ /* rrm_used - indication regarding the current connection */
+ unsigned int rrm_used:1;
+
+ /*
+ * notify_neighbor_rep - Callback for notifying report requester
+ */
+ void (*notify_neighbor_rep)(void *ctx, struct wpabuf *neighbor_rep);
+
+ /*
+ * neighbor_rep_cb_ctx - Callback context
+ * Received in the callback registration, and sent to the callback
+ * function as a parameter.
+ */
+ void *neighbor_rep_cb_ctx;
+
+ /* next_neighbor_rep_token - Next request's dialog token */
+ u8 next_neighbor_rep_token;
+};
+
+enum wpa_supplicant_test_failure {
+ WPAS_TEST_FAILURE_NONE,
+ WPAS_TEST_FAILURE_SCAN_TRIGGER,
+};
+
+/**
+ * struct wpa_supplicant - Internal data for wpa_supplicant interface
+ *
+ * This structure contains the internal data for core wpa_supplicant code. This
+ * should be only used directly from the core code. However, a pointer to this
+ * data is used from other files as an arbitrary context pointer in calls to
+ * core functions.
+ */
+struct wpa_supplicant {
+ struct wpa_global *global;
+ struct wpa_radio *radio; /* shared radio context */
+ struct dl_list radio_list; /* list head: struct wpa_radio::ifaces */
+ struct wpa_supplicant *parent;
+ struct wpa_supplicant *next;
+ struct l2_packet_data *l2;
+ struct l2_packet_data *l2_br;
+ unsigned char own_addr[ETH_ALEN];
+ unsigned char perm_addr[ETH_ALEN];
+ char ifname[100];
+#ifdef CONFIG_CTRL_IFACE_DBUS
+ char *dbus_path;
+#endif /* CONFIG_CTRL_IFACE_DBUS */
+#ifdef CONFIG_CTRL_IFACE_DBUS_NEW
+ char *dbus_new_path;
+ char *dbus_groupobj_path;
+#ifdef CONFIG_AP
+ char *preq_notify_peer;
+#endif /* CONFIG_AP */
+#endif /* CONFIG_CTRL_IFACE_DBUS_NEW */
+ char bridge_ifname[16];
+
+ char *confname;
+ char *confanother;
+
+ struct wpa_config *conf;
+ int countermeasures;
+ struct os_reltime last_michael_mic_error;
+ u8 bssid[ETH_ALEN];
+ u8 pending_bssid[ETH_ALEN]; /* If wpa_state == WPA_ASSOCIATING, this
+ * field contains the target BSSID. */
+ int reassociate; /* reassociation requested */
+ int reassoc_same_bss; /* reassociating to the same bss */
+ int disconnected; /* all connections disabled; i.e., do no reassociate
+ * before this has been cleared */
+ struct wpa_ssid *current_ssid;
+ struct wpa_ssid *last_ssid;
+ struct wpa_bss *current_bss;
+ int ap_ies_from_associnfo;
+ unsigned int assoc_freq;
+
+ /* Selected configuration (based on Beacon/ProbeResp WPA IE) */
+ int pairwise_cipher;
+ int group_cipher;
+ int key_mgmt;
+ int wpa_proto;
+ int mgmt_group_cipher;
+
+ void *drv_priv; /* private data used by driver_ops */
+ void *global_drv_priv;
+
+ u8 *bssid_filter;
+ size_t bssid_filter_count;
+
+ u8 *disallow_aps_bssid;
+ size_t disallow_aps_bssid_count;
+ struct wpa_ssid_value *disallow_aps_ssid;
+ size_t disallow_aps_ssid_count;
+
+ enum set_band setband;
+
+ /* Preferred network for the next connection attempt */
+ struct wpa_ssid *next_ssid;
+
+ /* previous scan was wildcard when interleaving between
+ * wildcard scans and specific SSID scan when max_ssids=1 */
+ int prev_scan_wildcard;
+ struct wpa_ssid *prev_scan_ssid; /* previously scanned SSID;
+ * NULL = not yet initialized (start
+ * with wildcard SSID)
+ * WILDCARD_SSID_SCAN = wildcard
+ * SSID was used in the previous scan
+ */
+#define WILDCARD_SSID_SCAN ((struct wpa_ssid *) 1)
+
+ struct wpa_ssid *prev_sched_ssid; /* last SSID used in sched scan */
+ int sched_scan_timeout;
+ int sched_scan_interval;
+ int first_sched_scan;
+ int sched_scan_timed_out;
+
+ void (*scan_res_handler)(struct wpa_supplicant *wpa_s,
+ struct wpa_scan_results *scan_res);
+ struct dl_list bss; /* struct wpa_bss::list */
+ struct dl_list bss_id; /* struct wpa_bss::list_id */
+ size_t num_bss;
+ unsigned int bss_update_idx;
+ unsigned int bss_next_id;
+
+ /*
+ * Pointers to BSS entries in the order they were in the last scan
+ * results.
+ */
+ struct wpa_bss **last_scan_res;
+ unsigned int last_scan_res_used;
+ unsigned int last_scan_res_size;
+ struct os_reltime last_scan;
+
+ const struct wpa_driver_ops *driver;
+ int interface_removed; /* whether the network interface has been
+ * removed */
+ struct wpa_sm *wpa;
+ struct eapol_sm *eapol;
+
+ struct ctrl_iface_priv *ctrl_iface;
+
+ enum wpa_states wpa_state;
+ struct wpa_radio_work *scan_work;
+ int scanning;
+ int sched_scanning;
+ int new_connection;
+
+ int eapol_received; /* number of EAPOL packets received after the
+ * previous association event */
+
+ struct scard_data *scard;
+ char imsi[20];
+ int mnc_len;
+
+ unsigned char last_eapol_src[ETH_ALEN];
+
+ unsigned int keys_cleared; /* bitfield of key indexes that the driver is
+ * known not to be configured with a key */
+
+ struct wpa_blacklist *blacklist;
+
+ /**
+ * extra_blacklist_count - Sum of blacklist counts after last connection
+ *
+ * This variable is used to maintain a count of temporary blacklisting
+ * failures (maximum number for any BSS) over blacklist clear
+ * operations. This is needed for figuring out whether there has been
+ * failures prior to the last blacklist clear operation which happens
+ * whenever no other not-blacklisted BSS candidates are available. This
+ * gets cleared whenever a connection has been established successfully.
+ */
+ int extra_blacklist_count;
+
+ /**
+ * scan_req - Type of the scan request
+ */
+ enum scan_req_type {
+ /**
+ * NORMAL_SCAN_REQ - Normal scan request
+ *
+ * This is used for scans initiated by wpa_supplicant to find an
+ * AP for a connection.
+ */
+ NORMAL_SCAN_REQ,
+
+ /**
+ * INITIAL_SCAN_REQ - Initial scan request
+ *
+ * This is used for the first scan on an interface to force at
+ * least one scan to be run even if the configuration does not
+ * include any enabled networks.
+ */
+ INITIAL_SCAN_REQ,
+
+ /**
+ * MANUAL_SCAN_REQ - Manual scan request
+ *
+ * This is used for scans where the user request a scan or
+ * a specific wpa_supplicant operation (e.g., WPS) requires scan
+ * to be run.
+ */
+ MANUAL_SCAN_REQ
+ } scan_req, last_scan_req;
+ enum wpa_states scan_prev_wpa_state;
+ struct os_reltime scan_trigger_time, scan_start_time;
+ /* Minimum freshness requirement for connection purposes */
+ struct os_reltime scan_min_time;
+ int scan_runs; /* number of scan runs since WPS was started */
+ int *next_scan_freqs;
+ int *manual_scan_freqs;
+ int *manual_sched_scan_freqs;
+ unsigned int manual_scan_passive:1;
+ unsigned int manual_scan_use_id:1;
+ unsigned int manual_scan_only_new:1;
+ unsigned int own_scan_requested:1;
+ unsigned int own_scan_running:1;
+ unsigned int clear_driver_scan_cache:1;
+ unsigned int manual_scan_id;
+ int scan_interval; /* time in sec between scans to find suitable AP */
+ int normal_scans; /* normal scans run before sched_scan */
+ int scan_for_connection; /* whether the scan request was triggered for
+ * finding a connection */
+#define MAX_SCAN_ID 16
+ int scan_id[MAX_SCAN_ID];
+ unsigned int scan_id_count;
+
+ struct wpa_ssid_value *ssids_from_scan_req;
+ unsigned int num_ssids_from_scan_req;
+
+ u64 drv_flags;
+ unsigned int drv_enc;
+ unsigned int drv_smps_modes;
+ unsigned int drv_rrm_flags;
+
+ /*
+ * A bitmap of supported protocols for probe response offload. See
+ * struct wpa_driver_capa in driver.h
+ */
+ unsigned int probe_resp_offloads;
+
+ /* extended capabilities supported by the driver */
+ const u8 *extended_capa, *extended_capa_mask;
+ unsigned int extended_capa_len;
+
+ int max_scan_ssids;
+ int max_sched_scan_ssids;
+ int sched_scan_supported;
+ unsigned int max_match_sets;
+ unsigned int max_remain_on_chan;
+ unsigned int max_stations;
+
+ int pending_mic_error_report;
+ int pending_mic_error_pairwise;
+ int mic_errors_seen; /* Michael MIC errors with the current PTK */
+
+ struct wps_context *wps;
+ int wps_success; /* WPS success event received */
+ struct wps_er *wps_er;
+ unsigned int wps_run;
+ struct os_reltime wps_pin_start_time;
+ int blacklist_cleared;
+
+ struct wpabuf *pending_eapol_rx;
+ struct os_reltime pending_eapol_rx_time;
+ u8 pending_eapol_rx_src[ETH_ALEN];
+ unsigned int last_eapol_matches_bssid:1;
+ unsigned int eap_expected_failure:1;
+ unsigned int reattach:1; /* reassociation to the same BSS requested */
+ unsigned int mac_addr_changed:1;
+ unsigned int added_vif:1;
+
+ struct os_reltime last_mac_addr_change;
+ int last_mac_addr_style;
+
+ struct ibss_rsn *ibss_rsn;
+
+ int set_sta_uapsd;
+ int sta_uapsd;
+ int set_ap_uapsd;
+ int ap_uapsd;
+
+#ifdef CONFIG_SME
+ struct {
+ u8 ssid[SSID_MAX_LEN];
+ size_t ssid_len;
+ int freq;
+ u8 assoc_req_ie[200];
+ size_t assoc_req_ie_len;
+ int mfp;
+ int ft_used;
+ u8 mobility_domain[2];
+ u8 *ft_ies;
+ size_t ft_ies_len;
+ u8 prev_bssid[ETH_ALEN];
+ int prev_bssid_set;
+ int auth_alg;
+ int proto;
+
+ int sa_query_count; /* number of pending SA Query requests;
+ * 0 = no SA Query in progress */
+ int sa_query_timed_out;
+ u8 *sa_query_trans_id; /* buffer of WLAN_SA_QUERY_TR_ID_LEN *
+ * sa_query_count octets of pending
+ * SA Query transaction identifiers */
+ struct os_reltime sa_query_start;
+ struct os_reltime last_unprot_disconnect;
+ enum { HT_SEC_CHAN_UNKNOWN,
+ HT_SEC_CHAN_ABOVE,
+ HT_SEC_CHAN_BELOW } ht_sec_chan;
+ u8 sched_obss_scan;
+ u16 obss_scan_int;
+ u16 bss_max_idle_period;
+#ifdef CONFIG_SAE
+ struct sae_data sae;
+ struct wpabuf *sae_token;
+ int sae_group_index;
+ unsigned int sae_pmksa_caching:1;
+#endif /* CONFIG_SAE */
+ } sme;
+#endif /* CONFIG_SME */
+
+#ifdef CONFIG_AP
+ struct hostapd_iface *ap_iface;
+ void (*ap_configured_cb)(void *ctx, void *data);
+ void *ap_configured_cb_ctx;
+ void *ap_configured_cb_data;
+#endif /* CONFIG_AP */
+
+ struct hostapd_iface *ifmsh;
+#ifdef CONFIG_MESH
+ struct mesh_rsn *mesh_rsn;
+ int mesh_if_idx;
+ unsigned int mesh_if_created:1;
+ unsigned int mesh_ht_enabled:1;
+ int mesh_auth_block_duration; /* sec */
+#endif /* CONFIG_MESH */
+
+ unsigned int off_channel_freq;
+ struct wpabuf *pending_action_tx;
+ u8 pending_action_src[ETH_ALEN];
+ u8 pending_action_dst[ETH_ALEN];
+ u8 pending_action_bssid[ETH_ALEN];
+ unsigned int pending_action_freq;
+ int pending_action_no_cck;
+ int pending_action_without_roc;
+ unsigned int pending_action_tx_done:1;
+ void (*pending_action_tx_status_cb)(struct wpa_supplicant *wpa_s,
+ unsigned int freq, const u8 *dst,
+ const u8 *src, const u8 *bssid,
+ const u8 *data, size_t data_len,
+ enum offchannel_send_action_result
+ result);
+ unsigned int roc_waiting_drv_freq;
+ int action_tx_wait_time;
+
+ int p2p_mgmt;
+
+#ifdef CONFIG_P2P
+ struct p2p_go_neg_results *go_params;
+ int create_p2p_iface;
+ u8 pending_interface_addr[ETH_ALEN];
+ char pending_interface_name[100];
+ int pending_interface_type;
+ int p2p_group_idx;
+ unsigned int pending_listen_freq;
+ unsigned int pending_listen_duration;
+ enum {
+ NOT_P2P_GROUP_INTERFACE,
+ P2P_GROUP_INTERFACE_PENDING,
+ P2P_GROUP_INTERFACE_GO,
+ P2P_GROUP_INTERFACE_CLIENT
+ } p2p_group_interface;
+ struct p2p_group *p2p_group;
+ int p2p_long_listen; /* remaining time in long Listen state in ms */
+ char p2p_pin[10];
+ int p2p_wps_method;
+ u8 p2p_auth_invite[ETH_ALEN];
+ int p2p_sd_over_ctrl_iface;
+ int p2p_in_provisioning;
+ int p2p_in_invitation;
+ int p2p_invite_go_freq;
+ int pending_invite_ssid_id;
+ int show_group_started;
+ u8 go_dev_addr[ETH_ALEN];
+ int pending_pd_before_join;
+ u8 pending_join_iface_addr[ETH_ALEN];
+ u8 pending_join_dev_addr[ETH_ALEN];
+ int pending_join_wps_method;
+ u8 p2p_join_ssid[SSID_MAX_LEN];
+ size_t p2p_join_ssid_len;
+ int p2p_join_scan_count;
+ int auto_pd_scan_retry;
+ int force_long_sd;
+ u16 pending_pd_config_methods;
+ enum {
+ NORMAL_PD, AUTO_PD_GO_NEG, AUTO_PD_JOIN, AUTO_PD_ASP
+ } pending_pd_use;
+
+ /*
+ * Whether cross connection is disallowed by the AP to which this
+ * interface is associated (only valid if there is an association).
+ */
+ int cross_connect_disallowed;
+
+ /*
+ * Whether this P2P group is configured to use cross connection (only
+ * valid if this is P2P GO interface). The actual cross connect packet
+ * forwarding may not be configured depending on the uplink status.
+ */
+ int cross_connect_enabled;
+
+ /* Whether cross connection forwarding is in use at the moment. */
+ int cross_connect_in_use;
+
+ /*
+ * Uplink interface name for cross connection
+ */
+ char cross_connect_uplink[100];
+
+ unsigned int p2p_auto_join:1;
+ unsigned int p2p_auto_pd:1;
+ unsigned int p2p_persistent_group:1;
+ unsigned int p2p_fallback_to_go_neg:1;
+ unsigned int p2p_pd_before_go_neg:1;
+ unsigned int p2p_go_ht40:1;
+ unsigned int p2p_go_vht:1;
+ unsigned int user_initiated_pd:1;
+ unsigned int p2p_go_group_formation_completed:1;
+ unsigned int group_formation_reported:1;
+ unsigned int waiting_presence_resp;
+ int p2p_first_connection_timeout;
+ unsigned int p2p_nfc_tag_enabled:1;
+ unsigned int p2p_peer_oob_pk_hash_known:1;
+ unsigned int p2p_disable_ip_addr_req:1;
+ unsigned int p2ps_method_config_any:1;
+ unsigned int p2p_cli_probe:1;
+ int p2p_persistent_go_freq;
+ int p2p_persistent_id;
+ int p2p_go_intent;
+ int p2p_connect_freq;
+ struct os_reltime p2p_auto_started;
+ struct wpa_ssid *p2p_last_4way_hs_fail;
+ struct wpa_radio_work *p2p_scan_work;
+ struct wpa_radio_work *p2p_listen_work;
+ struct wpa_radio_work *p2p_send_action_work;
+
+ u16 p2p_oob_dev_pw_id; /* OOB Device Password Id for group formation */
+ struct wpabuf *p2p_oob_dev_pw; /* OOB Device Password for group
+ * formation */
+ u8 p2p_peer_oob_pubkey_hash[WPS_OOB_PUBKEY_HASH_LEN];
+ u8 p2p_ip_addr_info[3 * 4];
+
+ /* group common frequencies */
+ int *p2p_group_common_freqs;
+ unsigned int p2p_group_common_freqs_num;
+ u8 p2ps_join_addr[ETH_ALEN];
+#endif /* CONFIG_P2P */
+
+ struct wpa_ssid *bgscan_ssid;
+ const struct bgscan_ops *bgscan;
+ void *bgscan_priv;
+
+ const struct autoscan_ops *autoscan;
+ struct wpa_driver_scan_params *autoscan_params;
+ void *autoscan_priv;
+
+ struct wpa_ssid *connect_without_scan;
+
+ struct wps_ap_info *wps_ap;
+ size_t num_wps_ap;
+ int wps_ap_iter;
+
+ int after_wps;
+ int known_wps_freq;
+ unsigned int wps_freq;
+ int wps_fragment_size;
+ int auto_reconnect_disabled;
+
+ /* Channel preferences for AP/P2P GO use */
+ int best_24_freq;
+ int best_5_freq;
+ int best_overall_freq;
+
+ struct gas_query *gas;
+
+#ifdef CONFIG_INTERWORKING
+ unsigned int fetch_anqp_in_progress:1;
+ unsigned int network_select:1;
+ unsigned int auto_select:1;
+ unsigned int auto_network_select:1;
+ unsigned int interworking_fast_assoc_tried:1;
+ unsigned int fetch_all_anqp:1;
+ unsigned int fetch_osu_info:1;
+ unsigned int fetch_osu_waiting_scan:1;
+ unsigned int fetch_osu_icon_in_progress:1;
+ struct wpa_bss *interworking_gas_bss;
+ unsigned int osu_icon_id;
+ struct osu_provider *osu_prov;
+ size_t osu_prov_count;
+ struct os_reltime osu_icon_fetch_start;
+ unsigned int num_osu_scans;
+ unsigned int num_prov_found;
+#endif /* CONFIG_INTERWORKING */
+ unsigned int drv_capa_known;
+
+ struct {
+ struct hostapd_hw_modes *modes;
+ u16 num_modes;
+ u16 flags;
+ } hw;
+ enum local_hw_capab {
+ CAPAB_NO_HT_VHT,
+ CAPAB_HT,
+ CAPAB_HT40,
+ CAPAB_VHT,
+ } hw_capab;
+#ifdef CONFIG_MACSEC
+ struct ieee802_1x_kay *kay;
+#endif /* CONFIG_MACSEC */
+
+ int pno;
+ int pno_sched_pending;
+
+ /* WLAN_REASON_* reason codes. Negative if locally generated. */
+ int disconnect_reason;
+
+ struct ext_password_data *ext_pw;
+
+ struct wpabuf *last_gas_resp, *prev_gas_resp;
+ u8 last_gas_addr[ETH_ALEN], prev_gas_addr[ETH_ALEN];
+ u8 last_gas_dialog_token, prev_gas_dialog_token;
+
+ unsigned int no_keep_alive:1;
+ unsigned int ext_mgmt_frame_handling:1;
+ unsigned int ext_eapol_frame_io:1;
+ unsigned int wmm_ac_supported:1;
+ unsigned int ext_work_in_progress:1;
+ unsigned int own_disconnect_req:1;
+
+#define MAC_ADDR_RAND_SCAN BIT(0)
+#define MAC_ADDR_RAND_SCHED_SCAN BIT(1)
+#define MAC_ADDR_RAND_PNO BIT(2)
+#define MAC_ADDR_RAND_ALL (MAC_ADDR_RAND_SCAN | \
+ MAC_ADDR_RAND_SCHED_SCAN | \
+ MAC_ADDR_RAND_PNO)
+ unsigned int mac_addr_rand_supported;
+ unsigned int mac_addr_rand_enable;
+
+ /* MAC Address followed by mask (2 * ETH_ALEN) */
+ u8 *mac_addr_scan;
+ u8 *mac_addr_sched_scan;
+ u8 *mac_addr_pno;
+
+#ifdef CONFIG_WNM
+ u8 wnm_dialog_token;
+ u8 wnm_reply;
+ u8 wnm_num_neighbor_report;
+ u8 wnm_mode;
+ u16 wnm_dissoc_timer;
+ u8 wnm_bss_termination_duration[12];
+ struct neighbor_report *wnm_neighbor_report_elements;
+ struct os_reltime wnm_cand_valid_until;
+ u8 wnm_cand_from_bss[ETH_ALEN];
+#endif /* CONFIG_WNM */
+
+#ifdef CONFIG_TESTING_GET_GTK
+ u8 last_gtk[32];
+ size_t last_gtk_len;
+#endif /* CONFIG_TESTING_GET_GTK */
+
+ unsigned int num_multichan_concurrent;
+ struct wpa_radio_work *connect_work;
+
+ unsigned int ext_work_id;
+
+ struct wpabuf *vendor_elem[NUM_VENDOR_ELEM_FRAMES];
+
+#ifdef CONFIG_TESTING_OPTIONS
+ struct l2_packet_data *l2_test;
+ unsigned int extra_roc_dur;
+ enum wpa_supplicant_test_failure test_failure;
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ struct wmm_ac_assoc_data *wmm_ac_assoc_info;
+ struct wmm_tspec_element *tspecs[WMM_AC_NUM][TS_DIR_IDX_COUNT];
+ struct wmm_ac_addts_request *addts_request;
+ u8 wmm_ac_last_dialog_token;
+ struct wmm_tspec_element *last_tspecs;
+ u8 last_tspecs_count;
+
+ struct rrm_data rrm;
+
+#ifdef CONFIG_FST
+ struct fst_iface *fst;
+ const struct wpabuf *fst_ies;
+ struct wpabuf *received_mb_ies;
+#endif /* CONFIG_FST */
+};
+
+
+/* wpa_supplicant.c */
+void wpa_supplicant_apply_ht_overrides(
+ struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid,
+ struct wpa_driver_associate_params *params);
+void wpa_supplicant_apply_vht_overrides(
+ struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid,
+ struct wpa_driver_associate_params *params);
+
+int wpa_set_wep_keys(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid);
+int wpa_supplicant_set_wpa_none_key(struct wpa_supplicant *wpa_s,
+ struct wpa_ssid *ssid);
+
+int wpa_supplicant_reload_configuration(struct wpa_supplicant *wpa_s);
+
+const char * wpa_supplicant_state_txt(enum wpa_states state);
+int wpa_supplicant_update_mac_addr(struct wpa_supplicant *wpa_s);
+int wpa_supplicant_driver_init(struct wpa_supplicant *wpa_s);
+int wpa_supplicant_set_suites(struct wpa_supplicant *wpa_s,
+ struct wpa_bss *bss, struct wpa_ssid *ssid,
+ u8 *wpa_ie, size_t *wpa_ie_len);
+void wpa_supplicant_associate(struct wpa_supplicant *wpa_s,
+ struct wpa_bss *bss,
+ struct wpa_ssid *ssid);
+void wpa_supplicant_set_non_wpa_policy(struct wpa_supplicant *wpa_s,
+ struct wpa_ssid *ssid);
+void wpa_supplicant_initiate_eapol(struct wpa_supplicant *wpa_s);
+void wpa_clear_keys(struct wpa_supplicant *wpa_s, const u8 *addr);
+void wpa_supplicant_req_auth_timeout(struct wpa_supplicant *wpa_s,
+ int sec, int usec);
+void wpa_supplicant_reinit_autoscan(struct wpa_supplicant *wpa_s);
+void wpa_supplicant_set_state(struct wpa_supplicant *wpa_s,
+ enum wpa_states state);
+struct wpa_ssid * wpa_supplicant_get_ssid(struct wpa_supplicant *wpa_s);
+const char * wpa_supplicant_get_eap_mode(struct wpa_supplicant *wpa_s);
+void wpa_supplicant_cancel_auth_timeout(struct wpa_supplicant *wpa_s);
+void wpa_supplicant_deauthenticate(struct wpa_supplicant *wpa_s,
+ int reason_code);
+
+void wpa_supplicant_enable_network(struct wpa_supplicant *wpa_s,
+ struct wpa_ssid *ssid);
+void wpa_supplicant_disable_network(struct wpa_supplicant *wpa_s,
+ struct wpa_ssid *ssid);
+void wpa_supplicant_select_network(struct wpa_supplicant *wpa_s,
+ struct wpa_ssid *ssid);
+int wpas_set_pkcs11_engine_and_module_path(struct wpa_supplicant *wpa_s,
+ const char *pkcs11_engine_path,
+ const char *pkcs11_module_path);
+int wpa_supplicant_set_ap_scan(struct wpa_supplicant *wpa_s,
+ int ap_scan);
+int wpa_supplicant_set_bss_expiration_age(struct wpa_supplicant *wpa_s,
+ unsigned int expire_age);
+int wpa_supplicant_set_bss_expiration_count(struct wpa_supplicant *wpa_s,
+ unsigned int expire_count);
+int wpa_supplicant_set_scan_interval(struct wpa_supplicant *wpa_s,
+ int scan_interval);
+int wpa_supplicant_set_debug_params(struct wpa_global *global,
+ int debug_level, int debug_timestamp,
+ int debug_show_keys);
+void free_hw_features(struct wpa_supplicant *wpa_s);
+
+void wpa_show_license(void);
+
+struct wpa_supplicant * wpa_supplicant_add_iface(struct wpa_global *global,
+ struct wpa_interface *iface,
+ struct wpa_supplicant *parent);
+int wpa_supplicant_remove_iface(struct wpa_global *global,
+ struct wpa_supplicant *wpa_s,
+ int terminate);
+struct wpa_supplicant * wpa_supplicant_get_iface(struct wpa_global *global,
+ const char *ifname);
+struct wpa_global * wpa_supplicant_init(struct wpa_params *params);
+int wpa_supplicant_run(struct wpa_global *global);
+void wpa_supplicant_deinit(struct wpa_global *global);
+
+int wpa_supplicant_scard_init(struct wpa_supplicant *wpa_s,
+ struct wpa_ssid *ssid);
+void wpa_supplicant_terminate_proc(struct wpa_global *global);
+void wpa_supplicant_rx_eapol(void *ctx, const u8 *src_addr,
+ const u8 *buf, size_t len);
+void wpa_supplicant_update_config(struct wpa_supplicant *wpa_s);
+void wpa_supplicant_clear_status(struct wpa_supplicant *wpa_s);
+void wpas_connection_failed(struct wpa_supplicant *wpa_s, const u8 *bssid);
+int wpas_driver_bss_selection(struct wpa_supplicant *wpa_s);
+int wpas_is_p2p_prioritized(struct wpa_supplicant *wpa_s);
+void wpas_auth_failed(struct wpa_supplicant *wpa_s, char *reason);
+void wpas_clear_temp_disabled(struct wpa_supplicant *wpa_s,
+ struct wpa_ssid *ssid, int clear_failures);
+int disallowed_bssid(struct wpa_supplicant *wpa_s, const u8 *bssid);
+int disallowed_ssid(struct wpa_supplicant *wpa_s, const u8 *ssid,
+ size_t ssid_len);
+void wpas_request_connection(struct wpa_supplicant *wpa_s);
+int wpas_build_ext_capab(struct wpa_supplicant *wpa_s, u8 *buf, size_t buflen);
+int wpas_update_random_addr(struct wpa_supplicant *wpa_s, int style);
+int wpas_update_random_addr_disassoc(struct wpa_supplicant *wpa_s);
+void add_freq(int *freqs, int *num_freqs, int freq);
+
+void wpas_rrm_reset(struct wpa_supplicant *wpa_s);
+void wpas_rrm_process_neighbor_rep(struct wpa_supplicant *wpa_s,
+ const u8 *report, size_t report_len);
+int wpas_rrm_send_neighbor_rep_request(struct wpa_supplicant *wpa_s,
+ const struct wpa_ssid *ssid,
+ void (*cb)(void *ctx,
+ struct wpabuf *neighbor_rep),
+ void *cb_ctx);
+void wpas_rrm_handle_link_measurement_request(struct wpa_supplicant *wpa_s,
+ const u8 *src,
+ const u8 *frame, size_t len,
+ int rssi);
+
+/**
+ * wpa_supplicant_ctrl_iface_ctrl_rsp_handle - Handle a control response
+ * @wpa_s: Pointer to wpa_supplicant data
+ * @ssid: Pointer to the network block the reply is for
+ * @field: field the response is a reply for
+ * @value: value (ie, password, etc) for @field
+ * Returns: 0 on success, non-zero on error
+ *
+ * Helper function to handle replies to control interface requests.
+ */
+int wpa_supplicant_ctrl_iface_ctrl_rsp_handle(struct wpa_supplicant *wpa_s,
+ struct wpa_ssid *ssid,
+ const char *field,
+ const char *value);
+
+void ibss_mesh_setup_freq(struct wpa_supplicant *wpa_s,
+ const struct wpa_ssid *ssid,
+ struct hostapd_freq_params *freq);
+
+/* events.c */
+void wpa_supplicant_mark_disassoc(struct wpa_supplicant *wpa_s);
+int wpa_supplicant_connect(struct wpa_supplicant *wpa_s,
+ struct wpa_bss *selected,
+ struct wpa_ssid *ssid);
+void wpa_supplicant_stop_countermeasures(void *eloop_ctx, void *sock_ctx);
+void wpa_supplicant_delayed_mic_error_report(void *eloop_ctx, void *sock_ctx);
+void wnm_bss_keep_alive_deinit(struct wpa_supplicant *wpa_s);
+int wpa_supplicant_fast_associate(struct wpa_supplicant *wpa_s);
+struct wpa_bss * wpa_supplicant_pick_network(struct wpa_supplicant *wpa_s,
+ struct wpa_ssid **selected_ssid);
+
+/* eap_register.c */
+int eap_register_methods(void);
+
+/**
+ * Utility method to tell if a given network is for persistent group storage
+ * @ssid: Network object
+ * Returns: 1 if network is a persistent group, 0 otherwise
+ */
+static inline int network_is_persistent_group(struct wpa_ssid *ssid)
+{
+ return ssid->disabled == 2 && ssid->p2p_persistent_group;
+}
+
+int wpas_network_disabled(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid);
+int wpas_get_ssid_pmf(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid);
+
+int wpas_init_ext_pw(struct wpa_supplicant *wpa_s);
+
+void dump_freq_data(struct wpa_supplicant *wpa_s, const char *title,
+ struct wpa_used_freq_data *freqs_data,
+ unsigned int len);
+
+int get_shared_radio_freqs_data(struct wpa_supplicant *wpa_s,
+ struct wpa_used_freq_data *freqs_data,
+ unsigned int len);
+int get_shared_radio_freqs(struct wpa_supplicant *wpa_s,
+ int *freq_array, unsigned int len);
+
+void wpas_network_reenabled(void *eloop_ctx, void *timeout_ctx);
+
+#ifdef CONFIG_FST
+
+struct fst_wpa_obj;
+
+void fst_wpa_supplicant_fill_iface_obj(struct wpa_supplicant *wpa_s,
+ struct fst_wpa_obj *iface_obj);
+
+#endif /* CONFIG_FST */
+
+#endif /* WPA_SUPPLICANT_I_H */
diff --git a/freebsd/contrib/wpa/wpa_supplicant/wpas_glue.c b/freebsd/contrib/wpa/wpa_supplicant/wpas_glue.c
new file mode 100644
index 00000000..7ce05ddb
--- /dev/null
+++ b/freebsd/contrib/wpa/wpa_supplicant/wpas_glue.c
@@ -0,0 +1,1131 @@
+#include <machine/rtems-bsd-user-space.h>
+
+/*
+ * WPA Supplicant - Glue code to setup EAPOL and RSN modules
+ * Copyright (c) 2003-2015, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "eapol_supp/eapol_supp_sm.h"
+#include "rsn_supp/wpa.h"
+#include "eloop.h"
+#include "config.h"
+#include "l2_packet/l2_packet.h"
+#include "common/wpa_common.h"
+#include "wpa_supplicant_i.h"
+#include "driver_i.h"
+#include "rsn_supp/pmksa_cache.h"
+#include "sme.h"
+#include "common/ieee802_11_defs.h"
+#include "common/wpa_ctrl.h"
+#include "wpas_glue.h"
+#include "wps_supplicant.h"
+#include "bss.h"
+#include "scan.h"
+#include "notify.h"
+#include "wpas_kay.h"
+
+
+#ifndef CONFIG_NO_CONFIG_BLOBS
+#if defined(IEEE8021X_EAPOL) || !defined(CONFIG_NO_WPA)
+static void wpa_supplicant_set_config_blob(void *ctx,
+ struct wpa_config_blob *blob)
+{
+ struct wpa_supplicant *wpa_s = ctx;
+ wpa_config_set_blob(wpa_s->conf, blob);
+ if (wpa_s->conf->update_config) {
+ int ret = wpa_config_write(wpa_s->confname, wpa_s->conf);
+ if (ret) {
+ wpa_printf(MSG_DEBUG, "Failed to update config after "
+ "blob set");
+ }
+ }
+}
+
+
+static const struct wpa_config_blob *
+wpa_supplicant_get_config_blob(void *ctx, const char *name)
+{
+ struct wpa_supplicant *wpa_s = ctx;
+ return wpa_config_get_blob(wpa_s->conf, name);
+}
+#endif /* defined(IEEE8021X_EAPOL) || !defined(CONFIG_NO_WPA) */
+#endif /* CONFIG_NO_CONFIG_BLOBS */
+
+
+#if defined(IEEE8021X_EAPOL) || !defined(CONFIG_NO_WPA)
+static u8 * wpa_alloc_eapol(const struct wpa_supplicant *wpa_s, u8 type,
+ const void *data, u16 data_len,
+ size_t *msg_len, void **data_pos)
+{
+ struct ieee802_1x_hdr *hdr;
+
+ *msg_len = sizeof(*hdr) + data_len;
+ hdr = os_malloc(*msg_len);
+ if (hdr == NULL)
+ return NULL;
+
+ hdr->version = wpa_s->conf->eapol_version;
+ hdr->type = type;
+ hdr->length = host_to_be16(data_len);
+
+ if (data)
+ os_memcpy(hdr + 1, data, data_len);
+ else
+ os_memset(hdr + 1, 0, data_len);
+
+ if (data_pos)
+ *data_pos = hdr + 1;
+
+ return (u8 *) hdr;
+}
+
+
+/**
+ * wpa_ether_send - Send Ethernet frame
+ * @wpa_s: Pointer to wpa_supplicant data
+ * @dest: Destination MAC address
+ * @proto: Ethertype in host byte order
+ * @buf: Frame payload starting from IEEE 802.1X header
+ * @len: Frame payload length
+ * Returns: >=0 on success, <0 on failure
+ */
+static int wpa_ether_send(struct wpa_supplicant *wpa_s, const u8 *dest,
+ u16 proto, const u8 *buf, size_t len)
+{
+#ifdef CONFIG_TESTING_OPTIONS
+ if (wpa_s->ext_eapol_frame_io && proto == ETH_P_EAPOL) {
+ size_t hex_len = 2 * len + 1;
+ char *hex = os_malloc(hex_len);
+
+ if (hex == NULL)
+ return -1;
+ wpa_snprintf_hex(hex, hex_len, buf, len);
+ wpa_msg(wpa_s, MSG_INFO, "EAPOL-TX " MACSTR " %s",
+ MAC2STR(dest), hex);
+ os_free(hex);
+ return 0;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ if (wpa_s->l2) {
+ return l2_packet_send(wpa_s->l2, dest, proto, buf, len);
+ }
+
+ return -1;
+}
+#endif /* IEEE8021X_EAPOL || !CONFIG_NO_WPA */
+
+
+#ifdef IEEE8021X_EAPOL
+
+/**
+ * wpa_supplicant_eapol_send - Send IEEE 802.1X EAPOL packet to Authenticator
+ * @ctx: Pointer to wpa_supplicant data (wpa_s)
+ * @type: IEEE 802.1X packet type (IEEE802_1X_TYPE_*)
+ * @buf: EAPOL payload (after IEEE 802.1X header)
+ * @len: EAPOL payload length
+ * Returns: >=0 on success, <0 on failure
+ *
+ * This function adds Ethernet and IEEE 802.1X header and sends the EAPOL frame
+ * to the current Authenticator.
+ */
+static int wpa_supplicant_eapol_send(void *ctx, int type, const u8 *buf,
+ size_t len)
+{
+ struct wpa_supplicant *wpa_s = ctx;
+ u8 *msg, *dst, bssid[ETH_ALEN];
+ size_t msglen;
+ int res;
+
+ /* TODO: could add l2_packet_sendmsg that allows fragments to avoid
+ * extra copy here */
+
+ if (wpa_key_mgmt_wpa_psk(wpa_s->key_mgmt) ||
+ wpa_s->key_mgmt == WPA_KEY_MGMT_NONE) {
+ /* Current SSID is not using IEEE 802.1X/EAP, so drop possible
+ * EAPOL frames (mainly, EAPOL-Start) from EAPOL state
+ * machines. */
+ wpa_printf(MSG_DEBUG, "WPA: drop TX EAPOL in non-IEEE 802.1X "
+ "mode (type=%d len=%lu)", type,
+ (unsigned long) len);
+ return -1;
+ }
+
+ if (pmksa_cache_get_current(wpa_s->wpa) &&
+ type == IEEE802_1X_TYPE_EAPOL_START) {
+ /*
+ * We were trying to use PMKSA caching and sending EAPOL-Start
+ * would abort that and trigger full EAPOL authentication.
+ * However, we've already waited for the AP/Authenticator to
+ * start 4-way handshake or EAP authentication, and apparently
+ * it has not done so since the startWhen timer has reached zero
+ * to get the state machine sending EAPOL-Start. This is not
+ * really supposed to happen, but an interoperability issue with
+ * a deployed AP has been identified where the connection fails
+ * due to that AP failing to operate correctly if PMKID is
+ * included in the Association Request frame. To work around
+ * this, assume PMKSA caching failed and try to initiate full
+ * EAP authentication.
+ */
+ if (!wpa_s->current_ssid ||
+ wpa_s->current_ssid->eap_workaround) {
+ wpa_printf(MSG_DEBUG,
+ "RSN: Timeout on waiting for the AP to initiate 4-way handshake for PMKSA caching or EAP authentication - try to force it to start EAP authentication");
+ } else {
+ wpa_printf(MSG_DEBUG,
+ "RSN: PMKSA caching - do not send EAPOL-Start");
+ return -1;
+ }
+ }
+
+ if (is_zero_ether_addr(wpa_s->bssid)) {
+ wpa_printf(MSG_DEBUG, "BSSID not set when trying to send an "
+ "EAPOL frame");
+ if (wpa_drv_get_bssid(wpa_s, bssid) == 0 &&
+ !is_zero_ether_addr(bssid)) {
+ dst = bssid;
+ wpa_printf(MSG_DEBUG, "Using current BSSID " MACSTR
+ " from the driver as the EAPOL destination",
+ MAC2STR(dst));
+ } else {
+ dst = wpa_s->last_eapol_src;
+ wpa_printf(MSG_DEBUG, "Using the source address of the"
+ " last received EAPOL frame " MACSTR " as "
+ "the EAPOL destination",
+ MAC2STR(dst));
+ }
+ } else {
+ /* BSSID was already set (from (Re)Assoc event, so use it as
+ * the EAPOL destination. */
+ dst = wpa_s->bssid;
+ }
+
+ msg = wpa_alloc_eapol(wpa_s, type, buf, len, &msglen, NULL);
+ if (msg == NULL)
+ return -1;
+
+ wpa_printf(MSG_DEBUG, "TX EAPOL: dst=" MACSTR, MAC2STR(dst));
+ wpa_hexdump(MSG_MSGDUMP, "TX EAPOL", msg, msglen);
+ res = wpa_ether_send(wpa_s, dst, ETH_P_EAPOL, msg, msglen);
+ os_free(msg);
+ return res;
+}
+
+
+/**
+ * wpa_eapol_set_wep_key - set WEP key for the driver
+ * @ctx: Pointer to wpa_supplicant data (wpa_s)
+ * @unicast: 1 = individual unicast key, 0 = broadcast key
+ * @keyidx: WEP key index (0..3)
+ * @key: Pointer to key data
+ * @keylen: Key length in bytes
+ * Returns: 0 on success or < 0 on error.
+ */
+static int wpa_eapol_set_wep_key(void *ctx, int unicast, int keyidx,
+ const u8 *key, size_t keylen)
+{
+ struct wpa_supplicant *wpa_s = ctx;
+ if (wpa_s->key_mgmt == WPA_KEY_MGMT_IEEE8021X_NO_WPA) {
+ int cipher = (keylen == 5) ? WPA_CIPHER_WEP40 :
+ WPA_CIPHER_WEP104;
+ if (unicast)
+ wpa_s->pairwise_cipher = cipher;
+ else
+ wpa_s->group_cipher = cipher;
+ }
+ return wpa_drv_set_key(wpa_s, WPA_ALG_WEP,
+ unicast ? wpa_s->bssid : NULL,
+ keyidx, unicast, NULL, 0, key, keylen);
+}
+
+
+static void wpa_supplicant_aborted_cached(void *ctx)
+{
+ struct wpa_supplicant *wpa_s = ctx;
+ wpa_sm_aborted_cached(wpa_s->wpa);
+}
+
+
+static const char * result_str(enum eapol_supp_result result)
+{
+ switch (result) {
+ case EAPOL_SUPP_RESULT_FAILURE:
+ return "FAILURE";
+ case EAPOL_SUPP_RESULT_SUCCESS:
+ return "SUCCESS";
+ case EAPOL_SUPP_RESULT_EXPECTED_FAILURE:
+ return "EXPECTED_FAILURE";
+ }
+ return "?";
+}
+
+
+static void wpa_supplicant_eapol_cb(struct eapol_sm *eapol,
+ enum eapol_supp_result result,
+ void *ctx)
+{
+ struct wpa_supplicant *wpa_s = ctx;
+ int res, pmk_len;
+ u8 pmk[PMK_LEN];
+
+ wpa_printf(MSG_DEBUG, "EAPOL authentication completed - result=%s",
+ result_str(result));
+
+ if (wpas_wps_eapol_cb(wpa_s) > 0)
+ return;
+
+ wpa_s->eap_expected_failure = result ==
+ EAPOL_SUPP_RESULT_EXPECTED_FAILURE;
+
+ if (result != EAPOL_SUPP_RESULT_SUCCESS) {
+ /*
+ * Make sure we do not get stuck here waiting for long EAPOL
+ * timeout if the AP does not disconnect in case of
+ * authentication failure.
+ */
+ wpa_supplicant_req_auth_timeout(wpa_s, 2, 0);
+ } else {
+ ieee802_1x_notify_create_actor(wpa_s, wpa_s->last_eapol_src);
+ }
+
+ if (result != EAPOL_SUPP_RESULT_SUCCESS ||
+ !(wpa_s->drv_flags & WPA_DRIVER_FLAGS_4WAY_HANDSHAKE))
+ return;
+
+ if (!wpa_key_mgmt_wpa_ieee8021x(wpa_s->key_mgmt))
+ return;
+
+ wpa_printf(MSG_DEBUG, "Configure PMK for driver-based RSN 4-way "
+ "handshake");
+
+ pmk_len = PMK_LEN;
+ if (wpa_key_mgmt_ft(wpa_s->key_mgmt)) {
+#ifdef CONFIG_IEEE80211R
+ u8 buf[2 * PMK_LEN];
+ wpa_printf(MSG_DEBUG, "RSN: Use FT XXKey as PMK for "
+ "driver-based 4-way hs and FT");
+ res = eapol_sm_get_key(eapol, buf, 2 * PMK_LEN);
+ if (res == 0) {
+ os_memcpy(pmk, buf + PMK_LEN, PMK_LEN);
+ os_memset(buf, 0, sizeof(buf));
+ }
+#else /* CONFIG_IEEE80211R */
+ res = -1;
+#endif /* CONFIG_IEEE80211R */
+ } else {
+ res = eapol_sm_get_key(eapol, pmk, PMK_LEN);
+ if (res) {
+ /*
+ * EAP-LEAP is an exception from other EAP methods: it
+ * uses only 16-byte PMK.
+ */
+ res = eapol_sm_get_key(eapol, pmk, 16);
+ pmk_len = 16;
+ }
+ }
+
+ if (res) {
+ wpa_printf(MSG_DEBUG, "Failed to get PMK from EAPOL state "
+ "machines");
+ return;
+ }
+
+ wpa_hexdump_key(MSG_DEBUG, "RSN: Configure PMK for driver-based 4-way "
+ "handshake", pmk, pmk_len);
+
+ if (wpa_drv_set_key(wpa_s, WPA_ALG_PMK, NULL, 0, 0, NULL, 0, pmk,
+ pmk_len)) {
+ wpa_printf(MSG_DEBUG, "Failed to set PMK to the driver");
+ }
+
+ wpa_supplicant_cancel_scan(wpa_s);
+ wpa_supplicant_cancel_auth_timeout(wpa_s);
+ wpa_supplicant_set_state(wpa_s, WPA_COMPLETED);
+
+}
+
+
+static void wpa_supplicant_notify_eapol_done(void *ctx)
+{
+ struct wpa_supplicant *wpa_s = ctx;
+ wpa_msg(wpa_s, MSG_DEBUG, "WPA: EAPOL processing complete");
+ if (wpa_key_mgmt_wpa_ieee8021x(wpa_s->key_mgmt)) {
+ wpa_supplicant_set_state(wpa_s, WPA_4WAY_HANDSHAKE);
+ } else {
+ wpa_supplicant_cancel_auth_timeout(wpa_s);
+ wpa_supplicant_set_state(wpa_s, WPA_COMPLETED);
+ }
+}
+
+#endif /* IEEE8021X_EAPOL */
+
+
+#ifndef CONFIG_NO_WPA
+
+static int wpa_get_beacon_ie(struct wpa_supplicant *wpa_s)
+{
+ int ret = 0;
+ struct wpa_bss *curr = NULL, *bss;
+ struct wpa_ssid *ssid = wpa_s->current_ssid;
+ const u8 *ie;
+
+ dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) {
+ if (os_memcmp(bss->bssid, wpa_s->bssid, ETH_ALEN) != 0)
+ continue;
+ if (ssid == NULL ||
+ ((bss->ssid_len == ssid->ssid_len &&
+ os_memcmp(bss->ssid, ssid->ssid, ssid->ssid_len) == 0) ||
+ ssid->ssid_len == 0)) {
+ curr = bss;
+ break;
+ }
+ }
+
+ if (curr) {
+ ie = wpa_bss_get_vendor_ie(curr, WPA_IE_VENDOR_TYPE);
+ if (wpa_sm_set_ap_wpa_ie(wpa_s->wpa, ie, ie ? 2 + ie[1] : 0))
+ ret = -1;
+
+ ie = wpa_bss_get_ie(curr, WLAN_EID_RSN);
+ if (wpa_sm_set_ap_rsn_ie(wpa_s->wpa, ie, ie ? 2 + ie[1] : 0))
+ ret = -1;
+ } else {
+ ret = -1;
+ }
+
+ return ret;
+}
+
+
+static int wpa_supplicant_get_beacon_ie(void *ctx)
+{
+ struct wpa_supplicant *wpa_s = ctx;
+ if (wpa_get_beacon_ie(wpa_s) == 0) {
+ return 0;
+ }
+
+ /* No WPA/RSN IE found in the cached scan results. Try to get updated
+ * scan results from the driver. */
+ if (wpa_supplicant_update_scan_results(wpa_s) < 0)
+ return -1;
+
+ return wpa_get_beacon_ie(wpa_s);
+}
+
+
+static u8 * _wpa_alloc_eapol(void *wpa_s, u8 type,
+ const void *data, u16 data_len,
+ size_t *msg_len, void **data_pos)
+{
+ return wpa_alloc_eapol(wpa_s, type, data, data_len, msg_len, data_pos);
+}
+
+
+static int _wpa_ether_send(void *wpa_s, const u8 *dest, u16 proto,
+ const u8 *buf, size_t len)
+{
+ return wpa_ether_send(wpa_s, dest, proto, buf, len);
+}
+
+
+static void _wpa_supplicant_cancel_auth_timeout(void *wpa_s)
+{
+ wpa_supplicant_cancel_auth_timeout(wpa_s);
+}
+
+
+static void _wpa_supplicant_set_state(void *wpa_s, enum wpa_states state)
+{
+ wpa_supplicant_set_state(wpa_s, state);
+}
+
+
+/**
+ * wpa_supplicant_get_state - Get the connection state
+ * @wpa_s: Pointer to wpa_supplicant data
+ * Returns: The current connection state (WPA_*)
+ */
+static enum wpa_states wpa_supplicant_get_state(struct wpa_supplicant *wpa_s)
+{
+ return wpa_s->wpa_state;
+}
+
+
+static enum wpa_states _wpa_supplicant_get_state(void *wpa_s)
+{
+ return wpa_supplicant_get_state(wpa_s);
+}
+
+
+static void _wpa_supplicant_deauthenticate(void *wpa_s, int reason_code)
+{
+ wpa_supplicant_deauthenticate(wpa_s, reason_code);
+ /* Schedule a scan to make sure we continue looking for networks */
+ wpa_supplicant_req_scan(wpa_s, 5, 0);
+}
+
+
+static void * wpa_supplicant_get_network_ctx(void *wpa_s)
+{
+ return wpa_supplicant_get_ssid(wpa_s);
+}
+
+
+static int wpa_supplicant_get_bssid(void *ctx, u8 *bssid)
+{
+ struct wpa_supplicant *wpa_s = ctx;
+ return wpa_drv_get_bssid(wpa_s, bssid);
+}
+
+
+static int wpa_supplicant_set_key(void *_wpa_s, enum wpa_alg alg,
+ const u8 *addr, int key_idx, int set_tx,
+ const u8 *seq, size_t seq_len,
+ const u8 *key, size_t key_len)
+{
+ struct wpa_supplicant *wpa_s = _wpa_s;
+ if (alg == WPA_ALG_TKIP && key_idx == 0 && key_len == 32) {
+ /* Clear the MIC error counter when setting a new PTK. */
+ wpa_s->mic_errors_seen = 0;
+ }
+#ifdef CONFIG_TESTING_GET_GTK
+ if (key_idx > 0 && addr && is_broadcast_ether_addr(addr) &&
+ alg != WPA_ALG_NONE && key_len <= sizeof(wpa_s->last_gtk)) {
+ os_memcpy(wpa_s->last_gtk, key, key_len);
+ wpa_s->last_gtk_len = key_len;
+ }
+#endif /* CONFIG_TESTING_GET_GTK */
+ return wpa_drv_set_key(wpa_s, alg, addr, key_idx, set_tx, seq, seq_len,
+ key, key_len);
+}
+
+
+static int wpa_supplicant_mlme_setprotection(void *wpa_s, const u8 *addr,
+ int protection_type,
+ int key_type)
+{
+ return wpa_drv_mlme_setprotection(wpa_s, addr, protection_type,
+ key_type);
+}
+
+
+static int wpa_supplicant_add_pmkid(void *wpa_s,
+ const u8 *bssid, const u8 *pmkid)
+{
+ return wpa_drv_add_pmkid(wpa_s, bssid, pmkid);
+}
+
+
+static int wpa_supplicant_remove_pmkid(void *wpa_s,
+ const u8 *bssid, const u8 *pmkid)
+{
+ return wpa_drv_remove_pmkid(wpa_s, bssid, pmkid);
+}
+
+
+#ifdef CONFIG_IEEE80211R
+static int wpa_supplicant_update_ft_ies(void *ctx, const u8 *md,
+ const u8 *ies, size_t ies_len)
+{
+ struct wpa_supplicant *wpa_s = ctx;
+ if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME)
+ return sme_update_ft_ies(wpa_s, md, ies, ies_len);
+ return wpa_drv_update_ft_ies(wpa_s, md, ies, ies_len);
+}
+
+
+static int wpa_supplicant_send_ft_action(void *ctx, u8 action,
+ const u8 *target_ap,
+ const u8 *ies, size_t ies_len)
+{
+ struct wpa_supplicant *wpa_s = ctx;
+ int ret;
+ u8 *data, *pos;
+ size_t data_len;
+
+ if (action != 1) {
+ wpa_printf(MSG_ERROR, "Unsupported send_ft_action action %d",
+ action);
+ return -1;
+ }
+
+ /*
+ * Action frame payload:
+ * Category[1] = 6 (Fast BSS Transition)
+ * Action[1] = 1 (Fast BSS Transition Request)
+ * STA Address
+ * Target AP Address
+ * FT IEs
+ */
+
+ data_len = 2 + 2 * ETH_ALEN + ies_len;
+ data = os_malloc(data_len);
+ if (data == NULL)
+ return -1;
+ pos = data;
+ *pos++ = 0x06; /* FT Action category */
+ *pos++ = action;
+ os_memcpy(pos, wpa_s->own_addr, ETH_ALEN);
+ pos += ETH_ALEN;
+ os_memcpy(pos, target_ap, ETH_ALEN);
+ pos += ETH_ALEN;
+ os_memcpy(pos, ies, ies_len);
+
+ ret = wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0,
+ wpa_s->bssid, wpa_s->own_addr, wpa_s->bssid,
+ data, data_len, 0);
+ os_free(data);
+
+ return ret;
+}
+
+
+static int wpa_supplicant_mark_authenticated(void *ctx, const u8 *target_ap)
+{
+ struct wpa_supplicant *wpa_s = ctx;
+ struct wpa_driver_auth_params params;
+ struct wpa_bss *bss;
+
+ bss = wpa_bss_get_bssid(wpa_s, target_ap);
+ if (bss == NULL)
+ return -1;
+
+ os_memset(&params, 0, sizeof(params));
+ params.bssid = target_ap;
+ params.freq = bss->freq;
+ params.ssid = bss->ssid;
+ params.ssid_len = bss->ssid_len;
+ params.auth_alg = WPA_AUTH_ALG_FT;
+ params.local_state_change = 1;
+ return wpa_drv_authenticate(wpa_s, &params);
+}
+#endif /* CONFIG_IEEE80211R */
+
+
+#ifdef CONFIG_TDLS
+
+static int wpa_supplicant_tdls_get_capa(void *ctx, int *tdls_supported,
+ int *tdls_ext_setup,
+ int *tdls_chan_switch)
+{
+ struct wpa_supplicant *wpa_s = ctx;
+
+ *tdls_supported = 0;
+ *tdls_ext_setup = 0;
+ *tdls_chan_switch = 0;
+
+ if (!wpa_s->drv_capa_known)
+ return -1;
+
+ if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_TDLS_SUPPORT)
+ *tdls_supported = 1;
+
+ if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_TDLS_EXTERNAL_SETUP)
+ *tdls_ext_setup = 1;
+
+ if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_TDLS_CHANNEL_SWITCH)
+ *tdls_chan_switch = 1;
+
+ return 0;
+}
+
+
+static int wpa_supplicant_send_tdls_mgmt(void *ctx, const u8 *dst,
+ u8 action_code, u8 dialog_token,
+ u16 status_code, u32 peer_capab,
+ int initiator, const u8 *buf,
+ size_t len)
+{
+ struct wpa_supplicant *wpa_s = ctx;
+ return wpa_drv_send_tdls_mgmt(wpa_s, dst, action_code, dialog_token,
+ status_code, peer_capab, initiator, buf,
+ len);
+}
+
+
+static int wpa_supplicant_tdls_oper(void *ctx, int oper, const u8 *peer)
+{
+ struct wpa_supplicant *wpa_s = ctx;
+ return wpa_drv_tdls_oper(wpa_s, oper, peer);
+}
+
+
+static int wpa_supplicant_tdls_peer_addset(
+ void *ctx, const u8 *peer, int add, u16 aid, u16 capability,
+ const u8 *supp_rates, size_t supp_rates_len,
+ const struct ieee80211_ht_capabilities *ht_capab,
+ const struct ieee80211_vht_capabilities *vht_capab,
+ u8 qosinfo, int wmm, const u8 *ext_capab, size_t ext_capab_len,
+ const u8 *supp_channels, size_t supp_channels_len,
+ const u8 *supp_oper_classes, size_t supp_oper_classes_len)
+{
+ struct wpa_supplicant *wpa_s = ctx;
+ struct hostapd_sta_add_params params;
+
+ os_memset(&params, 0, sizeof(params));
+
+ params.addr = peer;
+ params.aid = aid;
+ params.capability = capability;
+ params.flags = WPA_STA_TDLS_PEER | WPA_STA_AUTHORIZED;
+
+ /*
+ * Don't rely only on qosinfo for WMM capability. It may be 0 even when
+ * present. Allow the WMM IE to also indicate QoS support.
+ */
+ if (wmm || qosinfo)
+ params.flags |= WPA_STA_WMM;
+
+ params.ht_capabilities = ht_capab;
+ params.vht_capabilities = vht_capab;
+ params.qosinfo = qosinfo;
+ params.listen_interval = 0;
+ params.supp_rates = supp_rates;
+ params.supp_rates_len = supp_rates_len;
+ params.set = !add;
+ params.ext_capab = ext_capab;
+ params.ext_capab_len = ext_capab_len;
+ params.supp_channels = supp_channels;
+ params.supp_channels_len = supp_channels_len;
+ params.supp_oper_classes = supp_oper_classes;
+ params.supp_oper_classes_len = supp_oper_classes_len;
+
+ return wpa_drv_sta_add(wpa_s, &params);
+}
+
+
+static int wpa_supplicant_tdls_enable_channel_switch(
+ void *ctx, const u8 *addr, u8 oper_class,
+ const struct hostapd_freq_params *params)
+{
+ struct wpa_supplicant *wpa_s = ctx;
+
+ return wpa_drv_tdls_enable_channel_switch(wpa_s, addr, oper_class,
+ params);
+}
+
+
+static int wpa_supplicant_tdls_disable_channel_switch(void *ctx, const u8 *addr)
+{
+ struct wpa_supplicant *wpa_s = ctx;
+
+ return wpa_drv_tdls_disable_channel_switch(wpa_s, addr);
+}
+
+#endif /* CONFIG_TDLS */
+
+#endif /* CONFIG_NO_WPA */
+
+
+enum wpa_ctrl_req_type wpa_supplicant_ctrl_req_from_string(const char *field)
+{
+ if (os_strcmp(field, "IDENTITY") == 0)
+ return WPA_CTRL_REQ_EAP_IDENTITY;
+ else if (os_strcmp(field, "PASSWORD") == 0)
+ return WPA_CTRL_REQ_EAP_PASSWORD;
+ else if (os_strcmp(field, "NEW_PASSWORD") == 0)
+ return WPA_CTRL_REQ_EAP_NEW_PASSWORD;
+ else if (os_strcmp(field, "PIN") == 0)
+ return WPA_CTRL_REQ_EAP_PIN;
+ else if (os_strcmp(field, "OTP") == 0)
+ return WPA_CTRL_REQ_EAP_OTP;
+ else if (os_strcmp(field, "PASSPHRASE") == 0)
+ return WPA_CTRL_REQ_EAP_PASSPHRASE;
+ else if (os_strcmp(field, "SIM") == 0)
+ return WPA_CTRL_REQ_SIM;
+ else if (os_strcmp(field, "PSK_PASSPHRASE") == 0)
+ return WPA_CTRL_REQ_PSK_PASSPHRASE;
+ return WPA_CTRL_REQ_UNKNOWN;
+}
+
+
+const char * wpa_supplicant_ctrl_req_to_string(enum wpa_ctrl_req_type field,
+ const char *default_txt,
+ const char **txt)
+{
+ const char *ret = NULL;
+
+ *txt = default_txt;
+
+ switch (field) {
+ case WPA_CTRL_REQ_EAP_IDENTITY:
+ *txt = "Identity";
+ ret = "IDENTITY";
+ break;
+ case WPA_CTRL_REQ_EAP_PASSWORD:
+ *txt = "Password";
+ ret = "PASSWORD";
+ break;
+ case WPA_CTRL_REQ_EAP_NEW_PASSWORD:
+ *txt = "New Password";
+ ret = "NEW_PASSWORD";
+ break;
+ case WPA_CTRL_REQ_EAP_PIN:
+ *txt = "PIN";
+ ret = "PIN";
+ break;
+ case WPA_CTRL_REQ_EAP_OTP:
+ ret = "OTP";
+ break;
+ case WPA_CTRL_REQ_EAP_PASSPHRASE:
+ *txt = "Private key passphrase";
+ ret = "PASSPHRASE";
+ break;
+ case WPA_CTRL_REQ_SIM:
+ ret = "SIM";
+ break;
+ case WPA_CTRL_REQ_PSK_PASSPHRASE:
+ *txt = "PSK or passphrase";
+ ret = "PSK_PASSPHRASE";
+ break;
+ default:
+ break;
+ }
+
+ /* txt needs to be something */
+ if (*txt == NULL) {
+ wpa_printf(MSG_WARNING, "No message for request %d", field);
+ ret = NULL;
+ }
+
+ return ret;
+}
+
+
+void wpas_send_ctrl_req(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid,
+ const char *field_name, const char *txt)
+{
+ char *buf;
+ size_t buflen;
+ int len;
+
+ buflen = 100 + os_strlen(txt) + ssid->ssid_len;
+ buf = os_malloc(buflen);
+ if (buf == NULL)
+ return;
+ len = os_snprintf(buf, buflen, "%s-%d:%s needed for SSID ",
+ field_name, ssid->id, txt);
+ if (os_snprintf_error(buflen, len)) {
+ os_free(buf);
+ return;
+ }
+ if (ssid->ssid && buflen > len + ssid->ssid_len) {
+ os_memcpy(buf + len, ssid->ssid, ssid->ssid_len);
+ len += ssid->ssid_len;
+ buf[len] = '\0';
+ }
+ buf[buflen - 1] = '\0';
+ wpa_msg(wpa_s, MSG_INFO, WPA_CTRL_REQ "%s", buf);
+ os_free(buf);
+}
+
+
+#ifdef IEEE8021X_EAPOL
+#if defined(CONFIG_CTRL_IFACE) || !defined(CONFIG_NO_STDOUT_DEBUG)
+static void wpa_supplicant_eap_param_needed(void *ctx,
+ enum wpa_ctrl_req_type field,
+ const char *default_txt)
+{
+ struct wpa_supplicant *wpa_s = ctx;
+ struct wpa_ssid *ssid = wpa_s->current_ssid;
+ const char *field_name, *txt = NULL;
+
+ if (ssid == NULL)
+ return;
+
+ wpas_notify_network_request(wpa_s, ssid, field, default_txt);
+
+ field_name = wpa_supplicant_ctrl_req_to_string(field, default_txt,
+ &txt);
+ if (field_name == NULL) {
+ wpa_printf(MSG_WARNING, "Unhandled EAP param %d needed",
+ field);
+ return;
+ }
+
+ wpas_notify_eap_status(wpa_s, "eap parameter needed", field_name);
+
+ wpas_send_ctrl_req(wpa_s, ssid, field_name, txt);
+}
+#else /* CONFIG_CTRL_IFACE || !CONFIG_NO_STDOUT_DEBUG */
+#define wpa_supplicant_eap_param_needed NULL
+#endif /* CONFIG_CTRL_IFACE || !CONFIG_NO_STDOUT_DEBUG */
+
+
+#ifdef CONFIG_EAP_PROXY
+static void wpa_supplicant_eap_proxy_cb(void *ctx)
+{
+ struct wpa_supplicant *wpa_s = ctx;
+ size_t len;
+
+ wpa_s->mnc_len = eapol_sm_get_eap_proxy_imsi(wpa_s->eapol,
+ wpa_s->imsi, &len);
+ if (wpa_s->mnc_len > 0) {
+ wpa_s->imsi[len] = '\0';
+ wpa_printf(MSG_DEBUG, "eap_proxy: IMSI %s (MNC length %d)",
+ wpa_s->imsi, wpa_s->mnc_len);
+ } else {
+ wpa_printf(MSG_DEBUG, "eap_proxy: IMSI not available");
+ }
+}
+#endif /* CONFIG_EAP_PROXY */
+
+
+static void wpa_supplicant_port_cb(void *ctx, int authorized)
+{
+ struct wpa_supplicant *wpa_s = ctx;
+#ifdef CONFIG_AP
+ if (wpa_s->ap_iface) {
+ wpa_printf(MSG_DEBUG, "AP mode active - skip EAPOL Supplicant "
+ "port status: %s",
+ authorized ? "Authorized" : "Unauthorized");
+ return;
+ }
+#endif /* CONFIG_AP */
+ wpa_printf(MSG_DEBUG, "EAPOL: Supplicant port status: %s",
+ authorized ? "Authorized" : "Unauthorized");
+ wpa_drv_set_supp_port(wpa_s, authorized);
+}
+
+
+static void wpa_supplicant_cert_cb(void *ctx, int depth, const char *subject,
+ const char *altsubject[], int num_altsubject,
+ const char *cert_hash,
+ const struct wpabuf *cert)
+{
+ struct wpa_supplicant *wpa_s = ctx;
+
+ wpas_notify_certification(wpa_s, depth, subject, altsubject,
+ num_altsubject, cert_hash, cert);
+}
+
+
+static void wpa_supplicant_status_cb(void *ctx, const char *status,
+ const char *parameter)
+{
+ struct wpa_supplicant *wpa_s = ctx;
+
+ wpas_notify_eap_status(wpa_s, status, parameter);
+}
+
+
+static void wpa_supplicant_set_anon_id(void *ctx, const u8 *id, size_t len)
+{
+ struct wpa_supplicant *wpa_s = ctx;
+ char *str;
+ int res;
+
+ wpa_hexdump_ascii(MSG_DEBUG, "EAP method updated anonymous_identity",
+ id, len);
+
+ if (wpa_s->current_ssid == NULL)
+ return;
+
+ if (id == NULL) {
+ if (wpa_config_set(wpa_s->current_ssid, "anonymous_identity",
+ "NULL", 0) < 0)
+ return;
+ } else {
+ str = os_malloc(len * 2 + 1);
+ if (str == NULL)
+ return;
+ wpa_snprintf_hex(str, len * 2 + 1, id, len);
+ res = wpa_config_set(wpa_s->current_ssid, "anonymous_identity",
+ str, 0);
+ os_free(str);
+ if (res < 0)
+ return;
+ }
+
+ if (wpa_s->conf->update_config) {
+ res = wpa_config_write(wpa_s->confname, wpa_s->conf);
+ if (res) {
+ wpa_printf(MSG_DEBUG, "Failed to update config after "
+ "anonymous_id update");
+ }
+ }
+}
+#endif /* IEEE8021X_EAPOL */
+
+
+int wpa_supplicant_init_eapol(struct wpa_supplicant *wpa_s)
+{
+#ifdef IEEE8021X_EAPOL
+ struct eapol_ctx *ctx;
+ ctx = os_zalloc(sizeof(*ctx));
+ if (ctx == NULL) {
+ wpa_printf(MSG_ERROR, "Failed to allocate EAPOL context.");
+ return -1;
+ }
+
+ ctx->ctx = wpa_s;
+ ctx->msg_ctx = wpa_s;
+ ctx->eapol_send_ctx = wpa_s;
+ ctx->preauth = 0;
+ ctx->eapol_done_cb = wpa_supplicant_notify_eapol_done;
+ ctx->eapol_send = wpa_supplicant_eapol_send;
+ ctx->set_wep_key = wpa_eapol_set_wep_key;
+#ifndef CONFIG_NO_CONFIG_BLOBS
+ ctx->set_config_blob = wpa_supplicant_set_config_blob;
+ ctx->get_config_blob = wpa_supplicant_get_config_blob;
+#endif /* CONFIG_NO_CONFIG_BLOBS */
+ ctx->aborted_cached = wpa_supplicant_aborted_cached;
+ ctx->opensc_engine_path = wpa_s->conf->opensc_engine_path;
+ ctx->pkcs11_engine_path = wpa_s->conf->pkcs11_engine_path;
+ ctx->pkcs11_module_path = wpa_s->conf->pkcs11_module_path;
+ ctx->openssl_ciphers = wpa_s->conf->openssl_ciphers;
+ ctx->wps = wpa_s->wps;
+ ctx->eap_param_needed = wpa_supplicant_eap_param_needed;
+#ifdef CONFIG_EAP_PROXY
+ ctx->eap_proxy_cb = wpa_supplicant_eap_proxy_cb;
+#endif /* CONFIG_EAP_PROXY */
+ ctx->port_cb = wpa_supplicant_port_cb;
+ ctx->cb = wpa_supplicant_eapol_cb;
+ ctx->cert_cb = wpa_supplicant_cert_cb;
+ ctx->cert_in_cb = wpa_s->conf->cert_in_cb;
+ ctx->status_cb = wpa_supplicant_status_cb;
+ ctx->set_anon_id = wpa_supplicant_set_anon_id;
+ ctx->cb_ctx = wpa_s;
+ wpa_s->eapol = eapol_sm_init(ctx);
+ if (wpa_s->eapol == NULL) {
+ os_free(ctx);
+ wpa_printf(MSG_ERROR, "Failed to initialize EAPOL state "
+ "machines.");
+ return -1;
+ }
+#endif /* IEEE8021X_EAPOL */
+
+ return 0;
+}
+
+
+#ifndef CONFIG_NO_WPA
+static void wpa_supplicant_set_rekey_offload(void *ctx,
+ const u8 *kek, size_t kek_len,
+ const u8 *kck, size_t kck_len,
+ const u8 *replay_ctr)
+{
+ struct wpa_supplicant *wpa_s = ctx;
+
+ wpa_drv_set_rekey_info(wpa_s, kek, kek_len, kck, kck_len, replay_ctr);
+}
+#endif /* CONFIG_NO_WPA */
+
+
+static int wpa_supplicant_key_mgmt_set_pmk(void *ctx, const u8 *pmk,
+ size_t pmk_len)
+{
+ struct wpa_supplicant *wpa_s = ctx;
+
+ if (wpa_s->conf->key_mgmt_offload &&
+ (wpa_s->drv_flags & WPA_DRIVER_FLAGS_KEY_MGMT_OFFLOAD))
+ return wpa_drv_set_key(wpa_s, WPA_ALG_PMK, NULL, 0, 0,
+ NULL, 0, pmk, pmk_len);
+ else
+ return 0;
+}
+
+
+int wpa_supplicant_init_wpa(struct wpa_supplicant *wpa_s)
+{
+#ifndef CONFIG_NO_WPA
+ struct wpa_sm_ctx *ctx;
+ ctx = os_zalloc(sizeof(*ctx));
+ if (ctx == NULL) {
+ wpa_printf(MSG_ERROR, "Failed to allocate WPA context.");
+ return -1;
+ }
+
+ ctx->ctx = wpa_s;
+ ctx->msg_ctx = wpa_s;
+ ctx->set_state = _wpa_supplicant_set_state;
+ ctx->get_state = _wpa_supplicant_get_state;
+ ctx->deauthenticate = _wpa_supplicant_deauthenticate;
+ ctx->set_key = wpa_supplicant_set_key;
+ ctx->get_network_ctx = wpa_supplicant_get_network_ctx;
+ ctx->get_bssid = wpa_supplicant_get_bssid;
+ ctx->ether_send = _wpa_ether_send;
+ ctx->get_beacon_ie = wpa_supplicant_get_beacon_ie;
+ ctx->alloc_eapol = _wpa_alloc_eapol;
+ ctx->cancel_auth_timeout = _wpa_supplicant_cancel_auth_timeout;
+ ctx->add_pmkid = wpa_supplicant_add_pmkid;
+ ctx->remove_pmkid = wpa_supplicant_remove_pmkid;
+#ifndef CONFIG_NO_CONFIG_BLOBS
+ ctx->set_config_blob = wpa_supplicant_set_config_blob;
+ ctx->get_config_blob = wpa_supplicant_get_config_blob;
+#endif /* CONFIG_NO_CONFIG_BLOBS */
+ ctx->mlme_setprotection = wpa_supplicant_mlme_setprotection;
+#ifdef CONFIG_IEEE80211R
+ ctx->update_ft_ies = wpa_supplicant_update_ft_ies;
+ ctx->send_ft_action = wpa_supplicant_send_ft_action;
+ ctx->mark_authenticated = wpa_supplicant_mark_authenticated;
+#endif /* CONFIG_IEEE80211R */
+#ifdef CONFIG_TDLS
+ ctx->tdls_get_capa = wpa_supplicant_tdls_get_capa;
+ ctx->send_tdls_mgmt = wpa_supplicant_send_tdls_mgmt;
+ ctx->tdls_oper = wpa_supplicant_tdls_oper;
+ ctx->tdls_peer_addset = wpa_supplicant_tdls_peer_addset;
+ ctx->tdls_enable_channel_switch =
+ wpa_supplicant_tdls_enable_channel_switch;
+ ctx->tdls_disable_channel_switch =
+ wpa_supplicant_tdls_disable_channel_switch;
+#endif /* CONFIG_TDLS */
+ ctx->set_rekey_offload = wpa_supplicant_set_rekey_offload;
+ ctx->key_mgmt_set_pmk = wpa_supplicant_key_mgmt_set_pmk;
+
+ wpa_s->wpa = wpa_sm_init(ctx);
+ if (wpa_s->wpa == NULL) {
+ wpa_printf(MSG_ERROR, "Failed to initialize WPA state "
+ "machine");
+ os_free(ctx);
+ return -1;
+ }
+#endif /* CONFIG_NO_WPA */
+
+ return 0;
+}
+
+
+void wpa_supplicant_rsn_supp_set_config(struct wpa_supplicant *wpa_s,
+ struct wpa_ssid *ssid)
+{
+ struct rsn_supp_config conf;
+ if (ssid) {
+ os_memset(&conf, 0, sizeof(conf));
+ conf.network_ctx = ssid;
+ conf.peerkey_enabled = ssid->peerkey;
+ conf.allowed_pairwise_cipher = ssid->pairwise_cipher;
+#ifdef IEEE8021X_EAPOL
+ conf.proactive_key_caching = ssid->proactive_key_caching < 0 ?
+ wpa_s->conf->okc : ssid->proactive_key_caching;
+ conf.eap_workaround = ssid->eap_workaround;
+ conf.eap_conf_ctx = &ssid->eap;
+#endif /* IEEE8021X_EAPOL */
+ conf.ssid = ssid->ssid;
+ conf.ssid_len = ssid->ssid_len;
+ conf.wpa_ptk_rekey = ssid->wpa_ptk_rekey;
+#ifdef CONFIG_P2P
+ if (ssid->p2p_group && wpa_s->current_bss &&
+ !wpa_s->p2p_disable_ip_addr_req) {
+ struct wpabuf *p2p;
+ p2p = wpa_bss_get_vendor_ie_multi(wpa_s->current_bss,
+ P2P_IE_VENDOR_TYPE);
+ if (p2p) {
+ u8 group_capab;
+ group_capab = p2p_get_group_capab(p2p);
+ if (group_capab &
+ P2P_GROUP_CAPAB_IP_ADDR_ALLOCATION)
+ conf.p2p = 1;
+ wpabuf_free(p2p);
+ }
+ }
+#endif /* CONFIG_P2P */
+ }
+ wpa_sm_set_config(wpa_s->wpa, ssid ? &conf : NULL);
+}
diff --git a/freebsd/contrib/wpa/wpa_supplicant/wpas_glue.h b/freebsd/contrib/wpa/wpa_supplicant/wpas_glue.h
new file mode 100644
index 00000000..5585e561
--- /dev/null
+++ b/freebsd/contrib/wpa/wpa_supplicant/wpas_glue.h
@@ -0,0 +1,28 @@
+/*
+ * WPA Supplicant - Glue code to setup EAPOL and RSN modules
+ * Copyright (c) 2003-2008, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef WPAS_GLUE_H
+#define WPAS_GLUE_H
+
+enum wpa_ctrl_req_type;
+
+int wpa_supplicant_init_eapol(struct wpa_supplicant *wpa_s);
+int wpa_supplicant_init_wpa(struct wpa_supplicant *wpa_s);
+void wpa_supplicant_rsn_supp_set_config(struct wpa_supplicant *wpa_s,
+ struct wpa_ssid *ssid);
+
+const char * wpa_supplicant_ctrl_req_to_string(enum wpa_ctrl_req_type field,
+ const char *default_txt,
+ const char **txt);
+
+enum wpa_ctrl_req_type wpa_supplicant_ctrl_req_from_string(const char *field);
+
+void wpas_send_ctrl_req(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid,
+ const char *field_name, const char *txt);
+
+#endif /* WPAS_GLUE_H */
diff --git a/freebsd/contrib/wpa/wpa_supplicant/wpas_kay.h b/freebsd/contrib/wpa/wpa_supplicant/wpas_kay.h
new file mode 100644
index 00000000..b7236d07
--- /dev/null
+++ b/freebsd/contrib/wpa/wpa_supplicant/wpas_kay.h
@@ -0,0 +1,41 @@
+/*
+ * IEEE 802.1X-2010 KaY Interface
+ * Copyright (c) 2013-2014, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef WPAS_KAY_H
+#define WPAS_KAY_H
+
+#ifdef CONFIG_MACSEC
+
+int ieee802_1x_alloc_kay_sm(struct wpa_supplicant *wpa_s,
+ struct wpa_ssid *ssid);
+void * ieee802_1x_notify_create_actor(struct wpa_supplicant *wpa_s,
+ const u8 *peer_addr);
+void ieee802_1x_dealloc_kay_sm(struct wpa_supplicant *wpa_s);
+
+#else /* CONFIG_MACSEC */
+
+static inline int ieee802_1x_alloc_kay_sm(struct wpa_supplicant *wpa_s,
+ struct wpa_ssid *ssid)
+{
+ return 0;
+}
+
+static inline void *
+ieee802_1x_notify_create_actor(struct wpa_supplicant *wpa_s,
+ const u8 *peer_addr)
+{
+ return NULL;
+}
+
+static inline void ieee802_1x_dealloc_kay_sm(struct wpa_supplicant *wpa_s)
+{
+}
+
+#endif /* CONFIG_MACSEC */
+
+#endif /* WPAS_KAY_H */
diff --git a/freebsd/contrib/wpa/wpa_supplicant/wps_supplicant.c b/freebsd/contrib/wpa/wpa_supplicant/wps_supplicant.c
new file mode 100644
index 00000000..b3c061b3
--- /dev/null
+++ b/freebsd/contrib/wpa/wpa_supplicant/wps_supplicant.c
@@ -0,0 +1,2904 @@
+#include <machine/rtems-bsd-user-space.h>
+
+/*
+ * wpa_supplicant / WPS integration
+ * Copyright (c) 2008-2014, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "eloop.h"
+#include "uuid.h"
+#include "crypto/random.h"
+#include "crypto/dh_group5.h"
+#include "common/ieee802_11_defs.h"
+#include "common/ieee802_11_common.h"
+#include "common/wpa_common.h"
+#include "common/wpa_ctrl.h"
+#include "eap_common/eap_wsc_common.h"
+#include "eap_peer/eap.h"
+#include "eapol_supp/eapol_supp_sm.h"
+#include "rsn_supp/wpa.h"
+#include "wps/wps_attr_parse.h"
+#include "config.h"
+#include "wpa_supplicant_i.h"
+#include "driver_i.h"
+#include "notify.h"
+#include "blacklist.h"
+#include "bss.h"
+#include "scan.h"
+#include "ap.h"
+#include "p2p/p2p.h"
+#include "p2p_supplicant.h"
+#include "wps_supplicant.h"
+
+
+#ifndef WPS_PIN_SCAN_IGNORE_SEL_REG
+#define WPS_PIN_SCAN_IGNORE_SEL_REG 3
+#endif /* WPS_PIN_SCAN_IGNORE_SEL_REG */
+
+/*
+ * The minimum time in seconds before trying to associate to a WPS PIN AP that
+ * does not have Selected Registrar TRUE.
+ */
+#ifndef WPS_PIN_TIME_IGNORE_SEL_REG
+#define WPS_PIN_TIME_IGNORE_SEL_REG 5
+#endif /* WPS_PIN_TIME_IGNORE_SEL_REG */
+
+static void wpas_wps_timeout(void *eloop_ctx, void *timeout_ctx);
+static void wpas_clear_wps(struct wpa_supplicant *wpa_s);
+
+
+static void wpas_wps_clear_ap_info(struct wpa_supplicant *wpa_s)
+{
+ os_free(wpa_s->wps_ap);
+ wpa_s->wps_ap = NULL;
+ wpa_s->num_wps_ap = 0;
+ wpa_s->wps_ap_iter = 0;
+}
+
+
+static void wpas_wps_assoc_with_cred(void *eloop_ctx, void *timeout_ctx)
+{
+ struct wpa_supplicant *wpa_s = eloop_ctx;
+ int use_fast_assoc = timeout_ctx != NULL;
+
+ wpa_printf(MSG_DEBUG, "WPS: Continuing association after eapol_cb");
+ if (!use_fast_assoc ||
+ wpa_supplicant_fast_associate(wpa_s) != 1)
+ wpa_supplicant_req_scan(wpa_s, 0, 0);
+}
+
+
+static void wpas_wps_assoc_with_cred_cancel(struct wpa_supplicant *wpa_s)
+{
+ eloop_cancel_timeout(wpas_wps_assoc_with_cred, wpa_s, (void *) 0);
+ eloop_cancel_timeout(wpas_wps_assoc_with_cred, wpa_s, (void *) 1);
+}
+
+
+int wpas_wps_eapol_cb(struct wpa_supplicant *wpa_s)
+{
+ if (wpas_p2p_wps_eapol_cb(wpa_s) > 0)
+ return 1;
+
+ if (!wpa_s->wps_success &&
+ wpa_s->current_ssid &&
+ eap_is_wps_pin_enrollee(&wpa_s->current_ssid->eap)) {
+ const u8 *bssid = wpa_s->bssid;
+ if (is_zero_ether_addr(bssid))
+ bssid = wpa_s->pending_bssid;
+
+ wpa_printf(MSG_DEBUG, "WPS: PIN registration with " MACSTR
+ " did not succeed - continue trying to find "
+ "suitable AP", MAC2STR(bssid));
+ wpa_blacklist_add(wpa_s, bssid);
+
+ wpa_supplicant_deauthenticate(wpa_s,
+ WLAN_REASON_DEAUTH_LEAVING);
+ wpa_s->reassociate = 1;
+ wpa_supplicant_req_scan(wpa_s,
+ wpa_s->blacklist_cleared ? 5 : 0, 0);
+ wpa_s->blacklist_cleared = 0;
+ return 1;
+ }
+
+ wpas_wps_clear_ap_info(wpa_s);
+ eloop_cancel_timeout(wpas_wps_timeout, wpa_s, NULL);
+ if (wpa_s->key_mgmt == WPA_KEY_MGMT_WPS && !wpa_s->wps_success)
+ wpa_msg(wpa_s, MSG_INFO, WPS_EVENT_FAIL);
+
+ if (wpa_s->key_mgmt == WPA_KEY_MGMT_WPS && wpa_s->current_ssid &&
+ !(wpa_s->current_ssid->key_mgmt & WPA_KEY_MGMT_WPS)) {
+ int disabled = wpa_s->current_ssid->disabled;
+ unsigned int freq = wpa_s->assoc_freq;
+ struct wpa_bss *bss;
+ struct wpa_ssid *ssid = NULL;
+ int use_fast_assoc = 0;
+
+ wpa_printf(MSG_DEBUG, "WPS: Network configuration replaced - "
+ "try to associate with the received credential "
+ "(freq=%u)", freq);
+ wpa_s->own_disconnect_req = 1;
+ wpa_supplicant_deauthenticate(wpa_s,
+ WLAN_REASON_DEAUTH_LEAVING);
+ if (disabled) {
+ wpa_printf(MSG_DEBUG, "WPS: Current network is "
+ "disabled - wait for user to enable");
+ return 1;
+ }
+ wpa_s->after_wps = 5;
+ wpa_s->wps_freq = freq;
+ wpa_s->normal_scans = 0;
+ wpa_s->reassociate = 1;
+
+ wpa_printf(MSG_DEBUG, "WPS: Checking whether fast association "
+ "without a new scan can be used");
+ bss = wpa_supplicant_pick_network(wpa_s, &ssid);
+ if (bss) {
+ struct wpabuf *wps;
+ struct wps_parse_attr attr;
+
+ wps = wpa_bss_get_vendor_ie_multi(bss,
+ WPS_IE_VENDOR_TYPE);
+ if (wps && wps_parse_msg(wps, &attr) == 0 &&
+ attr.wps_state &&
+ *attr.wps_state == WPS_STATE_CONFIGURED)
+ use_fast_assoc = 1;
+ wpabuf_free(wps);
+ }
+
+ /*
+ * Complete the next step from an eloop timeout to allow pending
+ * driver events related to the disconnection to be processed
+ * first. This makes it less likely for disconnection event to
+ * cause problems with the following connection.
+ */
+ wpa_printf(MSG_DEBUG, "WPS: Continue association from timeout");
+ wpas_wps_assoc_with_cred_cancel(wpa_s);
+ eloop_register_timeout(0, 10000,
+ wpas_wps_assoc_with_cred, wpa_s,
+ use_fast_assoc ? (void *) 1 :
+ (void *) 0);
+ return 1;
+ }
+
+ if (wpa_s->key_mgmt == WPA_KEY_MGMT_WPS && wpa_s->current_ssid) {
+ wpa_printf(MSG_DEBUG, "WPS: Registration completed - waiting "
+ "for external credential processing");
+ wpas_clear_wps(wpa_s);
+ wpa_s->own_disconnect_req = 1;
+ wpa_supplicant_deauthenticate(wpa_s,
+ WLAN_REASON_DEAUTH_LEAVING);
+ return 1;
+ }
+
+ return 0;
+}
+
+
+static void wpas_wps_security_workaround(struct wpa_supplicant *wpa_s,
+ struct wpa_ssid *ssid,
+ const struct wps_credential *cred)
+{
+ struct wpa_driver_capa capa;
+ struct wpa_bss *bss;
+ const u8 *ie;
+ struct wpa_ie_data adv;
+ int wpa2 = 0, ccmp = 0;
+
+ /*
+ * Many existing WPS APs do not know how to negotiate WPA2 or CCMP in
+ * case they are configured for mixed mode operation (WPA+WPA2 and
+ * TKIP+CCMP). Try to use scan results to figure out whether the AP
+ * actually supports stronger security and select that if the client
+ * has support for it, too.
+ */
+
+ if (wpa_drv_get_capa(wpa_s, &capa))
+ return; /* Unknown what driver supports */
+
+ if (ssid->ssid == NULL)
+ return;
+ bss = wpa_bss_get(wpa_s, cred->mac_addr, ssid->ssid, ssid->ssid_len);
+ if (bss == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS: The AP was not found from BSS "
+ "table - use credential as-is");
+ return;
+ }
+
+ wpa_printf(MSG_DEBUG, "WPS: AP found from BSS table");
+
+ ie = wpa_bss_get_ie(bss, WLAN_EID_RSN);
+ if (ie && wpa_parse_wpa_ie(ie, 2 + ie[1], &adv) == 0) {
+ wpa2 = 1;
+ if (adv.pairwise_cipher & WPA_CIPHER_CCMP)
+ ccmp = 1;
+ } else {
+ ie = wpa_bss_get_vendor_ie(bss, WPA_IE_VENDOR_TYPE);
+ if (ie && wpa_parse_wpa_ie(ie, 2 + ie[1], &adv) == 0 &&
+ adv.pairwise_cipher & WPA_CIPHER_CCMP)
+ ccmp = 1;
+ }
+
+ if (ie == NULL && (ssid->proto & WPA_PROTO_WPA) &&
+ (ssid->pairwise_cipher & WPA_CIPHER_TKIP)) {
+ /*
+ * TODO: This could be the initial AP configuration and the
+ * Beacon contents could change shortly. Should request a new
+ * scan and delay addition of the network until the updated
+ * scan results are available.
+ */
+ wpa_printf(MSG_DEBUG, "WPS: The AP did not yet advertise WPA "
+ "support - use credential as-is");
+ return;
+ }
+
+ if (ccmp && !(ssid->pairwise_cipher & WPA_CIPHER_CCMP) &&
+ (ssid->pairwise_cipher & WPA_CIPHER_TKIP) &&
+ (capa.key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_WPA2_PSK)) {
+ wpa_printf(MSG_DEBUG, "WPS: Add CCMP into the credential "
+ "based on scan results");
+ if (wpa_s->conf->ap_scan == 1)
+ ssid->pairwise_cipher |= WPA_CIPHER_CCMP;
+ else
+ ssid->pairwise_cipher = WPA_CIPHER_CCMP;
+ }
+
+ if (wpa2 && !(ssid->proto & WPA_PROTO_RSN) &&
+ (ssid->proto & WPA_PROTO_WPA) &&
+ (capa.enc & WPA_DRIVER_CAPA_ENC_CCMP)) {
+ wpa_printf(MSG_DEBUG, "WPS: Add WPA2 into the credential "
+ "based on scan results");
+ if (wpa_s->conf->ap_scan == 1)
+ ssid->proto |= WPA_PROTO_RSN;
+ else
+ ssid->proto = WPA_PROTO_RSN;
+ }
+}
+
+
+static void wpas_wps_remove_dup_network(struct wpa_supplicant *wpa_s,
+ struct wpa_ssid *new_ssid)
+{
+ struct wpa_ssid *ssid, *next;
+
+ for (ssid = wpa_s->conf->ssid, next = ssid ? ssid->next : NULL; ssid;
+ ssid = next, next = ssid ? ssid->next : NULL) {
+ /*
+ * new_ssid has already been added to the list in
+ * wpas_wps_add_network(), so skip it.
+ */
+ if (ssid == new_ssid)
+ continue;
+
+ if (ssid->bssid_set || new_ssid->bssid_set) {
+ if (ssid->bssid_set != new_ssid->bssid_set)
+ continue;
+ if (os_memcmp(ssid->bssid, new_ssid->bssid, ETH_ALEN) !=
+ 0)
+ continue;
+ }
+
+ /* compare SSID */
+ if (ssid->ssid_len == 0 || ssid->ssid_len != new_ssid->ssid_len)
+ continue;
+
+ if (ssid->ssid && new_ssid->ssid) {
+ if (os_memcmp(ssid->ssid, new_ssid->ssid,
+ ssid->ssid_len) != 0)
+ continue;
+ } else if (ssid->ssid || new_ssid->ssid)
+ continue;
+
+ /* compare security parameters */
+ if (ssid->auth_alg != new_ssid->auth_alg ||
+ ssid->key_mgmt != new_ssid->key_mgmt ||
+ (ssid->group_cipher != new_ssid->group_cipher &&
+ !(ssid->group_cipher & new_ssid->group_cipher &
+ WPA_CIPHER_CCMP)))
+ continue;
+
+ /*
+ * Some existing WPS APs will send two creds in case they are
+ * configured for mixed mode operation (WPA+WPA2 and TKIP+CCMP).
+ * Try to merge these two creds if they are received in the same
+ * M8 message.
+ */
+ if (ssid->wps_run && ssid->wps_run == new_ssid->wps_run &&
+ wpa_key_mgmt_wpa_psk(ssid->key_mgmt)) {
+ if (new_ssid->passphrase && ssid->passphrase &&
+ os_strcmp(new_ssid->passphrase, ssid->passphrase) !=
+ 0) {
+ wpa_printf(MSG_DEBUG,
+ "WPS: M8 Creds with different passphrase - do not merge");
+ continue;
+ }
+
+ if (new_ssid->psk_set &&
+ (!ssid->psk_set ||
+ os_memcmp(new_ssid->psk, ssid->psk, 32) != 0)) {
+ wpa_printf(MSG_DEBUG,
+ "WPS: M8 Creds with different PSK - do not merge");
+ continue;
+ }
+
+ if ((new_ssid->passphrase && !ssid->passphrase) ||
+ (!new_ssid->passphrase && ssid->passphrase)) {
+ wpa_printf(MSG_DEBUG,
+ "WPS: M8 Creds with different passphrase/PSK type - do not merge");
+ continue;
+ }
+
+ wpa_printf(MSG_DEBUG,
+ "WPS: Workaround - merge likely WPA/WPA2-mixed mode creds in same M8 message");
+ new_ssid->proto |= ssid->proto;
+ new_ssid->pairwise_cipher |= ssid->pairwise_cipher;
+ } else {
+ /*
+ * proto and pairwise_cipher difference matter for
+ * non-mixed-mode creds.
+ */
+ if (ssid->proto != new_ssid->proto ||
+ ssid->pairwise_cipher != new_ssid->pairwise_cipher)
+ continue;
+ }
+
+ /* Remove the duplicated older network entry. */
+ wpa_printf(MSG_DEBUG, "Remove duplicate network %d", ssid->id);
+ wpas_notify_network_removed(wpa_s, ssid);
+ if (wpa_s->current_ssid == ssid)
+ wpa_s->current_ssid = NULL;
+ wpa_config_remove_network(wpa_s->conf, ssid->id);
+ }
+}
+
+
+static int wpa_supplicant_wps_cred(void *ctx,
+ const struct wps_credential *cred)
+{
+ struct wpa_supplicant *wpa_s = ctx;
+ struct wpa_ssid *ssid = wpa_s->current_ssid;
+ u16 auth_type;
+#ifdef CONFIG_WPS_REG_DISABLE_OPEN
+ int registrar = 0;
+#endif /* CONFIG_WPS_REG_DISABLE_OPEN */
+
+ if ((wpa_s->conf->wps_cred_processing == 1 ||
+ wpa_s->conf->wps_cred_processing == 2) && cred->cred_attr) {
+ size_t blen = cred->cred_attr_len * 2 + 1;
+ char *buf = os_malloc(blen);
+ if (buf) {
+ wpa_snprintf_hex(buf, blen,
+ cred->cred_attr, cred->cred_attr_len);
+ wpa_msg(wpa_s, MSG_INFO, "%s%s",
+ WPS_EVENT_CRED_RECEIVED, buf);
+ os_free(buf);
+ }
+
+ wpas_notify_wps_credential(wpa_s, cred);
+ } else
+ wpa_msg(wpa_s, MSG_INFO, WPS_EVENT_CRED_RECEIVED);
+
+ wpa_hexdump_key(MSG_DEBUG, "WPS: Received Credential attribute",
+ cred->cred_attr, cred->cred_attr_len);
+
+ if (wpa_s->conf->wps_cred_processing == 1)
+ return 0;
+
+ wpa_hexdump_ascii(MSG_DEBUG, "WPS: SSID", cred->ssid, cred->ssid_len);
+ wpa_printf(MSG_DEBUG, "WPS: Authentication Type 0x%x",
+ cred->auth_type);
+ wpa_printf(MSG_DEBUG, "WPS: Encryption Type 0x%x", cred->encr_type);
+ wpa_printf(MSG_DEBUG, "WPS: Network Key Index %d", cred->key_idx);
+ wpa_hexdump_key(MSG_DEBUG, "WPS: Network Key",
+ cred->key, cred->key_len);
+ wpa_printf(MSG_DEBUG, "WPS: MAC Address " MACSTR,
+ MAC2STR(cred->mac_addr));
+
+ auth_type = cred->auth_type;
+ if (auth_type == (WPS_AUTH_WPAPSK | WPS_AUTH_WPA2PSK)) {
+ wpa_printf(MSG_DEBUG, "WPS: Workaround - convert mixed-mode "
+ "auth_type into WPA2PSK");
+ auth_type = WPS_AUTH_WPA2PSK;
+ }
+
+ if (auth_type != WPS_AUTH_OPEN &&
+ auth_type != WPS_AUTH_WPAPSK &&
+ auth_type != WPS_AUTH_WPA2PSK) {
+ wpa_printf(MSG_DEBUG, "WPS: Ignored credentials for "
+ "unsupported authentication type 0x%x",
+ auth_type);
+ return 0;
+ }
+
+ if (auth_type == WPS_AUTH_WPAPSK || auth_type == WPS_AUTH_WPA2PSK) {
+ if (cred->key_len < 8 || cred->key_len > 2 * PMK_LEN) {
+ wpa_printf(MSG_ERROR, "WPS: Reject PSK credential with "
+ "invalid Network Key length %lu",
+ (unsigned long) cred->key_len);
+ return -1;
+ }
+ }
+
+ if (ssid && (ssid->key_mgmt & WPA_KEY_MGMT_WPS)) {
+ wpa_printf(MSG_DEBUG, "WPS: Replace WPS network block based "
+ "on the received credential");
+#ifdef CONFIG_WPS_REG_DISABLE_OPEN
+ if (ssid->eap.identity &&
+ ssid->eap.identity_len == WSC_ID_REGISTRAR_LEN &&
+ os_memcmp(ssid->eap.identity, WSC_ID_REGISTRAR,
+ WSC_ID_REGISTRAR_LEN) == 0)
+ registrar = 1;
+#endif /* CONFIG_WPS_REG_DISABLE_OPEN */
+ os_free(ssid->eap.identity);
+ ssid->eap.identity = NULL;
+ ssid->eap.identity_len = 0;
+ os_free(ssid->eap.phase1);
+ ssid->eap.phase1 = NULL;
+ os_free(ssid->eap.eap_methods);
+ ssid->eap.eap_methods = NULL;
+ if (!ssid->p2p_group) {
+ ssid->temporary = 0;
+ ssid->bssid_set = 0;
+ }
+ ssid->disabled_until.sec = 0;
+ ssid->disabled_until.usec = 0;
+ ssid->auth_failures = 0;
+ } else {
+ wpa_printf(MSG_DEBUG, "WPS: Create a new network based on the "
+ "received credential");
+ ssid = wpa_config_add_network(wpa_s->conf);
+ if (ssid == NULL)
+ return -1;
+ if (wpa_s->current_ssid) {
+ /*
+ * Should the GO issue multiple credentials for some
+ * reason, each credential should be marked as a
+ * temporary P2P group similarly to the one that gets
+ * marked as such based on the pre-configured values
+ * used for the WPS network block.
+ */
+ ssid->p2p_group = wpa_s->current_ssid->p2p_group;
+ ssid->temporary = wpa_s->current_ssid->temporary;
+ }
+ wpas_notify_network_added(wpa_s, ssid);
+ }
+
+ wpa_config_set_network_defaults(ssid);
+ ssid->wps_run = wpa_s->wps_run;
+
+ os_free(ssid->ssid);
+ ssid->ssid = os_malloc(cred->ssid_len);
+ if (ssid->ssid) {
+ os_memcpy(ssid->ssid, cred->ssid, cred->ssid_len);
+ ssid->ssid_len = cred->ssid_len;
+ }
+
+ switch (cred->encr_type) {
+ case WPS_ENCR_NONE:
+ break;
+ case WPS_ENCR_TKIP:
+ ssid->pairwise_cipher = WPA_CIPHER_TKIP;
+ break;
+ case WPS_ENCR_AES:
+ ssid->pairwise_cipher = WPA_CIPHER_CCMP;
+ if (wpa_s->drv_capa_known &&
+ (wpa_s->drv_enc & WPA_DRIVER_CAPA_ENC_GCMP)) {
+ ssid->pairwise_cipher |= WPA_CIPHER_GCMP;
+ ssid->group_cipher |= WPA_CIPHER_GCMP;
+ }
+ break;
+ }
+
+ switch (auth_type) {
+ case WPS_AUTH_OPEN:
+ ssid->auth_alg = WPA_AUTH_ALG_OPEN;
+ ssid->key_mgmt = WPA_KEY_MGMT_NONE;
+ ssid->proto = 0;
+#ifdef CONFIG_WPS_REG_DISABLE_OPEN
+ if (registrar) {
+ wpa_msg(wpa_s, MSG_INFO, WPS_EVENT_OPEN_NETWORK
+ "id=%d - Credentials for an open "
+ "network disabled by default - use "
+ "'select_network %d' to enable",
+ ssid->id, ssid->id);
+ ssid->disabled = 1;
+ }
+#endif /* CONFIG_WPS_REG_DISABLE_OPEN */
+ break;
+ case WPS_AUTH_WPAPSK:
+ ssid->auth_alg = WPA_AUTH_ALG_OPEN;
+ ssid->key_mgmt = WPA_KEY_MGMT_PSK;
+ ssid->proto = WPA_PROTO_WPA;
+ break;
+ case WPS_AUTH_WPA2PSK:
+ ssid->auth_alg = WPA_AUTH_ALG_OPEN;
+ ssid->key_mgmt = WPA_KEY_MGMT_PSK;
+ ssid->proto = WPA_PROTO_RSN;
+ break;
+ }
+
+ if (ssid->key_mgmt == WPA_KEY_MGMT_PSK) {
+ if (cred->key_len == 2 * PMK_LEN) {
+ if (hexstr2bin((const char *) cred->key, ssid->psk,
+ PMK_LEN)) {
+ wpa_printf(MSG_ERROR, "WPS: Invalid Network "
+ "Key");
+ return -1;
+ }
+ ssid->psk_set = 1;
+ ssid->export_keys = 1;
+ } else if (cred->key_len >= 8 && cred->key_len < 2 * PMK_LEN) {
+ os_free(ssid->passphrase);
+ ssid->passphrase = os_malloc(cred->key_len + 1);
+ if (ssid->passphrase == NULL)
+ return -1;
+ os_memcpy(ssid->passphrase, cred->key, cred->key_len);
+ ssid->passphrase[cred->key_len] = '\0';
+ wpa_config_update_psk(ssid);
+ ssid->export_keys = 1;
+ } else {
+ wpa_printf(MSG_ERROR, "WPS: Invalid Network Key "
+ "length %lu",
+ (unsigned long) cred->key_len);
+ return -1;
+ }
+ }
+ ssid->priority = wpa_s->conf->wps_priority;
+
+ wpas_wps_security_workaround(wpa_s, ssid, cred);
+
+ wpas_wps_remove_dup_network(wpa_s, ssid);
+
+#ifndef CONFIG_NO_CONFIG_WRITE
+ if (wpa_s->conf->update_config &&
+ wpa_config_write(wpa_s->confname, wpa_s->conf)) {
+ wpa_printf(MSG_DEBUG, "WPS: Failed to update configuration");
+ return -1;
+ }
+#endif /* CONFIG_NO_CONFIG_WRITE */
+
+ if (ssid->priority)
+ wpa_config_update_prio_list(wpa_s->conf);
+
+ /*
+ * Optimize the post-WPS scan based on the channel used during
+ * the provisioning in case EAP-Failure is not received.
+ */
+ wpa_s->after_wps = 5;
+ wpa_s->wps_freq = wpa_s->assoc_freq;
+
+ return 0;
+}
+
+
+static void wpa_supplicant_wps_event_m2d(struct wpa_supplicant *wpa_s,
+ struct wps_event_m2d *m2d)
+{
+ wpa_msg(wpa_s, MSG_INFO, WPS_EVENT_M2D
+ "dev_password_id=%d config_error=%d",
+ m2d->dev_password_id, m2d->config_error);
+ wpas_notify_wps_event_m2d(wpa_s, m2d);
+#ifdef CONFIG_P2P
+ if (wpa_s->parent && wpa_s->parent != wpa_s) {
+ wpa_msg(wpa_s->parent, MSG_INFO, WPS_EVENT_M2D
+ "dev_password_id=%d config_error=%d",
+ m2d->dev_password_id, m2d->config_error);
+ }
+ if (m2d->config_error == WPS_CFG_MULTIPLE_PBC_DETECTED) {
+ /*
+ * Notify P2P from eloop timeout to avoid issues with the
+ * interface getting removed while processing a message.
+ */
+ eloop_register_timeout(0, 0, wpas_p2p_pbc_overlap_cb, wpa_s,
+ NULL);
+ }
+#endif /* CONFIG_P2P */
+}
+
+
+static void wpas_wps_clear_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+ struct wpa_supplicant *wpa_s = eloop_ctx;
+ wpa_printf(MSG_DEBUG, "WPS: Clear WPS network from timeout");
+ wpas_clear_wps(wpa_s);
+}
+
+
+static void wpa_supplicant_wps_event_fail(struct wpa_supplicant *wpa_s,
+ struct wps_event_fail *fail)
+{
+ if (fail->error_indication > 0 &&
+ fail->error_indication < NUM_WPS_EI_VALUES) {
+ wpa_msg(wpa_s, MSG_INFO,
+ WPS_EVENT_FAIL "msg=%d config_error=%d reason=%d (%s)",
+ fail->msg, fail->config_error, fail->error_indication,
+ wps_ei_str(fail->error_indication));
+ if (wpa_s->parent && wpa_s->parent != wpa_s)
+ wpa_msg(wpa_s->parent, MSG_INFO, WPS_EVENT_FAIL
+ "msg=%d config_error=%d reason=%d (%s)",
+ fail->msg, fail->config_error,
+ fail->error_indication,
+ wps_ei_str(fail->error_indication));
+ } else {
+ wpa_msg(wpa_s, MSG_INFO,
+ WPS_EVENT_FAIL "msg=%d config_error=%d",
+ fail->msg, fail->config_error);
+ if (wpa_s->parent && wpa_s->parent != wpa_s)
+ wpa_msg(wpa_s->parent, MSG_INFO, WPS_EVENT_FAIL
+ "msg=%d config_error=%d",
+ fail->msg, fail->config_error);
+ }
+
+ /*
+ * Need to allow WPS processing to complete, e.g., by sending WSC_NACK.
+ */
+ wpa_printf(MSG_DEBUG, "WPS: Register timeout to clear WPS network");
+ eloop_cancel_timeout(wpas_wps_clear_timeout, wpa_s, NULL);
+ eloop_register_timeout(0, 100000, wpas_wps_clear_timeout, wpa_s, NULL);
+
+ wpas_notify_wps_event_fail(wpa_s, fail);
+ wpas_p2p_wps_failed(wpa_s, fail);
+}
+
+
+static void wpas_wps_reenable_networks_cb(void *eloop_ctx, void *timeout_ctx);
+
+static void wpas_wps_reenable_networks(struct wpa_supplicant *wpa_s)
+{
+ struct wpa_ssid *ssid;
+ int changed = 0;
+
+ eloop_cancel_timeout(wpas_wps_reenable_networks_cb, wpa_s, NULL);
+
+ for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next) {
+ if (ssid->disabled_for_connect && ssid->disabled) {
+ ssid->disabled_for_connect = 0;
+ ssid->disabled = 0;
+ wpas_notify_network_enabled_changed(wpa_s, ssid);
+ changed++;
+ }
+ }
+
+ if (changed) {
+#ifndef CONFIG_NO_CONFIG_WRITE
+ if (wpa_s->conf->update_config &&
+ wpa_config_write(wpa_s->confname, wpa_s->conf)) {
+ wpa_printf(MSG_DEBUG, "WPS: Failed to update "
+ "configuration");
+ }
+#endif /* CONFIG_NO_CONFIG_WRITE */
+ }
+}
+
+
+static void wpas_wps_reenable_networks_cb(void *eloop_ctx, void *timeout_ctx)
+{
+ struct wpa_supplicant *wpa_s = eloop_ctx;
+ /* Enable the networks disabled during wpas_wps_reassoc */
+ wpas_wps_reenable_networks(wpa_s);
+}
+
+
+static void wpa_supplicant_wps_event_success(struct wpa_supplicant *wpa_s)
+{
+ wpa_msg(wpa_s, MSG_INFO, WPS_EVENT_SUCCESS);
+ wpa_s->wps_success = 1;
+ wpas_notify_wps_event_success(wpa_s);
+ if (wpa_s->current_ssid)
+ wpas_clear_temp_disabled(wpa_s, wpa_s->current_ssid, 1);
+ wpa_s->extra_blacklist_count = 0;
+
+ /*
+ * Enable the networks disabled during wpas_wps_reassoc after 10
+ * seconds. The 10 seconds timer is to allow the data connection to be
+ * formed before allowing other networks to be selected.
+ */
+ eloop_register_timeout(10, 0, wpas_wps_reenable_networks_cb, wpa_s,
+ NULL);
+
+ wpas_p2p_wps_success(wpa_s, wpa_s->bssid, 0);
+}
+
+
+static void wpa_supplicant_wps_event_er_ap_add(struct wpa_supplicant *wpa_s,
+ struct wps_event_er_ap *ap)
+{
+ char uuid_str[100];
+ char dev_type[WPS_DEV_TYPE_BUFSIZE];
+
+ uuid_bin2str(ap->uuid, uuid_str, sizeof(uuid_str));
+ if (ap->pri_dev_type)
+ wps_dev_type_bin2str(ap->pri_dev_type, dev_type,
+ sizeof(dev_type));
+ else
+ dev_type[0] = '\0';
+
+ wpa_msg(wpa_s, MSG_INFO, WPS_EVENT_ER_AP_ADD "%s " MACSTR
+ " pri_dev_type=%s wps_state=%d |%s|%s|%s|%s|%s|%s|",
+ uuid_str, MAC2STR(ap->mac_addr), dev_type, ap->wps_state,
+ ap->friendly_name ? ap->friendly_name : "",
+ ap->manufacturer ? ap->manufacturer : "",
+ ap->model_description ? ap->model_description : "",
+ ap->model_name ? ap->model_name : "",
+ ap->manufacturer_url ? ap->manufacturer_url : "",
+ ap->model_url ? ap->model_url : "");
+}
+
+
+static void wpa_supplicant_wps_event_er_ap_remove(struct wpa_supplicant *wpa_s,
+ struct wps_event_er_ap *ap)
+{
+ char uuid_str[100];
+ uuid_bin2str(ap->uuid, uuid_str, sizeof(uuid_str));
+ wpa_msg(wpa_s, MSG_INFO, WPS_EVENT_ER_AP_REMOVE "%s", uuid_str);
+}
+
+
+static void wpa_supplicant_wps_event_er_enrollee_add(
+ struct wpa_supplicant *wpa_s, struct wps_event_er_enrollee *enrollee)
+{
+ char uuid_str[100];
+ char dev_type[WPS_DEV_TYPE_BUFSIZE];
+
+ uuid_bin2str(enrollee->uuid, uuid_str, sizeof(uuid_str));
+ if (enrollee->pri_dev_type)
+ wps_dev_type_bin2str(enrollee->pri_dev_type, dev_type,
+ sizeof(dev_type));
+ else
+ dev_type[0] = '\0';
+
+ wpa_msg(wpa_s, MSG_INFO, WPS_EVENT_ER_ENROLLEE_ADD "%s " MACSTR
+ " M1=%d config_methods=0x%x dev_passwd_id=%d pri_dev_type=%s "
+ "|%s|%s|%s|%s|%s|",
+ uuid_str, MAC2STR(enrollee->mac_addr), enrollee->m1_received,
+ enrollee->config_methods, enrollee->dev_passwd_id, dev_type,
+ enrollee->dev_name ? enrollee->dev_name : "",
+ enrollee->manufacturer ? enrollee->manufacturer : "",
+ enrollee->model_name ? enrollee->model_name : "",
+ enrollee->model_number ? enrollee->model_number : "",
+ enrollee->serial_number ? enrollee->serial_number : "");
+}
+
+
+static void wpa_supplicant_wps_event_er_enrollee_remove(
+ struct wpa_supplicant *wpa_s, struct wps_event_er_enrollee *enrollee)
+{
+ char uuid_str[100];
+ uuid_bin2str(enrollee->uuid, uuid_str, sizeof(uuid_str));
+ wpa_msg(wpa_s, MSG_INFO, WPS_EVENT_ER_ENROLLEE_REMOVE "%s " MACSTR,
+ uuid_str, MAC2STR(enrollee->mac_addr));
+}
+
+
+static void wpa_supplicant_wps_event_er_ap_settings(
+ struct wpa_supplicant *wpa_s,
+ struct wps_event_er_ap_settings *ap_settings)
+{
+ char uuid_str[100];
+ char key_str[65];
+ const struct wps_credential *cred = ap_settings->cred;
+
+ key_str[0] = '\0';
+ if (cred->auth_type & (WPS_AUTH_WPAPSK | WPS_AUTH_WPA2PSK)) {
+ if (cred->key_len >= 8 && cred->key_len <= 64) {
+ os_memcpy(key_str, cred->key, cred->key_len);
+ key_str[cred->key_len] = '\0';
+ }
+ }
+
+ uuid_bin2str(ap_settings->uuid, uuid_str, sizeof(uuid_str));
+ /* Use wpa_msg_ctrl to avoid showing the key in debug log */
+ wpa_msg_ctrl(wpa_s, MSG_INFO, WPS_EVENT_ER_AP_SETTINGS
+ "uuid=%s ssid=%s auth_type=0x%04x encr_type=0x%04x "
+ "key=%s",
+ uuid_str, wpa_ssid_txt(cred->ssid, cred->ssid_len),
+ cred->auth_type, cred->encr_type, key_str);
+}
+
+
+static void wpa_supplicant_wps_event_er_set_sel_reg(
+ struct wpa_supplicant *wpa_s,
+ struct wps_event_er_set_selected_registrar *ev)
+{
+ char uuid_str[100];
+
+ uuid_bin2str(ev->uuid, uuid_str, sizeof(uuid_str));
+ switch (ev->state) {
+ case WPS_ER_SET_SEL_REG_START:
+ wpa_msg(wpa_s, MSG_DEBUG, WPS_EVENT_ER_SET_SEL_REG
+ "uuid=%s state=START sel_reg=%d dev_passwd_id=%u "
+ "sel_reg_config_methods=0x%x",
+ uuid_str, ev->sel_reg, ev->dev_passwd_id,
+ ev->sel_reg_config_methods);
+ break;
+ case WPS_ER_SET_SEL_REG_DONE:
+ wpa_msg(wpa_s, MSG_DEBUG, WPS_EVENT_ER_SET_SEL_REG
+ "uuid=%s state=DONE", uuid_str);
+ break;
+ case WPS_ER_SET_SEL_REG_FAILED:
+ wpa_msg(wpa_s, MSG_INFO, WPS_EVENT_ER_SET_SEL_REG
+ "uuid=%s state=FAILED", uuid_str);
+ break;
+ }
+}
+
+
+static void wpa_supplicant_wps_event(void *ctx, enum wps_event event,
+ union wps_event_data *data)
+{
+ struct wpa_supplicant *wpa_s = ctx;
+ switch (event) {
+ case WPS_EV_M2D:
+ wpa_supplicant_wps_event_m2d(wpa_s, &data->m2d);
+ break;
+ case WPS_EV_FAIL:
+ wpa_supplicant_wps_event_fail(wpa_s, &data->fail);
+ break;
+ case WPS_EV_SUCCESS:
+ wpa_supplicant_wps_event_success(wpa_s);
+ break;
+ case WPS_EV_PWD_AUTH_FAIL:
+#ifdef CONFIG_AP
+ if (wpa_s->ap_iface && data->pwd_auth_fail.enrollee)
+ wpa_supplicant_ap_pwd_auth_fail(wpa_s);
+#endif /* CONFIG_AP */
+ break;
+ case WPS_EV_PBC_OVERLAP:
+ break;
+ case WPS_EV_PBC_TIMEOUT:
+ break;
+ case WPS_EV_PBC_ACTIVE:
+ wpa_msg(wpa_s, MSG_INFO, WPS_EVENT_ACTIVE);
+ break;
+ case WPS_EV_PBC_DISABLE:
+ wpa_msg(wpa_s, MSG_INFO, WPS_EVENT_DISABLE);
+ break;
+ case WPS_EV_ER_AP_ADD:
+ wpa_supplicant_wps_event_er_ap_add(wpa_s, &data->ap);
+ break;
+ case WPS_EV_ER_AP_REMOVE:
+ wpa_supplicant_wps_event_er_ap_remove(wpa_s, &data->ap);
+ break;
+ case WPS_EV_ER_ENROLLEE_ADD:
+ wpa_supplicant_wps_event_er_enrollee_add(wpa_s,
+ &data->enrollee);
+ break;
+ case WPS_EV_ER_ENROLLEE_REMOVE:
+ wpa_supplicant_wps_event_er_enrollee_remove(wpa_s,
+ &data->enrollee);
+ break;
+ case WPS_EV_ER_AP_SETTINGS:
+ wpa_supplicant_wps_event_er_ap_settings(wpa_s,
+ &data->ap_settings);
+ break;
+ case WPS_EV_ER_SET_SELECTED_REGISTRAR:
+ wpa_supplicant_wps_event_er_set_sel_reg(wpa_s,
+ &data->set_sel_reg);
+ break;
+ case WPS_EV_AP_PIN_SUCCESS:
+ break;
+ }
+}
+
+
+static int wpa_supplicant_wps_rf_band(void *ctx)
+{
+ struct wpa_supplicant *wpa_s = ctx;
+
+ if (!wpa_s->current_ssid || !wpa_s->assoc_freq)
+ return 0;
+
+ return (wpa_s->assoc_freq > 50000) ? WPS_RF_60GHZ :
+ (wpa_s->assoc_freq > 2484) ? WPS_RF_50GHZ : WPS_RF_24GHZ;
+}
+
+
+enum wps_request_type wpas_wps_get_req_type(struct wpa_ssid *ssid)
+{
+ if (eap_is_wps_pbc_enrollee(&ssid->eap) ||
+ eap_is_wps_pin_enrollee(&ssid->eap))
+ return WPS_REQ_ENROLLEE;
+ else
+ return WPS_REQ_REGISTRAR;
+}
+
+
+static void wpas_clear_wps(struct wpa_supplicant *wpa_s)
+{
+ int id;
+ struct wpa_ssid *ssid, *remove_ssid = NULL, *prev_current;
+
+ wpa_s->after_wps = 0;
+ wpa_s->known_wps_freq = 0;
+
+ prev_current = wpa_s->current_ssid;
+
+ /* Enable the networks disabled during wpas_wps_reassoc */
+ wpas_wps_reenable_networks(wpa_s);
+
+ eloop_cancel_timeout(wpas_wps_timeout, wpa_s, NULL);
+ eloop_cancel_timeout(wpas_wps_clear_timeout, wpa_s, NULL);
+
+ /* Remove any existing WPS network from configuration */
+ ssid = wpa_s->conf->ssid;
+ while (ssid) {
+ if (ssid->key_mgmt & WPA_KEY_MGMT_WPS) {
+ if (ssid == wpa_s->current_ssid) {
+ wpa_s->own_disconnect_req = 1;
+ wpa_supplicant_deauthenticate(
+ wpa_s, WLAN_REASON_DEAUTH_LEAVING);
+ }
+ id = ssid->id;
+ remove_ssid = ssid;
+ } else
+ id = -1;
+ ssid = ssid->next;
+ if (id >= 0) {
+ if (prev_current == remove_ssid) {
+ wpa_sm_set_config(wpa_s->wpa, NULL);
+ eapol_sm_notify_config(wpa_s->eapol, NULL,
+ NULL);
+ }
+ wpas_notify_network_removed(wpa_s, remove_ssid);
+ wpa_config_remove_network(wpa_s->conf, id);
+ }
+ }
+
+ wpas_wps_clear_ap_info(wpa_s);
+}
+
+
+static void wpas_wps_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+ struct wpa_supplicant *wpa_s = eloop_ctx;
+ union wps_event_data data;
+
+ wpa_msg(wpa_s, MSG_INFO, WPS_EVENT_TIMEOUT "Requested operation timed "
+ "out");
+ os_memset(&data, 0, sizeof(data));
+ data.fail.config_error = WPS_CFG_MSG_TIMEOUT;
+ data.fail.error_indication = WPS_EI_NO_ERROR;
+ /*
+ * Call wpas_notify_wps_event_fail() directly instead of through
+ * wpa_supplicant_wps_event() which would end up registering unnecessary
+ * timeouts (those are only for the case where the failure happens
+ * during an EAP-WSC exchange).
+ */
+ wpas_notify_wps_event_fail(wpa_s, &data.fail);
+ wpas_clear_wps(wpa_s);
+}
+
+
+static struct wpa_ssid * wpas_wps_add_network(struct wpa_supplicant *wpa_s,
+ int registrar, const u8 *dev_addr,
+ const u8 *bssid)
+{
+ struct wpa_ssid *ssid;
+
+ ssid = wpa_config_add_network(wpa_s->conf);
+ if (ssid == NULL)
+ return NULL;
+ wpas_notify_network_added(wpa_s, ssid);
+ wpa_config_set_network_defaults(ssid);
+ ssid->temporary = 1;
+ if (wpa_config_set(ssid, "key_mgmt", "WPS", 0) < 0 ||
+ wpa_config_set(ssid, "eap", "WSC", 0) < 0 ||
+ wpa_config_set(ssid, "identity", registrar ?
+ "\"" WSC_ID_REGISTRAR "\"" :
+ "\"" WSC_ID_ENROLLEE "\"", 0) < 0) {
+ wpas_notify_network_removed(wpa_s, ssid);
+ wpa_config_remove_network(wpa_s->conf, ssid->id);
+ return NULL;
+ }
+
+#ifdef CONFIG_P2P
+ if (dev_addr)
+ os_memcpy(ssid->go_p2p_dev_addr, dev_addr, ETH_ALEN);
+#endif /* CONFIG_P2P */
+
+ if (bssid) {
+#ifndef CONFIG_P2P
+ struct wpa_bss *bss;
+ int count = 0;
+#endif /* CONFIG_P2P */
+
+ os_memcpy(ssid->bssid, bssid, ETH_ALEN);
+ ssid->bssid_set = 1;
+
+ /*
+ * Note: With P2P, the SSID may change at the time the WPS
+ * provisioning is started, so better not filter the AP based
+ * on the current SSID in the scan results.
+ */
+#ifndef CONFIG_P2P
+ dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) {
+ if (os_memcmp(bssid, bss->bssid, ETH_ALEN) != 0)
+ continue;
+
+ os_free(ssid->ssid);
+ ssid->ssid = os_malloc(bss->ssid_len);
+ if (ssid->ssid == NULL)
+ break;
+ os_memcpy(ssid->ssid, bss->ssid, bss->ssid_len);
+ ssid->ssid_len = bss->ssid_len;
+ wpa_hexdump_ascii(MSG_DEBUG, "WPS: Picked SSID from "
+ "scan results",
+ ssid->ssid, ssid->ssid_len);
+ count++;
+ }
+
+ if (count > 1) {
+ wpa_printf(MSG_DEBUG, "WPS: More than one SSID found "
+ "for the AP; use wildcard");
+ os_free(ssid->ssid);
+ ssid->ssid = NULL;
+ ssid->ssid_len = 0;
+ }
+#endif /* CONFIG_P2P */
+ }
+
+ return ssid;
+}
+
+
+static void wpas_wps_temp_disable(struct wpa_supplicant *wpa_s,
+ struct wpa_ssid *selected)
+{
+ struct wpa_ssid *ssid;
+
+ if (wpa_s->current_ssid) {
+ wpa_s->own_disconnect_req = 1;
+ wpa_supplicant_deauthenticate(
+ wpa_s, WLAN_REASON_DEAUTH_LEAVING);
+ }
+
+ /* Mark all other networks disabled and trigger reassociation */
+ ssid = wpa_s->conf->ssid;
+ while (ssid) {
+ int was_disabled = ssid->disabled;
+ ssid->disabled_for_connect = 0;
+ /*
+ * In case the network object corresponds to a persistent group
+ * then do not send out network disabled signal. In addition,
+ * do not change disabled status of persistent network objects
+ * from 2 to 1 should we connect to another network.
+ */
+ if (was_disabled != 2) {
+ ssid->disabled = ssid != selected;
+ if (was_disabled != ssid->disabled) {
+ if (ssid->disabled)
+ ssid->disabled_for_connect = 1;
+ wpas_notify_network_enabled_changed(wpa_s,
+ ssid);
+ }
+ }
+ ssid = ssid->next;
+ }
+}
+
+
+static void wpas_wps_reassoc(struct wpa_supplicant *wpa_s,
+ struct wpa_ssid *selected, const u8 *bssid,
+ int freq)
+{
+ struct wpa_bss *bss;
+
+ wpa_s->wps_run++;
+ if (wpa_s->wps_run == 0)
+ wpa_s->wps_run++;
+ wpa_s->after_wps = 0;
+ wpa_s->known_wps_freq = 0;
+ if (freq) {
+ wpa_s->after_wps = 5;
+ wpa_s->wps_freq = freq;
+ } else if (bssid) {
+ bss = wpa_bss_get_bssid_latest(wpa_s, bssid);
+ if (bss && bss->freq > 0) {
+ wpa_s->known_wps_freq = 1;
+ wpa_s->wps_freq = bss->freq;
+ }
+ }
+
+ wpas_wps_temp_disable(wpa_s, selected);
+
+ wpa_s->disconnected = 0;
+ wpa_s->reassociate = 1;
+ wpa_s->scan_runs = 0;
+ wpa_s->normal_scans = 0;
+ wpa_s->wps_success = 0;
+ wpa_s->blacklist_cleared = 0;
+
+ wpa_supplicant_cancel_sched_scan(wpa_s);
+ wpa_supplicant_req_scan(wpa_s, 0, 0);
+}
+
+
+int wpas_wps_start_pbc(struct wpa_supplicant *wpa_s, const u8 *bssid,
+ int p2p_group)
+{
+ struct wpa_ssid *ssid;
+
+#ifdef CONFIG_AP
+ if (wpa_s->ap_iface) {
+ wpa_printf(MSG_DEBUG,
+ "WPS: Reject request to start Registrar(as station) operation while AP mode is enabled");
+ return -1;
+ }
+#endif /* CONFIG_AP */
+ wpas_clear_wps(wpa_s);
+ ssid = wpas_wps_add_network(wpa_s, 0, NULL, bssid);
+ if (ssid == NULL)
+ return -1;
+ ssid->temporary = 1;
+ ssid->p2p_group = p2p_group;
+#ifdef CONFIG_P2P
+ if (p2p_group && wpa_s->go_params && wpa_s->go_params->ssid_len) {
+ ssid->ssid = os_zalloc(wpa_s->go_params->ssid_len + 1);
+ if (ssid->ssid) {
+ ssid->ssid_len = wpa_s->go_params->ssid_len;
+ os_memcpy(ssid->ssid, wpa_s->go_params->ssid,
+ ssid->ssid_len);
+ wpa_hexdump_ascii(MSG_DEBUG, "WPS: Use specific AP "
+ "SSID", ssid->ssid, ssid->ssid_len);
+ }
+ }
+#endif /* CONFIG_P2P */
+ if (wpa_config_set(ssid, "phase1", "\"pbc=1\"", 0) < 0)
+ return -1;
+ if (wpa_s->wps_fragment_size)
+ ssid->eap.fragment_size = wpa_s->wps_fragment_size;
+ eloop_register_timeout(WPS_PBC_WALK_TIME, 0, wpas_wps_timeout,
+ wpa_s, NULL);
+ wpas_wps_reassoc(wpa_s, ssid, bssid, 0);
+ return 0;
+}
+
+
+static int wpas_wps_start_dev_pw(struct wpa_supplicant *wpa_s,
+ const u8 *dev_addr, const u8 *bssid,
+ const char *pin, int p2p_group, u16 dev_pw_id,
+ const u8 *peer_pubkey_hash,
+ const u8 *ssid_val, size_t ssid_len, int freq)
+{
+ struct wpa_ssid *ssid;
+ char val[128 + 2 * WPS_OOB_PUBKEY_HASH_LEN];
+ unsigned int rpin = 0;
+ char hash[2 * WPS_OOB_PUBKEY_HASH_LEN + 10];
+
+#ifdef CONFIG_AP
+ if (wpa_s->ap_iface) {
+ wpa_printf(MSG_DEBUG,
+ "WPS: Reject request to start Registrar(as station) operation while AP mode is enabled");
+ return -1;
+ }
+#endif /* CONFIG_AP */
+ wpas_clear_wps(wpa_s);
+ if (bssid && is_zero_ether_addr(bssid))
+ bssid = NULL;
+ ssid = wpas_wps_add_network(wpa_s, 0, dev_addr, bssid);
+ if (ssid == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS: Could not add network");
+ return -1;
+ }
+ ssid->temporary = 1;
+ ssid->p2p_group = p2p_group;
+ if (ssid_val) {
+ ssid->ssid = os_malloc(ssid_len);
+ if (ssid->ssid) {
+ os_memcpy(ssid->ssid, ssid_val, ssid_len);
+ ssid->ssid_len = ssid_len;
+ }
+ }
+ if (peer_pubkey_hash) {
+ os_memcpy(hash, " pkhash=", 8);
+ wpa_snprintf_hex_uppercase(hash + 8, sizeof(hash) - 8,
+ peer_pubkey_hash,
+ WPS_OOB_PUBKEY_HASH_LEN);
+ } else {
+ hash[0] = '\0';
+ }
+#ifdef CONFIG_P2P
+ if (p2p_group && wpa_s->go_params && wpa_s->go_params->ssid_len) {
+ os_free(ssid->ssid);
+ ssid->ssid = os_zalloc(wpa_s->go_params->ssid_len + 1);
+ if (ssid->ssid) {
+ ssid->ssid_len = wpa_s->go_params->ssid_len;
+ os_memcpy(ssid->ssid, wpa_s->go_params->ssid,
+ ssid->ssid_len);
+ wpa_hexdump_ascii(MSG_DEBUG, "WPS: Use specific AP "
+ "SSID", ssid->ssid, ssid->ssid_len);
+ }
+ }
+#endif /* CONFIG_P2P */
+ if (pin)
+ os_snprintf(val, sizeof(val), "\"pin=%s dev_pw_id=%u%s\"",
+ pin, dev_pw_id, hash);
+ else if (pin == NULL && dev_pw_id == DEV_PW_NFC_CONNECTION_HANDOVER) {
+ os_snprintf(val, sizeof(val), "\"dev_pw_id=%u%s\"",
+ dev_pw_id, hash);
+ } else {
+ rpin = wps_generate_pin();
+ os_snprintf(val, sizeof(val), "\"pin=%08d dev_pw_id=%u%s\"",
+ rpin, dev_pw_id, hash);
+ }
+ if (wpa_config_set(ssid, "phase1", val, 0) < 0) {
+ wpa_printf(MSG_DEBUG, "WPS: Failed to set phase1 '%s'", val);
+ return -1;
+ }
+ if (wpa_s->wps_fragment_size)
+ ssid->eap.fragment_size = wpa_s->wps_fragment_size;
+ eloop_register_timeout(WPS_PBC_WALK_TIME, 0, wpas_wps_timeout,
+ wpa_s, NULL);
+ wpa_s->wps_ap_iter = 1;
+ wpas_wps_reassoc(wpa_s, ssid, bssid, freq);
+ return rpin;
+}
+
+
+int wpas_wps_start_pin(struct wpa_supplicant *wpa_s, const u8 *bssid,
+ const char *pin, int p2p_group, u16 dev_pw_id)
+{
+ os_get_reltime(&wpa_s->wps_pin_start_time);
+ return wpas_wps_start_dev_pw(wpa_s, NULL, bssid, pin, p2p_group,
+ dev_pw_id, NULL, NULL, 0, 0);
+}
+
+
+void wpas_wps_pbc_overlap(struct wpa_supplicant *wpa_s)
+{
+ union wps_event_data data;
+
+ os_memset(&data, 0, sizeof(data));
+ data.fail.config_error = WPS_CFG_MULTIPLE_PBC_DETECTED;
+ data.fail.error_indication = WPS_EI_NO_ERROR;
+ /*
+ * Call wpas_notify_wps_event_fail() directly instead of through
+ * wpa_supplicant_wps_event() which would end up registering unnecessary
+ * timeouts (those are only for the case where the failure happens
+ * during an EAP-WSC exchange).
+ */
+ wpas_notify_wps_event_fail(wpa_s, &data.fail);
+}
+
+/* Cancel the wps pbc/pin requests */
+int wpas_wps_cancel(struct wpa_supplicant *wpa_s)
+{
+#ifdef CONFIG_AP
+ if (wpa_s->ap_iface) {
+ wpa_printf(MSG_DEBUG, "WPS: Cancelling in AP mode");
+ return wpa_supplicant_ap_wps_cancel(wpa_s);
+ }
+#endif /* CONFIG_AP */
+
+ if (wpa_s->wpa_state == WPA_SCANNING ||
+ wpa_s->wpa_state == WPA_DISCONNECTED) {
+ wpa_printf(MSG_DEBUG, "WPS: Cancel operation - cancel scan");
+ wpa_supplicant_cancel_scan(wpa_s);
+ wpas_clear_wps(wpa_s);
+ } else if (wpa_s->wpa_state >= WPA_ASSOCIATED) {
+ wpa_printf(MSG_DEBUG, "WPS: Cancel operation - "
+ "deauthenticate");
+ wpa_s->own_disconnect_req = 1;
+ wpa_supplicant_deauthenticate(wpa_s,
+ WLAN_REASON_DEAUTH_LEAVING);
+ wpas_clear_wps(wpa_s);
+ } else {
+ wpas_wps_reenable_networks(wpa_s);
+ wpas_wps_clear_ap_info(wpa_s);
+ if (eloop_cancel_timeout(wpas_wps_clear_timeout, wpa_s, NULL) >
+ 0)
+ wpas_clear_wps(wpa_s);
+ }
+
+ wpa_s->after_wps = 0;
+
+ return 0;
+}
+
+
+int wpas_wps_start_reg(struct wpa_supplicant *wpa_s, const u8 *bssid,
+ const char *pin, struct wps_new_ap_settings *settings)
+{
+ struct wpa_ssid *ssid;
+ char val[200];
+ char *pos, *end;
+ int res;
+
+#ifdef CONFIG_AP
+ if (wpa_s->ap_iface) {
+ wpa_printf(MSG_DEBUG,
+ "WPS: Reject request to start Registrar(as station) operation while AP mode is enabled");
+ return -1;
+ }
+#endif /* CONFIG_AP */
+ if (!pin)
+ return -1;
+ wpas_clear_wps(wpa_s);
+ ssid = wpas_wps_add_network(wpa_s, 1, NULL, bssid);
+ if (ssid == NULL)
+ return -1;
+ ssid->temporary = 1;
+ pos = val;
+ end = pos + sizeof(val);
+ res = os_snprintf(pos, end - pos, "\"pin=%s", pin);
+ if (os_snprintf_error(end - pos, res))
+ return -1;
+ pos += res;
+ if (settings) {
+ res = os_snprintf(pos, end - pos, " new_ssid=%s new_auth=%s "
+ "new_encr=%s new_key=%s",
+ settings->ssid_hex, settings->auth,
+ settings->encr, settings->key_hex);
+ if (os_snprintf_error(end - pos, res))
+ return -1;
+ pos += res;
+ }
+ res = os_snprintf(pos, end - pos, "\"");
+ if (os_snprintf_error(end - pos, res))
+ return -1;
+ if (wpa_config_set(ssid, "phase1", val, 0) < 0)
+ return -1;
+ if (wpa_s->wps_fragment_size)
+ ssid->eap.fragment_size = wpa_s->wps_fragment_size;
+ eloop_register_timeout(WPS_PBC_WALK_TIME, 0, wpas_wps_timeout,
+ wpa_s, NULL);
+ wpas_wps_reassoc(wpa_s, ssid, bssid, 0);
+ return 0;
+}
+
+
+static int wpas_wps_new_psk_cb(void *ctx, const u8 *mac_addr,
+ const u8 *p2p_dev_addr, const u8 *psk,
+ size_t psk_len)
+{
+ if (is_zero_ether_addr(p2p_dev_addr)) {
+ wpa_printf(MSG_DEBUG,
+ "Received new WPA/WPA2-PSK from WPS for STA " MACSTR,
+ MAC2STR(mac_addr));
+ } else {
+ wpa_printf(MSG_DEBUG,
+ "Received new WPA/WPA2-PSK from WPS for STA " MACSTR
+ " P2P Device Addr " MACSTR,
+ MAC2STR(mac_addr), MAC2STR(p2p_dev_addr));
+ }
+ wpa_hexdump_key(MSG_DEBUG, "Per-device PSK", psk, psk_len);
+
+ /* TODO */
+
+ return 0;
+}
+
+
+static void wpas_wps_pin_needed_cb(void *ctx, const u8 *uuid_e,
+ const struct wps_device_data *dev)
+{
+ char uuid[40], txt[400];
+ int len;
+ char devtype[WPS_DEV_TYPE_BUFSIZE];
+ if (uuid_bin2str(uuid_e, uuid, sizeof(uuid)))
+ return;
+ wpa_printf(MSG_DEBUG, "WPS: PIN needed for UUID-E %s", uuid);
+ len = os_snprintf(txt, sizeof(txt), "WPS-EVENT-PIN-NEEDED %s " MACSTR
+ " [%s|%s|%s|%s|%s|%s]",
+ uuid, MAC2STR(dev->mac_addr), dev->device_name,
+ dev->manufacturer, dev->model_name,
+ dev->model_number, dev->serial_number,
+ wps_dev_type_bin2str(dev->pri_dev_type, devtype,
+ sizeof(devtype)));
+ if (!os_snprintf_error(sizeof(txt), len))
+ wpa_printf(MSG_INFO, "%s", txt);
+}
+
+
+static void wpas_wps_set_sel_reg_cb(void *ctx, int sel_reg, u16 dev_passwd_id,
+ u16 sel_reg_config_methods)
+{
+#ifdef CONFIG_WPS_ER
+ struct wpa_supplicant *wpa_s = ctx;
+
+ if (wpa_s->wps_er == NULL)
+ return;
+ wpa_printf(MSG_DEBUG, "WPS ER: SetSelectedRegistrar - sel_reg=%d "
+ "dev_password_id=%u sel_reg_config_methods=0x%x",
+ sel_reg, dev_passwd_id, sel_reg_config_methods);
+ wps_er_set_sel_reg(wpa_s->wps_er, sel_reg, dev_passwd_id,
+ sel_reg_config_methods);
+#endif /* CONFIG_WPS_ER */
+}
+
+
+static u16 wps_fix_config_methods(u16 config_methods)
+{
+ if ((config_methods &
+ (WPS_CONFIG_DISPLAY | WPS_CONFIG_VIRT_DISPLAY |
+ WPS_CONFIG_PHY_DISPLAY)) == WPS_CONFIG_DISPLAY) {
+ wpa_printf(MSG_INFO, "WPS: Converting display to "
+ "virtual_display for WPS 2.0 compliance");
+ config_methods |= WPS_CONFIG_VIRT_DISPLAY;
+ }
+ if ((config_methods &
+ (WPS_CONFIG_PUSHBUTTON | WPS_CONFIG_VIRT_PUSHBUTTON |
+ WPS_CONFIG_PHY_PUSHBUTTON)) == WPS_CONFIG_PUSHBUTTON) {
+ wpa_printf(MSG_INFO, "WPS: Converting push_button to "
+ "virtual_push_button for WPS 2.0 compliance");
+ config_methods |= WPS_CONFIG_VIRT_PUSHBUTTON;
+ }
+
+ return config_methods;
+}
+
+
+static void wpas_wps_set_uuid(struct wpa_supplicant *wpa_s,
+ struct wps_context *wps)
+{
+ char buf[50];
+ const char *src;
+
+ if (is_nil_uuid(wpa_s->conf->uuid)) {
+ struct wpa_supplicant *first;
+ first = wpa_s->global->ifaces;
+ while (first && first->next)
+ first = first->next;
+ if (first && first != wpa_s) {
+ if (wps != wpa_s->global->ifaces->wps)
+ os_memcpy(wps->uuid,
+ wpa_s->global->ifaces->wps->uuid,
+ WPS_UUID_LEN);
+ src = "from the first interface";
+ } else {
+ uuid_gen_mac_addr(wpa_s->own_addr, wps->uuid);
+ src = "based on MAC address";
+ }
+ } else {
+ os_memcpy(wps->uuid, wpa_s->conf->uuid, WPS_UUID_LEN);
+ src = "based on configuration";
+ }
+
+ uuid_bin2str(wps->uuid, buf, sizeof(buf));
+ wpa_dbg(wpa_s, MSG_DEBUG, "WPS: UUID %s: %s", src, buf);
+}
+
+
+static void wpas_wps_set_vendor_ext_m1(struct wpa_supplicant *wpa_s,
+ struct wps_context *wps)
+{
+ wpabuf_free(wps->dev.vendor_ext_m1);
+ wps->dev.vendor_ext_m1 = NULL;
+
+ if (wpa_s->conf->wps_vendor_ext_m1) {
+ wps->dev.vendor_ext_m1 =
+ wpabuf_dup(wpa_s->conf->wps_vendor_ext_m1);
+ if (!wps->dev.vendor_ext_m1) {
+ wpa_printf(MSG_ERROR, "WPS: Cannot "
+ "allocate memory for vendor_ext_m1");
+ }
+ }
+}
+
+
+int wpas_wps_init(struct wpa_supplicant *wpa_s)
+{
+ struct wps_context *wps;
+ struct wps_registrar_config rcfg;
+ struct hostapd_hw_modes *modes;
+ u16 m;
+
+ wps = os_zalloc(sizeof(*wps));
+ if (wps == NULL)
+ return -1;
+
+ wps->cred_cb = wpa_supplicant_wps_cred;
+ wps->event_cb = wpa_supplicant_wps_event;
+ wps->rf_band_cb = wpa_supplicant_wps_rf_band;
+ wps->cb_ctx = wpa_s;
+
+ wps->dev.device_name = wpa_s->conf->device_name;
+ wps->dev.manufacturer = wpa_s->conf->manufacturer;
+ wps->dev.model_name = wpa_s->conf->model_name;
+ wps->dev.model_number = wpa_s->conf->model_number;
+ wps->dev.serial_number = wpa_s->conf->serial_number;
+ wps->config_methods =
+ wps_config_methods_str2bin(wpa_s->conf->config_methods);
+ if ((wps->config_methods & (WPS_CONFIG_DISPLAY | WPS_CONFIG_LABEL)) ==
+ (WPS_CONFIG_DISPLAY | WPS_CONFIG_LABEL)) {
+ wpa_printf(MSG_ERROR, "WPS: Both Label and Display config "
+ "methods are not allowed at the same time");
+ os_free(wps);
+ return -1;
+ }
+ wps->config_methods = wps_fix_config_methods(wps->config_methods);
+ wps->dev.config_methods = wps->config_methods;
+ os_memcpy(wps->dev.pri_dev_type, wpa_s->conf->device_type,
+ WPS_DEV_TYPE_LEN);
+
+ wps->dev.num_sec_dev_types = wpa_s->conf->num_sec_device_types;
+ os_memcpy(wps->dev.sec_dev_type, wpa_s->conf->sec_device_type,
+ WPS_DEV_TYPE_LEN * wps->dev.num_sec_dev_types);
+
+ wpas_wps_set_vendor_ext_m1(wpa_s, wps);
+
+ wps->dev.os_version = WPA_GET_BE32(wpa_s->conf->os_version);
+ modes = wpa_s->hw.modes;
+ if (modes) {
+ for (m = 0; m < wpa_s->hw.num_modes; m++) {
+ if (modes[m].mode == HOSTAPD_MODE_IEEE80211B ||
+ modes[m].mode == HOSTAPD_MODE_IEEE80211G)
+ wps->dev.rf_bands |= WPS_RF_24GHZ;
+ else if (modes[m].mode == HOSTAPD_MODE_IEEE80211A)
+ wps->dev.rf_bands |= WPS_RF_50GHZ;
+ else if (modes[m].mode == HOSTAPD_MODE_IEEE80211AD)
+ wps->dev.rf_bands |= WPS_RF_60GHZ;
+ }
+ }
+ if (wps->dev.rf_bands == 0) {
+ /*
+ * Default to claiming support for both bands if the driver
+ * does not provide support for fetching supported bands.
+ */
+ wps->dev.rf_bands = WPS_RF_24GHZ | WPS_RF_50GHZ;
+ }
+ os_memcpy(wps->dev.mac_addr, wpa_s->own_addr, ETH_ALEN);
+ wpas_wps_set_uuid(wpa_s, wps);
+
+ wps->auth_types = WPS_AUTH_WPA2PSK | WPS_AUTH_WPAPSK;
+ wps->encr_types = WPS_ENCR_AES | WPS_ENCR_TKIP;
+
+ os_memset(&rcfg, 0, sizeof(rcfg));
+ rcfg.new_psk_cb = wpas_wps_new_psk_cb;
+ rcfg.pin_needed_cb = wpas_wps_pin_needed_cb;
+ rcfg.set_sel_reg_cb = wpas_wps_set_sel_reg_cb;
+ rcfg.cb_ctx = wpa_s;
+
+ wps->registrar = wps_registrar_init(wps, &rcfg);
+ if (wps->registrar == NULL) {
+ wpa_printf(MSG_DEBUG, "Failed to initialize WPS Registrar");
+ os_free(wps);
+ return -1;
+ }
+
+ wpa_s->wps = wps;
+
+ return 0;
+}
+
+
+#ifdef CONFIG_WPS_ER
+static void wpas_wps_nfc_clear(struct wps_context *wps)
+{
+ wps->ap_nfc_dev_pw_id = 0;
+ wpabuf_free(wps->ap_nfc_dh_pubkey);
+ wps->ap_nfc_dh_pubkey = NULL;
+ wpabuf_free(wps->ap_nfc_dh_privkey);
+ wps->ap_nfc_dh_privkey = NULL;
+ wpabuf_free(wps->ap_nfc_dev_pw);
+ wps->ap_nfc_dev_pw = NULL;
+}
+#endif /* CONFIG_WPS_ER */
+
+
+void wpas_wps_deinit(struct wpa_supplicant *wpa_s)
+{
+ wpas_wps_assoc_with_cred_cancel(wpa_s);
+ eloop_cancel_timeout(wpas_wps_timeout, wpa_s, NULL);
+ eloop_cancel_timeout(wpas_wps_clear_timeout, wpa_s, NULL);
+ eloop_cancel_timeout(wpas_wps_reenable_networks_cb, wpa_s, NULL);
+ wpas_wps_clear_ap_info(wpa_s);
+
+#ifdef CONFIG_P2P
+ eloop_cancel_timeout(wpas_p2p_pbc_overlap_cb, wpa_s, NULL);
+#endif /* CONFIG_P2P */
+
+ if (wpa_s->wps == NULL)
+ return;
+
+#ifdef CONFIG_WPS_ER
+ wps_er_deinit(wpa_s->wps_er, NULL, NULL);
+ wpa_s->wps_er = NULL;
+ wpas_wps_nfc_clear(wpa_s->wps);
+#endif /* CONFIG_WPS_ER */
+
+ wps_registrar_deinit(wpa_s->wps->registrar);
+ wpabuf_free(wpa_s->wps->dh_pubkey);
+ wpabuf_free(wpa_s->wps->dh_privkey);
+ wpabuf_free(wpa_s->wps->dev.vendor_ext_m1);
+ os_free(wpa_s->wps->network_key);
+ os_free(wpa_s->wps);
+ wpa_s->wps = NULL;
+}
+
+
+int wpas_wps_ssid_bss_match(struct wpa_supplicant *wpa_s,
+ struct wpa_ssid *ssid, struct wpa_bss *bss)
+{
+ struct wpabuf *wps_ie;
+
+ if (!(ssid->key_mgmt & WPA_KEY_MGMT_WPS))
+ return -1;
+
+ wps_ie = wpa_bss_get_vendor_ie_multi(bss, WPS_IE_VENDOR_TYPE);
+ if (eap_is_wps_pbc_enrollee(&ssid->eap)) {
+ if (!wps_ie) {
+ wpa_printf(MSG_DEBUG, " skip - non-WPS AP");
+ return 0;
+ }
+
+ if (!wps_is_selected_pbc_registrar(wps_ie)) {
+ wpa_printf(MSG_DEBUG, " skip - WPS AP "
+ "without active PBC Registrar");
+ wpabuf_free(wps_ie);
+ return 0;
+ }
+
+ /* TODO: overlap detection */
+ wpa_printf(MSG_DEBUG, " selected based on WPS IE "
+ "(Active PBC)");
+ wpabuf_free(wps_ie);
+ return 1;
+ }
+
+ if (eap_is_wps_pin_enrollee(&ssid->eap)) {
+ if (!wps_ie) {
+ wpa_printf(MSG_DEBUG, " skip - non-WPS AP");
+ return 0;
+ }
+
+ /*
+ * Start with WPS APs that advertise our address as an
+ * authorized MAC (v2.0) or active PIN Registrar (v1.0) and
+ * allow any WPS AP after couple of scans since some APs do not
+ * set Selected Registrar attribute properly when using
+ * external Registrar.
+ */
+ if (!wps_is_addr_authorized(wps_ie, wpa_s->own_addr, 1)) {
+ struct os_reltime age;
+
+ os_reltime_age(&wpa_s->wps_pin_start_time, &age);
+
+ if (wpa_s->scan_runs < WPS_PIN_SCAN_IGNORE_SEL_REG ||
+ age.sec < WPS_PIN_TIME_IGNORE_SEL_REG) {
+ wpa_printf(MSG_DEBUG,
+ " skip - WPS AP without active PIN Registrar (scan_runs=%d age=%d)",
+ wpa_s->scan_runs, (int) age.sec);
+ wpabuf_free(wps_ie);
+ return 0;
+ }
+ wpa_printf(MSG_DEBUG, " selected based on WPS IE");
+ } else {
+ wpa_printf(MSG_DEBUG, " selected based on WPS IE "
+ "(Authorized MAC or Active PIN)");
+ }
+ wpabuf_free(wps_ie);
+ return 1;
+ }
+
+ if (wps_ie) {
+ wpa_printf(MSG_DEBUG, " selected based on WPS IE");
+ wpabuf_free(wps_ie);
+ return 1;
+ }
+
+ return -1;
+}
+
+
+int wpas_wps_ssid_wildcard_ok(struct wpa_supplicant *wpa_s,
+ struct wpa_ssid *ssid,
+ struct wpa_bss *bss)
+{
+ struct wpabuf *wps_ie = NULL;
+ int ret = 0;
+
+ if (eap_is_wps_pbc_enrollee(&ssid->eap)) {
+ wps_ie = wpa_bss_get_vendor_ie_multi(bss, WPS_IE_VENDOR_TYPE);
+ if (wps_ie && wps_is_selected_pbc_registrar(wps_ie)) {
+ /* allow wildcard SSID for WPS PBC */
+ ret = 1;
+ }
+ } else if (eap_is_wps_pin_enrollee(&ssid->eap)) {
+ wps_ie = wpa_bss_get_vendor_ie_multi(bss, WPS_IE_VENDOR_TYPE);
+ if (wps_ie &&
+ (wps_is_addr_authorized(wps_ie, wpa_s->own_addr, 1) ||
+ wpa_s->scan_runs >= WPS_PIN_SCAN_IGNORE_SEL_REG)) {
+ /* allow wildcard SSID for WPS PIN */
+ ret = 1;
+ }
+ }
+
+ if (!ret && ssid->bssid_set &&
+ os_memcmp(ssid->bssid, bss->bssid, ETH_ALEN) == 0) {
+ /* allow wildcard SSID due to hardcoded BSSID match */
+ ret = 1;
+ }
+
+#ifdef CONFIG_WPS_STRICT
+ if (wps_ie) {
+ if (wps_validate_beacon_probe_resp(wps_ie, bss->beacon_ie_len >
+ 0, bss->bssid) < 0)
+ ret = 0;
+ if (bss->beacon_ie_len) {
+ struct wpabuf *bcn_wps;
+ bcn_wps = wpa_bss_get_vendor_ie_multi_beacon(
+ bss, WPS_IE_VENDOR_TYPE);
+ if (bcn_wps == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS: Mandatory WPS IE "
+ "missing from AP Beacon");
+ ret = 0;
+ } else {
+ if (wps_validate_beacon(wps_ie) < 0)
+ ret = 0;
+ wpabuf_free(bcn_wps);
+ }
+ }
+ }
+#endif /* CONFIG_WPS_STRICT */
+
+ wpabuf_free(wps_ie);
+
+ return ret;
+}
+
+
+int wpas_wps_scan_pbc_overlap(struct wpa_supplicant *wpa_s,
+ struct wpa_bss *selected, struct wpa_ssid *ssid)
+{
+ const u8 *sel_uuid;
+ struct wpabuf *wps_ie;
+ int ret = 0;
+ size_t i;
+
+ if (!eap_is_wps_pbc_enrollee(&ssid->eap))
+ return 0;
+
+ wpa_printf(MSG_DEBUG, "WPS: Check whether PBC session overlap is "
+ "present in scan results; selected BSSID " MACSTR,
+ MAC2STR(selected->bssid));
+
+ /* Make sure that only one AP is in active PBC mode */
+ wps_ie = wpa_bss_get_vendor_ie_multi(selected, WPS_IE_VENDOR_TYPE);
+ if (wps_ie) {
+ sel_uuid = wps_get_uuid_e(wps_ie);
+ wpa_hexdump(MSG_DEBUG, "WPS: UUID of the selected BSS",
+ sel_uuid, UUID_LEN);
+ } else {
+ wpa_printf(MSG_DEBUG, "WPS: Selected BSS does not include "
+ "WPS IE?!");
+ sel_uuid = NULL;
+ }
+
+ for (i = 0; i < wpa_s->num_wps_ap; i++) {
+ struct wps_ap_info *ap = &wpa_s->wps_ap[i];
+
+ if (!ap->pbc_active ||
+ os_memcmp(selected->bssid, ap->bssid, ETH_ALEN) == 0)
+ continue;
+
+ wpa_printf(MSG_DEBUG, "WPS: Another BSS in active PBC mode: "
+ MACSTR, MAC2STR(ap->bssid));
+ wpa_hexdump(MSG_DEBUG, "WPS: UUID of the other BSS",
+ ap->uuid, UUID_LEN);
+ if (sel_uuid == NULL ||
+ os_memcmp(sel_uuid, ap->uuid, UUID_LEN) != 0) {
+ ret = 1; /* PBC overlap */
+ wpa_msg(wpa_s, MSG_INFO, "WPS: PBC overlap detected: "
+ MACSTR " and " MACSTR,
+ MAC2STR(selected->bssid),
+ MAC2STR(ap->bssid));
+ break;
+ }
+
+ /* TODO: verify that this is reasonable dual-band situation */
+ }
+
+ wpabuf_free(wps_ie);
+
+ return ret;
+}
+
+
+void wpas_wps_notify_scan_results(struct wpa_supplicant *wpa_s)
+{
+ struct wpa_bss *bss;
+ unsigned int pbc = 0, auth = 0, pin = 0, wps = 0;
+
+ if (wpa_s->disconnected || wpa_s->wpa_state >= WPA_ASSOCIATED)
+ return;
+
+ dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) {
+ struct wpabuf *ie;
+ ie = wpa_bss_get_vendor_ie_multi(bss, WPS_IE_VENDOR_TYPE);
+ if (!ie)
+ continue;
+ if (wps_is_selected_pbc_registrar(ie))
+ pbc++;
+ else if (wps_is_addr_authorized(ie, wpa_s->own_addr, 0))
+ auth++;
+ else if (wps_is_selected_pin_registrar(ie))
+ pin++;
+ else
+ wps++;
+ wpabuf_free(ie);
+ }
+
+ if (pbc)
+ wpa_msg_ctrl(wpa_s, MSG_INFO, WPS_EVENT_AP_AVAILABLE_PBC);
+ else if (auth)
+ wpa_msg_ctrl(wpa_s, MSG_INFO, WPS_EVENT_AP_AVAILABLE_AUTH);
+ else if (pin)
+ wpa_msg_ctrl(wpa_s, MSG_INFO, WPS_EVENT_AP_AVAILABLE_PIN);
+ else if (wps)
+ wpa_msg_ctrl(wpa_s, MSG_INFO, WPS_EVENT_AP_AVAILABLE);
+}
+
+
+int wpas_wps_searching(struct wpa_supplicant *wpa_s)
+{
+ struct wpa_ssid *ssid;
+
+ for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next) {
+ if ((ssid->key_mgmt & WPA_KEY_MGMT_WPS) && !ssid->disabled)
+ return 1;
+ }
+
+ return 0;
+}
+
+
+int wpas_wps_scan_result_text(const u8 *ies, size_t ies_len, char *buf,
+ char *end)
+{
+ struct wpabuf *wps_ie;
+ int ret;
+
+ wps_ie = ieee802_11_vendor_ie_concat(ies, ies_len, WPS_DEV_OUI_WFA);
+ if (wps_ie == NULL)
+ return 0;
+
+ ret = wps_attr_text(wps_ie, buf, end);
+ wpabuf_free(wps_ie);
+ return ret;
+}
+
+
+int wpas_wps_er_start(struct wpa_supplicant *wpa_s, const char *filter)
+{
+#ifdef CONFIG_WPS_ER
+ if (wpa_s->wps_er) {
+ wps_er_refresh(wpa_s->wps_er);
+ return 0;
+ }
+ wpa_s->wps_er = wps_er_init(wpa_s->wps, wpa_s->ifname, filter);
+ if (wpa_s->wps_er == NULL)
+ return -1;
+ return 0;
+#else /* CONFIG_WPS_ER */
+ return 0;
+#endif /* CONFIG_WPS_ER */
+}
+
+
+void wpas_wps_er_stop(struct wpa_supplicant *wpa_s)
+{
+#ifdef CONFIG_WPS_ER
+ wps_er_deinit(wpa_s->wps_er, NULL, NULL);
+ wpa_s->wps_er = NULL;
+#endif /* CONFIG_WPS_ER */
+}
+
+
+#ifdef CONFIG_WPS_ER
+int wpas_wps_er_add_pin(struct wpa_supplicant *wpa_s, const u8 *addr,
+ const char *uuid, const char *pin)
+{
+ u8 u[UUID_LEN];
+ const u8 *use_uuid = NULL;
+ u8 addr_buf[ETH_ALEN];
+
+ if (os_strcmp(uuid, "any") == 0) {
+ } else if (uuid_str2bin(uuid, u) == 0) {
+ use_uuid = u;
+ } else if (hwaddr_aton(uuid, addr_buf) == 0) {
+ use_uuid = wps_er_get_sta_uuid(wpa_s->wps_er, addr_buf);
+ if (use_uuid == NULL)
+ return -1;
+ } else
+ return -1;
+ return wps_registrar_add_pin(wpa_s->wps->registrar, addr,
+ use_uuid,
+ (const u8 *) pin, os_strlen(pin), 300);
+}
+
+
+int wpas_wps_er_pbc(struct wpa_supplicant *wpa_s, const char *uuid)
+{
+ u8 u[UUID_LEN], *use_uuid = NULL;
+ u8 addr[ETH_ALEN], *use_addr = NULL;
+
+ if (uuid_str2bin(uuid, u) == 0)
+ use_uuid = u;
+ else if (hwaddr_aton(uuid, addr) == 0)
+ use_addr = addr;
+ else
+ return -1;
+ return wps_er_pbc(wpa_s->wps_er, use_uuid, use_addr);
+}
+
+
+int wpas_wps_er_learn(struct wpa_supplicant *wpa_s, const char *uuid,
+ const char *pin)
+{
+ u8 u[UUID_LEN], *use_uuid = NULL;
+ u8 addr[ETH_ALEN], *use_addr = NULL;
+
+ if (uuid_str2bin(uuid, u) == 0)
+ use_uuid = u;
+ else if (hwaddr_aton(uuid, addr) == 0)
+ use_addr = addr;
+ else
+ return -1;
+
+ return wps_er_learn(wpa_s->wps_er, use_uuid, use_addr, (const u8 *) pin,
+ os_strlen(pin));
+}
+
+
+static int wpas_wps_network_to_cred(struct wpa_ssid *ssid,
+ struct wps_credential *cred)
+{
+ os_memset(cred, 0, sizeof(*cred));
+ if (ssid->ssid_len > SSID_MAX_LEN)
+ return -1;
+ os_memcpy(cred->ssid, ssid->ssid, ssid->ssid_len);
+ cred->ssid_len = ssid->ssid_len;
+ if (ssid->key_mgmt & WPA_KEY_MGMT_PSK) {
+ cred->auth_type = (ssid->proto & WPA_PROTO_RSN) ?
+ WPS_AUTH_WPA2PSK : WPS_AUTH_WPAPSK;
+ if (ssid->pairwise_cipher & WPA_CIPHER_CCMP)
+ cred->encr_type = WPS_ENCR_AES;
+ else
+ cred->encr_type = WPS_ENCR_TKIP;
+ if (ssid->passphrase) {
+ cred->key_len = os_strlen(ssid->passphrase);
+ if (cred->key_len >= 64)
+ return -1;
+ os_memcpy(cred->key, ssid->passphrase, cred->key_len);
+ } else if (ssid->psk_set) {
+ cred->key_len = 32;
+ os_memcpy(cred->key, ssid->psk, 32);
+ } else
+ return -1;
+ } else {
+ cred->auth_type = WPS_AUTH_OPEN;
+ cred->encr_type = WPS_ENCR_NONE;
+ }
+
+ return 0;
+}
+
+
+int wpas_wps_er_set_config(struct wpa_supplicant *wpa_s, const char *uuid,
+ int id)
+{
+ u8 u[UUID_LEN], *use_uuid = NULL;
+ u8 addr[ETH_ALEN], *use_addr = NULL;
+ struct wpa_ssid *ssid;
+ struct wps_credential cred;
+ int ret;
+
+ if (uuid_str2bin(uuid, u) == 0)
+ use_uuid = u;
+ else if (hwaddr_aton(uuid, addr) == 0)
+ use_addr = addr;
+ else
+ return -1;
+ ssid = wpa_config_get_network(wpa_s->conf, id);
+ if (ssid == NULL || ssid->ssid == NULL)
+ return -1;
+
+ if (wpas_wps_network_to_cred(ssid, &cred) < 0)
+ return -1;
+ ret = wps_er_set_config(wpa_s->wps_er, use_uuid, use_addr, &cred);
+ os_memset(&cred, 0, sizeof(cred));
+ return ret;
+}
+
+
+int wpas_wps_er_config(struct wpa_supplicant *wpa_s, const char *uuid,
+ const char *pin, struct wps_new_ap_settings *settings)
+{
+ u8 u[UUID_LEN], *use_uuid = NULL;
+ u8 addr[ETH_ALEN], *use_addr = NULL;
+ struct wps_credential cred;
+ size_t len;
+
+ if (uuid_str2bin(uuid, u) == 0)
+ use_uuid = u;
+ else if (hwaddr_aton(uuid, addr) == 0)
+ use_addr = addr;
+ else
+ return -1;
+ if (settings->ssid_hex == NULL || settings->auth == NULL ||
+ settings->encr == NULL || settings->key_hex == NULL)
+ return -1;
+
+ os_memset(&cred, 0, sizeof(cred));
+ len = os_strlen(settings->ssid_hex);
+ if ((len & 1) || len > 2 * sizeof(cred.ssid) ||
+ hexstr2bin(settings->ssid_hex, cred.ssid, len / 2))
+ return -1;
+ cred.ssid_len = len / 2;
+
+ len = os_strlen(settings->key_hex);
+ if ((len & 1) || len > 2 * sizeof(cred.key) ||
+ hexstr2bin(settings->key_hex, cred.key, len / 2))
+ return -1;
+ cred.key_len = len / 2;
+
+ if (os_strcmp(settings->auth, "OPEN") == 0)
+ cred.auth_type = WPS_AUTH_OPEN;
+ else if (os_strcmp(settings->auth, "WPAPSK") == 0)
+ cred.auth_type = WPS_AUTH_WPAPSK;
+ else if (os_strcmp(settings->auth, "WPA2PSK") == 0)
+ cred.auth_type = WPS_AUTH_WPA2PSK;
+ else
+ return -1;
+
+ if (os_strcmp(settings->encr, "NONE") == 0)
+ cred.encr_type = WPS_ENCR_NONE;
+#ifdef CONFIG_TESTING_OPTIONS
+ else if (os_strcmp(settings->encr, "WEP") == 0)
+ cred.encr_type = WPS_ENCR_WEP;
+#endif /* CONFIG_TESTING_OPTIONS */
+ else if (os_strcmp(settings->encr, "TKIP") == 0)
+ cred.encr_type = WPS_ENCR_TKIP;
+ else if (os_strcmp(settings->encr, "CCMP") == 0)
+ cred.encr_type = WPS_ENCR_AES;
+ else
+ return -1;
+
+ return wps_er_config(wpa_s->wps_er, use_uuid, use_addr,
+ (const u8 *) pin, os_strlen(pin), &cred);
+}
+
+
+#ifdef CONFIG_WPS_NFC
+struct wpabuf * wpas_wps_er_nfc_config_token(struct wpa_supplicant *wpa_s,
+ int ndef, const char *uuid)
+{
+ struct wpabuf *ret;
+ u8 u[UUID_LEN], *use_uuid = NULL;
+ u8 addr[ETH_ALEN], *use_addr = NULL;
+
+ if (!wpa_s->wps_er)
+ return NULL;
+
+ if (uuid_str2bin(uuid, u) == 0)
+ use_uuid = u;
+ else if (hwaddr_aton(uuid, addr) == 0)
+ use_addr = addr;
+ else
+ return NULL;
+
+ ret = wps_er_nfc_config_token(wpa_s->wps_er, use_uuid, use_addr);
+ 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 */
+
+
+static int callbacks_pending = 0;
+
+static void wpas_wps_terminate_cb(void *ctx)
+{
+ wpa_printf(MSG_DEBUG, "WPS ER: Terminated");
+ if (--callbacks_pending <= 0)
+ eloop_terminate();
+}
+#endif /* CONFIG_WPS_ER */
+
+
+int wpas_wps_terminate_pending(struct wpa_supplicant *wpa_s)
+{
+#ifdef CONFIG_WPS_ER
+ if (wpa_s->wps_er) {
+ callbacks_pending++;
+ wps_er_deinit(wpa_s->wps_er, wpas_wps_terminate_cb, wpa_s);
+ wpa_s->wps_er = NULL;
+ return 1;
+ }
+#endif /* CONFIG_WPS_ER */
+ return 0;
+}
+
+
+void wpas_wps_update_config(struct wpa_supplicant *wpa_s)
+{
+ struct wps_context *wps = wpa_s->wps;
+
+ if (wps == NULL)
+ return;
+
+ if (wpa_s->conf->changed_parameters & CFG_CHANGED_CONFIG_METHODS) {
+ wps->config_methods = wps_config_methods_str2bin(
+ wpa_s->conf->config_methods);
+ if ((wps->config_methods &
+ (WPS_CONFIG_DISPLAY | WPS_CONFIG_LABEL)) ==
+ (WPS_CONFIG_DISPLAY | WPS_CONFIG_LABEL)) {
+ wpa_printf(MSG_ERROR, "WPS: Both Label and Display "
+ "config methods are not allowed at the "
+ "same time");
+ wps->config_methods &= ~WPS_CONFIG_LABEL;
+ }
+ }
+ wps->config_methods = wps_fix_config_methods(wps->config_methods);
+ wps->dev.config_methods = wps->config_methods;
+
+ if (wpa_s->conf->changed_parameters & CFG_CHANGED_DEVICE_TYPE)
+ os_memcpy(wps->dev.pri_dev_type, wpa_s->conf->device_type,
+ WPS_DEV_TYPE_LEN);
+
+ if (wpa_s->conf->changed_parameters & CFG_CHANGED_SEC_DEVICE_TYPE) {
+ wps->dev.num_sec_dev_types = wpa_s->conf->num_sec_device_types;
+ os_memcpy(wps->dev.sec_dev_type, wpa_s->conf->sec_device_type,
+ wps->dev.num_sec_dev_types * WPS_DEV_TYPE_LEN);
+ }
+
+ if (wpa_s->conf->changed_parameters & CFG_CHANGED_VENDOR_EXTENSION)
+ wpas_wps_set_vendor_ext_m1(wpa_s, wps);
+
+ if (wpa_s->conf->changed_parameters & CFG_CHANGED_OS_VERSION)
+ wps->dev.os_version = WPA_GET_BE32(wpa_s->conf->os_version);
+
+ if (wpa_s->conf->changed_parameters & CFG_CHANGED_UUID)
+ wpas_wps_set_uuid(wpa_s, wps);
+
+ if (wpa_s->conf->changed_parameters &
+ (CFG_CHANGED_DEVICE_NAME | CFG_CHANGED_WPS_STRING)) {
+ /* Update pointers to make sure they refer current values */
+ wps->dev.device_name = wpa_s->conf->device_name;
+ wps->dev.manufacturer = wpa_s->conf->manufacturer;
+ wps->dev.model_name = wpa_s->conf->model_name;
+ wps->dev.model_number = wpa_s->conf->model_number;
+ wps->dev.serial_number = wpa_s->conf->serial_number;
+ }
+}
+
+
+#ifdef CONFIG_WPS_NFC
+
+#ifdef CONFIG_WPS_ER
+static struct wpabuf *
+wpas_wps_network_config_token(struct wpa_supplicant *wpa_s, int ndef,
+ struct wpa_ssid *ssid)
+{
+ struct wpabuf *ret;
+ struct wps_credential cred;
+
+ if (wpas_wps_network_to_cred(ssid, &cred) < 0)
+ return NULL;
+
+ ret = wps_er_config_token_from_cred(wpa_s->wps, &cred);
+
+ 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_ER */
+
+
+struct wpabuf * wpas_wps_nfc_config_token(struct wpa_supplicant *wpa_s,
+ int ndef, const char *id_str)
+{
+#ifdef CONFIG_WPS_ER
+ if (id_str) {
+ int id;
+ char *end = NULL;
+ struct wpa_ssid *ssid;
+
+ id = strtol(id_str, &end, 10);
+ if (end && *end)
+ return NULL;
+
+ ssid = wpa_config_get_network(wpa_s->conf, id);
+ if (ssid == NULL)
+ return NULL;
+ return wpas_wps_network_config_token(wpa_s, ndef, ssid);
+ }
+#endif /* CONFIG_WPS_ER */
+#ifdef CONFIG_AP
+ if (wpa_s->ap_iface)
+ return wpas_ap_wps_nfc_config_token(wpa_s, ndef);
+#endif /* CONFIG_AP */
+ return NULL;
+}
+
+
+struct wpabuf * wpas_wps_nfc_token(struct wpa_supplicant *wpa_s, int ndef)
+{
+ if (wpa_s->conf->wps_nfc_pw_from_config) {
+ return wps_nfc_token_build(ndef,
+ wpa_s->conf->wps_nfc_dev_pw_id,
+ wpa_s->conf->wps_nfc_dh_pubkey,
+ wpa_s->conf->wps_nfc_dev_pw);
+ }
+
+ return wps_nfc_token_gen(ndef, &wpa_s->conf->wps_nfc_dev_pw_id,
+ &wpa_s->conf->wps_nfc_dh_pubkey,
+ &wpa_s->conf->wps_nfc_dh_privkey,
+ &wpa_s->conf->wps_nfc_dev_pw);
+}
+
+
+int wpas_wps_start_nfc(struct wpa_supplicant *wpa_s, const u8 *go_dev_addr,
+ const u8 *bssid,
+ const struct wpabuf *dev_pw, u16 dev_pw_id,
+ int p2p_group, const u8 *peer_pubkey_hash,
+ const u8 *ssid, size_t ssid_len, int freq)
+{
+ struct wps_context *wps = wpa_s->wps;
+ char pw[32 * 2 + 1];
+
+ if (dev_pw_id != DEV_PW_NFC_CONNECTION_HANDOVER && dev_pw == NULL) {
+ dev_pw = wpa_s->conf->wps_nfc_dev_pw;
+ dev_pw_id = wpa_s->conf->wps_nfc_dev_pw_id;
+ }
+
+ if (wpa_s->conf->wps_nfc_dh_pubkey == NULL ||
+ wpa_s->conf->wps_nfc_dh_privkey == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS: Missing DH params - "
+ "cannot start NFC-triggered connection");
+ return -1;
+ }
+
+ if (dev_pw_id != DEV_PW_NFC_CONNECTION_HANDOVER && dev_pw == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS: Missing Device Password (id=%u) - "
+ "cannot start NFC-triggered connection", dev_pw_id);
+ return -1;
+ }
+
+ dh5_free(wps->dh_ctx);
+ wpabuf_free(wps->dh_pubkey);
+ wpabuf_free(wps->dh_privkey);
+ wps->dh_privkey = wpabuf_dup(wpa_s->conf->wps_nfc_dh_privkey);
+ wps->dh_pubkey = wpabuf_dup(wpa_s->conf->wps_nfc_dh_pubkey);
+ if (wps->dh_privkey == NULL || wps->dh_pubkey == NULL) {
+ wps->dh_ctx = NULL;
+ wpabuf_free(wps->dh_pubkey);
+ wps->dh_pubkey = NULL;
+ wpabuf_free(wps->dh_privkey);
+ wps->dh_privkey = NULL;
+ wpa_printf(MSG_DEBUG, "WPS: Failed to get DH priv/pub key");
+ return -1;
+ }
+ wps->dh_ctx = dh5_init_fixed(wps->dh_privkey, wps->dh_pubkey);
+ if (wps->dh_ctx == NULL) {
+ wpabuf_free(wps->dh_pubkey);
+ wps->dh_pubkey = NULL;
+ wpabuf_free(wps->dh_privkey);
+ wps->dh_privkey = NULL;
+ wpa_printf(MSG_DEBUG, "WPS: Failed to initialize DH context");
+ return -1;
+ }
+
+ if (dev_pw) {
+ wpa_snprintf_hex_uppercase(pw, sizeof(pw),
+ wpabuf_head(dev_pw),
+ wpabuf_len(dev_pw));
+ }
+ return wpas_wps_start_dev_pw(wpa_s, go_dev_addr, bssid,
+ dev_pw ? pw : NULL,
+ p2p_group, dev_pw_id, peer_pubkey_hash,
+ ssid, ssid_len, freq);
+}
+
+
+static int wpas_wps_use_cred(struct wpa_supplicant *wpa_s,
+ struct wps_parse_attr *attr)
+{
+ /*
+ * Disable existing networks temporarily to allow the newly learned
+ * credential to be preferred. Enable the temporarily disabled networks
+ * after 10 seconds.
+ */
+ wpas_wps_temp_disable(wpa_s, NULL);
+ eloop_register_timeout(10, 0, wpas_wps_reenable_networks_cb, wpa_s,
+ NULL);
+
+ if (wps_oob_use_cred(wpa_s->wps, attr) < 0)
+ return -1;
+
+ if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED)
+ return 0;
+
+ if (attr->ap_channel) {
+ u16 chan = WPA_GET_BE16(attr->ap_channel);
+ int freq = 0;
+
+ if (chan >= 1 && chan <= 13)
+ freq = 2407 + 5 * chan;
+ else if (chan == 14)
+ freq = 2484;
+ else if (chan >= 30)
+ freq = 5000 + 5 * chan;
+
+ if (freq) {
+ wpa_printf(MSG_DEBUG, "WPS: Credential container indicated AP channel %u -> %u MHz",
+ chan, freq);
+ wpa_s->after_wps = 5;
+ wpa_s->wps_freq = freq;
+ }
+ }
+
+ wpa_printf(MSG_DEBUG, "WPS: Request reconnection with new network "
+ "based on the received credential added");
+ wpa_s->normal_scans = 0;
+ wpa_supplicant_reinit_autoscan(wpa_s);
+ wpa_s->disconnected = 0;
+ wpa_s->reassociate = 1;
+
+ wpa_supplicant_cancel_sched_scan(wpa_s);
+ wpa_supplicant_req_scan(wpa_s, 0, 0);
+
+ return 0;
+}
+
+
+#ifdef CONFIG_WPS_ER
+static int wpas_wps_add_nfc_password_token(struct wpa_supplicant *wpa_s,
+ struct wps_parse_attr *attr)
+{
+ return wps_registrar_add_nfc_password_token(
+ wpa_s->wps->registrar, attr->oob_dev_password,
+ attr->oob_dev_password_len);
+}
+#endif /* CONFIG_WPS_ER */
+
+
+static int wpas_wps_nfc_tag_process(struct wpa_supplicant *wpa_s,
+ const struct wpabuf *wps)
+{
+ struct wps_parse_attr attr;
+
+ wpa_hexdump_buf(MSG_DEBUG, "WPS: Received NFC tag payload", wps);
+
+ if (wps_parse_msg(wps, &attr)) {
+ wpa_printf(MSG_DEBUG, "WPS: Ignore invalid data from NFC tag");
+ return -1;
+ }
+
+ if (attr.num_cred)
+ return wpas_wps_use_cred(wpa_s, &attr);
+
+#ifdef CONFIG_WPS_ER
+ if (attr.oob_dev_password)
+ return wpas_wps_add_nfc_password_token(wpa_s, &attr);
+#endif /* CONFIG_WPS_ER */
+
+ wpa_printf(MSG_DEBUG, "WPS: Ignore unrecognized NFC tag");
+ return -1;
+}
+
+
+int wpas_wps_nfc_tag_read(struct wpa_supplicant *wpa_s,
+ const struct wpabuf *data, int forced_freq)
+{
+ const struct wpabuf *wps = data;
+ struct wpabuf *tmp = NULL;
+ int ret;
+
+ if (wpabuf_len(data) < 4)
+ return -1;
+
+ if (*wpabuf_head_u8(data) != 0x10) {
+ /* Assume this contains full NDEF record */
+ tmp = ndef_parse_wifi(data);
+ if (tmp == NULL) {
+#ifdef CONFIG_P2P
+ tmp = ndef_parse_p2p(data);
+ if (tmp) {
+ ret = wpas_p2p_nfc_tag_process(wpa_s, tmp,
+ forced_freq);
+ wpabuf_free(tmp);
+ return ret;
+ }
+#endif /* CONFIG_P2P */
+ wpa_printf(MSG_DEBUG, "WPS: Could not parse NDEF");
+ return -1;
+ }
+ wps = tmp;
+ }
+
+ ret = wpas_wps_nfc_tag_process(wpa_s, wps);
+ wpabuf_free(tmp);
+ return ret;
+}
+
+
+struct wpabuf * wpas_wps_nfc_handover_req(struct wpa_supplicant *wpa_s,
+ int ndef)
+{
+ struct wpabuf *ret;
+
+ if (wpa_s->conf->wps_nfc_dh_pubkey == NULL &&
+ wps_nfc_gen_dh(&wpa_s->conf->wps_nfc_dh_pubkey,
+ &wpa_s->conf->wps_nfc_dh_privkey) < 0)
+ return NULL;
+
+ ret = wps_build_nfc_handover_req(wpa_s->wps,
+ wpa_s->conf->wps_nfc_dh_pubkey);
+
+ if (ndef && ret) {
+ struct wpabuf *tmp;
+ tmp = ndef_build_wifi(ret);
+ wpabuf_free(ret);
+ if (tmp == NULL)
+ return NULL;
+ ret = tmp;
+ }
+
+ return ret;
+}
+
+
+#ifdef CONFIG_WPS_NFC
+
+static struct wpabuf *
+wpas_wps_er_nfc_handover_sel(struct wpa_supplicant *wpa_s, int ndef,
+ const char *uuid)
+{
+#ifdef CONFIG_WPS_ER
+ struct wpabuf *ret;
+ u8 u[UUID_LEN], *use_uuid = NULL;
+ u8 addr[ETH_ALEN], *use_addr = NULL;
+ struct wps_context *wps = wpa_s->wps;
+
+ if (wps == NULL)
+ return NULL;
+
+ if (uuid == NULL)
+ return NULL;
+ if (uuid_str2bin(uuid, u) == 0)
+ use_uuid = u;
+ else if (hwaddr_aton(uuid, addr) == 0)
+ use_addr = addr;
+ else
+ return NULL;
+
+ if (wpa_s->conf->wps_nfc_dh_pubkey == NULL) {
+ if (wps_nfc_gen_dh(&wpa_s->conf->wps_nfc_dh_pubkey,
+ &wpa_s->conf->wps_nfc_dh_privkey) < 0)
+ return NULL;
+ }
+
+ wpas_wps_nfc_clear(wps);
+ wps->ap_nfc_dev_pw_id = DEV_PW_NFC_CONNECTION_HANDOVER;
+ wps->ap_nfc_dh_pubkey = wpabuf_dup(wpa_s->conf->wps_nfc_dh_pubkey);
+ wps->ap_nfc_dh_privkey = wpabuf_dup(wpa_s->conf->wps_nfc_dh_privkey);
+ if (!wps->ap_nfc_dh_pubkey || !wps->ap_nfc_dh_privkey) {
+ wpas_wps_nfc_clear(wps);
+ return NULL;
+ }
+
+ ret = wps_er_nfc_handover_sel(wpa_s->wps_er, wpa_s->wps, use_uuid,
+ use_addr, wpa_s->conf->wps_nfc_dh_pubkey);
+ if (ndef && ret) {
+ struct wpabuf *tmp;
+ tmp = ndef_build_wifi(ret);
+ wpabuf_free(ret);
+ if (tmp == NULL)
+ return NULL;
+ ret = tmp;
+ }
+
+ return ret;
+#else /* CONFIG_WPS_ER */
+ return NULL;
+#endif /* CONFIG_WPS_ER */
+}
+#endif /* CONFIG_WPS_NFC */
+
+
+struct wpabuf * wpas_wps_nfc_handover_sel(struct wpa_supplicant *wpa_s,
+ int ndef, int cr, const char *uuid)
+{
+ struct wpabuf *ret;
+ if (!cr)
+ return NULL;
+ ret = wpas_ap_wps_nfc_handover_sel(wpa_s, ndef);
+ if (ret)
+ return ret;
+ return wpas_wps_er_nfc_handover_sel(wpa_s, ndef, uuid);
+}
+
+
+static int wpas_wps_nfc_rx_handover_sel(struct wpa_supplicant *wpa_s,
+ const struct wpabuf *data)
+{
+ struct wpabuf *wps;
+ int ret = -1;
+ u16 wsc_len;
+ const u8 *pos;
+ struct wpabuf msg;
+ struct wps_parse_attr attr;
+ u16 dev_pw_id;
+ const u8 *bssid = NULL;
+ int freq = 0;
+
+ wps = ndef_parse_wifi(data);
+ if (wps == NULL)
+ return -1;
+ wpa_printf(MSG_DEBUG, "WPS: Received application/vnd.wfa.wsc "
+ "payload from NFC connection handover");
+ wpa_hexdump_buf(MSG_DEBUG, "WPS: NFC payload", wps);
+ if (wpabuf_len(wps) < 2) {
+ wpa_printf(MSG_DEBUG, "WPS: Too short Wi-Fi Handover Select "
+ "Message");
+ goto out;
+ }
+ pos = wpabuf_head(wps);
+ wsc_len = WPA_GET_BE16(pos);
+ if (wsc_len > wpabuf_len(wps) - 2) {
+ wpa_printf(MSG_DEBUG, "WPS: Invalid WSC attribute length (%u) "
+ "in Wi-Fi Handover Select Message", wsc_len);
+ goto out;
+ }
+ pos += 2;
+
+ wpa_hexdump(MSG_DEBUG,
+ "WPS: WSC attributes in Wi-Fi Handover Select Message",
+ pos, wsc_len);
+ if (wsc_len < wpabuf_len(wps) - 2) {
+ wpa_hexdump(MSG_DEBUG,
+ "WPS: Ignore extra data after WSC attributes",
+ pos + wsc_len, wpabuf_len(wps) - 2 - wsc_len);
+ }
+
+ wpabuf_set(&msg, pos, wsc_len);
+ ret = wps_parse_msg(&msg, &attr);
+ if (ret < 0) {
+ wpa_printf(MSG_DEBUG, "WPS: Could not parse WSC attributes in "
+ "Wi-Fi Handover Select Message");
+ goto out;
+ }
+
+ if (attr.oob_dev_password == NULL ||
+ attr.oob_dev_password_len < WPS_OOB_PUBKEY_HASH_LEN + 2) {
+ wpa_printf(MSG_DEBUG, "WPS: No Out-of-Band Device Password "
+ "included in Wi-Fi Handover Select Message");
+ ret = -1;
+ goto out;
+ }
+
+ if (attr.ssid == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS: No SSID included in Wi-Fi Handover "
+ "Select Message");
+ ret = -1;
+ goto out;
+ }
+
+ wpa_hexdump_ascii(MSG_DEBUG, "WPS: SSID", attr.ssid, attr.ssid_len);
+
+ if (attr.mac_addr) {
+ bssid = attr.mac_addr;
+ wpa_printf(MSG_DEBUG, "WPS: MAC Address (BSSID): " MACSTR,
+ MAC2STR(bssid));
+ }
+
+ if (attr.rf_bands)
+ wpa_printf(MSG_DEBUG, "WPS: RF Bands: %d", *attr.rf_bands);
+
+ if (attr.ap_channel) {
+ u16 chan = WPA_GET_BE16(attr.ap_channel);
+
+ wpa_printf(MSG_DEBUG, "WPS: AP Channel: %d", chan);
+
+ if (chan >= 1 && chan <= 13 &&
+ (attr.rf_bands == NULL || *attr.rf_bands & WPS_RF_24GHZ))
+ freq = 2407 + 5 * chan;
+ else if (chan == 14 &&
+ (attr.rf_bands == NULL ||
+ *attr.rf_bands & WPS_RF_24GHZ))
+ freq = 2484;
+ else if (chan >= 30 &&
+ (attr.rf_bands == NULL ||
+ *attr.rf_bands & WPS_RF_50GHZ))
+ freq = 5000 + 5 * chan;
+ else if (chan >= 1 && chan <= 4 &&
+ (attr.rf_bands == NULL ||
+ *attr.rf_bands & WPS_RF_60GHZ))
+ freq = 56160 + 2160 * chan;
+
+ if (freq) {
+ wpa_printf(MSG_DEBUG,
+ "WPS: AP indicated channel %u -> %u MHz",
+ chan, freq);
+ }
+ }
+
+ wpa_hexdump(MSG_DEBUG, "WPS: Out-of-Band Device Password",
+ attr.oob_dev_password, attr.oob_dev_password_len);
+ dev_pw_id = WPA_GET_BE16(attr.oob_dev_password +
+ WPS_OOB_PUBKEY_HASH_LEN);
+ if (dev_pw_id != DEV_PW_NFC_CONNECTION_HANDOVER) {
+ wpa_printf(MSG_DEBUG, "WPS: Unexpected OOB Device Password ID "
+ "%u in Wi-Fi Handover Select Message", dev_pw_id);
+ ret = -1;
+ goto out;
+ }
+ wpa_hexdump(MSG_DEBUG, "WPS: AP Public Key hash",
+ attr.oob_dev_password, WPS_OOB_PUBKEY_HASH_LEN);
+
+ ret = wpas_wps_start_nfc(wpa_s, NULL, bssid, NULL, dev_pw_id, 0,
+ attr.oob_dev_password,
+ attr.ssid, attr.ssid_len, freq);
+
+out:
+ wpabuf_free(wps);
+ return ret;
+}
+
+
+int wpas_wps_nfc_report_handover(struct wpa_supplicant *wpa_s,
+ const struct wpabuf *req,
+ const struct wpabuf *sel)
+{
+ wpa_printf(MSG_DEBUG, "NFC: WPS connection handover reported");
+ wpa_hexdump_buf_key(MSG_DEBUG, "WPS: Carrier record in request", req);
+ wpa_hexdump_buf_key(MSG_DEBUG, "WPS: Carrier record in select", sel);
+ return wpas_wps_nfc_rx_handover_sel(wpa_s, sel);
+}
+
+
+int wpas_er_wps_nfc_report_handover(struct wpa_supplicant *wpa_s,
+ const struct wpabuf *req,
+ const struct wpabuf *sel)
+{
+ struct wpabuf *wps;
+ int ret = -1;
+ u16 wsc_len;
+ const u8 *pos;
+ struct wpabuf msg;
+ struct wps_parse_attr attr;
+ u16 dev_pw_id;
+
+ /*
+ * Enrollee/station is always initiator of the NFC connection handover,
+ * so use the request message here to find Enrollee public key hash.
+ */
+ wps = ndef_parse_wifi(req);
+ if (wps == NULL)
+ return -1;
+ wpa_printf(MSG_DEBUG, "WPS: Received application/vnd.wfa.wsc "
+ "payload from NFC connection handover");
+ wpa_hexdump_buf(MSG_DEBUG, "WPS: NFC payload", wps);
+ if (wpabuf_len(wps) < 2) {
+ wpa_printf(MSG_DEBUG, "WPS: Too short Wi-Fi Handover Request "
+ "Message");
+ goto out;
+ }
+ pos = wpabuf_head(wps);
+ wsc_len = WPA_GET_BE16(pos);
+ if (wsc_len > wpabuf_len(wps) - 2) {
+ wpa_printf(MSG_DEBUG, "WPS: Invalid WSC attribute length (%u) "
+ "in rt Wi-Fi Handover Request Message", wsc_len);
+ goto out;
+ }
+ pos += 2;
+
+ wpa_hexdump(MSG_DEBUG,
+ "WPS: WSC attributes in Wi-Fi Handover Request Message",
+ pos, wsc_len);
+ if (wsc_len < wpabuf_len(wps) - 2) {
+ wpa_hexdump(MSG_DEBUG,
+ "WPS: Ignore extra data after WSC attributes",
+ pos + wsc_len, wpabuf_len(wps) - 2 - wsc_len);
+ }
+
+ wpabuf_set(&msg, pos, wsc_len);
+ ret = wps_parse_msg(&msg, &attr);
+ if (ret < 0) {
+ wpa_printf(MSG_DEBUG, "WPS: Could not parse WSC attributes in "
+ "Wi-Fi Handover Request Message");
+ goto out;
+ }
+
+ if (attr.oob_dev_password == NULL ||
+ attr.oob_dev_password_len < WPS_OOB_PUBKEY_HASH_LEN + 2) {
+ wpa_printf(MSG_DEBUG, "WPS: No Out-of-Band Device Password "
+ "included in Wi-Fi Handover Request Message");
+ ret = -1;
+ goto out;
+ }
+
+ if (attr.uuid_e == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS: No UUID-E included in Wi-Fi "
+ "Handover Request Message");
+ ret = -1;
+ goto out;
+ }
+
+ wpa_hexdump(MSG_DEBUG, "WPS: UUID-E", attr.uuid_e, WPS_UUID_LEN);
+
+ wpa_hexdump(MSG_DEBUG, "WPS: Out-of-Band Device Password",
+ attr.oob_dev_password, attr.oob_dev_password_len);
+ dev_pw_id = WPA_GET_BE16(attr.oob_dev_password +
+ WPS_OOB_PUBKEY_HASH_LEN);
+ if (dev_pw_id != DEV_PW_NFC_CONNECTION_HANDOVER) {
+ wpa_printf(MSG_DEBUG, "WPS: Unexpected OOB Device Password ID "
+ "%u in Wi-Fi Handover Request Message", dev_pw_id);
+ ret = -1;
+ goto out;
+ }
+ wpa_hexdump(MSG_DEBUG, "WPS: Enrollee Public Key hash",
+ attr.oob_dev_password, WPS_OOB_PUBKEY_HASH_LEN);
+
+ ret = wps_registrar_add_nfc_pw_token(wpa_s->wps->registrar,
+ attr.oob_dev_password,
+ DEV_PW_NFC_CONNECTION_HANDOVER,
+ NULL, 0, 1);
+
+out:
+ wpabuf_free(wps);
+ return ret;
+}
+
+#endif /* CONFIG_WPS_NFC */
+
+
+static void wpas_wps_dump_ap_info(struct wpa_supplicant *wpa_s)
+{
+ size_t i;
+ struct os_reltime now;
+
+ if (wpa_debug_level > MSG_DEBUG)
+ return;
+
+ if (wpa_s->wps_ap == NULL)
+ return;
+
+ os_get_reltime(&now);
+
+ for (i = 0; i < wpa_s->num_wps_ap; i++) {
+ struct wps_ap_info *ap = &wpa_s->wps_ap[i];
+ struct wpa_blacklist *e = wpa_blacklist_get(wpa_s, ap->bssid);
+
+ wpa_printf(MSG_DEBUG, "WPS: AP[%d] " MACSTR " type=%d "
+ "tries=%d last_attempt=%d sec ago blacklist=%d",
+ (int) i, MAC2STR(ap->bssid), ap->type, ap->tries,
+ ap->last_attempt.sec > 0 ?
+ (int) now.sec - (int) ap->last_attempt.sec : -1,
+ e ? e->count : 0);
+ }
+}
+
+
+static struct wps_ap_info * wpas_wps_get_ap_info(struct wpa_supplicant *wpa_s,
+ const u8 *bssid)
+{
+ size_t i;
+
+ if (wpa_s->wps_ap == NULL)
+ return NULL;
+
+ for (i = 0; i < wpa_s->num_wps_ap; i++) {
+ struct wps_ap_info *ap = &wpa_s->wps_ap[i];
+ if (os_memcmp(ap->bssid, bssid, ETH_ALEN) == 0)
+ return ap;
+ }
+
+ return NULL;
+}
+
+
+static void wpas_wps_update_ap_info_bss(struct wpa_supplicant *wpa_s,
+ struct wpa_scan_res *res)
+{
+ struct wpabuf *wps;
+ enum wps_ap_info_type type;
+ struct wps_ap_info *ap;
+ int r, pbc_active;
+ const u8 *uuid;
+
+ if (wpa_scan_get_vendor_ie(res, WPS_IE_VENDOR_TYPE) == NULL)
+ return;
+
+ wps = wpa_scan_get_vendor_ie_multi(res, WPS_IE_VENDOR_TYPE);
+ if (wps == NULL)
+ return;
+
+ r = wps_is_addr_authorized(wps, wpa_s->own_addr, 1);
+ if (r == 2)
+ type = WPS_AP_SEL_REG_OUR;
+ else if (r == 1)
+ type = WPS_AP_SEL_REG;
+ else
+ type = WPS_AP_NOT_SEL_REG;
+
+ uuid = wps_get_uuid_e(wps);
+ pbc_active = wps_is_selected_pbc_registrar(wps);
+
+ ap = wpas_wps_get_ap_info(wpa_s, res->bssid);
+ if (ap) {
+ if (ap->type != type) {
+ wpa_printf(MSG_DEBUG, "WPS: AP " MACSTR
+ " changed type %d -> %d",
+ MAC2STR(res->bssid), ap->type, type);
+ ap->type = type;
+ if (type != WPS_AP_NOT_SEL_REG)
+ wpa_blacklist_del(wpa_s, ap->bssid);
+ }
+ ap->pbc_active = pbc_active;
+ if (uuid)
+ os_memcpy(ap->uuid, uuid, WPS_UUID_LEN);
+ goto out;
+ }
+
+ ap = os_realloc_array(wpa_s->wps_ap, wpa_s->num_wps_ap + 1,
+ sizeof(struct wps_ap_info));
+ if (ap == NULL)
+ goto out;
+
+ wpa_s->wps_ap = ap;
+ ap = &wpa_s->wps_ap[wpa_s->num_wps_ap];
+ wpa_s->num_wps_ap++;
+
+ os_memset(ap, 0, sizeof(*ap));
+ os_memcpy(ap->bssid, res->bssid, ETH_ALEN);
+ ap->type = type;
+ ap->pbc_active = pbc_active;
+ if (uuid)
+ os_memcpy(ap->uuid, uuid, WPS_UUID_LEN);
+ wpa_printf(MSG_DEBUG, "WPS: AP " MACSTR " type %d added",
+ MAC2STR(ap->bssid), ap->type);
+
+out:
+ wpabuf_free(wps);
+}
+
+
+void wpas_wps_update_ap_info(struct wpa_supplicant *wpa_s,
+ struct wpa_scan_results *scan_res)
+{
+ size_t i;
+
+ for (i = 0; i < scan_res->num; i++)
+ wpas_wps_update_ap_info_bss(wpa_s, scan_res->res[i]);
+
+ wpas_wps_dump_ap_info(wpa_s);
+}
+
+
+void wpas_wps_notify_assoc(struct wpa_supplicant *wpa_s, const u8 *bssid)
+{
+ struct wps_ap_info *ap;
+
+ wpa_s->after_wps = 0;
+
+ if (!wpa_s->wps_ap_iter)
+ return;
+ ap = wpas_wps_get_ap_info(wpa_s, bssid);
+ if (ap == NULL)
+ return;
+ ap->tries++;
+ os_get_reltime(&ap->last_attempt);
+}
diff --git a/freebsd/contrib/wpa/wpa_supplicant/wps_supplicant.h b/freebsd/contrib/wpa/wpa_supplicant/wps_supplicant.h
new file mode 100644
index 00000000..3c25ca86
--- /dev/null
+++ b/freebsd/contrib/wpa/wpa_supplicant/wps_supplicant.h
@@ -0,0 +1,152 @@
+/*
+ * wpa_supplicant / WPS integration
+ * 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_SUPPLICANT_H
+#define WPS_SUPPLICANT_H
+
+struct wpa_scan_results;
+
+#ifdef CONFIG_WPS
+
+#include "wps/wps.h"
+#include "wps/wps_defs.h"
+
+struct wpa_bss;
+
+struct wps_new_ap_settings {
+ const char *ssid_hex;
+ const char *auth;
+ const char *encr;
+ const char *key_hex;
+};
+
+int wpas_wps_init(struct wpa_supplicant *wpa_s);
+void wpas_wps_deinit(struct wpa_supplicant *wpa_s);
+int wpas_wps_eapol_cb(struct wpa_supplicant *wpa_s);
+enum wps_request_type wpas_wps_get_req_type(struct wpa_ssid *ssid);
+int wpas_wps_start_pbc(struct wpa_supplicant *wpa_s, const u8 *bssid,
+ int p2p_group);
+int wpas_wps_start_pin(struct wpa_supplicant *wpa_s, const u8 *bssid,
+ const char *pin, int p2p_group, u16 dev_pw_id);
+void wpas_wps_pbc_overlap(struct wpa_supplicant *wpa_s);
+int wpas_wps_cancel(struct wpa_supplicant *wpa_s);
+int wpas_wps_start_reg(struct wpa_supplicant *wpa_s, const u8 *bssid,
+ const char *pin, struct wps_new_ap_settings *settings);
+int wpas_wps_ssid_bss_match(struct wpa_supplicant *wpa_s,
+ struct wpa_ssid *ssid, struct wpa_bss *bss);
+int wpas_wps_ssid_wildcard_ok(struct wpa_supplicant *wpa_s,
+ struct wpa_ssid *ssid, struct wpa_bss *bss);
+int wpas_wps_scan_pbc_overlap(struct wpa_supplicant *wpa_s,
+ struct wpa_bss *selected, struct wpa_ssid *ssid);
+void wpas_wps_notify_scan_results(struct wpa_supplicant *wpa_s);
+int wpas_wps_searching(struct wpa_supplicant *wpa_s);
+int wpas_wps_scan_result_text(const u8 *ies, size_t ies_len, char *pos,
+ char *end);
+int wpas_wps_er_start(struct wpa_supplicant *wpa_s, const char *filter);
+void wpas_wps_er_stop(struct wpa_supplicant *wpa_s);
+int wpas_wps_er_add_pin(struct wpa_supplicant *wpa_s, const u8 *addr,
+ const char *uuid, const char *pin);
+int wpas_wps_er_pbc(struct wpa_supplicant *wpa_s, const char *uuid);
+int wpas_wps_er_learn(struct wpa_supplicant *wpa_s, const char *uuid,
+ const char *pin);
+int wpas_wps_er_set_config(struct wpa_supplicant *wpa_s, const char *uuid,
+ int id);
+int wpas_wps_er_config(struct wpa_supplicant *wpa_s, const char *uuid,
+ const char *pin, struct wps_new_ap_settings *settings);
+struct wpabuf * wpas_wps_er_nfc_config_token(struct wpa_supplicant *wpa_s,
+ int ndef, const char *uuid);
+int wpas_wps_terminate_pending(struct wpa_supplicant *wpa_s);
+void wpas_wps_update_config(struct wpa_supplicant *wpa_s);
+struct wpabuf * wpas_wps_nfc_config_token(struct wpa_supplicant *wpa_s,
+ int ndef, const char *id_str);
+struct wpabuf * wpas_wps_nfc_token(struct wpa_supplicant *wpa_s, int ndef);
+int wpas_wps_start_nfc(struct wpa_supplicant *wpa_s, const u8 *dev_addr,
+ const u8 *bssid,
+ const struct wpabuf *dev_pw, u16 dev_pw_id,
+ int p2p_group, const u8 *peer_pubkey_hash,
+ const u8 *ssid, size_t ssid_len, int freq);
+int wpas_wps_nfc_tag_read(struct wpa_supplicant *wpa_s,
+ const struct wpabuf *data, int forced_freq);
+struct wpabuf * wpas_wps_nfc_handover_req(struct wpa_supplicant *wpa_s,
+ int ndef);
+struct wpabuf * wpas_wps_nfc_handover_sel(struct wpa_supplicant *wpa_s,
+ int ndef, int cr, const char *uuid);
+int wpas_wps_nfc_report_handover(struct wpa_supplicant *wpa_s,
+ const struct wpabuf *req,
+ const struct wpabuf *sel);
+int wpas_er_wps_nfc_report_handover(struct wpa_supplicant *wpa_s,
+ const struct wpabuf *req,
+ const struct wpabuf *sel);
+void wpas_wps_update_ap_info(struct wpa_supplicant *wpa_s,
+ struct wpa_scan_results *scan_res);
+void wpas_wps_notify_assoc(struct wpa_supplicant *wpa_s, const u8 *bssid);
+
+#else /* CONFIG_WPS */
+
+static inline int wpas_wps_init(struct wpa_supplicant *wpa_s)
+{
+ return 0;
+}
+
+static inline void wpas_wps_deinit(struct wpa_supplicant *wpa_s)
+{
+}
+
+static inline int wpas_wps_eapol_cb(struct wpa_supplicant *wpa_s)
+{
+ return 0;
+}
+
+static inline u8 wpas_wps_get_req_type(struct wpa_ssid *ssid)
+{
+ return 0;
+}
+
+static inline int wpas_wps_ssid_bss_match(struct wpa_supplicant *wpa_s,
+ struct wpa_ssid *ssid,
+ struct wpa_bss *bss)
+{
+ return -1;
+}
+
+static inline int wpas_wps_ssid_wildcard_ok(struct wpa_supplicant *wpa_s,
+ struct wpa_ssid *ssid,
+ struct wpa_bss *bss)
+{
+ return 0;
+}
+
+static inline int wpas_wps_scan_pbc_overlap(struct wpa_supplicant *wpa_s,
+ struct wpa_bss *selected,
+ struct wpa_ssid *ssid)
+{
+ return 0;
+}
+
+static inline void wpas_wps_notify_scan_results(struct wpa_supplicant *wpa_s)
+{
+}
+
+static inline int wpas_wps_searching(struct wpa_supplicant *wpa_s)
+{
+ return 0;
+}
+
+static inline void wpas_wps_update_ap_info(struct wpa_supplicant *wpa_s,
+ struct wpa_scan_results *scan_res)
+{
+}
+
+static inline void wpas_wps_notify_assoc(struct wpa_supplicant *wpa_s,
+ const u8 *bssid)
+{
+}
+
+#endif /* CONFIG_WPS */
+
+#endif /* WPS_SUPPLICANT_H */
diff --git a/freebsd/usr.sbin/wpa/wpa_supplicant/Packet32.c b/freebsd/usr.sbin/wpa/wpa_supplicant/Packet32.c
new file mode 100644
index 00000000..b22d3488
--- /dev/null
+++ b/freebsd/usr.sbin/wpa/wpa_supplicant/Packet32.c
@@ -0,0 +1,415 @@
+#include <machine/rtems-bsd-user-space.h>
+
+/*-
+ * Copyright (c) 2005
+ * Bill Paul <wpaul@windriver.com>. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Bill Paul.
+ * 4. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR THE VOICES IN HIS HEAD
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * This file implements a small portion of the Winpcap API for the
+ * Windows NDIS interface in wpa_supplicant. It provides just enough
+ * routines to fool wpa_supplicant into thinking it's really running
+ * in a Windows environment.
+ */
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <sys/errno.h>
+#include <sys/sysctl.h>
+#include <sys/fcntl.h>
+#include <net/if.h>
+#include <net/if_dl.h>
+
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <net/route.h>
+
+#include <net80211/ieee80211_ioctl.h>
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <pcap.h>
+
+#include "Packet32.h"
+
+#define OID_802_11_ADD_KEY 0x0d01011D
+
+typedef ULONGLONG NDIS_802_11_KEY_RSC;
+typedef UCHAR NDIS_802_11_MAC_ADDRESS[6];
+
+typedef struct NDIS_802_11_KEY {
+ ULONG Length;
+ ULONG KeyIndex;
+ ULONG KeyLength;
+ NDIS_802_11_MAC_ADDRESS BSSID;
+ NDIS_802_11_KEY_RSC KeyRSC;
+ UCHAR KeyMaterial[1];
+} NDIS_802_11_KEY;
+
+typedef struct NDIS_802_11_KEY_COMPAT {
+ ULONG Length;
+ ULONG KeyIndex;
+ ULONG KeyLength;
+ NDIS_802_11_MAC_ADDRESS BSSID;
+ UCHAR Pad[6]; /* Make struct layout match Windows. */
+ NDIS_802_11_KEY_RSC KeyRSC;
+#ifdef notdef
+ UCHAR KeyMaterial[1];
+#endif
+} NDIS_802_11_KEY_COMPAT;
+
+#define TRUE 1
+#define FALSE 0
+
+struct adapter {
+ int socket;
+ char name[IFNAMSIZ];
+ int prev_roaming;
+};
+
+PCHAR
+PacketGetVersion(void)
+{
+ return("FreeBSD WinPcap compatibility shim v1.0");
+}
+
+void *
+PacketOpenAdapter(CHAR *iface)
+{
+ struct adapter *a;
+ int s;
+ int ifflags;
+ struct ifreq ifr;
+ struct ieee80211req ireq;
+
+ s = socket(PF_INET, SOCK_DGRAM, 0);
+
+ if (s == -1)
+ return(NULL);
+
+ a = malloc(sizeof(struct adapter));
+ if (a == NULL)
+ return(NULL);
+
+ a->socket = s;
+ if (strncmp(iface, "\\Device\\NPF_", 12) == 0)
+ iface += 12;
+ else if (strncmp(iface, "\\DEVICE\\", 8) == 0)
+ iface += 8;
+ snprintf(a->name, IFNAMSIZ, "%s", iface);
+
+ /* Turn off net80211 roaming */
+ bzero((char *)&ireq, sizeof(ireq));
+ strncpy(ireq.i_name, iface, sizeof (ifr.ifr_name));
+ ireq.i_type = IEEE80211_IOC_ROAMING;
+ if (ioctl(a->socket, SIOCG80211, &ireq) == 0) {
+ a->prev_roaming = ireq.i_val;
+ ireq.i_val = IEEE80211_ROAMING_MANUAL;
+ if (ioctl(a->socket, SIOCS80211, &ireq) < 0)
+ fprintf(stderr,
+ "Could not set IEEE80211_ROAMING_MANUAL\n");
+ }
+
+ bzero((char *)&ifr, sizeof(ifr));
+ strncpy(ifr.ifr_name, iface, sizeof (ifr.ifr_name));
+ if (ioctl(a->socket, SIOCGIFFLAGS, (caddr_t)&ifr) < 0) {
+ free(a);
+ close(s);
+ return(NULL);
+ }
+ ifr.ifr_flags |= IFF_UP;
+ if (ioctl(a->socket, SIOCSIFFLAGS, (caddr_t)&ifr) < 0) {
+ free(a);
+ close(s);
+ return(NULL);
+ }
+
+ return(a);
+}
+
+int
+PacketRequest(void *iface, BOOLEAN set, PACKET_OID_DATA *oid)
+{
+ struct adapter *a;
+ uint32_t retval;
+ struct ifreq ifr;
+ NDIS_802_11_KEY *old;
+ NDIS_802_11_KEY_COMPAT *new;
+ PACKET_OID_DATA *o = NULL;
+
+ if (iface == NULL)
+ return(-1);
+
+ a = iface;
+ bzero((char *)&ifr, sizeof(ifr));
+
+ /*
+ * This hack is necessary to work around a difference
+ * betwee the GNU C and Microsoft C compilers. The NDIS_802_11_KEY
+ * structure has a uint64_t in it, right after an array of
+ * chars. The Microsoft compiler inserts padding right before
+ * the 64-bit value to align it on a 64-bit boundary, but
+ * GCC only aligns it on a 32-bit boundary. Trying to pass
+ * the GCC-formatted structure to an NDIS binary driver
+ * fails because some of the fields appear to be at the
+ * wrong offsets.
+ *
+ * To get around this, if we detect someone is trying to do
+ * a set operation on OID_802_11_ADD_KEY, we shuffle the data
+ * into a properly padded structure and pass that into the
+ * driver instead. This allows the driver_ndis.c code supplied
+ * with wpa_supplicant to work unmodified.
+ */
+
+ if (set == TRUE && oid->Oid == OID_802_11_ADD_KEY) {
+ old = (NDIS_802_11_KEY *)&oid->Data;
+ o = malloc(sizeof(PACKET_OID_DATA) +
+ sizeof(NDIS_802_11_KEY_COMPAT) + old->KeyLength);
+ if (o == NULL)
+ return(0);
+ bzero((char *)o, sizeof(PACKET_OID_DATA) +
+ sizeof(NDIS_802_11_KEY_COMPAT) + old->KeyLength);
+ o->Oid = oid->Oid;
+ o->Length = sizeof(NDIS_802_11_KEY_COMPAT) + old->KeyLength;
+ new = (NDIS_802_11_KEY_COMPAT *)&o->Data;
+ new->KeyRSC = old->KeyRSC;
+ new->Length = o->Length;
+ new->KeyIndex = old->KeyIndex;
+ new->KeyLength = old->KeyLength;
+ bcopy(old->BSSID, new->BSSID, sizeof(NDIS_802_11_MAC_ADDRESS));
+ bcopy(old->KeyMaterial, (char *)new +
+ sizeof(NDIS_802_11_KEY_COMPAT), new->KeyLength);
+ ifr.ifr_data = (caddr_t)o;
+ } else
+ ifr.ifr_data = (caddr_t)oid;
+
+ strlcpy(ifr.ifr_name, a->name, sizeof(ifr.ifr_name));
+
+ if (set == TRUE)
+ retval = ioctl(a->socket, SIOCSDRVSPEC, &ifr);
+ else
+ retval = ioctl(a->socket, SIOCGDRVSPEC, &ifr);
+
+ if (o != NULL)
+ free(o);
+
+ if (retval)
+ return(0);
+
+ return(1);
+}
+
+int
+PacketGetAdapterNames(CHAR *namelist, ULONG *len)
+{
+ int mib[6];
+ size_t needed;
+ struct if_msghdr *ifm;
+ struct sockaddr_dl *sdl;
+ char *buf, *lim, *next;
+ char *plist;
+ int spc;
+ int i, ifcnt = 0;
+
+ plist = namelist;
+ spc = 0;
+
+ bzero(plist, *len);
+
+ needed = 0;
+ mib[0] = CTL_NET;
+ mib[1] = PF_ROUTE;
+ mib[2] = 0; /* protocol */
+ mib[3] = 0; /* wildcard address family */
+ mib[4] = NET_RT_IFLIST;
+ mib[5] = 0; /* no flags */
+
+ if (sysctl (mib, 6, NULL, &needed, NULL, 0) < 0)
+ return(FALSE);
+
+ buf = malloc (needed);
+ if (buf == NULL)
+ return(FALSE);
+
+ if (sysctl (mib, 6, buf, &needed, NULL, 0) < 0) {
+ free(buf);
+ return(FALSE);
+ }
+
+ lim = buf + needed;
+
+ /* Generate interface name list. */
+
+ next = buf;
+ while (next < lim) {
+ ifm = (struct if_msghdr *)next;
+ if (ifm->ifm_type == RTM_IFINFO) {
+ sdl = (struct sockaddr_dl *)(ifm + 1);
+ if (strnstr(sdl->sdl_data, "wlan", sdl->sdl_nlen)) {
+ if ((spc + sdl->sdl_nlen) > *len) {
+ free(buf);
+ return(FALSE);
+ }
+ strncpy(plist, sdl->sdl_data, sdl->sdl_nlen);
+ plist += (sdl->sdl_nlen + 1);
+ spc += (sdl->sdl_nlen + 1);
+ ifcnt++;
+ }
+ }
+ next += ifm->ifm_msglen;
+ }
+
+
+ /* Insert an extra "" as a spacer */
+
+ plist++;
+ spc++;
+
+ /*
+ * Now generate the interface description list. There
+ * must be a unique description for each interface, and
+ * they have to match what the ndis_events program will
+ * feed in later. To keep this simple, we just repeat
+ * the interface list over again.
+ */
+
+ next = buf;
+ while (next < lim) {
+ ifm = (struct if_msghdr *)next;
+ if (ifm->ifm_type == RTM_IFINFO) {
+ sdl = (struct sockaddr_dl *)(ifm + 1);
+ if (strnstr(sdl->sdl_data, "wlan", sdl->sdl_nlen)) {
+ if ((spc + sdl->sdl_nlen) > *len) {
+ free(buf);
+ return(FALSE);
+ }
+ strncpy(plist, sdl->sdl_data, sdl->sdl_nlen);
+ plist += (sdl->sdl_nlen + 1);
+ spc += (sdl->sdl_nlen + 1);
+ ifcnt++;
+ }
+ }
+ next += ifm->ifm_msglen;
+ }
+
+ free (buf);
+
+ *len = spc + 1;
+
+ return(TRUE);
+}
+
+void
+PacketCloseAdapter(void *iface)
+{
+ struct adapter *a;
+ struct ifreq ifr;
+ struct ieee80211req ireq;
+
+ if (iface == NULL)
+ return;
+
+ a = iface;
+
+ /* Reset net80211 roaming */
+ bzero((char *)&ireq, sizeof(ireq));
+ strncpy(ireq.i_name, a->name, sizeof (ifr.ifr_name));
+ ireq.i_type = IEEE80211_IOC_ROAMING;
+ ireq.i_val = a->prev_roaming;
+ ioctl(a->socket, SIOCS80211, &ireq);
+
+ bzero((char *)&ifr, sizeof(ifr));
+ strncpy(ifr.ifr_name, a->name, sizeof (ifr.ifr_name));
+ ioctl(a->socket, SIOCGIFFLAGS, (caddr_t)&ifr);
+ ifr.ifr_flags &= ~IFF_UP;
+ ioctl(a->socket, SIOCSIFFLAGS, (caddr_t)&ifr);
+ close(a->socket);
+ free(a);
+
+ return;
+}
+
+#if __FreeBSD_version < 600000
+
+/*
+ * The version of libpcap in FreeBSD 5.2.1 doesn't have these routines.
+ * Call me insane if you will, but I still run 5.2.1 on my laptop, and
+ * I'd like to use WPA there.
+ */
+
+int
+pcap_get_selectable_fd(pcap_t *p)
+{
+ return(pcap_fileno(p));
+}
+
+/*
+ * The old version of libpcap opens its BPF descriptor in read-only
+ * mode. We need to temporarily create a new one we can write to.
+ */
+
+int
+pcap_inject(pcap_t *p, const void *buf, size_t len)
+{
+ int fd;
+ int res, n = 0;
+ char device[sizeof "/dev/bpf0000000000"];
+ struct ifreq ifr;
+
+ /*
+ * Go through all the minors and find one that isn't in use.
+ */
+ do {
+ (void)snprintf(device, sizeof(device), "/dev/bpf%d", n++);
+ fd = open(device, O_RDWR);
+ } while (fd < 0 && errno == EBUSY);
+
+ if (fd == -1)
+ return(-1);
+
+ bzero((char *)&ifr, sizeof(ifr));
+ ioctl(pcap_fileno(p), BIOCGETIF, (caddr_t)&ifr);
+ ioctl(fd, BIOCSETIF, (caddr_t)&ifr);
+
+ res = write(fd, buf, len);
+
+ close(fd);
+
+ return(res);
+}
+#endif
diff --git a/freebsd/usr.sbin/wpa/wpa_supplicant/Packet32.h b/freebsd/usr.sbin/wpa/wpa_supplicant/Packet32.h
new file mode 100644
index 00000000..e0598e70
--- /dev/null
+++ b/freebsd/usr.sbin/wpa/wpa_supplicant/Packet32.h
@@ -0,0 +1,67 @@
+/*-
+ * Copyright (c) 2005
+ * Bill Paul <wpaul@windriver.com>. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Bill Paul.
+ * 4. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR THE VOICES IN HIS HEAD
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _PACKET32_H_
+#define _PACKET32_H_
+
+#include <sys/types.h>
+#include <ntddndis.h>
+
+struct PACKET_OID_DATA {
+ uint32_t Oid;
+ uint32_t Length;
+ uint8_t Data[1];
+};
+
+
+typedef struct PACKET_OID_DATA PACKET_OID_DATA;
+
+extern PCHAR PacketGetVersion(void);
+extern void *PacketOpenAdapter(CHAR *);
+extern int PacketRequest(void *, BOOLEAN, PACKET_OID_DATA *);
+extern int PacketGetAdapterNames(CHAR *, ULONG *);
+extern void PacketCloseAdapter(void *);
+
+/*
+ * This is for backwards compatibility on FreeBSD 5.
+ */
+
+#ifndef SIOCGDRVSPEC
+#define SIOCSDRVSPEC _IOW('i', 123, struct ifreq) /* set driver-specific
+ parameters */
+#define SIOCGDRVSPEC _IOWR('i', 123, struct ifreq) /* get driver-specific
+ parameters */
+#endif
+
+#endif /* _PACKET32_H_ */
diff --git a/freebsd/usr.sbin/wpa/wpa_supplicant/ntddndis.h b/freebsd/usr.sbin/wpa/wpa_supplicant/ntddndis.h
new file mode 100644
index 00000000..42e403d7
--- /dev/null
+++ b/freebsd/usr.sbin/wpa/wpa_supplicant/ntddndis.h
@@ -0,0 +1,31 @@
+#ifndef _NTDDNDIS_H_
+#define _NTDDNDIS_H_
+
+/*
+ * $FreeBSD$
+ */
+
+/*
+ * Fake up some of the Windows type definitions so that the NDIS
+ * interface module in wpa_supplicant will build.
+ */
+
+#define ULONG uint32_t
+#define USHORT uint16_t
+#define UCHAR uint8_t
+#define LONG int32_t
+#define SHORT int16_t
+#define CHAR int8_t
+#define ULONGLONG uint64_t
+#define LONGLONG int64_t
+#define BOOLEAN uint8_t
+typedef void * LPADAPTER;
+typedef char * PTSTR;
+typedef char * PCHAR;
+
+#define TRUE 1
+#define FALSE 0
+
+#define OID_802_3_CURRENT_ADDRESS 0x01010102
+
+#endif /* _NTDDNDIS_H_ */